Merge "Ports part of the backup/encryption/chunking code from gmscore."
diff --git a/Android.bp b/Android.bp
index faad6f3..9f93d39 100644
--- a/Android.bp
+++ b/Android.bp
@@ -152,9 +152,10 @@
":libcamera_client_framework_aidl",
"core/java/android/hardware/IConsumerIrService.aidl",
"core/java/android/hardware/ISerialManager.aidl",
+ "core/java/android/hardware/biometrics/IBiometricEnabledOnKeyguardCallback.aidl",
+ "core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl",
"core/java/android/hardware/biometrics/IBiometricService.aidl",
"core/java/android/hardware/biometrics/IBiometricServiceReceiver.aidl",
- "core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl",
"core/java/android/hardware/biometrics/IBiometricServiceLockoutResetCallback.aidl",
"core/java/android/hardware/display/IDisplayManager.aidl",
"core/java/android/hardware/display/IDisplayManagerCallback.aidl",
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/api/current.txt b/api/current.txt
index 81f71ac..4c67bae 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";
@@ -22,6 +23,7 @@
field public static final java.lang.String BIND_ACCESSIBILITY_SERVICE = "android.permission.BIND_ACCESSIBILITY_SERVICE";
field public static final java.lang.String BIND_APPWIDGET = "android.permission.BIND_APPWIDGET";
field public static final java.lang.String BIND_AUTOFILL_SERVICE = "android.permission.BIND_AUTOFILL_SERVICE";
+ field public static final java.lang.String BIND_CALL_REDIRECTION_SERVICE = "android.permission.BIND_CALL_REDIRECTION_SERVICE";
field public static final deprecated java.lang.String BIND_CARRIER_MESSAGING_SERVICE = "android.permission.BIND_CARRIER_MESSAGING_SERVICE";
field public static final java.lang.String BIND_CARRIER_SERVICES = "android.permission.BIND_CARRIER_SERVICES";
field public static final java.lang.String BIND_CHOOSER_TARGET_SERVICE = "android.permission.BIND_CHOOSER_TARGET_SERVICE";
@@ -279,7 +281,7 @@
field public static final int allowBackup = 16843392; // 0x1010280
field public static final int allowClearUserData = 16842757; // 0x1010005
field public static final int allowEmbedded = 16843765; // 0x10103f5
- field public static final int allowForceDark = 16844171; // 0x101058b
+ field public static final int allowForceDark = 16844172; // 0x101058c
field public static final int allowParallelSyncs = 16843570; // 0x1010332
field public static final int allowSingleTap = 16843353; // 0x1010259
field public static final int allowTaskReparenting = 16843268; // 0x1010204
@@ -773,9 +775,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 = 16844176; // 0x1010590
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 = 16844177; // 0x1010591
field public static final int isStatic = 16844122; // 0x101055a
field public static final int isSticky = 16843335; // 0x1010247
field public static final int isolatedProcess = 16843689; // 0x10103a9
@@ -935,7 +939,7 @@
field public static final int minSdkVersion = 16843276; // 0x101020c
field public static final int minWidth = 16843071; // 0x101013f
field public static final int minimumHorizontalAngle = 16843901; // 0x101047d
- field public static final int minimumUiTimeout = 16844174; // 0x101058e
+ field public static final int minimumUiTimeout = 16844175; // 0x101058f
field public static final int minimumVerticalAngle = 16843902; // 0x101047e
field public static final int mipMap = 16843725; // 0x10103cd
field public static final int mirrorForRtl = 16843726; // 0x10103ce
@@ -975,10 +979,10 @@
field public static final int onClick = 16843375; // 0x101026f
field public static final int oneshot = 16843159; // 0x1010197
field public static final int opacity = 16843550; // 0x101031e
- field public static final int opticalInsetBottom = 16844170; // 0x101058a
- field public static final int opticalInsetLeft = 16844167; // 0x1010587
- field public static final int opticalInsetRight = 16844169; // 0x1010589
- field public static final int opticalInsetTop = 16844168; // 0x1010588
+ field public static final int opticalInsetBottom = 16844171; // 0x101058b
+ field public static final int opticalInsetLeft = 16844168; // 0x1010588
+ field public static final int opticalInsetRight = 16844170; // 0x101058a
+ field public static final int opticalInsetTop = 16844169; // 0x1010589
field public static final int order = 16843242; // 0x10101ea
field public static final int orderInCategory = 16843231; // 0x10101df
field public static final int ordering = 16843490; // 0x10102e2
@@ -994,6 +998,7 @@
field public static final int overlapAnchor = 16843874; // 0x1010462
field public static final int overridesImplicitlyEnabledSubtype = 16843682; // 0x10103a2
field public static final int packageNames = 16843649; // 0x1010381
+ field public static final int packageType = 16844167; // 0x1010587
field public static final int padding = 16842965; // 0x10100d5
field public static final int paddingBottom = 16842969; // 0x10100d9
field public static final int paddingEnd = 16843700; // 0x10103b4
@@ -1303,7 +1308,7 @@
field public static final int summaryColumn = 16843426; // 0x10102a2
field public static final int summaryOff = 16843248; // 0x10101f0
field public static final int summaryOn = 16843247; // 0x10101ef
- field public static final int supportsAmbientMode = 16844172; // 0x101058c
+ field public static final int supportsAmbientMode = 16844173; // 0x101058d
field public static final int supportsAssist = 16844016; // 0x10104f0
field public static final int supportsLaunchVoiceAssistFromKeyguard = 16844017; // 0x10104f1
field public static final int supportsLocalInteraction = 16844047; // 0x101050f
@@ -4297,6 +4302,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);
@@ -6718,14 +6737,21 @@
field public static final java.lang.String EXTRA_PROVISIONING_SKIP_ENCRYPTION = "android.app.extra.PROVISIONING_SKIP_ENCRYPTION";
field public static final java.lang.String EXTRA_PROVISIONING_SKIP_USER_CONSENT = "android.app.extra.PROVISIONING_SKIP_USER_CONSENT";
field public static final java.lang.String EXTRA_PROVISIONING_TIME_ZONE = "android.app.extra.PROVISIONING_TIME_ZONE";
+ field public static final java.lang.String EXTRA_PROVISIONING_WIFI_ANONYMOUS_IDENTITY = "android.app.extra.PROVISIONING_WIFI_ANONYMOUS_IDENTITY";
+ field public static final java.lang.String EXTRA_PROVISIONING_WIFI_CA_CERTIFICATE = "android.app.extra.PROVISIONING_WIFI_CA_CERTIFICATE";
+ field public static final java.lang.String EXTRA_PROVISIONING_WIFI_DOMAIN = "android.app.extra.PROVISIONING_WIFI_DOMAIN";
+ field public static final java.lang.String EXTRA_PROVISIONING_WIFI_EAP_METHOD = "android.app.extra.PROVISIONING_WIFI_EAP_METHOD";
field public static final java.lang.String EXTRA_PROVISIONING_WIFI_HIDDEN = "android.app.extra.PROVISIONING_WIFI_HIDDEN";
+ field public static final java.lang.String EXTRA_PROVISIONING_WIFI_IDENTITY = "android.app.extra.PROVISIONING_WIFI_IDENTITY";
field public static final java.lang.String EXTRA_PROVISIONING_WIFI_PAC_URL = "android.app.extra.PROVISIONING_WIFI_PAC_URL";
field public static final java.lang.String EXTRA_PROVISIONING_WIFI_PASSWORD = "android.app.extra.PROVISIONING_WIFI_PASSWORD";
+ field public static final java.lang.String EXTRA_PROVISIONING_WIFI_PHASE2_AUTH = "android.app.extra.PROVISIONING_WIFI_PHASE2_AUTH";
field public static final java.lang.String EXTRA_PROVISIONING_WIFI_PROXY_BYPASS = "android.app.extra.PROVISIONING_WIFI_PROXY_BYPASS";
field public static final java.lang.String EXTRA_PROVISIONING_WIFI_PROXY_HOST = "android.app.extra.PROVISIONING_WIFI_PROXY_HOST";
field public static final java.lang.String EXTRA_PROVISIONING_WIFI_PROXY_PORT = "android.app.extra.PROVISIONING_WIFI_PROXY_PORT";
field public static final java.lang.String EXTRA_PROVISIONING_WIFI_SECURITY_TYPE = "android.app.extra.PROVISIONING_WIFI_SECURITY_TYPE";
field public static final java.lang.String EXTRA_PROVISIONING_WIFI_SSID = "android.app.extra.PROVISIONING_WIFI_SSID";
+ field public static final java.lang.String EXTRA_PROVISIONING_WIFI_USER_CERTIFICATE = "android.app.extra.PROVISIONING_WIFI_USER_CERTIFICATE";
field public static final int FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY = 1; // 0x1
field public static final int FLAG_MANAGED_CAN_ACCESS_PARENT = 2; // 0x2
field public static final int FLAG_PARENT_CAN_ACCESS_MANAGED = 1; // 0x1
@@ -13380,6 +13406,8 @@
method public void drawCircle(float, float, float, android.graphics.Paint);
method public void drawColor(int);
method public void drawColor(int, android.graphics.PorterDuff.Mode);
+ method public void drawDoubleRoundRect(android.graphics.RectF, float, float, android.graphics.RectF, float, float, android.graphics.Paint);
+ method public void drawDoubleRoundRect(android.graphics.RectF, float[], android.graphics.RectF, float[], android.graphics.Paint);
method public void drawLine(float, float, float, float, android.graphics.Paint);
method public void drawLines(float[], int, int, android.graphics.Paint);
method public void drawLines(float[], android.graphics.Paint);
@@ -13731,6 +13759,7 @@
method public static android.graphics.ImageDecoder.Source createSource(android.content.res.AssetManager, java.lang.String);
method public static android.graphics.ImageDecoder.Source createSource(java.nio.ByteBuffer);
method public static android.graphics.ImageDecoder.Source createSource(java.io.File);
+ method public static android.graphics.ImageDecoder.Source createSource(java.util.concurrent.Callable<android.content.res.AssetFileDescriptor>);
method public static android.graphics.Bitmap decodeBitmap(android.graphics.ImageDecoder.Source, android.graphics.ImageDecoder.OnHeaderDecodedListener) throws java.io.IOException;
method public static android.graphics.Bitmap decodeBitmap(android.graphics.ImageDecoder.Source) throws java.io.IOException;
method public static android.graphics.drawable.Drawable decodeDrawable(android.graphics.ImageDecoder.Source, android.graphics.ImageDecoder.OnHeaderDecodedListener) throws java.io.IOException;
@@ -15320,7 +15349,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();
}
@@ -15946,7 +15975,11 @@
package android.hardware.biometrics {
public class BiometricManager {
- method public boolean hasEnrolledBiometrics();
+ method public int canAuthenticate();
+ field public static final int ERROR_NONE = 0; // 0x0
+ field public static final int ERROR_NO_BIOMETRICS = 11; // 0xb
+ field public static final int ERROR_NO_HARDWARE = 12; // 0xc
+ field public static final int ERROR_UNAVAILABLE = 1; // 0x1
}
public class BiometricPrompt {
@@ -21633,7 +21666,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);
@@ -32506,6 +32539,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;
@@ -32534,6 +32568,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;
@@ -33708,6 +33750,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";
@@ -36928,6 +36971,7 @@
field public static final java.lang.String ACTION_APPLICATION_DEVELOPMENT_SETTINGS = "android.settings.APPLICATION_DEVELOPMENT_SETTINGS";
field public static final java.lang.String ACTION_APPLICATION_SETTINGS = "android.settings.APPLICATION_SETTINGS";
field public static final java.lang.String ACTION_APP_NOTIFICATION_SETTINGS = "android.settings.APP_NOTIFICATION_SETTINGS";
+ field public static final java.lang.String ACTION_APP_SEARCH_SETTINGS = "android.settings.APP_SEARCH_SETTINGS";
field public static final java.lang.String ACTION_BATTERY_SAVER_SETTINGS = "android.settings.BATTERY_SAVER_SETTINGS";
field public static final java.lang.String ACTION_BLUETOOTH_SETTINGS = "android.settings.BLUETOOTH_SETTINGS";
field public static final java.lang.String ACTION_CAPTIONING_SETTINGS = "android.settings.CAPTIONING_SETTINGS";
@@ -42553,7 +42597,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);
@@ -43014,7 +43058,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();
@@ -45025,11 +45068,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);
@@ -45672,6 +45724,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();
@@ -45692,6 +45745,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>);
@@ -46263,6 +46317,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);
}
@@ -46281,6 +46336,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);
}
@@ -46602,7 +46658,12 @@
}
public final class DisplayCutout {
- ctor public DisplayCutout(android.graphics.Rect, java.util.List<android.graphics.Rect>);
+ ctor public DisplayCutout(android.graphics.Insets, android.graphics.Rect, android.graphics.Rect, android.graphics.Rect, android.graphics.Rect);
+ ctor public deprecated DisplayCutout(android.graphics.Rect, java.util.List<android.graphics.Rect>);
+ method public android.graphics.Rect getBoundingRectBottom();
+ method public android.graphics.Rect getBoundingRectLeft();
+ method public android.graphics.Rect getBoundingRectRight();
+ method public android.graphics.Rect getBoundingRectTop();
method public java.util.List<android.graphics.Rect> getBoundingRects();
method public int getSafeInsetBottom();
method public int getSafeInsetLeft();
@@ -50298,7 +50359,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();
@@ -50322,7 +50383,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);
@@ -55230,6 +55291,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 5785e4a..e134554 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";
@@ -3995,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
@@ -4320,13 +4322,6 @@
field public static final java.lang.String STATE = "state";
}
- public static final class ContactsContract.RawContacts implements android.provider.BaseColumns android.provider.ContactsContract.ContactNameColumns android.provider.ContactsContract.ContactOptionsColumns android.provider.ContactsContract.RawContactsColumns android.provider.ContactsContract.SyncColumns {
- field public static final android.net.Uri RAW_CONTACTS_NOTIFICATION_DELETE_URI;
- field public static final android.net.Uri RAW_CONTACTS_NOTIFICATION_INSERT_URI;
- field public static final android.net.Uri RAW_CONTACTS_NOTIFICATION_UPDATE_URI;
- field public static final android.net.Uri RAW_CONTACTS_NOTIFICATION_URI;
- }
-
public abstract class SearchIndexableData {
ctor public SearchIndexableData();
ctor public SearchIndexableData(android.content.Context);
@@ -5335,9 +5330,12 @@
}
public class ServiceState implements android.os.Parcelable {
+ method public android.telephony.NetworkRegistrationState getNetworkRegistrationState(int, int);
method public java.util.List<android.telephony.NetworkRegistrationState> getNetworkRegistrationStates();
- method public java.util.List<android.telephony.NetworkRegistrationState> getNetworkRegistrationStates(int);
- method public android.telephony.NetworkRegistrationState getNetworkRegistrationStates(int, int);
+ method public deprecated java.util.List<android.telephony.NetworkRegistrationState> getNetworkRegistrationStates(int);
+ method public deprecated android.telephony.NetworkRegistrationState getNetworkRegistrationStates(int, int);
+ method public java.util.List<android.telephony.NetworkRegistrationState> getNetworkRegistrationStatesForDomain(int);
+ method public java.util.List<android.telephony.NetworkRegistrationState> getNetworkRegistrationStatesForTransportType(int);
}
public final class SmsManager {
@@ -5854,7 +5852,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_EMERGENCY_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";
diff --git a/api/test-current.txt b/api/test-current.txt
index 9567616..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;
}
@@ -932,13 +936,6 @@
field public static final android.net.Uri ENTERPRISE_CONTENT_URI;
}
- public static final class ContactsContract.RawContacts implements android.provider.BaseColumns android.provider.ContactsContract.ContactNameColumns android.provider.ContactsContract.ContactOptionsColumns android.provider.ContactsContract.RawContactsColumns android.provider.ContactsContract.SyncColumns {
- field public static final android.net.Uri RAW_CONTACTS_NOTIFICATION_DELETE_URI;
- field public static final android.net.Uri RAW_CONTACTS_NOTIFICATION_INSERT_URI;
- field public static final android.net.Uri RAW_CONTACTS_NOTIFICATION_UPDATE_URI;
- field public static final android.net.Uri RAW_CONTACTS_NOTIFICATION_URI;
- }
-
public static final class ContactsContract.RawContactsEntity implements android.provider.BaseColumns android.provider.ContactsContract.DataColumns android.provider.ContactsContract.RawContactsColumns {
field public static final android.net.Uri CORP_CONTENT_URI;
}
diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk
index d3496ed..f6b0db8 100644
--- a/cmds/statsd/Android.mk
+++ b/cmds/statsd/Android.mk
@@ -99,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 \
@@ -227,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/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index 2ef1169..8da2d44 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -992,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 0618927..1f1d782 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -27,6 +27,8 @@
#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>
@@ -38,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;
@@ -45,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();
@@ -146,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;
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 1e9c354..8ab67e3 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -31,6 +31,7 @@
import "frameworks/base/core/proto/android/view/enums.proto";
import "frameworks/base/proto/src/stats_enums.proto";
import "frameworks/base/core/proto/android/service/procstats.proto";
+import "frameworks/base/core/proto/android/internal/powerprofile.proto";
/**
* The master atom class. This message defines all of the available
@@ -138,6 +139,9 @@
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.
@@ -172,9 +176,11 @@
DirectoryUsage directory_usage = 10026;
AppSize app_size = 10027;
CategorySize category_size = 10028;
+ ProcStats proc_stats = 10029;
BatteryVoltage battery_voltage = 10030;
NumFingerprints num_fingerprints = 10031;
- ProcStats proc_stats = 10029;
+ DiskIo disk_io = 10032;
+ PowerProfile power_profile = 10033;
}
// DO NOT USE field numbers above 100,000 in AOSP.
@@ -200,9 +206,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;
}
}
@@ -769,6 +776,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;
}
/**
@@ -1309,6 +1319,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:
@@ -2553,6 +2625,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.
@@ -2571,3 +2667,11 @@
message ProcStats {
optional android.service.procstats.ProcessStatsSectionProto proc_stats_section = 1;
}
+
+/**
+ * power_profile.xml and other constants for power model calculations.
+ * Pulled from PowerProfile.java
+ */
+message PowerProfile {
+ optional com.android.internal.os.PowerProfileProto power_profile_proto = 1;
+}
\ No newline at end of file
diff --git a/cmds/statsd/src/external/StatsCompanionServicePuller.cpp b/cmds/statsd/src/external/StatsCompanionServicePuller.cpp
index 6d7bba0..3eb05a9 100644
--- a/cmds/statsd/src/external/StatsCompanionServicePuller.cpp
+++ b/cmds/statsd/src/external/StatsCompanionServicePuller.cpp
@@ -54,7 +54,7 @@
vector<StatsLogEventWrapper> returned_value;
Status status = statsCompanionServiceCopy->pullData(mTagId, &returned_value);
if (!status.isOk()) {
- ALOGW("StatsCompanionServicePuller::pull failed to pull for %d", mTagId);
+ ALOGW("StatsCompanionServicePuller::pull failed for %d", mTagId);
return false;
}
data->clear();
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index 66392f8..cd215b4 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -163,10 +163,7 @@
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, 9},
@@ -204,17 +201,26 @@
{{}, {}, 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)}},
+ // PowerProfile constants for power model calculations.
+ {android::util::POWER_PROFILE,
+ {{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::POWER_PROFILE)}},
+};
StatsPullerManager::StatsPullerManager() : mNextPullTimeNs(NO_ALARM_UPDATE) {
}
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/shell/ShellSubscriber.cpp b/cmds/statsd/src/shell/ShellSubscriber.cpp
index 3cd49d7..1306a46 100644
--- a/cmds/statsd/src/shell/ShellSubscriber.cpp
+++ b/cmds/statsd/src/shell/ShellSubscriber.cpp
@@ -113,12 +113,12 @@
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.
- event.ToProto(mProto);
mProto.flush(mOutput);
mProto.clear();
break;
@@ -137,4 +137,4 @@
} // namespace statsd
} // namespace os
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/cmds/statsd/tests/LogEvent_test.cpp b/cmds/statsd/tests/LogEvent_test.cpp
index 6e3b04c..ced65f2 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(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(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(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(0x2020684, 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(0x2020785, 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(0x2028885, 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/config/hiddenapi-light-greylist.txt b/config/hiddenapi-light-greylist.txt
index ac16fd3..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
diff --git a/config/preloaded-classes b/config/preloaded-classes
index d93befd..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
@@ -3303,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/core/java/android/accounts/AbstractAccountAuthenticator.java b/core/java/android/accounts/AbstractAccountAuthenticator.java
index a3b3a9f..25cd342 100644
--- a/core/java/android/accounts/AbstractAccountAuthenticator.java
+++ b/core/java/android/accounts/AbstractAccountAuthenticator.java
@@ -17,7 +17,6 @@
package android.accounts;
import android.Manifest;
-import android.annotation.SystemApi;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
@@ -32,8 +31,8 @@
/**
* Abstract base class for creating AccountAuthenticators.
- * In order to be an authenticator one must extend this class, provider implementations for the
- * abstract methods and write a service that returns the result of {@link #getIBinder()}
+ * In order to be an authenticator one must extend this class, provide implementations for the
+ * abstract methods, and write a service that returns the result of {@link #getIBinder()}
* in the service's {@link android.app.Service#onBind(android.content.Intent)} when invoked
* with an intent with action {@link AccountManager#ACTION_AUTHENTICATOR_INTENT}. This service
* must specify the following intent filter and metadata tags in its AndroidManifest.xml file
@@ -974,7 +973,8 @@
*
* @param response to send the result back to the AccountManager, will never be null.
* @param account the account to check, will never be null
- * @param statusToken a String of token to check if update of credentials is suggested.
+ * @param statusToken a String of token which can be used to check the status of locally
+ * stored credentials and if update of credentials is suggested
* @return a Bundle result or null if the result is to be returned via the response. The result
* will contain either:
* <ul>
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/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 294a3ec..76b90f5 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -245,4 +245,6 @@
public abstract ComponentName startServiceInPackage(int uid, Intent service,
String resolvedType, boolean fgRequired, String callingPackage, int userId)
throws TransactionTooLargeException;
+
+ public abstract void disconnectActivityFromServices(Object connectionHolder);
}
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/Instrumentation.java b/core/java/android/app/Instrumentation.java
index 34c2282..1144e26 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -1760,7 +1760,7 @@
/**
* Like {@link #execStartActivity(android.content.Context, android.os.IBinder,
* android.os.IBinder, String, android.content.Intent, int, android.os.Bundle)},
- * but for calls from a {#link Fragment}.
+ * but for calls from a {@link Fragment}.
*
* @param who The Context from which the activity is being started.
* @param contextThread The main thread of the Context from which the activity
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 07a8504..3f07024 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -48,6 +48,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
@@ -722,10 +723,15 @@
public List<NotificationChannelGroup> getNotificationChannelGroups() {
INotificationManager service = getService();
try {
- return service.getNotificationChannelGroups(mContext.getPackageName()).getList();
+ final ParceledListSlice<NotificationChannelGroup> parceledList =
+ service.getNotificationChannelGroups(mContext.getPackageName());
+ if (parceledList != null) {
+ return parceledList.getList();
+ }
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
+ return new ArrayList<>();
}
/**
diff --git a/core/java/android/app/Service.java b/core/java/android/app/Service.java
index 67acfe9..16f6bda 100644
--- a/core/java/android/app/Service.java
+++ b/core/java/android/app/Service.java
@@ -21,13 +21,13 @@
import android.annotation.UnsupportedAppUsage;
import android.content.ComponentCallbacks2;
import android.content.ComponentName;
-import android.content.Intent;
-import android.content.ContextWrapper;
import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.Intent;
import android.content.res.Configuration;
import android.os.Build;
-import android.os.RemoteException;
import android.os.IBinder;
+import android.os.RemoteException;
import android.util.Log;
import java.io.FileDescriptor;
@@ -391,7 +391,7 @@
* don't recreate until a future explicit call to
* {@link Context#startService Context.startService(Intent)}. The
* service will not receive a {@link #onStartCommand(Intent, int, int)}
- * call with a null Intent because it will not be re-started if there
+ * call with a null Intent because it will not be restarted if there
* are no pending Intents to deliver.
*
* <p>This mode makes sense for things that want to do some work as a
@@ -416,7 +416,7 @@
* redelivery until the service calls {@link #stopSelf(int)} with the
* start ID provided to {@link #onStartCommand}. The
* service will not receive a {@link #onStartCommand(Intent, int, int)}
- * call with a null Intent because it will will only be re-started if
+ * call with a null Intent because it will only be restarted if
* it is not finished processing all Intents sent to it (and any such
* pending events will be delivered at the point of restart).
*/
diff --git a/core/java/android/app/SmsAppService.java b/core/java/android/app/SmsAppService.java
index 3f2b025..3829d71 100644
--- a/core/java/android/app/SmsAppService.java
+++ b/core/java/android/app/SmsAppService.java
@@ -24,21 +24,42 @@
* 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}
+ * <p>The service must have an {@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
+ * <p>The service must be associated with a non-main process, meaning it must have an
+ * {@code android:process} tag in its manifest entry.
+ *
+ * <p>An app 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.
+ * to disable or enable the service. An app 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.
+ *
+ * <p>Example: First, define a subclass in the application:
+ * <pre>
+ * public class MySmsAppService extends SmsAppService {
+ * }
+ * </pre>
+ * Then, declare it in its {@code AndroidManifest.xml}:
+ * <pre>
+ * <service
+ * android:name=".MySmsAppService"
+ * android:exported="false"
+ * android:process=":persistent"
+ * android:permission="android.permission.BIND_SMS_APP_SERVICE">
+ * <intent-filter>
+ * <action android:name="android.telephony.action.SMS_APP_SERVICE" />
+ * </intent-filter>
+ * </service>
+ * </pre>
*/
public class SmsAppService extends Service {
private final ISmsAppService mImpl;
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/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 fc67c10..09ab671 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -664,8 +664,8 @@
/**
* A String extra indicating the security type of the wifi network in
- * {@link #EXTRA_PROVISIONING_WIFI_SSID} and could be one of {@code NONE}, {@code WPA} or
- * {@code WEP}.
+ * {@link #EXTRA_PROVISIONING_WIFI_SSID} and could be one of {@code NONE}, {@code WPA},
+ * {@code WEP} or {@code EAP}.
*
* <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner
* provisioning via an NFC bump.
@@ -680,8 +680,89 @@
* <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner
* provisioning via an NFC bump.
*/
- public static final String EXTRA_PROVISIONING_WIFI_PASSWORD
- = "android.app.extra.PROVISIONING_WIFI_PASSWORD";
+ public static final String EXTRA_PROVISIONING_WIFI_PASSWORD =
+ "android.app.extra.PROVISIONING_WIFI_PASSWORD";
+
+ /**
+ * The EAP method of the wifi network in {@link #EXTRA_PROVISIONING_WIFI_SSID}
+ * and could be one of {@code PEAP}, {@code TLS}, {@code TTLS}, {@code PWD}, {@code SIM},
+ * {@code AKA} or {@code AKA_PRIME}. This is only used if the
+ * {@link #EXTRA_PROVISIONING_WIFI_SECURITY_TYPE} is {@code EAP}.
+ *
+ * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner
+ * provisioning via an NFC bump. It can also be used for QR code provisioning.
+ */
+ public static final String EXTRA_PROVISIONING_WIFI_EAP_METHOD =
+ "android.app.extra.PROVISIONING_WIFI_EAP_METHOD";
+
+ /**
+ * The phase 2 authentication of the wifi network in {@link #EXTRA_PROVISIONING_WIFI_SSID}
+ * and could be one of {@code NONE}, {@code PAP}, {@code MSCHAP}, {@code MSCHAPV2}, {@code GTC},
+ * {@code SIM}, {@code AKA} or {@code AKA_PRIME}. This is only used if the
+ * {@link #EXTRA_PROVISIONING_WIFI_SECURITY_TYPE} is {@code EAP}.
+ *
+ * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner
+ * provisioning via an NFC bump. It can also be used for QR code provisioning.
+ */
+ public static final String EXTRA_PROVISIONING_WIFI_PHASE2_AUTH =
+ "android.app.extra.PROVISIONING_WIFI_PHASE2_AUTH";
+
+ /**
+ * The CA certificate of the wifi network in {@link #EXTRA_PROVISIONING_WIFI_SSID}. This should
+ * be an X.509 certificate Base64 encoded DER format, ie. PEM representation of a certificate
+ * without header, footer and line breaks. <a href=
+ * "https://tools.ietf.org/html/rfc7468"> More information</a> This is only
+ * used if the {@link
+ * #EXTRA_PROVISIONING_WIFI_SECURITY_TYPE} is {@code EAP}.
+ *
+ * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner
+ * provisioning via an NFC bump. It can also be used for QR code provisioning.
+ */
+ public static final String EXTRA_PROVISIONING_WIFI_CA_CERTIFICATE =
+ "android.app.extra.PROVISIONING_WIFI_CA_CERTIFICATE";
+
+ /**
+ * The user certificate of the wifi network in {@link #EXTRA_PROVISIONING_WIFI_SSID}. This
+ * should be an X.509 certificate and private key Base64 encoded DER format, ie. PEM
+ * representation of a certificate and key without header, footer and line breaks. <a href=
+ * "https://tools.ietf.org/html/rfc7468"> More information</a> This is only
+ * used if the {@link #EXTRA_PROVISIONING_WIFI_SECURITY_TYPE} is {@code EAP}.
+ *
+ * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner
+ * provisioning via an NFC bump. It can also be used for QR code provisioning.
+ */
+ public static final String EXTRA_PROVISIONING_WIFI_USER_CERTIFICATE =
+ "android.app.extra.PROVISIONING_WIFI_USER_CERTIFICATE";
+
+ /**
+ * The identity of the wifi network in {@link #EXTRA_PROVISIONING_WIFI_SSID}. This is only used
+ * if the {@link #EXTRA_PROVISIONING_WIFI_SECURITY_TYPE} is {@code EAP}.
+ *
+ * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner
+ * provisioning via an NFC bump. It can also be used for QR code provisioning.
+ */
+ public static final String EXTRA_PROVISIONING_WIFI_IDENTITY =
+ "android.app.extra.PROVISIONING_WIFI_IDENTITY";
+
+ /**
+ * The anonymous identity of the wifi network in {@link #EXTRA_PROVISIONING_WIFI_SSID}. This is
+ * only used if the {@link #EXTRA_PROVISIONING_WIFI_SECURITY_TYPE} is {@code EAP}.
+ *
+ * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner
+ * provisioning via an NFC bump. It can also be used for QR code provisioning.
+ */
+
+ public static final String EXTRA_PROVISIONING_WIFI_ANONYMOUS_IDENTITY =
+ "android.app.extra.PROVISIONING_WIFI_ANONYMOUS_IDENTITY";
+ /**
+ * The domain of the wifi network in {@link #EXTRA_PROVISIONING_WIFI_SSID}. This is only used if
+ * the {@link #EXTRA_PROVISIONING_WIFI_SECURITY_TYPE} is {@code EAP}.
+ *
+ * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner
+ * provisioning via an NFC bump. It can also be used for QR code provisioning.
+ */
+ public static final String EXTRA_PROVISIONING_WIFI_DOMAIN =
+ "android.app.extra.PROVISIONING_WIFI_DOMAIN";
/**
* A String extra holding the proxy host for the wifi network in
@@ -1067,8 +1148,22 @@
* <li>{@link #EXTRA_PROVISIONING_WIFI_PROXY_PORT} (convert to String), optional</li>
* <li>{@link #EXTRA_PROVISIONING_WIFI_PROXY_BYPASS}, optional</li>
* <li>{@link #EXTRA_PROVISIONING_WIFI_PAC_URL}, optional</li>
- * <li>{@link #EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE}, optional, supported from
- * {@link android.os.Build.VERSION_CODES#M} </li></ul>
+ * <li>{@link #EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE}, optional, supported from {@link
+ * android.os.Build.VERSION_CODES#M} </li>
+ * <li>{@link #EXTRA_PROVISIONING_WIFI_EAP_METHOD}, optional, supported from {@link
+ * android.os.Build.VERSION_CODES#Q}</li>
+ * <li>{@link #EXTRA_PROVISIONING_WIFI_PHASE2_AUTH}, optional, supported from {@link
+ * android.os.Build.VERSION_CODES#Q}</li>
+ * <li>{@link #EXTRA_PROVISIONING_WIFI_CA_CERTIFICATE}, optional, supported from {@link
+ * android.os.Build.VERSION_CODES#Q}</li>
+ * <li>{@link #EXTRA_PROVISIONING_WIFI_USER_CERTIFICATE}, optional, supported from {@link
+ * android.os.Build.VERSION_CODES#Q}</li>
+ * <li>{@link #EXTRA_PROVISIONING_WIFI_IDENTITY}, optional, supported from {@link
+ * android.os.Build.VERSION_CODES#Q}</li>
+ * <li>{@link #EXTRA_PROVISIONING_WIFI_ANONYMOUS_IDENTITY}, optional, supported from {@link
+ * android.os.Build.VERSION_CODES#Q}</li>
+ * <li>{@link #EXTRA_PROVISIONING_WIFI_DOMAIN}, optional, supported from {@link
+ * android.os.Build.VERSION_CODES#Q}</li></ul>
*
* <p>
* As of {@link android.os.Build.VERSION_CODES#M}, the properties should contain
@@ -7404,6 +7499,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/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 654bfaf..8e6a385 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -2973,7 +2973,7 @@
* socket will be encrypted.
* <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming connections from a listening
* {@link BluetoothServerSocket}.
- * <p>The system will assign a dynamic PSM value. This PSM value can be read from the {#link
+ * <p>The system will assign a dynamic PSM value. This PSM value can be read from the {@link
* BluetoothServerSocket#getPsm()} and this value will be released when this server socket is
* closed, Bluetooth is turned off, or the application exits unexpectedly.
* <p>The mechanism of disclosing the assigned dynamic PSM value to the initiating peer is
@@ -3031,7 +3031,7 @@
* <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming connections from a listening
* {@link BluetoothServerSocket}.
* <p>The system will assign a dynamic protocol/service multiplexer (PSM) value. This PSM value
- * can be read from the {#link BluetoothServerSocket#getPsm()} and this value will be released
+ * can be read from the {@link BluetoothServerSocket#getPsm()} and this value will be released
* when this server socket is closed, Bluetooth is turned off, or the application exits
* unexpectedly.
* <p>The mechanism of disclosing the assigned dynamic PSM value to the initiating peer is
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index 73e98cd..30d5fbc 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -1596,7 +1596,7 @@
* For example, for Bluetooth 2.1 devices, if any of the devices does not
* have an input and output capability or just has the ability to
* display a numeric key, a secure socket connection is not possible.
- * In such a case, use {#link createInsecureRfcommSocket}.
+ * In such a case, use {@link createInsecureRfcommSocket}.
* For more details, refer to the Security Model section 5.2 (vol 3) of
* Bluetooth Core Specification version 2.1 + EDR.
* <p>Use {@link BluetoothSocket#connect} to initiate the outgoing
@@ -1631,7 +1631,7 @@
* For example, for Bluetooth 2.1 devices, if any of the devices does not
* have an input and output capability or just has the ability to
* display a numeric key, a secure socket connection is not possible.
- * In such a case, use {#link createInsecureRfcommSocket}.
+ * In such a case, use {@link createInsecureRfcommSocket}.
* For more details, refer to the Security Model section 5.2 (vol 3) of
* Bluetooth Core Specification version 2.1 + EDR.
* <p>Use {@link BluetoothSocket#connect} to initiate the outgoing
@@ -1688,7 +1688,7 @@
* For example, for Bluetooth 2.1 devices, if any of the devices does not
* have an input and output capability or just has the ability to
* display a numeric key, a secure socket connection is not possible.
- * In such a case, use {#link createInsecureRfcommSocketToServiceRecord}.
+ * In such a case, use {@link #createInsecureRfcommSocketToServiceRecord}.
* For more details, refer to the Security Model section 5.2 (vol 3) of
* Bluetooth Core Specification version 2.1 + EDR.
* <p>Hint: If you are connecting to a Bluetooth serial board then try
@@ -1972,7 +1972,7 @@
* encrypted.
* <p> Use this socket if an authenticated socket link is possible. Authentication refers
* to the authentication of the link key to prevent man-in-the-middle type of attacks. When a
- * secure socket connection is not possible, use {#link createInsecureLeL2capCocSocket(int,
+ * secure socket connection is not possible, use {@link createInsecureLeL2capCocSocket(int,
* int)}.
*
* @param psm dynamic PSM value from remote device
diff --git a/core/java/android/bluetooth/BluetoothServerSocket.java b/core/java/android/bluetooth/BluetoothServerSocket.java
index 5fc344a..758c68d 100644
--- a/core/java/android/bluetooth/BluetoothServerSocket.java
+++ b/core/java/android/bluetooth/BluetoothServerSocket.java
@@ -203,7 +203,7 @@
/**
* Returns the assigned dynamic protocol/service multiplexer (PSM) value for the listening L2CAP
* Connection-oriented Channel (CoC) server socket. This server socket must be returned by the
- * {#link BluetoothAdapter.listenUsingL2capChannel()} or {#link
+ * {@link BluetoothAdapter.listenUsingL2capChannel()} or {@link
* BluetoothAdapter.listenUsingInsecureL2capChannel()}. The returned value is undefined if this
* method is called on non-L2CAP server sockets.
*
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index a64eead..a13a438 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -2104,7 +2104,11 @@
// a source of security issues.
final String encodedPath = uri.getEncodedPath();
if (encodedPath != null && encodedPath.indexOf("//") != -1) {
- return uri.buildUpon().encodedPath(encodedPath.replaceAll("//+", "/")).build();
+ final Uri normalized = uri.buildUpon()
+ .encodedPath(encodedPath.replaceAll("//+", "/")).build();
+ Log.w(TAG, "Normalized " + uri + " to " + normalized
+ + " to avoid possible security issues");
+ return normalized;
} else {
return uri;
}
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 1fa5190..24675d3 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -452,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,
@@ -478,6 +480,7 @@
this.use32bitAbi = use32bitAbi;
this.extractNativeLibs = extractNativeLibs;
this.isolatedSplits = isolatedSplits;
+ this.isSplitRequired = isSplitRequired;
}
public long getLongVersionCode() {
@@ -1695,6 +1698,7 @@
boolean extractNativeLibs = true;
boolean isolatedSplits = false;
boolean isFeatureSplit = false;
+ boolean isSplitRequired = false;
String configForSplit = null;
String usesSplitName = null;
@@ -1717,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);
}
}
@@ -1772,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);
}
@@ -5779,52 +5785,32 @@
int AUTH = 16;
}
- /**
- * APK Signature Scheme v3 includes support for adding a proof-of-rotation record that
- * contains two pieces of information:
- * 1) the past signing certificates
- * 2) the flags that APK wants to assign to each of the past signing certificates.
- *
- * These flags, which have a one-to-one relationship for the {@code pastSigningCertificates}
- * collection, represent the second piece of information and are viewed as capabilities.
- * They are an APK's way of telling the platform: "this is how I want to trust my old certs,
- * please enforce that." This is useful for situation where this app itself is using its
- * signing certificate as an authorization mechanism, like whether or not to allow another
- * app to have its SIGNATURE permission. An app could specify whether to allow other apps
- * signed by its old cert 'X' to still get a signature permission it defines, for example.
- */
- @Nullable
- public final int[] pastSigningCertificatesFlags;
-
/** A representation of unknown signing details. Use instead of null. */
public static final SigningDetails UNKNOWN =
- new SigningDetails(null, SignatureSchemeVersion.UNKNOWN, null, null, null);
+ new SigningDetails(null, SignatureSchemeVersion.UNKNOWN, null, null);
@VisibleForTesting
public SigningDetails(Signature[] signatures,
@SignatureSchemeVersion int signatureSchemeVersion,
- ArraySet<PublicKey> keys, Signature[] pastSigningCertificates,
- int[] pastSigningCertificatesFlags) {
+ ArraySet<PublicKey> keys, Signature[] pastSigningCertificates) {
this.signatures = signatures;
this.signatureSchemeVersion = signatureSchemeVersion;
this.publicKeys = keys;
this.pastSigningCertificates = pastSigningCertificates;
- this.pastSigningCertificatesFlags = pastSigningCertificatesFlags;
}
public SigningDetails(Signature[] signatures,
@SignatureSchemeVersion int signatureSchemeVersion,
- Signature[] pastSigningCertificates, int[] pastSigningCertificatesFlags)
+ Signature[] pastSigningCertificates)
throws CertificateException {
this(signatures, signatureSchemeVersion, toSigningKeys(signatures),
- pastSigningCertificates, pastSigningCertificatesFlags);
+ pastSigningCertificates);
}
public SigningDetails(Signature[] signatures,
@SignatureSchemeVersion int signatureSchemeVersion)
throws CertificateException {
- this(signatures, signatureSchemeVersion,
- null, null);
+ this(signatures, signatureSchemeVersion, null);
}
public SigningDetails(SigningDetails orig) {
@@ -5838,17 +5824,14 @@
this.publicKeys = new ArraySet<>(orig.publicKeys);
if (orig.pastSigningCertificates != null) {
this.pastSigningCertificates = orig.pastSigningCertificates.clone();
- this.pastSigningCertificatesFlags = orig.pastSigningCertificatesFlags.clone();
} else {
this.pastSigningCertificates = null;
- this.pastSigningCertificatesFlags = null;
}
} else {
this.signatures = null;
this.signatureSchemeVersion = SignatureSchemeVersion.UNKNOWN;
this.publicKeys = null;
this.pastSigningCertificates = null;
- this.pastSigningCertificatesFlags = null;
}
}
@@ -5950,7 +5933,7 @@
if (Signature.areEffectiveMatch(
oldDetails.signatures[0],
pastSigningCertificates[i])
- && pastSigningCertificatesFlags[i] == flags) {
+ && pastSigningCertificates[i].getFlags() == flags) {
return true;
}
}
@@ -6000,7 +5983,7 @@
for (int i = 0; i < pastSigningCertificates.length - 1; i++) {
if (pastSigningCertificates[i].equals(signature)) {
if (flags == PAST_CERT_EXISTS
- || (flags & pastSigningCertificatesFlags[i]) == flags) {
+ || (flags & pastSigningCertificates[i].getFlags()) == flags) {
return true;
}
}
@@ -6084,7 +6067,7 @@
pastSigningCertificates[i].toByteArray());
if (Arrays.equals(sha256Certificate, digest)) {
if (flags == PAST_CERT_EXISTS
- || (flags & pastSigningCertificatesFlags[i]) == flags) {
+ || (flags & pastSigningCertificates[i].getFlags()) == flags) {
return true;
}
}
@@ -6121,7 +6104,6 @@
dest.writeInt(this.signatureSchemeVersion);
dest.writeArraySet(this.publicKeys);
dest.writeTypedArray(this.pastSigningCertificates, flags);
- dest.writeIntArray(this.pastSigningCertificatesFlags);
}
protected SigningDetails(Parcel in) {
@@ -6130,7 +6112,6 @@
this.signatureSchemeVersion = in.readInt();
this.publicKeys = (ArraySet<PublicKey>) in.readArraySet(boot);
this.pastSigningCertificates = in.createTypedArray(Signature.CREATOR);
- this.pastSigningCertificatesFlags = in.createIntArray();
}
public static final Creator<SigningDetails> CREATOR = new Creator<SigningDetails>() {
@@ -6169,9 +6150,6 @@
if (!Arrays.equals(pastSigningCertificates, that.pastSigningCertificates)) {
return false;
}
- if (!Arrays.equals(pastSigningCertificatesFlags, that.pastSigningCertificatesFlags)) {
- return false;
- }
return true;
}
@@ -6182,7 +6160,6 @@
result = 31 * result + signatureSchemeVersion;
result = 31 * result + (publicKeys != null ? publicKeys.hashCode() : 0);
result = 31 * result + Arrays.hashCode(pastSigningCertificates);
- result = 31 * result + Arrays.hashCode(pastSigningCertificatesFlags);
return result;
}
@@ -6193,7 +6170,6 @@
private Signature[] mSignatures;
private int mSignatureSchemeVersion = SignatureSchemeVersion.UNKNOWN;
private Signature[] mPastSigningCertificates;
- private int[] mPastSigningCertificatesFlags;
@UnsupportedAppUsage
public Builder() {
@@ -6220,34 +6196,12 @@
return this;
}
- /** set the flags for the {@code pastSigningCertificates} */
- @UnsupportedAppUsage
- public Builder setPastSigningCertificatesFlags(int[] pastSigningCertificatesFlags) {
- mPastSigningCertificatesFlags = pastSigningCertificatesFlags;
- return this;
- }
-
private void checkInvariants() {
// must have signatures and scheme version set
if (mSignatures == null) {
throw new IllegalStateException("SigningDetails requires the current signing"
+ " certificates.");
}
-
- // pastSigningCerts and flags must match up
- boolean pastMismatch = false;
- if (mPastSigningCertificates != null && mPastSigningCertificatesFlags != null) {
- if (mPastSigningCertificates.length != mPastSigningCertificatesFlags.length) {
- pastMismatch = true;
- }
- } else if (!(mPastSigningCertificates == null
- && mPastSigningCertificatesFlags == null)) {
- pastMismatch = true;
- }
- if (pastMismatch) {
- throw new IllegalStateException("SigningDetails must have a one to one mapping "
- + "between pastSigningCertificates and pastSigningCertificatesFlags");
- }
}
/** build a {@code SigningDetails} object */
@UnsupportedAppUsage
@@ -6255,7 +6209,7 @@
throws CertificateException {
checkInvariants();
return new SigningDetails(mSignatures, mSignatureSchemeVersion,
- mPastSigningCertificates, mPastSigningCertificatesFlags);
+ mPastSigningCertificates);
}
}
}
diff --git a/core/java/android/content/pm/Signature.java b/core/java/android/content/pm/Signature.java
index e58ca60..349bb69 100644
--- a/core/java/android/content/pm/Signature.java
+++ b/core/java/android/content/pm/Signature.java
@@ -45,6 +45,20 @@
private boolean mHaveHashCode;
private SoftReference<String> mStringRef;
private Certificate[] mCertificateChain;
+ /**
+ * APK Signature Scheme v3 includes support for adding a proof-of-rotation record that
+ * contains two pieces of information:
+ * 1) the past signing certificates
+ * 2) the flags that APK wants to assign to each of the past signing certificates.
+ *
+ * These flags represent the second piece of information and are viewed as capabilities.
+ * They are an APK's way of telling the platform: "this is how I want to trust my old certs,
+ * please enforce that." This is useful for situation where this app itself is using its
+ * signing certificate as an authorization mechanism, like whether or not to allow another
+ * app to have its SIGNATURE permission. An app could specify whether to allow other apps
+ * signed by its old cert 'X' to still get a signature permission it defines, for example.
+ */
+ private int mFlags;
/**
* Create Signature from an existing raw byte array.
@@ -109,6 +123,22 @@
}
/**
+ * Sets the flags representing the capabilities of the past signing certificate.
+ * @hide
+ */
+ public void setFlags(int flags) {
+ this.mFlags = flags;
+ }
+
+ /**
+ * Returns the flags representing the capabilities of the past signing certificate.
+ * @hide
+ */
+ public int getFlags() {
+ return mFlags;
+ }
+
+ /**
* Encode the Signature as ASCII text.
*/
public char[] toChars() {
@@ -328,4 +358,4 @@
return sPrime;
}
-}
+}
\ No newline at end of file
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/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/BiometricConstants.java b/core/java/android/hardware/biometrics/BiometricConstants.java
index 6150be3..2a64c2e 100644
--- a/core/java/android/hardware/biometrics/BiometricConstants.java
+++ b/core/java/android/hardware/biometrics/BiometricConstants.java
@@ -34,6 +34,13 @@
//
/**
+ * This was not added here since it would update BiometricPrompt API. But, is used in
+ * BiometricManager.
+ * @hide
+ */
+ int BIOMETRIC_ERROR_NONE = 0;
+
+ /**
* The hardware is unavailable. Try again later.
*/
int BIOMETRIC_ERROR_HW_UNAVAILABLE = 1;
diff --git a/core/java/android/hardware/biometrics/BiometricManager.java b/core/java/android/hardware/biometrics/BiometricManager.java
index eea5f9b..0faecb0 100644
--- a/core/java/android/hardware/biometrics/BiometricManager.java
+++ b/core/java/android/hardware/biometrics/BiometricManager.java
@@ -17,16 +17,44 @@
package android.hardware.biometrics;
import static android.Manifest.permission.USE_BIOMETRIC;
+import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
+import android.annotation.IntDef;
import android.annotation.RequiresPermission;
import android.content.Context;
import android.os.RemoteException;
+import android.util.Slog;
/**
* A class that contains biometric utilities. For authentication, see {@link BiometricPrompt}.
*/
public class BiometricManager {
+ private static final String TAG = "BiometricManager";
+
+ /**
+ * No error detected.
+ */
+ public static final int ERROR_NONE = BiometricConstants.BIOMETRIC_ERROR_NONE;
+
+ /**
+ * The hardware is unavailable. Try again later.
+ */
+ public static final int ERROR_UNAVAILABLE = BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE;
+
+ /**
+ * The user does not have any biometrics enrolled.
+ */
+ public static final int ERROR_NO_BIOMETRICS = BiometricConstants.BIOMETRIC_ERROR_NO_BIOMETRICS;
+
+ /**
+ * There is no biometric hardware.
+ */
+ public static final int ERROR_NO_HARDWARE = BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT;
+
+ @IntDef({ERROR_NONE, ERROR_UNAVAILABLE, ERROR_NO_BIOMETRICS, ERROR_NO_HARDWARE})
+ @interface BiometricError {}
+
private final Context mContext;
private final IBiometricService mService;
@@ -41,16 +69,42 @@
}
/**
- * Determine if there is at least one biometric enrolled.
+ * Determine if biometrics can be used. In other words, determine if {@link BiometricPrompt}
+ * can be expected to be shown (hardware available, templates enrolled, user-enabled).
*
- * @return true if at least one biometric is enrolled, false otherwise
+ * @return Returns {@link #ERROR_NO_BIOMETRICS} if the user does not have any enrolled, or
+ * {@link #ERROR_UNAVAILABLE} if none are currently supported/enabled. Returns
+ * {@link #ERROR_NONE} if a biometric can currently be used (enrolled and available).
*/
@RequiresPermission(USE_BIOMETRIC)
- public boolean hasEnrolledBiometrics() {
- try {
- return mService.hasEnrolledBiometrics(mContext.getOpPackageName());
- } catch (RemoteException e) {
- return false;
+ public @BiometricError int canAuthenticate() {
+ if (mService != null) {
+ try {
+ return mService.canAuthenticate(mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ } else {
+ Slog.w(TAG, "hasEnrolledBiometrics(): Service not connected");
+ return ERROR_UNAVAILABLE;
+ }
+ }
+
+ /**
+ * Listens for changes to biometric eligibility on keyguard from user settings.
+ * @param callback
+ * @hide
+ */
+ @RequiresPermission(USE_BIOMETRIC_INTERNAL)
+ public void registerEnabledOnKeyguardCallback(IBiometricEnabledOnKeyguardCallback callback) {
+ if (mService != null) {
+ try {
+ mService.registerEnabledOnKeyguardCallback(callback);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ } else {
+ Slog.w(TAG, "registerEnabledOnKeyguardCallback(): Service not connected");
}
}
}
diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java
index 92a814c..83998cc 100644
--- a/core/java/android/hardware/biometrics/BiometricPrompt.java
+++ b/core/java/android/hardware/biometrics/BiometricPrompt.java
@@ -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
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/events/StoppedDragingEvent.java b/core/java/android/hardware/biometrics/IBiometricEnabledOnKeyguardCallback.aidl
similarity index 62%
rename from packages/SystemUI/src/com/android/systemui/stackdivider/events/StoppedDragingEvent.java
rename to core/java/android/hardware/biometrics/IBiometricEnabledOnKeyguardCallback.aidl
index c50d6d6..d22e7e2 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/events/StoppedDragingEvent.java
+++ b/core/java/android/hardware/biometrics/IBiometricEnabledOnKeyguardCallback.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;
+package android.hardware.biometrics;
-import com.android.systemui.recents.events.EventBus;
+import android.hardware.biometrics.BiometricSourceType;
/**
- * Sent when the divider isn't draging anymore.
+ * @hide
*/
-public class StoppedDragingEvent extends EventBus.Event {
-}
+oneway interface IBiometricEnabledOnKeyguardCallback {
+ void onChanged(in BiometricSourceType type, boolean enabled);
+}
\ No newline at end of file
diff --git a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl b/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
index 67c9346..27d25b8 100644
--- a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
+++ b/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
@@ -15,9 +15,6 @@
*/
package android.hardware.biometrics;
-import android.os.Bundle;
-import android.os.UserHandle;
-
/**
* Communication channel from the BiometricPrompt (SysUI) back to AuthenticationClient.
* @hide
diff --git a/core/java/android/hardware/biometrics/IBiometricService.aidl b/core/java/android/hardware/biometrics/IBiometricService.aidl
index fd9d572..51e4ecb 100644
--- a/core/java/android/hardware/biometrics/IBiometricService.aidl
+++ b/core/java/android/hardware/biometrics/IBiometricService.aidl
@@ -17,6 +17,7 @@
package android.hardware.biometrics;
import android.os.Bundle;
+import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
import android.hardware.biometrics.IBiometricPromptReceiver;
import android.hardware.biometrics.IBiometricServiceReceiver;
@@ -37,6 +38,9 @@
// 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);
+ // Checks if biometrics can be used.
+ int canAuthenticate(String opPackageName);
+
+ // Register callback for when keyguard biometric eligibility changes.
+ void registerEnabledOnKeyguardCallback(IBiometricEnabledOnKeyguardCallback callback);
}
\ No newline at end of file
diff --git a/core/java/android/hardware/biometrics/IBiometricServiceReceiver.aidl b/core/java/android/hardware/biometrics/IBiometricServiceReceiver.aidl
index 71abdd2..a6e3696 100644
--- a/core/java/android/hardware/biometrics/IBiometricServiceReceiver.aidl
+++ b/core/java/android/hardware/biometrics/IBiometricServiceReceiver.aidl
@@ -15,10 +15,6 @@
*/
package android.hardware.biometrics;
-import android.hardware.biometrics.BiometricSourceType;
-import android.os.Bundle;
-import android.os.UserHandle;
-
/**
* Communication channel from the BiometricService back to BiometricPrompt.
* @hide
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index e8fb287..09113e5 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -297,6 +297,15 @@
*/
public static final int VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL = 1 << 8;
+ /**
+ * Virtual display flag: Indicates that the display should support system decorations. Virtual
+ * displays without this flag shouldn't show home, IME or any other system decorations.
+ *
+ * @see #createVirtualDisplay
+ * @hide
+ */
+ public static final int VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS = 1 << 9;
+
/** @hide */
public DisplayManager(Context context) {
mContext = context;
diff --git a/core/java/android/hardware/display/DisplayViewport.java b/core/java/android/hardware/display/DisplayViewport.java
index 496f34c..df0d46b 100644
--- a/core/java/android/hardware/display/DisplayViewport.java
+++ b/core/java/android/hardware/display/DisplayViewport.java
@@ -16,9 +16,14 @@
package android.hardware.display;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.annotation.IntDef;
import android.graphics.Rect;
import android.text.TextUtils;
+import java.lang.annotation.Retention;
+
/**
* Describes how the pixels of physical display device reflects the content of
* a logical display.
@@ -35,6 +40,10 @@
public static final int VIEWPORT_INTERNAL = 1;
public static final int VIEWPORT_EXTERNAL = 2;
public static final int VIEWPORT_VIRTUAL = 3;
+ @IntDef(prefix = { "VIEWPORT_" }, value = {
+ VIEWPORT_INTERNAL, VIEWPORT_EXTERNAL, VIEWPORT_VIRTUAL})
+ @Retention(SOURCE)
+ public @interface ViewportType {};
// True if this viewport is valid.
public boolean valid;
@@ -62,6 +71,8 @@
// The ID used to uniquely identify this display.
public String uniqueId;
+ public @ViewportType int type;
+
public void copyFrom(DisplayViewport viewport) {
valid = viewport.valid;
displayId = viewport.displayId;
@@ -71,6 +82,7 @@
deviceWidth = viewport.deviceWidth;
deviceHeight = viewport.deviceHeight;
uniqueId = viewport.uniqueId;
+ type = viewport.type;
}
/**
@@ -100,7 +112,8 @@
&& physicalFrame.equals(other.physicalFrame)
&& deviceWidth == other.deviceWidth
&& deviceHeight == other.deviceHeight
- && TextUtils.equals(uniqueId, other.uniqueId);
+ && TextUtils.equals(uniqueId, other.uniqueId)
+ && type == other.type;
}
@Override
@@ -115,13 +128,15 @@
result += prime * result + deviceWidth;
result += prime * result + deviceHeight;
result += prime * result + uniqueId.hashCode();
+ result += prime * result + type;
return result;
}
// For debugging purposes.
@Override
public String toString() {
- return "DisplayViewport{valid=" + valid
+ return "DisplayViewport{type=" + typeToString(type)
+ + ", valid=" + valid
+ ", displayId=" + displayId
+ ", uniqueId='" + uniqueId + "'"
+ ", orientation=" + orientation
@@ -131,4 +146,20 @@
+ ", deviceHeight=" + deviceHeight
+ "}";
}
+
+ /**
+ * Human-readable viewport type.
+ */
+ public static String typeToString(@ViewportType int viewportType) {
+ switch (viewportType) {
+ case VIEWPORT_INTERNAL:
+ return "INTERNAL";
+ case VIEWPORT_EXTERNAL:
+ return "EXTERNAL";
+ case VIEWPORT_VIRTUAL:
+ return "VIRTUAL";
+ default:
+ return "UNKNOWN (" + viewportType + ")";
+ }
+ }
}
diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java
index 66613ea..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();
}
diff --git a/core/java/android/hardware/face/IFaceService.aidl b/core/java/android/hardware/face/IFaceService.aidl
index 50d0744..6681bd7 100644
--- a/core/java/android/hardware/face/IFaceService.aidl
+++ b/core/java/android/hardware/face/IFaceService.aidl
@@ -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/face/IFaceServiceReceiver.aidl b/core/java/android/hardware/face/IFaceServiceReceiver.aidl
index 16fb690..b88574b 100644
--- a/core/java/android/hardware/face/IFaceServiceReceiver.aidl
+++ b/core/java/android/hardware/face/IFaceServiceReceiver.aidl
@@ -16,8 +16,6 @@
package android.hardware.face;
import android.hardware.face.Face;
-import android.os.Bundle;
-import android.os.UserHandle;
/**
* Communication channel from the FaceService back to FaceAuthenticationManager.
diff --git a/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl b/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl
index 370383f..cf1c94e 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl
@@ -16,8 +16,6 @@
package android.hardware.fingerprint;
import android.hardware.fingerprint.Fingerprint;
-import android.os.Bundle;
-import android.os.UserHandle;
/**
* Communication channel from the FingerprintService back to FingerprintManager.
diff --git a/core/java/android/hardware/input/InputManagerInternal.java b/core/java/android/hardware/input/InputManagerInternal.java
index c4d7e40..d8da548 100644
--- a/core/java/android/hardware/input/InputManagerInternal.java
+++ b/core/java/android/hardware/input/InputManagerInternal.java
@@ -40,8 +40,7 @@
* Called by the display manager to set information about the displays as needed
* by the input system. The input system must copy this information to retain it.
*/
- public abstract void setDisplayViewports(DisplayViewport defaultViewport,
- DisplayViewport externalTouchViewport, List<DisplayViewport> virtualTouchViewports);
+ public abstract void setDisplayViewports(List<DisplayViewport> viewports);
/**
* Called by the power manager to tell the input manager whether it should start
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 ae12f93..097a3e3 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -2095,7 +2095,7 @@
* Called when the application has reported a new location of its text
* cursor. This is only called if explicitly requested by the input method.
* The default implementation does nothing.
- * @deprecated Use {#link onUpdateCursorAnchorInfo(CursorAnchorInfo)} instead.
+ * @deprecated Use {@link #onUpdateCursorAnchorInfo(CursorAnchorInfo)} instead.
*/
@Deprecated
public void onUpdateCursor(Rect newCursor) {
@@ -2162,7 +2162,7 @@
}
/**
- * @return {#link ExtractEditText} if it is considered to be visible and active. Otherwise
+ * @return {@link ExtractEditText} if it is considered to be visible and active. Otherwise
* {@code null} is returned.
*/
private ExtractEditText getExtractEditTextIfVisible() {
@@ -2803,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;
}
/**
diff --git a/core/java/android/inputmethodservice/SoftInputWindow.java b/core/java/android/inputmethodservice/SoftInputWindow.java
index b4b8887..0513fee 100644
--- a/core/java/android/inputmethodservice/SoftInputWindow.java
+++ b/core/java/android/inputmethodservice/SoftInputWindow.java
@@ -162,9 +162,9 @@
/**
* Set which boundary of the screen the DockWindow sticks to.
*
- * @param gravity The boundary of the screen to stick. See {#link
- * android.view.Gravity.LEFT}, {#link android.view.Gravity.TOP},
- * {#link android.view.Gravity.BOTTOM}, {#link
+ * @param gravity The boundary of the screen to stick. See {@link
+ * android.view.Gravity.LEFT}, {@link android.view.Gravity.TOP},
+ * {@link android.view.Gravity.BOTTOM}, {@link
* android.view.Gravity.RIGHT}.
*/
public void setGravity(int gravity) {
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index f2e9078..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;
@@ -3801,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;
}
@@ -3819,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);
}
diff --git a/core/java/android/net/NetworkMisc.java b/core/java/android/net/NetworkMisc.java
index 69f50a2..a2da6ea 100644
--- a/core/java/android/net/NetworkMisc.java
+++ b/core/java/android/net/NetworkMisc.java
@@ -46,7 +46,7 @@
/**
* Set if the user desires to use this network even if it is unvalidated. This field has meaning
- * only if {#link explicitlySelected} is true. If it is, this field must also be set to the
+ * only if {@link explicitlySelected} is true. If it is, this field must also be set to the
* appropriate value based on previous user choice.
*/
public boolean acceptUnvalidated;
diff --git a/core/java/android/net/UrlQuerySanitizer.java b/core/java/android/net/UrlQuerySanitizer.java
index d2073b4..5b67406 100644
--- a/core/java/android/net/UrlQuerySanitizer.java
+++ b/core/java/android/net/UrlQuerySanitizer.java
@@ -287,7 +287,7 @@
/**
* Sanitize a value.
* <ol>
- * <li>If script URLs are not OK, the will be removed.
+ * <li>If script URLs are not OK, they will be removed.
* <li>If neither spaces nor other white space is OK, then
* white space will be trimmed from the beginning and end of
* the URL. (Just the actual white space characters are trimmed, not
@@ -563,7 +563,7 @@
}
/**
- * Constructs a UrlQuerySanitizer and parse a URL.
+ * Constructs a UrlQuerySanitizer and parses a URL.
* This constructor is provided for convenience when the
* default parsing behavior is acceptable.
* <p>
@@ -644,7 +644,7 @@
}
/**
- * An array list of all of the parameter value pairs in the sanitized
+ * An array list of all of the parameter-value pairs in the sanitized
* query, in the order they appeared in the query. May contain duplicate
* parameters.
* <p class="note"><b>Note:</b> Do not modify this list. Treat it as a read-only list.</p>
@@ -656,7 +656,7 @@
/**
* Check if a parameter exists in the current sanitized query.
* @param parameter the unencoded name of a parameter.
- * @return true if the paramater exists in the current sanitized queary.
+ * @return true if the parameter exists in the current sanitized queary.
*/
public boolean hasParameter(String parameter) {
return mEntries.containsKey(parameter);
@@ -766,7 +766,7 @@
* the value. If all goes well then addSanitizedValue is called with
* the unescaped parameter and the sanitized unescaped value.
* @param parameter an escaped parameter
- * @param value an unsanitzied escaped value
+ * @param value an unsanitized escaped value
*/
protected void parseEntry(String parameter, String value) {
String unescapedParameter = unescape(parameter);
@@ -812,7 +812,7 @@
/**
* Get the effective value sanitizer for a parameter. Like getValueSanitizer,
* except if there is no value sanitizer registered for a parameter, and
- * unregistered paramaters are allowed, then the default value sanitizer is
+ * unregistered parameters are allowed, then the default value sanitizer is
* returned.
* @param parameter an unescaped parameter
* @return the effective value sanitizer for a parameter.
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/Environment.java b/core/java/android/os/Environment.java
index 3c43fd18..483b764 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -219,12 +219,11 @@
* services to store files relating to the user. This directory will be
* automatically deleted when the user is removed.
*
- * @deprecated This directory is valid and still exists, but callers should
- * <em>strongly</em> consider switching to
- * {@link #getDataSystemCeDirectory(int)} which is protected
- * with user credentials or
- * {@link #getDataSystemDeDirectory(int)} which supports fast
- * user wipe.
+ * @deprecated This directory is valid and still exists, but but callers
+ * should <em>strongly</em> consider switching to using either
+ * {@link #getDataSystemCeDirectory(int)} or
+ * {@link #getDataSystemDeDirectory(int)}, both of which support
+ * fast user wipe.
* @hide
*/
@Deprecated
@@ -292,12 +291,42 @@
return buildPath(getDataDirectory(), "system_ce");
}
- /** {@hide} */
+ /**
+ * Return the "credential encrypted" system directory for a user. This is
+ * for use by system services to store files relating to the user. This
+ * directory supports fast user wipe, and will be automatically deleted when
+ * the user is removed.
+ * <p>
+ * Data stored under this path is "credential encrypted", which uses an
+ * encryption key that is entangled with user credentials, such as a PIN or
+ * password. The contents will only be available once the user has been
+ * unlocked, as reported by {@code SystemService.onUnlockUser()}.
+ * <p>
+ * New code should <em>strongly</em> prefer storing sensitive data in these
+ * credential encrypted areas.
+ *
+ * @hide
+ */
public static File getDataSystemCeDirectory(int userId) {
return buildPath(getDataDirectory(), "system_ce", String.valueOf(userId));
}
- /** {@hide} */
+ /**
+ * Return the "device encrypted" system directory for a user. This is for
+ * use by system services to store files relating to the user. This
+ * directory supports fast user wipe, and will be automatically deleted when
+ * the user is removed.
+ * <p>
+ * Data stored under this path is "device encrypted", which uses an
+ * encryption key that is tied to the physical device. The contents will
+ * only be available once the device has finished a {@code dm-verity}
+ * protected boot.
+ * <p>
+ * New code should <em>strongly</em> avoid storing sensitive data in these
+ * device encrypted areas.
+ *
+ * @hide
+ */
public static File getDataSystemDeDirectory(int userId) {
return buildPath(getDataDirectory(), "system_de", String.valueOf(userId));
}
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/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 6fab3c4..0f64c45 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -483,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.
@@ -501,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 */
@@ -519,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/StatsLogEventWrapper.java b/core/java/android/os/StatsLogEventWrapper.java
index 051ab75..72e1ab9 100644
--- a/core/java/android/os/StatsLogEventWrapper.java
+++ b/core/java/android/os/StatsLogEventWrapper.java
@@ -70,11 +70,17 @@
}
};
+ /**
+ * Write a int value.
+ */
public void writeInt(int val) {
mTypes.add(EVENT_TYPE_INT);
mValues.add(val);
}
+ /**
+ * Write a long value.
+ */
public void writeLong(long val) {
mTypes.add(EVENT_TYPE_LONG);
mValues.add(val);
@@ -89,12 +95,23 @@
mValues.add(val == null ? "" : val);
}
+ /**
+ * Write a float value.
+ */
public void writeFloat(float val) {
mTypes.add(EVENT_TYPE_FLOAT);
mValues.add(val);
}
/**
+ * Write a double value.
+ */
+ public void writeDouble(double val) {
+ mTypes.add(EVENT_TYPE_DOUBLE);
+ mValues.add(val);
+ }
+
+ /**
* Write a storage value.
*/
public void writeStorage(byte[] val) {
@@ -102,6 +119,9 @@
mValues.add(val);
}
+ /**
+ * Write a boolean value.
+ */
public void writeBoolean(boolean val) {
mTypes.add(EVENT_TYPE_INT);
mValues.add(val ? 1 : 0);
@@ -134,6 +154,9 @@
case EVENT_TYPE_FLOAT:
out.writeFloat((float) mValues.get(i));
break;
+ case EVENT_TYPE_DOUBLE:
+ out.writeDouble((double) mValues.get(i));
+ break;
case EVENT_TYPE_STRING:
out.writeString((String) mValues.get(i));
break;
diff --git a/core/java/android/os/SystemProperties.java b/core/java/android/os/SystemProperties.java
index fb34a52..bdc776d 100644
--- a/core/java/android/os/SystemProperties.java
+++ b/core/java/android/os/SystemProperties.java
@@ -208,13 +208,18 @@
return;
}
ArrayList<Runnable> callbacks = new ArrayList<Runnable>(sChangeCallbacks);
- for (int i=0; i<callbacks.size(); i++) {
- try {
- callbacks.get(i).run();
- } catch (Throwable t) {
- Log.wtf(TAG, "Exception in SystemProperties change callback", t);
- // Ignore and try to go on.
+ final long token = Binder.clearCallingIdentity();
+ try {
+ for (int i = 0; i < callbacks.size(); i++) {
+ try {
+ callbacks.get(i).run();
+ } catch (Throwable t) {
+ Log.wtf(TAG, "Exception in SystemProperties change callback", t);
+ // Ignore and try to go on.
+ }
}
+ } finally {
+ Binder.restoreCallingIdentity(token);
}
}
}
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/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index f126c49..112329e 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -2783,48 +2783,7 @@
* The content:// style URI for this table, which requests a directory of
* raw contact rows matching the selection criteria.
*/
- public static final Uri CONTENT_URI =
- Uri.withAppendedPath(AUTHORITY_URI, "raw_contacts");
-
- /**
- * The URI to register for all raw contacts change notification.
- *
- * @hide
- */
- @SystemApi
- @TestApi
- public static final Uri RAW_CONTACTS_NOTIFICATION_URI =
- Uri.parse("content://com.android.contacts.raw_contacts");
-
- /**
- * The URI to register for raw contacts insert notification.
- *
- * @hide
- */
- @SystemApi
- @TestApi
- public static final Uri RAW_CONTACTS_NOTIFICATION_INSERT_URI =
- Uri.withAppendedPath(RAW_CONTACTS_NOTIFICATION_URI, "insert");
-
- /**
- * The URI to register for raw contacts update notification.
- *
- * @hide
- */
- @SystemApi
- @TestApi
- public static final Uri RAW_CONTACTS_NOTIFICATION_UPDATE_URI =
- Uri.withAppendedPath(RAW_CONTACTS_NOTIFICATION_URI, "update");
-
- /**
- * The URI to register for raw contacts delete notification.
- *
- * @hide
- */
- @SystemApi
- @TestApi
- public static final Uri RAW_CONTACTS_NOTIFICATION_DELETE_URI =
- Uri.withAppendedPath(RAW_CONTACTS_NOTIFICATION_URI, "delete");
+ public static final Uri CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI, "raw_contacts");
/**
* The MIME type of the results from {@link #CONTENT_URI} when a specific
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..3f264b7 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -497,6 +497,16 @@
"android.settings.BLUETOOTH_SETTINGS";
/**
+ * Activity action: Show Settings app search UI when this action is available for device.
+ * <p>
+ * Input: Nothing.
+ * <p>
+ * Output: Nothing.
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_APP_SEARCH_SETTINGS = "android.settings.APP_SEARCH_SETTINGS";
+
+ /**
* Activity Action: Show settings to allow configuration of Assist Gesture.
* <p>
* In some cases, a matching Activity may not exist, so ensure you
@@ -7267,12 +7277,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 +7699,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 +8203,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 +8351,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);
@@ -9242,6 +9263,19 @@
public static final String MOBILE_DATA_ALWAYS_ON = "mobile_data_always_on";
/**
+ * Whether the wifi data connection should remain active even when higher
+ * priority networks like Ethernet are active, to keep both networks.
+ * In the case where higher priority networks are connected, wifi will be
+ * unused unless an application explicitly requests to use it.
+ *
+ * See ConnectivityService for more info.
+ *
+ * (0 = disabled, 1 = enabled)
+ * @hide
+ */
+ public static final String WIFI_ALWAYS_REQUESTED = "wifi_always_requested";
+
+ /**
* Size of the event buffer for IP connectivity metrics.
* @hide
*/
@@ -9593,8 +9627,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 +10844,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 +11609,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/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/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 c4ef77a..4608e20 100644
--- a/core/java/android/transition/TransitionManager.java
+++ b/core/java/android/transition/TransitionManager.java
@@ -188,8 +188,12 @@
final ViewGroup sceneRoot = scene.getSceneRoot();
if (!sPendingTransitions.contains(sceneRoot)) {
+ Scene oldScene = Scene.getCurrentScene(sceneRoot);
if (transition == null) {
- exitPreviousScene(sceneRoot);
+ // Notify old scene that it is being exited
+ if (oldScene != null) {
+ oldScene.exit();
+ }
scene.enter();
} else {
@@ -198,7 +202,6 @@
Transition transitionClone = transition.clone();
transitionClone.setSceneRoot(sceneRoot);
- Scene oldScene = Scene.getCurrentScene(sceneRoot);
if (oldScene != null && oldScene.isCreatedFromLayoutResource()) {
transitionClone.setCanRemoveViews(true);
}
@@ -212,14 +215,6 @@
}
}
- private static void exitPreviousScene(final ViewGroup sceneRoot) {
- // Notify previous scene that it is being exited
- final Scene previousScene = Scene.getCurrentScene(sceneRoot);
- if (previousScene != null) {
- previousScene.exit();
- }
- }
-
@UnsupportedAppUsage
private static ArrayMap<ViewGroup, ArrayList<Transition>> getRunningTransitions() {
WeakReference<ArrayMap<ViewGroup, ArrayList<Transition>>> runningTransitions =
@@ -349,7 +344,11 @@
transition.captureValues(sceneRoot, true);
}
- exitPreviousScene(sceneRoot);
+ // Notify previous scene that it is being exited
+ Scene previousScene = Scene.getCurrentScene(sceneRoot);
+ if (previousScene != null) {
+ previousScene.exit();
+ }
}
/**
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/JsonReader.java b/core/java/android/util/JsonReader.java
index 7d1c6c4..50f63f8 100644
--- a/core/java/android/util/JsonReader.java
+++ b/core/java/android/util/JsonReader.java
@@ -16,13 +16,15 @@
package android.util;
+import libcore.internal.StringPool;
+
import java.io.Closeable;
import java.io.EOFException;
import java.io.IOException;
import java.io.Reader;
import java.util.ArrayList;
import java.util.List;
-import libcore.internal.StringPool;
+
/**
* Reads a JSON (<a href="http://www.ietf.org/rfc/rfc4627.txt">RFC 4627</a>)
@@ -295,7 +297,7 @@
/**
* Consumes the next token from the JSON stream and asserts that it is the
- * end of the current array.
+ * end of the current object.
*/
public void endObject() throws IOException {
expect(JsonToken.END_OBJECT);
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 1203541..1bbef8e9 100644
--- a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
+++ b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
@@ -410,7 +410,7 @@
NoSuchAlgorithmException {
try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) {
SignatureInfo signatureInfo = findSignature(apk);
- return ApkVerityBuilder.generateApkVerity(apkPath, bufferFactory, signatureInfo);
+ return VerityBuilder.generateApkVerity(apkPath, bufferFactory, signatureInfo);
}
}
@@ -423,7 +423,7 @@
if (vSigner.verityRootHash == null) {
return null;
}
- return ApkVerityBuilder.generateApkVerityRootHash(
+ 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 939522d..1471870 100644
--- a/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java
+++ b/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java
@@ -534,7 +534,7 @@
NoSuchAlgorithmException {
try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) {
SignatureInfo signatureInfo = findSignature(apk);
- return ApkVerityBuilder.generateApkVerity(apkPath, bufferFactory, signatureInfo);
+ return VerityBuilder.generateApkVerity(apkPath, bufferFactory, signatureInfo);
}
}
@@ -547,7 +547,7 @@
if (vSigner.verityRootHash == null) {
return null;
}
- return ApkVerityBuilder.generateApkVerityRootHash(
+ 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 a299b11..ac4ea75 100644
--- a/core/java/android/util/apk/ApkSignatureVerifier.java
+++ b/core/java/android/util/apk/ApkSignatureVerifier.java
@@ -81,19 +81,17 @@
Certificate[][] signerCerts = new Certificate[][] { vSigner.certs };
Signature[] signerSigs = convertToSignatures(signerCerts);
Signature[] pastSignerSigs = null;
- int[] pastSignerSigsFlags = null;
if (vSigner.por != null) {
// populate proof-of-rotation information
pastSignerSigs = new Signature[vSigner.por.certs.size()];
- pastSignerSigsFlags = new int[vSigner.por.flagsList.size()];
for (int i = 0; i < pastSignerSigs.length; i++) {
pastSignerSigs[i] = new Signature(vSigner.por.certs.get(i).getEncoded());
- pastSignerSigsFlags[i] = vSigner.por.flagsList.get(i);
+ pastSignerSigs[i].setFlags(vSigner.por.flagsList.get(i));
}
}
return new PackageParser.SigningDetails(
signerSigs, SignatureSchemeVersion.SIGNING_BLOCK_V3,
- pastSignerSigs, pastSignerSigsFlags);
+ pastSignerSigs);
} catch (SignatureNotFoundException e) {
// not signed with v3, try older if allowed
if (minSignatureSchemeVersion >= SignatureSchemeVersion.SIGNING_BLOCK_V3) {
@@ -323,19 +321,17 @@
Certificate[][] signerCerts = new Certificate[][] { vSigner.certs };
Signature[] signerSigs = convertToSignatures(signerCerts);
Signature[] pastSignerSigs = null;
- int[] pastSignerSigsFlags = null;
if (vSigner.por != null) {
// populate proof-of-rotation information
pastSignerSigs = new Signature[vSigner.por.certs.size()];
- pastSignerSigsFlags = new int[vSigner.por.flagsList.size()];
for (int i = 0; i < pastSignerSigs.length; i++) {
pastSignerSigs[i] = new Signature(vSigner.por.certs.get(i).getEncoded());
- pastSignerSigsFlags[i] = vSigner.por.flagsList.get(i);
+ pastSignerSigs[i].setFlags(vSigner.por.flagsList.get(i));
}
}
return new PackageParser.SigningDetails(
signerSigs, SignatureSchemeVersion.SIGNING_BLOCK_V3,
- pastSignerSigs, pastSignerSigsFlags);
+ pastSignerSigs);
} catch (SignatureNotFoundException e) {
// not signed with v3, try older if allowed
if (minSignatureSchemeVersion >= SignatureSchemeVersion.SIGNING_BLOCK_V3) {
diff --git a/core/java/android/util/apk/ApkSigningBlockUtils.java b/core/java/android/util/apk/ApkSigningBlockUtils.java
index 081033a..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.generateApkVerityTree(apk,
+ VerityBuilder.VerityResult verity = VerityBuilder.generateApkVerityTree(apk,
signatureInfo, new ByteBufferFactory() {
@Override
public ByteBuffer create(int capacity) {
diff --git a/core/java/android/util/apk/ApkVerityBuilder.java b/core/java/android/util/apk/VerityBuilder.java
similarity index 93%
rename from core/java/android/util/apk/ApkVerityBuilder.java
rename to core/java/android/util/apk/VerityBuilder.java
index edd09f8..443bbd8 100644
--- a/core/java/android/util/apk/ApkVerityBuilder.java
+++ b/core/java/android/util/apk/VerityBuilder.java
@@ -29,19 +29,18 @@
import java.util.ArrayList;
/**
- * ApkVerityBuilder builds the APK verity tree and the verity header. The generated tree format can
- * be stored on disk for apk-verity setup and used by kernel. Note that since the current
- * implementation is different from the upstream, we call this implementation apk-verity instead of
- * fs-verity.
+ * 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
*/
-public 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
@@ -52,7 +51,7 @@
private static final byte[] DEFAULT_SALT = new byte[8];
/** Result generated by the builder. */
- public static class ApkVerityResult {
+ public static class VerityResult {
/** Raw fs-verity metadata and Merkle tree ready to be deployed on disk. */
public final ByteBuffer verityData;
@@ -62,7 +61,7 @@
/** Root hash of the Merkle tree. */
public final byte[] rootHash;
- private ApkVerityResult(ByteBuffer verityData, int merkleTreeSize, byte[] rootHash) {
+ private VerityResult(ByteBuffer verityData, int merkleTreeSize, byte[] rootHash) {
this.verityData = verityData;
this.merkleTreeSize = merkleTreeSize;
this.rootHash = rootHash;
@@ -74,14 +73,14 @@
* 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 a buffer with the generated Merkle tree stored at the
+ * @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 ApkVerityResult generateFsVerityTree(@NonNull RandomAccessFile apk,
+ public static VerityResult generateFsVerityTree(@NonNull RandomAccessFile apk,
@NonNull ByteBufferFactory bufferFactory)
throws IOException, SecurityException, NoSuchAlgorithmException, DigestException {
- return generateVerityTree(apk, bufferFactory, null /* signatureInfo */,
+ return generateVerityTreeInternal(apk, bufferFactory, null /* signatureInfo */,
false /* skipSigningBlock */);
}
@@ -91,18 +90,19 @@
* 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 ApkVerityResult containing a buffer with the generated Merkle tree stored at the
+ * @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 ApkVerityResult generateApkVerityTree(@NonNull RandomAccessFile apk,
+ public static VerityResult generateApkVerityTree(@NonNull RandomAccessFile apk,
@Nullable SignatureInfo signatureInfo, @NonNull ByteBufferFactory bufferFactory)
throws IOException, SecurityException, NoSuchAlgorithmException, DigestException {
- return generateVerityTree(apk, bufferFactory, signatureInfo, true /* skipSigningBlock */);
+ return generateVerityTreeInternal(apk, bufferFactory, signatureInfo,
+ true /* skipSigningBlock */);
}
@NonNull
- private static ApkVerityResult generateVerityTree(@NonNull RandomAccessFile apk,
+ private static VerityResult generateVerityTreeInternal(@NonNull RandomAccessFile apk,
@NonNull ByteBufferFactory bufferFactory, @Nullable SignatureInfo signatureInfo,
boolean skipSigningBlock)
throws IOException, SecurityException, NoSuchAlgorithmException, DigestException {
@@ -124,7 +124,7 @@
byte[] salt = skipSigningBlock ? DEFAULT_SALT : null;
byte[] apkRootHash = generateVerityTreeInternal(apk, signatureInfo, salt, levelOffset,
tree, skipSigningBlock);
- return new ApkVerityResult(output, merkleTreeSize, apkRootHash);
+ return new VerityResult(output, merkleTreeSize, apkRootHash);
}
static void generateApkVerityFooter(@NonNull RandomAccessFile apk,
@@ -173,7 +173,7 @@
throws IOException, SignatureNotFoundException, SecurityException, DigestException,
NoSuchAlgorithmException {
try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) {
- ApkVerityResult result = generateVerityTree(apk, bufferFactory, signatureInfo,
+ VerityResult result = generateVerityTreeInternal(apk, bufferFactory, signatureInfo,
true /* skipSigningBlock */);
ByteBuffer footer = slice(result.verityData, result.merkleTreeSize,
result.verityData.limit());
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 4d96fc3..719a401 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -221,6 +221,18 @@
public static final int FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD = 1 << 5;
/**
+ * Display flag: Indicates that the display should show system decorations.
+ * <p>
+ * This flag identifies secondary displays that should show system decorations, such as status
+ * bar, navigation bar, home activity or IME.
+ * </p>
+ *
+ * @see #supportsSystemDecorations
+ * @hide
+ */
+ public static final int FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS = 1 << 6;
+
+ /**
* Display flag: Indicates that the contents of the display should not be scaled
* to fit the physical screen dimensions. Used for development only to emulate
* devices with smaller physicals screens while preserving density.
@@ -874,6 +886,16 @@
}
/**
+ * Returns whether this display should support showing system decorations.
+ *
+ * @see #FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS
+ * @hide
+ */
+ public boolean supportsSystemDecorations() {
+ return (mDisplayInfo.flags & FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS) != 0;
+ }
+
+ /**
* Returns the display's HDR capabilities.
*
* @see #isHdr()
diff --git a/core/java/android/view/DisplayCutout.java b/core/java/android/view/DisplayCutout.java
index 5f80d31..f428c79 100644
--- a/core/java/android/view/DisplayCutout.java
+++ b/core/java/android/view/DisplayCutout.java
@@ -18,12 +18,19 @@
import static android.util.DisplayMetrics.DENSITY_DEFAULT;
import static android.util.DisplayMetrics.DENSITY_DEVICE_STABLE;
-import static android.view.DisplayCutoutProto.BOUNDS;
+import static android.view.DisplayCutoutProto.BOUND_BOTTOM;
+import static android.view.DisplayCutoutProto.BOUND_LEFT;
+import static android.view.DisplayCutoutProto.BOUND_RIGHT;
+import static android.view.DisplayCutoutProto.BOUND_TOP;
import static android.view.DisplayCutoutProto.INSETS;
import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.res.Resources;
+import android.graphics.Insets;
import android.graphics.Matrix;
import android.graphics.Path;
import android.graphics.Rect;
@@ -42,7 +49,10 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
/**
@@ -68,14 +78,14 @@
"com.android.internal.display_cutout_emulation";
private static final Rect ZERO_RECT = new Rect();
- private static final Region EMPTY_REGION = new Region();
/**
* An instance where {@link #isEmpty()} returns {@code true}.
*
* @hide
*/
- public static final DisplayCutout NO_CUTOUT = new DisplayCutout(ZERO_RECT, EMPTY_REGION,
+ public static final DisplayCutout NO_CUTOUT = new DisplayCutout(
+ ZERO_RECT, ZERO_RECT, ZERO_RECT, ZERO_RECT, ZERO_RECT,
false /* copyArguments */);
@@ -94,7 +104,152 @@
private static Pair<Path, DisplayCutout> sCachedCutout = NULL_PAIR;
private final Rect mSafeInsets;
- private final Region mBounds;
+
+
+ /**
+ * The bound is at the left of the screen.
+ * @hide
+ */
+ public static final int BOUNDS_POSITION_LEFT = 0;
+
+ /**
+ * The bound is at the top of the screen.
+ * @hide
+ */
+ public static final int BOUNDS_POSITION_TOP = 1;
+
+ /**
+ * The bound is at the right of the screen.
+ * @hide
+ */
+ public static final int BOUNDS_POSITION_RIGHT = 2;
+
+ /**
+ * The bound is at the bottom of the screen.
+ * @hide
+ */
+ public static final int BOUNDS_POSITION_BOTTOM = 3;
+
+ /**
+ * The number of possible positions at which bounds can be located.
+ * @hide
+ */
+ public static final int BOUNDS_POSITION_LENGTH = 4;
+
+ /** @hide */
+ @IntDef(prefix = { "BOUNDS_POSITION_" }, value = {
+ BOUNDS_POSITION_LEFT,
+ BOUNDS_POSITION_TOP,
+ BOUNDS_POSITION_RIGHT,
+ BOUNDS_POSITION_BOTTOM
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface BoundsPosition {}
+
+ private static class Bounds {
+ private final Rect[] mRects;
+
+ private Bounds(Rect left, Rect top, Rect right, Rect bottom, boolean copyArguments) {
+ mRects = new Rect[BOUNDS_POSITION_LENGTH];
+ mRects[BOUNDS_POSITION_LEFT] = getCopyOrRef(left, copyArguments);
+ mRects[BOUNDS_POSITION_TOP] = getCopyOrRef(top, copyArguments);
+ mRects[BOUNDS_POSITION_RIGHT] = getCopyOrRef(right, copyArguments);
+ mRects[BOUNDS_POSITION_BOTTOM] = getCopyOrRef(bottom, copyArguments);
+
+ }
+
+ private Bounds(Rect[] rects, boolean copyArguments) {
+ if (rects.length != BOUNDS_POSITION_LENGTH) {
+ throw new IllegalArgumentException(
+ "rects must have exactly 4 elements: rects=" + Arrays.toString(rects));
+ }
+ if (copyArguments) {
+ mRects = new Rect[BOUNDS_POSITION_LENGTH];
+ for (int i = 0; i < BOUNDS_POSITION_LENGTH; ++i) {
+ mRects[i] = new Rect(rects[i]);
+ }
+ } else {
+ for (Rect rect : rects) {
+ if (rect == null) {
+ throw new IllegalArgumentException(
+ "rects must have non-null elements: rects="
+ + Arrays.toString(rects));
+ }
+ }
+ mRects = rects;
+ }
+ }
+
+ private boolean isEmpty() {
+ for (Rect rect : mRects) {
+ if (!rect.isEmpty()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private Rect getRect(@BoundsPosition int pos) {
+ return new Rect(mRects[pos]);
+ }
+
+ private Rect[] getRects() {
+ Rect[] rects = new Rect[BOUNDS_POSITION_LENGTH];
+ for (int i = 0; i < BOUNDS_POSITION_LENGTH; ++i) {
+ rects[i] = new Rect(mRects[i]);
+ }
+ return rects;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 0;
+ for (Rect rect : mRects) {
+ result = result * 48271 + rect.hashCode();
+ }
+ return result;
+ }
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) {
+ return true;
+ }
+ if (o instanceof Bounds) {
+ Bounds b = (Bounds) o;
+ return Arrays.deepEquals(mRects, b.mRects);
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return "Bounds=" + Arrays.toString(mRects);
+ }
+
+ }
+
+ private final Bounds mBounds;
+
+ /**
+ * Creates a DisplayCutout instance.
+ *
+ * @param safeInsets the insets from each edge which avoid the display cutout as returned by
+ * {@link #getSafeInsetTop()} etc.
+ * @param boundLeft the left bounding rect of the display cutout in pixels. If null is passed,
+ * it's treated as an empty rectangle (0,0)-(0,0).
+ * @param boundTop the top bounding rect of the display cutout in pixels. If null is passed,
+ * it's treated as an empty rectangle (0,0)-(0,0).
+ * @param boundRight the right bounding rect of the display cutout in pixels. If null is
+ * passed, it's treated as an empty rectangle (0,0)-(0,0).
+ * @param boundBottom the bottom bounding rect of the display cutout in pixels. If null is
+ * passed, it's treated as an empty rectangle (0,0)-(0,0).
+ */
+ // TODO(b/73953958): @VisibleForTesting(visibility = PRIVATE)
+ public DisplayCutout(
+ Insets safeInsets, @Nullable Rect boundLeft, @Nullable Rect boundTop,
+ @Nullable Rect boundRight, @Nullable Rect boundBottom) {
+ this(safeInsets.toRect(), boundLeft, boundTop, boundRight, boundBottom, true);
+ }
/**
* Creates a DisplayCutout instance.
@@ -103,25 +258,85 @@
* {@link #getSafeInsetTop()} etc.
* @param boundingRects the bounding rects of the display cutouts as returned by
* {@link #getBoundingRects()} ()}.
+ * @deprecated Use {@link DisplayCutout#DisplayCutout(Insets, Rect, Rect, Rect, Rect)} instead.
*/
// TODO(b/73953958): @VisibleForTesting(visibility = PRIVATE)
+ @Deprecated
public DisplayCutout(Rect safeInsets, List<Rect> boundingRects) {
- this(safeInsets != null ? new Rect(safeInsets) : ZERO_RECT,
- boundingRectsToRegion(boundingRects),
+ this(safeInsets, extractBoundsFromList(safeInsets, boundingRects),
true /* copyArguments */);
}
/**
* Creates a DisplayCutout instance.
*
+ * @param safeInsets the insets from each edge which avoid the display cutout as returned by
+ * {@link #getSafeInsetTop()} etc.
* @param copyArguments if true, create a copy of the arguments. If false, the passed arguments
* are not copied and MUST remain unchanged forever.
*/
- private DisplayCutout(Rect safeInsets, Region bounds, boolean copyArguments) {
- mSafeInsets = safeInsets == null ? ZERO_RECT :
- (copyArguments ? new Rect(safeInsets) : safeInsets);
- mBounds = bounds == null ? Region.obtain() :
- (copyArguments ? Region.obtain(bounds) : bounds);
+ private DisplayCutout(Rect safeInsets, Rect boundLeft, Rect boundTop, Rect boundRight,
+ Rect boundBottom, boolean copyArguments) {
+ mSafeInsets = getCopyOrRef(safeInsets, copyArguments);
+ mBounds = new Bounds(boundLeft, boundTop, boundRight, boundBottom, copyArguments);
+ }
+
+ private DisplayCutout(Rect safeInsets, Rect[] bounds, boolean copyArguments) {
+ mSafeInsets = getCopyOrRef(safeInsets, copyArguments);
+ mBounds = new Bounds(bounds, copyArguments);
+ }
+
+ private DisplayCutout(Rect safeInsets, Bounds bounds) {
+ mSafeInsets = safeInsets;
+ mBounds = bounds;
+
+ }
+
+ private static Rect getCopyOrRef(Rect r, boolean copyArguments) {
+ if (r == null) {
+ return ZERO_RECT;
+ } else if (copyArguments) {
+ return new Rect(r);
+ } else {
+ return r;
+ }
+ }
+
+ /**
+ * Find the position of the bounding rect, and create an array of Rect whose index represents
+ * the position (= BoundsPosition).
+ *
+ * @hide
+ */
+ public static Rect[] extractBoundsFromList(Rect safeInsets, List<Rect> boundingRects) {
+ Rect[] sortedBounds = new Rect[BOUNDS_POSITION_LENGTH];
+ for (int i = 0; i < sortedBounds.length; ++i) {
+ sortedBounds[i] = ZERO_RECT;
+ }
+ for (Rect bound : boundingRects) {
+ // There will be at most one non-functional area per short edge of the device, and none
+ // on the long edges, so either safeInsets.right or safeInsets.bottom must be 0.
+ // TODO(b/117199965): Refine the logic to handle edge cases.
+ if (bound.left == 0) {
+ sortedBounds[BOUNDS_POSITION_LEFT] = bound;
+ } else if (bound.top == 0) {
+ sortedBounds[BOUNDS_POSITION_TOP] = bound;
+ } else if (safeInsets.right > 0) {
+ sortedBounds[BOUNDS_POSITION_RIGHT] = bound;
+ } else if (safeInsets.bottom > 0) {
+ sortedBounds[BOUNDS_POSITION_BOTTOM] = bound;
+ }
+ }
+ return sortedBounds;
+ }
+
+ /**
+ * Returns true if there is no cutout, i.e. the bounds are empty.
+ *
+ * @hide
+ */
+ public boolean isBoundsEmpty() {
+ return mBounds.isEmpty();
}
/**
@@ -134,15 +349,6 @@
return mSafeInsets.equals(ZERO_RECT);
}
- /**
- * Returns true if there is no cutout, i.e. the bounds are empty.
- *
- * @hide
- */
- public boolean isBoundsEmpty() {
- return mBounds.isEmpty();
- }
-
/** Returns the inset from the top which avoids the display cutout in pixels. */
public int getSafeInsetTop() {
return mSafeInsets.top;
@@ -174,69 +380,89 @@
}
/**
- * Returns the bounding region of the cutout.
- *
- * <p>
- * <strong>Note:</strong> There may be more than one cutout, in which case the returned
- * {@code Region} will be non-contiguous and its bounding rect will be meaningless without
- * intersecting it first.
- *
- * Example:
- * <pre>
- * // Getting the bounding rectangle of the top display cutout
- * Region bounds = displayCutout.getBounds();
- * bounds.op(0, 0, Integer.MAX_VALUE, displayCutout.getSafeInsetTop(), Region.Op.INTERSECT);
- * Rect topDisplayCutout = bounds.getBoundingRect();
- * </pre>
- *
- * @return the bounding region of the cutout. Coordinates are relative
- * to the top-left corner of the content view and in pixel units.
- * @hide
- */
- public Region getBounds() {
- return Region.obtain(mBounds);
- }
-
- /**
* Returns a list of {@code Rect}s, each of which is the bounding rectangle for a non-functional
* area on the display.
*
* There will be at most one non-functional area per short edge of the device, and none on
* the long edges.
*
- * @return a list of bounding {@code Rect}s, one for each display cutout area.
+ * @return a list of bounding {@code Rect}s, one for each display cutout area. No empty Rect is
+ * returned.
*/
public List<Rect> getBoundingRects() {
List<Rect> result = new ArrayList<>();
- Region bounds = Region.obtain();
- // top
- bounds.set(mBounds);
- bounds.op(0, 0, Integer.MAX_VALUE, getSafeInsetTop(), Region.Op.INTERSECT);
- if (!bounds.isEmpty()) {
- result.add(bounds.getBounds());
+ for (Rect bound : getBoundingRectsAll()) {
+ if (!bound.isEmpty()) {
+ result.add(new Rect(bound));
+ }
}
- // left
- bounds.set(mBounds);
- bounds.op(0, 0, getSafeInsetLeft(), Integer.MAX_VALUE, Region.Op.INTERSECT);
- if (!bounds.isEmpty()) {
- result.add(bounds.getBounds());
- }
- // right & bottom
- bounds.set(mBounds);
- bounds.op(getSafeInsetLeft() + 1, getSafeInsetTop() + 1,
- Integer.MAX_VALUE, Integer.MAX_VALUE, Region.Op.INTERSECT);
- if (!bounds.isEmpty()) {
- result.add(bounds.getBounds());
- }
- bounds.recycle();
return result;
}
+ /**
+ * Returns an array of {@code Rect}s, each of which is the bounding rectangle for a non-
+ * functional area on the display. Ordinal value of BoundPosition is used as an index of
+ * the array.
+ *
+ * There will be at most one non-functional area per short edge of the device, and none on
+ * the long edges.
+ *
+ * @return an array of bounding {@code Rect}s, one for each display cutout area. This might
+ * contain ZERO_RECT, which means there is no cutout area at the position.
+ *
+ * @hide
+ */
+ public Rect[] getBoundingRectsAll() {
+ return mBounds.getRects();
+ }
+
+ /**
+ * Returns a bounding rectangle for a non-functional area on the display which is located on
+ * the left of the screen.
+ *
+ * @return bounding rectangle in pixels. In case of no bounding rectangle, an empty rectangle
+ * is returned.
+ */
+ public @NonNull Rect getBoundingRectLeft() {
+ return mBounds.getRect(BOUNDS_POSITION_LEFT);
+ }
+
+ /**
+ * Returns a bounding rectangle for a non-functional area on the display which is located on
+ * the top of the screen.
+ *
+ * @return bounding rectangle in pixels. In case of no bounding rectangle, an empty rectangle
+ * is returned.
+ */
+ public @NonNull Rect getBoundingRectTop() {
+ return mBounds.getRect(BOUNDS_POSITION_TOP);
+ }
+
+ /**
+ * Returns a bounding rectangle for a non-functional area on the display which is located on
+ * the right of the screen.
+ *
+ * @return bounding rectangle in pixels. In case of no bounding rectangle, an empty rectangle
+ * is returned.
+ */
+ public @NonNull Rect getBoundingRectRight() {
+ return mBounds.getRect(BOUNDS_POSITION_RIGHT);
+ }
+
+ /**
+ * Returns a bounding rectangle for a non-functional area on the display which is located on
+ * the bottom of the screen.
+ *
+ * @return bounding rectangle in pixels. In case of no bounding rectangle, an empty rectangle
+ * is returned.
+ */
+ public @NonNull Rect getBoundingRectBottom() {
+ return mBounds.getRect(BOUNDS_POSITION_BOTTOM);
+ }
+
@Override
public int hashCode() {
- int result = mSafeInsets.hashCode();
- result = result * 31 + mBounds.getBounds().hashCode();
- return result;
+ return mSafeInsets.hashCode() * 48271 + mBounds.hashCode();
}
@Override
@@ -246,8 +472,7 @@
}
if (o instanceof DisplayCutout) {
DisplayCutout c = (DisplayCutout) o;
- return mSafeInsets.equals(c.mSafeInsets)
- && mBounds.equals(c.mBounds);
+ return mSafeInsets.equals(c.mSafeInsets) && mBounds.equals(c.mBounds);
}
return false;
}
@@ -255,7 +480,7 @@
@Override
public String toString() {
return "DisplayCutout{insets=" + mSafeInsets
- + " boundingRect=" + mBounds.getBounds()
+ + " boundingRect={" + mBounds + "}"
+ "}";
}
@@ -265,7 +490,10 @@
public void writeToProto(ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
mSafeInsets.writeToProto(proto, INSETS);
- mBounds.getBounds().writeToProto(proto, BOUNDS);
+ mBounds.getRect(BOUNDS_POSITION_LEFT).writeToProto(proto, BOUND_LEFT);
+ mBounds.getRect(BOUNDS_POSITION_TOP).writeToProto(proto, BOUND_TOP);
+ mBounds.getRect(BOUNDS_POSITION_RIGHT).writeToProto(proto, BOUND_RIGHT);
+ mBounds.getRect(BOUNDS_POSITION_BOTTOM).writeToProto(proto, BOUND_BOTTOM);
proto.end(token);
}
@@ -276,13 +504,12 @@
* @hide
*/
public DisplayCutout inset(int insetLeft, int insetTop, int insetRight, int insetBottom) {
- if (mBounds.isEmpty()
+ if (isBoundsEmpty()
|| insetLeft == 0 && insetTop == 0 && insetRight == 0 && insetBottom == 0) {
return this;
}
Rect safeInsets = new Rect(mSafeInsets);
- Region bounds = Region.obtain(mBounds);
// Note: it's not really well defined what happens when the inset is negative, because we
// don't know if the safe inset needs to expand in general.
@@ -299,7 +526,13 @@
safeInsets.right = atLeastZero(safeInsets.right - insetRight);
}
- bounds.translate(-insetLeft, -insetTop);
+ Rect[] bounds = mBounds.getRects();
+ for (int i = 0; i < bounds.length; ++i) {
+ if (!bounds[i].equals(ZERO_RECT)) {
+ bounds[i].offset(-insetLeft, -insetTop);
+ }
+ }
+
return new DisplayCutout(safeInsets, bounds, false /* copyArguments */);
}
@@ -312,7 +545,7 @@
* @hide
*/
public DisplayCutout replaceSafeInsets(Rect safeInsets) {
- return new DisplayCutout(new Rect(safeInsets), mBounds, false /* copyArguments */);
+ return new DisplayCutout(new Rect(safeInsets), mBounds);
}
private static int atLeastZero(int value) {
@@ -326,10 +559,13 @@
* @hide
*/
@VisibleForTesting
- public static DisplayCutout fromBoundingRect(int left, int top, int right, int bottom) {
- Region r = Region.obtain();
- r.set(left, top, right, bottom);
- return fromBounds(r);
+ public static DisplayCutout fromBoundingRect(
+ int left, int top, int right, int bottom, @BoundsPosition int pos) {
+ Rect[] bounds = new Rect[BOUNDS_POSITION_LENGTH];
+ for (int i = 0; i < BOUNDS_POSITION_LENGTH; ++i) {
+ bounds[i] = (pos == i) ? new Rect(left, top, right, bottom) : new Rect();
+ }
+ return new DisplayCutout(ZERO_RECT, bounds, false /* copyArguments */);
}
/**
@@ -337,8 +573,8 @@
*
* @hide
*/
- public static DisplayCutout fromBounds(Region region) {
- return new DisplayCutout(ZERO_RECT, region, false /* copyArguments */);
+ public static DisplayCutout fromBounds(Rect[] bounds) {
+ return new DisplayCutout(ZERO_RECT, bounds, false /* copyArguments */);
}
/**
@@ -423,10 +659,11 @@
m.postTranslate(offsetX, 0);
p.transform(m);
- final Rect tmpRect = new Rect();
- toRectAndAddToRegion(p, r, tmpRect);
- final int topInset = tmpRect.bottom;
+ Rect boundTop = new Rect();
+ toRectAndAddToRegion(p, r, boundTop);
+ final int topInset = boundTop.bottom;
+ Rect boundBottom = null;
final int bottomInset;
if (bottomSpec != null) {
final Path bottomPath;
@@ -440,15 +677,17 @@
m.postTranslate(0, displayHeight);
bottomPath.transform(m);
p.addPath(bottomPath);
- toRectAndAddToRegion(bottomPath, r, tmpRect);
- bottomInset = displayHeight - tmpRect.top;
+ boundBottom = new Rect();
+ toRectAndAddToRegion(bottomPath, r, boundBottom);
+ bottomInset = displayHeight - boundBottom.top;
} else {
bottomInset = 0;
}
- // Reuse tmpRect as the inset rect we store into the DisplayCutout instance.
- tmpRect.set(0, topInset, 0, bottomInset);
- final DisplayCutout cutout = new DisplayCutout(tmpRect, r, false /* copyArguments */);
+ Rect safeInset = new Rect(0, topInset, 0, bottomInset);
+ final DisplayCutout cutout = new DisplayCutout(
+ safeInset, null /* boundLeft */, boundTop, null /* boundRight */, boundBottom,
+ false /* copyArguments */);
final Pair<Path, DisplayCutout> result = new Pair<>(p, cutout);
synchronized (CACHE_LOCK) {
@@ -468,16 +707,6 @@
inoutRegion.op(inoutRect, Op.UNION);
}
- private static Region boundingRectsToRegion(List<Rect> rects) {
- Region result = Region.obtain();
- if (rects != null) {
- for (Rect r : rects) {
- result.op(r, Region.Op.UNION);
- }
- }
- return result;
- }
-
/**
* Helper class for passing {@link DisplayCutout} through binder.
*
@@ -520,7 +749,7 @@
} else {
out.writeInt(1);
out.writeTypedObject(cutout.mSafeInsets, flags);
- out.writeTypedObject(cutout.mBounds, flags);
+ out.writeTypedArray(cutout.mBounds.getRects(), flags);
}
}
@@ -561,7 +790,8 @@
}
Rect safeInsets = in.readTypedObject(Rect.CREATOR);
- Region bounds = in.readTypedObject(Region.CREATOR);
+ Rect[] bounds = new Rect[BOUNDS_POSITION_LENGTH];
+ in.readTypedArray(bounds, Rect.CREATOR);
return new DisplayCutout(safeInsets, bounds, false /* copyArguments */);
}
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 3bab87a..3e559d9 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -87,7 +87,6 @@
void setEventDispatching(boolean enabled);
void addWindowToken(IBinder token, int type, int displayId);
void removeWindowToken(IBinder token, int displayId);
- void setFocusedApp(IBinder token, boolean moveFocusNow);
void prepareAppTransition(int transit, boolean alwaysKeepCurrent);
int getPendingAppTransition();
void overridePendingAppTransition(String packageName, int enterAnim, int exitAnim,
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index c38fcf3..0a3403b 100644
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -896,8 +896,8 @@
* @deprecated No longer used by the input system.
* {@link #getAction} value: multiple duplicate key events have
* occurred in a row, or a complex string is being delivered. If the
- * key code is not {#link {@link #KEYCODE_UNKNOWN} then the
- * {#link {@link #getRepeatCount()} method returns the number of times
+ * key code is not {@link #KEYCODE_UNKNOWN} then the
+ * {@link #getRepeatCount()} method returns the number of times
* the given key code should be executed.
* Otherwise, if the key code is {@link #KEYCODE_UNKNOWN}, then
* this is a sequence of characters as returned by {@link #getCharacters}.
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 982e5c2..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);
}
@@ -873,26 +899,42 @@
// 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);
}
@@ -906,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
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 f4be9f2..cddd83c 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() {
@@ -14639,9 +14639,10 @@
* Recomputes the matrix if necessary.
*
* @return True if the transform matrix is the identity matrix, false otherwise.
+ * @hide
*/
@UnsupportedAppUsage
- final boolean hasIdentityMatrix() {
+ public final boolean hasIdentityMatrix() {
return mRenderNode.hasIdentityMatrix();
}
@@ -15255,6 +15256,10 @@
* 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
@@ -15632,7 +15637,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.
*/
@@ -20600,7 +20605,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 9be9ed0..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;
@@ -991,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);
@@ -1002,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);
}
@@ -1073,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;
@@ -1081,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;
@@ -4073,6 +4100,8 @@
mForceNextWindowRelayout = true;
requestLayout();
}
+
+ updateForceDarkMode();
}
/**
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/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 7abe19e79..d4c7069 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -803,10 +803,6 @@
return true;
}
}
- if (sVerbose) {
- Log.v(TAG, "not ignoring notifyViewEntered(flags=" + flags + ", view=" + id
- + ", state " + getStateAsStringLocked() + ", enteredIds=" + mEnteredIds);
- }
return false;
}
@@ -845,6 +841,9 @@
ensureServiceClientAddedIfNeededLocked();
if (!mEnabled) {
+ if (sVerbose) {
+ Log.v(TAG, "ignoring notifyViewEntered(" + id + "): disabled");
+ }
if (mCallback != null) {
callback = mCallback;
}
@@ -995,6 +994,9 @@
ensureServiceClientAddedIfNeededLocked();
if (!mEnabled) {
+ if (sVerbose) {
+ Log.v(TAG, "ignoring notifyViewEntered(" + id + "): disabled");
+ }
if (mCallback != null) {
callback = mCallback;
}
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/widget/Editor.java b/core/java/android/widget/Editor.java
index 9d74c98..8027dd7 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -101,6 +101,7 @@
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
+import android.view.ViewParent;
import android.view.ViewTreeObserver;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityNodeInfo;
@@ -4800,6 +4801,24 @@
return glyphHeight > magnifierContentHeight;
}
+ private boolean viewIsMatrixTransformed() {
+ if (mMagnifierAnimator.mMagnifierIsShowing) {
+ // Do not check again when the magnifier is currently showing.
+ return false;
+ }
+ if (!mTextView.hasIdentityMatrix()) {
+ return true;
+ }
+ ViewParent viewParent = mTextView.getParent();
+ while (viewParent != null) {
+ if (viewParent instanceof View && !((View) viewParent).hasIdentityMatrix()) {
+ return true;
+ }
+ viewParent = viewParent.getParent();
+ }
+ return false;
+ }
+
/**
* Computes the position where the magnifier should be shown, relative to
* {@code mTextView}, and writes them to {@code showPosInView}. Also decides
@@ -4928,6 +4947,7 @@
final PointF showPosInView = new PointF();
final boolean shouldShow = !tooLargeTextForMagnifier()
+ && !viewIsMatrixTransformed()
&& obtainMagnifierShowCoordinates(event, showPosInView);
if (shouldShow) {
// Make the cursor visible and stop blinking.
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/TextView.java b/core/java/android/widget/TextView.java
index f95b3ce..9d06680 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -882,9 +882,6 @@
private boolean mTextSetFromXmlOrResourceId = false;
// Resource id used to set the text.
private @StringRes int mTextId = ResourceId.ID_NULL;
- // Last value used on AFM.notifyValueChanged(), used to optimize autofill workflow by avoiding
- // calls when the value did not change
- private CharSequence mLastValueSentToAutofillManager;
//
// End of autofill-related attributes
@@ -5884,7 +5881,7 @@
if (needEditableForNotification) {
sendAfterTextChanged((Editable) text);
} else {
- notifyAutoFillManagerAfterTextChangedIfNeeded();
+ notifyAutoFillManagerAfterTextChanged();
}
// SelectionModifierCursorController depends on textCanBeSelected, which depends on text
@@ -9933,33 +9930,23 @@
}
// Always notify AutoFillManager - it will return right away if autofill is disabled.
- notifyAutoFillManagerAfterTextChangedIfNeeded();
+ notifyAutoFillManagerAfterTextChanged();
hideErrorIfUnchanged();
}
- private void notifyAutoFillManagerAfterTextChangedIfNeeded() {
+ private void notifyAutoFillManagerAfterTextChanged() {
// It is important to not check whether the view is important for autofill
// since the user can trigger autofill manually on not important views.
if (!isAutofillable()) {
return;
}
final AutofillManager afm = mContext.getSystemService(AutofillManager.class);
- if (afm == null) {
- return;
- }
-
- if (mLastValueSentToAutofillManager == null
- || !mLastValueSentToAutofillManager.equals(mText)) {
+ if (afm != null) {
if (android.view.autofill.Helper.sVerbose) {
- Log.v(LOG_TAG, "notifying AFM after text changed");
+ Log.v(LOG_TAG, "notifyAutoFillManagerAfterTextChanged");
}
afm.notifyValueChanged(TextView.this);
- mLastValueSentToAutofillManager = mText;
- } else {
- if (android.view.autofill.Helper.sVerbose) {
- Log.v(LOG_TAG, "not notifying AFM on unchanged text");
- }
}
}
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/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/BinderCallsStats.java b/core/java/com/android/internal/os/BinderCallsStats.java
index c0c358d..856712f 100644
--- a/core/java/com/android/internal/os/BinderCallsStats.java
+++ b/core/java/com/android/internal/os/BinderCallsStats.java
@@ -436,6 +436,12 @@
}
public void setSamplingInterval(int samplingInterval) {
+ if (samplingInterval <= 0) {
+ Slog.w(TAG, "Ignored invalid sampling interval (value must be positive): "
+ + samplingInterval);
+ return;
+ }
+
synchronized (mLock) {
if (samplingInterval != mPeriodicSamplingInterval) {
mPeriodicSamplingInterval = samplingInterval;
diff --git a/core/java/com/android/internal/os/PowerProfile.java b/core/java/com/android/internal/os/PowerProfile.java
index 997b722..8338d78 100644
--- a/core/java/com/android/internal/os/PowerProfile.java
+++ b/core/java/com/android/internal/os/PowerProfile.java
@@ -21,6 +21,7 @@
import android.content.Context;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
+import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.XmlUtils;
@@ -501,4 +502,181 @@
public double getBatteryCapacity() {
return getAveragePower(POWER_BATTERY_CAPACITY);
}
+
+ /**
+ * Dump power constants into PowerProfileProto
+ */
+ public void writeToProto(ProtoOutputStream proto) {
+ // cpu.suspend
+ writePowerConstantToProto(proto, POWER_CPU_SUSPEND, PowerProfileProto.CPU_SUSPEND);
+
+ // cpu.idle
+ writePowerConstantToProto(proto, POWER_CPU_IDLE, PowerProfileProto.CPU_IDLE);
+
+ // cpu.active
+ writePowerConstantToProto(proto, POWER_CPU_ACTIVE, PowerProfileProto.CPU_ACTIVE);
+
+ // cpu.clusters.cores
+ // cpu.cluster_power.cluster
+ // cpu.core_speeds.cluster
+ // cpu.core_power.cluster
+ for (int cluster = 0; cluster < mCpuClusters.length; cluster++) {
+ final long token = proto.start(PowerProfileProto.CPU_CLUSTER);
+ proto.write(PowerProfileProto.CpuCluster.ID, cluster);
+ proto.write(PowerProfileProto.CpuCluster.CLUSTER_POWER,
+ sPowerItemMap.get(mCpuClusters[cluster].clusterPowerKey));
+ proto.write(PowerProfileProto.CpuCluster.CORES, mCpuClusters[cluster].numCpus);
+ for (Double speed : sPowerArrayMap.get(mCpuClusters[cluster].freqKey)) {
+ proto.write(PowerProfileProto.CpuCluster.SPEED, speed);
+ }
+ for (Double corePower : sPowerArrayMap.get(mCpuClusters[cluster].corePowerKey)) {
+ proto.write(PowerProfileProto.CpuCluster.CORE_POWER, corePower);
+ }
+ proto.end(token);
+ }
+
+ // wifi.scan
+ writePowerConstantToProto(proto, POWER_WIFI_SCAN, PowerProfileProto.WIFI_SCAN);
+
+ // wifi.on
+ writePowerConstantToProto(proto, POWER_WIFI_ON, PowerProfileProto.WIFI_ON);
+
+ // wifi.active
+ writePowerConstantToProto(proto, POWER_WIFI_ACTIVE, PowerProfileProto.WIFI_ACTIVE);
+
+ // wifi.controller.idle
+ writePowerConstantToProto(proto, POWER_WIFI_CONTROLLER_IDLE,
+ PowerProfileProto.WIFI_CONTROLLER_IDLE);
+
+ // wifi.controller.rx
+ writePowerConstantToProto(proto, POWER_WIFI_CONTROLLER_RX,
+ PowerProfileProto.WIFI_CONTROLLER_RX);
+
+ // wifi.controller.tx
+ writePowerConstantToProto(proto, POWER_WIFI_CONTROLLER_TX,
+ PowerProfileProto.WIFI_CONTROLLER_TX);
+
+ // wifi.controller.tx_levels
+ writePowerConstantArrayToProto(proto, POWER_WIFI_CONTROLLER_TX_LEVELS,
+ PowerProfileProto.WIFI_CONTROLLER_TX_LEVELS);
+
+ // wifi.controller.voltage
+ writePowerConstantToProto(proto, POWER_WIFI_CONTROLLER_OPERATING_VOLTAGE,
+ PowerProfileProto.WIFI_CONTROLLER_OPERATING_VOLTAGE);
+
+ // bluetooth.controller.idle
+ writePowerConstantToProto(proto, POWER_BLUETOOTH_CONTROLLER_IDLE,
+ PowerProfileProto.BLUETOOTH_CONTROLLER_IDLE);
+
+ // bluetooth.controller.rx
+ writePowerConstantToProto(proto, POWER_BLUETOOTH_CONTROLLER_RX,
+ PowerProfileProto.BLUETOOTH_CONTROLLER_RX);
+
+ // bluetooth.controller.tx
+ writePowerConstantToProto(proto, POWER_BLUETOOTH_CONTROLLER_TX,
+ PowerProfileProto.BLUETOOTH_CONTROLLER_TX);
+
+ // bluetooth.controller.voltage
+ writePowerConstantToProto(proto, POWER_BLUETOOTH_CONTROLLER_OPERATING_VOLTAGE,
+ PowerProfileProto.BLUETOOTH_CONTROLLER_OPERATING_VOLTAGE);
+
+ // modem.controller.sleep
+ writePowerConstantToProto(proto, POWER_MODEM_CONTROLLER_SLEEP,
+ PowerProfileProto.MODEM_CONTROLLER_SLEEP);
+
+ // modem.controller.idle
+ writePowerConstantToProto(proto, POWER_MODEM_CONTROLLER_IDLE,
+ PowerProfileProto.MODEM_CONTROLLER_IDLE);
+
+ // modem.controller.rx
+ writePowerConstantToProto(proto, POWER_MODEM_CONTROLLER_RX,
+ PowerProfileProto.MODEM_CONTROLLER_RX);
+
+ // modem.controller.tx
+ writePowerConstantArrayToProto(proto, POWER_MODEM_CONTROLLER_TX,
+ PowerProfileProto.MODEM_CONTROLLER_TX);
+
+ // modem.controller.voltage
+ writePowerConstantToProto(proto, POWER_MODEM_CONTROLLER_OPERATING_VOLTAGE,
+ PowerProfileProto.MODEM_CONTROLLER_OPERATING_VOLTAGE);
+
+ // gps.on
+ writePowerConstantToProto(proto, POWER_GPS_ON, PowerProfileProto.GPS_ON);
+
+ // gps.signalqualitybased
+ writePowerConstantArrayToProto(proto, POWER_GPS_SIGNAL_QUALITY_BASED,
+ PowerProfileProto.GPS_SIGNAL_QUALITY_BASED);
+
+ // gps.voltage
+ writePowerConstantToProto(proto, POWER_GPS_OPERATING_VOLTAGE,
+ PowerProfileProto.GPS_OPERATING_VOLTAGE);
+
+ // bluetooth.on
+ writePowerConstantToProto(proto, POWER_BLUETOOTH_ON, PowerProfileProto.BLUETOOTH_ON);
+
+ // bluetooth.active
+ writePowerConstantToProto(proto, POWER_BLUETOOTH_ACTIVE,
+ PowerProfileProto.BLUETOOTH_ACTIVE);
+
+ // bluetooth.at
+ writePowerConstantToProto(proto, POWER_BLUETOOTH_AT_CMD,
+ PowerProfileProto.BLUETOOTH_AT_CMD);
+
+ // ambient.on
+ writePowerConstantToProto(proto, POWER_AMBIENT_DISPLAY, PowerProfileProto.AMBIENT_DISPLAY);
+
+ // screen.on
+ writePowerConstantToProto(proto, POWER_SCREEN_ON, PowerProfileProto.SCREEN_ON);
+
+ // radio.on
+ writePowerConstantToProto(proto, POWER_RADIO_ON, PowerProfileProto.RADIO_ON);
+
+ // radio.scanning
+ writePowerConstantToProto(proto, POWER_RADIO_SCANNING, PowerProfileProto.RADIO_SCANNING);
+
+ // radio.active
+ writePowerConstantToProto(proto, POWER_RADIO_ACTIVE, PowerProfileProto.RADIO_ACTIVE);
+
+ // screen.full
+ writePowerConstantToProto(proto, POWER_SCREEN_FULL, PowerProfileProto.SCREEN_FULL);
+
+ // audio
+ writePowerConstantToProto(proto, POWER_AUDIO, PowerProfileProto.AUDIO);
+
+ // video
+ writePowerConstantToProto(proto, POWER_VIDEO, PowerProfileProto.VIDEO);
+
+ // camera.flashlight
+ writePowerConstantToProto(proto, POWER_FLASHLIGHT, PowerProfileProto.FLASHLIGHT);
+
+ // memory.bandwidths
+ writePowerConstantToProto(proto, POWER_MEMORY, PowerProfileProto.MEMORY);
+
+ // camera.avg
+ writePowerConstantToProto(proto, POWER_CAMERA, PowerProfileProto.CAMERA);
+
+ // wifi.batchedscan
+ writePowerConstantToProto(proto, POWER_WIFI_BATCHED_SCAN,
+ PowerProfileProto.WIFI_BATCHED_SCAN);
+
+ // battery.capacity
+ writePowerConstantToProto(proto, POWER_BATTERY_CAPACITY,
+ PowerProfileProto.BATTERY_CAPACITY);
+ }
+
+ // Writes items in sPowerItemMap to proto if exists.
+ private void writePowerConstantToProto(ProtoOutputStream proto, String key, long fieldId) {
+ if (sPowerItemMap.containsKey(key)) {
+ proto.write(fieldId, sPowerItemMap.get(key));
+ }
+ }
+
+ // Writes items in sPowerArrayMap to proto if exists.
+ private void writePowerConstantArrayToProto(ProtoOutputStream proto, String key, long fieldId) {
+ if (sPowerArrayMap.containsKey(key)) {
+ for (Double d : sPowerArrayMap.get(key)) {
+ proto.write(fieldId, d);
+ }
+ }
+ }
}
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/jni/Android.bp b/core/jni/Android.bp
index 762b430..8e24d10 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -209,6 +209,8 @@
"com_android_internal_view_animation_NativeInterpolatorFactoryHelper.cpp",
"hwbinder/EphemeralStorage.cpp",
"fd_utils.cpp",
+ "android_hardware_input_InputWindowHandle.cpp",
+ "android_hardware_input_InputApplicationHandle.cpp",
],
include_dirs: [
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index 897f6fa5..644a974 100755
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -9,7 +9,6 @@
#include "SkColor.h"
#include "SkColorPriv.h"
#include "SkColorSpace.h"
-#include "SkColorSpaceXform.h"
#include "SkHalf.h"
#include "SkMatrix44.h"
#include "SkPM4f.h"
@@ -559,13 +558,10 @@
if (!p3.tryAllocPixels(info)) {
return JNI_FALSE;
}
- auto xform = SkColorSpaceXform::New(skbitmap.colorSpace(), info.colorSpace());
- if (!xform) {
- return JNI_FALSE;
- }
- if (!xform->apply(SkColorSpaceXform::kRGBA_8888_ColorFormat, p3.getPixels(),
- SkColorSpaceXform::kRGBA_F16_ColorFormat, skbitmap.getPixels(),
- info.width() * info.height(), kUnpremul_SkAlphaType)) {
+
+ SkPixmap pm;
+ SkAssertResult(p3.peekPixels(&pm)); // should always work if tryAllocPixels() did.
+ if (!skbitmap.readPixels(pm)) {
return JNI_FALSE;
}
skbitmap = p3;
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..dca2da3 100644
--- a/core/jni/android_graphics_Canvas.cpp
+++ b/core/jni/android_graphics_Canvas.cpp
@@ -273,6 +273,32 @@
get_canvas(canvasHandle)->drawRect(left, top, right, bottom, *paint);
}
+static void drawDoubleRoundRectXY(JNIEnv* env, jobject, jlong canvasHandle, jfloat outerLeft,
+ jfloat outerTop, jfloat outerRight, jfloat outerBottom, jfloat outerRx,
+ jfloat outerRy, jfloat innerLeft, jfloat innerTop, jfloat innerRight,
+ jfloat innerBottom, jfloat innerRx, jfloat innerRy, jlong paintHandle) {
+ const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+ get_canvas(canvasHandle)->drawDoubleRoundRectXY(
+ outerLeft, outerTop, outerRight, outerBottom, outerRx, outerRy,
+ innerLeft, innerTop, innerRight, innerBottom, innerRx, innerRy, *paint);
+}
+
+static void drawDoubleRoundRectRadii(JNIEnv* env, jobject, jlong canvasHandle, jfloat outerLeft,
+ jfloat outerTop, jfloat outerRight, jfloat outerBottom, jfloatArray jouterRadii,
+ jfloat innerLeft, jfloat innerTop, jfloat innerRight,
+ jfloat innerBottom, jfloatArray jinnerRadii, jlong paintHandle) {
+ const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+
+ float outerRadii[8];
+ float innerRadii[8];
+ env->GetFloatArrayRegion(jouterRadii, 0, 8, outerRadii);
+ env->GetFloatArrayRegion(jinnerRadii, 0, 8, innerRadii);
+ get_canvas(canvasHandle)->drawDoubleRoundRectRadii(
+ outerLeft, outerTop, outerRight, outerBottom, outerRadii,
+ innerLeft, innerTop, innerRight, innerBottom, innerRadii, *paint);
+
+}
+
static void drawRegion(JNIEnv* env, jobject, jlong canvasHandle, jlong regionHandle,
jlong paintHandle) {
const SkRegion* region = reinterpret_cast<SkRegion*>(regionHandle);
@@ -651,6 +677,8 @@
{"nDrawRect","(JFFFFJ)V", (void*) CanvasJNI::drawRect},
{"nDrawRegion", "(JJJ)V", (void*) CanvasJNI::drawRegion },
{"nDrawRoundRect","(JFFFFFFJ)V", (void*) CanvasJNI::drawRoundRect},
+ {"nDrawDoubleRoundRect", "(JFFFFFFFFFFFFJ)V", (void*) CanvasJNI::drawDoubleRoundRectXY},
+ {"nDrawDoubleRoundRect", "(JFFFF[FFFFF[FJ)V", (void*) CanvasJNI::drawDoubleRoundRectRadii},
{"nDrawCircle","(JFFFJ)V", (void*) CanvasJNI::drawCircle},
{"nDrawOval","(JFFFFJ)V", (void*) CanvasJNI::drawOval},
{"nDrawArc","(JFFFFFFZJ)V", (void*) CanvasJNI::drawArc},
@@ -674,7 +702,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_hardware_display_DisplayViewport.cpp b/core/jni/android_hardware_display_DisplayViewport.cpp
index ab8e685..05f6556 100644
--- a/core/jni/android_hardware_display_DisplayViewport.cpp
+++ b/core/jni/android_hardware_display_DisplayViewport.cpp
@@ -40,6 +40,7 @@
jfieldID deviceWidth;
jfieldID deviceHeight;
jfieldID uniqueId;
+ jfieldID type;
} gDisplayViewportClassInfo;
static struct {
@@ -64,6 +65,9 @@
viewport->uniqueId = ScopedUtfChars(env, uniqueId).c_str();
}
+ viewport->type = static_cast<ViewportType>(env->GetIntField(viewportObj,
+ gDisplayViewportClassInfo.type));
+
jobject logicalFrameObj =
env->GetObjectField(viewportObj, gDisplayViewportClassInfo.logicalFrame);
viewport->logicalLeft = env->GetIntField(logicalFrameObj, gRectClassInfo.left);
@@ -108,6 +112,9 @@
gDisplayViewportClassInfo.uniqueId = GetFieldIDOrDie(env,
gDisplayViewportClassInfo.clazz, "uniqueId", "Ljava/lang/String;");
+ gDisplayViewportClassInfo.type = GetFieldIDOrDie(env,
+ gDisplayViewportClassInfo.clazz, "type", "I");
+
clazz = FindClassOrDie(env, "android/graphics/Rect");
gRectClassInfo.left = GetFieldIDOrDie(env, clazz, "left", "I");
gRectClassInfo.top = GetFieldIDOrDie(env, clazz, "top", "I");
diff --git a/services/core/jni/com_android_server_input_InputApplicationHandle.cpp b/core/jni/android_hardware_input_InputApplicationHandle.cpp
similarity index 98%
rename from services/core/jni/com_android_server_input_InputApplicationHandle.cpp
rename to core/jni/android_hardware_input_InputApplicationHandle.cpp
index 514b6e1..8ace8da 100644
--- a/services/core/jni/com_android_server_input_InputApplicationHandle.cpp
+++ b/core/jni/android_hardware_input_InputApplicationHandle.cpp
@@ -21,7 +21,7 @@
#include <android_runtime/AndroidRuntime.h>
#include <utils/threads.h>
-#include "com_android_server_input_InputApplicationHandle.h"
+#include "android_hardware_input_InputApplicationHandle.h"
namespace android {
diff --git a/services/core/jni/com_android_server_input_InputApplicationHandle.h b/core/jni/android_hardware_input_InputApplicationHandle.h
similarity index 95%
rename from services/core/jni/com_android_server_input_InputApplicationHandle.h
rename to core/jni/android_hardware_input_InputApplicationHandle.h
index c9af711..7115611 100644
--- a/services/core/jni/com_android_server_input_InputApplicationHandle.h
+++ b/core/jni/android_hardware_input_InputApplicationHandle.h
@@ -17,7 +17,9 @@
#ifndef _ANDROID_SERVER_INPUT_APPLICATION_HANDLE_H
#define _ANDROID_SERVER_INPUT_APPLICATION_HANDLE_H
-#include <inputflinger/InputApplication.h>
+#include <string>
+
+#include <input/InputApplication.h>
#include <nativehelper/JNIHelp.h>
#include "jni.h"
diff --git a/services/core/jni/com_android_server_input_InputWindowHandle.cpp b/core/jni/android_hardware_input_InputWindowHandle.cpp
similarity index 98%
rename from services/core/jni/com_android_server_input_InputWindowHandle.cpp
rename to core/jni/android_hardware_input_InputWindowHandle.cpp
index c13aa38..f4829ad 100644
--- a/services/core/jni/com_android_server_input_InputWindowHandle.cpp
+++ b/core/jni/android_hardware_input_InputWindowHandle.cpp
@@ -25,8 +25,8 @@
#include <android/graphics/Region.h>
#include <ui/Region.h>
-#include "com_android_server_input_InputWindowHandle.h"
-#include "com_android_server_input_InputApplicationHandle.h"
+#include "android_hardware_input_InputWindowHandle.h"
+#include "android_hardware_input_InputApplicationHandle.h"
namespace android {
diff --git a/services/core/jni/com_android_server_input_InputWindowHandle.h b/core/jni/android_hardware_input_InputWindowHandle.h
similarity index 96%
rename from services/core/jni/com_android_server_input_InputWindowHandle.h
rename to core/jni/android_hardware_input_InputWindowHandle.h
index 44d4620..2be267e 100644
--- a/services/core/jni/com_android_server_input_InputWindowHandle.h
+++ b/core/jni/android_hardware_input_InputWindowHandle.h
@@ -17,7 +17,7 @@
#ifndef _ANDROID_SERVER_INPUT_WINDOW_HANDLE_H
#define _ANDROID_SERVER_INPUT_WINDOW_HANDLE_H
-#include <inputflinger/InputWindow.h>
+#include <input/InputWindow.h>
#include <nativehelper/JNIHelp.h>
#include "jni.h"
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_InputChannel.cpp b/core/jni/android_view_InputChannel.cpp
index 0a90b97..2f17907 100644
--- a/core/jni/android_view_InputChannel.cpp
+++ b/core/jni/android_view_InputChannel.cpp
@@ -202,17 +202,9 @@
if (parcel) {
bool isInitialized = parcel->readInt32();
if (isInitialized) {
- String8 name = parcel->readString8();
- int rawFd = parcel->readFileDescriptor();
- int dupFd = dup(rawFd);
- if (dupFd < 0) {
- ALOGE("Error %d dup channel fd %d.", errno, rawFd);
- jniThrowRuntimeException(env,
- "Could not read input channel file descriptors from parcel.");
- return;
- }
+ InputChannel* inputChannel = new InputChannel();
+ inputChannel->read(*parcel);
- InputChannel* inputChannel = new InputChannel(name.string(), dupFd);
NativeInputChannel* nativeInputChannel = new NativeInputChannel(inputChannel);
android_view_InputChannel_setNativeInputChannel(env, obj, nativeInputChannel);
@@ -230,8 +222,7 @@
sp<InputChannel> inputChannel = nativeInputChannel->getInputChannel();
parcel->writeInt32(1);
- parcel->writeString8(String8(inputChannel->getName().c_str()));
- parcel->writeDupFileDescriptor(inputChannel->getFd());
+ inputChannel->write(*parcel);
} else {
parcel->writeInt32(0);
}
diff --git a/core/jni/android_view_RenderNode.cpp b/core/jni/android_view_RenderNode.cpp
index 0701f3e..63b0046 100644
--- a/core/jni/android_view_RenderNode.cpp
+++ b/core/jni/android_view_RenderNode.cpp
@@ -473,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) {
@@ -531,7 +531,7 @@
return;
}
- env->CallVoidMethod(localref, gSurfaceViewPositionLostMethod,
+ env->CallVoidMethod(localref, gPositionListener_PositionLostMethod,
info ? info->canvasContext.getFrameNumber() : 0);
env->DeleteLocalRef(localref);
}
@@ -555,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);
}
@@ -588,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 },
@@ -677,11 +677,11 @@
};
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_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/internal/powerprofile.proto b/core/proto/android/internal/powerprofile.proto
new file mode 100644
index 0000000..9dd5e7e
--- /dev/null
+++ b/core/proto/android/internal/powerprofile.proto
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+package com.android.internal.os;
+
+option java_multiple_files = true;
+
+// next: 41
+message PowerProfileProto {
+ optional double cpu_suspend = 1;
+
+ optional double cpu_idle = 2;
+
+ optional double cpu_active = 3;
+
+ message CpuCluster {
+ optional int32 id = 1;
+ optional double cluster_power = 2;
+ optional int32 cores = 3;
+ repeated int64 speed = 4;
+ repeated double core_power = 5;
+ }
+
+ repeated CpuCluster cpu_cluster = 41;
+
+ optional double wifi_scan = 4;
+
+ optional double wifi_on = 5;
+
+ optional double wifi_active = 6;
+
+ optional double wifi_controller_idle = 7;
+
+ optional double wifi_controller_rx = 8;
+
+ optional double wifi_controller_tx = 9;
+
+ repeated double wifi_controller_tx_levels = 10;
+
+ optional double wifi_controller_operating_voltage = 11;
+
+ optional double bluetooth_controller_idle = 12;
+
+ optional double bluetooth_controller_rx = 13;
+
+ optional double bluetooth_controller_tx = 14;
+
+ optional double bluetooth_controller_operating_voltage = 15;
+
+ optional double modem_controller_sleep = 16;
+
+ optional double modem_controller_idle = 17;
+
+ optional double modem_controller_rx = 18;
+
+ repeated double modem_controller_tx = 19;
+
+ optional double modem_controller_operating_voltage = 20;
+
+ optional double gps_on = 21;
+
+ repeated double gps_signal_quality_based = 22;
+
+ optional double gps_operating_voltage = 23;
+
+ optional double bluetooth_on = 24;
+
+ optional double bluetooth_active = 25;
+
+ optional double bluetooth_at_cmd = 26;
+
+ optional double ambient_display = 27;
+
+ optional double screen_on = 29;
+
+ optional double radio_on = 30;
+
+ optional double radio_scanning = 31;
+
+ optional double radio_active = 32;
+
+ optional double screen_full = 33;
+
+ optional double audio = 34;
+
+ optional double video = 35;
+
+ optional double flashlight = 36;
+
+ optional double memory = 37;
+
+ optional double camera = 38;
+
+ optional double wifi_batched_scan = 39;
+
+ optional double battery_capacity = 40;
+}
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/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 d33ea0c..c1c86f0 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -158,6 +158,7 @@
optional ScreenRotationAnimationProto screen_rotation_animation = 12;
optional DisplayFramesProto display_frames = 13;
optional int32 surface_size = 14;
+ optional string focused_app = 15;
}
/* represents DisplayFrames */
diff --git a/core/proto/android/view/displaycutout.proto b/core/proto/android/view/displaycutout.proto
index f4744da..0a33101 100644
--- a/core/proto/android/view/displaycutout.proto
+++ b/core/proto/android/view/displaycutout.proto
@@ -26,5 +26,9 @@
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
optional .android.graphics.RectProto insets = 1;
- optional .android.graphics.RectProto bounds = 2;
+ reserved 2;
+ optional .android.graphics.RectProto bound_left = 3;
+ optional .android.graphics.RectProto bound_top = 4;
+ optional .android.graphics.RectProto bound_right = 5;
+ optional .android.graphics.RectProto bound_bottom = 6;
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index ade0b11..f3f012d 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -941,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"
@@ -1101,7 +1100,7 @@
<!-- Allows a calling application which manages it own calls through the self-managed
{@link android.telecom.ConnectionService} APIs. See
- {@link android.telecom.PhoneAccount#CAPABILITY_SELF_MANAGED for more information on the
+ {@link android.telecom.PhoneAccount#CAPABILITY_SELF_MANAGED} for more information on the
self-managed ConnectionService APIs.
<p>Protection level: normal
-->
@@ -1186,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>
@@ -1933,6 +1932,13 @@
<permission android:name="android.permission.BIND_SCREENING_SERVICE"
android:protectionLevel="signature|privileged" />
+ <!-- Must be required by a {@link android.telecom.CallRedirectionService},
+ to ensure that only the system can bind to it.
+ <p>Protection level: signature|privileged
+ -->
+ <permission android:name="android.permission.BIND_CALL_REDIRECTION_SERVICE"
+ android:protectionLevel="signature|privileged" />
+
<!-- Must be required by a {@link android.telecom.ConnectionService},
to ensure that only the system can bind to it.
@deprecated {@link android.telecom.ConnectionService}s should require
@@ -3407,6 +3413,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"
@@ -4148,6 +4160,11 @@
<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"
@@ -4463,14 +4480,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/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 3b124da..5028150 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -528,10 +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>
- <!-- no translation found for face_authenticated_no_confirmation_required (4018680978348659031) -->
- <skip />
- <!-- no translation found for face_authenticated_confirmation_required (8778347003507633610) -->
- <skip />
+ <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>
@@ -568,8 +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>
- <!-- no translation found for face_error_user_canceled (8943921120862164539) -->
- <skip />
+ <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>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index 3cd1b50..217d631 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -528,10 +528,8 @@
</string-array>
<string name="biometric_not_recognized" msgid="5770511773560736082">"አልታወቀም"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"የጣት አሻራ ትክክለኛነት ተረጋግጧል"</string>
- <!-- no translation found for face_authenticated_no_confirmation_required (4018680978348659031) -->
- <skip />
- <!-- no translation found for face_authenticated_confirmation_required (8778347003507633610) -->
- <skip />
+ <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>
@@ -568,8 +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>
- <!-- no translation found for face_error_user_canceled (8943921120862164539) -->
- <skip />
+ <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>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index cf3b035..c11279a 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -540,10 +540,8 @@
</string-array>
<string name="biometric_not_recognized" msgid="5770511773560736082">"لم يتم التعرف عليها."</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"تم مصادقة بصمة الإصبع"</string>
- <!-- no translation found for face_authenticated_no_confirmation_required (4018680978348659031) -->
- <skip />
- <!-- no translation found for face_authenticated_confirmation_required (8778347003507633610) -->
- <skip />
+ <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>
@@ -580,8 +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>
- <!-- no translation found for face_error_user_canceled (8943921120862164539) -->
- <skip />
+ <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>
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index 9974867..5a0a7ec 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -312,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>
@@ -528,10 +528,8 @@
</string-array>
<string name="biometric_not_recognized" msgid="5770511773560736082">"চিনাক্ত কৰিব পৰা নাই"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"ফিংগাৰপ্ৰিণ্টৰ সত্যাপন কৰা হ’ল"</string>
- <!-- no translation found for face_authenticated_no_confirmation_required (4018680978348659031) -->
- <skip />
- <!-- no translation found for face_authenticated_confirmation_required (8778347003507633610) -->
- <skip />
+ <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>
@@ -568,8 +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>
- <!-- no translation found for face_error_user_canceled (8943921120862164539) -->
- <skip />
+ <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>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index 3236b52..af65bbb 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -528,10 +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>
- <!-- no translation found for face_authenticated_no_confirmation_required (4018680978348659031) -->
- <skip />
- <!-- no translation found for face_authenticated_confirmation_required (8778347003507633610) -->
- <skip />
+ <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>
@@ -568,8 +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>
- <!-- no translation found for face_error_user_canceled (8943921120862164539) -->
- <skip />
+ <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>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index 2bbf9b2..805082a 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -531,10 +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>
- <!-- no translation found for face_authenticated_no_confirmation_required (4018680978348659031) -->
- <skip />
- <!-- no translation found for face_authenticated_confirmation_required (8778347003507633610) -->
- <skip />
+ <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>
@@ -571,8 +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>
- <!-- no translation found for face_error_user_canceled (8943921120862164539) -->
- <skip />
+ <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>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index 0ec976f..7692220 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -534,10 +534,8 @@
</string-array>
<string name="biometric_not_recognized" msgid="5770511773560736082">"Не распазнана"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Адбітак пальца распазнаны"</string>
- <!-- no translation found for face_authenticated_no_confirmation_required (4018680978348659031) -->
- <skip />
- <!-- no translation found for face_authenticated_confirmation_required (8778347003507633610) -->
- <skip />
+ <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>
@@ -574,8 +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>
- <!-- no translation found for face_error_user_canceled (8943921120862164539) -->
- <skip />
+ <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>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 3af930f..33c4898 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -528,10 +528,8 @@
</string-array>
<string name="biometric_not_recognized" msgid="5770511773560736082">"Не е разпознато"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Отпечатъкът е удостоверен"</string>
- <!-- no translation found for face_authenticated_no_confirmation_required (4018680978348659031) -->
- <skip />
- <!-- no translation found for face_authenticated_confirmation_required (8778347003507633610) -->
- <skip />
+ <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>
@@ -568,8 +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>
- <!-- no translation found for face_error_user_canceled (8943921120862164539) -->
- <skip />
+ <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>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index 301bcc0..f0787b4 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -528,10 +528,8 @@
</string-array>
<string name="biometric_not_recognized" msgid="5770511773560736082">"স্বীকৃত নয়"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"আঙ্গুলের ছাপ যাচাই করা হয়েছে"</string>
- <!-- no translation found for face_authenticated_no_confirmation_required (4018680978348659031) -->
- <skip />
- <!-- no translation found for face_authenticated_confirmation_required (8778347003507633610) -->
- <skip />
+ <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>
@@ -568,8 +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>
- <!-- no translation found for face_error_user_canceled (8943921120862164539) -->
- <skip />
+ <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>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index e6fde5b..e210d90e 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -531,10 +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>
- <!-- no translation found for face_authenticated_no_confirmation_required (4018680978348659031) -->
- <skip />
- <!-- no translation found for face_authenticated_confirmation_required (8778347003507633610) -->
- <skip />
+ <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>
@@ -571,8 +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>
- <!-- no translation found for face_error_user_canceled (8943921120862164539) -->
- <skip />
+ <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>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 9318715..b1d4994 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -528,10 +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>
- <!-- no translation found for face_authenticated_no_confirmation_required (4018680978348659031) -->
- <skip />
- <!-- no translation found for face_authenticated_confirmation_required (8778347003507633610) -->
- <skip />
+ <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>
@@ -568,8 +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>
- <!-- no translation found for face_error_user_canceled (8943921120862164539) -->
- <skip />
+ <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>
@@ -814,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>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 45c57d0..a537d6d 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -534,10 +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>
- <!-- no translation found for face_authenticated_no_confirmation_required (4018680978348659031) -->
- <skip />
- <!-- no translation found for face_authenticated_confirmation_required (8778347003507633610) -->
- <skip />
+ <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>
@@ -574,8 +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>
- <!-- no translation found for face_error_user_canceled (8943921120862164539) -->
- <skip />
+ <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>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index f279f0b..28c08b1 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -528,10 +528,8 @@
</string-array>
<string name="biometric_not_recognized" msgid="5770511773560736082">"Ikke genkendt"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Fingeraftrykket blev godkendt"</string>
- <!-- no translation found for face_authenticated_no_confirmation_required (4018680978348659031) -->
- <skip />
- <!-- no translation found for face_authenticated_confirmation_required (8778347003507633610) -->
- <skip />
+ <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>
@@ -568,8 +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>
- <!-- no translation found for face_error_user_canceled (8943921120862164539) -->
- <skip />
+ <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>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 7ec4401..4a2efbd 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -528,10 +528,8 @@
</string-array>
<string name="biometric_not_recognized" msgid="5770511773560736082">"Nicht erkannt"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Fingerabdruck wurde authentifiziert"</string>
- <!-- no translation found for face_authenticated_no_confirmation_required (4018680978348659031) -->
- <skip />
- <!-- no translation found for face_authenticated_confirmation_required (8778347003507633610) -->
- <skip />
+ <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. Bitte versuche es noch einmal."</string>
@@ -568,8 +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>
- <!-- no translation found for face_error_user_canceled (8943921120862164539) -->
- <skip />
+ <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>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index f2309e8..04b9749 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -528,10 +528,8 @@
</string-array>
<string name="biometric_not_recognized" msgid="5770511773560736082">"Δεν αναγνωρίστηκε"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Η ταυτότητα του δακτυλικού αποτυπώματος ελέγχθηκε"</string>
- <!-- no translation found for face_authenticated_no_confirmation_required (4018680978348659031) -->
- <skip />
- <!-- no translation found for face_authenticated_confirmation_required (8778347003507633610) -->
- <skip />
+ <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>
@@ -568,8 +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>
- <!-- no translation found for face_error_user_canceled (8943921120862164539) -->
- <skip />
+ <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>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index 1eeb1c3..85923550b 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -528,10 +528,8 @@
</string-array>
<string name="biometric_not_recognized" msgid="5770511773560736082">"Not recognised"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Fingerprint authenticated"</string>
- <!-- no translation found for face_authenticated_no_confirmation_required (4018680978348659031) -->
- <skip />
- <!-- no translation found for face_authenticated_confirmation_required (8778347003507633610) -->
- <skip />
+ <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>
@@ -568,8 +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>
- <!-- no translation found for face_error_user_canceled (8943921120862164539) -->
- <skip />
+ <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>
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index 699e50f..1e075f8 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -528,10 +528,8 @@
</string-array>
<string name="biometric_not_recognized" msgid="5770511773560736082">"Not recognised"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Fingerprint authenticated"</string>
- <!-- no translation found for face_authenticated_no_confirmation_required (4018680978348659031) -->
- <skip />
- <!-- no translation found for face_authenticated_confirmation_required (8778347003507633610) -->
- <skip />
+ <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>
@@ -568,8 +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>
- <!-- no translation found for face_error_user_canceled (8943921120862164539) -->
- <skip />
+ <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>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index 1eeb1c3..85923550b 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -528,10 +528,8 @@
</string-array>
<string name="biometric_not_recognized" msgid="5770511773560736082">"Not recognised"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Fingerprint authenticated"</string>
- <!-- no translation found for face_authenticated_no_confirmation_required (4018680978348659031) -->
- <skip />
- <!-- no translation found for face_authenticated_confirmation_required (8778347003507633610) -->
- <skip />
+ <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>
@@ -568,8 +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>
- <!-- no translation found for face_error_user_canceled (8943921120862164539) -->
- <skip />
+ <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>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index 1eeb1c3..85923550b 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -528,10 +528,8 @@
</string-array>
<string name="biometric_not_recognized" msgid="5770511773560736082">"Not recognised"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Fingerprint authenticated"</string>
- <!-- no translation found for face_authenticated_no_confirmation_required (4018680978348659031) -->
- <skip />
- <!-- no translation found for face_authenticated_confirmation_required (8778347003507633610) -->
- <skip />
+ <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>
@@ -568,8 +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>
- <!-- no translation found for face_error_user_canceled (8943921120862164539) -->
- <skip />
+ <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>
diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml
index 63726f8..9138098 100644
--- a/core/res/res/values-en-rXC/strings.xml
+++ b/core/res/res/values-en-rXC/strings.xml
@@ -528,10 +528,8 @@
</string-array>
<string name="biometric_not_recognized" msgid="5770511773560736082">"Not recognized"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Fingerprint authenticated"</string>
- <!-- no translation found for face_authenticated_no_confirmation_required (4018680978348659031) -->
- <skip />
- <!-- no translation found for face_authenticated_confirmation_required (8778347003507633610) -->
- <skip />
+ <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>
@@ -568,8 +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>
- <!-- no translation found for face_error_user_canceled (8943921120862164539) -->
- <skip />
+ <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>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 6a49e03..6176b7b 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -528,10 +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>
- <!-- no translation found for face_authenticated_no_confirmation_required (4018680978348659031) -->
- <skip />
- <!-- no translation found for face_authenticated_confirmation_required (8778347003507633610) -->
- <skip />
+ <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>
@@ -568,8 +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>
- <!-- no translation found for face_error_user_canceled (8943921120862164539) -->
- <skip />
+ <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>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index b62f4c9b..dd766f7 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -528,10 +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>
- <!-- no translation found for face_authenticated_no_confirmation_required (4018680978348659031) -->
- <skip />
- <!-- no translation found for face_authenticated_confirmation_required (8778347003507633610) -->
- <skip />
+ <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>
@@ -568,8 +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>
- <!-- no translation found for face_error_user_canceled (8943921120862164539) -->
- <skip />
+ <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>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index 8f011f8..650fcb6 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -528,10 +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>
- <!-- no translation found for face_authenticated_no_confirmation_required (4018680978348659031) -->
- <skip />
- <!-- no translation found for face_authenticated_confirmation_required (8778347003507633610) -->
- <skip />
+ <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>
@@ -568,8 +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>
- <!-- no translation found for face_error_user_canceled (8943921120862164539) -->
- <skip />
+ <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>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index a070cc5..6a6fdbf 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -528,10 +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>
- <!-- no translation found for face_authenticated_no_confirmation_required (4018680978348659031) -->
- <skip />
- <!-- no translation found for face_authenticated_confirmation_required (8778347003507633610) -->
- <skip />
+ <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>
@@ -568,8 +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>
- <!-- no translation found for face_error_user_canceled (8943921120862164539) -->
- <skip />
+ <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>
@@ -1139,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>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 3101b72..2310f9e 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -528,10 +528,8 @@
</string-array>
<string name="biometric_not_recognized" msgid="5770511773560736082">"شناسایی نشد"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"اثر انگشت احراز هویت شد"</string>
- <!-- no translation found for face_authenticated_no_confirmation_required (4018680978348659031) -->
- <skip />
- <!-- no translation found for face_authenticated_confirmation_required (8778347003507633610) -->
- <skip />
+ <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>
@@ -568,8 +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>
- <!-- no translation found for face_error_user_canceled (8943921120862164539) -->
- <skip />
+ <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>
@@ -900,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>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 346992f..4f2531c 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -528,10 +528,8 @@
</string-array>
<string name="biometric_not_recognized" msgid="5770511773560736082">"Ei tunnistettu"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Sormenjälki tunnistettu"</string>
- <!-- no translation found for face_authenticated_no_confirmation_required (4018680978348659031) -->
- <skip />
- <!-- no translation found for face_authenticated_confirmation_required (8778347003507633610) -->
- <skip />
+ <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>
@@ -568,8 +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>
- <!-- no translation found for face_error_user_canceled (8943921120862164539) -->
- <skip />
+ <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>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index 16fd62a..39889ec 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -528,10 +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>
- <!-- no translation found for face_authenticated_no_confirmation_required (4018680978348659031) -->
- <skip />
- <!-- no translation found for face_authenticated_confirmation_required (8778347003507633610) -->
- <skip />
+ <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>
@@ -568,8 +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>
- <!-- no translation found for face_error_user_canceled (8943921120862164539) -->
- <skip />
+ <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>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 76eed1f..9759415 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -528,10 +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>
- <!-- no translation found for face_authenticated_no_confirmation_required (4018680978348659031) -->
- <skip />
- <!-- no translation found for face_authenticated_confirmation_required (8778347003507633610) -->
- <skip />
+ <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>
@@ -568,8 +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>
- <!-- no translation found for face_error_user_canceled (8943921120862164539) -->
- <skip />
+ <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>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index f644919..318dd3e 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -528,10 +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>
- <!-- no translation found for face_authenticated_no_confirmation_required (4018680978348659031) -->
- <skip />
- <!-- no translation found for face_authenticated_confirmation_required (8778347003507633610) -->
- <skip />
+ <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>
@@ -568,8 +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>
- <!-- no translation found for face_error_user_canceled (8943921120862164539) -->
- <skip />
+ <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>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index 4ea553c..13090c6 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -528,10 +528,8 @@
</string-array>
<string name="biometric_not_recognized" msgid="5770511773560736082">"ઓળખાયેલ નથી"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"ફિંગરપ્રિન્ટ પ્રમાણિત કરી"</string>
- <!-- no translation found for face_authenticated_no_confirmation_required (4018680978348659031) -->
- <skip />
- <!-- no translation found for face_authenticated_confirmation_required (8778347003507633610) -->
- <skip />
+ <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>
@@ -568,8 +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>
- <!-- no translation found for face_error_user_canceled (8943921120862164539) -->
- <skip />
+ <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>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index c24b0ae..bebd489 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -530,10 +530,8 @@
<!-- no translation found for biometric_not_recognized (5770511773560736082) -->
<skip />
<string name="fingerprint_authenticated" msgid="5309333983002526448">"फ़िंगरप्रिंट की पुष्टि हो गई"</string>
- <!-- no translation found for face_authenticated_no_confirmation_required (4018680978348659031) -->
- <skip />
- <!-- no translation found for face_authenticated_confirmation_required (8778347003507633610) -->
- <skip />
+ <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>
@@ -570,8 +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>
- <!-- no translation found for face_error_user_canceled (8943921120862164539) -->
- <skip />
+ <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>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 6a9d307..839d423 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -531,10 +531,8 @@
</string-array>
<string name="biometric_not_recognized" msgid="5770511773560736082">"Nije prepoznato"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Autentificirano otiskom prsta"</string>
- <!-- no translation found for face_authenticated_no_confirmation_required (4018680978348659031) -->
- <skip />
- <!-- no translation found for face_authenticated_confirmation_required (8778347003507633610) -->
- <skip />
+ <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>
@@ -571,8 +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>
- <!-- no translation found for face_error_user_canceled (8943921120862164539) -->
- <skip />
+ <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>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 474c73a..bc54bfa 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -528,10 +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>
- <!-- no translation found for face_authenticated_no_confirmation_required (4018680978348659031) -->
- <skip />
- <!-- no translation found for face_authenticated_confirmation_required (8778347003507633610) -->
- <skip />
+ <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>
@@ -568,8 +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>
- <!-- no translation found for face_error_user_canceled (8943921120862164539) -->
- <skip />
+ <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>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index b9e6c62..4a828fa 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -528,10 +528,8 @@
</string-array>
<string name="biometric_not_recognized" msgid="5770511773560736082">"Չհաջողվեց ճանաչել"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Մատնահետքը նույնականացվեց"</string>
- <!-- no translation found for face_authenticated_no_confirmation_required (4018680978348659031) -->
- <skip />
- <!-- no translation found for face_authenticated_confirmation_required (8778347003507633610) -->
- <skip />
+ <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>
@@ -568,8 +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>
- <!-- no translation found for face_error_user_canceled (8943921120862164539) -->
- <skip />
+ <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>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 8298a18..167082d 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -528,10 +528,8 @@
</string-array>
<string name="biometric_not_recognized" msgid="5770511773560736082">"Tidak dikenali"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Sidik jari diautentikasi"</string>
- <!-- no translation found for face_authenticated_no_confirmation_required (4018680978348659031) -->
- <skip />
- <!-- no translation found for face_authenticated_confirmation_required (8778347003507633610) -->
- <skip />
+ <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>
@@ -568,8 +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>
- <!-- no translation found for face_error_user_canceled (8943921120862164539) -->
- <skip />
+ <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>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index 2649a1a..0c7e6b8 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -528,10 +528,8 @@
</string-array>
<string name="biometric_not_recognized" msgid="5770511773560736082">"Þekktist ekki"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Fingrafar staðfest"</string>
- <!-- no translation found for face_authenticated_no_confirmation_required (4018680978348659031) -->
- <skip />
- <!-- no translation found for face_authenticated_confirmation_required (8778347003507633610) -->
- <skip />
+ <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>
@@ -568,8 +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>
- <!-- no translation found for face_error_user_canceled (8943921120862164539) -->
- <skip />
+ <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>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 5c28097..a6d5d23 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -528,10 +528,8 @@
</string-array>
<string name="biometric_not_recognized" msgid="5770511773560736082">"Non riconosciuto"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Impronta digitale autenticata"</string>
- <!-- no translation found for face_authenticated_no_confirmation_required (4018680978348659031) -->
- <skip />
- <!-- no translation found for face_authenticated_confirmation_required (8778347003507633610) -->
- <skip />
+ <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>
@@ -568,8 +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>
- <!-- no translation found for face_error_user_canceled (8943921120862164539) -->
- <skip />
+ <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>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 5dd1617..48933f9 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -534,10 +534,8 @@
</string-array>
<string name="biometric_not_recognized" msgid="5770511773560736082">"לא זוהתה"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"טביעת האצבע אומתה"</string>
- <!-- no translation found for face_authenticated_no_confirmation_required (4018680978348659031) -->
- <skip />
- <!-- no translation found for face_authenticated_confirmation_required (8778347003507633610) -->
- <skip />
+ <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>
@@ -574,8 +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>
- <!-- no translation found for face_error_user_canceled (8943921120862164539) -->
- <skip />
+ <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>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 7b53ed4..994039c 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -528,10 +528,8 @@
</string-array>
<string name="biometric_not_recognized" msgid="5770511773560736082">"認識されませんでした"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"指紋認証を完了しました"</string>
- <!-- no translation found for face_authenticated_no_confirmation_required (4018680978348659031) -->
- <skip />
- <!-- no translation found for face_authenticated_confirmation_required (8778347003507633610) -->
- <skip />
+ <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>
@@ -568,8 +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>
- <!-- no translation found for face_error_user_canceled (8943921120862164539) -->
- <skip />
+ <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>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index 7226296..a9ce748 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -528,10 +528,8 @@
</string-array>
<string name="biometric_not_recognized" msgid="5770511773560736082">"არ არის ამოცნობილი"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"თითის ანაბეჭდი ავტორიზებულია"</string>
- <!-- no translation found for face_authenticated_no_confirmation_required (4018680978348659031) -->
- <skip />
- <!-- no translation found for face_authenticated_confirmation_required (8778347003507633610) -->
- <skip />
+ <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>
@@ -568,8 +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>
- <!-- no translation found for face_error_user_canceled (8943921120862164539) -->
- <skip />
+ <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>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index 457933e..a8b527b 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -528,10 +528,8 @@
</string-array>
<string name="biometric_not_recognized" msgid="5770511773560736082">"Танылмады"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Саусақ ізі аутентификацияланды"</string>
- <!-- no translation found for face_authenticated_no_confirmation_required (4018680978348659031) -->
- <skip />
- <!-- no translation found for face_authenticated_confirmation_required (8778347003507633610) -->
- <skip />
+ <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>
@@ -568,8 +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>
- <!-- no translation found for face_error_user_canceled (8943921120862164539) -->
- <skip />
+ <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>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index b3a3a01..2620ce4 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -528,10 +528,8 @@
</string-array>
<string name="biometric_not_recognized" msgid="5770511773560736082">"មិនអាចសម្គាល់បានទេ"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"បានផ្ទៀងផ្ទាត់ស្នាមម្រាមដៃ"</string>
- <!-- no translation found for face_authenticated_no_confirmation_required (4018680978348659031) -->
- <skip />
- <!-- no translation found for face_authenticated_confirmation_required (8778347003507633610) -->
- <skip />
+ <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>
@@ -568,8 +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>
- <!-- no translation found for face_error_user_canceled (8943921120862164539) -->
- <skip />
+ <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>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index 2dc0bb1..1b83ec2 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -528,10 +528,8 @@
</string-array>
<string name="biometric_not_recognized" msgid="5770511773560736082">"ಗುರುತಿಸಲಾಗಿಲ್ಲ"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"ಫಿಂಗರ್ಪ್ರಿಂಟ್ ಅನ್ನು ಪ್ರಮಾಣೀಕರಣ ಮಾಡಲಾಗಿದೆ"</string>
- <!-- no translation found for face_authenticated_no_confirmation_required (4018680978348659031) -->
- <skip />
- <!-- no translation found for face_authenticated_confirmation_required (8778347003507633610) -->
- <skip />
+ <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>
@@ -568,8 +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>
- <!-- no translation found for face_error_user_canceled (8943921120862164539) -->
- <skip />
+ <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>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index abed24c..6223dab 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -528,10 +528,8 @@
</string-array>
<string name="biometric_not_recognized" msgid="5770511773560736082">"인식할 수 없음"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"지문이 인증됨"</string>
- <!-- no translation found for face_authenticated_no_confirmation_required (4018680978348659031) -->
- <skip />
- <!-- no translation found for face_authenticated_confirmation_required (8778347003507633610) -->
- <skip />
+ <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>
@@ -568,8 +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>
- <!-- no translation found for face_error_user_canceled (8943921120862164539) -->
- <skip />
+ <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>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index fb8a816..f49746f 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -528,10 +528,8 @@
</string-array>
<string name="biometric_not_recognized" msgid="5770511773560736082">"Таанылган жок"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Манжа изинин аныктыгы текшерилди"</string>
- <!-- no translation found for face_authenticated_no_confirmation_required (4018680978348659031) -->
- <skip />
- <!-- no translation found for face_authenticated_confirmation_required (8778347003507633610) -->
- <skip />
+ <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>
@@ -568,8 +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>
- <!-- no translation found for face_error_user_canceled (8943921120862164539) -->
- <skip />
+ <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>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index 8b6a052..9fc6033 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -528,10 +528,8 @@
</string-array>
<string name="biometric_not_recognized" msgid="5770511773560736082">"ບໍ່ຮັບຮູ້"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"ພິສູດຢືນຢັນລາຍນິ້ວມືແລ້ວ"</string>
- <!-- no translation found for face_authenticated_no_confirmation_required (4018680978348659031) -->
- <skip />
- <!-- no translation found for face_authenticated_confirmation_required (8778347003507633610) -->
- <skip />
+ <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>
@@ -568,8 +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>
- <!-- no translation found for face_error_user_canceled (8943921120862164539) -->
- <skip />
+ <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>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index 7fc1c98..d6869c1 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -534,10 +534,8 @@
</string-array>
<string name="biometric_not_recognized" msgid="5770511773560736082">"Neatpažinta"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Kontrolinis kodas autentifikuotas"</string>
- <!-- no translation found for face_authenticated_no_confirmation_required (4018680978348659031) -->
- <skip />
- <!-- no translation found for face_authenticated_confirmation_required (8778347003507633610) -->
- <skip />
+ <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>
@@ -574,8 +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>
- <!-- no translation found for face_error_user_canceled (8943921120862164539) -->
- <skip />
+ <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>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 60a32de..156a1a9 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -531,10 +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>
- <!-- no translation found for face_authenticated_no_confirmation_required (4018680978348659031) -->
- <skip />
- <!-- no translation found for face_authenticated_confirmation_required (8778347003507633610) -->
- <skip />
+ <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>
@@ -571,8 +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>
- <!-- no translation found for face_error_user_canceled (8943921120862164539) -->
- <skip />
+ <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>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index 4ae6f31..8bca75e 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -528,10 +528,8 @@
</string-array>
<string name="biometric_not_recognized" msgid="5770511773560736082">"Непознат"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Отпечатокот е проверен"</string>
- <!-- no translation found for face_authenticated_no_confirmation_required (4018680978348659031) -->
- <skip />
- <!-- no translation found for face_authenticated_confirmation_required (8778347003507633610) -->
- <skip />
+ <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>
@@ -568,8 +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>
- <!-- no translation found for face_error_user_canceled (8943921120862164539) -->
- <skip />
+ <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>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index 2cd83cc..1efdc72 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -528,10 +528,8 @@
</string-array>
<string name="biometric_not_recognized" msgid="5770511773560736082">"തിരിച്ചറിഞ്ഞില്ല"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"ഫിംഗർപ്രിന്റ് പരിശോധിച്ചുറപ്പിച്ചു"</string>
- <!-- no translation found for face_authenticated_no_confirmation_required (4018680978348659031) -->
- <skip />
- <!-- no translation found for face_authenticated_confirmation_required (8778347003507633610) -->
- <skip />
+ <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>
@@ -568,8 +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>
- <!-- no translation found for face_error_user_canceled (8943921120862164539) -->
- <skip />
+ <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>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index 35936ee..86ae178 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -528,10 +528,8 @@
</string-array>
<string name="biometric_not_recognized" msgid="5770511773560736082">"Таниагүй"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Хурууны хээг нотолсон"</string>
- <!-- no translation found for face_authenticated_no_confirmation_required (4018680978348659031) -->
- <skip />
- <!-- no translation found for face_authenticated_confirmation_required (8778347003507633610) -->
- <skip />
+ <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>
@@ -568,8 +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>
- <!-- no translation found for face_error_user_canceled (8943921120862164539) -->
- <skip />
+ <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>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index ec96ec1..70ed012 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -528,10 +528,8 @@
</string-array>
<string name="biometric_not_recognized" msgid="5770511773560736082">"ओळखले नाही"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"फिंगरप्रिंट ऑथेंटिकेट केली आहे"</string>
- <!-- no translation found for face_authenticated_no_confirmation_required (4018680978348659031) -->
- <skip />
- <!-- no translation found for face_authenticated_confirmation_required (8778347003507633610) -->
- <skip />
+ <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>
@@ -568,8 +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>
- <!-- no translation found for face_error_user_canceled (8943921120862164539) -->
- <skip />
+ <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>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index 7de8dc1..208fed6 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -528,10 +528,8 @@
</string-array>
<string name="biometric_not_recognized" msgid="5770511773560736082">"Tidak dikenali"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Cap jari disahkan"</string>
- <!-- no translation found for face_authenticated_no_confirmation_required (4018680978348659031) -->
- <skip />
- <!-- no translation found for face_authenticated_confirmation_required (8778347003507633610) -->
- <skip />
+ <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>
@@ -568,8 +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>
- <!-- no translation found for face_error_user_canceled (8943921120862164539) -->
- <skip />
+ <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>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index 9f107f8..71dd4b6 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -528,10 +528,8 @@
</string-array>
<string name="biometric_not_recognized" msgid="5770511773560736082">"မသိပါ"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"လက်ဗွေကို အထောက်အထား စိစစ်ပြီးပါပြီ"</string>
- <!-- no translation found for face_authenticated_no_confirmation_required (4018680978348659031) -->
- <skip />
- <!-- no translation found for face_authenticated_confirmation_required (8778347003507633610) -->
- <skip />
+ <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>
@@ -568,8 +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>
- <!-- no translation found for face_error_user_canceled (8943921120862164539) -->
- <skip />
+ <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>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index d315aca..28e7d33 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -528,10 +528,8 @@
</string-array>
<string name="biometric_not_recognized" msgid="5770511773560736082">"Ikke gjenkjent"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Fingeravtrykket er godkjent"</string>
- <!-- no translation found for face_authenticated_no_confirmation_required (4018680978348659031) -->
- <skip />
- <!-- no translation found for face_authenticated_confirmation_required (8778347003507633610) -->
- <skip />
+ <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>
@@ -568,8 +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>
- <!-- no translation found for face_error_user_canceled (8943921120862164539) -->
- <skip />
+ <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>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index c67a871..875f87d 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -528,10 +528,8 @@
</string-array>
<string name="biometric_not_recognized" msgid="5770511773560736082">"पहिचान भएन"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"फिंगरप्रिन्ट प्रमाणीकरण गरियो"</string>
- <!-- no translation found for face_authenticated_no_confirmation_required (4018680978348659031) -->
- <skip />
- <!-- no translation found for face_authenticated_confirmation_required (8778347003507633610) -->
- <skip />
+ <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>
@@ -568,8 +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>
- <!-- no translation found for face_error_user_canceled (8943921120862164539) -->
- <skip />
+ <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>
diff --git a/core/res/res/values-night/values.xml b/core/res/res/values-night/values.xml
index 45cf0f0..a2ad3b9 100644
--- a/core/res/res/values-night/values.xml
+++ b/core/res/res/values-night/values.xml
@@ -32,6 +32,8 @@
<item name="panelColorBackground">@color/material_grey_800</item>
</style>
+ <style name="Theme.DeviceDefault.QuickSettings.Dialog" parent="Theme.DeviceDefault.Dialog" />
+
<style name="TextAppearance.Material.Notification">
<item name="textColor">@color/notification_secondary_text_color_dark</item>
<item name="textSize">@dimen/notification_text_size</item>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 345d9d4..b95ba19 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -528,10 +528,8 @@
</string-array>
<string name="biometric_not_recognized" msgid="5770511773560736082">"Niet herkend"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Vingerafdruk geverifieerd"</string>
- <!-- no translation found for face_authenticated_no_confirmation_required (4018680978348659031) -->
- <skip />
- <!-- no translation found for face_authenticated_confirmation_required (8778347003507633610) -->
- <skip />
+ <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>
@@ -568,8 +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>
- <!-- no translation found for face_error_user_canceled (8943921120862164539) -->
- <skip />
+ <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>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index 3e4260f..9d3fa8e 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -528,10 +528,8 @@
</string-array>
<string name="biometric_not_recognized" msgid="5770511773560736082">"ଚିହ୍ନଟ ହେଲାନାହିଁ"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"ଆଙ୍ଗୁଠି ଚିହ୍ନ ପ୍ରମାଣୀକୃତ ହେଲା"</string>
- <!-- no translation found for face_authenticated_no_confirmation_required (4018680978348659031) -->
- <skip />
- <!-- no translation found for face_authenticated_confirmation_required (8778347003507633610) -->
- <skip />
+ <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>
@@ -568,8 +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>
- <!-- no translation found for face_error_user_canceled (8943921120862164539) -->
- <skip />
+ <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>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index bdcf2fe..6e615a8 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -528,10 +528,8 @@
</string-array>
<string name="biometric_not_recognized" msgid="5770511773560736082">"ਪਛਾਣ ਨਹੀਂ ਹੋਈ"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਪ੍ਰਮਾਣਿਤ ਹੋਇਆ"</string>
- <!-- no translation found for face_authenticated_no_confirmation_required (4018680978348659031) -->
- <skip />
- <!-- no translation found for face_authenticated_confirmation_required (8778347003507633610) -->
- <skip />
+ <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>
@@ -568,8 +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>
- <!-- no translation found for face_error_user_canceled (8943921120862164539) -->
- <skip />
+ <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>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 3e08d50..81a869a 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -534,10 +534,8 @@
</string-array>
<string name="biometric_not_recognized" msgid="5770511773560736082">"Nie rozpoznano"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Uwierzytelniono odciskiem palca"</string>
- <!-- no translation found for face_authenticated_no_confirmation_required (4018680978348659031) -->
- <skip />
- <!-- no translation found for face_authenticated_confirmation_required (8778347003507633610) -->
- <skip />
+ <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>
@@ -574,8 +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>
- <!-- no translation found for face_error_user_canceled (8943921120862164539) -->
- <skip />
+ <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>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index d604aab..5ac7c2e5 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -528,10 +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>
- <!-- no translation found for face_authenticated_no_confirmation_required (4018680978348659031) -->
- <skip />
- <!-- no translation found for face_authenticated_confirmation_required (8778347003507633610) -->
- <skip />
+ <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>
@@ -568,8 +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>
- <!-- no translation found for face_error_user_canceled (8943921120862164539) -->
- <skip />
+ <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>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index cde63d5..52f067f 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -423,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>
@@ -528,10 +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>
- <!-- no translation found for face_authenticated_no_confirmation_required (4018680978348659031) -->
- <skip />
- <!-- no translation found for face_authenticated_confirmation_required (8778347003507633610) -->
- <skip />
+ <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>
@@ -568,8 +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>
- <!-- no translation found for face_error_user_canceled (8943921120862164539) -->
- <skip />
+ <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>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index d604aab..5ac7c2e5 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -528,10 +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>
- <!-- no translation found for face_authenticated_no_confirmation_required (4018680978348659031) -->
- <skip />
- <!-- no translation found for face_authenticated_confirmation_required (8778347003507633610) -->
- <skip />
+ <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>
@@ -568,8 +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>
- <!-- no translation found for face_error_user_canceled (8943921120862164539) -->
- <skip />
+ <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>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index add43b8..0c945f7 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -531,10 +531,8 @@
</string-array>
<string name="biometric_not_recognized" msgid="5770511773560736082">"Nu este recunoscut"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Amprentă autentificată"</string>
- <!-- no translation found for face_authenticated_no_confirmation_required (4018680978348659031) -->
- <skip />
- <!-- no translation found for face_authenticated_confirmation_required (8778347003507633610) -->
- <skip />
+ <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>
@@ -571,8 +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>
- <!-- no translation found for face_error_user_canceled (8943921120862164539) -->
- <skip />
+ <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>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 3a4ed82..fd70e9c 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -534,10 +534,8 @@
</string-array>
<string name="biometric_not_recognized" msgid="5770511773560736082">"Не распознано"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Отпечаток пальца проверен"</string>
- <!-- no translation found for face_authenticated_no_confirmation_required (4018680978348659031) -->
- <skip />
- <!-- no translation found for face_authenticated_confirmation_required (8778347003507633610) -->
- <skip />
+ <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>
@@ -574,8 +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>
- <!-- no translation found for face_error_user_canceled (8943921120862164539) -->
- <skip />
+ <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>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index 17ead66..3bd8ceb 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -528,10 +528,8 @@
</string-array>
<string name="biometric_not_recognized" msgid="5770511773560736082">"හඳුනා නොගන්නා ලදී"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"ඇඟිලි සලකුණ සත්යාපනය කරන ලදී"</string>
- <!-- no translation found for face_authenticated_no_confirmation_required (4018680978348659031) -->
- <skip />
- <!-- no translation found for face_authenticated_confirmation_required (8778347003507633610) -->
- <skip />
+ <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>
@@ -568,8 +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>
- <!-- no translation found for face_error_user_canceled (8943921120862164539) -->
- <skip />
+ <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>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index cb44b74..c0981ce 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -534,10 +534,8 @@
</string-array>
<string name="biometric_not_recognized" msgid="5770511773560736082">"Nerozpoznané"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Odtlačok bol overený"</string>
- <!-- no translation found for face_authenticated_no_confirmation_required (4018680978348659031) -->
- <skip />
- <!-- no translation found for face_authenticated_confirmation_required (8778347003507633610) -->
- <skip />
+ <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>
@@ -574,8 +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>
- <!-- no translation found for face_error_user_canceled (8943921120862164539) -->
- <skip />
+ <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>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 039bbaa..261e6ed 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -534,10 +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>
- <!-- no translation found for face_authenticated_no_confirmation_required (4018680978348659031) -->
- <skip />
- <!-- no translation found for face_authenticated_confirmation_required (8778347003507633610) -->
- <skip />
+ <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>
@@ -574,8 +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>
- <!-- no translation found for face_error_user_canceled (8943921120862164539) -->
- <skip />
+ <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>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index ee0960b..4b6cb09 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -528,10 +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>
- <!-- no translation found for face_authenticated_no_confirmation_required (4018680978348659031) -->
- <skip />
- <!-- no translation found for face_authenticated_confirmation_required (8778347003507633610) -->
- <skip />
+ <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>
@@ -568,8 +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>
- <!-- no translation found for face_error_user_canceled (8943921120862164539) -->
- <skip />
+ <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>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 6ff3a78..449fdfc 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -531,10 +531,8 @@
</string-array>
<string name="biometric_not_recognized" msgid="5770511773560736082">"Није препознато"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Отисак прста је потврђен"</string>
- <!-- no translation found for face_authenticated_no_confirmation_required (4018680978348659031) -->
- <skip />
- <!-- no translation found for face_authenticated_confirmation_required (8778347003507633610) -->
- <skip />
+ <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>
@@ -571,8 +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>
- <!-- no translation found for face_error_user_canceled (8943921120862164539) -->
- <skip />
+ <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>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 20969d3..9f46514 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -528,10 +528,8 @@
</string-array>
<string name="biometric_not_recognized" msgid="5770511773560736082">"Identifierades inte"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Fingeravtrycket har autentiserats"</string>
- <!-- no translation found for face_authenticated_no_confirmation_required (4018680978348659031) -->
- <skip />
- <!-- no translation found for face_authenticated_confirmation_required (8778347003507633610) -->
- <skip />
+ <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>
@@ -568,8 +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>
- <!-- no translation found for face_error_user_canceled (8943921120862164539) -->
- <skip />
+ <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>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index ce81bc7..b0858dc 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -130,9 +130,9 @@
</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>Kupiga simu Kupitia Wi-Fi"</string>
+ <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">"<xliff:g id="SPN">%s</xliff:g> 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>
@@ -528,10 +528,8 @@
</string-array>
<string name="biometric_not_recognized" msgid="5770511773560736082">"Hayatambuliki"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Imethibitisha alama ya kidole"</string>
- <!-- no translation found for face_authenticated_no_confirmation_required (4018680978348659031) -->
- <skip />
- <!-- no translation found for face_authenticated_confirmation_required (8778347003507633610) -->
- <skip />
+ <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>
@@ -568,8 +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>
- <!-- no translation found for face_error_user_canceled (8943921120862164539) -->
- <skip />
+ <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>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index 9683e4c..b84d68e 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -528,10 +528,8 @@
</string-array>
<string name="biometric_not_recognized" msgid="5770511773560736082">"அடையாளங்காணபடவில்லை"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"கைரேகை அங்கீகரிக்கப்பட்டது"</string>
- <!-- no translation found for face_authenticated_no_confirmation_required (4018680978348659031) -->
- <skip />
- <!-- no translation found for face_authenticated_confirmation_required (8778347003507633610) -->
- <skip />
+ <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>
@@ -568,8 +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>
- <!-- no translation found for face_error_user_canceled (8943921120862164539) -->
- <skip />
+ <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>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index 9757720..a5430b6 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -528,10 +528,8 @@
</string-array>
<string name="biometric_not_recognized" msgid="5770511773560736082">"గుర్తించలేదు"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"వేలిముద్ర ప్రమాణీకరించబడింది"</string>
- <!-- no translation found for face_authenticated_no_confirmation_required (4018680978348659031) -->
- <skip />
- <!-- no translation found for face_authenticated_confirmation_required (8778347003507633610) -->
- <skip />
+ <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>
@@ -568,8 +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>
- <!-- no translation found for face_error_user_canceled (8943921120862164539) -->
- <skip />
+ <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>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 89202ca..0b7e031 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -528,10 +528,8 @@
</string-array>
<string name="biometric_not_recognized" msgid="5770511773560736082">"ไม่รู้จัก"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"ตรวจสอบสิทธิ์ลายนิ้วมือแล้ว"</string>
- <!-- no translation found for face_authenticated_no_confirmation_required (4018680978348659031) -->
- <skip />
- <!-- no translation found for face_authenticated_confirmation_required (8778347003507633610) -->
- <skip />
+ <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>
@@ -568,8 +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>
- <!-- no translation found for face_error_user_canceled (8943921120862164539) -->
- <skip />
+ <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>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 983b53f..e21f59c 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -528,10 +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>
- <!-- no translation found for face_authenticated_no_confirmation_required (4018680978348659031) -->
- <skip />
- <!-- no translation found for face_authenticated_confirmation_required (8778347003507633610) -->
- <skip />
+ <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>
@@ -568,8 +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>
- <!-- no translation found for face_error_user_canceled (8943921120862164539) -->
- <skip />
+ <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>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 0315276..912db20 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -528,10 +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>
- <!-- no translation found for face_authenticated_no_confirmation_required (4018680978348659031) -->
- <skip />
- <!-- no translation found for face_authenticated_confirmation_required (8778347003507633610) -->
- <skip />
+ <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>
@@ -568,8 +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>
- <!-- no translation found for face_error_user_canceled (8943921120862164539) -->
- <skip />
+ <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>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 88e2475..f84e3e2 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -534,10 +534,8 @@
</string-array>
<string name="biometric_not_recognized" msgid="5770511773560736082">"Не розпізнано"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Відбиток автентифіковано"</string>
- <!-- no translation found for face_authenticated_no_confirmation_required (4018680978348659031) -->
- <skip />
- <!-- no translation found for face_authenticated_confirmation_required (8778347003507633610) -->
- <skip />
+ <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>
@@ -574,8 +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>
- <!-- no translation found for face_error_user_canceled (8943921120862164539) -->
- <skip />
+ <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>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index 6e98ebb..dc801cd 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -528,10 +528,8 @@
</string-array>
<string name="biometric_not_recognized" msgid="5770511773560736082">"تسلیم شدہ نہیں ہے"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"فنگر پرنٹ کی تصدیق ہو گئی"</string>
- <!-- no translation found for face_authenticated_no_confirmation_required (4018680978348659031) -->
- <skip />
- <!-- no translation found for face_authenticated_confirmation_required (8778347003507633610) -->
- <skip />
+ <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>
@@ -568,8 +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>
- <!-- no translation found for face_error_user_canceled (8943921120862164539) -->
- <skip />
+ <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>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index 15671d1..e51b810 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -528,10 +528,8 @@
</string-array>
<string name="biometric_not_recognized" msgid="5770511773560736082">"Aniqlanmadi"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Barmoq izi tekshirildi"</string>
- <!-- no translation found for face_authenticated_no_confirmation_required (4018680978348659031) -->
- <skip />
- <!-- no translation found for face_authenticated_confirmation_required (8778347003507633610) -->
- <skip />
+ <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>
@@ -568,8 +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>
- <!-- no translation found for face_error_user_canceled (8943921120862164539) -->
- <skip />
+ <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>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 00ebbbc..4d8a20b 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -528,10 +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>
- <!-- no translation found for face_authenticated_no_confirmation_required (4018680978348659031) -->
- <skip />
- <!-- no translation found for face_authenticated_confirmation_required (8778347003507633610) -->
- <skip />
+ <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>
@@ -568,8 +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>
- <!-- no translation found for face_error_user_canceled (8943921120862164539) -->
- <skip />
+ <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>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index e60afa7..609f9bcf 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -528,10 +528,8 @@
</string-array>
<string name="biometric_not_recognized" msgid="5770511773560736082">"无法识别"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"已验证指纹"</string>
- <!-- no translation found for face_authenticated_no_confirmation_required (4018680978348659031) -->
- <skip />
- <!-- no translation found for face_authenticated_confirmation_required (8778347003507633610) -->
- <skip />
+ <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>
@@ -568,8 +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>
- <!-- no translation found for face_error_user_canceled (8943921120862164539) -->
- <skip />
+ <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>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index cb93c61..2e3c7d7 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -528,10 +528,8 @@
</string-array>
<string name="biometric_not_recognized" msgid="5770511773560736082">"未能識別"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"驗證咗指紋"</string>
- <!-- no translation found for face_authenticated_no_confirmation_required (4018680978348659031) -->
- <skip />
- <!-- no translation found for face_authenticated_confirmation_required (8778347003507633610) -->
- <skip />
+ <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>
@@ -568,8 +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>
- <!-- no translation found for face_error_user_canceled (8943921120862164539) -->
- <skip />
+ <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>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 9c3d75b..9149f47 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -528,10 +528,8 @@
</string-array>
<string name="biometric_not_recognized" msgid="5770511773560736082">"無法辨識"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"指紋驗證成功"</string>
- <!-- no translation found for face_authenticated_no_confirmation_required (4018680978348659031) -->
- <skip />
- <!-- no translation found for face_authenticated_confirmation_required (8778347003507633610) -->
- <skip />
+ <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>
@@ -568,8 +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>
- <!-- no translation found for face_error_user_canceled (8943921120862164539) -->
- <skip />
+ <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>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index 07c05aa..75709ca 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -528,10 +528,8 @@
</string-array>
<string name="biometric_not_recognized" msgid="5770511773560736082">"Akwaziwa"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Isingxivizo somunwe sigunyaziwe"</string>
- <!-- no translation found for face_authenticated_no_confirmation_required (4018680978348659031) -->
- <skip />
- <!-- no translation found for face_authenticated_confirmation_required (8778347003507633610) -->
- <skip />
+ <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>
@@ -568,8 +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>
- <!-- no translation found for face_error_user_canceled (8943921120862164539) -->
- <skip />
+ <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>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 65b8807..cdb65ed 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
@@ -4184,9 +4184,9 @@
row is full. The rowCount attribute may be used similarly in the vertical case.
The default is horizontal. -->
<attr name="orientation" />
- <!-- The maxmimum number of rows to create when automatically positioning children. -->
+ <!-- The maximum number of rows to create when automatically positioning children. -->
<attr name="rowCount" format="integer" />
- <!-- The maxmimum number of columns to create when automatically positioning children. -->
+ <!-- The maximum number of columns to create when automatically positioning children. -->
<attr name="columnCount" format="integer" />
<!-- When set to true, tells GridLayout to use default margins when none are specified
in a view's layout parameters.
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 3c0e51e..99af0de 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
@@ -1437,8 +1442,11 @@
{@link #AndroidManifestService service},
{@link #AndroidManifestReceiver receiver},
{@link #AndroidManifestActivity activity},
- {@link #AndroidManifestActivityAlias activity-alias}, and
- {@link #AndroidManifestUsesLibrary uses-library}. The application tag
+ {@link #AndroidManifestActivityAlias activity-alias},
+ {@link #AndroidManifestUsesLibrary uses-library},
+ {@link #AndroidManifestUsesStaticLibrary uses-static-library}, and
+ {@link #AndroidManifestUsesPackage uses-package}.
+ The application tag
appears as a child of the root {@link #AndroidManifest manifest} tag in
an application's manifest file. -->
<declare-styleable name="AndroidManifestApplication" parent="AndroidManifest">
@@ -1872,12 +1880,35 @@
library is singed with more than one certificate.
<p>This appears as a child tag of the
- {@link #AndroidManifestUsesStaticLibrary uses-static-library} tag. -->
+ {@link #AndroidManifestUsesStaticLibrary uses-static-library} or
+ {@link #AndroidManifestUsesPackage uses-package} tag. -->
<declare-styleable name="AndroidManifestAdditionalCertificate" parent="AndroidManifestUsesStaticLibrary">
<!-- The SHA-256 digest of the library signing certificate. -->
<attr name="certDigest" />
</declare-styleable>
+ <!-- The <code>uses-package</code> specifies some kind of dependency on another
+ package. It does not have any impact on the app's execution on the device,
+ but provides information about dependencies it has on other packages that need
+ to be satisfied for it to run correctly. That is, this is primarily for
+ installers to know what other apps need to be installed along with this one.
+
+ <p>This appears as a child tag of the
+ {@link #AndroidManifestApplication application} tag. -->
+ <declare-styleable name="AndroidManifestUsesPackage" parent="AndroidManifestApplication">
+ <!-- Required type of association with the package, for example "android.package.ad_service"
+ if it provides an advertising service. -->
+ <attr name="packageType" format="string" />
+ <!-- Required name of the package you use. -->
+ <attr name="name" />
+ <!-- Optional minimum version of the package that satisfies the dependency. -->
+ <attr name="version" />
+ <!-- Optional minimum major version of the package that satisfies the dependency. -->
+ <attr name="versionMajor" format="integer" />
+ <!-- Optional SHA-256 digest of the package signing certificate. -->
+ <attr name="certDigest" format="string" />
+ </declare-styleable>
+
<!-- The <code>supports-screens</code> specifies the screen dimensions an
application supports. By default a modern application supports all
screen sizes and must explicitly disable certain screen sizes here;
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 9aebf6c..8b73b67 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1527,7 +1527,7 @@
<bool name="config_checkWallpaperAtBoot">true</bool>
<!-- Class name of WallpaperManagerService. -->
- <string name="config_wallpaperManagerServiceName">com.android.server.wallpaper.WallpaperManagerService</string>
+ <string name="config_wallpaperManagerServiceName" translatable="false">com.android.server.wallpaper.WallpaperManagerService</string>
<!-- Enables the TimeZoneRuleManager service. This is the master switch for the updateable time
zone update mechanism. -->
@@ -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>
@@ -3550,4 +3550,7 @@
<!-- Pre-scale volume at volume step 3 for Absolute Volume -->
<fraction name="config_prescaleAbsoluteVolume_index3">85%</fraction>
+
+ <!-- Whether or not the "SMS app service" feature is enabled -->
+ <bool name="config_useSmsAppService">true</bool>
</resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index cc99a4e..2e42e4a 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2903,6 +2903,7 @@
<eat-comment />
<public-group type="attr" first-id="0x01010587">
+ <public name="packageType" />
<public name="opticalInsetLeft" />
<public name="opticalInsetTop" />
<public name="opticalInsetRight" />
@@ -2912,6 +2913,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/symbols.xml b/core/res/res/values/symbols.xml
index 9f2256a..a7b6dde 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3279,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" />
@@ -3475,4 +3475,6 @@
<java-symbol type="fraction" name="config_prescaleAbsoluteVolume_index1" />
<java-symbol type="fraction" name="config_prescaleAbsoluteVolume_index2" />
<java-symbol type="fraction" name="config_prescaleAbsoluteVolume_index3" />
+
+ <java-symbol type="bool" name="config_useSmsAppService" />
</resources>
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..4ecdc42 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,
@@ -461,6 +463,7 @@
Settings.Global.WFC_IMS_MODE,
Settings.Global.WFC_IMS_ROAMING_ENABLED,
Settings.Global.WFC_IMS_ROAMING_MODE,
+ Settings.Global.WIFI_ALWAYS_REQUESTED,
Settings.Global.WIFI_BADGING_THRESHOLDS,
Settings.Global.WIFI_BOUNCE_DELAY_OVERRIDE_MS,
Settings.Global.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED,
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/DisplayCutoutTest.java b/core/tests/coretests/src/android/view/DisplayCutoutTest.java
index fe45fe7..c8d994c 100644
--- a/core/tests/coretests/src/android/view/DisplayCutoutTest.java
+++ b/core/tests/coretests/src/android/view/DisplayCutoutTest.java
@@ -17,6 +17,7 @@
package android.view;
import static android.view.DisplayCutout.NO_CUTOUT;
+import static android.view.DisplayCutout.extractBoundsFromList;
import static android.view.DisplayCutout.fromSpec;
import static org.hamcrest.Matchers.equalTo;
@@ -28,6 +29,7 @@
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
+import android.graphics.Insets;
import android.graphics.Rect;
import android.os.Parcel;
import android.platform.test.annotations.Presubmit;
@@ -39,38 +41,83 @@
import org.junit.runner.RunWith;
import java.util.Arrays;
+import java.util.Collections;
+
@RunWith(AndroidJUnit4.class)
@SmallTest
@Presubmit
public class DisplayCutoutTest {
+ private static final Rect ZERO_RECT = new Rect();
+
/** This is not a consistent cutout. Useful for verifying insets in one go though. */
final DisplayCutout mCutoutNumbers = new DisplayCutout(
- new Rect(1, 2, 3, 4),
- Arrays.asList(new Rect(5, 6, 7, 8)));
+ Insets.of(5, 6, 7, 8) /* safeInsets */,
+ null /* boundLeft */,
+ new Rect(9, 0, 10, 1) /* boundTop */,
+ null /* boundRight */,
+ null /* boundBottom */);
final DisplayCutout mCutoutTop = createCutoutTop();
@Test
+ public void testExtractBoundsFromList_left() {
+ Rect safeInsets = new Rect(10, 0, 0, 0);
+ Rect bound = new Rect(0, 80, 10, 120);
+ assertThat(extractBoundsFromList(safeInsets, Collections.singletonList(bound)),
+ equalTo(new Rect[]{bound, ZERO_RECT, ZERO_RECT, ZERO_RECT}));
+ }
+
+ @Test
+ public void testExtractBoundsFromList_top() {
+ Rect safeInsets = new Rect(0, 10, 0, 0);
+ Rect bound = new Rect(80, 0, 120, 10);
+ assertThat(extractBoundsFromList(safeInsets, Collections.singletonList(bound)),
+ equalTo(new Rect[]{ZERO_RECT, bound, ZERO_RECT, ZERO_RECT}));
+ }
+
+ @Test
+ public void testExtractBoundsFromList_right() {
+ Rect safeInsets = new Rect(0, 0, 10, 0);
+ Rect bound = new Rect(190, 80, 200, 120);
+ assertThat(extractBoundsFromList(safeInsets, Collections.singletonList(bound)),
+ equalTo(new Rect[]{ZERO_RECT, ZERO_RECT, bound, ZERO_RECT}));
+ }
+
+ @Test
+ public void testExtractBoundsFromList_bottom() {
+ Rect safeInsets = new Rect(0, 0, 0, 10);
+ Rect bound = new Rect(80, 190, 120, 200);
+ assertThat(extractBoundsFromList(safeInsets, Collections.singletonList(bound)),
+ equalTo(new Rect[]{ZERO_RECT, ZERO_RECT, ZERO_RECT, bound}));
+ }
+
+ @Test
+ public void testExtractBoundsFromList_top_and_bottom() {
+ Rect safeInsets = new Rect(0, 1, 0, 10);
+ Rect boundTop = new Rect(80, 0, 120, 10);
+ Rect boundBottom = new Rect(80, 190, 120, 200);
+ assertThat(extractBoundsFromList(safeInsets,
+ Arrays.asList(new Rect[]{boundTop, boundBottom})),
+ equalTo(new Rect[]{ZERO_RECT, boundTop, ZERO_RECT, boundBottom}));
+ }
+
+
+ @Test
public void hasCutout() throws Exception {
assertTrue(NO_CUTOUT.isEmpty());
assertFalse(mCutoutTop.isEmpty());
}
@Test
- public void getSafeInsets() throws Exception {
- assertEquals(1, mCutoutNumbers.getSafeInsetLeft());
- assertEquals(2, mCutoutNumbers.getSafeInsetTop());
- assertEquals(3, mCutoutNumbers.getSafeInsetRight());
- assertEquals(4, mCutoutNumbers.getSafeInsetBottom());
+ public void testGetSafeInsets() throws Exception {
+ assertEquals(5, mCutoutNumbers.getSafeInsetLeft());
+ assertEquals(6, mCutoutNumbers.getSafeInsetTop());
+ assertEquals(7, mCutoutNumbers.getSafeInsetRight());
+ assertEquals(8, mCutoutNumbers.getSafeInsetBottom());
- assertEquals(new Rect(1, 2, 3, 4), mCutoutNumbers.getSafeInsets());
- }
-
- @Test
- public void getBoundingRect() throws Exception {
- assertEquals(new Rect(50, 0, 75, 100), mCutoutTop.getBounds().getBounds());
+ assertEquals(new Rect(5, 6, 7, 8), mCutoutNumbers.getSafeInsets());
}
@Test
@@ -102,30 +149,30 @@
public void inset_insets_withLeftCutout() throws Exception {
DisplayCutout cutout = createCutoutWithInsets(100, 0, 0, 0).inset(1, 2, 3, 4);
- assertEquals(cutout.getSafeInsetLeft(), 99);
- assertEquals(cutout.getSafeInsetTop(), 0);
- assertEquals(cutout.getSafeInsetRight(), 0);
- assertEquals(cutout.getSafeInsetBottom(), 0);
+ assertEquals(99, cutout.getSafeInsetLeft());
+ assertEquals(0, cutout.getSafeInsetTop());
+ assertEquals(0, cutout.getSafeInsetRight());
+ assertEquals(0, cutout.getSafeInsetBottom());
}
@Test
public void inset_insets_withTopCutout() throws Exception {
DisplayCutout cutout = mCutoutTop.inset(1, 2, 3, 4);
- assertEquals(cutout.getSafeInsetLeft(), 0);
- assertEquals(cutout.getSafeInsetTop(), 98);
- assertEquals(cutout.getSafeInsetRight(), 0);
- assertEquals(cutout.getSafeInsetBottom(), 0);
+ assertEquals(0, cutout.getSafeInsetLeft());
+ assertEquals(98, cutout.getSafeInsetTop());
+ assertEquals(0, cutout.getSafeInsetRight());
+ assertEquals(0, cutout.getSafeInsetBottom());
}
@Test
public void inset_insets_withRightCutout() throws Exception {
DisplayCutout cutout = createCutoutWithInsets(0, 0, 100, 0).inset(1, 2, 3, 4);
- assertEquals(cutout.getSafeInsetLeft(), 0);
- assertEquals(cutout.getSafeInsetTop(), 0);
- assertEquals(cutout.getSafeInsetRight(), 97);
- assertEquals(cutout.getSafeInsetBottom(), 0);
+ assertEquals(0, cutout.getSafeInsetLeft());
+ assertEquals(0, cutout.getSafeInsetTop());
+ assertEquals(97, cutout.getSafeInsetRight());
+ assertEquals(0, cutout.getSafeInsetBottom());
}
@Test
@@ -153,16 +200,20 @@
@Test
public void inset_bounds() throws Exception {
DisplayCutout cutout = mCutoutTop.inset(1, 2, 3, 4);
-
- assertEquals(new Rect(49, -2, 74, 98), cutout.getBounds().getBounds());
+ assertThat(cutout.getBoundingRectsAll(), equalTo(
+ new Rect[]{ ZERO_RECT, new Rect(49, -2, 74, 98), ZERO_RECT, ZERO_RECT }));
}
+
+ // TODO: Deprecate fromBoundingRect.
+ /*
@Test
public void fromBoundingPolygon() throws Exception {
assertEquals(
new Rect(50, 0, 75, 100),
DisplayCutout.fromBoundingRect(50, 0, 75, 100).getBounds().getBounds());
}
+ */
@Test
public void parcel_unparcel_regular() {
@@ -231,6 +282,10 @@
DisplayCutout cutout = fromSpec("M -50,0 v 20 h 100 v -20 z"
+ "@bottom M -50,0 v -10,0 h 100 v 20 z", 200, 400, 2f);
assertThat(cutout.getSafeInsets(), equalTo(new Rect(0, 20, 0, 10)));
+ assertThat(cutout.getBoundingRectsAll(), equalTo(new Rect[]{
+ ZERO_RECT, new Rect(50, 0, 150, 20),
+ ZERO_RECT, new Rect(50, 390, 150, 410)
+ }));
}
@Test
@@ -281,8 +336,10 @@
}
private static DisplayCutout createCutoutWithInsets(int left, int top, int right, int bottom) {
+ Insets safeInset = Insets.of(left, top, right, bottom);
+ Rect boundTop = new Rect(50, 0, 75, 100);
return new DisplayCutout(
- new Rect(left, top, right, bottom),
- Arrays.asList(new Rect(50, 0, 75, 100)));
+ safeInset, null /* boundLeft */, boundTop, null /* boundRight */,
+ null /* boundBottom */);
}
}
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/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/core/tests/coretests/src/com/android/internal/widget/ActionBarOverlayLayoutTest.java b/core/tests/coretests/src/com/android/internal/widget/ActionBarOverlayLayoutTest.java
index cac4e88..218566e 100644
--- a/core/tests/coretests/src/com/android/internal/widget/ActionBarOverlayLayoutTest.java
+++ b/core/tests/coretests/src/com/android/internal/widget/ActionBarOverlayLayoutTest.java
@@ -27,6 +27,7 @@
import static org.junit.Assert.assertThat;
import android.content.Context;
+import android.graphics.Insets;
import android.graphics.Rect;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
@@ -44,18 +45,20 @@
import org.junit.runner.RunWith;
import java.lang.reflect.Field;
-import java.util.Arrays;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class ActionBarOverlayLayoutTest {
- private static final Rect TOP_INSET_5 = new Rect(0, 5, 0, 0);
- private static final Rect TOP_INSET_25 = new Rect(0, 25, 0, 0);
- private static final Rect ZERO_INSET = new Rect(0, 0, 0, 0);
+ private static final Insets TOP_INSET_5 = Insets.of(0, 5, 0, 0);
+ private static final Insets TOP_INSET_25 = Insets.of(0, 25, 0, 0);
private static final DisplayCutout CONSUMED_CUTOUT = null;
- private static final DisplayCutout CUTOUT_5 = new DisplayCutout(TOP_INSET_5, Arrays.asList(
- new Rect(100, 0, 200, 5)));
+ private static final DisplayCutout CUTOUT_5 = new DisplayCutout(
+ TOP_INSET_5,
+ null /* boundLeft */,
+ new Rect(100, 0, 200, 5),
+ null /* boundRight */,
+ null /* boundBottom*/);
private static final int EXACTLY_1000 = makeMeasureSpec(1000, EXACTLY);
private Context mContext;
@@ -112,7 +115,7 @@
mLayout.measure(EXACTLY_1000, EXACTLY_1000);
- assertThat(mContentInsetsListener.captured, is(insetsWith(ZERO_INSET, CONSUMED_CUTOUT)));
+ assertThat(mContentInsetsListener.captured, is(insetsWith(Insets.NONE, CONSUMED_CUTOUT)));
}
@Test
@@ -136,7 +139,7 @@
mLayout.measure(EXACTLY_1000, EXACTLY_1000);
- assertThat(mContentInsetsListener.captured, is(insetsWith(ZERO_INSET, NO_CUTOUT)));
+ assertThat(mContentInsetsListener.captured, is(insetsWith(Insets.NONE, NO_CUTOUT)));
}
@Test
@@ -160,11 +163,11 @@
mLayout.measure(EXACTLY_1000, EXACTLY_1000);
- assertThat(mContentInsetsListener.captured, is(insetsWith(ZERO_INSET, NO_CUTOUT)));
+ assertThat(mContentInsetsListener.captured, is(insetsWith(Insets.NONE, NO_CUTOUT)));
}
- private WindowInsets insetsWith(Rect content, DisplayCutout cutout) {
- return new WindowInsets(content, null, null, false, false, cutout);
+ private WindowInsets insetsWith(Insets content, DisplayCutout cutout) {
+ return new WindowInsets(content.toRect(), null, null, false, false, cutout);
}
private ViewGroup createViewGroupWithId(int id) {
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/graphics/java/android/graphics/BaseCanvas.java b/graphics/java/android/graphics/BaseCanvas.java
index 53e9826..0885a05 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.
@@ -377,6 +376,53 @@
drawRoundRect(rect.left, rect.top, rect.right, rect.bottom, rx, ry, paint);
}
+ /**
+ * Make lint happy.
+ * See {@link Canvas#drawDoubleRoundRect(RectF, float, float, RectF, float, float, Paint)}
+ */
+ public void drawDoubleRoundRect(@NonNull RectF outer, float outerRx, float outerRy,
+ @NonNull RectF inner, float innerRx, float innerRy, @NonNull Paint paint) {
+ throwIfHasHwBitmapInSwMode(paint);
+ float outerLeft = outer.left;
+ float outerTop = outer.top;
+ float outerRight = outer.right;
+ float outerBottom = outer.bottom;
+
+ float innerLeft = inner.left;
+ float innerTop = inner.top;
+ float innerRight = inner.right;
+ float innerBottom = inner.bottom;
+ nDrawDoubleRoundRect(mNativeCanvasWrapper, outerLeft, outerTop, outerRight, outerBottom,
+ outerRx, outerRy, innerLeft, innerTop, innerRight, innerBottom, innerRx, innerRy,
+ paint.getNativeInstance());
+ }
+
+ /**
+ * Make lint happy.
+ * See {@link Canvas#drawDoubleRoundRect(RectF, float[], RectF, float[], Paint)}
+ */
+ public void drawDoubleRoundRect(@NonNull RectF outer, float[] outerRadii,
+ @NonNull RectF inner, float[] innerRadii, @NonNull Paint paint) {
+ throwIfHasHwBitmapInSwMode(paint);
+ if (innerRadii == null || outerRadii == null
+ || innerRadii.length != 8 || outerRadii.length != 8) {
+ throw new IllegalArgumentException("Both inner and outer radii arrays must contain "
+ + "exactly 8 values");
+ }
+ float outerLeft = outer.left;
+ float outerTop = outer.top;
+ float outerRight = outer.right;
+ float outerBottom = outer.bottom;
+
+ float innerLeft = inner.left;
+ float innerTop = inner.top;
+ float innerRight = inner.right;
+ float innerBottom = inner.bottom;
+ nDrawDoubleRoundRect(mNativeCanvasWrapper, outerLeft, outerTop, outerRight,
+ outerBottom, outerRadii, innerLeft, innerTop, innerRight, innerBottom, innerRadii,
+ paint.getNativeInstance());
+ }
+
public void drawText(@NonNull char[] text, int index, int count, float x, float y,
@NonNull Paint paint) {
if ((index | count | (index + count) |
@@ -632,6 +678,16 @@
private static native void nDrawRoundRect(long nativeCanvas, float left, float top, float right,
float bottom, float rx, float ry, long nativePaint);
+ private static native void nDrawDoubleRoundRect(long nativeCanvas, float outerLeft,
+ float outerTop, float outerRight, float outerBottom, float outerRx, float outerRy,
+ float innerLeft, float innerTop, float innerRight, float innerBottom, float innerRx,
+ float innerRy, long nativePaint);
+
+ private static native void nDrawDoubleRoundRect(long nativeCanvas, float outerLeft,
+ float outerTop, float outerRight, float outerBottom, float[] outerRadii,
+ float innerLeft, float innerTop, float innerRight, float innerBottom,
+ float[] innerRadii, long nativePaint);
+
private static native void nDrawPath(long nativeCanvas, long nativePath, long nativePaint);
private static native void nDrawRegion(long nativeCanvas, long nativeRegion, long nativePaint);
diff --git a/core/java/android/view/RecordingCanvas.java b/graphics/java/android/graphics/BaseRecordingCanvas.java
similarity index 93%
rename from core/java/android/view/RecordingCanvas.java
rename to graphics/java/android/graphics/BaseRecordingCanvas.java
index 3364483..fb30ca2 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);
}
@@ -390,6 +377,24 @@
}
@Override
+ public final void drawDoubleRoundRect(@NonNull RectF outer, float outerRx, float outerRy,
+ @NonNull RectF inner, float innerRx, float innerRy, @NonNull Paint paint) {
+ nDrawDoubleRoundRect(mNativeCanvasWrapper,
+ outer.left, outer.top, outer.right, outer.bottom, outerRx, outerRy,
+ inner.left, inner.top, inner.right, inner.bottom, innerRx, innerRy,
+ paint.getNativeInstance());
+ }
+
+ @Override
+ public final void drawDoubleRoundRect(@NonNull RectF outer, float[] outerRadii,
+ @NonNull RectF inner, float[] innerRadii, @NonNull Paint paint) {
+ nDrawDoubleRoundRect(mNativeCanvasWrapper,
+ outer.left, outer.top, outer.right, outer.bottom, outerRadii,
+ inner.left, inner.top, inner.right, inner.bottom, innerRadii,
+ paint.getNativeInstance());
+ }
+
+ @Override
public final void drawText(@NonNull char[] text, int index, int count, float x, float y,
@NonNull Paint paint) {
if ((index | count | (index + count)
@@ -606,6 +611,18 @@
float bottom, float rx, float ry, long nativePaint);
@FastNative
+ private static native void nDrawDoubleRoundRect(long nativeCanvas,
+ float outerLeft, float outerTop, float outerRight, float outerBottom,
+ float outerRx, float outerRy, float innerLeft, float innerTop, float innerRight,
+ float innerBottom, float innerRx, float innerRy, long nativePaint);
+
+ @FastNative
+ private static native void nDrawDoubleRoundRect(long nativeCanvas, float outerLeft,
+ float outerTop, float outerRight, float outerBottom, float[] outerRadii,
+ float innerLeft, float innerTop, float innerRight, float innerBottom,
+ float[] innerRadii, long nativePaint);
+
+ @FastNative
private static native void nDrawPath(long nativeCanvas, long nativePath, long nativePaint);
@FastNative
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index 36c1c21..e35a3be 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -1877,6 +1877,51 @@
}
/**
+ * Draws a double rounded rectangle using the specified paint. The resultant round rect
+ * will be filled in the area defined between the outer and inner rectangular bounds if
+ * the {@link Paint} configured with {@link Paint.Style#FILL}.
+ * Otherwise if {@link Paint.Style#STROKE} is used, then 2 rounded rect strokes will
+ * be drawn at the outer and inner rounded rectangles
+ *
+ * @param outer The outer rectangular bounds of the roundRect to be drawn
+ * @param outerRx The x-radius of the oval used to round the corners on the outer rectangle
+ * @param outerRy The y-radius of the oval used to round the corners on the outer rectangle
+ * @param inner The inner rectangular bounds of the roundRect to be drawn
+ * @param innerRx The x-radius of the oval used to round the corners on the inner rectangle
+ * @param innerRy The y-radius of the oval used to round the corners on the outer rectangle
+ * @param paint The paint used to draw the double roundRect
+ */
+ @Override
+ public void drawDoubleRoundRect(@NonNull RectF outer, float outerRx, float outerRy,
+ @NonNull RectF inner, float innerRx, float innerRy, @NonNull Paint paint) {
+ super.drawDoubleRoundRect(outer, outerRx, outerRy, inner, innerRx, innerRy, paint);
+ }
+
+ /**
+ * Draws a double rounded rectangle using the specified paint. The resultant round rect
+ * will be filled in the area defined between the outer and inner rectangular bounds if
+ * the {@link Paint} configured with {@link Paint.Style#FILL}.
+ * Otherwise if {@link Paint.Style#STROKE} is used, then 2 rounded rect strokes will
+ * be drawn at the outer and inner rounded rectangles
+ *
+ * @param outer The outer rectangular bounds of the roundRect to be drawn
+ * @param outerRadii Array of 8 float representing the x, y corner radii for top left,
+ * top right, bottom right, bottom left corners respectively on the outer
+ * rounded rectangle
+ *
+ * @param inner The inner rectangular bounds of the roundRect to be drawn
+ * @param innerRadii Array of 8 float representing the x, y corner radii for top left,
+ * top right, bottom right, bottom left corners respectively on the
+ * outer rounded rectangle
+ * @param paint The paint used to draw the double roundRect
+ */
+ @Override
+ public void drawDoubleRoundRect(@NonNull RectF outer, float[] outerRadii,
+ @NonNull RectF inner, float[] innerRadii, @NonNull Paint paint) {
+ super.drawDoubleRoundRect(outer, outerRadii, inner, innerRadii, paint);
+ }
+
+ /**
* Draw the text, with origin at (x,y), using the specified paint. The origin is interpreted
* based on the Align setting in the paint.
*
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 6ce66bd..009e042 100644
--- a/graphics/java/android/graphics/ImageDecoder.java
+++ b/graphics/java/android/graphics/ImageDecoder.java
@@ -59,6 +59,7 @@
import java.io.InputStream;
import java.lang.annotation.Retention;
import java.nio.ByteBuffer;
+import java.util.concurrent.Callable;
import java.util.concurrent.atomic.AtomicBoolean;
/**
@@ -283,26 +284,7 @@
return createFromStream(is, true, this);
}
-
- final FileDescriptor fd = assetFd.getFileDescriptor();
- final long offset = assetFd.getStartOffset();
-
- ImageDecoder decoder = null;
- try {
- try {
- Os.lseek(fd, offset, SEEK_SET);
- decoder = nCreate(fd, this);
- } catch (ErrnoException e) {
- decoder = createFromStream(new FileInputStream(fd), true, this);
- }
- } finally {
- if (decoder == null) {
- IoUtils.closeQuietly(assetFd);
- } else {
- decoder.mAssetFd = assetFd;
- }
- }
- return decoder;
+ return createFromAssetFileDescriptor(assetFd, this);
}
}
@@ -354,6 +336,30 @@
return decoder;
}
+ @NonNull
+ private static ImageDecoder createFromAssetFileDescriptor(@NonNull AssetFileDescriptor assetFd,
+ Source source) throws IOException {
+ final FileDescriptor fd = assetFd.getFileDescriptor();
+ final long offset = assetFd.getStartOffset();
+
+ ImageDecoder decoder = null;
+ try {
+ try {
+ Os.lseek(fd, offset, SEEK_SET);
+ decoder = nCreate(fd, source);
+ } catch (ErrnoException e) {
+ decoder = createFromStream(new FileInputStream(fd), true, source);
+ }
+ } finally {
+ if (decoder == null) {
+ IoUtils.closeQuietly(assetFd);
+ } else {
+ decoder.mAssetFd = assetFd;
+ }
+ }
+ return decoder;
+ }
+
/**
* For backwards compatibility, this does *not* close the InputStream.
*
@@ -528,6 +534,29 @@
}
}
+ private static class CallableSource extends Source {
+ CallableSource(@NonNull Callable<AssetFileDescriptor> callable) {
+ mCallable = callable;
+ }
+
+ private final Callable<AssetFileDescriptor> mCallable;
+
+ @Override
+ public ImageDecoder createImageDecoder() throws IOException {
+ AssetFileDescriptor assetFd = null;
+ try {
+ assetFd = mCallable.call();
+ } catch (Exception e) {
+ if (e instanceof IOException) {
+ throw (IOException) e;
+ } else {
+ throw new IOException(e);
+ }
+ }
+ return createFromAssetFileDescriptor(assetFd, this);
+ }
+ }
+
/**
* Information about an encoded image.
*/
@@ -971,6 +1000,27 @@
}
/**
+ * Create a new {@link Source Source} from a {@link Callable} that returns a
+ * new {@link AssetFileDescriptor} for each request. This provides control
+ * over how the {@link AssetFileDescriptor} is created, such as passing
+ * options into {@link ContentResolver#openTypedAssetFileDescriptor}, or
+ * enabling use of a {@link android.os.CancellationSignal}.
+ * <p>
+ * It's important for the given {@link Callable} to return a new, unique
+ * {@link AssetFileDescriptor} for each invocation, to support reuse of the
+ * returned {@link Source Source}.
+ *
+ * @return a new Source object, which can be passed to
+ * {@link #decodeDrawable decodeDrawable} or {@link #decodeBitmap
+ * decodeBitmap}.
+ */
+ @AnyThread
+ @NonNull
+ public static Source createSource(@NonNull Callable<AssetFileDescriptor> callable) {
+ return new CallableSource(callable);
+ }
+
+ /**
* Return the width and height of a given sample size.
*
* <p>This takes an input that functions like
diff --git a/graphics/java/android/graphics/Insets.java b/graphics/java/android/graphics/Insets.java
index c3449dd..de110c8 100644
--- a/graphics/java/android/graphics/Insets.java
+++ b/graphics/java/android/graphics/Insets.java
@@ -73,6 +73,15 @@
}
/**
+ * Returns a Rect intance with the appropriate values.
+ *
+ * @hide
+ */
+ public @NonNull Rect toRect() {
+ return new Rect(left, top, right, bottom);
+ }
+
+ /**
* Two Insets instances are equal iff they belong to the same class and their fields are
* pairwise equal.
*
diff --git a/graphics/java/android/graphics/Rect.java b/graphics/java/android/graphics/Rect.java
index 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/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/Layer.cpp b/libs/hwui/Layer.cpp
index cc95051..32aaa54 100644
--- a/libs/hwui/Layer.cpp
+++ b/libs/hwui/Layer.cpp
@@ -77,5 +77,13 @@
mRenderState.postDecStrong(this);
}
+SkBlendMode Layer::getMode() const {
+ if (mBlend || mode != SkBlendMode::kSrcOver) {
+ return mode;
+ } else {
+ return SkBlendMode::kSrc;
+ }
+}
+
}; // namespace uirenderer
}; // namespace android
diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h
index 6f07a43..e4f96e9 100644
--- a/libs/hwui/Layer.h
+++ b/libs/hwui/Layer.h
@@ -67,7 +67,7 @@
inline int getAlpha() const { return alpha; }
- inline SkBlendMode getMode() const { return mode; }
+ SkBlendMode getMode() const;
inline SkColorFilter* getColorFilter() const { return mColorFilter.get(); }
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 c30af84..f928de9 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -23,6 +23,7 @@
#include "SkDrawShadowInfo.h"
#include "SkImage.h"
#include "SkImageFilter.h"
+#include "SkLatticeIter.h"
#include "SkMath.h"
#include "SkPicture.h"
#include "SkRSXform.h"
@@ -280,7 +281,8 @@
struct DrawImage final : Op {
static const auto kType = Type::DrawImage;
- DrawImage(sk_sp<const SkImage>&& image, SkScalar x, SkScalar y, const SkPaint* paint, BitmapPalette palette)
+ 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;
@@ -312,7 +314,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, BitmapPalette palette)
+ 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) {
@@ -331,8 +334,14 @@
struct DrawImageLattice final : Op {
static const auto kType = Type::DrawImageLattice;
DrawImageLattice(sk_sp<const SkImage>&& image, int xs, int ys, int fs, const SkIRect& src,
- const SkRect& dst, const SkPaint* paint)
- : image(std::move(image)), xs(xs), ys(ys), fs(fs), src(src), dst(dst) {
+ const SkRect& dst, const SkPaint* paint, BitmapPalette palette)
+ : image(std::move(image))
+ , xs(xs)
+ , ys(ys)
+ , fs(fs)
+ , src(src)
+ , dst(dst)
+ , palette(palette) {
if (paint) {
this->paint = *paint;
}
@@ -342,6 +351,7 @@
SkIRect src;
SkRect dst;
SkPaint paint;
+ BitmapPalette palette;
void draw(SkCanvas* c, const SkMatrix&) const {
auto xdivs = pod<int>(this, 0), ydivs = pod<int>(this, xs * sizeof(int));
auto colors = (0 == fs) ? nullptr : pod<SkColor>(this, (xs + ys) * sizeof(int));
@@ -511,16 +521,13 @@
tree->getPaintFor(&paint, tree->stagingProperties());
}
- void draw(SkCanvas* canvas, const SkMatrix&) const {
- mRoot->draw(canvas, mBounds, paint);
- }
+ 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>
@@ -647,14 +654,15 @@
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) {
+ const SkRect& dst, const SkPaint* paint,
+ BitmapPalette palette) {
int xs = lattice.fXCount, ys = lattice.fYCount;
int fs = lattice.fRectTypes ? (xs + 1) * (ys + 1) : 0;
size_t bytes = (xs + ys) * sizeof(int) + fs * sizeof(SkCanvas::Lattice::RectType) +
fs * sizeof(SkColor);
SkASSERT(lattice.fBounds);
void* pod = this->push<DrawImageLattice>(bytes, std::move(image), xs, ys, fs, *lattice.fBounds,
- dst, paint);
+ dst, paint, palette);
copy_v(pod, lattice.fXDivs, xs, lattice.fYDivs, ys, lattice.fColors, fs, lattice.fRectTypes,
fs);
}
@@ -779,22 +787,26 @@
template <class T>
constexpr color_transform_fn colorTransformForOp() {
- 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 {
+ 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;
}
}
@@ -931,11 +943,12 @@
}
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, BitmapPalette::Unknown);
+ 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);
+ fDL->drawImageLattice(SkImage::MakeFromBitmap(bm), lattice, dst, paint, BitmapPalette::Unknown);
}
void RecordingCanvas::drawImage(const sk_sp<SkImage>& image, SkScalar x, SkScalar y,
@@ -943,11 +956,34 @@
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) {
+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::drawImageLattice(const sk_sp<SkImage>& image, const Lattice& lattice,
+ const SkRect& dst, const SkPaint* paint,
+ BitmapPalette palette) {
+ if (!image || dst.isEmpty()) {
+ return;
+ }
+
+ SkIRect bounds;
+ Lattice latticePlusBounds = lattice;
+ if (!latticePlusBounds.fBounds) {
+ bounds = SkIRect::MakeWH(image->width(), image->height());
+ latticePlusBounds.fBounds = &bounds;
+ }
+
+ if (SkLatticeIter::Valid(image->width(), image->height(), latticePlusBounds)) {
+ fDL->drawImageLattice(image, latticePlusBounds, dst, paint, palette);
+ } else {
+ fDL->drawImageRect(image, nullptr, dst, paint, SrcRectConstraint::kFast_SrcRectConstraint,
+ palette);
+ }
+}
+
void RecordingCanvas::onDrawImage(const SkImage* img, SkScalar x, SkScalar y,
const SkPaint* paint) {
fDL->drawImage(sk_ref_sp(img), x, y, paint, BitmapPalette::Unknown);
@@ -962,7 +998,7 @@
}
void RecordingCanvas::onDrawImageLattice(const SkImage* img, const SkCanvas::Lattice& lattice,
const SkRect& dst, const SkPaint* paint) {
- fDL->drawImageLattice(sk_ref_sp(img), lattice, dst, paint);
+ fDL->drawImageLattice(sk_ref_sp(img), lattice, dst, paint, BitmapPalette::Unknown);
}
void RecordingCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
@@ -975,8 +1011,8 @@
fDL->drawPoints(mode, count, pts, paint);
}
void RecordingCanvas::onDrawVerticesObject(const SkVertices* vertices,
- const SkVertices::Bone bones[], int boneCount,
- SkBlendMode mode, const SkPaint& paint) {
+ const SkVertices::Bone bones[], int boneCount,
+ SkBlendMode mode, const SkPaint& paint) {
fDL->drawVertices(vertices, bones, boneCount, mode, paint);
}
void RecordingCanvas::onDrawAtlas(const SkImage* atlas, const SkRSXform xforms[],
diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h
index 80c80ca..099e0be 100644
--- a/libs/hwui/RecordingCanvas.h
+++ b/libs/hwui/RecordingCanvas.h
@@ -110,7 +110,7 @@
void drawImageRect(sk_sp<const SkImage>, const SkRect*, const SkRect&, const SkPaint*,
SkCanvas::SrcRectConstraint, BitmapPalette palette);
void drawImageLattice(sk_sp<const SkImage>, const SkCanvas::Lattice&, const SkRect&,
- const SkPaint*);
+ const SkPaint*, BitmapPalette);
void drawPatch(const SkPoint[12], const SkColor[4], const SkPoint[4], SkBlendMode,
const SkPaint&);
@@ -185,11 +185,13 @@
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 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 drawImageLattice(const sk_sp<SkImage>& image, const Lattice& lattice, const SkRect& dst,
+ const SkPaint* paint, BitmapPalette palette);
void onDrawImage(const SkImage*, SkScalar, SkScalar, const SkPaint*) override;
void onDrawImageLattice(const SkImage*, const Lattice&, const SkRect&, const SkPaint*) override;
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index d9a7cc3..d2a8f02 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -282,25 +282,45 @@
mStagingDisplayList = nullptr;
if (mDisplayList) {
mDisplayList->syncContents();
+ handleForceDark(info);
+ }
+}
- 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;
- } else if (mDisplayList->mChildNodes.size() == 1 &&
- mDisplayList->mChildNodes.front().getRenderNode()->usageHint() !=
- UsageHint::Background) {
- usage = UsageHint::Background;
- }
- }
- mDisplayList->mDisplayList.applyColorTransform(
- usage == UsageHint::Background ? ColorTransform::Dark : ColorTransform::Light);
+void RenderNode::handleForceDark(android::uirenderer::TreeInfo *info) {
+ if (CC_LIKELY(!info || info->disableForceDark)) {
+ return;
+ }
+ auto usage = usageHint();
+ const auto& children = mDisplayList->mChildNodes;
+ if (mDisplayList->hasText()) {
+ usage = UsageHint::Foreground;
+ }
+ if (usage == UsageHint::Unknown) {
+ if (children.size() > 1) {
+ usage = UsageHint::Background;
+ } else if (children.size() == 1 &&
+ children.front().getRenderNode()->usageHint() !=
+ UsageHint::Background) {
+ usage = UsageHint::Background;
}
}
+ if (children.size() > 1) {
+ // Crude overlap check
+ SkRect drawn = SkRect::MakeEmpty();
+ for (auto iter = children.rbegin(); iter != children.rend(); ++iter) {
+ const auto& child = iter->getRenderNode();
+ // We use stagingProperties here because we haven't yet sync'd the children
+ SkRect bounds = SkRect::MakeXYWH(child->stagingProperties().getX(), child->stagingProperties().getY(),
+ child->stagingProperties().getWidth(), child->stagingProperties().getHeight());
+ if (bounds.contains(drawn)) {
+ // This contains everything drawn after it, so make it a background
+ child->setUsageHint(UsageHint::Background);
+ }
+ drawn.join(bounds);
+ }
+ }
+ mDisplayList->mDisplayList.applyColorTransform(
+ usage == UsageHint::Background ? ColorTransform::Dark : ColorTransform::Light);
}
void RenderNode::pushStagingDisplayListChanges(TreeObserver& observer, TreeInfo& info) {
diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h
index 211dd2d..be0b46b 100644
--- a/libs/hwui/RenderNode.h
+++ b/libs/hwui/RenderNode.h
@@ -220,6 +220,7 @@
void syncProperties();
void syncDisplayList(TreeObserver& observer, TreeInfo* info);
+ void handleForceDark(TreeInfo* info);
void prepareTreeImpl(TreeObserver& observer, TreeInfo& info, bool functorsNeedLayer);
void pushStagingPropertiesChanges(TreeInfo& info);
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index 17f1a3b..2e5aef5 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -504,6 +504,11 @@
mCanvas->drawRoundRect(rect, rx, ry, *filterPaint(paint));
}
+void SkiaCanvas::drawDoubleRoundRect(const SkRRect& outer, const SkRRect& inner,
+ const SkPaint& paint) {
+ mCanvas->drawDRRect(outer, inner, *filterPaint(paint));
+}
+
void SkiaCanvas::drawCircle(float x, float y, float radius, const SkPaint& paint) {
if (CC_UNLIKELY(radius <= 0 || paint.nothingToDraw())) return;
mCanvas->drawCircle(x, y, radius, *filterPaint(paint));
diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h
index 24b7ec6..3a877cf 100644
--- a/libs/hwui/SkiaCanvas.h
+++ b/libs/hwui/SkiaCanvas.h
@@ -107,6 +107,10 @@
virtual void drawRegion(const SkRegion& region, const SkPaint& paint) override;
virtual void drawRoundRect(float left, float top, float right, float bottom, float rx, float ry,
const SkPaint& paint) override;
+
+ virtual void drawDoubleRoundRect(const SkRRect& outer, const SkRRect& inner,
+ const SkPaint& paint) override;
+
virtual void drawCircle(float x, float y, float radius, const SkPaint& paint) override;
virtual void drawOval(float left, float top, float right, float bottom,
const SkPaint& paint) override;
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 caa5762..a0d9605 100644
--- a/libs/hwui/TreeInfo.h
+++ b/libs/hwui/TreeInfo.h
@@ -16,8 +16,8 @@
#pragma once
-#include "utils/Macros.h"
#include "Properties.h"
+#include "utils/Macros.h"
#include <utils/Timers.h>
@@ -40,7 +40,7 @@
virtual void onError(const std::string& message) = 0;
protected:
- virtual ~ErrorHandler() {}
+ virtual ~ErrorHandler() = default;
};
class TreeObserver {
@@ -52,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
@@ -71,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
@@ -94,7 +93,7 @@
bool updateWindowPositions = false;
- int disableForceDark = Properties::forceDarkMode ? 0 : 1;
+ int disableForceDark;
struct Out {
bool hasFunctors = false;
diff --git a/libs/hwui/VectorDrawable.cpp b/libs/hwui/VectorDrawable.cpp
index dbbe9f3..6cf04bf 100644
--- a/libs/hwui/VectorDrawable.cpp
+++ b/libs/hwui/VectorDrawable.cpp
@@ -470,10 +470,10 @@
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) {
+ if (prop.getColorFilter() != nullptr) {
outPaint->setColorFilter(sk_ref_sp(prop.getColorFilter()));
- outPaint->setAlpha(prop.getRootAlpha() * 255);
}
+ outPaint->setAlpha(prop.getRootAlpha() * 255);
}
Bitmap& Tree::getBitmapUpdateIfDirty() {
diff --git a/libs/hwui/hwui/Canvas.cpp b/libs/hwui/hwui/Canvas.cpp
index af7f013..e2ea2bc 100644
--- a/libs/hwui/hwui/Canvas.cpp
+++ b/libs/hwui/hwui/Canvas.cpp
@@ -178,6 +178,41 @@
MinikinUtils::forFontRun(layout, &paint, f);
}
+void Canvas::drawDoubleRoundRectXY(float outerLeft, float outerTop, float outerRight,
+ float outerBottom, float outerRx, float outerRy, float innerLeft,
+ float innerTop, float innerRight, float innerBottom, float innerRx,
+ float innerRy, const SkPaint& paint) {
+ if (CC_UNLIKELY(paint.nothingToDraw())) return;
+ SkRect outer = SkRect::MakeLTRB(outerLeft, outerTop, outerRight, outerBottom);
+ SkRect inner = SkRect::MakeLTRB(innerLeft, innerTop, innerRight, innerBottom);
+
+ SkRRect outerRRect;
+ outerRRect.setRectXY(outer, outerRx, outerRy);
+
+ SkRRect innerRRect;
+ innerRRect.setRectXY(inner, innerRx, innerRy);
+ drawDoubleRoundRect(outerRRect, innerRRect, paint);
+}
+
+void Canvas::drawDoubleRoundRectRadii(float outerLeft, float outerTop, float outerRight,
+ float outerBottom, const float* outerRadii, float innerLeft,
+ float innerTop, float innerRight, float innerBottom,
+ const float* innerRadii, const SkPaint& paint) {
+ static_assert(sizeof(SkVector) == sizeof(float) * 2);
+ if (CC_UNLIKELY(paint.nothingToDraw())) return;
+ SkRect outer = SkRect::MakeLTRB(outerLeft, outerTop, outerRight, outerBottom);
+ SkRect inner = SkRect::MakeLTRB(innerLeft, innerTop, innerRight, innerBottom);
+
+ SkRRect outerRRect;
+ const SkVector* outerSkVector = reinterpret_cast<const SkVector*>(outerRadii);
+ outerRRect.setRectRadii(outer, outerSkVector);
+
+ SkRRect innerRRect;
+ const SkVector* innerSkVector = reinterpret_cast<const SkVector*>(innerRadii);
+ innerRRect.setRectRadii(inner, innerSkVector);
+ drawDoubleRoundRect(outerRRect, innerRRect, paint);
+}
+
class DrawTextOnPathFunctor {
public:
DrawTextOnPathFunctor(const minikin::Layout& layout, Canvas* canvas, float hOffset,
diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h
index b9af7de2..e99742b 100644
--- a/libs/hwui/hwui/Canvas.h
+++ b/libs/hwui/hwui/Canvas.h
@@ -236,6 +236,8 @@
virtual void drawRegion(const SkRegion& region, const SkPaint& paint) = 0;
virtual void drawRoundRect(float left, float top, float right, float bottom, float rx, float ry,
const SkPaint& paint) = 0;
+ virtual void drawDoubleRoundRect(const SkRRect& outer, const SkRRect& inner,
+ const SkPaint& paint) = 0;
virtual void drawCircle(float x, float y, float radius, const SkPaint& paint) = 0;
virtual void drawOval(float left, float top, float right, float bottom,
const SkPaint& paint) = 0;
@@ -284,6 +286,16 @@
const SkPath& path, float hOffset, float vOffset, const Paint& paint,
const Typeface* typeface);
+ void drawDoubleRoundRectXY(float outerLeft, float outerTop, float outerRight,
+ float outerBottom, float outerRx, float outerRy, float innerLeft,
+ float innerTop, float innerRight, float innerBottom, float innerRx,
+ float innerRy, const SkPaint& paint);
+
+ void drawDoubleRoundRectRadii(float outerLeft, float outerTop, float outerRight,
+ float outerBottom, const float* outerRadii, float innerLeft,
+ float innerTop, float innerRight, float innerBottom,
+ const float* innerRadii, const SkPaint& paint);
+
static int GetApiLevel() { return sApiLevel; }
protected:
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
index e8bf492..d401b38 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
@@ -167,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);
@@ -184,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 086a760..4ab3541 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h
@@ -47,8 +47,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);
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.h b/libs/hwui/pipeline/skia/SkiaPipeline.h
index ee9158c..42a411a 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.h
@@ -48,6 +48,9 @@
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);
@@ -106,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 fac07d7..3fa73a4 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
@@ -245,8 +245,9 @@
}
sk_sp<SkColorFilter> colorFilter;
sk_sp<SkImage> image = bitmap.makeImage(&colorFilter);
- mRecorder.drawImageLattice(image.get(), lattice, dst,
- filterBitmap(std::move(filteredPaint), std::move(colorFilter)));
+ mRecorder.drawImageLattice(image, lattice, dst,
+ filterBitmap(std::move(filteredPaint), std::move(colorFilter)),
+ bitmap.palette());
if (!bitmap.isImmutable() && image.get() && !image->unique() && !dst.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..2ca110f 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
@@ -123,8 +123,13 @@
}
if (surface) {
- // TODO: handle color mode
- mVkSurface = mVkManager.createSurface(surface);
+ mVkSurface = mVkManager.createSurface(surface, colorMode);
+ }
+
+ if (colorMode == ColorMode::SRGB) {
+ mSurfaceColorType = SkColorType::kN32_SkColorType;
+ } else if (colorMode == ColorMode::WideColorGamut) {
+ mSurfaceColorType = SkColorType::kRGBA_F16_SkColorType;
}
return mVkSurface != nullptr;
@@ -138,14 +143,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/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index c8c394a..92a749f 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -144,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 2315cb9..2307ee4 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -182,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);
@@ -228,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 6106e24..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();
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index d22f56e..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);
diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp
index 285a1a5..b0d4505 100644
--- a/libs/hwui/renderthread/VulkanManager.cpp
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -78,7 +78,7 @@
0, // applicationVersion
"android framework", // pEngineName
0, // engineVerison
- VK_MAKE_VERSION(1, 0, 0), // apiVersion
+ VK_MAKE_VERSION(1, 1, 0), // apiVersion
};
std::vector<const char*> instanceExtensions;
@@ -133,6 +133,7 @@
GET_INST_PROC(DestroyInstance);
GET_INST_PROC(EnumeratePhysicalDevices);
+ GET_INST_PROC(GetPhysicalDeviceProperties);
GET_INST_PROC(GetPhysicalDeviceQueueFamilyProperties);
GET_INST_PROC(GetPhysicalDeviceFeatures2);
GET_INST_PROC(CreateDevice);
@@ -164,6 +165,13 @@
return false;
}
+ VkPhysicalDeviceProperties physDeviceProperties;
+ mGetPhysicalDeviceProperties(mPhysicalDevice, &physDeviceProperties);
+ if (physDeviceProperties.apiVersion < VK_MAKE_VERSION(1, 1, 0)) {
+ this->destroy();
+ return false;
+ }
+
// query to get the initial queue props size
uint32_t queueCount;
mGetPhysicalDeviceQueueFamilyProperties(mPhysicalDevice, &queueCount, nullptr);
@@ -618,7 +626,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);
@@ -733,24 +742,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;
}
@@ -812,14 +819,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));
diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h
index c211f5d..6702649 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;
@@ -178,6 +176,7 @@
VkPtr<PFN_vkDestroyInstance> mDestroyInstance;
VkPtr<PFN_vkEnumeratePhysicalDevices> mEnumeratePhysicalDevices;
+ VkPtr<PFN_vkGetPhysicalDeviceProperties> mGetPhysicalDeviceProperties;
VkPtr<PFN_vkGetPhysicalDeviceQueueFamilyProperties> mGetPhysicalDeviceQueueFamilyProperties;
VkPtr<PFN_vkGetPhysicalDeviceFeatures2> mGetPhysicalDeviceFeatures2;
VkPtr<PFN_vkCreateDevice> mCreateDevice;
diff --git a/libs/hwui/tests/unit/SkiaPipelineTests.cpp b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
index 680fcb3..cdf31da 100644
--- a/libs/hwui/tests/unit/SkiaPipelineTests.cpp
+++ b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
@@ -386,7 +386,7 @@
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::Legacy));
+ 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/libs/services/include/android/os/StatsLogEventWrapper.h b/libs/services/include/android/os/StatsLogEventWrapper.h
index 52cb75e..f60c338 100644
--- a/libs/services/include/android/os/StatsLogEventWrapper.h
+++ b/libs/services/include/android/os/StatsLogEventWrapper.h
@@ -58,6 +58,11 @@
type = FLOAT;
}
+ StatsLogValue(double v) {
+ double_value = v;
+ type = DOUBLE;
+ }
+
StatsLogValue(const std::string& v) {
str_value = v;
type = STRING;
diff --git a/libs/services/src/os/StatsLogEventWrapper.cpp b/libs/services/src/os/StatsLogEventWrapper.cpp
index 04c4629..a1a6d9f 100644
--- a/libs/services/src/os/StatsLogEventWrapper.cpp
+++ b/libs/services/src/os/StatsLogEventWrapper.cpp
@@ -85,6 +85,9 @@
case StatsLogValue::FLOAT:
mElements.push_back(StatsLogValue(in->readFloat()));
break;
+ case StatsLogValue::DOUBLE:
+ mElements.push_back(StatsLogValue(in->readDouble()));
+ break;
case StatsLogValue::STORAGE:
mElements.push_back(StatsLogValue());
mElements.back().setType(StatsLogValue::STORAGE);
diff --git a/location/java/android/location/Criteria.java b/location/java/android/location/Criteria.java
index a6099be..74eb445 100644
--- a/location/java/android/location/Criteria.java
+++ b/location/java/android/location/Criteria.java
@@ -21,9 +21,9 @@
/**
* A class indicating the application criteria for selecting a
- * location provider. Providers maybe ordered according to accuracy,
- * power usage, ability to report altitude, speed,
- * and bearing, and monetary cost.
+ * location provider. Providers may be ordered according to accuracy,
+ * power usage, ability to report altitude, speed, bearing, and monetary
+ * cost.
*/
public class Criteria implements Parcelable {
/**
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/AudioManager.java b/media/java/android/media/AudioManager.java
index 85eac4b..c074cce 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -4607,7 +4607,7 @@
/**
* The message sent to apps when the contents of the device list changes if they provide
- * a {#link Handler} object to addOnAudioDeviceConnectionListener().
+ * a {@link Handler} object to addOnAudioDeviceConnectionListener().
*/
private final static int MSG_DEVICES_CALLBACK_REGISTERED = 0;
private final static int MSG_DEVICES_DEVICES_ADDED = 1;
diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java
index 452ba0f..2a575b6 100644
--- a/media/java/android/media/AudioRecord.java
+++ b/media/java/android/media/AudioRecord.java
@@ -473,7 +473,7 @@
* .setSampleRate(32000)
* .setChannelMask(AudioFormat.CHANNEL_IN_MONO)
* .build())
- * .setBufferSize(2*minBuffSize)
+ * .setBufferSizeInBytes(2*minBuffSize)
* .build();
* </pre>
* <p>
diff --git a/media/java/android/media/Image.java b/media/java/android/media/Image.java
index dff5e9a..26b9b8c 100644
--- a/media/java/android/media/Image.java
+++ b/media/java/android/media/Image.java
@@ -75,7 +75,7 @@
/**
* Get the format for this image. This format determines the number of
* ByteBuffers needed to represent the image, and the general layout of the
- * pixel data in each in ByteBuffer.
+ * pixel data in each ByteBuffer.
*
* <p>
* The format is one of the values from
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index ed4da22..18d36eb 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -227,7 +227,7 @@
* transfers the object to the <em>Prepared</em> state once the method call
* returns, or a call to {@link #prepareAsync()} (asynchronous) which
* first transfers the object to the <em>Preparing</em> state after the
- * call returns (which occurs almost right way) while the internal
+ * call returns (which occurs almost right away) while the internal
* player engine continues working on the rest of preparation work
* until the preparation work completes. When the preparation completes or when {@link #prepare()} call returns,
* the internal player engine then calls a user supplied callback method,
@@ -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/MediaPlayer2.java b/media/java/android/media/MediaPlayer2.java
index 7492aa6..8665f28 100644
--- a/media/java/android/media/MediaPlayer2.java
+++ b/media/java/android/media/MediaPlayer2.java
@@ -30,6 +30,8 @@
import android.view.Surface;
import android.view.SurfaceHolder;
+import dalvik.system.CloseGuard;
+
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.InputStream;
@@ -45,425 +47,202 @@
/**
* @hide
- * MediaPlayer2 class can be used to control playback
- * of audio/video files and streams. An example on how to use the methods in
- * this class can be found in {@link android.widget.VideoView}.
+ *
+ * MediaPlayer2 class can be used to control playback of audio/video files and streams.
*
* <p>Topics covered here are:
* <ol>
- * <li><a href="#StateDiagram">State Diagram</a>
- * <li><a href="#Valid_and_Invalid_States">Valid and Invalid States</a>
+ * <li><a href="#PlayerStates">Player states</a>
+ * <li><a href="#InvalidStates">Invalid method calls</a>
* <li><a href="#Permissions">Permissions</a>
- * <li><a href="#Callbacks">Register informational and error callbacks</a>
+ * <li><a href="#Callbacks">Callbacks</a>
* </ol>
*
- * <div class="special reference">
- * <h3>Developer Guides</h3>
- * <p>For more information about how to use MediaPlayer2, read the
- * <a href="{@docRoot}guide/topics/media/mediaplayer.html">Media Playback</a> developer guide.</p>
- * </div>
*
- * <a name="StateDiagram"></a>
- * <h3>State Diagram</h3>
+ * <h3 id="PlayerStates">Player states</h3>
*
- * <p>Playback control of audio/video files and streams is managed as a state
- * machine. The following diagram shows the life cycle and the states of a
- * MediaPlayer2 object driven by the supported playback control operations.
- * The ovals represent the states a MediaPlayer2 object may reside
- * in. The arcs represent the playback control operations that drive the object
- * state transition. There are two types of arcs. The arcs with a single arrow
- * head represent synchronous method calls, while those with
- * a double arrow head represent asynchronous method calls.</p>
+ * <p>The playback control of audio/video files is managed as a state machine.</p>
+ * <p><div style="text-align:center;"><img src="../../../images/mediaplayer2_state_diagram.png"
+ * alt="MediaPlayer2 State diagram"
+ * border="0" /></div></p>
+ * <p>The MediaPlayer2 object has five states:</p>
+ * <ol>
+ * <li><p>{@link #PLAYER_STATE_IDLE}: MediaPlayer2 is in the <strong>Idle</strong>
+ * state after you create it using
+ * {@link #create()}, or after calling {@link #reset()}.</p>
*
- * <p><img src="../../../images/mediaplayer_state_diagram.gif"
- * alt="MediaPlayer State diagram"
- * border="0" /></p>
+ * <p>While in this state, you should call
+ * {@link #setDataSource(DataSourceDesc2) setDataSource()}. It is a good
+ * programming practice to register an {@link EventCallback#onCallCompleted onCallCompleted}
+ * <a href="#Callbacks">callback</a> and watch for {@link #CALL_STATUS_BAD_VALUE} and
+ * {@link #CALL_STATUS_ERROR_IO}, which might be caused by <code>setDataSource</code>.
+ * </p>
*
- * <p>From this state diagram, one can see that a MediaPlayer2 object has the
- * following states:</p>
+ * <p>Calling {@link #prepare()} transfers a MediaPlayer2 object to
+ * the <strong>Prepared</strong> state. Note
+ * that {@link #prepare()} is asynchronous. When the preparation completes,
+ * if you register an {@link EventCallback#onInfo onInfo} <a href="#Callbacks">callback</a>,
+ * the player executes the callback
+ * with {@link #MEDIA_INFO_PREPARED} and transitions to the
+ * <strong>Prepared</strong> state.</p>
+ * </li>
+ *
+ * <li>{@link #PLAYER_STATE_PREPARED}: A MediaPlayer object must be in the
+ * <strong>Prepared</strong> state before playback can be started for the first time.
+ * While in this state, you can set player properties
+ * such as audio/sound volume and looping by invoking the corresponding set methods.
+ * Calling {@link #play()} transfers a MediaPlayer2 object to
+ * the <strong>Playing</strong> state.
+ * </li>
+ *
+ * <li>{@link #PLAYER_STATE_PLAYING}:
+ * <p>The player plays the data source while in this state.
+ * If you register an {@link EventCallback#onInfo onInfo} <a href="#Callbacks">callback</a>,
+ * the player regularly executes the callback with
+ * {@link #MEDIA_INFO_BUFFERING_UPDATE}.
+ * This allows applications to keep track of the buffering status
+ * while streaming audio/video.</p>
+ *
+ * <p> When the playback reaches the end of stream, the behavior depends on whether or
+ * not you've enabled looping by calling {@link #loopCurrent(boolean) loopCurrent}:</p>
+ * <ul>
+ * <li>If the looping mode was set to <code>false</code>, the player will transfer
+ * to the <strong>Paused</strong> state. If you registered an {@link EventCallback#onInfo
+ * onInfo} <a href="#Callbacks">callback</a>
+ * the player calls the callback with {@link #MEDIA_INFO_DATA_SOURCE_END} and enters
+ * the <strong>Paused</strong> state.
+ * </li>
+ * <li>If the looping mode was set to <code>true</code>,
+ * the MediaPlayer2 object remains in the <strong>Playing</strong> state and replays its
+ * data source from the beginning.</li>
+ * </ul>
+ * </li>
+ *
+ * <li>{@link #PLAYER_STATE_PAUSED}: Audio/video playback pauses while in this state.
+ * Call {@link #play()} to resume playback from the position where it paused.</li>
+ *
+ * <li>{@link #PLAYER_STATE_ERROR}: <p>In general, playback might fail due to various
+ * reasons such as unsupported audio/video format, poorly interleaved
+ * audio/video, resolution too high, streaming timeout, and others.
+ * In addition, due to programming errors, a playback
+ * control operation might be performed from an <a href="#InvalidStates">invalid state</a>.
+ * In these cases the player transitions to the <strong>Error</strong> state.</p>
+ *
+ * <p>If you register an {@link EventCallback#onError onError}}
+ * <a href="#Callbacks">callback</a>,
+ * the callback will be performed when entering the state. When programming errors happen,
+ * such as calling {@link #prepare() prepare} and
+ * {@link #setDataSource(DataSourceDesc) setDataSource} methods
+ * from an <a href="#InvalidStates">invalid state</a>, the callback is called with
+ * {@link #CALL_STATUS_INVALID_OPERATION}. The MediaPlayer2 object enters the
+ * <strong>Error</strong> state whether or not a callback exists. </p>
+ *
+ * <p>To recover from an error and reuse a MediaPlayer2 object that is in the <strong>
+ * Error</strong> state,
+ * call {@link #reset() reset}. The object will return to the <strong>Idle</strong>
+ * state and all state information will be lost.</p>
+ * </li>
+ * </ol>
+ *
+ * <p>You should follow these best practices when coding an app that uses MediaPlayer2:</p>
+ *
* <ul>
- * <li>When a MediaPlayer2 object is just created using <code>create</code> or
- * after {@link #reset()} is called, it is in the <em>Idle</em> state; and after
- * {@link #close()} is called, it is in the <em>End</em> state. Between these
- * two states is the life cycle of the MediaPlayer2 object.
- * <ul>
- * <li> It is a programming error to invoke methods such
- * as {@link #getCurrentPosition()},
- * {@link #getDuration()}, {@link #getVideoHeight()},
- * {@link #getVideoWidth()}, {@link #setAudioAttributes(AudioAttributes)},
- * {@link #setPlayerVolume(float)}, {@link #pause()}, {@link #play()},
- * {@link #seekTo(long, int)} or
- * {@link #prepare()} in the <em>Idle</em> state.
- * <li>It is also recommended that once
- * a MediaPlayer2 object is no longer being used, call {@link #close()} immediately
- * so that resources used by the internal player engine associated with the
- * MediaPlayer2 object can be released immediately. Resource may include
- * singleton resources such as hardware acceleration components and
- * failure to call {@link #close()} may cause subsequent instances of
- * MediaPlayer2 objects to fallback to software implementations or fail
- * altogether. Once the MediaPlayer2
- * object is in the <em>End</em> state, it can no longer be used and
- * there is no way to bring it back to any other state. </li>
- * <li>Furthermore,
- * the MediaPlayer2 objects created using <code>new</code> is in the
- * <em>Idle</em> state.
- * </li>
- * </ul>
- * </li>
- * <li>In general, some playback control operation may fail due to various
- * reasons, such as unsupported audio/video format, poorly interleaved
- * audio/video, resolution too high, streaming timeout, and the like.
- * Thus, error reporting and recovery is an important concern under
- * these circumstances. Sometimes, due to programming errors, invoking a playback
- * control operation in an invalid state may also occur. Under all these
- * error conditions, the internal player engine invokes a user supplied
- * EventCallback.onError() method if an EventCallback has been
- * registered beforehand via
- * {@link #setEventCallback(Executor, EventCallback)}.
- * <ul>
- * <li>It is important to note that once an error occurs, the
- * MediaPlayer2 object enters the <em>Error</em> state (except as noted
- * above), even if an error listener has not been registered by the application.</li>
- * <li>In order to reuse a MediaPlayer2 object that is in the <em>
- * Error</em> state and recover from the error,
- * {@link #reset()} can be called to restore the object to its <em>Idle</em>
- * state.</li>
- * <li>It is good programming practice to have your application
- * register a OnErrorListener to look out for error notifications from
- * the internal player engine.</li>
- * <li>IllegalStateException is
- * thrown to prevent programming errors such as calling
- * {@link #prepare()}, {@link #setDataSource(DataSourceDesc)}
- * methods in an invalid state. </li>
- * </ul>
- * </li>
- * <li>Calling
- * {@link #setDataSource(DataSourceDesc)} transfers a
- * MediaPlayer2 object in the <em>Idle</em> state to the
- * <em>Initialized</em> state.
- * <ul>
- * <li>An IllegalStateException is thrown if
- * setDataSource() is called in any other state.</li>
- * <li>It is good programming
- * practice to always look out for <code>IllegalArgumentException</code>
- * and <code>IOException</code> that may be thrown from
- * <code>setDataSource</code>.</li>
- * </ul>
- * </li>
- * <li>A MediaPlayer2 object must first enter the <em>Prepared</em> state
- * before playback can be started.
- * <ul>
- * <li>There are an asynchronous way that the <em>Prepared</em> state can be reached:
- * a call to {@link #prepare()} (asynchronous) which
- * first transfers the object to the <em>Preparing</em> state after the
- * call returns (which occurs almost right way) while the internal
- * player engine continues working on the rest of preparation work
- * until the preparation work completes. When the preparation completes,
- * the internal player engine then calls a user supplied callback method,
- * onInfo() of the EventCallback interface with {@link #MEDIA_INFO_PREPARED},
- * if an EventCallback is registered beforehand via
- * {@link #setEventCallback(Executor, EventCallback)}.</li>
- * <li>It is important to note that
- * the <em>Preparing</em> state is a transient state, and the behavior
- * of calling any method with side effect while a MediaPlayer2 object is
- * in the <em>Preparing</em> state is undefined.</li>
- * <li>An IllegalStateException is
- * thrown if {@link #prepare()} is called in
- * any other state.</li>
- * <li>While in the <em>Prepared</em> state, properties
- * such as audio/sound volume, screenOnWhilePlaying, looping can be
- * adjusted by invoking the corresponding set methods.</li>
- * </ul>
- * </li>
- * <li>To start the playback, {@link #play()} must be called. After
- * {@link #play()} returns successfully, the MediaPlayer2 object is in the
- * <em>Started</em> state. {@link #getPlayerState()} can be called to test
- * whether the MediaPlayer2 object is in the <em>Started</em> state.
- * <ul>
- * <li>While in the <em>Started</em> state, the internal player engine calls
- * a user supplied callback method EventCallback.onInfo() with
- * {@link #MEDIA_INFO_BUFFERING_UPDATE} if an EventCallback has been
- * registered beforehand via
- * {@link #setEventCallback(Executor, EventCallback)}.
- * This callback allows applications to keep track of the buffering status
- * while streaming audio/video.</li>
- * <li>Calling {@link #play()} has not effect
- * on a MediaPlayer2 object that is already in the <em>Started</em> state.</li>
- * </ul>
- * </li>
- * <li>Playback can be paused and stopped, and the current playback position
- * can be adjusted. Playback can be paused via {@link #pause()}. When the call to
- * {@link #pause()} returns, the MediaPlayer2 object enters the
- * <em>Paused</em> state. Note that the transition from the <em>Started</em>
- * state to the <em>Paused</em> state and vice versa happens
- * asynchronously in the player engine. It may take some time before
- * the state is updated in calls to {@link #getPlayerState()}, and it can be
- * a number of seconds in the case of streamed content.
- * <ul>
- * <li>Calling {@link #play()} to resume playback for a paused
- * MediaPlayer2 object, and the resumed playback
- * position is the same as where it was paused. When the call to
- * {@link #play()} returns, the paused MediaPlayer2 object goes back to
- * the <em>Started</em> state.</li>
- * <li>Calling {@link #pause()} has no effect on
- * a MediaPlayer2 object that is already in the <em>Paused</em> state.</li>
- * </ul>
- * </li>
- * <li>The playback position can be adjusted with a call to
- * {@link #seekTo(long, int)}.
- * <ul>
- * <li>Although the asynchronuous {@link #seekTo(long, int)}
- * call returns right away, the actual seek operation may take a while to
- * finish, especially for audio/video being streamed. When the actual
- * seek operation completes, the internal player engine calls a user
- * supplied EventCallback.onCallCompleted() with
- * {@link #CALL_COMPLETED_SEEK_TO}
- * if an EventCallback has been registered beforehand via
- * {@link #setEventCallback(Executor, EventCallback)}.</li>
- * <li>Please
- * note that {@link #seekTo(long, int)} can also be called in the other states,
- * such as <em>Prepared</em>, <em>Paused</em> and <em>PlaybackCompleted
- * </em> state. When {@link #seekTo(long, int)} is called in those states,
- * one video frame will be displayed if the stream has video and the requested
- * position is valid.
- * </li>
- * <li>Furthermore, the actual current playback position
- * can be retrieved with a call to {@link #getCurrentPosition()}, which
- * is helpful for applications such as a Music player that need to keep
- * track of the playback progress.</li>
- * </ul>
- * </li>
- * <li>When the playback reaches the end of stream, the playback completes.
- * <ul>
- * <li>If current source is set to loop by {@link #loopCurrent(boolean)},
- * the MediaPlayer2 object shall remain in the <em>Started</em> state.</li>
- * <li>If the looping mode was set to <var>false
- * </var>, the player engine calls a user supplied callback method,
- * EventCallback.onCompletion(), if an EventCallback is
- * registered beforehand via
- * {@link #setEventCallback(Executor, EventCallback)}.
- * The invoke of the callback signals that the object is now in the <em>
- * PlaybackCompleted</em> state.</li>
- * <li>While in the <em>PlaybackCompleted</em>
- * state, calling {@link #play()} can restart the playback from the
- * beginning of the audio/video source.</li>
+ *
+ * <li>Use <a href="#Callbacks">callbacks</a> to respond to state changes and errors.</li>
+ *
+ * <li>When a MediaPlayer2 object is no longer being used, call {@link #close() close} as soon as
+ * possible to release the resources used by the internal player engine associated with the
+ * MediaPlayer2. Failure to call {@link #close() close} may cause subsequent instances of
+ * MediaPlayer2 objects to fallback to software implementations or fail altogether.
+ * You cannot use MediaPlayer2
+ * after you call {@link #close() close}. There is no way to bring it back to any other state.</li>
+ *
+ * <li>The current playback position can be retrieved with a call to
+ * {@link #getCurrentPosition() getCurrentPosition},
+ * which is helpful for applications such as a Music player that need to keep track of the playback
+ * progress.</li>
+ *
+ * <li>The playback position can be adjusted with a call to {@link #seekTo seekTo}. Although the
+ * asynchronous {@link #seekTo seekTo} call returns right away, the actual seek operation may take a
+ * while to finish, especially for audio/video being streamed. If you register an
+ * {@link EventCallback#onCallCompleted onCallCompleted} <a href="#Callbacks">callback</a>,
+ * the callback is
+ * called When the seek operation completes with {@link #CALL_COMPLETED_SEEK_TO}.</li>
+ *
+ * <li>You can call {@link #seekTo seekTo} from the <strong>Paused</strong> state.
+ * In this case, if you are playing a video stream and
+ * the requested position is valid one video frame is displayed.</li>
+ *
* </ul>
*
+ * <h3 id="InvalidStates">Invalid method calls</h3>
*
- * <a name="Valid_and_Invalid_States"></a>
- * <h3>Valid and invalid states</h3>
+ * <p>The only methods you safely call from the <strong>Error</strong> state are
+ * {@link #close() close},
+ * {@link #reset() reset},
+ * {@link #notifyWhenCommandLabelReached notifyWhenCommandLabelReached},
+ * {@link #clearPendingCommands() clearPendingCommands},
+ * {@link #setEventCallback setEventCallback},
+ * {@link #clearEventCallback() clearEventCallback}
+ * and {@link #getState() getState}.
+ * Any other methods might throw an exception, return meaningless data, or invoke a
+ * {@link EventCallback#onCallCompleted onCallCompleted} with an error code.</p>
+ *
+ * <p>Most methods can be called from any non-Error state. They will either perform their work or
+ * silently have no effect. The following table lists the methods that will invoke a
+ * {@link EventCallback#onCallCompleted onCallCompleted} with an error code
+ * or throw an exception when they are called from the associated invalid states.</p>
*
* <table border="0" cellspacing="0" cellpadding="0">
- * <tr><td>Method Name </p></td>
- * <td>Valid Sates </p></td>
- * <td>Invalid States </p></td>
- * <td>Comments </p></td></tr>
- * <tr><td>attachAuxEffect </p></td>
- * <td>{Initialized, Prepared, Started, Paused, Stopped, PlaybackCompleted} </p></td>
- * <td>{Idle, Error} </p></td>
- * <td>This method must be called after setDataSource.
- * Calling it does not change the object state. </p></td></tr>
- * <tr><td>getAudioSessionId </p></td>
- * <td>any </p></td>
- * <td>{} </p></td>
- * <td>This method can be called in any state and calling it does not change
- * the object state. </p></td></tr>
- * <tr><td>getCurrentPosition </p></td>
- * <td>{Idle, Initialized, Prepared, Started, Paused, Stopped,
- * PlaybackCompleted} </p></td>
- * <td>{Error}</p></td>
- * <td>Successful invoke of this method in a valid state does not change the
- * state. Calling this method in an invalid state transfers the object
- * to the <em>Error</em> state. </p></td></tr>
- * <tr><td>getDuration </p></td>
- * <td>{Prepared, Started, Paused, Stopped, PlaybackCompleted} </p></td>
- * <td>{Idle, Initialized, Error} </p></td>
- * <td>Successful invoke of this method in a valid state does not change the
- * state. Calling this method in an invalid state transfers the object
- * to the <em>Error</em> state. </p></td></tr>
- * <tr><td>getVideoHeight </p></td>
- * <td>{Idle, Initialized, Prepared, Started, Paused, Stopped,
- * PlaybackCompleted}</p></td>
- * <td>{Error}</p></td>
- * <td>Successful invoke of this method in a valid state does not change the
- * state. Calling this method in an invalid state transfers the object
- * to the <em>Error</em> state. </p></td></tr>
- * <tr><td>getVideoWidth </p></td>
- * <td>{Idle, Initialized, Prepared, Started, Paused, Stopped,
- * PlaybackCompleted}</p></td>
- * <td>{Error}</p></td>
- * <td>Successful invoke of this method in a valid state does not change
- * the state. Calling this method in an invalid state transfers the
- * object to the <em>Error</em> state. </p></td></tr>
- * <tr><td>getPlayerState </p></td>
- * <td>{Idle, Initialized, Prepared, Started, Paused, Stopped,
- * PlaybackCompleted}</p></td>
- * <td>{Error}</p></td>
- * <td>Successful invoke of this method in a valid state does not change
- * the state. Calling this method in an invalid state transfers the
- * object to the <em>Error</em> state. </p></td></tr>
- * <tr><td>pause </p></td>
- * <td>{Started, Paused, PlaybackCompleted}</p></td>
- * <td>{Idle, Initialized, Prepared, Stopped, Error}</p></td>
- * <td>Successful invoke of this method in a valid state transfers the
- * object to the <em>Paused</em> state. Calling this method in an
- * invalid state transfers the object to the <em>Error</em> state.</p></td></tr>
- * <tr><td>prepare </p></td>
- * <td>{Initialized, Stopped} </p></td>
- * <td>{Idle, Prepared, Started, Paused, PlaybackCompleted, Error} </p></td>
- * <td>Successful invoke of this method in a valid state transfers the
- * object to the <em>Preparing</em> state. Calling this method in an
- * invalid state throws an IllegalStateException.</p></td></tr>
- * <tr><td>release </p></td>
- * <td>any </p></td>
- * <td>{} </p></td>
- * <td>After {@link #close()}, the object is no longer available. </p></td></tr>
- * <tr><td>reset </p></td>
- * <td>{Idle, Initialized, Prepared, Started, Paused, Stopped,
- * PlaybackCompleted, Error}</p></td>
- * <td>{}</p></td>
- * <td>After {@link #reset()}, the object is like being just created.</p></td></tr>
- * <tr><td>seekTo </p></td>
- * <td>{Prepared, Started, Paused, PlaybackCompleted} </p></td>
- * <td>{Idle, Initialized, Stopped, Error}</p></td>
- * <td>Successful invoke of this method in a valid state does not change
- * the state. Calling this method in an invalid state transfers the
- * object to the <em>Error</em> state. </p></td></tr>
- * <tr><td>setAudioAttributes </p></td>
- * <td>{Idle, Initialized, Stopped, Prepared, Started, Paused,
- * PlaybackCompleted}</p></td>
- * <td>{Error}</p></td>
- * <td>Successful invoke of this method does not change the state. In order for the
- * target audio attributes type to become effective, this method must be called before
- * prepare().</p></td></tr>
- * <tr><td>setAudioSessionId </p></td>
- * <td>{Idle} </p></td>
- * <td>{Initialized, Prepared, Started, Paused, Stopped, PlaybackCompleted,
- * Error} </p></td>
- * <td>This method must be called in idle state as the audio session ID must be known before
- * calling setDataSource. Calling it does not change the object
- * state. </p></td></tr>
- * <tr><td>setAudioStreamType (deprecated)</p></td>
- * <td>{Idle, Initialized, Stopped, Prepared, Started, Paused,
- * PlaybackCompleted}</p></td>
- * <td>{Error}</p></td>
- * <td>Successful invoke of this method does not change the state. In order for the
- * target audio stream type to become effective, this method must be called before
- * prepare().</p></td></tr>
- * <tr><td>setAuxEffectSendLevel </p></td>
- * <td>any</p></td>
- * <td>{} </p></td>
- * <td>Calling this method does not change the object state. </p></td></tr>
- * <tr><td>setDataSource </p></td>
- * <td>{Idle} </p></td>
- * <td>{Initialized, Prepared, Started, Paused, Stopped, PlaybackCompleted,
- * Error} </p></td>
- * <td>Successful invoke of this method in a valid state transfers the
- * object to the <em>Initialized</em> state. Calling this method in an
- * invalid state throws an IllegalStateException.</p></td></tr>
- * <tr><td>setDisplay </p></td>
- * <td>any </p></td>
- * <td>{} </p></td>
- * <td>This method can be called in any state and calling it does not change
- * the object state. </p></td></tr>
- * <tr><td>setSurface </p></td>
- * <td>any </p></td>
- * <td>{} </p></td>
- * <td>This method can be called in any state and calling it does not change
- * the object state. </p></td></tr>
- * <tr><td>loopCurrent </p></td>
- * <td>{Idle, Initialized, Stopped, Prepared, Started, Paused,
- * PlaybackCompleted}</p></td>
- * <td>{Error}</p></td>
- * <td>Successful invoke of this method in a valid state does not change
- * the state. Calling this method in an
- * invalid state transfers the object to the <em>Error</em> state.</p></td></tr>
- * <tr><td>isLooping </p></td>
- * <td>any </p></td>
- * <td>{} </p></td>
- * <td>This method can be called in any state and calling it does not change
- * the object state. </p></td></tr>
- * <tr><td>setDrmEventCallback </p></td>
- * <td>any </p></td>
- * <td>{} </p></td>
- * <td>This method can be called in any state and calling it does not change
- * the object state. </p></td></tr>
- * <tr><td>setEventCallback </p></td>
- * <td>any </p></td>
- * <td>{} </p></td>
- * <td>This method can be called in any state and calling it does not change
- * the object state. </p></td></tr>
- * <tr><td>setPlaybackParams</p></td>
- * <td>{Initialized, Prepared, Started, Paused, PlaybackCompleted, Error}</p></td>
- * <td>{Idle, Stopped} </p></td>
- * <td>This method will change state in some cases, depending on when it's called.
- * </p></td></tr>
- * <tr><td>setPlayerVolume </p></td>
- * <td>{Idle, Initialized, Stopped, Prepared, Started, Paused,
- * PlaybackCompleted}</p></td>
- * <td>{Error}</p></td>
- * <td>Successful invoke of this method does not change the state.
- * <tr><td>play </p></td>
- * <td>{Prepared, Started, Paused, PlaybackCompleted}</p></td>
- * <td>{Idle, Initialized, Stopped, Error}</p></td>
- * <td>Successful invoke of this method in a valid state transfers the
- * object to the <em>Started</em> state. Calling this method in an
- * invalid state transfers the object to the <em>Error</em> state.</p></td></tr>
- * <tr><td>stop </p></td>
- * <td>{Prepared, Started, Stopped, Paused, PlaybackCompleted}</p></td>
- * <td>{Idle, Initialized, Error}</p></td>
- * <td>Successful invoke of this method in a valid state transfers the
- * object to the <em>Stopped</em> state. Calling this method in an
- * invalid state transfers the object to the <em>Error</em> state.</p></td></tr>
- * <tr><td>getTrackInfo </p></td>
- * <td>{Prepared, Started, Stopped, Paused, PlaybackCompleted}</p></td>
- * <td>{Idle, Initialized, Error}</p></td>
- * <td>Successful invoke of this method does not change the state.</p></td></tr>
- * <tr><td>selectTrack </p></td>
- * <td>{Prepared, Started, Stopped, Paused, PlaybackCompleted}</p></td>
- * <td>{Idle, Initialized, Error}</p></td>
- * <td>Successful invoke of this method does not change the state.</p></td></tr>
- * <tr><td>deselectTrack </p></td>
- * <td>{Prepared, Started, Stopped, Paused, PlaybackCompleted}</p></td>
- * <td>{Idle, Initialized, Error}</p></td>
- * <td>Successful invoke of this method does not change the state.</p></td></tr>
+ * <tr><th>Method Name</th>
+ * <th>Invalid States</th></tr>
*
+ * <tr><td>setDataSource</td> <td>{Prepared, Paused, Playing}</td></tr>
+ * <tr><td>prepare</td> <td>{Prepared, Paused, Playing}</td></tr>
+ * <tr><td>play</td> <td>{Idle}</td></tr>
+ * <tr><td>pause</td> <td>{Idle}</td></tr>
+ * <tr><td>seekTo</td> <td>{Idle}</td></tr>
+ * <tr><td>getCurrentPosition</td> <td>{Idle}</td></tr>
+ * <tr><td>getDuration</td> <td>{Idle}</td></tr>
+ * <tr><td>getBufferedPosition</td> <td>{Idle}</td></tr>
+ * <tr><td>getTrackInfo</td> <td>{Idle}</td></tr>
+ * <tr><td>getSelectedTrack</td> <td>{Idle}</td></tr>
+ * <tr><td>selectTrack</td> <td>{Idle}</td></tr>
+ * <tr><td>deselectTrack</td> <td>{Idle}</td></tr>
* </table>
*
- * <a name="Permissions"></a>
- * <h3>Permissions</h3>
- * <p>One may need to declare a corresponding WAKE_LOCK permission {@link
- * android.R.styleable#AndroidManifestUsesPermission <uses-permission>}
- * element.
- *
+ * <h3 id="Permissions">Permissions</h3>
* <p>This class requires the {@link android.Manifest.permission#INTERNET} permission
* when used with network-based content.
*
- * <a name="Callbacks"></a>
- * <h3>Callbacks</h3>
- * <p>Applications may want to register for informational and error
- * events in order to be informed of some internal state update and
- * possible runtime errors during playback or streaming. Registration for
- * these events is done by properly setting the appropriate listeners (via calls
- * to
- * {@link #setEventCallback(Executor, EventCallback)},
- * {@link #setDrmEventCallback(Executor, DrmEventCallback)}).
- * In order to receive the respective callback
- * associated with these listeners, applications are required to create
- * MediaPlayer2 objects on a thread with its own Looper running (main UI
- * thread by default has a Looper running).
+ * <h3 id="Callbacks">Callbacks</h3>
+ * <p>Many errors do not result in a transition to the <strong>Error</strong> state.
+ * It is good programming practice to register callback listeners using
+ * {@link #setEventCallback(Executor, EventCallback) setEventCallback} and
+ * {@link #setDrmEventCallback(Executor, DrmEventCallback) setDrmEventCallback}).
+ * You can receive a callback at any time and from any state.</p>
*
+ * <p>If it's important for your app to respond to state changes (for instance, to update the
+ * controls on a transport UI), you should register an
+ * {@link EventCallback#onCallCompleted onCallCompleted} and
+ * detect state change commands by testing the <code>what</code> parameter for a callback from one
+ * of the state transition methods: {@link #CALL_COMPLETED_PREPARE}, {@link #CALL_COMPLETED_PLAY},
+ * and {@link #CALL_COMPLETED_PAUSE}.
+ * Then check the <code>status</code> parameter. The value {@link #CALL_STATUS_NO_ERROR} indicates a
+ * successful transition. Any other value will be an error. Call {@link #getState()} to
+ * determine the current state. </p>
*/
public abstract class MediaPlayer2 implements SubtitleController.Listener
, AutoCloseable
, AudioRouting {
+ private final CloseGuard mGuard = CloseGuard.get();
+
/**
* Create a MediaPlayer2 object.
*
* @return A MediaPlayer2 object created
*/
public static final MediaPlayer2 create() {
- // TODO: load MediaUpdate APK
return new MediaPlayer2Impl();
}
@@ -512,7 +291,9 @@
* @hide
*/
// add hidden empty constructor so it doesn't show in SDK
- public MediaPlayer2() { }
+ public MediaPlayer2() {
+ mGuard.open("close");
+ }
/**
* Returns a {@link MediaPlayerBase} implementation which runs based on
@@ -545,7 +326,22 @@
*/
// This is a synchronous call.
@Override
- public abstract void close();
+ public void close() {
+ synchronized (mGuard) {
+ mGuard.close();
+ }
+ }
+
+ // Have to declare protected for finalize() since it is protected
+ // in the base class Object.
+ @Override
+ protected void finalize() throws Throwable {
+ if (mGuard != null) {
+ mGuard.warnIfOpen();
+ }
+
+ close();
+ }
/**
* Starts or resumes playback. If playback had previously been paused,
diff --git a/media/java/android/media/MediaPlayer2Impl.java b/media/java/android/media/MediaPlayer2Impl.java
index 6b48f09..678cb9a 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;
@@ -55,12 +53,8 @@
import com.android.framework.protobuf.InvalidProtocolBufferException;
import com.android.internal.annotations.GuardedBy;
-import com.android.internal.util.Preconditions;
-
-import dalvik.system.CloseGuard;
import libcore.io.IoBridge;
-import libcore.io.Streams;
import java.io.ByteArrayOutputStream;
import java.io.File;
@@ -82,7 +76,6 @@
import java.util.List;
import java.util.Map;
import java.util.Scanner;
-import java.util.Set;
import java.util.UUID;
import java.util.Vector;
import java.util.concurrent.Executor;
@@ -108,7 +101,6 @@
private boolean mScreenOnWhilePlaying;
private boolean mStayAwake;
private int mStreamType = AudioManager.USE_DEFAULT_STREAM_TYPE;
- private final CloseGuard mGuard = CloseGuard.get();
private final Object mSrcLock = new Object();
//--- guarded by |mSrcLock| start
@@ -148,6 +140,9 @@
@GuardedBy("mTaskLock")
private Task mCurrentTask;
+ @GuardedBy("this")
+ private boolean mReleased;
+
/**
* Default constructor.
* <p>When done with the MediaPlayer2Impl, you should call {@link #close()},
@@ -162,7 +157,6 @@
mTimeProvider = new TimeProvider(this);
mOpenSubtitleSources = new Vector<InputStream>();
- mGuard.open("close");
/* Native setup requires a weak reference to our object.
* It's easier to create it here than in C++.
@@ -200,9 +194,8 @@
*/
@Override
public void close() {
- synchronized (mGuard) {
- release();
- }
+ super.close();
+ release();
}
/**
@@ -254,22 +247,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;
- }
- sendEvent(new EventNotifier() {
- @Override
- public void notify(EventCallback callback) {
- callback.onInfo(
- MediaPlayer2Impl.this, dsd, MEDIA_INFO_DATA_SOURCE_START, 0);
- }
- });
- }
-
_pause();
}
});
@@ -287,7 +264,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();
}
});
}
@@ -348,19 +328,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;
}
@@ -374,7 +349,7 @@
addTask(new Task(CALL_COMPLETED_SET_DATA_SOURCE, false) {
@Override
void process() throws IOException {
- Preconditions.checkArgument(dsd != null, "the DataSourceDesc cannot be null");
+ checkArgument(dsd != null, "the DataSourceDesc cannot be null");
int state = getState();
if (state != PLAYER_STATE_ERROR && state != PLAYER_STATE_IDLE) {
throw new IllegalStateException("called in wrong state " + state);
@@ -400,7 +375,7 @@
addTask(new Task(CALL_COMPLETED_SET_NEXT_DATA_SOURCE, false) {
@Override
void process() {
- Preconditions.checkArgument(dsd != null, "the DataSourceDesc cannot be null");
+ checkArgument(dsd != null, "the DataSourceDesc cannot be null");
synchronized (mSrcLock) {
mNextDSDs = new ArrayList<DataSourceDesc>(1);
mNextDSDs.add(dsd);
@@ -714,7 +689,7 @@
private void handleDataSource(boolean isCurrent, @NonNull DataSourceDesc dsd, long srcId)
throws IOException {
- Preconditions.checkNotNull(dsd, "the DataSourceDesc cannot be null");
+ checkArgument(dsd != null, "the DataSourceDesc cannot be null");
switch (dsd.getType()) {
case DataSourceDesc.TYPE_CALLBACK:
@@ -1314,7 +1289,7 @@
addTask(new Task(CALL_COMPLETED_SET_BUFFERING_PARAMS, false) {
@Override
void process() {
- Preconditions.checkArgument(params != null, "the BufferingParams cannot be null");
+ checkArgument(params != null, "the BufferingParams cannot be null");
_setBufferingParams(params);
}
});
@@ -1378,7 +1353,7 @@
addTask(new Task(CALL_COMPLETED_SET_PLAYBACK_PARAMS, false) {
@Override
void process() {
- Preconditions.checkArgument(params != null, "the PlaybackParams cannot be null");
+ checkArgument(params != null, "the PlaybackParams cannot be null");
_setPlaybackParams(params);
}
});
@@ -1411,7 +1386,7 @@
addTask(new Task(CALL_COMPLETED_SET_SYNC_PARAMS, false) {
@Override
void process() {
- Preconditions.checkArgument(params != null, "the SyncParams cannot be null");
+ checkArgument(params != null, "the SyncParams cannot be null");
_setSyncParams(params);
}
});
@@ -1601,9 +1576,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);
/**
@@ -2468,15 +2443,14 @@
// in the base class Object.
@Override
protected void finalize() throws Throwable {
- if (mGuard != null) {
- mGuard.warnIfOpen();
- }
-
- close();
+ super.finalize();
native_finalize();
}
- private void release() {
+ private synchronized void release() {
+ if (mReleased) {
+ return;
+ }
stayAwake(false);
updateSurfaceScreenOn();
synchronized (mEventCbLock) {
@@ -2499,6 +2473,7 @@
resetDrmState();
_release();
+ mReleased = true;
}
private native void _release();
@@ -3051,6 +3026,12 @@
}
}
+ public static void checkArgument(boolean expression, String errorMessage) {
+ if (!expression) {
+ throw new IllegalArgumentException(errorMessage);
+ }
+ }
+
private void sendEvent(final EventNotifier notifier) {
synchronized (mEventCbLock) {
try {
@@ -3694,7 +3675,7 @@
supportedSchemes = new UUID[supportedDRMsCount];
for (int i = 0; i < supportedDRMsCount; i++) {
byte[] uuid = new byte[16];
- in.next().getBytesValue().copyTo(uuid, uuid.length);
+ in.next().getBytesValue().copyTo(uuid, 0);
supportedSchemes[i] = bytesToUUID(uuid);
@@ -3702,7 +3683,7 @@
supportedSchemes[i]);
}
- Log.v(TAG, "DrmInfoImpl() Parcel psshsize: " + pssh.length +
+ Log.v(TAG, "DrmInfoImpl() psshsize: " + pssh.length +
" supportedDRMsCount: " + supportedDRMsCount);
}
@@ -3827,7 +3808,7 @@
private native void _prepareDrm(@NonNull byte[] uuid, @NonNull byte[] drmSessionId);
- // Modular DRM helpers
+ // Modular DRM helpers
private void prepareDrm_createDrmStep(@NonNull UUID uuid)
throws UnsupportedSchemeException {
@@ -3967,7 +3948,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);
@@ -4047,6 +4028,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) {
@@ -4682,7 +4686,7 @@
private void sendCompleteNotification(int status) {
// In {@link #notifyWhenCommandLabelReached} case, a separate callback
- // {#link #onCommandLabelReached} is already called in {@code process()}.
+ // {@link #onCommandLabelReached} is already called in {@code process()}.
if (mMediaCallType == CALL_COMPLETED_NOTIFY_WHEN_COMMAND_LABEL_REACHED) {
return;
}
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/java/android/media/ThumbnailUtils.java b/media/java/android/media/ThumbnailUtils.java
index 4cc86fd..fd14060 100644
--- a/media/java/android/media/ThumbnailUtils.java
+++ b/media/java/android/media/ThumbnailUtils.java
@@ -160,7 +160,15 @@
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
try {
retriever.setDataSource(filePath);
- bitmap = retriever.getFrameAtTime(-1);
+ // First retrieve album art in metadata if set.
+ byte[] embeddedPicture = retriever.getEmbeddedPicture();
+ if (embeddedPicture != null && embeddedPicture.length > 0) {
+ bitmap = BitmapFactory.decodeByteArray(embeddedPicture, 0, embeddedPicture.length);
+ }
+ // Fall back to first frame of the video.
+ if (bitmap == null) {
+ bitmap = retriever.getFrameAtTime(-1);
+ }
} catch (IllegalArgumentException ex) {
// Assume this is a corrupt video file
} catch (RuntimeException ex) {
diff --git a/media/jni/OWNERS b/media/jni/OWNERS
new file mode 100644
index 0000000..bb91d4b
--- /dev/null
+++ b/media/jni/OWNERS
@@ -0,0 +1,2 @@
+# extra for MTP related files
+per-file android_mtp_*.cpp=marcone@google.com,jsharkey@android.com,jameswei@google.com,rmojumder@google.com
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/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/com.android.media.remotedisplay.xml b/media/lib/remotedisplay/com.android.media.remotedisplay.xml
deleted file mode 100644
index 77a91d2..0000000
--- a/media/lib/remotedisplay/com.android.media.remotedisplay.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<permissions>
- <library name="com.android.media.remotedisplay"
- file="/system/framework/com.android.media.remotedisplay.jar" />
-</permissions>
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/media/mca/filterpacks/java/android/filterpacks/videosrc/SurfaceTextureSource.java b/media/mca/filterpacks/java/android/filterpacks/videosrc/SurfaceTextureSource.java
index 6595baa..7919723 100644
--- a/media/mca/filterpacks/java/android/filterpacks/videosrc/SurfaceTextureSource.java
+++ b/media/mca/filterpacks/java/android/filterpacks/videosrc/SurfaceTextureSource.java
@@ -38,7 +38,7 @@
* <p>To use, connect up the sourceListener callback, and then when executing
* the graph, use the SurfaceTexture object passed to the callback to feed
* frames into the filter graph. For example, pass the SurfaceTexture into
- * {#link
+ * {@link
* android.hardware.Camera.setPreviewTexture(android.graphics.SurfaceTexture)}.
* This filter is intended for applications that need for flexibility than the
* CameraSource and MediaSource provide. Note that the application needs to
diff --git a/media/tests/MtpTests/OWNERS b/media/tests/MtpTests/OWNERS
new file mode 100644
index 0000000..1928ba8
--- /dev/null
+++ b/media/tests/MtpTests/OWNERS
@@ -0,0 +1,7 @@
+set noparent
+
+marcone@google.com
+jsharkey@android.com
+jameswei@google.com
+rmojumder@google.com
+
diff --git a/native/android/system_fonts.cpp b/native/android/system_fonts.cpp
index b95adad..761a475 100644
--- a/native/android/system_fonts.cpp
+++ b/native/android/system_fonts.cpp
@@ -43,6 +43,9 @@
struct ASystemFontIterator {
XmlDocUniquePtr mXmlDoc;
xmlNode* mFontNode;
+
+ // The OEM customization XML.
+ XmlDocUniquePtr mCustomizationXmlDoc;
};
struct ASystemFont {
@@ -93,29 +96,30 @@
return nullptr;
}
-void copyFont(ASystemFontIterator* ite, ASystemFont* out) {
+void copyFont(const XmlDocUniquePtr& xmlDoc, xmlNode* fontNode, ASystemFont* out,
+ const std::string& pathPrefix) {
const xmlChar* LOCALE_ATTR_NAME = BAD_CAST("lang");
XmlCharUniquePtr filePathStr(
- xmlNodeListGetString(ite->mXmlDoc.get(), ite->mFontNode->xmlChildrenNode, 1));
- out->mFilePath = "/system/fonts/" + xmlTrim(
+ xmlNodeListGetString(xmlDoc.get(), fontNode->xmlChildrenNode, 1));
+ out->mFilePath = pathPrefix + xmlTrim(
std::string(filePathStr.get(), filePathStr.get() + xmlStrlen(filePathStr.get())));
const xmlChar* WEIGHT_ATTR_NAME = BAD_CAST("weight");
- XmlCharUniquePtr weightStr(xmlGetProp(ite->mFontNode, WEIGHT_ATTR_NAME));
+ XmlCharUniquePtr weightStr(xmlGetProp(fontNode, WEIGHT_ATTR_NAME));
out->mWeight = weightStr ?
strtol(reinterpret_cast<const char*>(weightStr.get()), nullptr, 10) : 400;
const xmlChar* STYLE_ATTR_NAME = BAD_CAST("style");
const xmlChar* ITALIC_ATTR_VALUE = BAD_CAST("italic");
- XmlCharUniquePtr styleStr(xmlGetProp(ite->mFontNode, STYLE_ATTR_NAME));
+ XmlCharUniquePtr styleStr(xmlGetProp(fontNode, STYLE_ATTR_NAME));
out->mItalic = styleStr ? xmlStrEqual(styleStr.get(), ITALIC_ATTR_VALUE) : false;
const xmlChar* INDEX_ATTR_NAME = BAD_CAST("index");
- XmlCharUniquePtr indexStr(xmlGetProp(ite->mFontNode, INDEX_ATTR_NAME));
+ XmlCharUniquePtr indexStr(xmlGetProp(fontNode, INDEX_ATTR_NAME));
out->mCollectionIndex = indexStr ?
strtol(reinterpret_cast<const char*>(indexStr.get()), nullptr, 10) : 0;
- XmlCharUniquePtr localeStr(xmlGetProp(ite->mXmlDoc->parent, LOCALE_ATTR_NAME));
+ XmlCharUniquePtr localeStr(xmlGetProp(xmlDoc->parent, LOCALE_ATTR_NAME));
out->mLocale.reset(
localeStr ? new std::string(reinterpret_cast<const char*>(localeStr.get())) : nullptr);
@@ -123,7 +127,7 @@
const xmlChar* STYLEVALUE_ATTR_NAME = BAD_CAST("stylevalue");
const xmlChar* AXIS_TAG = BAD_CAST("axis");
out->mAxes.clear();
- for (xmlNode* axis = firstElement(ite->mFontNode, AXIS_TAG); axis;
+ for (xmlNode* axis = firstElement(fontNode, AXIS_TAG); axis;
axis = nextSibling(axis, AXIS_TAG)) {
XmlCharUniquePtr tagStr(xmlGetProp(axis, TAG_ATTR_NAME));
if (!tagStr || xmlStrlen(tagStr.get()) != 4) {
@@ -154,8 +158,8 @@
return S_ISREG(st.st_mode);
}
-xmlNode* findFirstFontNode(xmlDoc* doc) {
- xmlNode* familySet = xmlDocGetRootElement(doc);
+xmlNode* findFirstFontNode(const XmlDocUniquePtr& doc) {
+ xmlNode* familySet = xmlDocGetRootElement(doc.get());
if (familySet == nullptr) {
return nullptr;
}
@@ -180,6 +184,7 @@
ASystemFontIterator* ASystemFontIterator_open() {
std::unique_ptr<ASystemFontIterator> ite(new ASystemFontIterator());
ite->mXmlDoc.reset(xmlReadFile("/system/etc/fonts.xml", nullptr, 0));
+ ite->mCustomizationXmlDoc.reset(xmlReadFile("/product/etc/fonts_customization.xml", nullptr, 0));
return ite.release();
}
@@ -187,47 +192,64 @@
delete ite;
}
-ASystemFont* ASystemFontIterator_next(ASystemFontIterator* ite) {
- LOG_ALWAYS_FATAL_IF(ite == nullptr, "nullptr has passed as iterator argument");
- if (ite->mFontNode == nullptr) {
- if (ite->mXmlDoc == nullptr) {
+xmlNode* findNextFontNode(const XmlDocUniquePtr& xmlDoc, xmlNode* fontNode) {
+ if (fontNode == nullptr) {
+ if (!xmlDoc) {
return nullptr; // Already at the end.
} else {
// First time to query font.
- ite->mFontNode = findFirstFontNode(ite->mXmlDoc.get());
- if (ite->mFontNode == nullptr) {
- ite->mXmlDoc.reset();
- return nullptr; // No font node found.
- }
- std::unique_ptr<ASystemFont> font = std::make_unique<ASystemFont>();
- copyFont(ite, font.get());
- return font.release();
+ return findFirstFontNode(xmlDoc);
}
} else {
- xmlNode* nextNode = nextSibling(ite->mFontNode, FONT_TAG);
+ xmlNode* nextNode = nextSibling(fontNode, FONT_TAG);
while (nextNode == nullptr) {
- xmlNode* family = nextSibling(ite->mFontNode->parent, FAMILY_TAG);
+ xmlNode* family = nextSibling(fontNode->parent, FAMILY_TAG);
if (family == nullptr) {
break;
}
nextNode = firstElement(family, FONT_TAG);
}
- ite->mFontNode = nextNode;
- if (nextNode == nullptr) {
- ite->mXmlDoc.reset();
- return nullptr;
- }
-
- std::unique_ptr<ASystemFont> font = std::make_unique<ASystemFont>();
- copyFont(ite, font.get());
- if (!isFontFileAvailable(font->mFilePath)) {
- // fonts.xml intentionally contains missing font configuration. Skip it.
- return ASystemFontIterator_next(ite);
- }
- return font.release();
+ return nextNode;
}
}
+ASystemFont* ASystemFontIterator_next(ASystemFontIterator* ite) {
+ LOG_ALWAYS_FATAL_IF(ite == nullptr, "nullptr has passed as iterator argument");
+ if (ite->mXmlDoc) {
+ ite->mFontNode = findNextFontNode(ite->mXmlDoc, ite->mFontNode);
+ if (ite->mFontNode == nullptr) {
+ // Reached end of the XML file. Continue OEM customization.
+ ite->mXmlDoc.reset();
+ ite->mFontNode = nullptr;
+ } else {
+ std::unique_ptr<ASystemFont> font = std::make_unique<ASystemFont>();
+ copyFont(ite->mXmlDoc, ite->mFontNode, font.get(), "/system/fonts/");
+ if (!isFontFileAvailable(font->mFilePath)) {
+ return ASystemFontIterator_next(ite);
+ }
+ return font.release();
+ }
+ }
+ if (ite->mCustomizationXmlDoc) {
+ // TODO: Filter only customizationType="new-named-family"
+ ite->mFontNode = findNextFontNode(ite->mCustomizationXmlDoc, ite->mFontNode);
+ if (ite->mFontNode == nullptr) {
+ // Reached end of the XML file. Finishing
+ ite->mCustomizationXmlDoc.reset();
+ ite->mFontNode = nullptr;
+ return nullptr;
+ } else {
+ std::unique_ptr<ASystemFont> font = std::make_unique<ASystemFont>();
+ copyFont(ite->mCustomizationXmlDoc, ite->mFontNode, font.get(), "/product/fonts/");
+ if (!isFontFileAvailable(font->mFilePath)) {
+ return ASystemFontIterator_next(ite);
+ }
+ return font.release();
+ }
+ }
+ return nullptr;
+}
+
void ASystemFont_close(ASystemFont* font) {
delete font;
}
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 96%
rename from core/java/com/android/internal/backup/LocalTransport.java
rename to packages/LocalTransport/src/com/android/localtransport/LocalTransport.java
index d0f0272..4408ef5 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
@@ -96,11 +95,15 @@
private long mFullBackupSize;
private FileInputStream mCurFullRestoreStream;
- private FileOutputStream mFullRestoreSocketStream;
private byte[] mFullRestoreBuffer;
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();
@@ -191,6 +194,15 @@
@Override
public int performBackup(PackageInfo packageInfo, ParcelFileDescriptor data, int flags) {
+ try {
+ return performBackupInternal(packageInfo, data, flags);
+ } finally {
+ IoUtils.closeQuietly(data);
+ }
+ }
+
+ private int performBackupInternal(
+ PackageInfo packageInfo, ParcelFileDescriptor data, int flags) {
boolean isIncremental = (flags & FLAG_INCREMENTAL) != 0;
boolean isNonIncremental = (flags & FLAG_NON_INCREMENTAL) != 0;
@@ -746,7 +758,6 @@
private void resetFullRestoreState() {
IoUtils.closeQuietly(mCurFullRestoreStream);
mCurFullRestoreStream = null;
- mFullRestoreSocketStream = null;
mFullRestoreBuffer = null;
}
@@ -791,10 +802,11 @@
Log.e(TAG, "Unable to read archive for " + name);
return TRANSPORT_PACKAGE_REJECTED;
}
- mFullRestoreSocketStream = new FileOutputStream(socket.getFileDescriptor());
mFullRestoreBuffer = new byte[2*1024];
}
+ FileOutputStream stream = new FileOutputStream(socket.getFileDescriptor());
+
int nRead;
try {
nRead = mCurFullRestoreStream.read(mFullRestoreBuffer);
@@ -811,14 +823,12 @@
if (DEBUG) {
Log.i(TAG, " delivering restore chunk: " + nRead);
}
- mFullRestoreSocketStream.write(mFullRestoreBuffer, 0, nRead);
+ stream.write(mFullRestoreBuffer, 0, nRead);
}
} catch (IOException e) {
return TRANSPORT_ERROR; // Hard error accessing the file; shouldn't happen
} finally {
- // Most transports will need to explicitly close 'socket' here, but this transport
- // is in the same process as the caller so it can leave it up to the backup manager
- // to manage both socket fds.
+ IoUtils.closeQuietly(socket);
}
return nRead;
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/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/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp
index ee4c954..89438e5 100644
--- a/packages/SettingsLib/Android.bp
+++ b/packages/SettingsLib/Android.bp
@@ -15,6 +15,7 @@
"SettingsLibHelpUtils",
"SettingsLibRestrictedLockUtils",
"SettingsLibAppPreference",
+ "SettingsLibSearchWidget",
],
// ANDROIDMK TRANSLATION ERROR: unsupported assignment to LOCAL_SHARED_JAVA_LIBRARIES
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/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-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index 3859092..bd9a6ec 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -446,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/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/LocalBluetoothProfileManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
index 0c29f43..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();
}
@@ -550,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/SapProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java
index 9a6f104..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,13 +94,11 @@
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);
}
@@ -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..cc970b9
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataForUidLoader.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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 final boolean mRetrieveDetail;
+
+ private NetworkCycleDataForUidLoader(Builder builder) {
+ super(builder);
+ mUid = builder.mUid;
+ mRetrieveDetail = builder.mRetrieveDetail;
+ 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 NetworkCycleDataForUid.Builder builder = new NetworkCycleDataForUid.Builder();
+ builder.setStartTime(start)
+ .setEndTime(end)
+ .setTotalUsage(total);
+ if (mRetrieveDetail) {
+ final long foreground = getForegroundUsage(start, end);
+ builder.setBackgroundUsage(total - foreground)
+ .setForegroundUsage(foreground);
+ }
+ 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;
+ private boolean mRetrieveDetail = true;
+
+ public Builder(Context context) {
+ super(context);
+ }
+
+ public Builder<T> setUid(int uid) {
+ mUid = uid;
+ return this;
+ }
+
+ public Builder<T> setRetrieveDetail(boolean retrieveDetail) {
+ mRetrieveDetail = retrieveDetail;
+ 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..b1c2c3a
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataLoader.java
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.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.ConnectivityManager;
+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 com.android.settingslib.NetworkPolicyEditor;
+
+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);
+ 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));
+ final NetworkPolicyEditor policyEditor =
+ new NetworkPolicyEditor(NetworkPolicyManager.from(builder.mContext));
+ policyEditor.read();
+ mPolicy = policyEditor.getPolicy(mNetworkTemplate);
+ }
+
+ @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 = Math.max(
+ historyStart, 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 String mSubId;
+ private int mNetworkType;
+ private NetworkTemplate mNetworkTemplate;
+
+ public Builder (Context context) {
+ mContext = context;
+ }
+
+ public Builder<T> setSubscriberId(String subId) {
+ mSubId = subId;
+ return this;
+ }
+
+ public Builder<T> setNetworkTemplate(NetworkTemplate template) {
+ mNetworkTemplate = template;
+ setNetworkType();
+ return this;
+ }
+
+ public abstract T build();
+
+ private void setNetworkType() {
+ if (mNetworkTemplate != null) {
+ final int matchRule = mNetworkTemplate.getMatchRule();
+ switch (matchRule) {
+ case NetworkTemplate.MATCH_MOBILE:
+ case NetworkTemplate.MATCH_MOBILE_WILDCARD:
+ mNetworkType = ConnectivityManager.TYPE_MOBILE;
+ break;
+ case NetworkTemplate.MATCH_WIFI:
+ mNetworkType = ConnectivityManager.TYPE_WIFI;
+ break;
+ case NetworkTemplate.MATCH_ETHERNET:
+ mNetworkType = ConnectivityManager.TYPE_ETHERNET;
+ break;
+ default:
+ mNetworkType = ConnectivityManager.TYPE_MOBILE;
+ }
+ }
+ }
+ }
+
+}
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/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/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..cad88b1
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleChartDataLoaderTest.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.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.net.NetworkPolicy;
+import android.net.NetworkPolicyManager;
+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 NetworkPolicyManager mNetworkPolicyManager;
+ @Mock
+ private Context mContext;
+
+ private NetworkCycleChartDataLoader mLoader;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ when(mContext.getSystemService(Context.NETWORK_STATS_SERVICE))
+ .thenReturn(mNetworkStatsManager);
+ when(mContext.getSystemService(Context.NETWORK_POLICY_SERVICE))
+ .thenReturn(mNetworkPolicyManager);
+ when(mNetworkPolicyManager.getNetworkPolicies()).thenReturn(new NetworkPolicy[0]);
+ }
+
+ @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)
+ .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..2314f27
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataForUidLoaderTest.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 com.android.settingslib.net;
+
+import static android.app.usage.NetworkStats.Bucket.STATE_FOREGROUND;
+import static android.net.NetworkStats.TAG_NONE;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.usage.NetworkStatsManager;
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.NetworkPolicy;
+import android.net.NetworkPolicyManager;
+import android.text.format.DateUtils;
+
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(SettingsLibRobolectricTestRunner.class)
+public class NetworkCycleDataForUidLoaderTest {
+
+ @Mock
+ private NetworkStatsManager mNetworkStatsManager;
+ @Mock
+ private NetworkPolicyManager mNetworkPolicyManager;
+ @Mock
+ private Context mContext;
+
+ private NetworkCycleDataForUidLoader mLoader;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ when(mContext.getSystemService(Context.NETWORK_STATS_SERVICE))
+ .thenReturn(mNetworkStatsManager);
+ when(mContext.getSystemService(Context.NETWORK_POLICY_SERVICE))
+ .thenReturn(mNetworkPolicyManager);
+ when(mNetworkPolicyManager.getNetworkPolicies()).thenReturn(new NetworkPolicy[0]);
+ }
+
+ @Test
+ public void recordUsage_shouldQueryNetworkDetailsForUidAndForegroundState() {
+ 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 = spy(NetworkCycleDataForUidLoader.builder(mContext)
+ .setUid(uid).setSubscriberId(subId).build());
+ doReturn(1024L).when(mLoader).getTotalUsage(any());
+
+ mLoader.recordUsage(start, end);
+
+ verify(mNetworkStatsManager).queryDetailsForUid(networkType, subId, start, end, uid);
+ verify(mNetworkStatsManager).queryDetailsForUidTagState(
+ networkType, subId, start, end, uid, TAG_NONE, STATE_FOREGROUND);
+ }
+
+ @Test
+ public void recordUsage_retrieveDetailIsFalse_shouldNotQueryNetworkForegroundState() {
+ 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 = spy(NetworkCycleDataForUidLoader.builder(mContext)
+ .setRetrieveDetail(false).setUid(uid).setSubscriberId(subId).build());
+ doReturn(1024L).when(mLoader).getTotalUsage(any());
+
+ mLoader.recordUsage(start, end);
+ verify(mNetworkStatsManager, never()).queryDetailsForUidTagState(
+ networkType, subId, start, end, uid, TAG_NONE, STATE_FOREGROUND);
+ }
+}
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..9d60a97
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.NetworkPolicyManager;
+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 NetworkPolicyManager mNetworkPolicyManager;
+ @Mock
+ private Context mContext;
+ @Mock
+ private NetworkPolicy mPolicy;
+ @Mock
+ private Iterator<Range<ZonedDateTime>> mIterator;
+ @Mock
+ private INetworkStatsService mNetworkStatsService;
+
+ private NetworkCycleDataTestLoader mLoader;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ when(mContext.getSystemService(Context.NETWORK_STATS_SERVICE))
+ .thenReturn(mNetworkStatsManager);
+ when(mContext.getSystemService(Context.NETWORK_POLICY_SERVICE))
+ .thenReturn(mNetworkPolicyManager);
+ when(mPolicy.cycleIterator()).thenReturn(mIterator);
+ when(mNetworkPolicyManager.getNetworkPolicies()).thenReturn(new NetworkPolicy[0]);
+ }
+
+ @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/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/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/layout/qs_paged_tile_layout.xml b/packages/SystemUI/res/layout/qs_paged_tile_layout.xml
index 11a0187..7083269 100644
--- a/packages/SystemUI/res/layout/qs_paged_tile_layout.xml
+++ b/packages/SystemUI/res/layout/qs_paged_tile_layout.xml
@@ -20,10 +20,13 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
- android:clipChildren="false"
- android:clipToPadding="false"
+ 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/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index 1bc2bcc..157934f 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -103,16 +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>
- <!-- no translation found for accessibility_biometric_dialog_help_area (8953787076940186847) -->
- <skip />
- <!-- no translation found for biometric_dialog_confirm (6468457350041712674) -->
- <skip />
+ <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>
- <!-- no translation found for face_dialog_looking_for_face (7049276266074494689) -->
- <skip />
- <!-- no translation found for accessibility_face_dialog_face_icon (2658119009870383490) -->
- <skip />
+ <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>
@@ -829,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 b663fbd..492fa8c 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -103,16 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"ካሜራ ክፈት"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"የአዲስ ተግባር አቀማመጥን ይምረጡ"</string>
<string name="cancel" msgid="6442560571259935130">"ይቅር"</string>
- <!-- no translation found for accessibility_biometric_dialog_help_area (8953787076940186847) -->
- <skip />
- <!-- no translation found for biometric_dialog_confirm (6468457350041712674) -->
- <skip />
+ <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>
- <!-- no translation found for face_dialog_looking_for_face (7049276266074494689) -->
- <skip />
- <!-- no translation found for accessibility_face_dialog_face_icon (2658119009870383490) -->
- <skip />
+ <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>
@@ -829,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 5f63aaf..fe27bb7 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -107,16 +107,12 @@
<string name="camera_label" msgid="7261107956054836961">"فتح الكاميرا"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"تحديد تنسيق جديد للمهمة"</string>
<string name="cancel" msgid="6442560571259935130">"إلغاء"</string>
- <!-- no translation found for accessibility_biometric_dialog_help_area (8953787076940186847) -->
- <skip />
- <!-- no translation found for biometric_dialog_confirm (6468457350041712674) -->
- <skip />
+ <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>
- <!-- no translation found for face_dialog_looking_for_face (7049276266074494689) -->
- <skip />
- <!-- no translation found for accessibility_face_dialog_face_icon (2658119009870383490) -->
- <skip />
+ <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>
@@ -853,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 d217663..8cde524 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -103,16 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"কেমেৰা খোলক"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"নতুন কাৰ্যৰ চানেকি বাছনি কৰক"</string>
<string name="cancel" msgid="6442560571259935130">"বাতিল কৰক"</string>
- <!-- no translation found for accessibility_biometric_dialog_help_area (8953787076940186847) -->
- <skip />
- <!-- no translation found for biometric_dialog_confirm (6468457350041712674) -->
- <skip />
+ <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>
- <!-- no translation found for face_dialog_looking_for_face (7049276266074494689) -->
- <skip />
- <!-- no translation found for accessibility_face_dialog_face_icon (2658119009870383490) -->
- <skip />
+ <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>
@@ -829,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 b4f6b4a8..2342a89 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -103,16 +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>
- <!-- no translation found for accessibility_biometric_dialog_help_area (8953787076940186847) -->
- <skip />
- <!-- no translation found for biometric_dialog_confirm (6468457350041712674) -->
- <skip />
+ <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>
- <!-- no translation found for face_dialog_looking_for_face (7049276266074494689) -->
- <skip />
- <!-- no translation found for accessibility_face_dialog_face_icon (2658119009870383490) -->
- <skip />
+ <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>
@@ -829,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 fde90bc..47422b2 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -104,16 +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>
- <!-- no translation found for accessibility_biometric_dialog_help_area (8953787076940186847) -->
- <skip />
- <!-- no translation found for biometric_dialog_confirm (6468457350041712674) -->
- <skip />
+ <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>
- <!-- no translation found for face_dialog_looking_for_face (7049276266074494689) -->
- <skip />
- <!-- no translation found for accessibility_face_dialog_face_icon (2658119009870383490) -->
- <skip />
+ <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>
@@ -835,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 c8fbb92..de01516 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -105,16 +105,12 @@
<string name="camera_label" msgid="7261107956054836961">"адкрыць камеру"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"Выберыце новы макет заданняў"</string>
<string name="cancel" msgid="6442560571259935130">"Скасаваць"</string>
- <!-- no translation found for accessibility_biometric_dialog_help_area (8953787076940186847) -->
- <skip />
- <!-- no translation found for biometric_dialog_confirm (6468457350041712674) -->
- <skip />
+ <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>
- <!-- no translation found for face_dialog_looking_for_face (7049276266074494689) -->
- <skip />
- <!-- no translation found for accessibility_face_dialog_face_icon (2658119009870383490) -->
- <skip />
+ <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>
@@ -843,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 75a63e6..29d89ec 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -103,16 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"отваряне на камерата"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"Избиране на ново оформление за задачите"</string>
<string name="cancel" msgid="6442560571259935130">"Отказ"</string>
- <!-- no translation found for accessibility_biometric_dialog_help_area (8953787076940186847) -->
- <skip />
- <!-- no translation found for biometric_dialog_confirm (6468457350041712674) -->
- <skip />
+ <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>
- <!-- no translation found for face_dialog_looking_for_face (7049276266074494689) -->
- <skip />
- <!-- no translation found for accessibility_face_dialog_face_icon (2658119009870383490) -->
- <skip />
+ <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 +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 38cfc8a..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,16 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"ক্যামেরা খুলুন"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"নতুন কার্য লেআউট বেছে নিন"</string>
<string name="cancel" msgid="6442560571259935130">"বাতিল করুন"</string>
- <!-- no translation found for accessibility_biometric_dialog_help_area (8953787076940186847) -->
- <skip />
- <!-- no translation found for biometric_dialog_confirm (6468457350041712674) -->
- <skip />
+ <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>
- <!-- no translation found for face_dialog_looking_for_face (7049276266074494689) -->
- <skip />
- <!-- no translation found for accessibility_face_dialog_face_icon (2658119009870383490) -->
- <skip />
+ <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>
@@ -180,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>
@@ -340,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>
@@ -685,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>
@@ -829,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 9865cb8..ac0c05e 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -104,16 +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>
- <!-- no translation found for accessibility_biometric_dialog_help_area (8953787076940186847) -->
- <skip />
- <!-- no translation found for biometric_dialog_confirm (6468457350041712674) -->
- <skip />
+ <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>
- <!-- no translation found for face_dialog_looking_for_face (7049276266074494689) -->
- <skip />
- <!-- no translation found for accessibility_face_dialog_face_icon (2658119009870383490) -->
- <skip />
+ <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>
@@ -837,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 1613613..b4a2f3b 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -103,16 +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>
- <!-- no translation found for accessibility_biometric_dialog_help_area (8953787076940186847) -->
- <skip />
- <!-- no translation found for biometric_dialog_confirm (6468457350041712674) -->
- <skip />
+ <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>
- <!-- no translation found for face_dialog_looking_for_face (7049276266074494689) -->
- <skip />
- <!-- no translation found for accessibility_face_dialog_face_icon (2658119009870383490) -->
- <skip />
+ <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>
@@ -829,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 867225c..6d4af4a 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -105,16 +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>
- <!-- no translation found for accessibility_biometric_dialog_help_area (8953787076940186847) -->
- <skip />
- <!-- no translation found for biometric_dialog_confirm (6468457350041712674) -->
- <skip />
+ <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>
- <!-- no translation found for face_dialog_looking_for_face (7049276266074494689) -->
- <skip />
- <!-- no translation found for accessibility_face_dialog_face_icon (2658119009870383490) -->
- <skip />
+ <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>
@@ -843,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 baba3ac..ade7db1 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -103,16 +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>
- <!-- no translation found for accessibility_biometric_dialog_help_area (8953787076940186847) -->
- <skip />
- <!-- no translation found for biometric_dialog_confirm (6468457350041712674) -->
- <skip />
+ <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>
- <!-- no translation found for face_dialog_looking_for_face (7049276266074494689) -->
- <skip />
- <!-- no translation found for accessibility_face_dialog_face_icon (2658119009870383490) -->
- <skip />
+ <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>
@@ -829,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 e8c8c5c..06aac5c 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -103,16 +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>
- <!-- no translation found for accessibility_biometric_dialog_help_area (8953787076940186847) -->
- <skip />
- <!-- no translation found for biometric_dialog_confirm (6468457350041712674) -->
- <skip />
+ <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>
- <!-- no translation found for face_dialog_looking_for_face (7049276266074494689) -->
- <skip />
- <!-- no translation found for accessibility_face_dialog_face_icon (2658119009870383490) -->
- <skip />
+ <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>
@@ -833,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 4765c9b..1892da6 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -103,16 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"άνοιγμα φωτογραφικής μηχανής"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"Επιλέξτε τη νέα διάταξη εργασίας"</string>
<string name="cancel" msgid="6442560571259935130">"Ακύρωση"</string>
- <!-- no translation found for accessibility_biometric_dialog_help_area (8953787076940186847) -->
- <skip />
- <!-- no translation found for biometric_dialog_confirm (6468457350041712674) -->
- <skip />
+ <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>
- <!-- no translation found for face_dialog_looking_for_face (7049276266074494689) -->
- <skip />
- <!-- no translation found for accessibility_face_dialog_face_icon (2658119009870383490) -->
- <skip />
+ <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 +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 0a2fec2..6503470 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -103,16 +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>
- <!-- no translation found for accessibility_biometric_dialog_help_area (8953787076940186847) -->
- <skip />
- <!-- no translation found for biometric_dialog_confirm (6468457350041712674) -->
- <skip />
+ <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>
- <!-- no translation found for face_dialog_looking_for_face (7049276266074494689) -->
- <skip />
- <!-- no translation found for accessibility_face_dialog_face_icon (2658119009870383490) -->
- <skip />
+ <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>
@@ -829,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 7b10212..9e4081c 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -103,16 +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>
- <!-- no translation found for accessibility_biometric_dialog_help_area (8953787076940186847) -->
- <skip />
- <!-- no translation found for biometric_dialog_confirm (6468457350041712674) -->
- <skip />
+ <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>
- <!-- no translation found for face_dialog_looking_for_face (7049276266074494689) -->
- <skip />
- <!-- no translation found for accessibility_face_dialog_face_icon (2658119009870383490) -->
- <skip />
+ <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>
@@ -829,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 0a2fec2..6503470 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -103,16 +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>
- <!-- no translation found for accessibility_biometric_dialog_help_area (8953787076940186847) -->
- <skip />
- <!-- no translation found for biometric_dialog_confirm (6468457350041712674) -->
- <skip />
+ <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>
- <!-- no translation found for face_dialog_looking_for_face (7049276266074494689) -->
- <skip />
- <!-- no translation found for accessibility_face_dialog_face_icon (2658119009870383490) -->
- <skip />
+ <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>
@@ -829,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 0a2fec2..6503470 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -103,16 +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>
- <!-- no translation found for accessibility_biometric_dialog_help_area (8953787076940186847) -->
- <skip />
- <!-- no translation found for biometric_dialog_confirm (6468457350041712674) -->
- <skip />
+ <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>
- <!-- no translation found for face_dialog_looking_for_face (7049276266074494689) -->
- <skip />
- <!-- no translation found for accessibility_face_dialog_face_icon (2658119009870383490) -->
- <skip />
+ <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>
@@ -829,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 259d3ca..40205b9 100644
--- a/packages/SystemUI/res/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res/values-en-rXC/strings.xml
@@ -103,16 +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>
- <!-- no translation found for accessibility_biometric_dialog_help_area (8953787076940186847) -->
- <skip />
- <!-- no translation found for biometric_dialog_confirm (6468457350041712674) -->
- <skip />
+ <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>
- <!-- no translation found for face_dialog_looking_for_face (7049276266074494689) -->
- <skip />
- <!-- no translation found for accessibility_face_dialog_face_icon (2658119009870383490) -->
- <skip />
+ <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>
@@ -829,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 33177f0..d4adfdc 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -103,16 +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>
- <!-- no translation found for accessibility_biometric_dialog_help_area (8953787076940186847) -->
- <skip />
- <!-- no translation found for biometric_dialog_confirm (6468457350041712674) -->
- <skip />
+ <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>
- <!-- no translation found for face_dialog_looking_for_face (7049276266074494689) -->
- <skip />
- <!-- no translation found for accessibility_face_dialog_face_icon (2658119009870383490) -->
- <skip />
+ <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>
@@ -831,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 c80c495..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,16 +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>
- <!-- no translation found for accessibility_biometric_dialog_help_area (8953787076940186847) -->
- <skip />
- <!-- no translation found for biometric_dialog_confirm (6468457350041712674) -->
- <skip />
+ <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>
- <!-- no translation found for face_dialog_looking_for_face (7049276266074494689) -->
- <skip />
- <!-- no translation found for accessibility_face_dialog_face_icon (2658119009870383490) -->
- <skip />
+ <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>
@@ -567,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>
@@ -590,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>
@@ -831,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 d4923e2..246f1f8 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -103,16 +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>
- <!-- no translation found for accessibility_biometric_dialog_help_area (8953787076940186847) -->
- <skip />
- <!-- no translation found for biometric_dialog_confirm (6468457350041712674) -->
- <skip />
+ <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>
- <!-- no translation found for face_dialog_looking_for_face (7049276266074494689) -->
- <skip />
- <!-- no translation found for accessibility_face_dialog_face_icon (2658119009870383490) -->
- <skip />
+ <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>
@@ -831,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 379683f..b73946e 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -103,16 +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>
- <!-- no translation found for accessibility_biometric_dialog_help_area (8953787076940186847) -->
- <skip />
- <!-- no translation found for biometric_dialog_confirm (6468457350041712674) -->
- <skip />
+ <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>
- <!-- no translation found for face_dialog_looking_for_face (7049276266074494689) -->
- <skip />
- <!-- no translation found for accessibility_face_dialog_face_icon (2658119009870383490) -->
- <skip />
+ <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>
@@ -831,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 cafba14..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,16 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"باز کردن دوربین"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"انتخاب طرحبندی جدید کار"</string>
<string name="cancel" msgid="6442560571259935130">"لغو"</string>
- <!-- no translation found for accessibility_biometric_dialog_help_area (8953787076940186847) -->
- <skip />
- <!-- no translation found for biometric_dialog_confirm (6468457350041712674) -->
- <skip />
+ <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>
- <!-- no translation found for face_dialog_looking_for_face (7049276266074494689) -->
- <skip />
- <!-- no translation found for accessibility_face_dialog_face_icon (2658119009870383490) -->
- <skip />
+ <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>
@@ -545,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>
@@ -829,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 311d3bd..6e8cb4c 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -103,16 +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>
- <!-- no translation found for accessibility_biometric_dialog_help_area (8953787076940186847) -->
- <skip />
- <!-- no translation found for biometric_dialog_confirm (6468457350041712674) -->
- <skip />
+ <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>
- <!-- no translation found for face_dialog_looking_for_face (7049276266074494689) -->
- <skip />
- <!-- no translation found for accessibility_face_dialog_face_icon (2658119009870383490) -->
- <skip />
+ <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>
@@ -829,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 2534961..f6d66b7 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -103,16 +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>
- <!-- no translation found for accessibility_biometric_dialog_help_area (8953787076940186847) -->
- <skip />
- <!-- no translation found for biometric_dialog_confirm (6468457350041712674) -->
- <skip />
+ <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>
- <!-- no translation found for face_dialog_looking_for_face (7049276266074494689) -->
- <skip />
- <!-- no translation found for accessibility_face_dialog_face_icon (2658119009870383490) -->
- <skip />
+ <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>
@@ -831,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 a82ae24..326b7c7 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -103,16 +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>
- <!-- no translation found for accessibility_biometric_dialog_help_area (8953787076940186847) -->
- <skip />
- <!-- no translation found for biometric_dialog_confirm (6468457350041712674) -->
- <skip />
+ <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>
- <!-- no translation found for face_dialog_looking_for_face (7049276266074494689) -->
- <skip />
- <!-- no translation found for accessibility_face_dialog_face_icon (2658119009870383490) -->
- <skip />
+ <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>
@@ -831,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 d987c56..192c132 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -103,16 +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>
- <!-- no translation found for accessibility_biometric_dialog_help_area (8953787076940186847) -->
- <skip />
- <!-- no translation found for biometric_dialog_confirm (6468457350041712674) -->
- <skip />
+ <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>
- <!-- no translation found for face_dialog_looking_for_face (7049276266074494689) -->
- <skip />
- <!-- no translation found for accessibility_face_dialog_face_icon (2658119009870383490) -->
- <skip />
+ <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>
@@ -831,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 70a75fc..f392268 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -103,16 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"કૅમેરો ખોલો"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"નવું કાર્ય લેઆઉટ પસંદ કરો"</string>
<string name="cancel" msgid="6442560571259935130">"રદ કરો"</string>
- <!-- no translation found for accessibility_biometric_dialog_help_area (8953787076940186847) -->
- <skip />
- <!-- no translation found for biometric_dialog_confirm (6468457350041712674) -->
- <skip />
+ <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>
- <!-- no translation found for face_dialog_looking_for_face (7049276266074494689) -->
- <skip />
- <!-- no translation found for accessibility_face_dialog_face_icon (2658119009870383490) -->
- <skip />
+ <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>
@@ -829,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-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index e098657..8795264 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -103,16 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"कैमरा खोलें"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"नया कार्य लेआउट चुनें"</string>
<string name="cancel" msgid="6442560571259935130">"रद्द करें"</string>
- <!-- no translation found for accessibility_biometric_dialog_help_area (8953787076940186847) -->
- <skip />
- <!-- no translation found for biometric_dialog_confirm (6468457350041712674) -->
- <skip />
+ <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>
- <!-- no translation found for face_dialog_looking_for_face (7049276266074494689) -->
- <skip />
- <!-- no translation found for accessibility_face_dialog_face_icon (2658119009870383490) -->
- <skip />
+ <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>
@@ -829,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 53250db..b6fa4f5 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -104,16 +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>
- <!-- no translation found for accessibility_biometric_dialog_help_area (8953787076940186847) -->
- <skip />
- <!-- no translation found for biometric_dialog_confirm (6468457350041712674) -->
- <skip />
+ <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>
- <!-- no translation found for face_dialog_looking_for_face (7049276266074494689) -->
- <skip />
- <!-- no translation found for accessibility_face_dialog_face_icon (2658119009870383490) -->
- <skip />
+ <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>
@@ -835,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 ee8ff0b..9076379 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -103,16 +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>
- <!-- no translation found for accessibility_biometric_dialog_help_area (8953787076940186847) -->
- <skip />
- <!-- no translation found for biometric_dialog_confirm (6468457350041712674) -->
- <skip />
+ <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>
- <!-- no translation found for face_dialog_looking_for_face (7049276266074494689) -->
- <skip />
- <!-- no translation found for accessibility_face_dialog_face_icon (2658119009870383490) -->
- <skip />
+ <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>
@@ -829,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 bb8af54..54401f5 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -103,16 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"բացել ֆոտոխցիկը"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"Ընտրել առաջադրանքի նոր դասավորություն"</string>
<string name="cancel" msgid="6442560571259935130">"Չեղարկել"</string>
- <!-- no translation found for accessibility_biometric_dialog_help_area (8953787076940186847) -->
- <skip />
- <!-- no translation found for biometric_dialog_confirm (6468457350041712674) -->
- <skip />
+ <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>
- <!-- no translation found for face_dialog_looking_for_face (7049276266074494689) -->
- <skip />
- <!-- no translation found for accessibility_face_dialog_face_icon (2658119009870383490) -->
- <skip />
+ <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>
@@ -588,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>
@@ -829,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 2b38702..2474158 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -103,16 +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>
- <!-- no translation found for accessibility_biometric_dialog_help_area (8953787076940186847) -->
- <skip />
- <!-- no translation found for biometric_dialog_confirm (6468457350041712674) -->
- <skip />
+ <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>
- <!-- no translation found for face_dialog_looking_for_face (7049276266074494689) -->
- <skip />
- <!-- no translation found for accessibility_face_dialog_face_icon (2658119009870383490) -->
- <skip />
+ <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>
@@ -829,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 184ebed..c2a020b 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -103,16 +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>
- <!-- no translation found for accessibility_biometric_dialog_help_area (8953787076940186847) -->
- <skip />
- <!-- no translation found for biometric_dialog_confirm (6468457350041712674) -->
- <skip />
+ <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>
- <!-- no translation found for face_dialog_looking_for_face (7049276266074494689) -->
- <skip />
- <!-- no translation found for accessibility_face_dialog_face_icon (2658119009870383490) -->
- <skip />
+ <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>
@@ -829,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 9a6e38b..aefaa6d 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -103,16 +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>
- <!-- no translation found for accessibility_biometric_dialog_help_area (8953787076940186847) -->
- <skip />
- <!-- no translation found for biometric_dialog_confirm (6468457350041712674) -->
- <skip />
+ <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>
- <!-- no translation found for face_dialog_looking_for_face (7049276266074494689) -->
- <skip />
- <!-- no translation found for accessibility_face_dialog_face_icon (2658119009870383490) -->
- <skip />
+ <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>
@@ -831,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 cf1d337..b83f5df 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -105,16 +105,12 @@
<string name="camera_label" msgid="7261107956054836961">"פתח את המצלמה"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"בחר פריסה חדשה להצגת משימות"</string>
<string name="cancel" msgid="6442560571259935130">"ביטול"</string>
- <!-- no translation found for accessibility_biometric_dialog_help_area (8953787076940186847) -->
- <skip />
- <!-- no translation found for biometric_dialog_confirm (6468457350041712674) -->
- <skip />
+ <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>
- <!-- no translation found for face_dialog_looking_for_face (7049276266074494689) -->
- <skip />
- <!-- no translation found for accessibility_face_dialog_face_icon (2658119009870383490) -->
- <skip />
+ <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>
@@ -841,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 f6674d6..e5b0a2b 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -103,16 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"カメラを起動"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"新しいタスクレイアウトの選択"</string>
<string name="cancel" msgid="6442560571259935130">"キャンセル"</string>
- <!-- no translation found for accessibility_biometric_dialog_help_area (8953787076940186847) -->
- <skip />
- <!-- no translation found for biometric_dialog_confirm (6468457350041712674) -->
- <skip />
+ <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>
- <!-- no translation found for face_dialog_looking_for_face (7049276266074494689) -->
- <skip />
- <!-- no translation found for accessibility_face_dialog_face_icon (2658119009870383490) -->
- <skip />
+ <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>
@@ -831,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 26de531..19e9f47 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -103,16 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"კამერის გახსნა"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"ახალი ამოცანის განლაგების არჩევა"</string>
<string name="cancel" msgid="6442560571259935130">"გაუქმება"</string>
- <!-- no translation found for accessibility_biometric_dialog_help_area (8953787076940186847) -->
- <skip />
- <!-- no translation found for biometric_dialog_confirm (6468457350041712674) -->
- <skip />
+ <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>
- <!-- no translation found for face_dialog_looking_for_face (7049276266074494689) -->
- <skip />
- <!-- no translation found for accessibility_face_dialog_face_icon (2658119009870383490) -->
- <skip />
+ <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 +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 d06c0f5..15f8cd5 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -103,16 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"камераны ашу"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"Жаңа тапсырма пішімін таңдау"</string>
<string name="cancel" msgid="6442560571259935130">"Бас тарту"</string>
- <!-- no translation found for accessibility_biometric_dialog_help_area (8953787076940186847) -->
- <skip />
- <!-- no translation found for biometric_dialog_confirm (6468457350041712674) -->
- <skip />
+ <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>
- <!-- no translation found for face_dialog_looking_for_face (7049276266074494689) -->
- <skip />
- <!-- no translation found for accessibility_face_dialog_face_icon (2658119009870383490) -->
- <skip />
+ <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 +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 bf9ad75..af8d4ce 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -103,16 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"បើកម៉ាស៊ីនថត"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"ជ្រើសប្លង់ភារកិច្ចថ្មី"</string>
<string name="cancel" msgid="6442560571259935130">"បោះបង់"</string>
- <!-- no translation found for accessibility_biometric_dialog_help_area (8953787076940186847) -->
- <skip />
- <!-- no translation found for biometric_dialog_confirm (6468457350041712674) -->
- <skip />
+ <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>
- <!-- no translation found for face_dialog_looking_for_face (7049276266074494689) -->
- <skip />
- <!-- no translation found for accessibility_face_dialog_face_icon (2658119009870383490) -->
- <skip />
+ <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>
@@ -829,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 4823437..ac36e73 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -103,16 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"ಕ್ಯಾಮರಾ ತೆರೆಯಿರಿ"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"ಹೊಸ ಕಾರ್ಯ ವಿನ್ಯಾಸವನ್ನು ಆಯ್ಕೆಮಾಡಿ"</string>
<string name="cancel" msgid="6442560571259935130">"ರದ್ದುಮಾಡಿ"</string>
- <!-- no translation found for accessibility_biometric_dialog_help_area (8953787076940186847) -->
- <skip />
- <!-- no translation found for biometric_dialog_confirm (6468457350041712674) -->
- <skip />
+ <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>
- <!-- no translation found for face_dialog_looking_for_face (7049276266074494689) -->
- <skip />
- <!-- no translation found for accessibility_face_dialog_face_icon (2658119009870383490) -->
- <skip />
+ <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>
@@ -829,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 10c1e28..1e2ab42 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -103,16 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"카메라 열기"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"새 작업 레이아웃 선택"</string>
<string name="cancel" msgid="6442560571259935130">"취소"</string>
- <!-- no translation found for accessibility_biometric_dialog_help_area (8953787076940186847) -->
- <skip />
- <!-- no translation found for biometric_dialog_confirm (6468457350041712674) -->
- <skip />
+ <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>
- <!-- no translation found for face_dialog_looking_for_face (7049276266074494689) -->
- <skip />
- <!-- no translation found for accessibility_face_dialog_face_icon (2658119009870383490) -->
- <skip />
+ <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>
@@ -831,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 a0e630a..585e290 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -103,16 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"камераны ачуу"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"Жаңы тапшырманын планын тандаңыз"</string>
<string name="cancel" msgid="6442560571259935130">"Жокко чыгаруу"</string>
- <!-- no translation found for accessibility_biometric_dialog_help_area (8953787076940186847) -->
- <skip />
- <!-- no translation found for biometric_dialog_confirm (6468457350041712674) -->
- <skip />
+ <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>
- <!-- no translation found for face_dialog_looking_for_face (7049276266074494689) -->
- <skip />
- <!-- no translation found for accessibility_face_dialog_face_icon (2658119009870383490) -->
- <skip />
+ <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 +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 3cc1ac1..0952df4 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -103,16 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"ເປີດກ້ອງ"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"ເລືອກແຜນຜັງໜ້າວຽກໃໝ່"</string>
<string name="cancel" msgid="6442560571259935130">"ຍົກເລີກ"</string>
- <!-- no translation found for accessibility_biometric_dialog_help_area (8953787076940186847) -->
- <skip />
- <!-- no translation found for biometric_dialog_confirm (6468457350041712674) -->
- <skip />
+ <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>
- <!-- no translation found for face_dialog_looking_for_face (7049276266074494689) -->
- <skip />
- <!-- no translation found for accessibility_face_dialog_face_icon (2658119009870383490) -->
- <skip />
+ <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 +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 e7d423b..ac6cfc7 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -105,16 +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>
- <!-- no translation found for accessibility_biometric_dialog_help_area (8953787076940186847) -->
- <skip />
- <!-- no translation found for biometric_dialog_confirm (6468457350041712674) -->
- <skip />
+ <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>
- <!-- no translation found for face_dialog_looking_for_face (7049276266074494689) -->
- <skip />
- <!-- no translation found for accessibility_face_dialog_face_icon (2658119009870383490) -->
- <skip />
+ <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>
@@ -841,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 4fd9668..01e2e39 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -104,16 +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>
- <!-- no translation found for accessibility_biometric_dialog_help_area (8953787076940186847) -->
- <skip />
- <!-- no translation found for biometric_dialog_confirm (6468457350041712674) -->
- <skip />
+ <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>
- <!-- no translation found for face_dialog_looking_for_face (7049276266074494689) -->
- <skip />
- <!-- no translation found for accessibility_face_dialog_face_icon (2658119009870383490) -->
- <skip />
+ <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>
@@ -835,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 e29e649..8b4b458 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -103,16 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"отвори камера"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"Изберете нов распоред на задача"</string>
<string name="cancel" msgid="6442560571259935130">"Откажи"</string>
- <!-- no translation found for accessibility_biometric_dialog_help_area (8953787076940186847) -->
- <skip />
- <!-- no translation found for biometric_dialog_confirm (6468457350041712674) -->
- <skip />
+ <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>
- <!-- no translation found for face_dialog_looking_for_face (7049276266074494689) -->
- <skip />
- <!-- no translation found for accessibility_face_dialog_face_icon (2658119009870383490) -->
- <skip />
+ <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 +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 8d4ce0a..fc32b88 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -103,16 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"ക്യാമറ തുറക്കുക"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"പുതിയ ടാസ്ക് ലേഔട്ട് തിരഞ്ഞെടുക്കുക"</string>
<string name="cancel" msgid="6442560571259935130">"റദ്ദാക്കുക"</string>
- <!-- no translation found for accessibility_biometric_dialog_help_area (8953787076940186847) -->
- <skip />
- <!-- no translation found for biometric_dialog_confirm (6468457350041712674) -->
- <skip />
+ <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>
- <!-- no translation found for face_dialog_looking_for_face (7049276266074494689) -->
- <skip />
- <!-- no translation found for accessibility_face_dialog_face_icon (2658119009870383490) -->
- <skip />
+ <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>
@@ -829,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 421c6d8..331e9b7 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -101,16 +101,12 @@
<string name="camera_label" msgid="7261107956054836961">"камер нээх"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"Шинэ ажиллах талбарыг сонгоно уу"</string>
<string name="cancel" msgid="6442560571259935130">"Цуцлах"</string>
- <!-- no translation found for accessibility_biometric_dialog_help_area (8953787076940186847) -->
- <skip />
- <!-- no translation found for biometric_dialog_confirm (6468457350041712674) -->
- <skip />
+ <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>
- <!-- no translation found for face_dialog_looking_for_face (7049276266074494689) -->
- <skip />
- <!-- no translation found for accessibility_face_dialog_face_icon (2658119009870383490) -->
- <skip />
+ <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>
@@ -827,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 589ac52..33761ca 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -103,16 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"कॅमेरा उघडा"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"नवीन कार्य लेआउट निवडा"</string>
<string name="cancel" msgid="6442560571259935130">"रद्द करा"</string>
- <!-- no translation found for accessibility_biometric_dialog_help_area (8953787076940186847) -->
- <skip />
- <!-- no translation found for biometric_dialog_confirm (6468457350041712674) -->
- <skip />
+ <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>
- <!-- no translation found for face_dialog_looking_for_face (7049276266074494689) -->
- <skip />
- <!-- no translation found for accessibility_face_dialog_face_icon (2658119009870383490) -->
- <skip />
+ <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>
@@ -829,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 f13a864..d6a71ef 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -103,16 +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>
- <!-- no translation found for accessibility_biometric_dialog_help_area (8953787076940186847) -->
- <skip />
- <!-- no translation found for biometric_dialog_confirm (6468457350041712674) -->
- <skip />
+ <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>
- <!-- no translation found for face_dialog_looking_for_face (7049276266074494689) -->
- <skip />
- <!-- no translation found for accessibility_face_dialog_face_icon (2658119009870383490) -->
- <skip />
+ <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>
@@ -829,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 b9f4cc7..2a6e3c2 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -103,16 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"ကင်မရာ ဖွင့်ရန်"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"အလုပ်သစ်စီစဥ်မှုကို ရွေးပါ။"</string>
<string name="cancel" msgid="6442560571259935130">"မလုပ်တော့"</string>
- <!-- no translation found for accessibility_biometric_dialog_help_area (8953787076940186847) -->
- <skip />
- <!-- no translation found for biometric_dialog_confirm (6468457350041712674) -->
- <skip />
+ <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>
- <!-- no translation found for face_dialog_looking_for_face (7049276266074494689) -->
- <skip />
- <!-- no translation found for accessibility_face_dialog_face_icon (2658119009870383490) -->
- <skip />
+ <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>
@@ -829,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 cb070e6..d90eba3 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -103,16 +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>
- <!-- no translation found for accessibility_biometric_dialog_help_area (8953787076940186847) -->
- <skip />
- <!-- no translation found for biometric_dialog_confirm (6468457350041712674) -->
- <skip />
+ <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>
- <!-- no translation found for face_dialog_looking_for_face (7049276266074494689) -->
- <skip />
- <!-- no translation found for accessibility_face_dialog_face_icon (2658119009870383490) -->
- <skip />
+ <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>
@@ -829,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 d922e71..4e3cddd 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -103,16 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"क्यामेरा खोल्नुहोस्"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"नयाँ कार्य लेआउट चयन गर्नुहोस्"</string>
<string name="cancel" msgid="6442560571259935130">"रद्द गर्नुहोस्"</string>
- <!-- no translation found for accessibility_biometric_dialog_help_area (8953787076940186847) -->
- <skip />
- <!-- no translation found for biometric_dialog_confirm (6468457350041712674) -->
- <skip />
+ <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>
- <!-- no translation found for face_dialog_looking_for_face (7049276266074494689) -->
- <skip />
- <!-- no translation found for accessibility_face_dialog_face_icon (2658119009870383490) -->
- <skip />
+ <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>
@@ -829,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-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index 8bb229a..9dda44e 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -103,16 +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>
- <!-- no translation found for accessibility_biometric_dialog_help_area (8953787076940186847) -->
- <skip />
- <!-- no translation found for biometric_dialog_confirm (6468457350041712674) -->
- <skip />
+ <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>
- <!-- no translation found for face_dialog_looking_for_face (7049276266074494689) -->
- <skip />
- <!-- no translation found for accessibility_face_dialog_face_icon (2658119009870383490) -->
- <skip />
+ <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>
@@ -829,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 f961bde..91c62bb 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -103,16 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"କ୍ୟାମେରା ଖୋଲନ୍ତୁ"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"ନୂଆ ଟାସ୍କ ଲେଆଉଟ୍ ଚୟନ କରନ୍ତୁ"</string>
<string name="cancel" msgid="6442560571259935130">"କ୍ୟାନ୍ସଲ୍ କରନ୍ତୁ"</string>
- <!-- no translation found for accessibility_biometric_dialog_help_area (8953787076940186847) -->
- <skip />
- <!-- no translation found for biometric_dialog_confirm (6468457350041712674) -->
- <skip />
+ <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>
- <!-- no translation found for face_dialog_looking_for_face (7049276266074494689) -->
- <skip />
- <!-- no translation found for accessibility_face_dialog_face_icon (2658119009870383490) -->
- <skip />
+ <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>
@@ -829,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 44785fb..96fb641f5 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -103,16 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"ਕੈਮਰਾ ਖੋਲ੍ਹੋ"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"ਨਵਾਂ ਕੰਮ ਲੇਆਉਟ ਚੁਣੋ"</string>
<string name="cancel" msgid="6442560571259935130">"ਰੱਦ ਕਰੋ"</string>
- <!-- no translation found for accessibility_biometric_dialog_help_area (8953787076940186847) -->
- <skip />
- <!-- no translation found for biometric_dialog_confirm (6468457350041712674) -->
- <skip />
+ <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>
- <!-- no translation found for face_dialog_looking_for_face (7049276266074494689) -->
- <skip />
- <!-- no translation found for accessibility_face_dialog_face_icon (2658119009870383490) -->
- <skip />
+ <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 +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 a79634e..28d24bf 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -105,16 +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>
- <!-- no translation found for accessibility_biometric_dialog_help_area (8953787076940186847) -->
- <skip />
- <!-- no translation found for biometric_dialog_confirm (6468457350041712674) -->
- <skip />
+ <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>
- <!-- no translation found for face_dialog_looking_for_face (7049276266074494689) -->
- <skip />
- <!-- no translation found for accessibility_face_dialog_face_icon (2658119009870383490) -->
- <skip />
+ <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>
@@ -841,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 1897d09..ab0f5f2 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -103,16 +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>
- <!-- no translation found for accessibility_biometric_dialog_help_area (8953787076940186847) -->
- <skip />
- <!-- no translation found for biometric_dialog_confirm (6468457350041712674) -->
- <skip />
+ <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>
- <!-- no translation found for face_dialog_looking_for_face (7049276266074494689) -->
- <skip />
- <!-- no translation found for accessibility_face_dialog_face_icon (2658119009870383490) -->
- <skip />
+ <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>
@@ -831,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 6d333b1..fb0675a 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -103,16 +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>
- <!-- no translation found for accessibility_biometric_dialog_help_area (8953787076940186847) -->
- <skip />
- <!-- no translation found for biometric_dialog_confirm (6468457350041712674) -->
- <skip />
+ <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>
- <!-- no translation found for face_dialog_looking_for_face (7049276266074494689) -->
- <skip />
- <!-- no translation found for accessibility_face_dialog_face_icon (2658119009870383490) -->
- <skip />
+ <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>
@@ -829,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 1897d09..ab0f5f2 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -103,16 +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>
- <!-- no translation found for accessibility_biometric_dialog_help_area (8953787076940186847) -->
- <skip />
- <!-- no translation found for biometric_dialog_confirm (6468457350041712674) -->
- <skip />
+ <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>
- <!-- no translation found for face_dialog_looking_for_face (7049276266074494689) -->
- <skip />
- <!-- no translation found for accessibility_face_dialog_face_icon (2658119009870383490) -->
- <skip />
+ <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>
@@ -831,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 d174ee9..9b5f5e1 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -104,16 +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>
- <!-- no translation found for accessibility_biometric_dialog_help_area (8953787076940186847) -->
- <skip />
- <!-- no translation found for biometric_dialog_confirm (6468457350041712674) -->
- <skip />
+ <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>
- <!-- no translation found for face_dialog_looking_for_face (7049276266074494689) -->
- <skip />
- <!-- no translation found for accessibility_face_dialog_face_icon (2658119009870383490) -->
- <skip />
+ <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>
@@ -837,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 1d968e4..8069317 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -105,16 +105,12 @@
<string name="camera_label" msgid="7261107956054836961">"Открыть камеру."</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"Выберите другой макет"</string>
<string name="cancel" msgid="6442560571259935130">"Отмена"</string>
- <!-- no translation found for accessibility_biometric_dialog_help_area (8953787076940186847) -->
- <skip />
- <!-- no translation found for biometric_dialog_confirm (6468457350041712674) -->
- <skip />
+ <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>
- <!-- no translation found for face_dialog_looking_for_face (7049276266074494689) -->
- <skip />
- <!-- no translation found for accessibility_face_dialog_face_icon (2658119009870383490) -->
- <skip />
+ <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>
@@ -843,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 6d3014c..8802833 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -103,16 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"කැමරාව විවෘත කරන්න"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"නව කාර්යය සැකැස්ම තෝරන්න"</string>
<string name="cancel" msgid="6442560571259935130">"අවලංගු කරන්න"</string>
- <!-- no translation found for accessibility_biometric_dialog_help_area (8953787076940186847) -->
- <skip />
- <!-- no translation found for biometric_dialog_confirm (6468457350041712674) -->
- <skip />
+ <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>
- <!-- no translation found for face_dialog_looking_for_face (7049276266074494689) -->
- <skip />
- <!-- no translation found for accessibility_face_dialog_face_icon (2658119009870383490) -->
- <skip />
+ <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>
@@ -829,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 8352896..233a81b 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -105,16 +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>
- <!-- no translation found for accessibility_biometric_dialog_help_area (8953787076940186847) -->
- <skip />
- <!-- no translation found for biometric_dialog_confirm (6468457350041712674) -->
- <skip />
+ <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>
- <!-- no translation found for face_dialog_looking_for_face (7049276266074494689) -->
- <skip />
- <!-- no translation found for accessibility_face_dialog_face_icon (2658119009870383490) -->
- <skip />
+ <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>
@@ -843,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 2611f7a..c7e661a 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -105,16 +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>
- <!-- no translation found for accessibility_biometric_dialog_help_area (8953787076940186847) -->
- <skip />
- <!-- no translation found for biometric_dialog_confirm (6468457350041712674) -->
- <skip />
+ <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>
- <!-- no translation found for face_dialog_looking_for_face (7049276266074494689) -->
- <skip />
- <!-- no translation found for accessibility_face_dialog_face_icon (2658119009870383490) -->
- <skip />
+ <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>
@@ -843,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 e7b3254..f4d3d2a 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -103,16 +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>
- <!-- no translation found for accessibility_biometric_dialog_help_area (8953787076940186847) -->
- <skip />
- <!-- no translation found for biometric_dialog_confirm (6468457350041712674) -->
- <skip />
+ <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>
- <!-- no translation found for face_dialog_looking_for_face (7049276266074494689) -->
- <skip />
- <!-- no translation found for accessibility_face_dialog_face_icon (2658119009870383490) -->
- <skip />
+ <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>
@@ -829,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 323d78b..5825231 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -104,16 +104,12 @@
<string name="camera_label" msgid="7261107956054836961">"отвори камеру"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"Изабери нови распоред задатака"</string>
<string name="cancel" msgid="6442560571259935130">"Откажи"</string>
- <!-- no translation found for accessibility_biometric_dialog_help_area (8953787076940186847) -->
- <skip />
- <!-- no translation found for biometric_dialog_confirm (6468457350041712674) -->
- <skip />
+ <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>
- <!-- no translation found for face_dialog_looking_for_face (7049276266074494689) -->
- <skip />
- <!-- no translation found for accessibility_face_dialog_face_icon (2658119009870383490) -->
- <skip />
+ <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>
@@ -835,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 97e0278..7040ec2 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -103,16 +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>
- <!-- no translation found for accessibility_biometric_dialog_help_area (8953787076940186847) -->
- <skip />
- <!-- no translation found for biometric_dialog_confirm (6468457350041712674) -->
- <skip />
+ <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>
- <!-- no translation found for face_dialog_looking_for_face (7049276266074494689) -->
- <skip />
- <!-- no translation found for accessibility_face_dialog_face_icon (2658119009870383490) -->
- <skip />
+ <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>
@@ -829,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 1419dce..0633e8a 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -103,16 +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>
- <!-- no translation found for accessibility_biometric_dialog_help_area (8953787076940186847) -->
- <skip />
- <!-- no translation found for biometric_dialog_confirm (6468457350041712674) -->
- <skip />
+ <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>
- <!-- no translation found for face_dialog_looking_for_face (7049276266074494689) -->
- <skip />
- <!-- no translation found for accessibility_face_dialog_face_icon (2658119009870383490) -->
- <skip />
+ <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>
@@ -829,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 ba75212..469e797 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -103,16 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"கேமராவைத் திற"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"புதிய பணி தளவமைப்பைத் தேர்ந்தெடுக்கவும்"</string>
<string name="cancel" msgid="6442560571259935130">"ரத்துசெய்"</string>
- <!-- no translation found for accessibility_biometric_dialog_help_area (8953787076940186847) -->
- <skip />
- <!-- no translation found for biometric_dialog_confirm (6468457350041712674) -->
- <skip />
+ <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>
- <!-- no translation found for face_dialog_looking_for_face (7049276266074494689) -->
- <skip />
- <!-- no translation found for accessibility_face_dialog_face_icon (2658119009870383490) -->
- <skip />
+ <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>
@@ -829,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 268ad18..5d869f1 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -103,16 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"కెమెరాను తెరువు"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"కొత్త విధి లేఅవుట్ను ఎంచుకోండి"</string>
<string name="cancel" msgid="6442560571259935130">"రద్దు చేయి"</string>
- <!-- no translation found for accessibility_biometric_dialog_help_area (8953787076940186847) -->
- <skip />
- <!-- no translation found for biometric_dialog_confirm (6468457350041712674) -->
- <skip />
+ <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>
- <!-- no translation found for face_dialog_looking_for_face (7049276266074494689) -->
- <skip />
- <!-- no translation found for accessibility_face_dialog_face_icon (2658119009870383490) -->
- <skip />
+ <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>
@@ -829,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 dea18e5..408c5a0 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -103,16 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"เปิดกล้อง"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"เลือกรูปแบบงานใหม่"</string>
<string name="cancel" msgid="6442560571259935130">"ยกเลิก"</string>
- <!-- no translation found for accessibility_biometric_dialog_help_area (8953787076940186847) -->
- <skip />
- <!-- no translation found for biometric_dialog_confirm (6468457350041712674) -->
- <skip />
+ <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>
- <!-- no translation found for face_dialog_looking_for_face (7049276266074494689) -->
- <skip />
- <!-- no translation found for accessibility_face_dialog_face_icon (2658119009870383490) -->
- <skip />
+ <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>
@@ -829,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 11e07a0..8da4946 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -103,16 +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>
- <!-- no translation found for accessibility_biometric_dialog_help_area (8953787076940186847) -->
- <skip />
- <!-- no translation found for biometric_dialog_confirm (6468457350041712674) -->
- <skip />
+ <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>
- <!-- no translation found for face_dialog_looking_for_face (7049276266074494689) -->
- <skip />
- <!-- no translation found for accessibility_face_dialog_face_icon (2658119009870383490) -->
- <skip />
+ <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>
@@ -829,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 bda7fef..de8ed9d 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -103,16 +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>
- <!-- no translation found for accessibility_biometric_dialog_help_area (8953787076940186847) -->
- <skip />
- <!-- no translation found for biometric_dialog_confirm (6468457350041712674) -->
- <skip />
+ <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>
- <!-- no translation found for face_dialog_looking_for_face (7049276266074494689) -->
- <skip />
- <!-- no translation found for accessibility_face_dialog_face_icon (2658119009870383490) -->
- <skip />
+ <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>
@@ -829,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 ce3572e..6c2e661 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -105,16 +105,12 @@
<string name="camera_label" msgid="7261107956054836961">"відкрити камеру"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"Виберіть новий макет завдання"</string>
<string name="cancel" msgid="6442560571259935130">"Скасувати"</string>
- <!-- no translation found for accessibility_biometric_dialog_help_area (8953787076940186847) -->
- <skip />
- <!-- no translation found for biometric_dialog_confirm (6468457350041712674) -->
- <skip />
+ <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>
- <!-- no translation found for face_dialog_looking_for_face (7049276266074494689) -->
- <skip />
- <!-- no translation found for accessibility_face_dialog_face_icon (2658119009870383490) -->
- <skip />
+ <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>
@@ -843,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 50412d2..88391b0 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -103,16 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"کیمرا کھولیں"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"نئے کام کا لے آؤٹ منتخب کریں"</string>
<string name="cancel" msgid="6442560571259935130">"منسوخ کریں"</string>
- <!-- no translation found for accessibility_biometric_dialog_help_area (8953787076940186847) -->
- <skip />
- <!-- no translation found for biometric_dialog_confirm (6468457350041712674) -->
- <skip />
+ <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>
- <!-- no translation found for face_dialog_looking_for_face (7049276266074494689) -->
- <skip />
- <!-- no translation found for accessibility_face_dialog_face_icon (2658119009870383490) -->
- <skip />
+ <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>
@@ -829,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 9446ac1..37d31c2 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -103,16 +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>
- <!-- no translation found for accessibility_biometric_dialog_help_area (8953787076940186847) -->
- <skip />
- <!-- no translation found for biometric_dialog_confirm (6468457350041712674) -->
- <skip />
+ <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>
- <!-- no translation found for face_dialog_looking_for_face (7049276266074494689) -->
- <skip />
- <!-- no translation found for accessibility_face_dialog_face_icon (2658119009870383490) -->
- <skip />
+ <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>
@@ -831,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 c2045af..f476790 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -103,16 +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>
- <!-- no translation found for accessibility_biometric_dialog_help_area (8953787076940186847) -->
- <skip />
- <!-- no translation found for biometric_dialog_confirm (6468457350041712674) -->
- <skip />
+ <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>
- <!-- no translation found for face_dialog_looking_for_face (7049276266074494689) -->
- <skip />
- <!-- no translation found for accessibility_face_dialog_face_icon (2658119009870383490) -->
- <skip />
+ <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>
@@ -829,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 bc13033..aaa4c63 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -103,16 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"打开相机"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"选择新的任务布局"</string>
<string name="cancel" msgid="6442560571259935130">"取消"</string>
- <!-- no translation found for accessibility_biometric_dialog_help_area (8953787076940186847) -->
- <skip />
- <!-- no translation found for biometric_dialog_confirm (6468457350041712674) -->
- <skip />
+ <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>
- <!-- no translation found for face_dialog_looking_for_face (7049276266074494689) -->
- <skip />
- <!-- no translation found for accessibility_face_dialog_face_icon (2658119009870383490) -->
- <skip />
+ <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>
@@ -829,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 ff045b1..aad4b4f 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -103,16 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"開啟相機"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"選取新的工作版面配置"</string>
<string name="cancel" msgid="6442560571259935130">"取消"</string>
- <!-- no translation found for accessibility_biometric_dialog_help_area (8953787076940186847) -->
- <skip />
- <!-- no translation found for biometric_dialog_confirm (6468457350041712674) -->
- <skip />
+ <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>
- <!-- no translation found for face_dialog_looking_for_face (7049276266074494689) -->
- <skip />
- <!-- no translation found for accessibility_face_dialog_face_icon (2658119009870383490) -->
- <skip />
+ <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>
@@ -831,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 143bf91..6b88545 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -103,16 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"開啟攝影機"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"選取新工作版面配置"</string>
<string name="cancel" msgid="6442560571259935130">"取消"</string>
- <!-- no translation found for accessibility_biometric_dialog_help_area (8953787076940186847) -->
- <skip />
- <!-- no translation found for biometric_dialog_confirm (6468457350041712674) -->
- <skip />
+ <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>
- <!-- no translation found for face_dialog_looking_for_face (7049276266074494689) -->
- <skip />
- <!-- no translation found for accessibility_face_dialog_face_icon (2658119009870383490) -->
- <skip />
+ <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>
@@ -829,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 89030b7..085c3b3 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -103,16 +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>
- <!-- no translation found for accessibility_biometric_dialog_help_area (8953787076940186847) -->
- <skip />
- <!-- no translation found for biometric_dialog_confirm (6468457350041712674) -->
- <skip />
+ <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>
- <!-- no translation found for face_dialog_looking_for_face (7049276266074494689) -->
- <skip />
- <!-- no translation found for accessibility_face_dialog_face_icon (2658119009870383490) -->
- <skip />
+ <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>
@@ -829,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/strings.xml b/packages/SystemUI/res/values/strings.xml
index 2b51aaa..e1c71fa 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1081,7 +1081,7 @@
<string name="clear_all_notifications_text">Clear all</string>
<!-- The text for the manage notifications link. [CHAR LIMIT=40] -->
- <string name="manage_notifications_text">Manage notifications</string>
+ <string name="manage_notifications_text">Manage</string>
<!-- The text to show in the notifications shade when dnd is suppressing notifications. [CHAR LIMIT=100] -->
<string name="dnd_suppressing_shade_text">Notifications paused by Do Not Disturb</string>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 8442dd1..6446367 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -279,7 +279,7 @@
<item name="android:fontFamily">sans-serif</item>
</style>
- <style name="BaseBrightnessDialogContainer">
+ <style name="BaseBrightnessDialogContainer" parent="@style/Theme.SystemUI">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">wrap_content</item>
</style>
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 95%
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 7bc7e5f..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;
@@ -39,12 +39,14 @@
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;
-import com.android.systemui.R;
public class PluginInstanceManager<T extends Plugin> {
@@ -71,8 +73,7 @@
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,
- context.getResources().getStringArray(R.array.config_pluginWhitelist));
+ manager, Build.IS_DEBUGGABLE, manager.getWhitelistedPlugins());
}
@VisibleForTesting
@@ -114,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();
@@ -132,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);
@@ -143,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));
}
@@ -165,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);
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 90%
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 1cbf1fe..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;
@@ -41,10 +41,11 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
-import com.android.systemui.Dependency;
-import com.android.systemui.R;
-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;
@@ -79,31 +80,33 @@
private Looper mLooper;
private boolean mWtfsSet;
- public PluginManagerImpl(Context context) {
+ public PluginManagerImpl(Context context, PluginInitializer initializer) {
this(context, new PluginInstanceManagerFactory(), Build.IS_DEBUGGABLE,
- context.getResources().getStringArray(R.array.config_pluginWhitelist),
- Thread.getUncaughtExceptionPreHandler());
+ Thread.getUncaughtExceptionPreHandler(), initializer);
}
@VisibleForTesting
PluginManagerImpl(Context context, PluginInstanceManagerFactory factory, boolean debuggable,
- String[] whitelistedPlugins, 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(whitelistedPlugins));
+ mWhitelistedPlugins.addAll(Arrays.asList(initializer.getWhitelistedPlugins(mContext)));
mPluginPrefs = new PluginPrefs(mContext);
PluginExceptionHandler uncaughtExceptionHandler = new PluginExceptionHandler(
defaultHandler);
Thread.setUncaughtExceptionPreHandler(uncaughtExceptionHandler);
- 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) {
@@ -121,7 +124,9 @@
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();
@@ -140,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,
@@ -293,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/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/NavigationBarCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/NavigationBarCompat.java
index d38cc0f..69aea2c 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/NavigationBarCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/NavigationBarCompat.java
@@ -24,8 +24,6 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import sun.misc.Resource;
-
public class NavigationBarCompat {
/**
* Touch slopes and thresholds for quick step operations. Drag slop is the point where the
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/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 10c8ec0..c7685f8 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -41,7 +41,6 @@
import android.app.trust.TrustManager;
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,13 +48,14 @@
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.database.ContentObserver;
+import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.BiometricSourceType;
+import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
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;
@@ -86,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;
@@ -251,51 +250,6 @@
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) {
@@ -401,6 +355,18 @@
}
};
+ private boolean mFaceSettingEnabledForUser;
+ private BiometricManager mBiometricManager;
+ private IBiometricEnabledOnKeyguardCallback mBiometricEnabledCallback =
+ new IBiometricEnabledOnKeyguardCallback.Stub() {
+ @Override
+ public void onChanged(BiometricSourceType type, boolean enabled) throws RemoteException {
+ if (type == BiometricSourceType.FACE) {
+ mFaceSettingEnabledForUser = enabled;
+ }
+ }
+ };
+
private OnSubscriptionsChangedListener mSubscriptionListener =
new OnSubscriptionsChangedListener() {
@Override
@@ -1166,7 +1132,7 @@
private CancellationSignal mFingerprintCancelSignal;
private CancellationSignal mFaceCancelSignal;
private FingerprintManager mFpm;
- private FaceManager mFaceAuthenticationManager;
+ private FaceManager mFaceManager;
/**
* When we receive a
@@ -1435,7 +1401,6 @@
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...
@@ -1505,17 +1470,21 @@
if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
mFpm = (FingerprintManager) context.getSystemService(Context.FINGERPRINT_SERVICE);
}
-
if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FACE)) {
- mFaceAuthenticationManager =
- (FaceManager) context.getSystemService(Context.FACE_SERVICE);
+ mFaceManager = (FaceManager) context.getSystemService(Context.FACE_SERVICE);
}
+
+ if (mFpm != null || mFaceManager != null) {
+ mBiometricManager = context.getSystemService(BiometricManager.class);
+ mBiometricManager.registerEnabledOnKeyguardCallback(mBiometricEnabledCallback);
+ }
+
updateBiometricListeningState();
if (mFpm != null) {
mFpm.addLockoutResetCallback(mFingerprintLockoutResetCallback);
}
- if (mFaceAuthenticationManager != null) {
- mFaceAuthenticationManager.addLockoutResetCallback(mFaceLockoutResetCallback);
+ if (mFaceManager != null) {
+ mFaceManager.addLockoutResetCallback(mFaceLockoutResetCallback);
}
ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
@@ -1630,7 +1599,7 @@
mFaceCancelSignal.cancel();
}
mFaceCancelSignal = new CancellationSignal();
- mFaceAuthenticationManager.authenticate(null, mFaceCancelSignal, 0,
+ mFaceManager.authenticate(null, mFaceCancelSignal, 0,
mFaceAuthenticationCallback, null);
setFaceRunningState(BIOMETRIC_STATE_RUNNING);
}
@@ -1642,9 +1611,9 @@
}
public boolean isUnlockWithFacePossible(int userId) {
- return mFaceAuthenticationManager != null && mFaceAuthenticationManager.isHardwareDetected()
+ return mFaceManager != null && mFaceManager.isHardwareDetected()
&& !isFaceDisabled(userId)
- && mFaceAuthenticationManager.hasEnrolledTemplates(userId);
+ && mFaceManager.hasEnrolledTemplates(userId);
}
private void stopListeningForFingerprint() {
@@ -1766,7 +1735,6 @@
* 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) {
@@ -2218,8 +2186,8 @@
}
}
- private final SysUiTaskStackChangeListener
- mTaskStackListener = new SysUiTaskStackChangeListener() {
+ private final TaskStackChangeListener
+ mTaskStackListener = new TaskStackChangeListener() {
@Override
public void onTaskStackChangedBackground() {
try {
@@ -2438,7 +2406,7 @@
pw.println(" strongAuthFlags=" + Integer.toHexString(strongAuthFlags));
pw.println(" trustManaged=" + getUserTrustIsManaged(userId));
}
- if (mFaceAuthenticationManager != null && mFaceAuthenticationManager.isHardwareDetected()) {
+ if (mFaceManager != null && mFaceManager.isHardwareDetected()) {
final int userId = ActivityManager.getCurrentUser();
final int strongAuthFlags = mStrongAuthTracker.getStrongAuthForUser(userId);
pw.println(" Face authentication state (user=" + userId + ")");
@@ -2450,6 +2418,7 @@
pw.println(" possible=" + isUnlockWithFacePossible(userId));
pw.println(" strongAuthFlags=" + Integer.toHexString(strongAuthFlags));
pw.println(" trustManaged=" + getUserTrustIsManaged(userId));
+ pw.println(" enabledByUser=" + mFaceSettingEnabledForUser);
}
}
}
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/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
index 408e599..77f4bf5 100644
--- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
@@ -256,6 +256,12 @@
Log.d(TAG, "onSurfaceRedrawNeeded");
}
super.onSurfaceRedrawNeeded(holder);
+ // At the end of this method we should have drawn into the surface.
+ // This means that the bitmap should be loaded synchronously if
+ // it was already unloaded.
+ if (mBackground == null) {
+ updateBitmap(mWallpaperManager.getBitmap(true /* hardware */));
+ }
mSurfaceRedrawNeeded = true;
drawFrame();
}
diff --git a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
index bd2b7a5..d351c4f3 100644
--- a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
@@ -16,6 +16,15 @@
package com.android.systemui;
+import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
+import static android.view.MotionEvent.ACTION_DOWN;
+import static android.view.MotionEvent.ACTION_UP;
+import static android.view.MotionEvent.ACTION_CANCEL;
+
+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 +42,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 +51,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.
*/
@@ -91,6 +90,7 @@
private boolean mIsEnabled;
private int mCurrentBoundedUserId = -1;
private float mBackButtonAlpha;
+ private MotionEvent mStatusBarGestureDownEvent;
private ISystemUiProxy mSysUiProxy = new ISystemUiProxy.Stub() {
@@ -113,6 +113,9 @@
}
public void onStatusBarMotionEvent(MotionEvent event) {
+ if (!verifyCaller("onStatusBarMotionEvent")) {
+ return;
+ }
long token = Binder.clearCallingIdentity();
try {
// TODO move this logic to message queue
@@ -120,6 +123,16 @@
StatusBar bar = SysUiServiceProvider.getComponent(mContext, StatusBar.class);
if (bar != null) {
bar.dispatchNotificationsPanelTouchEvent(event);
+
+ int action = event.getActionMasked();
+ if (action == ACTION_DOWN) {
+ mStatusBarGestureDownEvent = MotionEvent.obtain(event);
+ }
+ if (action == ACTION_UP || action == ACTION_CANCEL) {
+ mStatusBarGestureDownEvent.recycle();
+ mStatusBarGestureDownEvent = null;
+ }
+ event.recycle();
}
});
} finally {
@@ -133,7 +146,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);
}
@@ -300,7 +316,7 @@
// This is the death handler for the binder from the launcher service
private final IBinder.DeathRecipient mOverviewServiceDeathRcpt
- = this::startConnectionToCurrentUser;
+ = this::cleanupAfterDeath;
public OverviewProxyService(Context context) {
mContext = context;
@@ -314,8 +330,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);
@@ -331,6 +346,22 @@
return mBackButtonAlpha;
}
+ public void cleanupAfterDeath() {
+ if (mStatusBarGestureDownEvent != null) {
+ mHandler.post(()-> {
+ StatusBar bar = SysUiServiceProvider.getComponent(mContext, StatusBar.class);
+ if (bar != null) {
+ System.out.println("MERONG dispatchNotificationPanelTouchEvent");
+ mStatusBarGestureDownEvent.setAction(MotionEvent.ACTION_CANCEL);
+ bar.dispatchNotificationsPanelTouchEvent(mStatusBarGestureDownEvent);
+ mStatusBarGestureDownEvent.recycle();
+ mStatusBarGestureDownEvent = null;
+ }
+ });
+ }
+ startConnectionToCurrentUser();
+ }
+
public void startConnectionToCurrentUser() {
if (mHandler.getLooper() != Looper.myLooper()) {
mHandler.post(mConnectionRunnable);
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/RegionInterceptingFrameLayout.java b/packages/SystemUI/src/com/android/systemui/RegionInterceptingFrameLayout.java
index 646f69e..6dc2d67 100644
--- a/packages/SystemUI/src/com/android/systemui/RegionInterceptingFrameLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/RegionInterceptingFrameLayout.java
@@ -76,7 +76,7 @@
continue;
}
- internalInsetsInfo.touchableRegion.op(riv.getInterceptRegion(), Op.UNION);
+ internalInsetsInfo.touchableRegion.op(unionRegion, Op.UNION);
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index 48181bc..4d24d82 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -76,6 +76,9 @@
import com.android.systemui.tuner.TunerService.Tunable;
import com.android.systemui.util.leak.RotationUtils;
+import java.util.ArrayList;
+import java.util.List;
+
import androidx.annotation.VisibleForTesting;
/**
@@ -108,6 +111,23 @@
private boolean mPendingRotationChange;
private Handler mHandler;
+ /**
+ * Converts a set of {@link Rect}s into a {@link Region}
+ *
+ * @hide
+ */
+ public static Region rectsToRegion(List<Rect> rects) {
+ Region result = Region.obtain();
+ if (rects != null) {
+ for (Rect r : rects) {
+ if (r != null && !r.isEmpty()) {
+ result.op(r, Region.Op.UNION);
+ }
+ }
+ }
+ return result;
+ }
+
@Override
public void start() {
mHandler = startHandlerThread();
@@ -539,7 +559,7 @@
private final DisplayInfo mInfo = new DisplayInfo();
private final Paint mPaint = new Paint();
- private final Region mBounds = new Region();
+ private final List<Rect> mBounds = new ArrayList();
private final Rect mBoundingRect = new Rect();
private final Path mBoundingPath = new Path();
private final int[] mLocation = new int[2];
@@ -629,12 +649,12 @@
mStart = isStart();
requestLayout();
getDisplay().getDisplayInfo(mInfo);
- mBounds.setEmpty();
+ mBounds.clear();
mBoundingRect.setEmpty();
mBoundingPath.reset();
int newVisible;
if (shouldDrawCutout(getContext()) && hasCutout()) {
- mBounds.set(mInfo.displayCutout.getBounds());
+ mBounds.addAll(mInfo.displayCutout.getBoundingRects());
localBounds(mBoundingRect);
updateBoundingPath();
invalidate();
@@ -713,32 +733,17 @@
public static void boundsFromDirection(DisplayCutout displayCutout, int gravity,
Rect out) {
- Region bounds = boundsFromDirection(displayCutout, gravity);
- out.set(bounds.getBounds());
- bounds.recycle();
- }
-
- public static Region boundsFromDirection(DisplayCutout displayCutout, int gravity) {
- Region bounds = displayCutout.getBounds();
switch (gravity) {
case Gravity.TOP:
- bounds.op(0, 0, Integer.MAX_VALUE, displayCutout.getSafeInsetTop(),
- Region.Op.INTERSECT);
- break;
+ out.set(displayCutout.getBoundingRectTop());
case Gravity.LEFT:
- bounds.op(0, 0, displayCutout.getSafeInsetLeft(), Integer.MAX_VALUE,
- Region.Op.INTERSECT);
- break;
+ out.set(displayCutout.getBoundingRectLeft());
case Gravity.BOTTOM:
- bounds.op(0, displayCutout.getSafeInsetTop() + 1, Integer.MAX_VALUE,
- Integer.MAX_VALUE, Region.Op.INTERSECT);
- break;
+ out.set(displayCutout.getBoundingRectBottom());
case Gravity.RIGHT:
- bounds.op(displayCutout.getSafeInsetLeft() + 1, 0, Integer.MAX_VALUE,
- Integer.MAX_VALUE, Region.Op.INTERSECT);
- break;
+ out.set(displayCutout.getBoundingRectRight());
}
- return bounds;
+ out.setEmpty();
}
private void localBounds(Rect out) {
@@ -771,7 +776,8 @@
}
View rootView = getRootView();
- Region cutoutBounds = mInfo.displayCutout.getBounds();
+ Region cutoutBounds = rectsToRegion(
+ mInfo.displayCutout.getBoundingRects());
// Transform to window's coordinate space
rootView.getLocationOnScreen(mLocation);
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/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/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/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 31548b9..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();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index bde7f1b..512cd82 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -90,6 +90,7 @@
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.plugins.GlobalActions.GlobalActionsManager;
import com.android.systemui.statusbar.phone.ScrimController;
+import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.util.EmergencyDialerConstants;
import com.android.systemui.volume.SystemUIInterpolators.LogAccelerateInterpolator;
@@ -102,7 +103,8 @@
* is provisioned.
*/
class GlobalActionsDialog implements DialogInterface.OnDismissListener,
- DialogInterface.OnClickListener, DialogInterface.OnShowListener {
+ DialogInterface.OnClickListener, DialogInterface.OnShowListener,
+ ConfigurationController.ConfigurationListener {
static public final String SYSTEM_DIALOG_REASON_KEY = "reason";
static public final String SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS = "globalactions";
@@ -197,6 +199,8 @@
mEmergencyAffordanceManager = new EmergencyAffordanceManager(context);
mScreenshotHelper = new ScreenshotHelper(context);
+
+ Dependency.get(ConfigurationController.class).addCallback(this);
}
/**
@@ -417,6 +421,15 @@
|| state == SOME_AUTH_REQUIRED_AFTER_USER_REQUEST);
}
+ @Override
+ public void onUiModeChanged() {
+ mContext.getTheme().applyStyle(mContext.getThemeResId(), true);
+ }
+
+ public void destroy() {
+ Dependency.get(ConfigurationController.class).removeCallback(this);
+ }
+
private final class PowerAction extends SinglePressAction implements LongPressAction {
private PowerAction() {
super(R.drawable.ic_lock_power_off,
@@ -1530,7 +1543,6 @@
* ScrimController.GRADIENT_SCRIM_ALPHA * 255);
mGradientDrawable.setAlpha(alpha);
})
- .withEndAction(() -> getWindow().getDecorView().requestAccessibilityFocus())
.start();
}
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
index 1489c21..0394998 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
@@ -61,6 +61,10 @@
@Override
public void destroy() {
SysUiServiceProvider.getComponent(mContext, CommandQueue.class).removeCallbacks(this);
+ if (mGlobalActions != null) {
+ mGlobalActions.destroy();
+ mGlobalActions = null;
+ }
}
@Override
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/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/power/PowerUI.java b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
index 0b9067e..568a039 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
@@ -301,9 +301,10 @@
// mark if we've already shown a warning this cycle. This will prevent the notification
// trigger from spamming users by only showing low/critical warnings once per cycle
if (hybridEnabled) {
- if (mTimeRemaining < mEnhancedEstimates.getSevereWarningThreshold()
- || mBatteryLevel < mLowBatteryReminderLevels[1]) {
+ if (mTimeRemaining <= mEnhancedEstimates.getSevereWarningThreshold()
+ || mBatteryLevel <= mLowBatteryReminderLevels[1]) {
mSevereWarningShownThisChargeCycle = true;
+ mLowWarningShownThisChargeCycle = true;
} else {
mLowWarningShownThisChargeCycle = true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
index fcd479c..c398a4a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
@@ -6,9 +6,9 @@
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.content.Context;
-
-import androidx.viewpager.widget.PagerAdapter;
-import androidx.viewpager.widget.ViewPager;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.os.Bundle;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
@@ -18,6 +18,9 @@
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;
@@ -28,6 +31,7 @@
public class PagedTileLayout extends ViewPager implements QSTileLayout {
private static final boolean DEBUG = false;
+ private static final String CURRENT_PAGE = "current_page";
private static final String TAG = "PagedTileLayout";
private static final int REVEAL_SCROLL_DURATION_MILLIS = 750;
@@ -53,6 +57,9 @@
private AnimatorSet mBounceAnimatorSet;
private float mLastExpansion;
private boolean mDistributeTiles = false;
+ private int mPageToRestore = -1;
+ private int mLayoutOrientation;
+ private int mLayoutDirection;
public PagedTileLayout(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -60,13 +67,37 @@
setAdapter(mAdapter);
setOnPageChangeListener(mOnPageChangeListener);
setCurrentItem(0, false);
+ mLayoutOrientation = getResources().getConfiguration().orientation;
+ mLayoutDirection = getLayoutDirection();
+ }
+
+ public void saveInstanceState(Bundle outState) {
+ outState.putInt(CURRENT_PAGE, getCurrentItem());
+ }
+
+ public void restoreInstanceState(Bundle savedInstanceState) {
+ // There's only 1 page at this point. We want to restore the correct page once the
+ // pages have been inflated
+ mPageToRestore = savedInstanceState.getInt(CURRENT_PAGE, -1);
+ }
+
+ @Override
+ protected void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ if (mLayoutOrientation != newConfig.orientation) {
+ mLayoutOrientation = newConfig.orientation;
+ setCurrentItem(0, false);
+ }
}
@Override
public void onRtlPropertiesChanged(int layoutDirection) {
super.onRtlPropertiesChanged(layoutDirection);
- setAdapter(mAdapter);
- setCurrentItem(0, false);
+ if (mLayoutDirection != layoutDirection) {
+ mLayoutDirection = layoutDirection;
+ setAdapter(mAdapter);
+ setCurrentItem(0, false);
+ }
}
@Override
@@ -115,6 +146,7 @@
super.onFinishInflate();
mPages.add((TilePage) LayoutInflater.from(getContext())
.inflate(R.layout.qs_paged_page, this, false));
+ mAdapter.notifyDataSetChanged();
}
public void setPageIndicator(PageIndicator indicator) {
@@ -217,14 +249,19 @@
mPageIndicator.setNumPages(mPages.size());
setAdapter(mAdapter);
mAdapter.notifyDataSetChanged();
- setCurrentItem(0, false);
+ if (mPageToRestore != -1) {
+ setCurrentItem(mPageToRestore, false);
+ mPageToRestore = -1;
+ }
}
@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;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index c36cdf6..42dfcee 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -103,6 +103,9 @@
setListening(savedInstanceState.getBoolean(EXTRA_LISTENING));
setEditLocation(view);
mQSCustomizer.restoreInstanceState(savedInstanceState);
+ if (mQsExpanded) {
+ mQSPanel.getTileLayout().restoreInstanceState(savedInstanceState);
+ }
}
SysUiServiceProvider.getComponent(getContext(), CommandQueue.class).addCallbacks(this);
}
@@ -127,6 +130,9 @@
outState.putBoolean(EXTRA_EXPANDED, mQsExpanded);
outState.putBoolean(EXTRA_LISTENING, mListening);
mQSCustomizer.saveInstanceState(outState);
+ if (mQsExpanded) {
+ mQSPanel.getTileLayout().saveInstanceState(outState);
+ }
}
@VisibleForTesting
@@ -256,7 +262,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 +293,7 @@
mListening = listening;
mHeader.setListening(listening);
mFooter.setListening(listening);
- mQSPanel.setListening(mListening && mQsExpanded);
- mQSPanel.getFooter().setListening(listening);
+ mQSPanel.setListening(mListening, mQsExpanded);
}
@Override
@@ -365,7 +370,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..cf63e47 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -25,6 +25,7 @@
import android.content.res.Configuration;
import android.content.res.Resources;
import android.metrics.LogMaker;
+import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.service.quicksettings.Tile;
@@ -199,7 +200,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 +358,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();
}
}
@@ -653,6 +667,11 @@
}
public interface QSTileLayout {
+
+ default void saveInstanceState(Bundle outState) {}
+
+ default void restoreInstanceState(Bundle savedInstanceState) {}
+
void addTile(TileRecord tile);
void removeTile(TileRecord tile);
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/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
index 556786a..6f847c8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
@@ -253,7 +253,8 @@
final int availableWidth = getMeasuredWidth() - getPaddingStart() - getPaddingEnd();
final int leftoverWithespace = availableWidth - maxTiles * mCellWidth;
- final int smallestHorizontalMarginNeeded = leftoverWithespace / (maxTiles - 1);
+ final int smallestHorizontalMarginNeeded;
+ smallestHorizontalMarginNeeded = leftoverWithespace / Math.max(1, maxTiles - 1);
if (smallestHorizontalMarginNeeded > 0){
mCellMarginHorizontal = smallestHorizontalMarginNeeded;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
index 01ff72e..e884302 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
@@ -103,8 +103,7 @@
// 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 width = MeasureSpec.getSize(widthMeasureSpec);
final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
if (heightMode == MeasureSpec.UNSPECIFIED) {
mRows = (numTiles + mColumns - 1) / mColumns;
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/settings/BrightnessDialog.java b/packages/SystemUI/src/com/android/systemui/settings/BrightnessDialog.java
index 6918a63..2ae53b5 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/BrightnessDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/BrightnessDialog.java
@@ -46,11 +46,7 @@
window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
window.requestFeature(Window.FEATURE_NO_TITLE);
- // Use a dialog theme as the activity theme, but inflate the content as
- // the QS content.
- ContextThemeWrapper themedContext = new ContextThemeWrapper(this,
- com.android.internal.R.style.Theme_DeviceDefault_QuickSettings);
- View v = LayoutInflater.from(themedContext).inflate(
+ View v = LayoutInflater.from(this).inflate(
R.layout.quick_settings_brightness_dialog, null);
setContentView(v);
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/stackdivider/events/StartedDragingEvent.java b/packages/SystemUI/src/com/android/systemui/stackdivider/events/StartedDragingEvent.java
deleted file mode 100644
index 5d19851..0000000
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/events/StartedDragingEvent.java
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.stackdivider.events;
-
-import com.android.systemui.recents.events.EventBus;
-
-/**
- * Sent when the divider is being draged either manually or by an animation.
- */
-public class StartedDragingEvent extends EventBus.Event {
-}
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/car/CarStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
index 2d03ed0..2450e44 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -35,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;
@@ -460,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/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index d5a5274..a3e982e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -35,6 +35,8 @@
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;
@@ -58,7 +60,6 @@
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;
@@ -127,11 +128,11 @@
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;
@@ -223,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);
}
@@ -687,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)
@@ -898,7 +906,13 @@
return false;
}
- boolean inUse = mPowerManager.isScreenOn() && !mSystemServicesProxy.isDreaming();
+ 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) {
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 9b1d334..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;
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 8969aca..0577841 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
@@ -426,6 +426,10 @@
return mDarkAmount == 1;
}
+ public boolean isDarkAtAll() {
+ return mDarkAmount != 0;
+ }
+
public void setDarkTopPadding(int darkTopPadding) {
mDarkTopPadding = darkTopPadding;
}
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 72c2c0b..9ddab7c 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
@@ -25,7 +25,6 @@
import android.animation.PropertyValuesHolder;
import android.animation.TimeAnimator;
import android.animation.ValueAnimator;
-import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.annotation.Nullable;
import android.app.WallpaperManager;
import android.content.Context;
@@ -40,7 +39,6 @@
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.os.Bundle;
-import android.os.Handler;
import android.os.ServiceManager;
import android.provider.Settings;
import android.service.notification.StatusBarNotification;
@@ -82,12 +80,10 @@
import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.SwipeHelper;
-import com.android.systemui.SwipeHelper.Callback;
import com.android.systemui.classifier.FalsingManager;
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;
@@ -109,6 +105,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;
@@ -146,11 +143,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";
@@ -164,7 +159,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();
@@ -291,10 +286,6 @@
*/
private int mMaxScrollAfterExpand;
private ExpandableNotificationRow.LongPressListener mLongPressListener;
-
- private NotificationMenuRowPlugin mCurrMenuRow;
- private View mTranslatingParentView;
- private View mMenuExposedView;
boolean mCheckForLeavebehind;
/**
@@ -466,6 +457,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);
@@ -495,7 +489,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);
@@ -639,41 +634,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);
@@ -1295,111 +1255,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) {
@@ -1418,41 +1273,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);
@@ -1696,18 +1516,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;
@@ -1787,8 +1600,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)
@@ -3013,11 +2826,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 */);
}
@@ -3077,8 +2890,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);
}
@@ -3918,12 +3731,14 @@
return y < getHeight() - getEmptyBottomMargin();
}
+ @VisibleForTesting
@ShadeViewRefactor(RefactorComponent.INPUT)
- private void setIsBeingDragged(boolean isDragged) {
+ void setIsBeingDragged(boolean isDragged) {
mIsBeingDragged = isDragged;
if (isDragged) {
requestDisallowInterceptTouchEvent(true);
cancelLongPress();
+ resetExposedMenuView(true /* animate */, true /* force */);
}
}
@@ -3986,7 +3801,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;
@@ -4053,6 +3868,7 @@
public void onPanelTrackingStarted() {
mPanelTracking = true;
mAmbientState.setPanelTracking(true);
+ resetExposedMenuView(true /* animate */, true /* force */);
}
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
@@ -4068,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);
@@ -4455,8 +4271,10 @@
mLinearDarkAmount = linearDarkAmount;
mInterpolatedDarkAmount = interpolatedDarkAmount;
boolean wasFullyDark = mAmbientState.isFullyDark();
+ boolean wasDarkAtAll = mAmbientState.isDarkAtAll();
mAmbientState.setDarkAmount(interpolatedDarkAmount);
boolean nowFullyDark = mAmbientState.isFullyDark();
+ boolean nowDarkAtAll = mAmbientState.isDarkAtAll();
if (nowFullyDark != wasFullyDark) {
updateContentHeight();
DozeParameters dozeParameters = DozeParameters.getInstance(mContext);
@@ -4467,6 +4285,9 @@
mIconAreaController.setFullyDark(nowFullyDark);
}
}
+ if (!wasDarkAtAll && nowDarkAtAll) {
+ resetExposedMenuView(true /* animate */, true /* animate */);
+ }
updateAlgorithmHeightAndPadding();
updateBackgroundDimming();
updatePanelTranslation();
@@ -5242,8 +5063,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);
@@ -5274,8 +5095,8 @@
mScrimController.setNotificationCount(getNotGoneChildCount());
}
- @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
- public void setNotificationPanel(NotificationPanelView notificationPanelView) {
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
+ public void setNotificationPanel(NotificationPanelView notificationPanelView) {
mNotificationPanel = notificationPanelView;
}
@@ -5293,306 +5114,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;
-
- private static final long SWIPE_MENU_TIMING = 200;
-
- 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.onTouchStart();
- }
- 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.setMenuClickListener(NotificationStackScrollLayout.this);
- mCurrMenuRow.onTouchStart();
- }
- }
- }
-
- private boolean swipedEnoughToShowMenu(NotificationMenuRowPlugin menuRow) {
- return !swipedFarEnough() && menuRow.isSwipedEnoughToShowMenu();
- }
-
- @Override
- public void onMoveUpdate(View view, MotionEvent ev, float translation, float delta) {
- mHandler.removeCallbacks(mFalsingCheck);
- if (mCurrMenuRow != null) {
- mCurrMenuRow.onTouchMove(delta);
- }
- }
-
- @Override
- public boolean handleUpEvent(MotionEvent ev, View animView, float velocity,
- float translation) {
- if (mCurrMenuRow != null) {
- mCurrMenuRow.onTouchEnd();
- handleMenuRowSwipe(ev, animView, velocity, mCurrMenuRow);
- return true;
- }
- return false;
- }
-
- @Override
- public boolean swipedFarEnough(float translation, float viewSize) {
- return swipedFarEnough();
- }
-
- private 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 {
- snapBack(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 =
- mSwipeHelper.getMinDismissVelocity() <= 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 {
- snapBack(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 {
- snapBack(animView, velocity);
- menuRow.onSnapClosed();
- }
- }
-
- @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);
- }
-
- 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 snapOpen(View animView, int targetLeft, float velocity) {
- snapChild(animView, targetLeft, velocity);
- }
-
- private void snapBack(View animView, float velocity) {
- snapChild(animView, 0, velocity);
- }
-
- @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();
}
@@ -5656,8 +5200,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();
@@ -5677,24 +5221,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
@@ -5718,11 +5244,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[]{
@@ -6022,8 +5566,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) {
@@ -6036,9 +5580,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/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
index cfc3271..976327a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
@@ -22,6 +22,8 @@
import android.content.res.Configuration;
import android.content.res.Resources;
import androidx.collection.ArraySet;
+
+import android.graphics.Rect;
import android.graphics.Region;
import android.graphics.Region.Op;
import android.util.Log;
@@ -324,11 +326,10 @@
// Expand touchable region such that we also catch touches that just start below the notch
// area.
- Region bounds = ScreenDecorations.DisplayCutoutView.boundsFromDirection(
- cutout, Gravity.TOP);
- bounds.translate(0, mDisplayCutoutTouchableRegionSize);
+ Rect bounds = new Rect();
+ ScreenDecorations.DisplayCutoutView.boundsFromDirection(cutout, Gravity.TOP, bounds);
+ bounds.offset(0, mDisplayCutoutTouchableRegionSize);
region.op(bounds, Op.UNION);
- bounds.recycle();
}
@Override
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 cbbb0e3..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) {
@@ -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 71b35e0..16b2987 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -40,7 +40,6 @@
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;
@@ -64,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;
@@ -144,8 +142,6 @@
private Configuration mConfiguration;
private NavigationBarInflaterView mNavigationInflaterView;
- private RecentsComponent mRecentsComponent;
- private Divider mDivider;
private RecentsOnboarding mRecentsOnboarding;
private NotificationPanelView mPanelView;
@@ -314,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);
}
}
@@ -1077,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();
}
@@ -1123,6 +1115,7 @@
pw.println(" }");
mContextualButtonGroup.dump(pw);
+ mGestureHelper.dump(pw);
mRecentsOnboarding.dump(pw);
}
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 3a4c218..6ea4c92 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -733,7 +733,11 @@
mQsExpandImmediate = true;
mNotificationStackScroller.setShouldShowShelfOnly(true);
}
- expand(true /* animate */);
+ if (isFullyCollapsed()){
+ expand(true /* animate */);
+ } else {
+ flingSettings(0 /* velocity */, FLING_EXPAND);
+ }
}
public void expandWithoutQs() {
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 1afdc66b..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;
@@ -797,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/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index ff38380b..90ed97f 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,10 +182,6 @@
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;
@@ -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();
@@ -1214,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);
+ }
}
}
}
@@ -2180,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;
@@ -2189,7 +2191,6 @@
// Settings are not available in setup
if (!mUserSetup) return;
-
if (subPanel != null) {
mQSPanel.openDetails(subPanel);
}
@@ -2851,6 +2852,7 @@
}
}
else if (Intent.ACTION_SCREEN_OFF.equals(action)) {
+ mStatusBarWindowController.setNotTouchable(false);
finishBarAnimations();
resetUserExpandedStates();
}
@@ -4246,12 +4248,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
@@ -4663,6 +4665,7 @@
protected WindowManager mWindowManager;
protected IWindowManager mWindowManagerService;
+ private IDreamManager mDreamManager;
protected Display mDisplay;
@@ -4967,7 +4970,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/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/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
index 12b6f9d..298a93e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.policy;
+import static android.view.Display.INVALID_DISPLAY;
import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK;
import static android.view.accessibility.AccessibilityNodeInfo.ACTION_LONG_CLICK;
@@ -33,6 +34,7 @@
import android.os.SystemClock;
import android.util.AttributeSet;
import android.util.TypedValue;
+import android.view.Display;
import android.view.HapticFeedbackConstants;
import android.view.InputDevice;
import android.view.KeyCharacterMap;
@@ -307,6 +309,14 @@
0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
flags | KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY,
InputDevice.SOURCE_KEYBOARD);
+ //Make KeyEvent work on multi-display environment
+ if (getDisplay() != null) {
+ final int displayId = getDisplay().getDisplayId();
+
+ if (displayId != INVALID_DISPLAY) {
+ ev.setDisplayId(displayId);
+ }
+ }
InputManager.getInstance().injectInputEvent(ev,
InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
}
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/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/statusbar/policy/ZenModeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
index ef51bf0..8d2552f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
@@ -55,6 +55,7 @@
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private final ArrayList<Callback> mCallbacks = new ArrayList<>();
+ private final Object mCallbacksLock = new Object();
private final Context mContext;
private final GlobalSetting mModeSetting;
private final GlobalSetting mConfigSetting;
@@ -114,12 +115,16 @@
@Override
public void addCallback(Callback callback) {
- mCallbacks.add(callback);
+ synchronized (mCallbacksLock) {
+ mCallbacks.add(callback);
+ }
}
@Override
public void removeCallback(Callback callback) {
- mCallbacks.remove(callback);
+ synchronized (mCallbacksLock) {
+ mCallbacks.remove(callback);
+ }
}
@Override
@@ -183,28 +188,40 @@
}
private void fireNextAlarmChanged() {
- Utils.safeForeach(mCallbacks, c -> c.onNextAlarmChanged());
+ synchronized (mCallbacksLock) {
+ Utils.safeForeach(mCallbacks, c -> c.onNextAlarmChanged());
+ }
}
private void fireEffectsSuppressorChanged() {
- Utils.safeForeach(mCallbacks, c -> c.onEffectsSupressorChanged());
+ synchronized (mCallbacksLock) {
+ Utils.safeForeach(mCallbacks, c -> c.onEffectsSupressorChanged());
+ }
}
private void fireZenChanged(int zen) {
- Utils.safeForeach(mCallbacks, c -> c.onZenChanged(zen));
+ synchronized (mCallbacksLock) {
+ Utils.safeForeach(mCallbacks, c -> c.onZenChanged(zen));
+ }
}
private void fireZenAvailableChanged(boolean available) {
- Utils.safeForeach(mCallbacks, c -> c.onZenAvailableChanged(available));
+ synchronized (mCallbacksLock) {
+ Utils.safeForeach(mCallbacks, c -> c.onZenAvailableChanged(available));
+ }
}
private void fireManualRuleChanged(ZenRule rule) {
- Utils.safeForeach(mCallbacks, c -> c.onManualRuleChanged(rule));
+ synchronized (mCallbacksLock) {
+ Utils.safeForeach(mCallbacks, c -> c.onManualRuleChanged(rule));
+ }
}
@VisibleForTesting
protected void fireConfigChanged(ZenModeConfig config) {
- Utils.safeForeach(mCallbacks, c -> c.onConfigChanged(config));
+ synchronized (mCallbacksLock) {
+ Utils.safeForeach(mCallbacks, c -> c.onConfigChanged(config));
+ }
}
@VisibleForTesting
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/systemui/ScreenDecorationsTest.java b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
index cc96917..b84f85b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
@@ -16,6 +16,7 @@
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
+import static com.android.systemui.ScreenDecorations.rectsToRegion;
import static com.android.systemui.tuner.TunablePadding.FLAG_END;
import static com.android.systemui.tuner.TunablePadding.FLAG_START;
@@ -35,6 +36,7 @@
import android.app.Fragment;
import android.content.res.Configuration;
+import android.graphics.Rect;
import android.os.Handler;
import android.support.test.filters.SmallTest;
import android.testing.AndroidTestingRunner;
@@ -58,6 +60,8 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.Collections;
+
@RunWithLooper
@RunWith(AndroidTestingRunner.class)
@SmallTest
@@ -240,4 +244,11 @@
mScreenDecorations.onConfigurationChanged(null);
assertEquals(mScreenDecorations.mRoundedDefault, 5);
}
+
+ @Test
+ public void testBoundingRectsToRegion() throws Exception {
+ Rect rect = new Rect(1, 2, 3, 4);
+ assertThat(rectsToRegion(Collections.singletonList(rect)).getBounds(), is(rect));
+ }
+
}
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/power/PowerUITest.java b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
index a9d49f9..b44630a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
@@ -65,10 +65,12 @@
private static final long ONE_HOUR_MILLIS = Duration.ofHours(1).toMillis();
public static final int BELOW_WARNING_BUCKET = -1;
public static final long BELOW_HYBRID_THRESHOLD = TimeUnit.HOURS.toMillis(2);
+ public static final long BELOW_SEVERE_HYBRID_THRESHOLD = TimeUnit.MINUTES.toMillis(30);
public static final long ABOVE_HYBRID_THRESHOLD = TimeUnit.HOURS.toMillis(4);
private static final long ABOVE_CHARGE_CYCLE_THRESHOLD = Duration.ofHours(8).toMillis();
private static final int OLD_BATTERY_LEVEL_NINE = 9;
private static final int OLD_BATTERY_LEVEL_10 = 10;
+ private static final long VERY_BELOW_SEVERE_HYBRID_THRESHOLD = TimeUnit.MINUTES.toMillis(15);
private HardwarePropertiesManager mHardProps;
private WarningsUI mMockWarnings;
private PowerUI mPowerUI;
@@ -467,6 +469,35 @@
}
@Test
+ public void testSevereWarning_countsAsLowAndSevere_WarningOnlyShownOnce() {
+ mPowerUI.start();
+ when(mEnhancedEstimates.isHybridNotificationEnabled()).thenReturn(true);
+ when(mEnhancedEstimates.getLowWarningThreshold()).thenReturn(PowerUI.THREE_HOURS_IN_MILLIS);
+ when(mEnhancedEstimates.getSevereWarningThreshold()).thenReturn(ONE_HOUR_MILLIS);
+ when(mEnhancedEstimates.getEstimate())
+ .thenReturn(new Estimate(BELOW_SEVERE_HYBRID_THRESHOLD, true));
+ mPowerUI.mBatteryStatus = BatteryManager.BATTERY_HEALTH_GOOD;
+
+ // reduce battery level to handle time based trigger -> level trigger interactions
+ mPowerUI.mBatteryLevel = 5;
+ boolean shouldShow =
+ mPowerUI.shouldShowLowBatteryWarning(UNPLUGGED, UNPLUGGED, ABOVE_WARNING_BUCKET,
+ ABOVE_WARNING_BUCKET, BELOW_SEVERE_HYBRID_THRESHOLD,
+ POWER_SAVER_OFF, BatteryManager.BATTERY_HEALTH_GOOD);
+ assertTrue(shouldShow);
+
+ // actually run the end to end since it handles changing the internal state.
+ mPowerUI.maybeShowBatteryWarning(OLD_BATTERY_LEVEL_10, UNPLUGGED, UNPLUGGED,
+ ABOVE_WARNING_BUCKET, ABOVE_WARNING_BUCKET);
+
+ shouldShow =
+ mPowerUI.shouldShowLowBatteryWarning(UNPLUGGED, UNPLUGGED, ABOVE_WARNING_BUCKET,
+ ABOVE_WARNING_BUCKET, VERY_BELOW_SEVERE_HYBRID_THRESHOLD,
+ POWER_SAVER_OFF, BatteryManager.BATTERY_HEALTH_GOOD);
+ assertFalse(shouldShow);
+ }
+
+ @Test
public void testMaybeShowBatteryWarning_onlyQueriesEstimateOnBatteryLevelChangeOrNull() {
mPowerUI.start();
Estimate estimate = new Estimate(BELOW_HYBRID_THRESHOLD, true);
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 97%
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 19974f8..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;
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 87%
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 438f9e4..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,7 +11,7 @@
* 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;
@@ -27,6 +27,7 @@
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
+import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -35,9 +36,12 @@
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.systemui.Dependency;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.plugins.PluginInstanceManager.PluginInfo;
-import com.android.systemui.plugins.PluginManagerImpl.PluginInstanceManagerFactory;
+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.shared.plugins.PluginInstanceManager.PluginInfo;
+import com.android.systemui.shared.plugins.PluginManagerImpl.PluginInstanceManagerFactory;
import org.junit.Before;
import org.junit.Test;
@@ -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, new String[0],
- mMockExceptionHandler);
+
+ mPluginManager = new PluginManagerImpl(getContext(), mMockFactory, true,
+ mMockExceptionHandler, new PluginInitializerImpl() {
+ @Override
+ public String[] getWhitelistedPlugins(Context context) {
+ return new String[0];
+ }
+ });
resetExceptionHandler();
mMockListener = mock(PluginListener.class);
}
@@ -109,7 +119,12 @@
@RunWithLooper(setAsMainLooper = true)
public void testNonDebuggable_noWhitelist() {
mPluginManager = new PluginManagerImpl(getContext(), mMockFactory, false,
- new String[0], mMockExceptionHandler);
+ mMockExceptionHandler, new PluginInitializerImpl() {
+ @Override
+ public String[] getWhitelistedPlugins(Context context) {
+ return new String[0];
+ }
+ });
resetExceptionHandler();
mPluginManager.addPluginListener("myAction", mMockListener, TestPlugin.class);
@@ -121,7 +136,12 @@
@RunWithLooper(setAsMainLooper = true)
public void testNonDebuggable_whitelistedPkg() {
mPluginManager = new PluginManagerImpl(getContext(), mMockFactory, false,
- new String[] {WHITELISTED_PACKAGE}, mMockExceptionHandler);
+ mMockExceptionHandler, new PluginInitializerImpl() {
+ @Override
+ public String[] getWhitelistedPlugins(Context context) {
+ return new String[] {WHITELISTED_PACKAGE};
+ }
+ });
resetExceptionHandler();
mPluginManager.addPluginListener("myAction", mMockListener, TestPlugin.class);
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/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/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index 1e3d42b..f8b2436 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
@@ -15,6 +15,8 @@
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertNull;
+
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -32,14 +34,15 @@
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;
+import android.view.View;
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 +94,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 +114,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);
@@ -314,6 +317,36 @@
verify(mStackScroller).setEmptyShadeView(any());
}
+ @Test
+ @UiThreadTest
+ public void testSetIsBeingDraggedResetsExposedMenu() {
+ NotificationSwipeHelper swipeActionHelper =
+ (NotificationSwipeHelper) mStackScroller.getSwipeActionHelper();
+ swipeActionHelper.setExposedMenuView(new View(mContext));
+ mStackScroller.setIsBeingDragged(true);
+ assertNull(swipeActionHelper.getExposedMenuView());
+ }
+
+ @Test
+ @UiThreadTest
+ public void testPanelTrackingStartResetsExposedMenu() {
+ NotificationSwipeHelper swipeActionHelper =
+ (NotificationSwipeHelper) mStackScroller.getSwipeActionHelper();
+ swipeActionHelper.setExposedMenuView(new View(mContext));
+ mStackScroller.onPanelTrackingStarted();
+ assertNull(swipeActionHelper.getExposedMenuView());
+ }
+
+ @Test
+ @UiThreadTest
+ public void testDarkModeResetsExposedMenu() {
+ NotificationSwipeHelper swipeActionHelper =
+ (NotificationSwipeHelper) mStackScroller.getSwipeActionHelper();
+ swipeActionHelper.setExposedMenuView(new View(mContext));
+ mStackScroller.setDarkAmount(0.1f, 0.1f);
+ assertNull(swipeActionHelper.getExposedMenuView());
+ }
+
private void setBarStateForTest(int state) {
ArgumentCaptor<StatusBarStateController.StateListener> captor =
ArgumentCaptor.forClass(StatusBarStateController.StateListener.class);
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/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index 5006b0b..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 testShouldHeadsUp_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")
@@ -379,12 +379,12 @@
}
@Test
- public void testShouldHeadsUp_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")
@@ -400,11 +400,11 @@
}
@Test
- public void testShouldHeadsUp_suppressedHeadsUp() {
+ 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);
@@ -418,11 +418,11 @@
}
@Test
- public void testShouldHeadsUp_noSuppressedHeadsUp() {
+ 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);
@@ -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 90c10fd..5e87707 100644
--- a/proto/src/metrics_constants/metrics_constants.proto
+++ b/proto/src/metrics_constants/metrics_constants.proto
@@ -5926,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.
@@ -6447,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
diff --git a/proto/src/wifi.proto b/proto/src/wifi.proto
index 7f8989d..033e996 100644
--- a/proto/src/wifi.proto
+++ b/proto/src/wifi.proto
@@ -479,6 +479,9 @@
// Hardware revision (EVT, DVT, PVT etc.)
optional string hardware_revision = 124;
+
+ // Total wifi link layer usage data over the logging duration in ms.
+ optional WifiLinkLayerUsageStats wifi_link_layer_usage_stats = 125;
}
// Information that gets logged for every WiFi connection.
@@ -1654,4 +1657,21 @@
// Num of installed Passpoint profile with same eap method
optional int32 count = 2;
+}
+
+message WifiLinkLayerUsageStats {
+ // Total logging duration in ms.
+ optional int64 logging_duration_ms = 1;
+
+ // Total time the wifi radio is on in ms over the logging duration.
+ optional int64 radio_on_time_ms = 2;
+
+ // Total time the wifi radio is doing tx in ms over the logging duration.
+ optional int64 radio_tx_time_ms = 3;
+
+ // Total time the wifi radio is doing rx in ms over the logging duration.
+ optional int64 radio_rx_time_ms = 4;
+
+ // Total time the wifi radio is scanning in ms over the logging duration.
+ optional int64 radio_scan_time_ms = 5;
}
\ No newline at end of file
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 9bee8db..44ef8b6d 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -379,25 +379,20 @@
@Override
public void onPackageUpdateFinished(String packageName, int uid) {
- // Unbind all services from this package, and then update the user state to
- // re-bind new versions of them.
+ // The package should already be removed from mBoundServices, and added into
+ // mBindingServices in binderDied() during updating. Remove services from this
+ // package from mBindingServices, and then update the user state to re-bind new
+ // versions of them.
synchronized (mLock) {
final int userId = getChangingUserId();
if (userId != mCurrentUserId) {
return;
}
UserState userState = getUserStateLocked(userId);
- boolean unboundAService = false;
- for (int i = userState.mBoundServices.size() - 1; i >= 0; i--) {
- AccessibilityServiceConnection boundService =
- userState.mBoundServices.get(i);
- String servicePkg = boundService.mComponentName.getPackageName();
- if (servicePkg.equals(packageName)) {
- boundService.unbindLocked();
- unboundAService = true;
- }
- }
- if (unboundAService) {
+ boolean reboundAService = userState.mBindingServices.removeIf(
+ component -> component != null
+ && component.getPackageName().equals(packageName));
+ if (reboundAService) {
onUserStateChangedLocked(userState);
}
}
@@ -419,6 +414,7 @@
String compPkg = comp.getPackageName();
if (compPkg.equals(packageName)) {
it.remove();
+ userState.mBindingServices.remove(comp);
// Update the enabled services setting.
persistComponentNamesToSettingLocked(
Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
@@ -457,6 +453,7 @@
return true;
}
it.remove();
+ userState.mBindingServices.remove(comp);
persistComponentNamesToSettingLocked(
Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
userState.mEnabledServices, userId);
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
index e0eb269..9d84f57 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
@@ -253,11 +253,11 @@
return;
}
mWasConnectedAndDied = true;
- mSystemSupport.getKeyEventDispatcher().flush(this);
UserState userState = mUserStateWeakReference.get();
if (userState != null) {
userState.serviceDisconnectedLocked(this);
}
+ resetLocked();
mSystemSupport.getMagnificationController().resetIfNeeded(mId);
mSystemSupport.onClientChange(false);
}
diff --git a/services/art-profile b/services/art-profile
index 3c60eee..328f8f7 100644
--- a/services/art-profile
+++ b/services/art-profile
@@ -2254,8 +2254,8 @@
HPLcom/android/server/wm/DisplayContent;->lambda$new$8(Lcom/android/server/wm/DisplayContent;Lcom/android/server/wm/WindowState;)V
HPLcom/android/server/wm/DisplayContent;->prepareSurfaces()V
HPLcom/android/server/wm/DisplayContent;->resetAnimationBackgroundAnimator()V
-HPLcom/android/server/wm/DisplayContent;->setTouchExcludeRegion(Lcom/android/server/wm/Task;)V
HPLcom/android/server/wm/DisplayContent;->skipTraverseChild(Lcom/android/server/wm/WindowContainer;)Z
+HPLcom/android/server/wm/DisplayContent;->updateTouchExcludeRegion()V
HPLcom/android/server/wm/DockedStackDividerController;->isResizing()Z
HPLcom/android/server/wm/DragDropController;->dragDropActiveLocked()Z
HPLcom/android/server/wm/InputMonitor$UpdateInputForAllWindowsConsumer;->accept(Lcom/android/server/wm/WindowState;)V
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index ad2f82c..af33cbc 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -142,6 +142,8 @@
static final boolean RECORD_DEVICE_IDLE_ALARMS = false;
static final String TIMEZONE_PROPERTY = "persist.sys.timezone";
+ static final int TICK_HISTORY_DEPTH = 10;
+
// Indices into the APP_STANDBY_MIN_DELAYS and KEYS_APP_STANDBY_DELAY arrays
static final int ACTIVE_INDEX = 0;
static final int WORKING_INDEX = 1;
@@ -176,21 +178,25 @@
private long mNextNonWakeUpSetAt;
private long mLastWakeup;
private long mLastTrigger;
+
private long mLastTickSet;
- private long mLastTickIssued; // elapsed
private long mLastTickReceived;
private long mLastTickAdded;
private long mLastTickRemoved;
+ // ring buffer of recent TIME_TICK issuance, in the elapsed timebase
+ private final long[] mTickHistory = new long[TICK_HISTORY_DEPTH];
+ private int mNextTickHistory;
+
private final Injector mInjector;
int mBroadcastRefCount = 0;
PowerManager.WakeLock mWakeLock;
- boolean mLastWakeLockUnimportantForLogging;
ArrayList<Alarm> mPendingNonWakeupAlarms = new ArrayList<>();
ArrayList<InFlight> mInFlight = new ArrayList<>();
AlarmHandler mHandler;
ClockReceiver mClockReceiver;
final DeliveryTracker mDeliveryTracker = new DeliveryTracker();
- PendingIntent mTimeTickSender;
+ Intent mTimeTickIntent;
+ IAlarmListener mTimeTickTrigger;
PendingIntent mDateChangeSender;
Random mRandom;
boolean mInteractive = true;
@@ -509,7 +515,7 @@
end = clampPositive(seed.maxWhenElapsed);
flags = seed.flags;
alarms.add(seed);
- if (seed.operation == mTimeTickSender) {
+ if (seed.listener == mTimeTickTrigger) {
mLastTickAdded = mInjector.getCurrentTimeMillis();
}
}
@@ -534,7 +540,7 @@
index = 0 - index - 1;
}
alarms.add(index, alarm);
- if (alarm.operation == mTimeTickSender) {
+ if (alarm.listener == mTimeTickTrigger) {
mLastTickAdded = mInjector.getCurrentTimeMillis();
}
if (DEBUG_BATCH) {
@@ -572,7 +578,7 @@
if (alarm.alarmClock != null) {
mNextAlarmClockMayChange = true;
}
- if (alarm.operation == mTimeTickSender) {
+ if (alarm.listener == mTimeTickTrigger) {
mLastTickRemoved = mInjector.getCurrentTimeMillis();
}
} else {
@@ -690,8 +696,7 @@
Alarm a = alarms.get(i);
final int alarmPrio;
- if (a.operation != null
- && Intent.ACTION_TIME_TICK.equals(a.operation.getIntent().getAction())) {
+ if (a.listener == mTimeTickTrigger) {
alarmPrio = PRIO_TICK;
} else if (a.wakeup) {
alarmPrio = PRIO_WAKEUP;
@@ -823,7 +828,7 @@
}
final int batchSize = alarms.size();
for (int j = 0; j < batchSize; j++) {
- if (alarms.get(j).operation == mTimeTickSender) {
+ if (alarms.get(j).listener == mTimeTickTrigger) {
return true;
}
}
@@ -1111,10 +1116,7 @@
updateNextAlarmClockLocked();
// And send a TIME_TICK right now, since it is important to get the UI updated.
- try {
- mTimeTickSender.send();
- } catch (PendingIntent.CanceledException e) {
- }
+ mHandler.post(() -> getContext().sendBroadcastAsUser(mTimeTickIntent, UserHandle.ALL));
}
static final class InFlight {
@@ -1312,12 +1314,36 @@
}
mWakeLock = mInjector.getAlarmWakeLock();
- mTimeTickSender = PendingIntent.getBroadcastAsUser(getContext(), 0,
- new Intent(Intent.ACTION_TIME_TICK).addFlags(
- Intent.FLAG_RECEIVER_REGISTERED_ONLY
- | Intent.FLAG_RECEIVER_FOREGROUND
- | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS), 0,
- UserHandle.ALL);
+ mTimeTickIntent = new Intent(Intent.ACTION_TIME_TICK).addFlags(
+ Intent.FLAG_RECEIVER_REGISTERED_ONLY
+ | Intent.FLAG_RECEIVER_FOREGROUND
+ | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
+
+ mTimeTickTrigger = new IAlarmListener.Stub() {
+ @Override
+ public void doAlarm(final IAlarmCompleteListener callback) throws RemoteException {
+ if (DEBUG_BATCH) {
+ Slog.v(TAG, "Received TIME_TICK alarm; rescheduling");
+ }
+
+ // Via handler because dispatch invokes this within its lock. OnAlarmListener
+ // takes care of this automatically, but we're using the direct internal
+ // interface here rather than that client-side wrapper infrastructure.
+ mHandler.post(() -> {
+ getContext().sendBroadcastAsUser(mTimeTickIntent, UserHandle.ALL);
+
+ try {
+ callback.alarmComplete(this);
+ } catch (RemoteException e) { /* local method call */ }
+ });
+
+ synchronized (mLock) {
+ mLastTickReceived = mInjector.getCurrentTimeMillis();
+ }
+ mClockReceiver.scheduleTimeTickEvent();
+ }
+ };
+
Intent intent = new Intent(Intent.ACTION_DATE_CHANGED);
intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING
| Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
@@ -1438,12 +1464,9 @@
}
}
- void removeImpl(PendingIntent operation) {
- if (operation == null) {
- return;
- }
+ void removeImpl(PendingIntent operation, IAlarmListener listener) {
synchronized (mLock) {
- removeLocked(operation, null);
+ removeLocked(operation, listener);
}
}
@@ -1887,9 +1910,9 @@
pw.println(" App Standby Parole: " + mAppStandbyParole);
pw.println();
- final long nowRTC = mInjector.getCurrentTimeMillis();
final long nowELAPSED = mInjector.getElapsedRealtime();
final long nowUPTIME = SystemClock.uptimeMillis();
+ final long nowRTC = mInjector.getCurrentTimeMillis();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
pw.print(" nowRTC="); pw.print(nowRTC);
@@ -1899,13 +1922,27 @@
pw.print(" mLastTimeChangeClockTime="); pw.print(mLastTimeChangeClockTime);
pw.print("="); pw.println(sdf.format(new Date(mLastTimeChangeClockTime)));
pw.print(" mLastTimeChangeRealtime="); pw.println(mLastTimeChangeRealtime);
- pw.print(" mLastTickIssued=");
- pw.println(sdf.format(new Date(nowRTC - (nowELAPSED - mLastTickIssued))));
pw.print(" mLastTickReceived="); pw.println(sdf.format(new Date(mLastTickReceived)));
pw.print(" mLastTickSet="); pw.println(sdf.format(new Date(mLastTickSet)));
pw.print(" mLastTickAdded="); pw.println(sdf.format(new Date(mLastTickAdded)));
pw.print(" mLastTickRemoved="); pw.println(sdf.format(new Date(mLastTickRemoved)));
+ if (RECORD_ALARMS_IN_HISTORY) {
+ pw.println();
+ pw.println(" Recent TIME_TICK history:");
+ int i = mNextTickHistory;
+ do {
+ i--;
+ if (i < 0) i = TICK_HISTORY_DEPTH - 1;
+ final long time = mTickHistory[i];
+ pw.print(" ");
+ pw.println((time > 0)
+ ? sdf.format(new Date(nowRTC - (nowELAPSED - time)))
+ : "-");
+ } while (i != mNextTickHistory);
+ pw.println();
+ }
+
SystemServiceManager ssm = LocalServices.getService(SystemServiceManager.class);
if (ssm != null) {
pw.println();
@@ -3640,8 +3677,8 @@
}
// StatsLog requires currentTimeMillis(), which == nowRTC to within usecs.
StatsLog.write(StatsLog.WALL_CLOCK_TIME_SHIFTED, nowRTC);
- removeImpl(mTimeTickSender);
- removeImpl(mDateChangeSender);
+ removeImpl(null, mTimeTickTrigger);
+ removeImpl(mDateChangeSender, null);
rebatchAllAlarms();
mClockReceiver.scheduleTimeTickEvent();
mClockReceiver.scheduleDateChangedEvent();
@@ -3764,14 +3801,8 @@
void setWakelockWorkSource(PendingIntent pi, WorkSource ws, int type, String tag,
int knownUid, boolean first) {
try {
- final boolean unimportant = pi == mTimeTickSender;
- mWakeLock.setUnimportantForLogging(unimportant);
- if (first || mLastWakeLockUnimportantForLogging) {
- mWakeLock.setHistoryTag(tag);
- } else {
- mWakeLock.setHistoryTag(null);
- }
- mLastWakeLockUnimportantForLogging = unimportant;
+ mWakeLock.setHistoryTag(first ? tag : null);
+
if (ws != null) {
mWakeLock.setWorkSource(ws);
return;
@@ -3828,7 +3859,7 @@
if (alarm.repeatInterval > 0) {
// This IntentSender is no longer valid, but this
// is a repeating alarm, so toss the hoser.
- removeImpl(alarm.operation);
+ removeImpl(alarm.operation, null);
}
}
}
@@ -3886,22 +3917,13 @@
class ClockReceiver extends BroadcastReceiver {
public ClockReceiver() {
IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_TIME_TICK);
filter.addAction(Intent.ACTION_DATE_CHANGED);
getContext().registerReceiver(this, filter);
}
@Override
public void onReceive(Context context, Intent intent) {
- if (intent.getAction().equals(Intent.ACTION_TIME_TICK)) {
- if (DEBUG_BATCH) {
- Slog.v(TAG, "Received TIME_TICK alarm; rescheduling");
- }
- synchronized (mLock) {
- mLastTickReceived = mInjector.getCurrentTimeMillis();
- }
- scheduleTimeTickEvent();
- } else if (intent.getAction().equals(Intent.ACTION_DATE_CHANGED)) {
+ if (intent.getAction().equals(Intent.ACTION_DATE_CHANGED)) {
// Since the kernel does not keep track of DST, we need to
// reset the TZ information at the beginning of each day
// based off of the current Zone gmt offset + userspace tracked
@@ -3923,7 +3945,7 @@
final WorkSource workSource = null; // Let system take blame for time tick events.
setImpl(ELAPSED_REALTIME, mInjector.getElapsedRealtime() + tickEventDelay, 0,
- 0, mTimeTickSender, null, null, AlarmManager.FLAG_STANDALONE, workSource,
+ 0, null, mTimeTickTrigger, null, AlarmManager.FLAG_STANDALONE, workSource,
null, Process.myUid(), "android");
// Finally, remember when we set the tick alarm
@@ -4333,10 +4355,6 @@
// PendingIntent alarm
mSendCount++;
- if (alarm.priorityClass.priority == PRIO_TICK) {
- mLastTickIssued = nowELAPSED;
- }
-
try {
alarm.operation.send(getContext(), 0,
mBackgroundIntent.putExtra(
@@ -4344,13 +4362,10 @@
mDeliveryTracker, mHandler, null,
allowWhileIdle ? mIdleOptions : null);
} catch (PendingIntent.CanceledException e) {
- if (alarm.operation == mTimeTickSender) {
- Slog.wtf(TAG, "mTimeTickSender canceled");
- }
if (alarm.repeatInterval > 0) {
// This IntentSender is no longer valid, but this
// is a repeating alarm, so toss it
- removeImpl(alarm.operation);
+ removeImpl(alarm.operation, null);
}
// No actual delivery was possible, so the delivery tracker's
// 'finished' callback won't be invoked. We also don't need
@@ -4362,6 +4377,16 @@
} else {
// Direct listener callback alarm
mListenerCount++;
+
+ if (RECORD_ALARMS_IN_HISTORY) {
+ if (alarm.listener == mTimeTickTrigger) {
+ mTickHistory[mNextTickHistory++] = nowELAPSED;
+ if (mNextTickHistory >= TICK_HISTORY_DEPTH) {
+ mNextTickHistory = 0;
+ }
+ }
+ }
+
try {
if (DEBUG_LISTENER_CALLBACK) {
Slog.v(TAG, "Alarm to uid=" + alarm.uid
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index e41a09e..5e8ffb7 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -57,6 +57,7 @@
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;
@@ -88,6 +89,7 @@
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;
@@ -259,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;
@@ -390,9 +393,9 @@
private static final int EVENT_PROMPT_UNVALIDATED = 29;
/**
- * used internally to (re)configure mobile data always-on settings.
+ * used internally to (re)configure always-on networks.
*/
- private static final int EVENT_CONFIGURE_MOBILE_DATA_ALWAYS_ON = 30;
+ private static final int EVENT_CONFIGURE_ALWAYS_ON_NETWORKS = 30;
/**
* used to add a network listener with a pending intent
@@ -748,6 +751,12 @@
mDefaultMobileDataRequest = createDefaultInternetRequestForTransport(
NetworkCapabilities.TRANSPORT_CELLULAR, NetworkRequest.Type.BACKGROUND_REQUEST);
+ // The default WiFi request is a background request so that apps using WiFi are
+ // migrated to a better network (typically ethernet) when one comes up, instead
+ // of staying on WiFi forever.
+ mDefaultWifiRequest = createDefaultInternetRequestForTransport(
+ NetworkCapabilities.TRANSPORT_WIFI, NetworkRequest.Type.BACKGROUND_REQUEST);
+
mHandlerThread = new HandlerThread("ConnectivityServiceThread");
mHandlerThread.start();
mHandler = new InternalHandler(mHandlerThread.getLooper());
@@ -759,7 +768,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(
@@ -767,6 +776,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);
@@ -849,7 +859,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();
@@ -864,8 +874,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);
}
@@ -896,7 +906,7 @@
mMultipathPolicyTracker = new MultipathPolicyTracker(mContext, mHandler);
- mDnsManager = new DnsManager(mContext, mNetd, mSystemProperties);
+ mDnsManager = new DnsManager(mContext, mNMS, mSystemProperties);
registerPrivateDnsSettingsCallbacks();
}
@@ -912,7 +922,7 @@
return mDefaultRequest;
}
};
- return new Tethering(mContext, mNetd, mStatsService, mPolicyManager,
+ return new Tethering(mContext, mNMS, mStatsService, mPolicyManager,
IoThread.get().getLooper(), new MockableSystemProperties(),
deps);
}
@@ -944,8 +954,8 @@
// 2. Give FakeSettingsProvider an alternative notification mechanism and have the test use it
// by subclassing SettingsObserver.
@VisibleForTesting
- void updateMobileDataAlwaysOn() {
- mHandler.sendEmptyMessage(EVENT_CONFIGURE_MOBILE_DATA_ALWAYS_ON);
+ void updateAlwaysOnNetworks() {
+ mHandler.sendEmptyMessage(EVENT_CONFIGURE_ALWAYS_ON_NETWORKS);
}
// See FakeSettingsProvider comment above.
@@ -954,22 +964,30 @@
mHandler.sendEmptyMessage(EVENT_PRIVATE_DNS_SETTINGS_CHANGED);
}
- private void handleMobileDataAlwaysOn() {
+ private void handleAlwaysOnNetworkRequest(
+ NetworkRequest networkRequest, String settingName, boolean defaultValue) {
final boolean enable = toBool(Settings.Global.getInt(
- mContext.getContentResolver(), Settings.Global.MOBILE_DATA_ALWAYS_ON, 1));
- final boolean isEnabled = (mNetworkRequests.get(mDefaultMobileDataRequest) != null);
+ mContext.getContentResolver(), settingName, encodeBool(defaultValue)));
+ final boolean isEnabled = (mNetworkRequests.get(networkRequest) != null);
if (enable == isEnabled) {
return; // Nothing to do.
}
if (enable) {
handleRegisterNetworkRequest(new NetworkRequestInfo(
- null, mDefaultMobileDataRequest, new Binder()));
+ null, networkRequest, new Binder()));
} else {
- handleReleaseNetworkRequest(mDefaultMobileDataRequest, Process.SYSTEM_UID);
+ handleReleaseNetworkRequest(networkRequest, Process.SYSTEM_UID);
}
}
+ private void handleConfigureAlwaysOnNetworks() {
+ handleAlwaysOnNetworkRequest(
+ mDefaultMobileDataRequest,Settings.Global.MOBILE_DATA_ALWAYS_ON, true);
+ handleAlwaysOnNetworkRequest(mDefaultWifiRequest, Settings.Global.WIFI_ALWAYS_REQUESTED,
+ false);
+ }
+
private void registerSettingsCallbacks() {
// Watch for global HTTP proxy changes.
mSettingsObserver.observe(
@@ -979,7 +997,12 @@
// Watch for whether or not to keep mobile data always on.
mSettingsObserver.observe(
Settings.Global.getUriFor(Settings.Global.MOBILE_DATA_ALWAYS_ON),
- EVENT_CONFIGURE_MOBILE_DATA_ALWAYS_ON);
+ EVENT_CONFIGURE_ALWAYS_ON_NETWORKS);
+
+ // Watch for whether or not to keep wifi always on.
+ mSettingsObserver.observe(
+ Settings.Global.getUriFor(Settings.Global.WIFI_ALWAYS_REQUESTED),
+ EVENT_CONFIGURE_ALWAYS_ON_NETWORKS);
}
private void registerPrivateDnsSettingsCallbacks() {
@@ -1476,6 +1499,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
@@ -1486,6 +1523,9 @@
*/
@Override
public boolean requestRouteToHostAddress(int networkType, byte[] hostAddress) {
+ if (disallowedBecauseSystemCaller()) {
+ return false;
+ }
enforceChangePermission();
if (mProtectedNetworks.contains(networkType)) {
enforceConnectivityInternalPermission();
@@ -1563,7 +1603,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);
@@ -1797,7 +1837,7 @@
}
void systemReady() {
- loadGlobalProxy();
+ mProxyTracker.loadGlobalProxy();
registerNetdEventCallback();
synchronized (this) {
@@ -1814,8 +1854,8 @@
// for user to unlock device too.
updateLockdownVpn();
- // Configure whether mobile data is always on.
- mHandler.sendMessage(mHandler.obtainMessage(EVENT_CONFIGURE_MOBILE_DATA_ALWAYS_ON));
+ // Create network requests for always-on networks.
+ mHandler.sendMessage(mHandler.obtainMessage(EVENT_CONFIGURE_ALWAYS_ON_NETWORKS));
mHandler.sendMessage(mHandler.obtainMessage(EVENT_SYSTEM_READY));
@@ -1853,7 +1893,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);
@@ -1872,7 +1912,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);
}
@@ -1880,6 +1920,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.
*/
@@ -1907,7 +1959,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);
}
@@ -2561,7 +2613,7 @@
}
nai.clearLingerState();
if (nai.isSatisfyingRequest(mDefaultRequest.requestId)) {
- removeDataActivityTracking(nai);
+ updateDataActivityTracking(null /* newNetwork */, nai);
notifyLockdownVpn(nai);
ensureNetworkTransitionWakelock(nai.name());
}
@@ -2581,7 +2633,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);
}
@@ -2779,20 +2831,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
@@ -3106,8 +3144,8 @@
handlePromptUnvalidated((Network) msg.obj);
break;
}
- case EVENT_CONFIGURE_MOBILE_DATA_ALWAYS_ON: {
- handleMobileDataAlwaysOn();
+ case EVENT_CONFIGURE_ALWAYS_ON_NETWORKS: {
+ handleConfigureAlwaysOnNetworks();
break;
}
// Sent by KeepaliveTracker to process an app request on the state machine thread.
@@ -3417,31 +3455,6 @@
mProxyTracker.setGlobalProxy(proxyProperties);
}
- private void loadGlobalProxy() {
- ContentResolver res = mContext.getContentResolver();
- String host = Settings.Global.getString(res, Settings.Global.GLOBAL_HTTP_PROXY_HOST);
- int port = Settings.Global.getInt(res, Settings.Global.GLOBAL_HTTP_PROXY_PORT, 0);
- String exclList = Settings.Global.getString(res,
- Settings.Global.GLOBAL_HTTP_PROXY_EXCLUSION_LIST);
- String pacFileUrl = Settings.Global.getString(res, Settings.Global.GLOBAL_HTTP_PROXY_PAC);
- if (!TextUtils.isEmpty(host) || !TextUtils.isEmpty(pacFileUrl)) {
- ProxyInfo proxyProperties;
- if (!TextUtils.isEmpty(pacFileUrl)) {
- proxyProperties = new ProxyInfo(pacFileUrl);
- } else {
- proxyProperties = new ProxyInfo(host, port, exclList);
- }
- if (!proxyProperties.isValid()) {
- if (DBG) log("Invalid proxy properties, ignoring: " + proxyProperties.toString());
- return;
- }
-
- synchronized (mProxyTracker.mProxyLock) {
- mProxyTracker.mGlobalProxy = proxyProperties;
- }
- }
- }
-
@Override
@Nullable
public ProxyInfo getGlobalProxy() {
@@ -3760,7 +3773,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);
}
@@ -4015,7 +4028,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();
@@ -4535,6 +4548,10 @@
// priority networks like Wi-Fi are active.
private final NetworkRequest mDefaultMobileDataRequest;
+ // Request used to optionally keep wifi data active even when higher
+ // priority networks like ethernet are active.
+ private final NetworkRequest mDefaultWifiRequest;
+
private NetworkAgentInfo getNetworkForRequest(int requestId) {
synchronized (mNetworkForRequestId) {
return mNetworkForRequestId.get(requestId);
@@ -4632,7 +4649,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 {
@@ -4671,9 +4688,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);
@@ -4689,7 +4706,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);
@@ -4699,7 +4716,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);
}
@@ -4723,7 +4740,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);
@@ -4734,7 +4751,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);
@@ -4745,7 +4762,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);
}
@@ -4857,7 +4874,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);
}
@@ -4917,12 +4934,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!
@@ -5091,9 +5108,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);
}
@@ -5266,6 +5283,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.
@@ -5488,12 +5506,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) {
diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java
index a34c2b9..376bc0d 100644
--- a/services/core/java/com/android/server/DeviceIdleController.java
+++ b/services/core/java/com/android/server/DeviceIdleController.java
@@ -77,6 +77,7 @@
import android.util.Xml;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IBatteryStats;
import com.android.internal.os.AtomicFile;
import com.android.internal.os.BackgroundThread;
@@ -104,6 +105,126 @@
/**
* Keeps track of device idleness and drives low power mode based on that.
+ *
+ * Test: atest com.android.server.DeviceIdleControllerTest
+ *
+ * Current idling state machine (as of Android 9 Pie). This can be visualized using Graphviz:
+
+ digraph {
+ subgraph deep {
+ label="deep";
+
+ STATE_ACTIVE [label="STATE_ACTIVE\nScreen on OR Charging OR Alarm going off soon"]
+ STATE_INACTIVE [label="STATE_INACTIVE\nScreen off AND Not charging"]
+ STATE_IDLE_PENDING [
+ label="STATE_IDLE_PENDING\nSignificant motion monitoring turned on"
+ ]
+ STATE_SENSING [label="STATE_SENSING\nMonitoring for ANY motion"]
+ STATE_LOCATING [
+ label="STATE_LOCATING\nRequesting location, motion monitoring still on"
+ ]
+ STATE_IDLE [
+ label="STATE_IDLE\nLocation and motion detection turned off\n"
+ + "Significant motion monitoring still on"
+ ]
+ STATE_IDLE_MAINTENANCE [label="STATE_IDLE_MAINTENANCE\n"]
+
+ STATE_ACTIVE -> STATE_INACTIVE [label="becomeInactiveIfAppropriateLocked()"]
+
+ STATE_INACTIVE -> STATE_ACTIVE [
+ label="handleMotionDetectedLocked(), becomeActiveLocked()"
+ ]
+ STATE_INACTIVE -> STATE_IDLE_PENDING [label="stepIdleStateLocked()"]
+
+ STATE_IDLE_PENDING -> STATE_ACTIVE [
+ label="handleMotionDetectedLocked(), becomeActiveLocked()"
+ ]
+ STATE_IDLE_PENDING -> STATE_SENSING [label="stepIdleStateLocked()"]
+
+ STATE_SENSING -> STATE_ACTIVE [
+ label="handleMotionDetectedLocked(), becomeActiveLocked()"
+ ]
+ STATE_SENSING -> STATE_LOCATING [label="stepIdleStateLocked()"]
+ STATE_SENSING -> STATE_IDLE [
+ label="stepIdleStateLocked()\n"
+ + "No Location Manager OR (no Network provider AND no GPS provider)"
+ ]
+
+ STATE_LOCATING -> STATE_ACTIVE [
+ label="handleMotionDetectedLocked(), becomeActiveLocked()"
+ ]
+ STATE_LOCATING -> STATE_IDLE [label="stepIdleStateLocked()"]
+
+ STATE_IDLE -> STATE_ACTIVE [label="handleMotionDetectedLocked(), becomeActiveLocked()"]
+ STATE_IDLE -> STATE_IDLE_MAINTENANCE [label="stepIdleStateLocked()"]
+
+ STATE_IDLE_MAINTENANCE -> STATE_ACTIVE [
+ label="handleMotionDetectedLocked(), becomeActiveLocked()"
+ ]
+ STATE_IDLE_MAINTENANCE -> STATE_IDLE [
+ label="stepIdleStateLocked(), exitMaintenanceEarlyIfNeededLocked()"
+ ]
+ }
+
+ subgraph light {
+ label="light"
+
+ LIGHT_STATE_ACTIVE [
+ label="LIGHT_STATE_ACTIVE\nScreen on OR Charging OR Alarm going off soon"
+ ]
+ LIGHT_STATE_INACTIVE [label="LIGHT_STATE_INACTIVE\nScreen off AND Not charging"]
+ LIGHT_STATE_PRE_IDLE [
+ label="LIGHT_STATE_PRE_IDLE\n"
+ + "Delay going into LIGHT_STATE_IDLE due to some running jobs or alarms"
+ ]
+ LIGHT_STATE_IDLE [label="LIGHT_STATE_IDLE\n"]
+ LIGHT_STATE_WAITING_FOR_NETWORK [
+ label="LIGHT_STATE_WAITING_FOR_NETWORK\n"
+ + "Coming out of LIGHT_STATE_IDLE, waiting for network"
+ ]
+ LIGHT_STATE_IDLE_MAINTENANCE [label="LIGHT_STATE_IDLE_MAINTENANCE\n"]
+ LIGHT_STATE_OVERRIDE [
+ label="LIGHT_STATE_OVERRIDE\nDevice in deep doze, light no longer changing states"
+ ]
+
+ LIGHT_STATE_ACTIVE -> LIGHT_STATE_INACTIVE [
+ label="becomeInactiveIfAppropriateLocked()"
+ ]
+ LIGHT_STATE_ACTIVE -> LIGHT_STATE_OVERRIDE [label="deep goes to STATE_IDLE"]
+
+ LIGHT_STATE_INACTIVE -> LIGHT_STATE_ACTIVE [label="becomeActiveLocked()"]
+ LIGHT_STATE_INACTIVE -> LIGHT_STATE_PRE_IDLE [label="active jobs"]
+ LIGHT_STATE_INACTIVE -> LIGHT_STATE_IDLE [label="no active jobs"]
+ LIGHT_STATE_INACTIVE -> LIGHT_STATE_OVERRIDE [label="deep goes to STATE_IDLE"]
+
+ LIGHT_STATE_PRE_IDLE -> LIGHT_STATE_ACTIVE [label="becomeActiveLocked()"]
+ LIGHT_STATE_PRE_IDLE -> LIGHT_STATE_IDLE [
+ label="stepLightIdleStateLocked(), exitMaintenanceEarlyIfNeededLocked()"
+ ]
+ LIGHT_STATE_PRE_IDLE -> LIGHT_STATE_OVERRIDE [label="deep goes to STATE_IDLE"]
+
+ LIGHT_STATE_IDLE -> LIGHT_STATE_ACTIVE [label="becomeActiveLocked()"]
+ LIGHT_STATE_IDLE -> LIGHT_STATE_WAITING_FOR_NETWORK [label="no network"]
+ LIGHT_STATE_IDLE -> LIGHT_STATE_IDLE_MAINTENANCE
+ LIGHT_STATE_IDLE -> LIGHT_STATE_OVERRIDE [label="deep goes to STATE_IDLE"]
+
+ LIGHT_STATE_WAITING_FOR_NETWORK -> LIGHT_STATE_ACTIVE [label="becomeActiveLocked()"]
+ LIGHT_STATE_WAITING_FOR_NETWORK -> LIGHT_STATE_IDLE_MAINTENANCE
+ LIGHT_STATE_WAITING_FOR_NETWORK -> LIGHT_STATE_OVERRIDE [
+ label="deep goes to STATE_IDLE"
+ ]
+
+ LIGHT_STATE_IDLE_MAINTENANCE -> LIGHT_STATE_ACTIVE [label="becomeActiveLocked()"]
+ LIGHT_STATE_IDLE_MAINTENANCE -> LIGHT_STATE_IDLE [
+ label="stepLightIdleStateLocked(), exitMaintenanceEarlyIfNeededLocked()"
+ ]
+ LIGHT_STATE_IDLE_MAINTENANCE -> LIGHT_STATE_OVERRIDE [label="deep goes to STATE_IDLE"]
+
+ LIGHT_STATE_OVERRIDE -> LIGHT_STATE_ACTIVE [
+ label="handleMotionDetectedLocked(), becomeActiveLocked()"
+ ]
+ }
+ }
*/
public class DeviceIdleController extends SystemService
implements AnyMotionDetector.DeviceIdleCallback {
@@ -148,21 +269,29 @@
private boolean mScreenLocked;
/** Device is currently active. */
- private static final int STATE_ACTIVE = 0;
+ @VisibleForTesting
+ static final int STATE_ACTIVE = 0;
/** Device is inactive (screen off, no motion) and we are waiting to for idle. */
- private static final int STATE_INACTIVE = 1;
+ @VisibleForTesting
+ static final int STATE_INACTIVE = 1;
/** Device is past the initial inactive period, and waiting for the next idle period. */
- private static final int STATE_IDLE_PENDING = 2;
+ @VisibleForTesting
+ static final int STATE_IDLE_PENDING = 2;
/** Device is currently sensing motion. */
- private static final int STATE_SENSING = 3;
+ @VisibleForTesting
+ static final int STATE_SENSING = 3;
/** Device is currently finding location (and may still be sensing). */
- private static final int STATE_LOCATING = 4;
+ @VisibleForTesting
+ static final int STATE_LOCATING = 4;
/** Device is in the idle state, trying to stay asleep as much as possible. */
- private static final int STATE_IDLE = 5;
+ @VisibleForTesting
+ static final int STATE_IDLE = 5;
/** Device is in the idle state, but temporarily out of idle to do regular maintenance. */
- private static final int STATE_IDLE_MAINTENANCE = 6;
+ @VisibleForTesting
+ static final int STATE_IDLE_MAINTENANCE = 6;
- private static String stateToString(int state) {
+ @VisibleForTesting
+ static String stateToString(int state) {
switch (state) {
case STATE_ACTIVE: return "ACTIVE";
case STATE_INACTIVE: return "INACTIVE";
@@ -176,21 +305,30 @@
}
/** Device is currently active. */
- private static final int LIGHT_STATE_ACTIVE = 0;
+ @VisibleForTesting
+ static final int LIGHT_STATE_ACTIVE = 0;
/** Device is inactive (screen off) and we are waiting to for the first light idle. */
- private static final int LIGHT_STATE_INACTIVE = 1;
+ @VisibleForTesting
+ static final int LIGHT_STATE_INACTIVE = 1;
/** Device is about to go idle for the first time, wait for current work to complete. */
- private static final int LIGHT_STATE_PRE_IDLE = 3;
+ @VisibleForTesting
+ static final int LIGHT_STATE_PRE_IDLE = 3;
/** Device is in the light idle state, trying to stay asleep as much as possible. */
- private static final int LIGHT_STATE_IDLE = 4;
+ @VisibleForTesting
+ static final int LIGHT_STATE_IDLE = 4;
/** Device is in the light idle state, we want to go in to idle maintenance but are
* waiting for network connectivity before doing so. */
- private static final int LIGHT_STATE_WAITING_FOR_NETWORK = 5;
+ @VisibleForTesting
+ static final int LIGHT_STATE_WAITING_FOR_NETWORK = 5;
/** Device is in the light idle state, but temporarily out of idle to do regular maintenance. */
- private static final int LIGHT_STATE_IDLE_MAINTENANCE = 6;
+ @VisibleForTesting
+ static final int LIGHT_STATE_IDLE_MAINTENANCE = 6;
/** Device light idle state is overriden, now applying deep doze state. */
- private static final int LIGHT_STATE_OVERRIDE = 7;
- private static String lightStateToString(int state) {
+ @VisibleForTesting
+ static final int LIGHT_STATE_OVERRIDE = 7;
+
+ @VisibleForTesting
+ static String lightStateToString(int state) {
switch (state) {
case LIGHT_STATE_ACTIVE: return "ACTIVE";
case LIGHT_STATE_INACTIVE: return "INACTIVE";
@@ -382,6 +520,8 @@
public void onAlarm() {
if (mState == STATE_SENSING) {
synchronized (DeviceIdleController.this) {
+ // Restart the device idle progression in case the device moved but the screen
+ // didn't turn on.
becomeInactiveIfAppropriateLocked();
}
}
@@ -422,11 +562,16 @@
}
};
- private final class MotionListener extends TriggerEventListener
+ @VisibleForTesting
+ final class MotionListener extends TriggerEventListener
implements SensorEventListener {
boolean active = false;
+ public boolean isActive() {
+ return active;
+ }
+
@Override
public void onTrigger(TriggerEvent event) {
synchronized (DeviceIdleController.this) {
@@ -472,7 +617,7 @@
active = false;
}
}
- private final MotionListener mMotionListener = new MotionListener();
+ @VisibleForTesting final MotionListener mMotionListener = new MotionListener();
private final LocationListener mGenericLocationListener = new LocationListener() {
@Override
@@ -594,7 +739,7 @@
public float LIGHT_IDLE_FACTOR;
/**
- * This is the maximum time we will run in idle maintenence mode.
+ * This is the maximum time we will run in idle maintenance mode.
* @see Settings.Global#DEVICE_IDLE_CONSTANTS
* @see #KEY_LIGHT_MAX_IDLE_TIMEOUT
*/
@@ -1360,6 +1505,45 @@
}
}
+ static class Injector {
+ private final Context mContext;
+
+ Injector(Context ctx) {
+ mContext = ctx;
+ }
+
+ AlarmManager getAlarmManager() {
+ return mContext.getSystemService(AlarmManager.class);
+ }
+
+ AnyMotionDetector getAnyMotionDetector(Handler handler, SensorManager sm,
+ AnyMotionDetector.DeviceIdleCallback callback, float angleThreshold) {
+ return new AnyMotionDetector(getPowerManager(), handler, sm, callback, angleThreshold);
+ }
+
+ AppStateTracker getAppStateTracker(Context ctx, Looper looper) {
+ return new AppStateTracker(ctx, looper);
+ }
+
+ ConnectivityService getConnectivityService() {
+ return (ConnectivityService) ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
+ }
+
+ LocationManager getLocationManager() {
+ return mContext.getSystemService(LocationManager.class);
+ }
+
+ MyHandler getHandler(DeviceIdleController ctlr) {
+ return ctlr.new MyHandler(BackgroundThread.getHandler().getLooper());
+ }
+
+ PowerManager getPowerManager() {
+ return mContext.getSystemService(PowerManager.class);
+ }
+ }
+
+ private final Injector mInjector;
+
private ActivityTaskManagerInternal.ScreenObserver mScreenObserver =
new ActivityTaskManagerInternal.ScreenObserver() {
@Override
@@ -1373,14 +1557,19 @@
}
};
- public DeviceIdleController(Context context) {
+ @VisibleForTesting DeviceIdleController(Context context, Injector injector) {
super(context);
+ mInjector = injector;
mConfigFile = new AtomicFile(new File(getSystemDir(), "deviceidle.xml"));
- mHandler = new MyHandler(BackgroundThread.getHandler().getLooper());
- mAppStateTracker = new AppStateTracker(context, FgThread.get().getLooper());
+ mHandler = mInjector.getHandler(this);
+ mAppStateTracker = mInjector.getAppStateTracker(context, FgThread.get().getLooper());
LocalServices.addService(AppStateTracker.class, mAppStateTracker);
}
+ public DeviceIdleController(Context context) {
+ this(context, new Injector(context));
+ }
+
boolean isAppOnWhitelistInternal(int appid) {
synchronized (this) {
return Arrays.binarySearch(mPowerSaveWhitelistAllAppIdArray, appid) >= 0;
@@ -1459,20 +1648,19 @@
public void onBootPhase(int phase) {
if (phase == PHASE_SYSTEM_SERVICES_READY) {
synchronized (this) {
- mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
+ mAlarmManager = mInjector.getAlarmManager();
mBatteryStats = BatteryStatsService.getService();
mLocalActivityManager = getLocalService(ActivityManagerInternal.class);
mLocalActivityTaskManager = getLocalService(ActivityTaskManagerInternal.class);
mLocalPowerManager = getLocalService(PowerManagerInternal.class);
- mPowerManager = getContext().getSystemService(PowerManager.class);
+ mPowerManager = mInjector.getPowerManager();
mActiveIdleWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
"deviceidle_maint");
mActiveIdleWakeLock.setReferenceCounted(false);
mGoingIdleWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
"deviceidle_going_idle");
mGoingIdleWakeLock.setReferenceCounted(true);
- mConnectivityService = (ConnectivityService)ServiceManager.getService(
- Context.CONNECTIVITY_SERVICE);
+ mConnectivityService = mInjector.getConnectivityService();
mNetworkPolicyManager = INetworkPolicyManager.Stub.asInterface(
ServiceManager.getService(Context.NETWORK_POLICY_SERVICE));
mNetworkPolicyManagerInternal = getLocalService(NetworkPolicyManagerInternal.class);
@@ -1495,8 +1683,7 @@
if (getContext().getResources().getBoolean(
com.android.internal.R.bool.config_autoPowerModePrefetchLocation)) {
- mLocationManager = (LocationManager) getContext().getSystemService(
- Context.LOCATION_SERVICE);
+ mLocationManager = mInjector.getLocationManager();
mLocationRequest = new LocationRequest()
.setQuality(LocationRequest.ACCURACY_FINE)
.setInterval(0)
@@ -1506,9 +1693,8 @@
float angleThreshold = getContext().getResources().getInteger(
com.android.internal.R.integer.config_autoPowerModeThresholdAngle) / 100f;
- mAnyMotionDetector = new AnyMotionDetector(
- (PowerManager) getContext().getSystemService(Context.POWER_SERVICE),
- mHandler, mSensorManager, this, angleThreshold);
+ mAnyMotionDetector = mInjector.getAnyMotionDetector(mHandler, mSensorManager, this,
+ angleThreshold);
mAppStateTracker.onSystemServicesReady();
@@ -2005,6 +2191,11 @@
}
}
+ @VisibleForTesting
+ boolean isScreenOn() {
+ return mScreenOn;
+ }
+
void updateInteractivityLocked() {
// The interactivity state from the power manager tells us whether the display is
// in a state that we need to keep things running so they will update at a normal
@@ -2024,6 +2215,11 @@
}
}
+ @VisibleForTesting
+ boolean isCharging() {
+ return mCharging;
+ }
+
void updateChargingLocked(boolean charging) {
if (DEBUG) Slog.i(TAG, "updateChargingLocked: charging=" + charging);
if (!charging && mCharging) {
@@ -2071,6 +2267,18 @@
}
}
+ /** Must only be used in tests. */
+ @VisibleForTesting
+ void setDeepEnabledForTest(boolean enabled) {
+ mDeepEnabled = enabled;
+ }
+
+ /** Must only be used in tests. */
+ @VisibleForTesting
+ void setLightEnabledForTest(boolean enabled) {
+ mLightEnabled = enabled;
+ }
+
void becomeInactiveIfAppropriateLocked() {
if (DEBUG) Slog.d(TAG, "becomeInactiveIfAppropriateLocked()");
if ((!mScreenOn && !mCharging) || mForceIdle) {
@@ -2093,7 +2301,7 @@
}
}
- void resetIdleManagementLocked() {
+ private void resetIdleManagementLocked() {
mNextIdlePendingDelay = 0;
mNextIdleDelay = 0;
mNextLightIdleDelay = 0;
@@ -2104,7 +2312,7 @@
mAnyMotionDetector.stop();
}
- void resetLightIdleManagementLocked() {
+ private void resetLightIdleManagementLocked() {
cancelLightAlarmLocked();
}
@@ -2117,6 +2325,11 @@
}
}
+ @VisibleForTesting
+ int getLightState() {
+ return mLightState;
+ }
+
void stepLightIdleStateLocked(String reason) {
if (mLightState == LIGHT_STATE_OVERRIDE) {
// If we are already in deep device idle mode, then
@@ -2200,6 +2413,18 @@
}
}
+ /** Must only be used in tests. */
+ @VisibleForTesting
+ void setLocationManagerForTest(LocationManager lm) {
+ mLocationManager = lm;
+ }
+
+ @VisibleForTesting
+ int getState() {
+ return mState;
+ }
+
+ @VisibleForTesting
void stepIdleStateLocked(String reason) {
if (DEBUG) Slog.d(TAG, "stepIdleStateLocked: mState=" + mState);
EventLogTags.writeDeviceIdleStep();
diff --git a/services/core/java/com/android/server/GestureLauncherService.java b/services/core/java/com/android/server/GestureLauncherService.java
index b7b5bd9..8077e34 100644
--- a/services/core/java/com/android/server/GestureLauncherService.java
+++ b/services/core/java/com/android/server/GestureLauncherService.java
@@ -356,6 +356,12 @@
public boolean interceptPowerKeyDown(KeyEvent event, boolean interactive,
MutableBoolean outLaunched) {
+ if (event.isLongPress()) {
+ // Long presses are sent as a second key down. If the long press threshold is set lower
+ // than the double tap of sequence interval thresholds, this could cause false double
+ // taps or consecutive taps, so we want to ignore the long press event.
+ return false;
+ }
boolean launched = false;
boolean intercept = false;
long powerTapInterval;
diff --git a/services/core/java/com/android/server/LooperStatsService.java b/services/core/java/com/android/server/LooperStatsService.java
index 4f0e170..96ce6a4 100644
--- a/services/core/java/com/android/server/LooperStatsService.java
+++ b/services/core/java/com/android/server/LooperStatsService.java
@@ -129,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/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 461d39d..8e64b50 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -1466,9 +1466,9 @@
+ ") when binding service " + service);
}
- ActivityRecord activity = null;
+ ActivityServiceConnectionsHolder<ConnectionRecord> activity = null;
if (token != null) {
- activity = ActivityRecord.isInStackLocked(token);
+ activity = mAm.mAtmInternal.getServiceConnectionsHolder(token);
if (activity == null) {
Slog.w(TAG, "Binding with unknown activity: " + token);
return 0;
@@ -1644,10 +1644,7 @@
clist.add(c);
b.connections.add(c);
if (activity != null) {
- if (activity.connections == null) {
- activity.connections = new HashSet<ConnectionRecord>();
- }
- activity.connections.add(c);
+ activity.addConnection(c);
}
b.client.connections.add(c);
c.startAssociationIfNeeded();
@@ -2861,8 +2858,8 @@
smap.ensureNotStartingBackgroundLocked(r);
}
- void removeConnectionLocked(
- ConnectionRecord c, ProcessRecord skipApp, ActivityRecord skipAct) {
+ void removeConnectionLocked(ConnectionRecord c, ProcessRecord skipApp,
+ ActivityServiceConnectionsHolder skipAct) {
IBinder binder = c.conn.asBinder();
AppBindRecord b = c.binding;
ServiceRecord s = b.service;
@@ -2876,9 +2873,7 @@
b.connections.remove(c);
c.stopAssociation();
if (c.activity != null && c.activity != skipAct) {
- if (c.activity.connections != null) {
- c.activity.connections.remove(c);
- }
+ c.activity.removeConnection(c);
}
if (b.client != skipApp) {
b.client.connections.remove(c);
diff --git a/services/core/java/com/android/server/am/ActivityDisplay.java b/services/core/java/com/android/server/am/ActivityDisplay.java
index fab967c..fcb717e 100644
--- a/services/core/java/com/android/server/am/ActivityDisplay.java
+++ b/services/core/java/com/android/server/am/ActivityDisplay.java
@@ -51,6 +51,7 @@
import android.app.ActivityOptions;
import android.app.WindowConfiguration;
import android.graphics.Point;
+import android.os.UserHandle;
import android.util.IntArray;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
@@ -112,6 +113,13 @@
*/
private boolean mRemoved;
+ /**
+ * A focusable stack that is purposely to be positioned at the top. Although the stack may not
+ * have the topmost index, it is used as a preferred candidate to prevent being unable to resume
+ * target stack properly when there are other focusable always-on-top stacks.
+ */
+ private ActivityStack mPreferredTopFocusableStack;
+
// Cached reference to some special stacks we tend to get a lot so we don't need to loop
// through the list to find them.
private ActivityStack mHomeStack = null;
@@ -164,6 +172,9 @@
if (DEBUG_STACK) Slog.v(TAG_STACK, "removeChild: detaching " + stack
+ " from displayId=" + mDisplayId);
mStacks.remove(stack);
+ if (mPreferredTopFocusableStack == stack) {
+ mPreferredTopFocusableStack = null;
+ }
removeStackReferenceIfNeeded(stack);
releaseSelfIfNeeded();
mSupervisor.mService.updateSleepIfNeededLocked();
@@ -185,9 +196,21 @@
private void positionChildAt(ActivityStack stack, int position, boolean includingParents) {
// TODO: Keep in sync with WindowContainer.positionChildAt(), once we change that to adjust
// the position internally, also update the logic here
- mStacks.remove(stack);
+ final boolean wasContained = mStacks.remove(stack);
final int insertPosition = getTopInsertPosition(stack, position);
mStacks.add(insertPosition, stack);
+
+ // The insert position may be adjusted to non-top when there is always-on-top stack. Since
+ // the original position is preferred to be top, the stack should have higher priority when
+ // we are looking for top focusable stack. The condition {@code wasContained} restricts the
+ // preferred stack is set only when moving an existing stack to top instead of adding a new
+ // stack that may be too early (e.g. in the middle of launching or reparenting).
+ if (wasContained && position >= mStacks.size() - 1 && stack.isFocusableAndVisible()) {
+ mPreferredTopFocusableStack = stack;
+ } else if (mPreferredTopFocusableStack == stack) {
+ mPreferredTopFocusableStack = null;
+ }
+
// Since positionChildAt() is called during the creation process of pinned stacks,
// ActivityStack#getWindowContainerController() can be null. In this special case,
// since DisplayContest#positionStackAt() is called in TaskStack#onConfigurationChanged(),
@@ -356,10 +379,18 @@
this, stackId, mSupervisor, windowingMode, activityType, onTop);
}
+ /**
+ * Get the preferred focusable stack in priority. If the preferred stack does not exist, find a
+ * focusable and visible stack from the top of stacks in this display.
+ */
ActivityStack getFocusedStack() {
+ if (mPreferredTopFocusableStack != null) {
+ return mPreferredTopFocusableStack;
+ }
+
for (int i = mStacks.size() - 1; i >= 0; --i) {
final ActivityStack stack = mStacks.get(i);
- if (stack.isFocusable() && stack.shouldBeVisible(null /* starting */)) {
+ if (stack.isFocusableAndVisible()) {
return stack;
}
}
@@ -381,7 +412,7 @@
if (ignoreCurrent && stack == currentFocus) {
continue;
}
- if (!stack.isFocusable() || !stack.shouldBeVisible(null)) {
+ if (!stack.isFocusableAndVisible()) {
continue;
}
@@ -911,6 +942,13 @@
return mDisplayAccessUIDs;
}
+ /**
+ * @see Display#FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS
+ */
+ boolean supportsSystemDecorations() {
+ return mDisplay.supportsSystemDecorations();
+ }
+
private boolean shouldDestroyContentOnRemove() {
return mDisplay.getRemoveMode() == REMOVE_MODE_DESTROY_CONTENT;
}
@@ -920,6 +958,10 @@
&& (mSupervisor.mService.mRunningVoice == null);
}
+ void setFocusedApp(ActivityRecord r, boolean moveFocusNow) {
+ mWindowContainerController.setFocusedApp(r.appToken, moveFocusNow);
+ }
+
/**
* @return the stack currently above the {@param stack}. Can be null if the {@param stack} is
* already top-most.
@@ -981,6 +1023,57 @@
positionChildAt(stack, Math.max(0, insertIndex));
}
+ void moveHomeStackToFront(String reason) {
+ if (mHomeStack != null) {
+ mHomeStack.moveToFront(reason);
+ }
+ }
+
+ /** Returns true if the focus activity was adjusted to the home stack top activity. */
+ boolean moveHomeActivityToTop(String reason) {
+ final ActivityRecord top = getHomeActivity();
+ if (top == null) {
+ return false;
+ }
+ mSupervisor.moveFocusableActivityToTop(top, reason);
+ return true;
+ }
+
+ @Nullable
+ ActivityStack getHomeStack() {
+ return mHomeStack;
+ }
+
+ @Nullable
+ ActivityRecord getHomeActivity() {
+ return getHomeActivityForUser(mSupervisor.mCurrentUser);
+ }
+
+ @Nullable
+ ActivityRecord getHomeActivityForUser(int userId) {
+ if (mHomeStack == null) {
+ return null;
+ }
+
+ final ArrayList<TaskRecord> tasks = mHomeStack.getAllTasks();
+ for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
+ final TaskRecord task = tasks.get(taskNdx);
+ if (!task.isActivityTypeHome()) {
+ continue;
+ }
+
+ final ArrayList<ActivityRecord> activities = task.mActivities;
+ for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
+ final ActivityRecord r = activities.get(activityNdx);
+ if (r.isActivityTypeHome()
+ && ((userId == UserHandle.USER_ALL) || (r.userId == userId))) {
+ return r;
+ }
+ }
+ }
+ return null;
+ }
+
boolean isSleeping() {
return mSleeping;
}
@@ -1042,6 +1135,9 @@
if (mSplitScreenPrimaryStack != null) {
pw.println(myPrefix + "mSplitScreenPrimaryStack=" + mSplitScreenPrimaryStack);
}
+ if (mPreferredTopFocusableStack != null) {
+ pw.println(myPrefix + "mPreferredTopFocusableStack=" + mPreferredTopFocusableStack);
+ }
}
public void dumpStacks(PrintWriter pw) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 6e90d5b..9c96968 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -90,6 +90,7 @@
import static android.provider.Settings.Global.NETWORK_ACCESS_TIMEOUT_MS;
import static android.provider.Settings.Global.WAIT_FOR_DEBUGGER;
import static android.text.format.DateUtils.DAY_IN_MILLIS;
+import static android.view.Display.DEFAULT_DISPLAY;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALL;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ANR;
@@ -368,7 +369,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;
@@ -2381,7 +2381,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
@@ -2397,6 +2398,9 @@
mUserController = new UserController(this);
+ mPendingIntentController = new PendingIntentController(
+ mHandlerThread.getLooper(), mUserController);
+
GL_ES_VERSION = SystemProperties.getInt("ro.opengles.version",
ConfigurationInfo.GL_ES_VERSION_UNDEFINED);
@@ -2412,9 +2416,6 @@
mAtmInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
mStackSupervisor = mActivityTaskManager.mStackSupervisor;
- mPendingIntentController = new PendingIntentController(
- mHandlerThread.getLooper(), mUserController);
-
mProcessCpuThread = new Thread("CpuTracker") {
@Override
public void run() {
@@ -3537,6 +3538,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");
@@ -3546,12 +3550,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!");
@@ -3720,6 +3726,14 @@
}
boolean startHomeActivityLocked(int userId, String reason) {
+ return startHomeActivityLocked(userId, reason, DEFAULT_DISPLAY);
+ }
+
+ /**
+ * This starts home activity on displays that can have system decorations and only if the
+ * home activity can have multiple instances.
+ */
+ boolean startHomeActivityLocked(int userId, String reason, int displayId) {
if (mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL
&& mTopAction == null) {
// We are running in factory test mode, but unable to find
@@ -3743,7 +3757,8 @@
// For ANR debugging to verify if the user activity is the one that actually
// launched.
final String myReason = reason + ":" + userId + ":" + resolvedUserId;
- mActivityTaskManager.getActivityStartController().startHomeActivity(intent, aInfo, myReason);
+ mActivityTaskManager.getActivityStartController().startHomeActivity(intent, aInfo,
+ myReason, displayId);
}
} else {
Slog.wtf(TAG, "No home screen found for " + intent, new Throwable());
@@ -4197,7 +4212,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) {
@@ -4238,18 +4252,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) {
@@ -7636,7 +7638,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;
@@ -8809,7 +8827,7 @@
? new ActivityOptions(options)
: ActivityOptions.makeBasic();
activityOptions.setLaunchTaskId(
- mStackSupervisor.getHomeActivity().getTask().taskId);
+ mStackSupervisor.getDefaultDisplayHomeActivity().getTask().taskId);
mContext.startActivityAsUser(intent, activityOptions.toBundle(),
UserHandle.CURRENT);
} finally {
@@ -9430,9 +9448,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);
}
}
@@ -10587,9 +10606,13 @@
currApp.importanceReasonImportance =
ActivityManager.RunningAppProcessInfo.procStateToImportance(
app.adjSourceProcState);
- } else if (app.adjSource instanceof ActivityRecord) {
- ActivityRecord r = (ActivityRecord)app.adjSource;
- if (r.app != null) currApp.importanceReasonPid = r.app.getPid();
+ } else if (app.adjSource instanceof ActivityServiceConnectionsHolder) {
+ ActivityServiceConnectionsHolder r =
+ (ActivityServiceConnectionsHolder) app.adjSource;
+ final int pid = r.getActivityPid();
+ if (pid != -1) {
+ currApp.importanceReasonPid = pid;
+ }
}
if (app.adjTarget instanceof ComponentName) {
currApp.importanceReasonComponent = (ComponentName)app.adjTarget;
@@ -17764,10 +17787,10 @@
if ((cr.flags&Context.BIND_TREAT_LIKE_ACTIVITY) != 0) {
app.treatLikeActivity = true;
}
- final ActivityRecord a = cr.activity;
+ final ActivityServiceConnectionsHolder a = cr.activity;
if ((cr.flags&Context.BIND_ADJUST_WITH_ACTIVITY) != 0) {
- if (a != null && adj > ProcessList.FOREGROUND_APP_ADJ && (a.visible
- || a.isState(ActivityState.RESUMED, ActivityState.PAUSING))) {
+ if (a != null && adj > ProcessList.FOREGROUND_APP_ADJ
+ && a.isActivityVisible()) {
adj = ProcessList.FOREGROUND_APP_ADJ;
if ((cr.flags&Context.BIND_NOT_FOREGROUND) == 0) {
if ((cr.flags&Context.BIND_IMPORTANT) != 0) {
@@ -20882,6 +20905,16 @@
return res;
}
}
+
+ @Override
+ public void disconnectActivityFromServices(Object connectionHolder) {
+ synchronized(ActivityManagerService.this) {
+ final ActivityServiceConnectionsHolder c =
+ (ActivityServiceConnectionsHolder) connectionHolder;
+ c.forEachConnection(cr -> mServices.removeConnectionLocked(
+ (ConnectionRecord) cr, null, c));
+ }
+ }
}
/**
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 4bcaf71..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);
}
diff --git a/services/core/java/com/android/server/am/ActivityMetricsLogger.java b/services/core/java/com/android/server/am/ActivityMetricsLogger.java
index 78b42f2..18cdb05 100644
--- a/services/core/java/com/android/server/am/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/am/ActivityMetricsLogger.java
@@ -75,6 +75,7 @@
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;
@@ -89,10 +90,14 @@
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;
@@ -100,7 +105,12 @@
import com.android.server.LocalServices;
/**
- * 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 {
@@ -115,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;
@@ -143,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 {
@@ -165,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;
@@ -204,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;
}
}
@@ -335,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.
@@ -351,6 +389,7 @@
mWindowingModeTransitionInfo.put(windowingMode, newInfo);
mLastWindowingModeTransitionInfo.put(windowingMode, newInfo);
mCurrentTransitionDeviceUptime = (int) (SystemClock.uptimeMillis() / 1000);
+ startTraces(newInfo);
}
/**
@@ -364,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;
}
/**
@@ -394,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) {
@@ -413,7 +455,7 @@
info.reason = windowingModeToReason.valueAt(index);
}
if (allWindowsDrawn()) {
- reset(false /* abort */);
+ reset(false /* abort */, null /* WindowingModeTransitionInfo */);
}
}
@@ -452,8 +494,9 @@
logAppTransitionCancel(info);
mWindowingModeTransitionInfo.remove(r.getWindowingMode());
if (mWindowingModeTransitionInfo.size() == 0) {
- reset(true /* abort */);
+ reset(true /* abort */, info);
}
+ stopFullyDrawnTraceIfNeeded();
}
}
}
@@ -488,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);
}
@@ -512,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);
@@ -533,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;
}
@@ -545,6 +588,7 @@
final int currentTransitionDelayMs = mCurrentTransitionDelayMs;
BackgroundThread.getHandler().post(() -> logAppTransition(
currentTransitionDeviceUptime, currentTransitionDelayMs, infoSnapshot));
+ BackgroundThread.getHandler().post(() -> logAppDisplayed(infoSnapshot));
info.launchedActivity.info.launchToken = null;
}
@@ -571,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);
}
@@ -612,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;
@@ -625,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);
@@ -652,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,
@@ -753,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) {
@@ -798,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 77cfb12..fe10baf 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -31,6 +31,7 @@
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,6 @@
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;
@@ -112,8 +112,6 @@
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;
@@ -164,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;
@@ -186,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;
@@ -266,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
@@ -288,7 +283,7 @@
ActivityOptions pendingOptions; // most recently given options
ActivityOptions returningOptions; // options that are coming back via convertToTranslucent
AppTimeTracker appTimeTracker; // set if we are tracking the time in this app/task/activity
- HashSet<ConnectionRecord> connections; // All ConnectionRecord we hold
+ ActivityServiceConnectionsHolder mServiceConnectionsHolder; // Service connections.
UriPermissionOwner uriPermissions; // current special URI access perms.
WindowProcessController app; // if non-null, hosting application
private ActivityState mState; // current state we are in
@@ -536,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) {
@@ -563,8 +549,8 @@
pw.print(" configChangeFlags=");
pw.println(Integer.toHexString(configChangeFlags));
}
- if (connections != null) {
- pw.print(prefix); pw.print("connections="); pw.println(connections);
+ if (mServiceConnectionsHolder != null) {
+ pw.print(prefix); pw.print("connections="); pw.println(mServiceConnectionsHolder);
}
if (info != null) {
pw.println(prefix + "resizeMode=" + ActivityInfo.resizeModeToString(info.resizeMode));
@@ -2006,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) {
@@ -2090,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;
diff --git a/services/core/java/com/android/server/am/ActivityServiceConnectionsHolder.java b/services/core/java/com/android/server/am/ActivityServiceConnectionsHolder.java
new file mode 100644
index 0000000..b1ced29
--- /dev/null
+++ b/services/core/java/com/android/server/am/ActivityServiceConnectionsHolder.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.am;
+
+import static com.android.server.am.ActivityStack.ActivityState.PAUSING;
+import static com.android.server.am.ActivityStack.ActivityState.RESUMED;
+
+import java.io.PrintWriter;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.function.Consumer;
+
+/**
+ * Class for tracking the connections to services on the AM side that activities on the
+ * WM side (in the future) bind with for things like oom score adjustment. Would normally be one
+ * instance of this per activity for tracking all services connected to that activity. AM will
+ * sometimes query this to bump the OOM score for the processes with services connected to visible
+ * activities.
+ */
+public class ActivityServiceConnectionsHolder<T> {
+
+ private final ActivityTaskManagerService mService;
+
+ /** The activity the owns this service connection object. */
+ private final ActivityRecord mActivity;
+
+ /**
+ * The service connection object bounded with the owning activity. They represent
+ * ConnectionRecord on the AM side, however we don't need to know their object representation
+ * on the WM side since we don't perform operations on the object. Mainly here for communication
+ * and booking with the AM side.
+ */
+ private HashSet<T> mConnections;
+
+ ActivityServiceConnectionsHolder(ActivityTaskManagerService service, ActivityRecord activity) {
+ mService = service;
+ mActivity = activity;
+ }
+
+ /** Adds a connection record that the activity has bound to a specific service. */
+ public void addConnection(T c) {
+ synchronized (mService.mGlobalLock) {
+ if (mConnections == null) {
+ mConnections = new HashSet<>();
+ }
+ mConnections.add(c);
+ }
+ }
+
+ /** Removed a connection record between the activity and a specific service. */
+ public void removeConnection(T c) {
+ synchronized (mService.mGlobalLock) {
+ if (mConnections == null) {
+ return;
+ }
+ mConnections.remove(c);
+ }
+ }
+
+ public boolean isActivityVisible() {
+ synchronized (mService.mGlobalLock) {
+ return mActivity.visible || mActivity.isState(RESUMED, PAUSING);
+ }
+ }
+
+ public int getActivityPid() {
+ synchronized (mService.mGlobalLock) {
+ return mActivity.hasProcess() ? mActivity.app.getPid() : -1;
+ }
+ }
+
+ public void forEachConnection(Consumer<T> consumer) {
+ synchronized (mService.mGlobalLock) {
+ if (mConnections == null || mConnections.isEmpty()) {
+ return;
+ }
+ final Iterator<T> it = mConnections.iterator();
+ while (it.hasNext()) {
+ T c = it.next();
+ consumer.accept(c);
+ }
+ }
+ }
+
+ /** Removes the connection between the activity and all services that were connected to it. */
+ void disconnectActivityFromServices() {
+ if (mConnections == null || mConnections.isEmpty()) {
+ return;
+ }
+ mService.mH.post(() -> mService.mAmInternal.disconnectActivityFromServices(this));
+ }
+
+ public void dump(PrintWriter pw, String prefix) {
+ synchronized (mService.mGlobalLock) {
+ pw.println(prefix + "activity=" + mActivity);
+ }
+ }
+
+}
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 9f59bd8..ea807ad 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -151,7 +151,6 @@
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;
@@ -1103,8 +1102,7 @@
if (!isActivityTypeHome() && returnsToHomeStack()) {
// Make sure the home stack is behind this stack since that is where we should return to
// when this stack is no longer visible.
- // TODO(b/111541062): Move home stack on the current display
- mStackSupervisor.moveHomeStackToFront(reason + " returnToHome");
+ display.moveHomeStackToFront(reason + " returnToHome");
}
display.positionChildAtTop(this, true /* includingParents */);
@@ -1148,6 +1146,10 @@
return mStackSupervisor.isFocusable(this, r != null && r.isFocusable());
}
+ boolean isFocusableAndVisible() {
+ return isFocusable() && shouldBeVisible(null /* starting */);
+ }
+
final boolean isAttached() {
return getParent() != null;
}
@@ -1319,16 +1321,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);
}
@@ -1514,7 +1513,7 @@
prev.getTask().touchActiveTime();
clearLaunchTime(prev);
- mStackSupervisor.getLaunchTimeTracker().stopFullyDrawnTraceIfNeeded(getWindowingMode());
+ mStackSupervisor.getActivityMetricsLogger().stopFullyDrawnTraceIfNeeded();
mService.updateCpuStats();
@@ -2858,9 +2857,7 @@
if (DEBUG_STATES) Slog.d(TAG_STATES,
"resumeTopActivityInNextFocusableStack: " + reason + ", go home");
if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
- // Only resume home if on home display
- return isOnHomeDisplay() &&
- mStackSupervisor.resumeHomeStackTask(prev, reason);
+ return mStackSupervisor.resumeHomeActivity(prev, reason, mDisplayId);
}
/** Returns the position the input task should be placed in this stack. */
@@ -3454,8 +3451,8 @@
final String myReason = reason + " adjustFocus";
if (next == r) {
- mStackSupervisor.moveFocusableActivityStackToFrontLocked(
- mStackSupervisor.topRunningActivityLocked(), myReason);
+ mStackSupervisor.moveFocusableActivityToTop(mStackSupervisor.topRunningActivityLocked(),
+ myReason);
return;
}
@@ -3486,7 +3483,7 @@
}
// Whatever...go home.
- mStackSupervisor.moveHomeStackTaskToTop(myReason);
+ getDisplay().moveHomeActivityToTop(myReason);
}
/**
@@ -3515,7 +3512,7 @@
if (stack.isActivityTypeHome() && (top == null || !top.visible)) {
// If we will be focusing on the home stack next and its current top activity isn't
// visible, then use the move the home stack task to top to make the activity visible.
- mStackSupervisor.moveHomeStackTaskToTop(reason);
+ stack.getDisplay().moveHomeActivityToTop(reason);
return stack;
}
@@ -4237,15 +4234,11 @@
* Perform clean-up of service connections in an activity record.
*/
private void cleanUpActivityServicesLocked(ActivityRecord r) {
- // Throw away any services that have been bound by this activity.
- if (r.connections != null) {
- Iterator<ConnectionRecord> it = r.connections.iterator();
- while (it.hasNext()) {
- ConnectionRecord c = it.next();
- mService.mAm.mServices.removeConnectionLocked(c, null, r);
- }
- r.connections = null;
+ if (r.mServiceConnectionsHolder == null) {
+ return;
}
+ // Throw away any services that have been bound by this activity.
+ r.mServiceConnectionsHolder.disconnectActivityFromServices();
}
final void scheduleDestroyActivities(WindowProcessController owner, String reason) {
@@ -4623,22 +4616,6 @@
mStackSupervisor.invalidateTaskLayers();
}
- void moveHomeStackTaskToTop() {
- if (!isActivityTypeHome()) {
- throw new IllegalStateException("Calling moveHomeStackTaskToTop() on non-home stack: "
- + this);
- }
- final int top = mTaskHistory.size() - 1;
- if (top >= 0) {
- final TaskRecord task = mTaskHistory.get(top);
- if (DEBUG_TASKS || DEBUG_STACK) Slog.d(TAG_STACK,
- "moveHomeStackTaskToTop: moving " + task);
- mTaskHistory.remove(top);
- mTaskHistory.add(top, task);
- updateTaskMovement(task, true);
- }
- }
-
final void moveTaskToFrontLocked(TaskRecord tr, boolean noAnimation, ActivityOptions options,
AppTimeTracker timeTracker, String reason) {
if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "moveTaskToFront: " + tr);
@@ -4686,7 +4663,7 @@
// Set focus to the top running activity of this stack.
final ActivityRecord r = topRunningActivityLocked();
- mStackSupervisor.moveFocusableActivityStackToFrontLocked(r, reason);
+ mStackSupervisor.moveFocusableActivityToTop(r, reason);
if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, "Prepare to front transition: task=" + tr);
if (noAnimation) {
@@ -5227,11 +5204,11 @@
if (DEBUG_STACK) Slog.i(TAG_STACK, "removeTask: removing stack=" + this);
// We only need to adjust focused stack if this stack is in focus and we are not in the
// process of moving the task to the top of the stack that will be focused.
- if (isOnHomeDisplay() && mode != REMOVE_TASK_MODE_MOVING_TO_TOP
+ if (mode != REMOVE_TASK_MODE_MOVING_TO_TOP
&& mStackSupervisor.isTopDisplayFocusedStack(this)) {
String myReason = reason + " leftTaskHistoryEmpty";
if (!inMultiWindowMode() || adjustFocusToNextFocusableStack(myReason) == null) {
- mStackSupervisor.moveHomeStackToFront(myReason);
+ getDisplay().moveHomeStackToFront(myReason);
}
}
if (isAttached()) {
@@ -5438,7 +5415,7 @@
// Do not sleep activities in this stack if we're marked as focused and the keyguard
// is in the process of going away.
- if (mStackSupervisor.getTopDisplayFocusedStack() == this
+ if (isFocusedStackOnDisplay()
&& mStackSupervisor.getKeyguardController().isKeyguardGoingAway()) {
return false;
}
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 877c856..a968ae4 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;
@@ -39,6 +40,8 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.app.WindowConfiguration.activityTypeToString;
import static android.app.WindowConfiguration.windowingModeToString;
+import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE;
+import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TASK;
import static android.content.pm.PackageManager.PERMISSION_DENIED;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.graphics.Rect.copyOrNull;
@@ -49,7 +52,6 @@
import static android.view.Display.INVALID_DISPLAY;
import static android.view.Display.TYPE_VIRTUAL;
import static android.view.WindowManager.TRANSIT_DOCK_TASK_FROM_RECENTS;
-
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALL;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_FOCUS;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_IDLE;
@@ -96,7 +98,6 @@
import static com.android.server.am.TaskRecord.REPARENT_KEEP_STACK_AT_FRONT;
import static com.android.server.am.TaskRecord.REPARENT_LEAVE_STACK_IN_PLACE;
import static com.android.server.am.TaskRecord.REPARENT_MOVE_STACK_TO_FRONT;
-
import static java.lang.Integer.MAX_VALUE;
import android.Manifest;
@@ -131,6 +132,7 @@
import android.content.pm.ResolveInfo;
import android.content.pm.UserInfo;
import android.content.res.Configuration;
+import android.content.res.Resources;
import android.graphics.Rect;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManager.DisplayListener;
@@ -155,6 +157,7 @@
import android.service.voice.IVoiceInteractionSession;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.DisplayMetrics;
import android.util.EventLog;
import android.util.IntArray;
import android.util.MergedConfiguration;
@@ -334,10 +337,6 @@
/** The current user */
int mCurrentUser;
- /** The stack containing the launcher app. Assumed to always be attached to
- * Display.DEFAULT_DISPLAY. */
- ActivityStack mHomeStack;
-
/** If this is the same as mFocusedStack then the activity on the top of the focused stack has
* been resumed. If stacks are changing position this will hold the old stack until the new
* stack becomes resumed after which it will be set to mFocusedStack. */
@@ -444,13 +443,12 @@
// The default minimal size that will be used if the activity doesn't specify its minimal size.
// It will be calculated when the default display gets added.
- int mDefaultMinSizeOfResizeableTask = -1;
+ int mDefaultMinSizeOfResizeableTaskDp = -1;
// Whether tasks have moved and we need to rank the tasks before next OOM scoring
private boolean mTaskLayersChanged = true;
private ActivityMetricsLogger mActivityMetricsLogger;
- private LaunchTimeTracker mLaunchTimeTracker = new LaunchTimeTracker();
private final ArrayList<ActivityRecord> mTmpActivityList = new ArrayList<>();
@@ -646,10 +644,6 @@
return mActivityMetricsLogger;
}
- LaunchTimeTracker getLaunchTimeTracker() {
- return mLaunchTimeTracker;
- }
-
public KeyguardController getKeyguardController() {
return mKeyguardController;
}
@@ -693,11 +687,12 @@
mDefaultDisplay = activityDisplay;
}
addChild(activityDisplay, ActivityDisplay.POSITION_TOP);
- calculateDefaultMinimalSizeOfResizeableTasks(activityDisplay);
}
+ calculateDefaultMinimalSizeOfResizeableTasks();
final ActivityDisplay defaultDisplay = getDefaultDisplay();
- mHomeStack = mLastFocusedStack = defaultDisplay.getOrCreateStack(
+
+ mLastFocusedStack = defaultDisplay.getOrCreateStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
positionChildAt(defaultDisplay, ActivityDisplay.POSITION_TOP);
}
@@ -738,10 +733,6 @@
}
ActivityRecord getTopResumedActivity() {
- if (mWindowManager == null) {
- return null;
- }
-
final ActivityStack focusedStack = getTopDisplayFocusedStack();
if (focusedStack == null) {
return null;
@@ -786,7 +777,7 @@
if (focusCandidate == null) {
Slog.w(TAG,
"setFocusStackUnchecked: No focusable stack found, focus home as default");
- focusCandidate = mHomeStack;
+ focusCandidate = getDefaultDisplay().getHomeStack();
}
}
@@ -807,10 +798,6 @@
}
}
- void moveHomeStackToFront(String reason) {
- mHomeStack.moveToFront(reason);
- }
-
void moveRecentsStackToFront(String reason) {
final ActivityStack recentsStack = getDefaultDisplay().getStack(
WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_RECENTS);
@@ -819,34 +806,47 @@
}
}
- /** Returns true if the focus activity was adjusted to the home stack top activity. */
- boolean moveHomeStackTaskToTop(String reason) {
- mHomeStack.moveHomeStackTaskToTop();
-
- final ActivityRecord top = getHomeActivity();
- if (top == null) {
- return false;
- }
- moveFocusableActivityStackToFrontLocked(top, reason);
- return true;
- }
-
- boolean resumeHomeStackTask(ActivityRecord prev, String reason) {
+ boolean resumeHomeActivity(ActivityRecord prev, String reason, int displayId) {
if (!mService.isBooting() && !mService.isBooted()) {
// Not ready yet!
return false;
}
- mHomeStack.moveHomeStackTaskToTop();
- ActivityRecord r = getHomeActivity();
- final String myReason = reason + " resumeHomeStackTask";
+ if (displayId == INVALID_DISPLAY) {
+ displayId = DEFAULT_DISPLAY;
+ }
+
+ final ActivityRecord r = getActivityDisplay(displayId).getHomeActivity();
+ final String myReason = reason + " resumeHomeActivity";
// Only resume home activity if isn't finishing.
if (r != null && !r.finishing) {
- moveFocusableActivityStackToFrontLocked(r, myReason);
- return resumeFocusedStacksTopActivitiesLocked(mHomeStack, prev, null);
+ moveFocusableActivityToTop(r, myReason);
+ return resumeFocusedStacksTopActivitiesLocked(r.getStack(), prev, null);
}
- return mService.mAm.startHomeActivityLocked(mCurrentUser, myReason);
+ return mService.mAm.startHomeActivityLocked(mCurrentUser, myReason, displayId);
+ }
+
+ boolean canStartHomeOnDisplay(ActivityInfo homeActivity, int displayId) {
+ if (displayId == DEFAULT_DISPLAY) {
+ // No restrictions to default display.
+ return true;
+ }
+
+ final ActivityDisplay display = getActivityDisplay(displayId);
+ if (display == null || display.isRemoved() || !display.supportsSystemDecorations()) {
+ // Can't launch home on display that doesn't support system decorations.
+ return false;
+ }
+
+ final boolean supportMultipleInstance = homeActivity.launchMode != LAUNCH_SINGLE_TASK
+ && homeActivity.launchMode != LAUNCH_SINGLE_INSTANCE;
+ if (!supportMultipleInstance) {
+ // Can't launch home on other displays if it requested to be single instance.
+ return false;
+ }
+
+ return true;
}
TaskRecord anyTaskForIdLocked(int id) {
@@ -1179,8 +1179,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);
}
@@ -1211,8 +1211,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);
}
}
@@ -1251,8 +1250,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);
@@ -1262,7 +1260,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.
}
@@ -1728,8 +1725,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
@@ -2082,7 +2077,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
@@ -2215,7 +2210,8 @@
*/
void updateUserStackLocked(int userId, ActivityStack stack) {
if (userId != mCurrentUser) {
- mUserStackInFront.put(userId, stack != null ? stack.getStackId() : mHomeStack.mStackId);
+ mUserStackInFront.put(userId, stack != null ? stack.getStackId()
+ : getDefaultDisplay().getHomeStack().mStackId);
}
}
@@ -2284,7 +2280,8 @@
return false;
}
- if (targetStack != null && targetStack.isTopStackOnDisplay()) {
+ if (targetStack != null && (targetStack.isTopStackOnDisplay()
+ || getTopDisplayFocusedStack() == targetStack)) {
return targetStack.resumeTopActivityUncheckedLocked(target, targetOptions);
}
@@ -2357,7 +2354,7 @@
*/
void findTaskToMoveToFront(TaskRecord task, int flags, ActivityOptions options, String reason,
boolean forceNonResizeable) {
- final ActivityStack currentStack = task.getStack();
+ ActivityStack currentStack = task.getStack();
if (currentStack == null) {
Slog.e(TAG, "findTaskToMoveToFront: can't move task="
+ task + " to front. Stack is null");
@@ -2368,13 +2365,15 @@
mUserLeaving = true;
}
+ // TODO(b/111363427): The moving-to-top task may not be on the top display, so it could be
+ // different from where the prev activity stays on.
final ActivityRecord prev = topRunningActivityLocked();
if ((flags & ActivityManager.MOVE_TASK_WITH_HOME) != 0
|| (prev != null && prev.isActivityTypeRecents())) {
// Caller wants the home activity moved with it or the previous task is recents in which
// case we always return home from the task we are moving to the front.
- moveHomeStackToFront("findTaskToMoveToFront");
+ currentStack.getDisplay().moveHomeStackToFront("findTaskToMoveToFront");
}
if (task.isResizeable() && canUseActivityOptionsLaunchBounds(options)) {
@@ -2386,7 +2385,7 @@
if (stack != currentStack) {
task.reparent(stack, ON_TOP, REPARENT_KEEP_STACK_AT_FRONT, !ANIMATE, DEFER_RESUME,
"findTaskToMoveToFront");
- stack = currentStack;
+ currentStack = stack;
// moveTaskToStackUncheckedLocked() should already placed the task on top,
// still need moveTaskToFrontLocked() below for any transition settings.
}
@@ -2653,6 +2652,12 @@
if (preferredFocusableStack != null) {
return preferredFocusableStack;
}
+ if (preferredDisplay.supportsSystemDecorations()) {
+ // Stop looking for focusable stack on other displays because the preferred display
+ // supports system decorations. Home activity would be launched on the same display if
+ // no focusable stack found.
+ return null;
+ }
// Now look through all displays
for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
@@ -2696,25 +2701,12 @@
return null;
}
- ActivityRecord getHomeActivity() {
- return getHomeActivityForUser(mCurrentUser);
+ ActivityRecord getDefaultDisplayHomeActivity() {
+ return getDefaultDisplayHomeActivityForUser(mCurrentUser);
}
- ActivityRecord getHomeActivityForUser(int userId) {
- final ArrayList<TaskRecord> tasks = mHomeStack.getAllTasks();
- for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
- final TaskRecord task = tasks.get(taskNdx);
- if (task.isActivityTypeHome()) {
- final ArrayList<ActivityRecord> activities = task.mActivities;
- for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
- final ActivityRecord r = activities.get(activityNdx);
- if (r.isActivityTypeHome()
- && ((userId == UserHandle.USER_ALL) || (r.userId == userId))) {
- return r;
- }
- }
- }
- }
+ ActivityRecord getDefaultDisplayHomeActivityForUser(int userId) {
+ getActivityDisplay(DEFAULT_DISPLAY).getHomeActivityForUser(userId);
return null;
}
@@ -3428,7 +3420,8 @@
}
/** Move activity with its stack to front and make the stack focused. */
- boolean moveFocusableActivityStackToFrontLocked(ActivityRecord r, String reason) {
+ // TODO(b/111363427): Move this method to ActivityRecord.
+ boolean moveFocusableActivityToTop(ActivityRecord r, String reason) {
if (r == null || !r.isFocusable()) {
if (DEBUG_FOCUS) Slog.d(TAG_FOCUS,
"moveActivityStackToFront: unfocusable r=" + r);
@@ -3597,7 +3590,7 @@
stack.goToSleepIfPossible(false /* shuttingDown */);
} else {
stack.awakeFromSleepingLocked();
- if (isTopDisplayFocusedStack(stack) && !getKeyguardController()
+ if (stack.isFocusedStackOnDisplay() && !getKeyguardController()
.isKeyguardOrAodShowing(display.mDisplayId)) {
// If the keyguard is unlocked - resume immediately.
// It is possible that the display will not be awake at the time we
@@ -3837,7 +3830,8 @@
removeStacksInWindowingModes(WINDOWING_MODE_PINNED);
mUserStackInFront.put(mCurrentUser, focusStackId);
- final int restoreStackId = mUserStackInFront.get(userId, mHomeStack.mStackId);
+ final int restoreStackId =
+ mUserStackInFront.get(userId, getDefaultDisplay().getHomeStack().mStackId);
mCurrentUser = userId;
mStartingUsers.add(uss);
@@ -3855,14 +3849,14 @@
ActivityStack stack = getStack(restoreStackId);
if (stack == null) {
- stack = mHomeStack;
+ stack = getDefaultDisplay().getHomeStack();
}
final boolean homeInFront = stack.isActivityTypeHome();
if (stack.isOnHomeDisplay()) {
stack.moveToFront("switchUserOnHomeDisplay");
} else {
// Stack was moved to another display while user was swapped out.
- resumeHomeStackTask(null, "switchUserOnOtherDisplay");
+ resumeHomeActivity(null, "switchUserOnOtherDisplay", DEFAULT_DISPLAY);
}
return homeInFront;
}
@@ -3985,8 +3979,12 @@
}
public void dump(PrintWriter pw, String prefix) {
- pw.print(prefix); pw.print("mFocusedStack=" + getTopDisplayFocusedStack());
- pw.print(" mLastFocusedStack="); pw.println(mLastFocusedStack);
+ pw.println();
+ pw.println("ActivityStackSupervisor state:");
+ pw.print(prefix);
+ pw.println("topDisplayFocusedStack=" + getTopDisplayFocusedStack());
+ pw.print(prefix);
+ pw.println("mLastFocusedStack=" + mLastFocusedStack);
pw.print(prefix);
pw.println("mCurTaskIdForUser=" + mCurTaskIdForUser);
pw.print(prefix); pw.println("mUserStackInFront=" + mUserStackInFront);
@@ -4282,6 +4280,7 @@
private void handleDisplayAdded(int displayId) {
synchronized (mService.mGlobalLock) {
getActivityDisplayOrCreateLocked(displayId);
+ mService.mAm.startHomeActivityLocked(mCurrentUser, "displayAdded", displayId);
}
}
@@ -4328,7 +4327,6 @@
// The display hasn't been added to ActivityManager yet, create a new record now.
activityDisplay = new ActivityDisplay(this, display);
addChild(activityDisplay, ActivityDisplay.POSITION_BOTTOM);
- calculateDefaultMinimalSizeOfResizeableTasks(activityDisplay);
mWindowManager.onDisplayAdded(displayId);
return activityDisplay;
}
@@ -4346,10 +4344,13 @@
mActivityDisplays.remove(activityDisplay);
}
- private void calculateDefaultMinimalSizeOfResizeableTasks(ActivityDisplay display) {
- mDefaultMinSizeOfResizeableTask =
- mService.mContext.getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.default_minimal_size_resizable_task);
+ private void calculateDefaultMinimalSizeOfResizeableTasks() {
+ final Resources res = mService.mContext.getResources();
+ final float minimalSize = res.getDimension(
+ com.android.internal.R.dimen.default_minimal_size_resizable_task);
+ final DisplayMetrics dm = res.getDisplayMetrics();
+
+ mDefaultMinSizeOfResizeableTaskDp = (int) (minimalSize / dm.density);
}
private void handleDisplayRemoved(int displayId) {
@@ -4845,7 +4846,8 @@
// We always want to return to the home activity instead of the recents activity
// from whatever is started from the recents activity, so move the home stack
// forward.
- moveHomeStackToFront("startActivityFromRecents");
+ // TODO (b/115289124): Multi-display supports for recents.
+ getDefaultDisplay().moveHomeStackToFront("startActivityFromRecents");
}
// If the user must confirm credentials (e.g. when first launching a work app and the
@@ -4888,12 +4890,13 @@
final ActivityStack topSecondaryStack =
display.getTopStackInWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
if (topSecondaryStack.isActivityTypeHome()) {
- // If the home activity if the top split-screen secondary stack, then the
+ // If the home activity is the top split-screen secondary stack, then the
// primary split-screen stack is in the minimized mode which means it can't
// receive input keys, so we should move the focused app to the home app so that
// window manager can correctly calculate the focus window that can receive
// input keys.
- moveHomeStackToFront("startActivityFromRecents: homeVisibleInSplitScreen");
+ display.moveHomeStackToFront(
+ "startActivityFromRecents: homeVisibleInSplitScreen");
// Immediately update the minimized docked stack mode, the upcoming animation
// for the docked activity (WMS.overridePendingAppTransitionMultiThumbFuture)
@@ -4940,10 +4943,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) {
@@ -4954,6 +4960,10 @@
return mResult;
}
+ public long getStartTime() {
+ return mStartTimeMs;
+ }
+
public ComponentName getComponent() {
return mTargetComponent;
}
diff --git a/services/core/java/com/android/server/am/ActivityStartController.java b/services/core/java/com/android/server/am/ActivityStartController.java
index 6e3a79c..5e73bc3 100644
--- a/services/core/java/com/android/server/am/ActivityStartController.java
+++ b/services/core/java/com/android/server/am/ActivityStartController.java
@@ -17,12 +17,14 @@
package com.android.server.am;
import static android.app.ActivityManager.START_SUCCESS;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY;
+import android.app.ActivityOptions;
import android.app.IApplicationThread;
import android.content.ComponentName;
import android.content.ContentResolver;
@@ -75,7 +77,7 @@
/** Temporary array to capture start activity results */
private ActivityRecord[] tmpOutRecord = new ActivityRecord[1];
- /**The result of the last home activity we attempted to start. */
+ /** The result of the last home activity we attempted to start. */
private int mLastHomeActivityStartResult;
/** A list of activities that are waiting to launch. */
@@ -161,13 +163,20 @@
mLastStarter.postStartActivityProcessing(r, result, targetStack);
}
- void startHomeActivity(Intent intent, ActivityInfo aInfo, String reason) {
- mSupervisor.moveHomeStackTaskToTop(reason);
+ void startHomeActivity(Intent intent, ActivityInfo aInfo, String reason, int displayId) {
+ if (!mSupervisor.canStartHomeOnDisplay(aInfo, displayId)) {
+ return;
+ }
+ final ActivityOptions options = ActivityOptions.makeBasic();
+ options.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ options.setLaunchActivityType(ACTIVITY_TYPE_HOME);
+ options.setLaunchDisplayId(displayId);
mLastHomeActivityStartResult = obtainStarter(intent, "startHomeActivity: " + reason)
.setOutActivity(tmpOutRecord)
.setCallingUid(0)
.setActivityInfo(aInfo)
+ .setActivityOptions(options.toBundle())
.execute();
mLastHomeActivityStartRecord = tmpOutRecord[0];
if (mSupervisor.inResumeTopActivity) {
diff --git a/services/core/java/com/android/server/am/ActivityStartInterceptor.java b/services/core/java/com/android/server/am/ActivityStartInterceptor.java
index 1fb8f87..4789ff3 100644
--- a/services/core/java/com/android/server/am/ActivityStartInterceptor.java
+++ b/services/core/java/com/android/server/am/ActivityStartInterceptor.java
@@ -277,7 +277,7 @@
mActivityOptions = ActivityOptions.makeBasic();
}
- ActivityRecord homeActivityRecord = mSupervisor.getHomeActivity();
+ ActivityRecord homeActivityRecord = mSupervisor.getDefaultDisplayHomeActivity();
if (homeActivityRecord != null && homeActivityRecord.getTask() != null) {
// Showing credential confirmation activity in home task to avoid stopping multi-windowed
// mode after showing the full-screen credential confirmation activity.
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index 890aafe..de3b9cf 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -975,7 +975,8 @@
clearedTask);
break;
case WINDOWING_MODE_SPLIT_SCREEN_PRIMARY:
- final ActivityStack homeStack = mSupervisor.mHomeStack;
+ final ActivityStack homeStack =
+ startedActivityStack.getDisplay().getHomeStack();
if (homeStack != null && homeStack.shouldBeVisible(null /* starting */)) {
mService.mWindowManager.showRecentApps();
}
@@ -1154,6 +1155,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;
@@ -1178,7 +1182,6 @@
outResult.timeout = false;
outResult.who = r.realActivity;
outResult.totalTime = 0;
- outResult.thisTime = 0;
break;
}
case START_TASK_TO_FRONT: {
@@ -1188,10 +1191,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 {
@@ -1205,7 +1207,6 @@
}
}
- mSupervisor.getActivityMetricsLogger().notifyActivityLaunched(res, outRecord[0]);
return res;
}
}
@@ -1280,6 +1281,15 @@
setInitialState(r, options, inTask, doResume, startFlags, sourceRecord, voiceSession,
voiceInteractor);
+ // Do not start home activity if it cannot be launched on preferred display. We are not
+ // doing this in ActivityStackSupervisor#canPlaceEntityOnDisplay because it might
+ // fallback to launch on other displays.
+ if (r.isActivityTypeHome()
+ && !mSupervisor.canStartHomeOnDisplay(r.info, mPreferredDisplayId)) {
+ Slog.w(TAG, "Cannot launch home on display " + mPreferredDisplayId);
+ return START_CANCELED;
+ }
+
computeLaunchingTaskFlags();
computeSourceStack();
@@ -1430,7 +1440,11 @@
&& top.userId == mStartActivity.userId
&& top.attachedToProcess()
&& ((mLaunchFlags & FLAG_ACTIVITY_SINGLE_TOP) != 0
- || isLaunchModeOneOf(LAUNCH_SINGLE_TOP, LAUNCH_SINGLE_TASK));
+ || isLaunchModeOneOf(LAUNCH_SINGLE_TOP, LAUNCH_SINGLE_TASK))
+ // This allows home activity to automatically launch on secondary display when
+ // display added, if home was the top activity on default display, instead of
+ // sending new intent to the home activity on default display.
+ && (!top.isActivityTypeHome() || top.getDisplayId() == mPreferredDisplayId);
if (dontStart) {
// For paranoia, make sure we have correctly resumed the top activity.
topStack.mLastPausedActivity = null;
@@ -1858,6 +1872,13 @@
intentActivity = mSupervisor.findTaskLocked(mStartActivity, mPreferredDisplayId);
}
}
+
+ if (mStartActivity.isActivityTypeHome() && intentActivity != null
+ && intentActivity.getDisplayId() != mPreferredDisplayId) {
+ // Do not reuse home activity on other displays.
+ intentActivity = null;
+ }
+
return intentActivity;
}
diff --git a/services/core/java/com/android/server/am/ActivityTaskManagerService.java b/services/core/java/com/android/server/am/ActivityTaskManagerService.java
index add9f2a..c1eab82 100644
--- a/services/core/java/com/android/server/am/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityTaskManagerService.java
@@ -1692,8 +1692,7 @@
return;
}
final ActivityRecord r = stack.topRunningActivityLocked();
- if (mStackSupervisor.moveFocusableActivityStackToFrontLocked(
- r, "setFocusedStack")) {
+ if (mStackSupervisor.moveFocusableActivityToTop(r, "setFocusedStack")) {
mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
}
}
@@ -1714,7 +1713,7 @@
return;
}
final ActivityRecord r = task.topRunningActivityLocked();
- if (mStackSupervisor.moveFocusableActivityStackToFrontLocked(r, "setFocusedTask")) {
+ if (mStackSupervisor.moveFocusableActivityToTop(r, "setFocusedTask")) {
mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
}
}
@@ -1960,10 +1959,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
@@ -1983,7 +1984,6 @@
} finally {
Binder.restoreCallingIdentity(origId);
}
- SafeActivityOptions.abort(options);
}
boolean checkAppSwitchAllowedLocked(int sourcePid, int sourceUid,
@@ -4845,8 +4845,7 @@
updateResumedAppTrace(r);
mLastResumedActivity = r;
- // TODO(b/111361570): Support multiple focused apps in WM
- mWindowManager.setFocusedApp(r.appToken, true);
+ r.getDisplay().setFocusedApp(r, true);
applyUpdateLockStateLocked(r);
applyUpdateVrModeLocked(r);
@@ -5262,7 +5261,8 @@
@Override
public ComponentName getHomeActivityForUser(int userId) {
synchronized (mGlobalLock) {
- ActivityRecord homeActivity = mStackSupervisor.getHomeActivityForUser(userId);
+ ActivityRecord homeActivity =
+ mStackSupervisor.getDefaultDisplayHomeActivityForUser(userId);
return homeActivity == null ? null : homeActivity.realActivity;
}
}
@@ -5436,8 +5436,7 @@
throw new IllegalArgumentException(
"setFocusedActivity: No activity record matching token=" + token);
}
- if (mStackSupervisor.moveFocusableActivityStackToFrontLocked(
- r, "setFocusedActivity")) {
+ if (mStackSupervisor.moveFocusableActivityToTop(r, "setFocusedActivity")) {
mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
}
}
@@ -5780,5 +5779,21 @@
resultWho, requestCode, intents, resolvedTypes, flags, bOptions);
}
}
+
+ @Override
+ public ActivityServiceConnectionsHolder getServiceConnectionsHolder(IBinder token) {
+ synchronized (mGlobalLock) {
+ final ActivityRecord r = ActivityRecord.isInStackLocked(token);
+ if (r == null) {
+ return null;
+ }
+ if (r.mServiceConnectionsHolder == null) {
+ r.mServiceConnectionsHolder = new ActivityServiceConnectionsHolder(
+ ActivityTaskManagerService.this, r);
+ }
+
+ return r.mServiceConnectionsHolder;
+ }
+ }
}
}
diff --git a/services/core/java/com/android/server/am/ConnectionRecord.java b/services/core/java/com/android/server/am/ConnectionRecord.java
index fa8e6c4..1242ed6 100644
--- a/services/core/java/com/android/server/am/ConnectionRecord.java
+++ b/services/core/java/com/android/server/am/ConnectionRecord.java
@@ -35,7 +35,7 @@
*/
final class ConnectionRecord {
final AppBindRecord binding; // The application/service binding.
- final ActivityRecord activity; // If non-null, the owning activity.
+ final ActivityServiceConnectionsHolder<ConnectionRecord> activity; // If non-null, the owning activity.
final IServiceConnection conn; // The client connection.
final int flags; // Binding options.
final int clientLabel; // String resource labeling this client.
@@ -85,13 +85,14 @@
void dump(PrintWriter pw, String prefix) {
pw.println(prefix + "binding=" + binding);
if (activity != null) {
- pw.println(prefix + "activity=" + activity);
+ activity.dump(pw, prefix);
}
pw.println(prefix + "conn=" + conn.asBinder()
+ " flags=0x" + Integer.toHexString(flags));
}
- ConnectionRecord(AppBindRecord _binding, ActivityRecord _activity,
+ ConnectionRecord(AppBindRecord _binding,
+ ActivityServiceConnectionsHolder<ConnectionRecord> _activity,
IServiceConnection _conn, int _flags,
int _clientLabel, PendingIntent _clientIntent,
int _clientUid, String _clientProcessName) {
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/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 228c71d..a8e1ccc 100644
--- a/services/core/java/com/android/server/am/MemoryStatUtil.java
+++ b/services/core/java/com/android/server/am/MemoryStatUtil.java
@@ -38,6 +38,7 @@
*/
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;
@@ -68,7 +69,7 @@
private static final int PGFAULT_INDEX = 9;
private static final int PGMAJFAULT_INDEX = 11;
- private static final int RSS_IN_BYTES_INDEX = 23;
+ private static final int RSS_IN_PAGES_INDEX = 23;
private MemoryStatUtil() {}
@@ -146,15 +147,15 @@
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;
}
@@ -163,7 +164,12 @@
if (memoryMaxUsageContents == null || memoryMaxUsageContents.isEmpty()) {
return 0;
}
- return Long.valueOf(memoryMaxUsageContents);
+ try {
+ return Long.parseLong(memoryMaxUsageContents);
+ } catch (NumberFormatException e) {
+ Slog.e(TAG, "Failed to parse value", e);
+ return 0;
+ }
}
/**
@@ -181,11 +187,16 @@
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;
+ }
}
/**
@@ -199,7 +210,7 @@
}
Matcher m = RSS_HIGH_WATERMARK_IN_BYTES.matcher(procStatusContents);
// Convert value read from /proc/pid/status from kilobytes to bytes.
- return m.find() ? Long.valueOf(m.group(1)) * BYTES_IN_KILOBYTE : 0;
+ return m.find() ? Long.parseLong(m.group(1)) * BYTES_IN_KILOBYTE : 0;
}
/**
diff --git a/services/core/java/com/android/server/am/PersistentConnection.java b/services/core/java/com/android/server/am/PersistentConnection.java
index c5edb26..3490b1d 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;
@@ -59,6 +59,8 @@
* know what to do when the service component has gone missing, for example. If the user of this
* class wants to restore the connection, then it should call {@link #unbind()} and {@link #bind}
* explicitly.
+ *
+ * atest ${ANDROID_BUILD_TOP}/frameworks/base/services/tests/mockingservicestests/src/com/android/server/am/PersistentConnectionTest.java
*/
public abstract class PersistentConnection<T> {
private final Object mLock = new Object();
@@ -76,6 +78,7 @@
private final long mRebindBackoffMs;
private final double mRebindBackoffIncrease;
private final long mRebindMaxBackoffMs;
+ private final long mResetBackoffDelay;
private long mReconnectTime;
@@ -100,6 +103,18 @@
@GuardedBy("mLock")
private T mService;
+ @GuardedBy("mLock")
+ private int mNumConnected;
+
+ @GuardedBy("mLock")
+ private int mNumDisconnected;
+
+ @GuardedBy("mLock")
+ private int mNumBindingDied;
+
+ @GuardedBy("mLock")
+ private long mLastConnectedTime;
+
private final ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
@@ -108,25 +123,35 @@
// 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;
+ mLastConnectedTime = injectUptimeMillis();
mService = asInterface(service);
+
+ scheduleStableCheckLocked();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
synchronized (mLock) {
- Slog.i(mTag, "Disconnected: " + mComponentName.flattenToShortString()
+ Log.i(mTag, "Disconnected: " + mComponentName.flattenToShortString()
+ " u" + mUserId);
+ mNumDisconnected++;
+
cleanUpConnectionLocked();
+
+ // Note we won't increase the rebind timeout here, because we don't explicitly
+ // rebind in this case.
}
}
@@ -136,13 +161,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();
}
}
@@ -152,7 +180,8 @@
public PersistentConnection(@NonNull String tag, @NonNull Context context,
@NonNull Handler handler, int userId, @NonNull ComponentName componentName,
- long rebindBackoffSeconds, double rebindBackoffIncrease, long rebindMaxBackoffSeconds) {
+ long rebindBackoffSeconds, double rebindBackoffIncrease, long rebindMaxBackoffSeconds,
+ long resetBackoffDelay) {
mTag = tag;
mContext = context;
mHandler = handler;
@@ -162,6 +191,7 @@
mRebindBackoffMs = rebindBackoffSeconds * 1000;
mRebindBackoffIncrease = rebindBackoffIncrease;
mRebindMaxBackoffMs = rebindMaxBackoffSeconds * 1000;
+ mResetBackoffDelay = resetBackoffDelay * 1000;
mNextBackoffMs = mRebindBackoffMs;
}
@@ -170,6 +200,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.
*
@@ -220,6 +256,42 @@
}
}
+ /** Return the next back-off time */
+ public long getNextBackoffMs() {
+ synchronized (mLock) {
+ return mNextBackoffMs;
+ }
+ }
+
+ /** Return the number of times the connected callback called. */
+ public int getNumConnected() {
+ synchronized (mLock) {
+ return mNumConnected;
+ }
+ }
+
+ /** Return the number of times the disconnected callback called. */
+ public int getNumDisconnected() {
+ synchronized (mLock) {
+ return mNumDisconnected;
+ }
+ }
+
+ /** Return the number of times the binding died callback called. */
+ public int getNumBindingDied() {
+ synchronized (mLock) {
+ return mNumBindingDied;
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void resetBackoffLocked() {
+ if (mNextBackoffMs != mRebindBackoffMs) {
+ mNextBackoffMs = mRebindBackoffMs;
+ Log.i(mTag, "Backoff reset to " + mNextBackoffMs);
+ }
+ }
+
@GuardedBy("mLock")
public final void bindInnerLocked(boolean resetBackoff) {
unscheduleRebindLocked();
@@ -229,23 +301,24 @@
}
mBound = true;
+ unscheduleStableCheckLocked();
+
if (resetBackoff) {
- // Note this is the only place we reset the backoff time.
- mNextBackoffMs = mRebindBackoffMs;
+ resetBackoffLocked();
}
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.");
}
}
@@ -265,6 +338,8 @@
private void cleanUpConnectionLocked() {
mIsConnected = false;
mService = null;
+
+ unscheduleStableCheckLocked();
}
/**
@@ -275,6 +350,7 @@
mShouldBeBound = false;
unbindLocked();
+ unscheduleStableCheckLocked();
}
}
@@ -285,7 +361,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 +379,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;
@@ -316,6 +392,33 @@
}
}
+ private final Runnable mStableCheck = this::stableConnectionCheck;
+
+ private void stableConnectionCheck() {
+ synchronized (mLock) {
+ final long now = injectUptimeMillis();
+ final long timeRemaining = (mLastConnectedTime + mResetBackoffDelay) - now;
+ if (DEBUG) {
+ Log.d(mTag, "stableConnectionCheck: bound=" + mBound + " connected=" + mIsConnected
+ + " remaining=" + timeRemaining);
+ }
+ if (mBound && mIsConnected && timeRemaining <= 0) {
+ resetBackoffLocked();
+ }
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void unscheduleStableCheckLocked() {
+ injectRemoveCallbacks(mStableCheck);
+ }
+
+ @GuardedBy("mLock")
+ private void scheduleStableCheckLocked() {
+ unscheduleStableCheckLocked();
+ injectPostAtTime(mStableCheck, injectUptimeMillis() + mResetBackoffDelay);
+ }
+
/** Must be implemented by a subclass to convert an {@link IBinder} to a stub. */
protected abstract T asInterface(IBinder binder);
@@ -323,10 +426,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 +439,20 @@
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);
+ if (mIsConnected) {
+ pw.print(" Duration: ");
+ TimeUtils.formatDuration((injectUptimeMillis() - mLastConnectedTime), pw);
+ }
+ pw.println();
}
}
@@ -373,6 +492,11 @@
}
@VisibleForTesting
+ Runnable getStableCheckRunnableForTest() {
+ return mStableCheck;
+ }
+
+ @VisibleForTesting
boolean shouldBeBoundForTest() {
return mShouldBeBound;
}
diff --git a/services/core/java/com/android/server/am/PersisterQueue.java b/services/core/java/com/android/server/am/PersisterQueue.java
new file mode 100644
index 0000000..60ea0fa
--- /dev/null
+++ b/services/core/java/com/android/server/am/PersisterQueue.java
@@ -0,0 +1,277 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.os.Process;
+import android.os.SystemClock;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.ArrayList;
+import java.util.function.Predicate;
+
+/**
+ * The common threading logic for persisters to use so that they can run in the same threads.
+ * Methods in this class are synchronized on its instance, so caller could also synchronize on
+ * its instance to perform modifications in items.
+ */
+class PersisterQueue {
+ private static final String TAG = "PersisterQueue";
+ private static final boolean DEBUG = false;
+
+ /** When not flushing don't write out files faster than this */
+ private static final long INTER_WRITE_DELAY_MS = 500;
+
+ /**
+ * When not flushing delay this long before writing the first file out. This gives the next task
+ * being launched a chance to load its resources without this occupying IO bandwidth.
+ */
+ private static final long PRE_TASK_DELAY_MS = 3000;
+
+ /** The maximum number of entries to keep in the queue before draining it automatically. */
+ private static final int MAX_WRITE_QUEUE_LENGTH = 6;
+
+ /** Special value for mWriteTime to mean don't wait, just write */
+ private static final long FLUSH_QUEUE = -1;
+
+ /** An {@link WriteQueueItem} that doesn't do anything. Used to trigger {@link
+ * Listener#onPreProcessItem}. */
+ static final WriteQueueItem EMPTY_ITEM = () -> { };
+
+ private final long mInterWriteDelayMs;
+ private final long mPreTaskDelayMs;
+ private final LazyTaskWriterThread mLazyTaskWriterThread;
+ private final ArrayList<WriteQueueItem> mWriteQueue = new ArrayList<>();
+
+ private final ArrayList<Listener> mListeners = new ArrayList<>();
+
+ /**
+ * Value determines write delay mode as follows: < 0 We are Flushing. No delays between writes
+ * until the image queue is drained and all tasks needing persisting are written to disk. There
+ * is no delay between writes. == 0 We are Idle. Next writes will be delayed by
+ * #PRE_TASK_DELAY_MS. > 0 We are Actively writing. Next write will be at this time. Subsequent
+ * writes will be delayed by #INTER_WRITE_DELAY_MS.
+ */
+ private long mNextWriteTime = 0;
+
+ PersisterQueue() {
+ this(INTER_WRITE_DELAY_MS, PRE_TASK_DELAY_MS);
+ }
+
+ /** Used for tests to reduce waiting time. */
+ @VisibleForTesting
+ PersisterQueue(long interWriteDelayMs, long preTaskDelayMs) {
+ if (interWriteDelayMs < 0 || preTaskDelayMs < 0) {
+ throw new IllegalArgumentException("Both inter-write delay and pre-task delay need to"
+ + "be non-negative. inter-write delay: " + interWriteDelayMs
+ + "ms pre-task delay: " + preTaskDelayMs);
+ }
+ mInterWriteDelayMs = interWriteDelayMs;
+ mPreTaskDelayMs = preTaskDelayMs;
+ mLazyTaskWriterThread = new LazyTaskWriterThread("LazyTaskWriterThread");
+ }
+
+ synchronized void startPersisting() {
+ if (!mLazyTaskWriterThread.isAlive()) {
+ mLazyTaskWriterThread.start();
+ }
+ }
+
+ /** Stops persisting thread. Should only be used in tests. */
+ @VisibleForTesting
+ void stopPersisting() throws InterruptedException {
+ if (!mLazyTaskWriterThread.isAlive()) {
+ return;
+ }
+
+ synchronized (this) {
+ mLazyTaskWriterThread.interrupt();
+ }
+ mLazyTaskWriterThread.join();
+ }
+
+ synchronized void addItem(WriteQueueItem item, boolean flush) {
+ mWriteQueue.add(item);
+
+ if (flush || mWriteQueue.size() > MAX_WRITE_QUEUE_LENGTH) {
+ mNextWriteTime = FLUSH_QUEUE;
+ } else if (mNextWriteTime == 0) {
+ mNextWriteTime = SystemClock.uptimeMillis() + mPreTaskDelayMs;
+ }
+ notify();
+ }
+
+ synchronized <T extends WriteQueueItem> T findLastItem(Predicate<T> predicate, Class<T> clazz) {
+ for (int i = mWriteQueue.size() - 1; i >= 0; --i) {
+ WriteQueueItem writeQueueItem = mWriteQueue.get(i);
+ if (clazz.isInstance(writeQueueItem)) {
+ T item = clazz.cast(writeQueueItem);
+ if (predicate.test(item)) {
+ return item;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ synchronized <T extends WriteQueueItem> void removeItems(Predicate<T> predicate,
+ Class<T> clazz) {
+ for (int i = mWriteQueue.size() - 1; i >= 0; --i) {
+ WriteQueueItem writeQueueItem = mWriteQueue.get(i);
+ if (clazz.isInstance(writeQueueItem)) {
+ T item = clazz.cast(writeQueueItem);
+ if (predicate.test(item)) {
+ if (DEBUG) Slog.d(TAG, "Removing " + item + " from write queue.");
+ mWriteQueue.remove(i);
+ }
+ }
+ }
+ }
+
+ synchronized void flush() {
+ mNextWriteTime = FLUSH_QUEUE;
+ notifyAll();
+ do {
+ try {
+ wait();
+ } catch (InterruptedException e) {
+ }
+ } while (mNextWriteTime == FLUSH_QUEUE);
+ }
+
+ void yieldIfQueueTooDeep() {
+ boolean stall = false;
+ synchronized (this) {
+ if (mNextWriteTime == FLUSH_QUEUE) {
+ stall = true;
+ }
+ }
+ if (stall) {
+ Thread.yield();
+ }
+ }
+
+ void addListener(Listener listener) {
+ mListeners.add(listener);
+ }
+
+ private void processNextItem() throws InterruptedException {
+ // This part is extracted into a method so that the GC can clearly see the end of the
+ // scope of the variable 'item'. If this part was in the loop in LazyTaskWriterThread, the
+ // last item it processed would always "leak".
+ // See https://b.corp.google.com/issues/64438652#comment7
+
+ // If mNextWriteTime, then don't delay between each call to saveToXml().
+ final WriteQueueItem item;
+ synchronized (this) {
+ if (mNextWriteTime != FLUSH_QUEUE) {
+ // The next write we don't have to wait so long.
+ mNextWriteTime = SystemClock.uptimeMillis() + mInterWriteDelayMs;
+ if (DEBUG) {
+ Slog.d(TAG, "Next write time may be in " + mInterWriteDelayMs
+ + " msec. (" + mNextWriteTime + ")");
+ }
+ }
+
+ while (mWriteQueue.isEmpty()) {
+ if (mNextWriteTime != 0) {
+ mNextWriteTime = 0; // idle.
+ notify(); // May need to wake up flush().
+ }
+ // Make sure we exit this thread correctly when interrupted before going to
+ // indefinite wait.
+ if (Thread.currentThread().isInterrupted()) {
+ throw new InterruptedException();
+ }
+ if (DEBUG) Slog.d(TAG, "LazyTaskWriter: waiting indefinitely.");
+ wait();
+ // Invariant: mNextWriteTime is either FLUSH_QUEUE or PRE_WRITE_DELAY_MS
+ // from now.
+ }
+ item = mWriteQueue.remove(0);
+
+ long now = SystemClock.uptimeMillis();
+ if (DEBUG) {
+ Slog.d(TAG, "LazyTaskWriter: now=" + now + " mNextWriteTime=" + mNextWriteTime
+ + " mWriteQueue.size=" + mWriteQueue.size());
+ }
+ while (now < mNextWriteTime) {
+ if (DEBUG) {
+ Slog.d(TAG, "LazyTaskWriter: waiting " + (mNextWriteTime - now));
+ }
+ wait(mNextWriteTime - now);
+ now = SystemClock.uptimeMillis();
+ }
+
+ // Got something to do.
+ }
+
+ item.process();
+ }
+
+ interface WriteQueueItem {
+ void process();
+ }
+
+ interface Listener {
+ /**
+ * Called before {@link PersisterQueue} tries to process next item.
+ *
+ * Note if the queue is empty, this callback will be called before the indefinite wait. This
+ * will be called once when {@link PersisterQueue} starts the internal thread before the
+ * indefinite wait.
+ *
+ * This callback is called w/o locking the instance of {@link PersisterQueue}.
+ *
+ * @param queueEmpty {@code true} if the queue is empty, which indicates {@link
+ * PersisterQueue} is likely to enter indefinite wait; or {@code false} if there is still
+ * item to process.
+ */
+ void onPreProcessItem(boolean queueEmpty);
+ }
+
+ private class LazyTaskWriterThread extends Thread {
+
+ private LazyTaskWriterThread(String name) {
+ super(name);
+ }
+
+ @Override
+ public void run() {
+ Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
+ try {
+ while (true) {
+ final boolean probablyDone;
+ synchronized (PersisterQueue.this) {
+ probablyDone = mWriteQueue.isEmpty();
+ }
+
+ for (int i = mListeners.size() - 1; i >= 0; --i) {
+ mListeners.get(i).onPreProcessItem(probablyDone);
+ }
+
+ processNextItem();
+ }
+ } catch (InterruptedException e) {
+ Slog.e(TAG, "Persister thread is exiting. Should never happen in prod, but"
+ + "it's OK in tests.");
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/am/RecentTasks.java b/services/core/java/com/android/server/am/RecentTasks.java
index e11e003..4d0b1da 100644
--- a/services/core/java/com/android/server/am/RecentTasks.java
+++ b/services/core/java/com/android/server/am/RecentTasks.java
@@ -30,8 +30,9 @@
import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
-
import static android.os.Process.SYSTEM_UID;
+import static android.view.Display.DEFAULT_DISPLAY;
+
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_RECENTS;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_RECENTS_TRIM_TASKS;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_TASKS;
@@ -55,7 +56,6 @@
import android.content.pm.UserInfo;
import android.content.res.Resources;
import android.graphics.Bitmap;
-import android.graphics.Rect;
import android.os.Bundle;
import android.os.Environment;
import android.os.IBinder;
@@ -431,7 +431,7 @@
void onSystemReadyLocked() {
loadRecentsComponent(mService.mContext.getResources());
mTasks.clear();
- mTaskPersister.startPersisting();
+ mTaskPersister.onSystemReady();
}
Bitmap getTaskDescriptionIcon(String path) {
@@ -1244,7 +1244,6 @@
*/
protected boolean isTrimmable(TaskRecord task) {
final ActivityStack stack = task.getStack();
- final ActivityStack homeStack = mSupervisor.mHomeStack;
// No stack for task, just trim it
if (stack == null) {
@@ -1252,13 +1251,14 @@
}
// Ignore tasks from different displays
- if (stack.getDisplay() != homeStack.getDisplay()) {
+ // TODO (b/115289124): No Recents on non-default displays.
+ if (stack.mDisplayId != DEFAULT_DISPLAY) {
return false;
}
// Trim tasks that are in stacks that are behind the home stack
final ActivityDisplay display = stack.getDisplay();
- return display.getIndexOf(stack) < display.getIndexOf(homeStack);
+ return display.getIndexOf(stack) < display.getIndexOf(display.getHomeStack());
}
/**
diff --git a/services/core/java/com/android/server/am/TaskPersister.java b/services/core/java/com/android/server/am/TaskPersister.java
index 481bb2b..fc5dfb4 100644
--- a/services/core/java/com/android/server/am/TaskPersister.java
+++ b/services/core/java/com/android/server/am/TaskPersister.java
@@ -16,13 +16,14 @@
package com.android.server.am;
+import static com.android.server.am.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS;
+
import android.annotation.NonNull;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Debug;
import android.os.Environment;
import android.os.FileUtils;
-import android.os.Process;
import android.os.SystemClock;
import android.util.ArraySet;
import android.util.AtomicFile;
@@ -34,6 +35,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.XmlUtils;
+
import libcore.io.IoUtils;
import org.xmlpull.v1.XmlPullParser;
@@ -54,32 +56,18 @@
import java.util.Comparator;
import java.util.List;
-import static com.android.server.am.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS;
-
-public class TaskPersister {
+/**
+ * Persister that saves recent tasks into disk.
+ */
+public class TaskPersister implements PersisterQueue.Listener {
static final String TAG = "TaskPersister";
static final boolean DEBUG = false;
-
- /** When not flushing don't write out files faster than this */
- private static final long INTER_WRITE_DELAY_MS = 500;
-
- /**
- * When not flushing delay this long before writing the first file out. This gives the next task
- * being launched a chance to load its resources without this occupying IO bandwidth.
- */
- private static final long PRE_TASK_DELAY_MS = 3000;
-
- /** The maximum number of entries to keep in the queue before draining it automatically. */
- private static final int MAX_WRITE_QUEUE_LENGTH = 6;
-
- /** Special value for mWriteTime to mean don't wait, just write */
- private static final long FLUSH_QUEUE = -1;
+ static final String IMAGE_EXTENSION = ".png";
private static final String TASKS_DIRNAME = "recent_tasks";
private static final String TASK_FILENAME_SUFFIX = "_task.xml";
private static final String IMAGES_DIRNAME = "recent_images";
private static final String PERSISTED_TASK_IDS_FILENAME = "persisted_taskIds.txt";
- static final String IMAGE_EXTENSION = ".png";
private static final String TAG_TASK = "task";
@@ -90,39 +78,9 @@
private final File mTaskIdsDir;
// To lock file operations in TaskPersister
private final Object mIoLock = new Object();
+ private final PersisterQueue mPersisterQueue;
- /**
- * Value determines write delay mode as follows: < 0 We are Flushing. No delays between writes
- * until the image queue is drained and all tasks needing persisting are written to disk. There
- * is no delay between writes. == 0 We are Idle. Next writes will be delayed by
- * #PRE_TASK_DELAY_MS. > 0 We are Actively writing. Next write will be at this time. Subsequent
- * writes will be delayed by #INTER_WRITE_DELAY_MS.
- */
- private long mNextWriteTime = 0;
-
- private final LazyTaskWriterThread mLazyTaskWriterThread;
-
- private static class WriteQueueItem {}
-
- private static class TaskWriteQueueItem extends WriteQueueItem {
- final TaskRecord mTask;
-
- TaskWriteQueueItem(TaskRecord task) {
- mTask = task;
- }
- }
-
- private static class ImageWriteQueueItem extends WriteQueueItem {
- final String mFilePath;
- Bitmap mImage;
-
- ImageWriteQueueItem(String filePath, Bitmap image) {
- mFilePath = filePath;
- mImage = image;
- }
- }
-
- ArrayList<WriteQueueItem> mWriteQueue = new ArrayList<WriteQueueItem>();
+ private final ArraySet<Integer> mTmpTaskIds = new ArraySet<>();
TaskPersister(File systemDir, ActivityStackSupervisor stackSupervisor,
ActivityTaskManagerService service, RecentTasks recentTasks) {
@@ -145,7 +103,8 @@
mStackSupervisor = stackSupervisor;
mService = service;
mRecentTasks = recentTasks;
- mLazyTaskWriterThread = new LazyTaskWriterThread("LazyTaskWriterThread");
+ mPersisterQueue = new PersisterQueue();
+ mPersisterQueue.addListener(this);
}
@VisibleForTesting
@@ -154,42 +113,21 @@
mStackSupervisor = null;
mService = null;
mRecentTasks = null;
- mLazyTaskWriterThread = new LazyTaskWriterThread("LazyTaskWriterThreadTest");
+ mPersisterQueue = new PersisterQueue();
+ mPersisterQueue.addListener(this);
}
- void startPersisting() {
- if (!mLazyTaskWriterThread.isAlive()) {
- mLazyTaskWriterThread.start();
- }
+ void onSystemReady() {
+ mPersisterQueue.startPersisting();
}
private void removeThumbnails(TaskRecord task) {
- final String taskString = Integer.toString(task.taskId);
- for (int queueNdx = mWriteQueue.size() - 1; queueNdx >= 0; --queueNdx) {
- final WriteQueueItem item = mWriteQueue.get(queueNdx);
- if (item instanceof ImageWriteQueueItem) {
- final File thumbnailFile = new File(((ImageWriteQueueItem) item).mFilePath);
- if (thumbnailFile.getName().startsWith(taskString)) {
- if (DEBUG) {
- Slog.d(TAG, "Removing " + ((ImageWriteQueueItem) item).mFilePath +
- " from write queue");
- }
- mWriteQueue.remove(queueNdx);
- }
- }
- }
- }
-
- private void yieldIfQueueTooDeep() {
- boolean stall = false;
- synchronized (this) {
- if (mNextWriteTime == FLUSH_QUEUE) {
- stall = true;
- }
- }
- if (stall) {
- Thread.yield();
- }
+ mPersisterQueue.removeItems(
+ item -> {
+ File file = new File(item.mFilePath);
+ return file.getName().startsWith(Integer.toString(task.taskId));
+ },
+ ImageWriteQueueItem.class);
}
@NonNull
@@ -251,84 +189,51 @@
}
void wakeup(TaskRecord task, boolean flush) {
- synchronized (this) {
+ synchronized (mPersisterQueue) {
if (task != null) {
- int queueNdx;
- for (queueNdx = mWriteQueue.size() - 1; queueNdx >= 0; --queueNdx) {
- final WriteQueueItem item = mWriteQueue.get(queueNdx);
- if (item instanceof TaskWriteQueueItem &&
- ((TaskWriteQueueItem) item).mTask == task) {
- if (!task.inRecents) {
- // This task is being removed.
- removeThumbnails(task);
- }
- break;
- }
+ final TaskWriteQueueItem item = mPersisterQueue.findLastItem(
+ queueItem -> task == queueItem.mTask, TaskWriteQueueItem.class);
+ if (item != null && !task.inRecents) {
+ removeThumbnails(task);
}
- if (queueNdx < 0 && task.isPersistable) {
- mWriteQueue.add(new TaskWriteQueueItem(task));
+
+ if (item == null && task.isPersistable) {
+ mPersisterQueue.addItem(new TaskWriteQueueItem(task, mService), flush);
}
} else {
// Dummy. Ensures removeObsoleteFiles is called when LazyTaskThreadWriter is
// notified.
- mWriteQueue.add(new WriteQueueItem());
+ mPersisterQueue.addItem(PersisterQueue.EMPTY_ITEM, flush);
}
- if (flush || mWriteQueue.size() > MAX_WRITE_QUEUE_LENGTH) {
- mNextWriteTime = FLUSH_QUEUE;
- } else if (mNextWriteTime == 0) {
- mNextWriteTime = SystemClock.uptimeMillis() + PRE_TASK_DELAY_MS;
+ if (DEBUG) {
+ Slog.d(TAG, "wakeup: task=" + task + " flush=" + flush + " Callers="
+ + Debug.getCallers(4));
}
- if (DEBUG) Slog.d(TAG, "wakeup: task=" + task + " flush=" + flush + " mNextWriteTime="
- + mNextWriteTime + " mWriteQueue.size=" + mWriteQueue.size()
- + " Callers=" + Debug.getCallers(4));
- notifyAll();
}
- yieldIfQueueTooDeep();
+ mPersisterQueue.yieldIfQueueTooDeep();
}
void flush() {
- synchronized (this) {
- mNextWriteTime = FLUSH_QUEUE;
- notifyAll();
- do {
- try {
- wait();
- } catch (InterruptedException e) {
- }
- } while (mNextWriteTime == FLUSH_QUEUE);
- }
+ mPersisterQueue.flush();
}
void saveImage(Bitmap image, String filePath) {
- synchronized (this) {
- int queueNdx;
- for (queueNdx = mWriteQueue.size() - 1; queueNdx >= 0; --queueNdx) {
- final WriteQueueItem item = mWriteQueue.get(queueNdx);
- if (item instanceof ImageWriteQueueItem) {
- ImageWriteQueueItem imageWriteQueueItem = (ImageWriteQueueItem) item;
- if (imageWriteQueueItem.mFilePath.equals(filePath)) {
- // replace the Bitmap with the new one.
- imageWriteQueueItem.mImage = image;
- break;
- }
- }
- }
- if (queueNdx < 0) {
- mWriteQueue.add(new ImageWriteQueueItem(filePath, image));
- }
- if (mWriteQueue.size() > MAX_WRITE_QUEUE_LENGTH) {
- mNextWriteTime = FLUSH_QUEUE;
- } else if (mNextWriteTime == 0) {
- mNextWriteTime = SystemClock.uptimeMillis() + PRE_TASK_DELAY_MS;
+ synchronized (mPersisterQueue) {
+ final ImageWriteQueueItem item = mPersisterQueue.findLastItem(
+ queueItem -> queueItem.mFilePath.equals(filePath), ImageWriteQueueItem.class);
+ if (item != null) {
+ // replace the Bitmap with the new one.
+ item.mImage = image;
+ } else {
+ mPersisterQueue.addItem(new ImageWriteQueueItem(filePath, image),
+ /* flush */ false);
}
if (DEBUG) Slog.d(TAG, "saveImage: filePath=" + filePath + " now=" +
- SystemClock.uptimeMillis() + " mNextWriteTime=" +
- mNextWriteTime + " Callers=" + Debug.getCallers(4));
- notifyAll();
+ SystemClock.uptimeMillis() + " Callers=" + Debug.getCallers(4));
}
- yieldIfQueueTooDeep();
+ mPersisterQueue.yieldIfQueueTooDeep();
}
Bitmap getTaskDescriptionIcon(String filePath) {
@@ -340,41 +245,10 @@
return restoreImage(filePath);
}
- Bitmap getImageFromWriteQueue(String filePath) {
- synchronized (this) {
- for (int queueNdx = mWriteQueue.size() - 1; queueNdx >= 0; --queueNdx) {
- final WriteQueueItem item = mWriteQueue.get(queueNdx);
- if (item instanceof ImageWriteQueueItem) {
- ImageWriteQueueItem imageWriteQueueItem = (ImageWriteQueueItem) item;
- if (imageWriteQueueItem.mFilePath.equals(filePath)) {
- return imageWriteQueueItem.mImage;
- }
- }
- }
- return null;
- }
- }
-
- private StringWriter saveToXml(TaskRecord task) throws IOException, XmlPullParserException {
- if (DEBUG) Slog.d(TAG, "saveToXml: task=" + task);
- final XmlSerializer xmlSerializer = new FastXmlSerializer();
- StringWriter stringWriter = new StringWriter();
- xmlSerializer.setOutput(stringWriter);
-
- if (DEBUG) xmlSerializer.setFeature(
- "http://xmlpull.org/v1/doc/features.html#indent-output", true);
-
- // save task
- xmlSerializer.startDocument(null, true);
-
- xmlSerializer.startTag(null, TAG_TASK);
- task.saveToXml(xmlSerializer);
- xmlSerializer.endTag(null, TAG_TASK);
-
- xmlSerializer.endDocument();
- xmlSerializer.flush();
-
- return stringWriter;
+ private Bitmap getImageFromWriteQueue(String filePath) {
+ final ImageWriteQueueItem item = mPersisterQueue.findLastItem(
+ queueItem -> queueItem.mFilePath.equals(filePath), ImageWriteQueueItem.class);
+ return item != null ? item.mImage : null;
}
private String fileToString(File file) {
@@ -534,6 +408,26 @@
return tasks;
}
+ @Override
+ public void onPreProcessItem(boolean queueEmpty) {
+ // We can't lock mService while locking the queue, but we don't want to
+ // call removeObsoleteFiles before every item, only the last time
+ // before going to sleep. The risk is that we call removeObsoleteFiles()
+ // successively.
+ if (queueEmpty) {
+ if (DEBUG) Slog.d(TAG, "Looking for obsolete files.");
+ mTmpTaskIds.clear();
+ synchronized (mService.mGlobalLock) {
+ if (DEBUG) Slog.d(TAG, "mRecents=" + mRecentTasks);
+ mRecentTasks.getPersistableTaskIds(mTmpTaskIds);
+ mService.mWindowManager.removeObsoleteTaskFiles(mTmpTaskIds,
+ mRecentTasks.usersWithRecentsLoadedLocked());
+ }
+ removeObsoleteFiles(mTmpTaskIds);
+ }
+ writeTaskIdsFiles();
+ }
+
private static void removeObsoleteFiles(ArraySet<Integer> persistentTaskIds, File[] files) {
if (DEBUG) Slog.d(TAG, "removeObsoleteFiles: persistentTaskIds=" + persistentTaskIds +
" files=" + files);
@@ -631,143 +525,117 @@
return parentDir.exists() || parentDir.mkdirs();
}
- private class LazyTaskWriterThread extends Thread {
+ private static class TaskWriteQueueItem implements PersisterQueue.WriteQueueItem {
+ private final ActivityTaskManagerService mService;
+ private final TaskRecord mTask;
- LazyTaskWriterThread(String name) {
- super(name);
+ TaskWriteQueueItem(TaskRecord task, ActivityTaskManagerService service) {
+ mTask = task;
+ mService = service;
+ }
+
+ private StringWriter saveToXml(TaskRecord task) throws IOException, XmlPullParserException {
+ if (DEBUG) Slog.d(TAG, "saveToXml: task=" + task);
+ final XmlSerializer xmlSerializer = new FastXmlSerializer();
+ StringWriter stringWriter = new StringWriter();
+ xmlSerializer.setOutput(stringWriter);
+
+ if (DEBUG) {
+ xmlSerializer.setFeature(
+ "http://xmlpull.org/v1/doc/features.html#indent-output", true);
+ }
+
+ // save task
+ xmlSerializer.startDocument(null, true);
+
+ xmlSerializer.startTag(null, TAG_TASK);
+ task.saveToXml(xmlSerializer);
+ xmlSerializer.endTag(null, TAG_TASK);
+
+ xmlSerializer.endDocument();
+ xmlSerializer.flush();
+
+ return stringWriter;
}
@Override
- public void run() {
- Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
- ArraySet<Integer> persistentTaskIds = new ArraySet<>();
- while (true) {
- // We can't lock mService while holding TaskPersister.this, but we don't want to
- // call removeObsoleteFiles every time through the loop, only the last time before
- // going to sleep. The risk is that we call removeObsoleteFiles() successively.
- final boolean probablyDone;
- synchronized (TaskPersister.this) {
- probablyDone = mWriteQueue.isEmpty();
- }
- if (probablyDone) {
- if (DEBUG) Slog.d(TAG, "Looking for obsolete files.");
- persistentTaskIds.clear();
- synchronized (mService.mGlobalLock) {
- if (DEBUG) Slog.d(TAG, "mRecents=" + mRecentTasks);
- mRecentTasks.getPersistableTaskIds(persistentTaskIds);
- mService.mWindowManager.removeObsoleteTaskFiles(persistentTaskIds,
- mRecentTasks.usersWithRecentsLoadedLocked());
+ public void process() {
+ // Write out one task.
+ StringWriter stringWriter = null;
+ TaskRecord task = mTask;
+ if (DEBUG) Slog.d(TAG, "Writing task=" + task);
+ synchronized (mService.mGlobalLock) {
+ if (task.inRecents) {
+ // Still there.
+ try {
+ if (DEBUG) Slog.d(TAG, "Saving task=" + task);
+ stringWriter = saveToXml(task);
+ } catch (IOException e) {
+ } catch (XmlPullParserException e) {
}
- removeObsoleteFiles(persistentTaskIds);
}
- writeTaskIdsFiles();
-
- processNextItem();
+ }
+ if (stringWriter != null) {
+ // Write out xml file while not holding mService lock.
+ FileOutputStream file = null;
+ AtomicFile atomicFile = null;
+ try {
+ atomicFile = new AtomicFile(new File(
+ getUserTasksDir(task.userId),
+ String.valueOf(task.taskId) + TASK_FILENAME_SUFFIX));
+ file = atomicFile.startWrite();
+ file.write(stringWriter.toString().getBytes());
+ file.write('\n');
+ atomicFile.finishWrite(file);
+ } catch (IOException e) {
+ if (file != null) {
+ atomicFile.failWrite(file);
+ }
+ Slog.e(TAG,
+ "Unable to open " + atomicFile + " for persisting. " + e);
+ }
}
}
- private void processNextItem() {
- // This part is extracted into a method so that the GC can clearly see the end of the
- // scope of the variable 'item'. If this part was in the loop above, the last item
- // it processed would always "leak".
- // See https://b.corp.google.com/issues/64438652#comment7
+ @Override
+ public String toString() {
+ return "TaskWriteQueueItem{task=" + mTask + "}";
+ }
+ }
- // If mNextWriteTime, then don't delay between each call to saveToXml().
- final WriteQueueItem item;
- synchronized (TaskPersister.this) {
- if (mNextWriteTime != FLUSH_QUEUE) {
- // The next write we don't have to wait so long.
- mNextWriteTime = SystemClock.uptimeMillis() + INTER_WRITE_DELAY_MS;
- if (DEBUG) Slog.d(TAG, "Next write time may be in " +
- INTER_WRITE_DELAY_MS + " msec. (" + mNextWriteTime + ")");
- }
+ private static class ImageWriteQueueItem implements PersisterQueue.WriteQueueItem {
+ final String mFilePath;
+ Bitmap mImage;
- while (mWriteQueue.isEmpty()) {
- if (mNextWriteTime != 0) {
- mNextWriteTime = 0; // idle.
- TaskPersister.this.notifyAll(); // wake up flush() if needed.
- }
- try {
- if (DEBUG) Slog.d(TAG, "LazyTaskWriter: waiting indefinitely.");
- TaskPersister.this.wait();
- } catch (InterruptedException e) {
- }
- // Invariant: mNextWriteTime is either FLUSH_QUEUE or PRE_WRITE_DELAY_MS
- // from now.
- }
- item = mWriteQueue.remove(0);
+ ImageWriteQueueItem(String filePath, Bitmap image) {
+ mFilePath = filePath;
+ mImage = image;
+ }
- long now = SystemClock.uptimeMillis();
- if (DEBUG) Slog.d(TAG, "LazyTaskWriter: now=" + now + " mNextWriteTime=" +
- mNextWriteTime + " mWriteQueue.size=" + mWriteQueue.size());
- while (now < mNextWriteTime) {
- try {
- if (DEBUG) Slog.d(TAG, "LazyTaskWriter: waiting " +
- (mNextWriteTime - now));
- TaskPersister.this.wait(mNextWriteTime - now);
- } catch (InterruptedException e) {
- }
- now = SystemClock.uptimeMillis();
- }
-
- // Got something to do.
+ @Override
+ public void process() {
+ final String filePath = mFilePath;
+ if (!createParentDirectory(filePath)) {
+ Slog.e(TAG, "Error while creating images directory for file: " + filePath);
+ return;
}
-
- if (item instanceof ImageWriteQueueItem) {
- ImageWriteQueueItem imageWriteQueueItem = (ImageWriteQueueItem) item;
- final String filePath = imageWriteQueueItem.mFilePath;
- if (!createParentDirectory(filePath)) {
- Slog.e(TAG, "Error while creating images directory for file: " + filePath);
- return;
- }
- final Bitmap bitmap = imageWriteQueueItem.mImage;
- if (DEBUG) Slog.d(TAG, "writing bitmap: filename=" + filePath);
- FileOutputStream imageFile = null;
- try {
- imageFile = new FileOutputStream(new File(filePath));
- bitmap.compress(Bitmap.CompressFormat.PNG, 100, imageFile);
- } catch (Exception e) {
- Slog.e(TAG, "saveImage: unable to save " + filePath, e);
- } finally {
- IoUtils.closeQuietly(imageFile);
- }
- } else if (item instanceof TaskWriteQueueItem) {
- // Write out one task.
- StringWriter stringWriter = null;
- TaskRecord task = ((TaskWriteQueueItem) item).mTask;
- if (DEBUG) Slog.d(TAG, "Writing task=" + task);
- synchronized (mService.mGlobalLock) {
- if (task.inRecents) {
- // Still there.
- try {
- if (DEBUG) Slog.d(TAG, "Saving task=" + task);
- stringWriter = saveToXml(task);
- } catch (IOException e) {
- } catch (XmlPullParserException e) {
- }
- }
- }
- if (stringWriter != null) {
- // Write out xml file while not holding mService lock.
- FileOutputStream file = null;
- AtomicFile atomicFile = null;
- try {
- atomicFile = new AtomicFile(new File(
- getUserTasksDir(task.userId),
- String.valueOf(task.taskId) + TASK_FILENAME_SUFFIX));
- file = atomicFile.startWrite();
- file.write(stringWriter.toString().getBytes());
- file.write('\n');
- atomicFile.finishWrite(file);
- } catch (IOException e) {
- if (file != null) {
- atomicFile.failWrite(file);
- }
- Slog.e(TAG,
- "Unable to open " + atomicFile + " for persisting. " + e);
- }
- }
+ final Bitmap bitmap = mImage;
+ if (DEBUG) Slog.d(TAG, "writing bitmap: filename=" + filePath);
+ FileOutputStream imageFile = null;
+ try {
+ imageFile = new FileOutputStream(new File(filePath));
+ bitmap.compress(Bitmap.CompressFormat.PNG, 100, imageFile);
+ } catch (Exception e) {
+ Slog.e(TAG, "saveImage: unable to save " + filePath, e);
+ } finally {
+ IoUtils.closeQuietly(imageFile);
}
}
+
+ @Override
+ public String toString() {
+ return "ImageWriteQueueItem{path=" + mFilePath
+ + ", image=(" + mImage.getWidth() + "x" + mImage.getHeight() + ")}";
+ }
}
}
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index ef8cb1c..05b0d59 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -45,7 +45,6 @@
import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
import static android.provider.Settings.Secure.USER_SETUP_COMPLETE;
import static android.view.Display.DEFAULT_DISPLAY;
-
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ADD_REMOVE;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LOCKTASK;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_RECENTS;
@@ -75,7 +74,6 @@
import static com.android.server.am.TaskRecordProto.REAL_ACTIVITY;
import static com.android.server.am.TaskRecordProto.RESIZE_MODE;
import static com.android.server.am.TaskRecordProto.STACK_ID;
-
import static java.lang.Integer.MAX_VALUE;
import android.annotation.IntDef;
@@ -1686,11 +1684,19 @@
// so that the user can not render the task too small to manipulate. We don't need
// to do this for the pinned stack as the bounds are controlled by the system.
if (!inPinnedWindowingMode()) {
+ final int defaultMinSizeDp =
+ mService.mStackSupervisor.mDefaultMinSizeOfResizeableTaskDp;
+ final ActivityDisplay display =
+ mService.mStackSupervisor.getActivityDisplay(mStack.mDisplayId);
+ final float density =
+ (float) display.getConfiguration().densityDpi / DisplayMetrics.DENSITY_DEFAULT;
+ final int defaultMinSize = (int) (defaultMinSizeDp * density);
+
if (minWidth == INVALID_MIN_SIZE) {
- minWidth = mService.mStackSupervisor.mDefaultMinSizeOfResizeableTask;
+ minWidth = defaultMinSize;
}
if (minHeight == INVALID_MIN_SIZE) {
- minHeight = mService.mStackSupervisor.mDefaultMinSizeOfResizeableTask;
+ minHeight = defaultMinSize;
}
}
final boolean adjustWidth = minWidth > bounds.width();
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..7184769
--- /dev/null
+++ b/services/core/java/com/android/server/appbinding/AppBindingConstants.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.appbinding;
+
+import android.content.Context;
+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}.
+ */
+public 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";
+
+ private static final String SERVICE_STABLE_CONNECTION_THRESHOLD_SEC_KEY =
+ "service_stable_connection_threshold_sec";
+
+ private static final String SMS_SERVICE_ENABLED_KEY =
+ "sms_service_enabled";
+
+ private static final String SMS_APP_BIND_FLAGS_KEY =
+ "sms_app_bind_flags";
+
+ 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;
+
+ /**
+ * If a connection lasts more than this duration, we reset the re-connect back-off time.
+ */
+ public final long SERVICE_STABLE_CONNECTION_THRESHOLD_SEC;
+
+ /**
+ * Whether to actually bind to the default SMS app service. (Feature flag)
+ */
+ public final boolean SMS_SERVICE_ENABLED;
+
+ /**
+ * Extra binding flags for SMS service.
+ */
+ public final int SMS_APP_BIND_FLAGS;
+
+ 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, 10);
+
+ double serviceReconnectBackoffIncrease = parser.getFloat(
+ SERVICE_RECONNECT_BACKOFF_INCREASE_KEY, 2f);
+
+ long serviceReconnectMaxBackoffSec = parser.getLong(
+ SERVICE_RECONNECT_MAX_BACKOFF_SEC_KEY, TimeUnit.HOURS.toSeconds(1));
+
+ boolean smsServiceEnabled = parser.getBoolean(SMS_SERVICE_ENABLED_KEY, true);
+
+ int smsAppBindFlags = parser.getInt(
+ SMS_APP_BIND_FLAGS_KEY,
+ Context.BIND_NOT_VISIBLE | Context.BIND_FOREGROUND_SERVICE);
+
+ long serviceStableConnectionThresholdSec = parser.getLong(
+ SERVICE_STABLE_CONNECTION_THRESHOLD_SEC_KEY, TimeUnit.MINUTES.toSeconds(2));
+
+ // 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;
+ SERVICE_STABLE_CONNECTION_THRESHOLD_SEC = serviceStableConnectionThresholdSec;
+ SMS_SERVICE_ENABLED = smsServiceEnabled;
+ SMS_APP_BIND_FLAGS = smsAppBindFlags;
+ }
+
+ /**
+ * 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.print("Constants: ");
+ pw.println(sourceSettings);
+
+ 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);
+
+ pw.print(prefix);
+ pw.print(" SERVICE_STABLE_CONNECTION_THRESHOLD_SEC: ");
+ pw.println(SERVICE_STABLE_CONNECTION_THRESHOLD_SEC);
+
+ pw.print(prefix);
+ pw.print(" SMS_SERVICE_ENABLED: ");
+ pw.println(SMS_SERVICE_ENABLED);
+
+ pw.print(prefix);
+ pw.print(" SMS_APP_BIND_FLAGS: 0x");
+ pw.println(Integer.toHexString(SMS_APP_BIND_FLAGS));
+ }
+}
diff --git a/services/core/java/com/android/server/appbinding/AppBindingService.java b/services/core/java/com/android/server/appbinding/AppBindingService.java
index 91b3b21..3131255 100644
--- a/services/core/java/com/android/server/appbinding/AppBindingService.java
+++ b/services/core/java/com/android/server/appbinding/AppBindingService.java
@@ -16,26 +16,59 @@
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.ContentResolver;
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.
+ *
+ * Relevant tests:
+ * atest CtsAppBindingHostTestCases
+ *
+ * TODO Maybe handle force-stop differently. Right now we just get "binder died" and re-bind
+ * after a timeout. b/116813347
*/
public class AppBindingService extends Binder {
public static final String TAG = "AppBindingService";
- private static final boolean DEBUG = false;
+ public static final boolean DEBUG = false; // DO NOT SUBMIT WITH TRUE
private final Object mLock = new Object();
@@ -44,38 +77,492 @@
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();
}
+
+ public String getGlobalSettingString(ContentResolver resolver, String key) {
+ return Settings.Global.getString(resolver, key);
+ }
}
/**
- * System service interacts with this service via this class.
+ * {@link SystemService} for this service.
*/
- public static final class Lifecycle extends SystemService {
+ public static class Lifecycle extends SystemService {
final AppBindingService mService;
public Lifecycle(Context context) {
+ this(context, new Injector());
+ }
+
+ Lifecycle(Context context, Injector injector) {
super(context);
- mService = new AppBindingService(new Injector(), context);
+ mService = new AppBindingService(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.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 = mInjector.getGlobalSettingString(
+ 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) {
+ if (DEBUG) {
+ Slog.d(TAG, "Broadcast received: " + 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, mConstants);
+ 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,
+ constants.SERVICE_STABLE_CONNECTION_THRESHOLD_SEC);
+ mFinder = finder;
+ mConstants = constants;
+ }
+
+ @Override
+ protected int getBindFlags() {
+ return mFinder.getBindFlags(mConstants);
+ }
+
+ @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.print(",#con=");
+ pw.print(conn.getNumConnected());
+ pw.print(",#dis=");
+ pw.print(conn.getNumDisconnected());
+ pw.print(",#died=");
+ pw.print(conn.getNumBindingDied());
+ pw.print(",backoff=");
+ pw.print(conn.getNextBackoffMs());
+ pw.println();
+ }
+ forAllAppsLocked((app) -> app.dumpSimple(pw));
+ }
+ }
+
+ AppBindingConstants getConstantsForTest() {
+ return mConstants;
}
}
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..a075c50
--- /dev/null
+++ b/services/core/java/com/android/server/appbinding/finders/AppServiceFinder.java
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.Log;
+import android.util.Slog;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.appbinding.AppBindingConstants;
+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(4);
+
+ @GuardedBy("mLock")
+ private final SparseArray<ServiceInfo> mTargetServices = new SparseArray(4);
+
+ @GuardedBy("mLock")
+ private final SparseArray<String> mLastMessages = new SparseArray(4);
+
+ public AppServiceFinder(Context context,
+ BiConsumer<AppServiceFinder, Integer> listener,
+ Handler callbackHandler) {
+ mContext = context;
+ mListener = listener;
+ mHandler = callbackHandler;
+ }
+
+ /** Whether this service should really be enabled. */
+ protected boolean isEnabled(AppBindingConstants constants) {
+ return true;
+ }
+
+ /** 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);
+ mLastMessages.delete(userId);
+ }
+ }
+
+ /**
+ * Find the target service from the target app on a given user.
+ */
+ @Nullable
+ public final ServiceInfo findService(int userId, IPackageManager ipm,
+ AppBindingConstants constants) {
+ synchronized (mLock) {
+ mTargetPackages.put(userId, null);
+ mTargetServices.put(userId, null);
+ mLastMessages.put(userId, null);
+
+ if (!isEnabled(constants)) {
+ final String message = "feature disabled";
+ mLastMessages.put(userId, message);
+ Slog.i(TAG, getAppDescription() + " " + message);
+ return 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) {
+ // This log is optional because findService() already did Log.e().
+ Slog.w(TAG, getAppDescription() + " package " + targetPackage + " u" + userId
+ + " " + message);
+ }
+ return null;
+ }
+ final String error = validateService(service);
+ if (error != null) {
+ mLastMessages.put(userId, error);
+ Log.e(TAG, error);
+ 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();
+
+ /**
+ * Subclass can implement it to decide whether to accept a service (by returning null) or not
+ * (by returning an error message.)
+ */
+ protected String validateService(ServiceInfo service) {
+ return null;
+ }
+
+ /** Return the bind flags for this service. */
+ public abstract int getBindFlags(AppBindingConstants constants);
+
+ /** 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++) {
+ final int userId = mTargetPackages.keyAt(i);
+ pw.print(prefix);
+ pw.print(" User: ");
+ pw.print(userId);
+ pw.println();
+
+ pw.print(prefix);
+ pw.print(" Package: ");
+ pw.print(mTargetPackages.get(userId));
+ pw.println();
+
+ pw.print(prefix);
+ pw.print(" Service: ");
+ pw.print(mTargetServices.get(userId));
+ pw.println();
+
+ pw.print(prefix);
+ pw.print(" Message: ");
+ pw.print(mLastMessages.get(userId));
+ pw.println();
+ }
+ }
+ }
+
+ /** Dumpys support */
+ public void dumpSimple(PrintWriter pw) {
+ synchronized (mLock) {
+ for (int i = 0; i < mTargetPackages.size(); i++) {
+ final int userId = mTargetPackages.keyAt(i);
+ pw.print("finder,");
+ pw.print(getAppDescription());
+ pw.print(",");
+ pw.print(userId);
+ pw.print(",");
+ pw.print(mTargetPackages.get(userId));
+ pw.print(",");
+ pw.print(mTargetServices.get(userId));
+ pw.print(",");
+ pw.print(mLastMessages.get(userId));
+ 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..fcc28f8
--- /dev/null
+++ b/services/core/java/com/android/server/appbinding/finders/SmsAppServiceFinder.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.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.content.pm.ServiceInfo;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.UserHandle;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+import android.util.Slog;
+
+import com.android.internal.R;
+import com.android.internal.telephony.SmsApplication;
+import com.android.server.appbinding.AppBindingConstants;
+
+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
+ protected boolean isEnabled(AppBindingConstants constants) {
+ return constants.SMS_SERVICE_ENABLED
+ && mContext.getResources().getBoolean(R.bool.config_useSmsAppService);
+ }
+
+ @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);
+ String ret = cn == null ? null : cn.getPackageName();
+
+ if (DEBUG) {
+ Slog.d(TAG, "getTargetPackage()=" + ret);
+ }
+
+ return ret;
+ }
+
+ @Override
+ public void startMonitoring() {
+ final IntentFilter filter = new IntentFilter(ACTION_DEFAULT_SMS_PACKAGE_CHANGED_INTERNAL);
+ mContext.registerReceiverAsUser(mSmsAppChangedWatcher, UserHandle.ALL, filter,
+ /* permission= */ null, mHandler);
+ }
+
+ @Override
+ protected String validateService(ServiceInfo service) {
+ final String packageName = service.packageName;
+ final String process = service.processName;
+
+ if (process == null || TextUtils.equals(packageName, process)) {
+ return "Service must not run on the main process";
+ }
+ return null; // Null means accept this service.
+ }
+
+ @Override
+ public int getBindFlags(AppBindingConstants constants) {
+ return constants.SMS_APP_BIND_FLAGS;
+ }
+
+ 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 66c7c43..f0ff570 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -4685,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);
@@ -4723,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;
@@ -5888,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));
}
@@ -7253,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 aa4d34e..8a72a6d 100644
--- a/services/core/java/com/android/server/biometrics/AuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/AuthenticationClient.java
@@ -195,7 +195,7 @@
// ERROR_CANCELED message.
return true;
}
- if (mBundle != null) {
+ if (mBundle != null && error != BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED) {
try {
mStatusBarService.onBiometricError(getErrorString(error, vendorCode));
} catch (RemoteException e) {
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index 0f68c68..abc0107 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -17,13 +17,20 @@
package com.android.server.biometrics;
import static android.Manifest.permission.USE_BIOMETRIC;
+import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
import static android.Manifest.permission.USE_FINGERPRINT;
+import android.app.ActivityManager;
import android.app.AppOpsManager;
+import android.app.UserSwitchObserver;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.PackageManager;
+import android.database.ContentObserver;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricConstants;
+import android.hardware.biometrics.BiometricSourceType;
+import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
import android.hardware.biometrics.IBiometricPromptReceiver;
import android.hardware.biometrics.IBiometricService;
import android.hardware.biometrics.IBiometricServiceReceiver;
@@ -31,27 +38,32 @@
import android.hardware.face.IFaceService;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.IFingerprintService;
+import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
+import android.os.DeadObjectException;
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.provider.Settings;
+import android.util.Pair;
import android.util.Slog;
import com.android.internal.R;
import com.android.server.SystemService;
import java.util.ArrayList;
+import java.util.List;
/**
* System service that arbitrates the modality for BiometricPrompt to use.
*/
public class BiometricService extends SystemService {
- private static final String TAG = "BiometricPromptService";
+ private static final String TAG = "BiometricService";
/**
* No biometric methods or nothing has been enrolled.
@@ -86,6 +98,8 @@
private final boolean mHasFeatureFingerprint;
private final boolean mHasFeatureIris;
private final boolean mHasFeatureFace;
+ private final SettingObserver mSettingObserver;
+ private final List<EnabledOnKeyguardCallback> mEnabledOnKeyguardCallbacks;
private IFingerprintService mFingerprintService;
private IFaceService mFaceService;
@@ -119,6 +133,107 @@
}
}
+ private final class SettingObserver extends ContentObserver {
+ private final Uri FACE_UNLOCK_KEYGUARD_ENABLED =
+ Settings.Secure.getUriFor(Settings.Secure.FACE_UNLOCK_KEYGUARD_ENABLED);
+ private final Uri FACE_UNLOCK_APP_ENABLED =
+ Settings.Secure.getUriFor(Settings.Secure.FACE_UNLOCK_APP_ENABLED);
+
+ private final ContentResolver mContentResolver;
+ private boolean mFaceEnabledOnKeyguard;
+ private boolean mFaceEnabledForApps;
+
+ /**
+ * Creates a content observer.
+ *
+ * @param handler The handler to run {@link #onChange} on, or null if none.
+ */
+ SettingObserver(Handler handler) {
+ super(handler);
+ mContentResolver = getContext().getContentResolver();
+ updateContentObserver();
+ }
+
+ void updateContentObserver() {
+ mContentResolver.unregisterContentObserver(this);
+ mContentResolver.registerContentObserver(FACE_UNLOCK_KEYGUARD_ENABLED,
+ false /* notifyForDescendents */,
+ this /* observer */,
+ UserHandle.USER_CURRENT);
+ mContentResolver.registerContentObserver(FACE_UNLOCK_APP_ENABLED,
+ false /* notifyForDescendents */,
+ this /* observer */,
+ UserHandle.USER_CURRENT);
+
+ // Update the value immediately
+ onChange(true /* selfChange */, FACE_UNLOCK_KEYGUARD_ENABLED);
+ onChange(true /* selfChange */, FACE_UNLOCK_APP_ENABLED);
+ }
+
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ if (FACE_UNLOCK_KEYGUARD_ENABLED.equals(uri)) {
+ mFaceEnabledOnKeyguard =
+ Settings.Secure.getIntForUser(
+ mContentResolver,
+ Settings.Secure.FACE_UNLOCK_KEYGUARD_ENABLED,
+ 1 /* default */,
+ UserHandle.USER_CURRENT) != 0;
+
+ List<EnabledOnKeyguardCallback> callbacks = mEnabledOnKeyguardCallbacks;
+ for (int i = 0; i < callbacks.size(); i++) {
+ callbacks.get(i).notify(BiometricSourceType.FACE, mFaceEnabledOnKeyguard);
+ }
+ } else if (FACE_UNLOCK_APP_ENABLED.equals(uri)) {
+ mFaceEnabledForApps =
+ Settings.Secure.getIntForUser(
+ mContentResolver,
+ Settings.Secure.FACE_UNLOCK_APP_ENABLED,
+ 1 /* default */,
+ UserHandle.USER_CURRENT) != 0;
+ }
+ }
+
+ boolean getFaceEnabledOnKeyguard() {
+ return mFaceEnabledOnKeyguard;
+ }
+
+ boolean getFaceEnabledForApps() {
+ return mFaceEnabledForApps;
+ }
+ }
+
+ private final class EnabledOnKeyguardCallback implements IBinder.DeathRecipient {
+
+ private final IBiometricEnabledOnKeyguardCallback mCallback;
+
+ EnabledOnKeyguardCallback(IBiometricEnabledOnKeyguardCallback callback) {
+ mCallback = callback;
+ try {
+ mCallback.asBinder().linkToDeath(EnabledOnKeyguardCallback.this, 0);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Unable to linkToDeath", e);
+ }
+ }
+
+ void notify(BiometricSourceType sourceType, boolean enabled) {
+ try {
+ mCallback.onChanged(sourceType, enabled);
+ } catch (DeadObjectException e) {
+ Slog.w(TAG, "Death while invoking notify", e);
+ mEnabledOnKeyguardCallbacks.remove(this);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to invoke onChanged", e);
+ }
+ }
+
+ @Override
+ public void binderDied() {
+ Slog.e(TAG, "Enabled callback binder died");
+ mEnabledOnKeyguardCallbacks.remove(this);
+ }
+ }
+
/**
* 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
@@ -130,7 +245,7 @@
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
+ // Check the USE_BIOMETRIC permission here. In the BiometricServiceBase, check do the
// AppOps and foreground check.
checkPermission();
@@ -145,8 +260,38 @@
final int callingUserId = UserHandle.getCallingUserId();
mHandler.post(() -> {
- mCurrentModality = checkAndGetBiometricModality(receiver);
+ final Pair<Integer, Integer> result = checkAndGetBiometricModality();
+ final int modality = result.first;
+ final int error = result.second;
+ // Check for errors, notify callback, and return
+ if (error != BiometricConstants.BIOMETRIC_ERROR_NONE) {
+ try {
+ final String hardwareUnavailable =
+ getContext().getString(R.string.biometric_error_hw_unavailable);
+ switch (error) {
+ case BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT:
+ receiver.onError(0 /* deviceId */, error, hardwareUnavailable);
+ break;
+ case BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE:
+ receiver.onError(0 /* deviceId */, error, hardwareUnavailable);
+ break;
+ case BiometricConstants.BIOMETRIC_ERROR_NO_BIOMETRICS:
+ receiver.onError(0 /* deviceId */, error,
+ getErrorString(modality, error, 0 /* vendorCode */));
+ break;
+ default:
+ Slog.e(TAG, "Unhandled error");
+ break;
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to send error", e);
+ }
+ return;
+ }
+
+ // Actually start authentication
+ mCurrentModality = modality;
try {
// No polymorphism :(
if (mCurrentModality == BIOMETRIC_FINGERPRINT) {
@@ -156,9 +301,9 @@
} else if (mCurrentModality == BIOMETRIC_IRIS) {
Slog.w(TAG, "Unsupported modality");
} else if (mCurrentModality == BIOMETRIC_FACE) {
- mFaceService.authenticateFromService(true /* requireConfirmation */, token,
- sessionId, userId, receiver, flags, opPackageName, bundle,
- dialogReceiver, callingUid, callingPid, callingUserId);
+ mFaceService.authenticateFromService(true /* requireConfirmation */,
+ token, sessionId, userId, receiver, flags, opPackageName,
+ bundle, dialogReceiver, callingUid, callingPid, callingUserId);
} else {
Slog.w(TAG, "Unsupported modality");
}
@@ -202,31 +347,46 @@
}
@Override // Binder call
- public boolean hasEnrolledBiometrics(String opPackageName) {
+ public int canAuthenticate(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");
- }
+ checkAppOp(opPackageName, Binder.getCallingUid());
final long ident = Binder.clearCallingIdentity();
- boolean hasEnrolled = false;
+ int error;
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;
- }
- }
+ final Pair<Integer, Integer> result = checkAndGetBiometricModality();
+ error = result.second;
} finally {
Binder.restoreCallingIdentity(ident);
}
- return hasEnrolled;
+ return error;
}
+
+ @Override
+ public void registerEnabledOnKeyguardCallback(IBiometricEnabledOnKeyguardCallback callback)
+ throws RemoteException {
+ checkInternalPermission();
+ mEnabledOnKeyguardCallbacks.add(new EnabledOnKeyguardCallback(callback));
+ try {
+ callback.onChanged(BiometricSourceType.FACE,
+ mSettingObserver.getFaceEnabledOnKeyguard());
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Remote exception", e);
+ }
+ }
+ }
+
+ private void checkAppOp(String opPackageName, int callingUid) {
+ if (mAppOps.noteOp(AppOpsManager.OP_USE_BIOMETRIC, callingUid,
+ opPackageName) != AppOpsManager.MODE_ALLOWED) {
+ Slog.w(TAG, "Rejecting " + opPackageName + "; permission denied");
+ throw new SecurityException("Permission denied");
+ }
+ }
+
+ private void checkInternalPermission() {
+ getContext().enforceCallingPermission(USE_BIOMETRIC_INTERNAL,
+ "Must have MANAGE_BIOMETRIC permission");
}
private void checkPermission() {
@@ -251,11 +411,26 @@
mAppOps = context.getSystemService(AppOpsManager.class);
mHandler = new Handler(Looper.getMainLooper());
+ mEnabledOnKeyguardCallbacks = new ArrayList<>();
+ mSettingObserver = new SettingObserver(mHandler);
final PackageManager pm = context.getPackageManager();
mHasFeatureFingerprint = pm.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT);
mHasFeatureIris = pm.hasSystemFeature(PackageManager.FEATURE_IRIS);
mHasFeatureFace = pm.hasSystemFeature(PackageManager.FEATURE_FACE);
+
+ try {
+ ActivityManager.getService().registerUserSwitchObserver(
+ new UserSwitchObserver() {
+ @Override
+ public void onUserSwitchComplete(int newUserId) {
+ mSettingObserver.updateContentObserver();
+ }
+ }, BiometricService.class.getName()
+ );
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to register user switch observer", e);
+ }
}
@Override
@@ -286,65 +461,91 @@
* 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.
+ *
+ * @Returns A pair [Modality, Error] with Modality being one of {@link #BIOMETRIC_NONE},
+ * {@link #BIOMETRIC_FINGERPRINT}, {@link #BIOMETRIC_IRIS}, {@link #BIOMETRIC_FACE}
+ * and the error containing one of the {@link BiometricConstants} errors.
*/
- private int checkAndGetBiometricModality(IBiometricServiceReceiver receiver) {
+ private Pair<Integer, Integer> checkAndGetBiometricModality() {
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;
+ return new Pair<>(BIOMETRIC_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT);
}
- // Find first authenticator that's both detected and enrolled
+ // Assuming that authenticators are listed in priority-order, the rest of this function
+ // will go through and find the first authenticator that's available, enrolled, and enabled.
+ // The tricky part is returning the correct error. Error strings that are modality-specific
+ // should also respect the priority-order.
+
+ // Find first authenticator that's detected, enrolled, and enabled.
boolean isHardwareDetected = false;
boolean hasTemplatesEnrolled = false;
+ boolean enabledForApps = false;
+
+ int firstHwAvailable = BIOMETRIC_NONE;
for (int i = 0; i < mAuthenticators.size(); i++) {
- int featureId = mAuthenticators.get(i).getType();
+ modality = mAuthenticators.get(i).getType();
BiometricAuthenticator authenticator = mAuthenticators.get(i).getAuthenticator();
if (authenticator.isHardwareDetected()) {
isHardwareDetected = true;
+ if (firstHwAvailable == BIOMETRIC_NONE) {
+ // Store the first one since we want to return the error in correct priority
+ // order.
+ firstHwAvailable = modality;
+ }
if (authenticator.hasEnrolledTemplates()) {
hasTemplatesEnrolled = true;
- modality = featureId;
- break;
+ if (isEnabledForApp(modality)) {
+ enabledForApps = true;
+ 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,
- FaceManager.getErrorString(getContext(),
- BiometricConstants.BIOMETRIC_ERROR_NO_BIOMETRICS,
- 0 /* vendorCode */));
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to send error", e);
- }
- return BIOMETRIC_NONE;
+ return new Pair<>(BIOMETRIC_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE);
+ } else if (!hasTemplatesEnrolled) {
+ // Return the modality here so the correct error string can be sent. This error is
+ // preferred over !enabledForApps
+ return new Pair<>(firstHwAvailable, BiometricConstants.BIOMETRIC_ERROR_NO_BIOMETRICS);
+ } else if (!enabledForApps) {
+ return new Pair<>(BIOMETRIC_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE);
}
- return modality;
+ return new Pair<>(modality, BiometricConstants.BIOMETRIC_ERROR_NONE);
+ }
+
+ private boolean isEnabledForApp(int modality) {
+ switch(modality) {
+ case BIOMETRIC_FINGERPRINT:
+ return true;
+ case BIOMETRIC_IRIS:
+ return true;
+ case BIOMETRIC_FACE:
+ return mSettingObserver.getFaceEnabledForApps();
+ default:
+ Slog.w(TAG, "Unsupported modality: " + modality);
+ return false;
+ }
+ }
+
+ private String getErrorString(int type, int error, int vendorCode) {
+ switch (type) {
+ case BIOMETRIC_FINGERPRINT:
+ return FingerprintManager.getErrorString(getContext(), error, vendorCode);
+ case BIOMETRIC_IRIS:
+ Slog.w(TAG, "Modality not supported");
+ return null; // not supported
+ case BIOMETRIC_FACE:
+ return FaceManager.getErrorString(getContext(), error, vendorCode);
+ default:
+ Slog.w(TAG, "Unable to get error string for modality: " + type);
+ return null;
+ }
}
private BiometricAuthenticator getAuthenticator(int type) {
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 98c38dd..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.IBiometricServiceReceiver;
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;
@@ -121,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
@@ -346,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;
+ }
}
/**
@@ -779,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(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/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java
index 873a8e3..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));
diff --git a/services/core/java/com/android/server/connectivity/ProxyTracker.java b/services/core/java/com/android/server/connectivity/ProxyTracker.java
index dc65e1e..b7bbd42 100644
--- a/services/core/java/com/android/server/connectivity/ProxyTracker.java
+++ b/services/core/java/com/android/server/connectivity/ProxyTracker.java
@@ -142,6 +142,35 @@
}
/**
+ * Read the global proxy settings and cache them in memory.
+ */
+ public void loadGlobalProxy() {
+ ContentResolver res = mContext.getContentResolver();
+ String host = Settings.Global.getString(res, Settings.Global.GLOBAL_HTTP_PROXY_HOST);
+ int port = Settings.Global.getInt(res, Settings.Global.GLOBAL_HTTP_PROXY_PORT, 0);
+ String exclList = Settings.Global.getString(res,
+ Settings.Global.GLOBAL_HTTP_PROXY_EXCLUSION_LIST);
+ String pacFileUrl = Settings.Global.getString(res, Settings.Global.GLOBAL_HTTP_PROXY_PAC);
+ if (!TextUtils.isEmpty(host) || !TextUtils.isEmpty(pacFileUrl)) {
+ ProxyInfo proxyProperties;
+ if (!TextUtils.isEmpty(pacFileUrl)) {
+ proxyProperties = new ProxyInfo(pacFileUrl);
+ } else {
+ proxyProperties = new ProxyInfo(host, port, exclList);
+ }
+ if (!proxyProperties.isValid()) {
+ if (DBG) Slog.d(TAG, "Invalid proxy properties, ignoring: " + proxyProperties);
+ return;
+ }
+
+ synchronized (mProxyLock) {
+ mGlobalProxy = proxyProperties;
+ }
+ }
+ // TODO : shouldn't this function call mPacManager.setCurrentProxyScriptUrl ?
+ }
+
+ /**
* Sends the system broadcast informing apps about a new proxy configuration.
*
* Confusingly this method also sets the PAC file URL. TODO : separate this, it has nothing
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/display/DisplayDevice.java b/services/core/java/com/android/server/display/DisplayDevice.java
index 2405925..7bfe9ce 100644
--- a/services/core/java/com/android/server/display/DisplayDevice.java
+++ b/services/core/java/com/android/server/display/DisplayDevice.java
@@ -19,7 +19,6 @@
import android.graphics.Rect;
import android.hardware.display.DisplayViewport;
import android.os.IBinder;
-import android.view.Display;
import android.view.Surface;
import android.view.SurfaceControl;
@@ -224,6 +223,8 @@
DisplayDeviceInfo info = getDisplayDeviceInfoLocked();
viewport.deviceWidth = isRotated ? info.height : info.width;
viewport.deviceHeight = isRotated ? info.width : info.height;
+
+ viewport.uniqueId = info.uniqueId;
}
/**
diff --git a/services/core/java/com/android/server/display/DisplayDeviceInfo.java b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
index 512e851..c51dc52 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceInfo.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
@@ -110,6 +110,13 @@
public static final int FLAG_MASK_DISPLAY_CUTOUT = 1 << 11;
/**
+ * Flag: This flag identifies secondary displays that should show system decorations, such as
+ * status bar, navigation bar, home activity or IME.
+ * @hide
+ */
+ public static final int FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS = 1 << 12;
+
+ /**
* Touch attachment: Display does not receive touch.
*/
public static final int TOUCH_NONE = 0;
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 0eff7f5..e70460a 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -17,15 +17,14 @@
package com.android.server.display;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR;
+import static android.hardware.display.DisplayManager
+ .VIRTUAL_DISPLAY_FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_SECURE;
-import static android.hardware.display.DisplayManager
- .VIRTUAL_DISPLAY_FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.DumpUtils;
-import com.android.internal.util.IndentingPrintWriter;
+import static android.hardware.display.DisplayViewport.VIEWPORT_EXTERNAL;
+import static android.hardware.display.DisplayViewport.VIEWPORT_INTERNAL;
+import static android.hardware.display.DisplayViewport.VIEWPORT_VIRTUAL;
import android.Manifest;
import android.annotation.NonNull;
@@ -45,8 +44,8 @@
import android.hardware.display.Curve;
import android.hardware.display.DisplayManagerGlobal;
import android.hardware.display.DisplayManagerInternal;
-import android.hardware.display.DisplayViewport;
import android.hardware.display.DisplayManagerInternal.DisplayTransactionListener;
+import android.hardware.display.DisplayViewport;
import android.hardware.display.IDisplayManager;
import android.hardware.display.IDisplayManagerCallback;
import android.hardware.display.IVirtualDisplayCallback;
@@ -83,14 +82,17 @@
import android.view.Surface;
import android.view.SurfaceControl;
-import com.android.internal.util.Preconditions;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.DumpUtils;
+import com.android.internal.util.IndentingPrintWriter;
import com.android.server.AnimationThread;
import com.android.server.DisplayThread;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.UiThread;
-import com.android.server.wm.WindowManagerInternal;
import com.android.server.wm.SurfaceAnimationThread;
+import com.android.server.wm.WindowManagerInternal;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -256,9 +258,8 @@
// Viewports of the default display and the display that should receive touch
// input from an external source. Used by the input system.
- private final DisplayViewport mDefaultViewport = new DisplayViewport();
- private final DisplayViewport mExternalTouchViewport = new DisplayViewport();
- private final ArrayList<DisplayViewport> mVirtualTouchViewports = new ArrayList<>();
+ @GuardedBy("mSyncRoot")
+ private final ArrayList<DisplayViewport> mViewports = new ArrayList<>();
// Persistent data store for all internal settings maintained by the display manager service.
private final PersistentDataStore mPersistentDataStore = new PersistentDataStore();
@@ -272,9 +273,7 @@
// Temporary viewports, used when sending new viewport information to the
// input system. May be used outside of the lock but only on the handler thread.
- private final DisplayViewport mTempDefaultViewport = new DisplayViewport();
- private final DisplayViewport mTempExternalTouchViewport = new DisplayViewport();
- private final ArrayList<DisplayViewport> mTempVirtualTouchViewports = new ArrayList<>();
+ private final ArrayList<DisplayViewport> mTempViewports = new ArrayList<>();
// The default color mode for default displays. Overrides the usual
// Display.Display.COLOR_MODE_DEFAULT for displays with the
@@ -1255,9 +1254,7 @@
}
private void clearViewportsLocked() {
- mDefaultViewport.valid = false;
- mExternalTouchViewport.valid = false;
- mVirtualTouchViewports.clear();
+ mViewports.clear();
}
private void configureDisplayLocked(SurfaceControl.Transaction t, DisplayDevice device) {
@@ -1287,40 +1284,89 @@
}
display.configureDisplayLocked(t, device, info.state == Display.STATE_OFF);
- // Update the viewports if needed.
- if (!mDefaultViewport.valid
- && (info.flags & DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY) != 0) {
- setViewportLocked(mDefaultViewport, display, device);
+ // Update the corresponding viewport.
+ DisplayViewport internalViewport = getInternalViewportLocked();
+ if ((info.flags & DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY) != 0) {
+ populateViewportLocked(internalViewport, display, device);
}
- if (!mExternalTouchViewport.valid
- && info.touch == DisplayDeviceInfo.TOUCH_EXTERNAL) {
- setViewportLocked(mExternalTouchViewport, display, device);
+ DisplayViewport externalViewport = getExternalViewportLocked();
+ if (info.touch == DisplayDeviceInfo.TOUCH_EXTERNAL) {
+ populateViewportLocked(externalViewport, display, device);
+ } else if (!externalViewport.valid) {
+ // TODO (b/116850516) move this logic into InputReader
+ externalViewport.copyFrom(internalViewport);
+ externalViewport.type = DisplayViewport.VIEWPORT_EXTERNAL;
}
if (info.touch == DisplayDeviceInfo.TOUCH_VIRTUAL && !TextUtils.isEmpty(info.uniqueId)) {
- final DisplayViewport viewport = getVirtualTouchViewportLocked(info.uniqueId);
- setViewportLocked(viewport, display, device);
+ final DisplayViewport viewport = getVirtualViewportLocked(info.uniqueId);
+ populateViewportLocked(viewport, display, device);
}
}
- /** Gets the virtual device viewport or creates it if not yet created. */
- private DisplayViewport getVirtualTouchViewportLocked(@NonNull String uniqueId) {
+ /** Get the virtual device viewport that has the specified uniqueId.
+ * If such viewport does not exist, create it. */
+ private DisplayViewport getVirtualViewportLocked(@NonNull String uniqueId) {
DisplayViewport viewport;
- final int count = mVirtualTouchViewports.size();
+ final int count = mViewports.size();
for (int i = 0; i < count; i++) {
- viewport = mVirtualTouchViewports.get(i);
+ viewport = mViewports.get(i);
if (uniqueId.equals(viewport.uniqueId)) {
+ if (viewport.type != VIEWPORT_VIRTUAL) {
+ Slog.wtf(TAG, "Found a viewport with uniqueId '" + uniqueId
+ + "' but it has type " + DisplayViewport.typeToString(viewport.type)
+ + " (expected VIRTUAL)");
+ continue;
+ }
return viewport;
}
}
viewport = new DisplayViewport();
viewport.uniqueId = uniqueId;
- mVirtualTouchViewports.add(viewport);
+ viewport.type = VIEWPORT_VIRTUAL;
+ mViewports.add(viewport);
return viewport;
}
- private static void setViewportLocked(DisplayViewport viewport,
+ private DisplayViewport getInternalViewportLocked() {
+ return getViewportByTypeLocked(VIEWPORT_INTERNAL);
+ }
+
+ private DisplayViewport getExternalViewportLocked() {
+ return getViewportByTypeLocked(VIEWPORT_EXTERNAL);
+ }
+
+ /**
+ * Get internal or external viewport. Create it if does not currently exist.
+ * @param viewportType - either INTERNAL or EXTERNAL
+ * @return the viewport with the requested type
+ */
+ private DisplayViewport getViewportByTypeLocked(int viewportType) {
+ // Only allow a single INTERNAL or EXTERNAL viewport, which makes this function possible.
+ // TODO (b/116824030) allow multiple EXTERNAL viewports and remove this function.
+ // Creates the viewport if none exists.
+ if (viewportType != VIEWPORT_INTERNAL && viewportType != VIEWPORT_EXTERNAL) {
+ Slog.wtf(TAG, "Cannot call getViewportByTypeLocked for type "
+ + DisplayViewport.typeToString(viewportType));
+ return null;
+ }
+ DisplayViewport viewport;
+ final int count = mViewports.size();
+ for (int i = 0; i < count; i++) {
+ viewport = mViewports.get(i);
+ if (viewport.type == viewportType) {
+ return viewport;
+ }
+ }
+
+ viewport = new DisplayViewport();
+ viewport.type = viewportType;
+ mViewports.add(viewport);
+ return viewport;
+ }
+
+ private static void populateViewportLocked(DisplayViewport viewport,
LogicalDisplay display, DisplayDevice device) {
viewport.valid = true;
viewport.displayId = display.getDisplayIdLocked();
@@ -1400,9 +1446,7 @@
pw.println(" mPendingTraversal=" + mPendingTraversal);
pw.println(" mGlobalDisplayState=" + Display.stateToString(mGlobalDisplayState));
pw.println(" mNextNonDefaultDisplayId=" + mNextNonDefaultDisplayId);
- pw.println(" mDefaultViewport=" + mDefaultViewport);
- pw.println(" mExternalTouchViewport=" + mExternalTouchViewport);
- pw.println(" mVirtualTouchViewports=" + mVirtualTouchViewports);
+ pw.println(" mViewports=" + mViewports);
pw.println(" mDefaultDisplayDefaultColorMode=" + mDefaultDisplayDefaultColorMode);
pw.println(" mSingleDisplayDemoMode=" + mSingleDisplayDemoMode);
pw.println(" mWifiDisplayScanRequestCount=" + mWifiDisplayScanRequestCount);
@@ -1522,18 +1566,19 @@
break;
case MSG_UPDATE_VIEWPORT: {
+ final boolean changed;
synchronized (mSyncRoot) {
- mTempDefaultViewport.copyFrom(mDefaultViewport);
- mTempExternalTouchViewport.copyFrom(mExternalTouchViewport);
- if (!mTempVirtualTouchViewports.equals(mVirtualTouchViewports)) {
- mTempVirtualTouchViewports.clear();
- for (DisplayViewport d : mVirtualTouchViewports) {
- mTempVirtualTouchViewports.add(d.makeCopy());
- }
+ changed = !mTempViewports.equals(mViewports);
+ if (changed) {
+ mTempViewports.clear();
+ for (DisplayViewport d : mViewports) {
+ mTempViewports.add(d.makeCopy());
+ }
}
}
- mInputManagerInternal.setDisplayViewports(mTempDefaultViewport,
- mTempExternalTouchViewport, mTempVirtualTouchViewports);
+ if (changed) {
+ mInputManagerInternal.setDisplayViewports(mTempViewports);
+ }
break;
}
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index 5b7c520..6f726e6 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -18,7 +18,6 @@
import android.graphics.Rect;
import android.hardware.display.DisplayManagerInternal;
-import android.os.SystemProperties;
import android.view.Display;
import android.view.DisplayInfo;
import android.view.Surface;
@@ -256,6 +255,9 @@
if ((deviceInfo.flags & DisplayDeviceInfo.FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD) != 0) {
mBaseDisplayInfo.flags |= Display.FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD;
}
+ if ((deviceInfo.flags & DisplayDeviceInfo.FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS) != 0) {
+ mBaseDisplayInfo.flags |= Display.FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS;
+ }
Rect maskingInsets = getMaskingInsets(deviceInfo);
int maskedWidth = deviceInfo.width - maskingInsets.left - maskingInsets.right;
int maskedHeight = deviceInfo.height - maskingInsets.top - maskingInsets.bottom;
diff --git a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
index 6111c23..5aa585f 100644
--- a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
@@ -17,15 +17,14 @@
package com.android.server.display;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR;
-import static android.hardware.display.DisplayManager
- .VIRTUAL_DISPLAY_FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD;
+import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD;
+import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC;
-import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_SECURE;
-import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_SUPPORTS_TOUCH;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_ROTATES_WITH_CONTENT;
-import static android.hardware.display.DisplayManager
- .VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL;
+import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_SECURE;
+import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS;
+import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_SUPPORTS_TOUCH;
import android.content.Context;
import android.hardware.display.IVirtualDisplayCallback;
@@ -33,10 +32,10 @@
import android.media.projection.IMediaProjectionCallback;
import android.os.Handler;
import android.os.IBinder;
-import android.os.SystemProperties;
import android.os.IBinder.DeathRecipient;
import android.os.Message;
import android.os.RemoteException;
+import android.os.SystemProperties;
import android.util.ArrayMap;
import android.util.Slog;
import android.view.Display;
@@ -60,7 +59,8 @@
static final boolean DEBUG = false;
// Unique id prefix for virtual displays
- private static final String UNIQUE_ID_PREFIX = "virtual:";
+ @VisibleForTesting
+ static final String UNIQUE_ID_PREFIX = "virtual:";
private final ArrayMap<IBinder, VirtualDisplayDevice> mVirtualDisplayDevices =
new ArrayMap<IBinder, VirtualDisplayDevice>();
@@ -366,7 +366,10 @@
mInfo.flags |= DisplayDeviceInfo.FLAG_ROTATES_WITH_CONTENT;
}
if ((mFlags & VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL) != 0) {
- mInfo.flags |= DisplayDeviceInfo.FLAG_DESTROY_CONTENT_ON_REMOVAL;
+ mInfo.flags |= DisplayDeviceInfo.FLAG_DESTROY_CONTENT_ON_REMOVAL;
+ }
+ if ((mFlags & VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS) != 0) {
+ mInfo.flags |= DisplayDeviceInfo.FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS;
}
mInfo.type = Display.TYPE_VIRTUAL;
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 0f284391..4913e8b 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -16,9 +16,6 @@
package com.android.server.input;
-import static android.hardware.display.DisplayViewport.VIEWPORT_INTERNAL;
-import static android.hardware.display.DisplayViewport.VIEWPORT_EXTERNAL;
-
import android.annotation.NonNull;
import android.app.IInputForwarder;
import android.app.Notification;
@@ -188,13 +185,8 @@
private static native long nativeInit(InputManagerService service,
Context context, MessageQueue messageQueue);
private static native void nativeStart(long ptr);
- private static native void nativeSetVirtualDisplayViewports(long ptr,
+ private static native void nativeSetDisplayViewports(long ptr,
DisplayViewport[] viewports);
- private static native void nativeSetDisplayViewport(long ptr, int viewportType,
- int displayId, int rotation,
- int logicalLeft, int logicalTop, int logicalRight, int logicalBottom,
- int physicalLeft, int physicalTop, int physicalRight, int physicalBottom,
- int deviceWidth, int deviceHeight, String uniqueId);
private static native int nativeGetScanCodeState(long ptr,
int deviceId, int sourceMask, int scanCode);
@@ -217,7 +209,8 @@
private static native void nativeSetInputDispatchMode(long ptr, boolean enabled, boolean frozen);
private static native void nativeSetSystemUiVisibility(long ptr, int visibility);
private static native void nativeSetFocusedApplication(long ptr,
- InputApplicationHandle application);
+ int displayId, InputApplicationHandle application);
+ private static native void nativeSetFocusedDisplay(long ptr, int displayId);
private static native boolean nativeTransferTouchFocus(long ptr,
InputChannel fromChannel, InputChannel toChannel);
private static native void nativeSetPointerSpeed(long ptr, int speed);
@@ -409,31 +402,8 @@
nativeReloadDeviceAliases(mPtr);
}
- private void setDisplayViewportsInternal(DisplayViewport defaultViewport,
- DisplayViewport externalTouchViewport,
- List<DisplayViewport> virtualTouchViewports) {
- if (defaultViewport.valid) {
- setDisplayViewport(VIEWPORT_INTERNAL, defaultViewport);
- }
-
- if (externalTouchViewport.valid) {
- setDisplayViewport(VIEWPORT_EXTERNAL, externalTouchViewport);
- } else if (defaultViewport.valid) {
- setDisplayViewport(VIEWPORT_EXTERNAL, defaultViewport);
- }
-
- nativeSetVirtualDisplayViewports(mPtr,
- virtualTouchViewports.toArray(new DisplayViewport[0]));
- }
-
- private void setDisplayViewport(int viewportType, DisplayViewport viewport) {
- nativeSetDisplayViewport(mPtr, viewportType,
- viewport.displayId, viewport.orientation,
- viewport.logicalFrame.left, viewport.logicalFrame.top,
- viewport.logicalFrame.right, viewport.logicalFrame.bottom,
- viewport.physicalFrame.left, viewport.physicalFrame.top,
- viewport.physicalFrame.right, viewport.physicalFrame.bottom,
- viewport.deviceWidth, viewport.deviceHeight, viewport.uniqueId);
+ private void setDisplayViewportsInternal(List<DisplayViewport> viewports) {
+ nativeSetDisplayViewports(mPtr, viewports.toArray(new DisplayViewport[0]));
}
/**
@@ -1462,21 +1432,27 @@
}
}
- public void setInputWindows(InputWindowHandle[] windowHandles,
- InputWindowHandle focusedWindowHandle, int displayId) {
- final IWindow newFocusedWindow =
- focusedWindowHandle != null ? focusedWindowHandle.clientWindow : null;
- if (mFocusedWindow != newFocusedWindow) {
- mFocusedWindow = newFocusedWindow;
- if (mFocusedWindowHasCapture) {
- setPointerCapture(false);
- }
- }
+ public void setInputWindows(InputWindowHandle[] windowHandles, int displayId) {
nativeSetInputWindows(mPtr, windowHandles, displayId);
}
- public void setFocusedApplication(InputApplicationHandle application) {
- nativeSetFocusedApplication(mPtr, application);
+ public void setFocusedApplication(int displayId, InputApplicationHandle application) {
+ nativeSetFocusedApplication(mPtr, displayId, application);
+ }
+
+ public void setFocusedWindow(InputWindowHandle focusedWindowHandle) {
+ final IWindow newFocusedWindow =
+ focusedWindowHandle != null ? focusedWindowHandle.clientWindow : null;
+ if (mFocusedWindow != newFocusedWindow) {
+ if (mFocusedWindowHasCapture) {
+ setPointerCapture(false);
+ }
+ mFocusedWindow = newFocusedWindow;
+ }
+ }
+
+ public void setFocusedDisplay(int displayId) {
+ nativeSetFocusedDisplay(mPtr, displayId);
}
@Override
@@ -1491,16 +1467,18 @@
return;
}
setPointerCapture(enabled);
- try {
- mFocusedWindow.dispatchPointerCaptureChanged(enabled);
- } catch (RemoteException ex) {
- /* ignore */
- }
}
private void setPointerCapture(boolean enabled) {
- mFocusedWindowHasCapture = enabled;
- nativeSetPointerCapture(mPtr, enabled);
+ if (mFocusedWindowHasCapture != enabled) {
+ mFocusedWindowHasCapture = enabled;
+ try {
+ mFocusedWindow.dispatchPointerCaptureChanged(enabled);
+ } catch (RemoteException ex) {
+ /* ignore */
+ }
+ nativeSetPointerCapture(mPtr, enabled);
+ }
}
public void setInputDispatchMode(boolean enabled, boolean frozen) {
@@ -2203,11 +2181,8 @@
private final class LocalService extends InputManagerInternal {
@Override
- public void setDisplayViewports(DisplayViewport defaultViewport,
- DisplayViewport externalTouchViewport,
- List<DisplayViewport> virtualTouchViewports) {
- setDisplayViewportsInternal(defaultViewport, externalTouchViewport,
- virtualTouchViewports);
+ public void setDisplayViewports(List<DisplayViewport> viewports) {
+ setDisplayViewportsInternal(viewports);
}
@Override
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index 60e9eaa..3f03169 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -159,8 +159,10 @@
static final boolean LOGD = Log.isLoggable(TAG, Log.DEBUG);
static final boolean LOGV = Log.isLoggable(TAG, Log.VERBOSE);
+ // Perform polling and persist all (FLAG_PERSIST_ALL).
private static final int MSG_PERFORM_POLL = 1;
- private static final int MSG_REGISTER_GLOBAL_ALERT = 2;
+ // Perform polling, persist network, and register the global alert again.
+ private static final int MSG_PERFORM_POLL_REGISTER_ALERT = 2;
/** Flags to control detail level of poll event. */
private static final int FLAG_PERSIST_NETWORK = 0x1;
@@ -168,6 +170,14 @@
private static final int FLAG_PERSIST_ALL = FLAG_PERSIST_NETWORK | FLAG_PERSIST_UID;
private static final int FLAG_PERSIST_FORCE = 0x100;
+ /**
+ * When global alert quota is high, wait for this delay before processing each polling,
+ * and do not schedule further polls once there is already one queued.
+ * This avoids firing the global alert too often on devices with high transfer speeds and
+ * high quota.
+ */
+ private static final int PERFORM_POLL_DELAY_MS = 1000;
+
private static final String TAG_NETSTATS_ERROR = "netstats_error";
private final Context mContext;
@@ -920,7 +930,7 @@
}
// Create baseline stats
- mHandler.sendMessage(mHandler.obtainMessage(MSG_PERFORM_POLL, FLAG_PERSIST_ALL));
+ mHandler.sendMessage(mHandler.obtainMessage(MSG_PERFORM_POLL));
return normalizedRequest;
}
@@ -1055,13 +1065,12 @@
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
if (LIMIT_GLOBAL_ALERT.equals(limitName)) {
- // kick off background poll to collect network stats; UID stats
- // are handled during normal polling interval.
- final int flags = FLAG_PERSIST_NETWORK;
- mHandler.obtainMessage(MSG_PERFORM_POLL, flags, 0).sendToTarget();
-
- // re-arm global alert for next update
- mHandler.obtainMessage(MSG_REGISTER_GLOBAL_ALERT).sendToTarget();
+ // kick off background poll to collect network stats unless there is already
+ // such a call pending; UID stats are handled during normal polling interval.
+ if (!mHandler.hasMessages(MSG_PERFORM_POLL_REGISTER_ALERT)) {
+ mHandler.sendEmptyMessageDelayed(MSG_PERFORM_POLL_REGISTER_ALERT,
+ PERFORM_POLL_DELAY_MS);
+ }
}
}
};
@@ -1673,11 +1682,11 @@
public boolean handleMessage(Message msg) {
switch (msg.what) {
case MSG_PERFORM_POLL: {
- final int flags = msg.arg1;
- mService.performPoll(flags);
+ mService.performPoll(FLAG_PERSIST_ALL);
return true;
}
- case MSG_REGISTER_GLOBAL_ALERT: {
+ case MSG_PERFORM_POLL_REGISTER_ALERT: {
+ mService.performPoll(FLAG_PERSIST_NETWORK);
mService.registerGlobalAlert();
return true;
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index cade07c..f9d49d7 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -16,6 +16,7 @@
package com.android.server.notification;
+import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
import static android.app.Notification.FLAG_FOREGROUND_SERVICE;
import static android.app.NotificationManager.ACTION_APP_BLOCK_STATE_CHANGED;
import static android.app.NotificationManager.ACTION_NOTIFICATION_CHANNEL_BLOCK_STATE_CHANGED;
@@ -2001,7 +2002,8 @@
return mInternalService;
}
- private final IBinder mService = new INotificationManager.Stub() {
+ @VisibleForTesting
+ final IBinder mService = new INotificationManager.Stub() {
// Toasts
// ============================================================================
@@ -2015,21 +2017,30 @@
}
if (pkg == null || callback == null) {
- Slog.e(TAG, "Not doing toast. pkg=" + pkg + " callback=" + callback);
+ Slog.e(TAG, "Not enqueuing toast. pkg=" + pkg + " callback=" + callback);
return ;
}
- final boolean isSystemToast = isCallerSystemOrPhone() || ("android".equals(pkg));
- final boolean isPackageSuspended =
- isPackageSuspendedForUser(pkg, Binder.getCallingUid());
- if (ENABLE_BLOCKED_TOASTS && !isSystemToast &&
- (!areNotificationsEnabledForPackage(pkg, Binder.getCallingUid())
- || isPackageSuspended)) {
- Slog.e(TAG, "Suppressing toast from package " + pkg
- + (isPackageSuspended
- ? " due to package suspended by administrator."
- : " by user request."));
- return;
+ final int callingUid = Binder.getCallingUid();
+ final boolean isSystemToast = isCallerSystemOrPhone()
+ || PackageManagerService.PLATFORM_PACKAGE_NAME.equals(pkg);
+ final boolean isPackageSuspended = isPackageSuspendedForUser(pkg, callingUid);
+ final boolean notificationsDisabledForPackage = !areNotificationsEnabledForPackage(pkg,
+ callingUid);
+
+ long callingIdentity = Binder.clearCallingIdentity();
+ try {
+ final boolean appIsForeground = mActivityManager.getUidImportance(callingUid)
+ == IMPORTANCE_FOREGROUND;
+ if (ENABLE_BLOCKED_TOASTS && !isSystemToast && ((notificationsDisabledForPackage
+ && !appIsForeground) || isPackageSuspended)) {
+ Slog.e(TAG, "Suppressing toast from package " + pkg
+ + (isPackageSuspended ? " due to package suspended."
+ : " by user request."));
+ return;
+ }
+ } finally {
+ Binder.restoreCallingIdentity(callingIdentity);
}
synchronized (mToastQueue) {
@@ -4289,7 +4300,8 @@
}
// posted from app A on behalf of app A
if (isCallerSameApp(targetPkg, callingUid, userId)
- && TextUtils.equals(callingPkg, targetPkg)) {
+ && (TextUtils.equals(callingPkg, targetPkg)
+ || isCallerSameApp(callingPkg, callingUid, userId))) {
return callingUid;
}
@@ -4306,7 +4318,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);
}
/**
@@ -4326,7 +4339,7 @@
if (!isSystemNotification && !isNotificationFromListener) {
synchronized (mNotificationLock) {
if (mNotificationsByKey.get(r.sbn.getKey()) == null
- && isCallerInstantApp(pkg, callingUid, r.getUserId())) {
+ && 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
@@ -4732,7 +4745,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() {
@@ -4902,6 +4916,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")
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 e10827b..329b1da 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -8474,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.
}
@@ -16004,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 |
@@ -23097,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 a9f1b5c..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");
@@ -2731,16 +2736,20 @@
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]");
diff --git a/services/core/java/com/android/server/pm/PackageSignatures.java b/services/core/java/com/android/server/pm/PackageSignatures.java
index 471729e..6bce788 100644
--- a/services/core/java/com/android/server/pm/PackageSignatures.java
+++ b/services/core/java/com/android/server/pm/PackageSignatures.java
@@ -16,18 +16,18 @@
package com.android.server.pm;
-import com.android.internal.util.XmlUtils;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
-
import android.annotation.NonNull;
import android.content.pm.PackageParser;
import android.content.pm.PackageParser.SigningDetails.SignatureSchemeVersion;
import android.content.pm.Signature;
import android.util.Log;
+import com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
import java.io.IOException;
import java.security.cert.CertificateException;
import java.util.ArrayList;
@@ -61,23 +61,22 @@
serializer.attribute(null, "count", Integer.toString(mSigningDetails.signatures.length));
serializer.attribute(null, "schemeVersion",
Integer.toString(mSigningDetails.signatureSchemeVersion));
- writeCertsListXml(serializer, writtenSignatures, mSigningDetails.signatures, null);
+ writeCertsListXml(serializer, writtenSignatures, mSigningDetails.signatures, false);
// if we have past signer certificate information, write it out
if (mSigningDetails.pastSigningCertificates != null) {
serializer.startTag(null, "pastSigs");
serializer.attribute(null, "count",
Integer.toString(mSigningDetails.pastSigningCertificates.length));
- writeCertsListXml(
- serializer, writtenSignatures, mSigningDetails.pastSigningCertificates,
- mSigningDetails.pastSigningCertificatesFlags);
+ writeCertsListXml(serializer, writtenSignatures,
+ mSigningDetails.pastSigningCertificates, true);
serializer.endTag(null, "pastSigs");
}
serializer.endTag(null, tagName);
}
private void writeCertsListXml(XmlSerializer serializer, ArrayList<Signature> writtenSignatures,
- Signature[] signatures, int[] flags) throws IOException {
+ Signature[] signatures, boolean isPastSigs) throws IOException {
for (int i=0; i<signatures.length; i++) {
serializer.startTag(null, "cert");
final Signature sig = signatures[i];
@@ -96,8 +95,10 @@
serializer.attribute(null, "index", Integer.toString(numWritten));
serializer.attribute(null, "key", sig.toCharsString());
}
- if (flags != null) {
- serializer.attribute(null, "flags", Integer.toString(flags[i]));
+ // The flags attribute is only written for previous signatures to represent the
+ // capabilities the developer wants to grant to the previous signing certificates.
+ if (isPastSigs) {
+ serializer.attribute(null, "flags", Integer.toString(sig.getFlags()));
}
serializer.endTag(null, "cert");
}
@@ -114,6 +115,7 @@
"Error in package manager settings: <sigs> has"
+ " no count at " + parser.getPositionDescription());
XmlUtils.skipCurrentTag(parser);
+ return;
}
final int count = Integer.parseInt(countStr);
@@ -128,16 +130,11 @@
signatureSchemeVersion = Integer.parseInt(schemeVersionStr);
}
builder.setSignatureSchemeVersion(signatureSchemeVersion);
- Signature[] signatures = new Signature[count];
- int pos = readCertsListXml(parser, readSignatures, signatures, null, builder);
+ ArrayList<Signature> signatureList = new ArrayList<>();
+ int pos = readCertsListXml(parser, readSignatures, signatureList, count, false, builder);
+ Signature[] signatures = signatureList.toArray(new Signature[signatureList.size()]);
builder.setSignatures(signatures);
if (pos < count) {
- // Should never happen -- there is an error in the written
- // settings -- but if it does we don't want to generate
- // a bad array.
- Signature[] newSigs = new Signature[pos];
- System.arraycopy(signatures, 0, newSigs, 0, pos);
- builder = builder.setSignatures(newSigs);
PackageManagerService.reportSettingsProblem(Log.WARN,
"Error in package manager settings: <sigs> count does not match number of "
+ " <cert> entries" + parser.getPositionDescription());
@@ -154,9 +151,9 @@
}
private int readCertsListXml(XmlPullParser parser, ArrayList<Signature> readSignatures,
- Signature[] signatures, int[] flags, PackageParser.SigningDetails.Builder builder)
+ ArrayList<Signature> signatures, int count, boolean isPastSigs,
+ PackageParser.SigningDetails.Builder builder)
throws IOException, XmlPullParserException {
- int count = signatures.length;
int pos = 0;
int outerDepth = parser.getDepth();
@@ -174,6 +171,7 @@
if (pos < count) {
String index = parser.getAttributeValue(null, "index");
if (index != null) {
+ boolean signatureParsed = false;
try {
int idx = Integer.parseInt(index);
String key = parser.getAttributeValue(null, "key");
@@ -181,7 +179,8 @@
if (idx >= 0 && idx < readSignatures.size()) {
Signature sig = readSignatures.get(idx);
if (sig != null) {
- signatures[pos] = readSignatures.get(idx);
+ signatures.add(sig);
+ signatureParsed = true;
} else {
PackageManagerService.reportSettingsProblem(Log.WARN,
"Error in package manager settings: <cert> "
@@ -195,12 +194,15 @@
+ parser.getPositionDescription());
}
} else {
- while (readSignatures.size() <= idx) {
+ // Create the signature first to prevent adding null entries to the
+ // output List if the key value is invalid.
+ Signature sig = new Signature(key);
+ while (readSignatures.size() < idx) {
readSignatures.add(null);
}
- Signature sig = new Signature(key);
- readSignatures.set(idx, sig);
- signatures[pos] = sig;
+ readSignatures.add(sig);
+ signatures.add(sig);
+ signatureParsed = true;
}
} catch (NumberFormatException e) {
PackageManagerService.reportSettingsProblem(Log.WARN,
@@ -215,11 +217,22 @@
+ e.getMessage());
}
- if (flags != null) {
+ if (isPastSigs) {
String flagsStr = parser.getAttributeValue(null, "flags");
if (flagsStr != null) {
try {
- flags[pos] = Integer.parseInt(flagsStr);
+ int flagsValue = Integer.parseInt(flagsStr);
+ // only modify the flags if the signature of the previous signer
+ // was successfully parsed above
+ if (signatureParsed) {
+ signatures.get(signatures.size() - 1).setFlags(flagsValue);
+ } else {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Error in package manager settings: signature not "
+ + "available at index "
+ + pos + " to set flags at "
+ + parser.getPositionDescription());
+ }
} catch (NumberFormatException e) {
PackageManagerService.reportSettingsProblem(Log.WARN,
"Error in package manager settings: <cert> "
@@ -246,7 +259,7 @@
pos++;
XmlUtils.skipCurrentTag(parser);
} else if (tagName.equals("pastSigs")) {
- if (flags == null) {
+ if (!isPastSigs) {
// we haven't encountered pastSigs yet, go ahead
String countStr = parser.getAttributeValue(null, "count");
if (countStr == null) {
@@ -254,32 +267,23 @@
"Error in package manager settings: <pastSigs> has"
+ " no count at " + parser.getPositionDescription());
XmlUtils.skipCurrentTag(parser);
+ continue;
}
try {
final int pastSigsCount = Integer.parseInt(countStr);
- Signature[] pastSignatures = new Signature[pastSigsCount];
- int[] pastSignaturesFlags = new int[pastSigsCount];
- int pastSigsPos = readCertsListXml(parser, readSignatures, pastSignatures,
- pastSignaturesFlags, builder);
- builder = builder
- .setPastSigningCertificates(pastSignatures)
- .setPastSigningCertificatesFlags(pastSignaturesFlags);
+ ArrayList<Signature> pastSignatureList = new ArrayList<>();
+ int pastSigsPos = readCertsListXml(parser, readSignatures,
+ pastSignatureList,
+ pastSigsCount, true, builder);
+ Signature[] pastSignatures = pastSignatureList.toArray(
+ new Signature[pastSignatureList.size()]);
+ builder = builder.setPastSigningCertificates(pastSignatures);
if (pastSigsPos < pastSigsCount) {
- // Should never happen -- there is an error in the written
- // settings -- but if it does we don't want to generate
- // a bad array.
- Signature[] newSigs = new Signature[pastSigsPos];
- System.arraycopy(pastSignatures, 0, newSigs, 0, pastSigsPos);
- int[] newFlags = new int[pastSigsPos];
- System.arraycopy(pastSignaturesFlags, 0, newFlags, 0, pastSigsPos);
- builder = builder
- .setPastSigningCertificates(newSigs)
- .setPastSigningCertificatesFlags(newFlags);
PackageManagerService.reportSettingsProblem(Log.WARN,
"Error in package manager settings: <pastSigs> count does not "
- + "match number of <cert> entries "
- + parser.getPositionDescription());
+ + "match number of <cert> entries "
+ + parser.getPositionDescription());
}
} catch (NumberFormatException e) {
PackageManagerService.reportSettingsProblem(Log.WARN,
@@ -326,7 +330,8 @@
buf.append(Integer.toHexString(
mSigningDetails.pastSigningCertificates[i].hashCode()));
buf.append(" flags: ");
- buf.append(Integer.toHexString(mSigningDetails.pastSigningCertificatesFlags[i]));
+ buf.append(
+ Integer.toHexString(mSigningDetails.pastSigningCertificates[i].getFlags()));
}
}
buf.append("]}");
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 910ea73..1f05dc9 100644
--- a/services/core/java/com/android/server/pm/dex/ArtManagerService.java
+++ b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
@@ -16,6 +16,8 @@
package com.android.server.pm.dex;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.AppOpsManager;
import android.content.Context;
@@ -57,8 +59,6 @@
import dalvik.system.VMRuntime;
import libcore.io.IoUtils;
-import libcore.util.NonNull;
-import libcore.util.Nullable;
import java.io.File;
import java.io.FileNotFoundException;
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/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 2557f46..21bf488 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -1655,11 +1655,9 @@
}
final boolean keyguardShowing = isKeyguardShowingAndNotOccluded();
mGlobalActions.showDialog(keyguardShowing, isDeviceProvisioned());
- if (keyguardShowing) {
- // since it took two seconds of long press to bring this up,
- // poke the wake lock so they have some time to see the dialog.
- mPowerManager.userActivity(SystemClock.uptimeMillis(), false);
- }
+ // since it took two seconds of long press to bring this up,
+ // poke the wake lock so they have some time to see the dialog.
+ mPowerManager.userActivity(SystemClock.uptimeMillis(), false);
}
boolean isDeviceProvisioned() {
@@ -4777,6 +4775,7 @@
pf.bottom = df.bottom = of.bottom = displayFrames.mUnrestricted.bottom;
// ...with content insets above the nav bar
cf.bottom = vf.bottom = displayFrames.mStable.bottom;
+ // TODO (b/111364446): Support showing IME on non-default displays
if (mStatusBar != null && mFocusedWindow == mStatusBar && canReceiveInput(mStatusBar)) {
// The status bar forces the navigation bar while it's visible. Make sure the IME
// avoids the navigation bar in that case.
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 9f6b3dd..b3f2a27 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -4404,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);
diff --git a/services/core/java/com/android/server/security/VerityUtils.java b/services/core/java/com/android/server/security/VerityUtils.java
index 9f69702..8070f3a 100644
--- a/services/core/java/com/android/server/security/VerityUtils.java
+++ b/services/core/java/com/android/server/security/VerityUtils.java
@@ -28,40 +28,74 @@
import android.util.apk.ApkSignatureVerifier;
import android.util.apk.ByteBufferFactory;
import android.util.apk.SignatureNotFoundException;
+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,9 +115,9 @@
}
/**
- * {@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.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 c6e6449..97992cf 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -15,6 +15,8 @@
*/
package com.android.server.stats;
+import static com.android.internal.util.Preconditions.checkNotNull;
+
import android.annotation.Nullable;
import android.app.ActivityManagerInternal;
import android.app.AlarmManager;
@@ -47,6 +49,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 +66,14 @@
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 android.util.proto.ProtoOutputStream;
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 +85,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 +104,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 +133,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 +182,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
@@ -1245,6 +1259,123 @@
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 pullPowerProfile(
+ int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
+ PowerProfile powerProfile = new PowerProfile(mContext);
+ checkNotNull(powerProfile);
+
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos,
+ wallClockNanos);
+ ProtoOutputStream proto = new ProtoOutputStream();
+ powerProfile.writeToProto(proto);
+ proto.flush();
+ e.writeStorage(proto.getBytes());
+ pulledData.add(e);
+ }
+
+ 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.
*/
@@ -1358,6 +1489,18 @@
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;
+ }
+ case StatsLog.POWER_PROFILE: {
+ pullPowerProfile(tagId, elapsedNanos, wallClockNanos, ret);
+ break;
+ }
default:
Slog.w(TAG, "No such tagId data as " + tagId);
return null;
@@ -1368,13 +1511,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
@@ -1419,7 +1562,8 @@
public void onStart() {
mStatsCompanionService = new StatsCompanionService(getContext());
try {
- publishBinderService(Context.STATS_COMPANION_SERVICE, mStatsCompanionService);
+ publishBinderService(Context.STATS_COMPANION_SERVICE,
+ mStatsCompanionService);
if (DEBUG) Slog.d(TAG, "Published " + Context.STATS_COMPANION_SERVICE);
} catch (Exception e) {
Slog.e(TAG, "Failed to publishBinderService", e);
@@ -1444,18 +1588,22 @@
}
/**
- * Tells statsd that statscompanion is ready. If the binder call returns, link to statsd.
+ * Tells statsd that statscompanion is ready. If the binder call returns, link to
+ * statsd.
*/
private void sayHiToStatsd() {
synchronized (sStatsdLock) {
if (sStatsd != null) {
Slog.e(TAG, "Trying to fetch statsd, but it was already fetched",
- new IllegalStateException("sStatsd is not null when being fetched"));
+ new IllegalStateException(
+ "sStatsd is not null when being fetched"));
return;
}
sStatsd = fetchStatsdService();
if (sStatsd == null) {
- Slog.i(TAG, "Could not yet find statsd to tell it that StatsCompanion is alive.");
+ Slog.i(TAG,
+ "Could not yet find statsd to tell it that StatsCompanion is "
+ + "alive.");
return;
}
if (DEBUG) Slog.d(TAG, "Saying hi to statsd");
@@ -1473,10 +1621,12 @@
filter.addAction(Intent.ACTION_PACKAGE_ADDED);
filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
filter.addDataScheme("package");
- mContext.registerReceiverAsUser(mAppUpdateReceiver, UserHandle.ALL, filter, null,
+ mContext.registerReceiverAsUser(mAppUpdateReceiver, UserHandle.ALL, filter,
+ null,
null);
- // Setup receiver for user initialize (which happens once for a new user) and
+ // Setup receiver for user initialize (which happens once for a new user)
+ // and
// if a user is removed.
filter = new IntentFilter(Intent.ACTION_USER_INITIALIZE);
filter.addAction(Intent.ACTION_USER_REMOVED);
@@ -1490,7 +1640,8 @@
mShutdownEventReceiver, UserHandle.ALL, filter, null, null);
final long token = Binder.clearCallingIdentity();
try {
- // Pull the latest state of UID->app name, version mapping when statsd starts.
+ // Pull the latest state of UID->app name, version mapping when
+ // statsd starts.
informAllUidsLocked(mContext);
} finally {
restoreCallingIdentity(token);
@@ -1552,7 +1703,8 @@
if (!DumpUtils.checkDumpPermission(mContext, TAG, writer)) return;
synchronized (sStatsdLock) {
- writer.println("Number of configuration files deleted: " + mDeletedFiles.size());
+ writer.println(
+ "Number of configuration files deleted: " + mDeletedFiles.size());
if (mDeletedFiles.size() > 0) {
writer.println(" timestamp, deleted file name");
}
@@ -1560,7 +1712,8 @@
SystemClock.currentThreadTimeMillis() - SystemClock.elapsedRealtime();
for (Long elapsedMillis : mDeletedFiles.keySet()) {
long deletionMillis = lastBootMillis + elapsedMillis;
- writer.println(" " + deletionMillis + ", " + mDeletedFiles.get(elapsedMillis));
+ writer.println(
+ " " + deletionMillis + ", " + mDeletedFiles.get(elapsedMillis));
}
}
}
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index e449111..74922f6 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -1043,7 +1043,8 @@
// Do not send the windows if there is no current focus as
// the window manager is still looking for where to put it.
// We will do the work when we get a focus change callback.
- if (mService.mCurrentFocus == null) {
+ // TODO(b/112273690): Support multiple displays
+ if (mService.getDefaultDisplayContentLocked().mCurrentFocus == null) {
return;
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index bcf9212..db0bd4f 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -33,6 +33,7 @@
import android.util.SparseIntArray;
import com.android.internal.app.IVoiceInteractor;
+import com.android.server.am.ActivityServiceConnectionsHolder;
import com.android.server.am.PendingIntentRecord;
import com.android.server.am.SafeActivityOptions;
import com.android.server.am.TaskRecord;
@@ -332,4 +333,7 @@
int callingUid, int userId, IBinder token, String resultWho,
int requestCode, Intent[] intents, String[] resolvedTypes, int flags,
Bundle bOptions);
+
+ /** @return the service connection holder for a given activity token. */
+ public abstract ActivityServiceConnectionsHolder getServiceConnectionsHolder(IBinder token);
}
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/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index d73606f..a9d0978 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -77,6 +77,7 @@
import static com.android.server.wm.AppTransitionProto.LAST_USED_APP_TRANSITION;
import android.annotation.DrawableRes;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.content.ComponentName;
@@ -93,6 +94,7 @@
import android.graphics.drawable.Drawable;
import android.os.Binder;
import android.os.Debug;
+import android.os.Handler;
import android.os.IBinder;
import android.os.IRemoteCallback;
import android.os.RemoteException;
@@ -120,8 +122,8 @@
import com.android.internal.R;
import com.android.internal.util.DumpUtils.Dump;
+import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.AttributeCache;
-import com.android.server.wm.WindowManagerService.H;
import com.android.server.wm.animation.ClipRectLRAnimation;
import com.android.server.wm.animation.ClipRectTBAnimation;
import com.android.server.wm.animation.CurvedTranslateAnimation;
@@ -252,9 +254,13 @@
private RemoteAnimationController mRemoteAnimationController;
+ final Handler mHandler;
+ final Runnable mHandleAppTransitionTimeoutRunnable = () -> handleAppTransitionTimeout();
+
AppTransition(Context context, WindowManagerService service) {
mContext = context;
mService = service;
+ mHandler = new Handler(service.mH.getLooper());
mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
com.android.internal.R.interpolator.linear_out_slow_in);
mFastOutLinearInInterpolator = AnimationUtils.loadInterpolator(context,
@@ -1349,7 +1355,8 @@
@Override
public void onAnimationEnd(Animation animation) {
- mService.mH.obtainMessage(H.DO_ANIMATION_CALLBACK, callback).sendToTarget();
+ mHandler.sendMessage(PooledLambda.obtainMessage(
+ AppTransition::doAnimationCallback, callback));
}
@Override
@@ -1756,7 +1763,7 @@
void postAnimationCallback() {
if (mNextAppTransitionCallback != null) {
- mService.mH.sendMessage(mService.mH.obtainMessage(H.DO_ANIMATION_CALLBACK,
+ mHandler.sendMessage(PooledLambda.obtainMessage(AppTransition::doAnimationCallback,
mNextAppTransitionCallback));
mNextAppTransitionCallback = null;
}
@@ -1869,7 +1876,7 @@
clear();
mNextAppTransitionType = NEXT_TRANSIT_TYPE_REMOTE;
mRemoteAnimationController = new RemoteAnimationController(mService,
- remoteAnimationAdapter, mService.mH);
+ remoteAnimationAdapter, mHandler);
}
}
@@ -2162,8 +2169,8 @@
}
boolean prepared = prepare();
if (isTransitionSet()) {
- mService.mH.removeMessages(H.APP_TRANSITION_TIMEOUT);
- mService.mH.sendEmptyMessageDelayed(H.APP_TRANSITION_TIMEOUT, APP_TRANSITION_TIMEOUT_MS);
+ removeAppTransitionTimeoutCallbacks();
+ mHandler.postDelayed(mHandleAppTransitionTimeoutRunnable, APP_TRANSITION_TIMEOUT_MS);
}
return prepared;
}
@@ -2208,4 +2215,32 @@
return mGridLayoutRecentsEnabled
|| orientation == Configuration.ORIENTATION_PORTRAIT;
}
+
+ private void handleAppTransitionTimeout() {
+ synchronized (mService.mWindowMap) {
+ if (isTransitionSet() || !mService.mOpeningApps.isEmpty()
+ || !mService.mClosingApps.isEmpty()) {
+ if (DEBUG_APP_TRANSITIONS) {
+ Slog.v(TAG_WM, "*** APP TRANSITION TIMEOUT."
+ + " isTransitionSet()="
+ + mService.mAppTransition.isTransitionSet()
+ + " mOpeningApps.size()=" + mService.mOpeningApps.size()
+ + " mClosingApps.size()=" + mService.mClosingApps.size());
+ }
+ setTimeout();
+ mService.mWindowPlacerLocked.performSurfacePlacement();
+ }
+ }
+ }
+
+ private static void doAnimationCallback(@NonNull IRemoteCallback callback) {
+ try {
+ ((IRemoteCallback) callback).sendResult(null);
+ } catch (RemoteException e) {
+ }
+ }
+
+ void removeAppTransitionTimeoutCallbacks() {
+ mHandler.removeCallbacks(mHandleAppTransitionTimeoutRunnable);
+ }
}
diff --git a/services/core/java/com/android/server/wm/AppWindowContainerController.java b/services/core/java/com/android/server/wm/AppWindowContainerController.java
index 36280dd..330c54c 100644
--- a/services/core/java/com/android/server/wm/AppWindowContainerController.java
+++ b/services/core/java/com/android/server/wm/AppWindowContainerController.java
@@ -407,8 +407,7 @@
if (mService.mAppTransition.getAppTransition()
== WindowManager.TRANSIT_TASK_OPEN_BEHIND) {
// We're launchingBehind, add the launching activity to mOpeningApps.
- final WindowState win =
- mService.getDefaultDisplayContentLocked().findFocusedWindow();
+ final WindowState win = mContainer.getDisplayContent().findFocusedWindow();
if (win != null) {
final AppWindowToken focusedToken = win.mAppToken;
if (focusedToken != null) {
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 6da9f10..e57fea3 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -679,11 +679,12 @@
removed = true;
stopFreezingScreen(true, true);
- if (mService.mFocusedApp == this) {
- if (DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM, "Removing focused app token:" + this);
- mService.mFocusedApp = null;
+ final DisplayContent dc = getDisplayContent();
+ if (dc.mFocusedApp == this) {
+ if (DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM, "Removing focused app token:" + this
+ + " displayId=" + dc.getDisplayId());
+ dc.setFocusedApp(null);
mService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, true /*updateInputWindows*/);
- getDisplayContent().getInputMonitor().setFocusedAppLw(null);
}
if (!delayed) {
@@ -1064,6 +1065,18 @@
}
}
+ @Override
+ void onDisplayChanged(DisplayContent dc) {
+ DisplayContent prevDc = mDisplayContent;
+ super.onDisplayChanged(dc);
+ if (prevDc != null && prevDc.mFocusedApp == this) {
+ prevDc.setFocusedApp(null);
+ if (dc.getTopStack().getTopChild().getTopChild() == this) {
+ dc.setFocusedApp(this);
+ }
+ }
+ }
+
/**
* Freezes the task bounds. The size of this task reported the app will be fixed to the bounds
* freezed by {@link Task#prepareFreezingBounds} until {@link #unfreezeBounds} gets called, even
@@ -1632,17 +1645,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 +1652,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 236982f..6f728fc 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -70,6 +70,7 @@
import static com.android.server.wm.DisplayContentProto.DISPLAY_INFO;
import static com.android.server.wm.DisplayContentProto.DOCKED_STACK_DIVIDER_CONTROLLER;
import static com.android.server.wm.DisplayContentProto.DPI;
+import static com.android.server.wm.DisplayContentProto.FOCUSED_APP;
import static com.android.server.wm.DisplayContentProto.ID;
import static com.android.server.wm.DisplayContentProto.IME_WINDOWS;
import static com.android.server.wm.DisplayContentProto.PINNED_STACK_CONTROLLER;
@@ -98,12 +99,17 @@
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerService.CUSTOM_SCREEN_ROTATION;
+import static com.android.server.wm.WindowManagerService.H.REPORT_FOCUS_CHANGE;
+import static com.android.server.wm.WindowManagerService.H.REPORT_LOSING_FOCUS;
import static com.android.server.wm.WindowManagerService.H.SEND_NEW_CONFIGURATION;
import static com.android.server.wm.WindowManagerService.H.UPDATE_DOCKED_STACK_DIVIDER;
import static com.android.server.wm.WindowManagerService.H.WINDOW_HIDE_TIMEOUT;
import static com.android.server.wm.WindowManagerService.LAYOUT_REPEAT_THRESHOLD;
import static com.android.server.wm.WindowManagerService.MAX_ANIMATION_DURATION;
import static com.android.server.wm.WindowManagerService.SEAMLESS_ROTATION_TIMEOUT_DURATION;
+import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_PLACING_SURFACES;
+import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_REMOVING_FOCUS;
+import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_ASSIGN_LAYERS;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES;
import static com.android.server.wm.WindowManagerService.WINDOWS_FREEZING_SCREENS_ACTIVE;
import static com.android.server.wm.WindowManagerService.WINDOWS_FREEZING_SCREENS_TIMEOUT;
@@ -114,7 +120,6 @@
import static com.android.server.wm.WindowStateAnimator.DRAW_PENDING;
import static com.android.server.wm.WindowStateAnimator.READY_TO_SHOW;
import static com.android.server.wm.WindowSurfacePlacer.SET_WALLPAPER_MAY_CHANGE;
-import static com.android.server.wm.utils.CoordinateTransforms.transformPhysicalToLogicalCoordinates;
import android.annotation.CallSuper;
import android.annotation.NonNull;
@@ -152,6 +157,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ToBooleanFunction;
import com.android.server.policy.WindowManagerPolicy;
+import com.android.server.wm.utils.DisplayRotationUtil;
import com.android.server.wm.utils.RotationCache;
import com.android.server.wm.utils.WmDisplayCutout;
@@ -334,6 +340,7 @@
private final Matrix mTmpMatrix = new Matrix();
private final Region mTmpRegion = new Region();
+
/** Used for handing back size of display */
private final Rect mTmpBounds = new Rect();
@@ -371,6 +378,36 @@
private final SurfaceSession mSession = new SurfaceSession();
/**
+ * Window that is currently interacting with the user. This window is responsible for receiving
+ * key events and pointer events from the user.
+ */
+ WindowState mCurrentFocus = null;
+
+ /**
+ * The last focused window that we've notified the client that the focus is changed.
+ */
+ WindowState mLastFocus = null;
+
+ /**
+ * Windows that have lost input focus and are waiting for the new focus window to be displayed
+ * before they are told about this.
+ */
+ ArrayList<WindowState> mLosingFocus = new ArrayList<>();
+
+ /**
+ * The foreground app of this display. Windows below this app cannot be the focused window. If
+ * the user taps on the area outside of the task of the focused app, we will notify AM about the
+ * new task the user wants to interact with.
+ */
+ AppWindowToken mFocusedApp = null;
+
+ /** Windows added since {@link #mCurrentFocus} was set to null. Used for ANR blaming. */
+ final ArrayList<WindowState> mWinAddedSinceNullFocus = new ArrayList<>();
+
+ /** Windows removed since {@link #mCurrentFocus} was set to null. Used for ANR blaming. */
+ final ArrayList<WindowState> mWinRemovedSinceNullFocus = new ArrayList<>();
+
+ /**
* We organize all top-level Surfaces in to the following layers.
* mOverlayLayer contains a few Surfaces which are always on top of others
* and omitted from Screen-Magnification, for example the strict mode flash or
@@ -412,6 +449,8 @@
/** Caches the value whether told display manager that we have content. */
private boolean mLastHasContent;
+ private DisplayRotationUtil mRotationUtil = new DisplayRotationUtil();
+
/**
* The input method window for this display.
*/
@@ -439,36 +478,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) {
@@ -490,7 +505,7 @@
};
private final ToBooleanFunction<WindowState> mFindFocusedWindow = w -> {
- final AppWindowToken focusedApp = mService.mFocusedApp;
+ final AppWindowToken focusedApp = mFocusedApp;
if (DEBUG_FOCUS) Slog.v(TAG_WM, "Looking for focus: " + w
+ ", flags=" + w.mAttrs.flags + ", canReceive=" + w.canReceiveKeys());
@@ -649,8 +664,6 @@
final boolean obscuredChanged = w.mObscured !=
mTmpApplySurfaceChangesTransactionState.obscured;
final RootWindowContainer root = mService.mRoot;
- // Only used if default window
- final boolean someoneLosingFocus = !mService.mLosingFocus.isEmpty();
// Update effect.
w.mObscured = mTmpApplySurfaceChangesTransactionState.obscured;
@@ -741,9 +754,8 @@
}
}
- if (isDefaultDisplay && someoneLosingFocus && w == mService.mCurrentFocus
- && w.isDisplayedLw()) {
- mTmpApplySurfaceChangesTransactionState.focusDisplayed = true;
+ if (!mLosingFocus.isEmpty() && w.isFocused() && w.isDisplayedLw()) {
+ mService.mH.obtainMessage(REPORT_LOSING_FOCUS, this).sendToTarget();
}
w.updateResizingWindowIfNeeded();
@@ -1367,21 +1379,12 @@
cutout, mInitialDisplayWidth, mInitialDisplayHeight);
}
final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270);
- final List<Rect> bounds = WmDisplayCutout.computeSafeInsets(
+ final Rect[] newBounds = mRotationUtil.getRotatedBounds(
+ WmDisplayCutout.computeSafeInsets(
cutout, mInitialDisplayWidth, mInitialDisplayHeight)
- .getDisplayCutout().getBoundingRects();
- transformPhysicalToLogicalCoordinates(rotation, mInitialDisplayWidth, mInitialDisplayHeight,
- mTmpMatrix);
- final Region region = Region.obtain();
- for (int i = 0; i < bounds.size(); i++) {
- final Rect rect = bounds.get(i);
- final RectF rectF = new RectF(bounds.get(i));
- mTmpMatrix.mapRect(rectF);
- rectF.round(rect);
- region.op(rect, Op.UNION);
- }
-
- return WmDisplayCutout.computeSafeInsets(DisplayCutout.fromBounds(region),
+ .getDisplayCutout().getBoundingRectsAll(),
+ rotation, mInitialDisplayWidth, mInitialDisplayHeight);
+ return WmDisplayCutout.computeSafeInsets(DisplayCutout.fromBounds(newBounds),
rotated ? mInitialDisplayHeight : mInitialDisplayWidth,
rotated ? mInitialDisplayWidth : mInitialDisplayHeight);
}
@@ -2094,22 +2097,22 @@
return null;
}
- void setTouchExcludeRegion(Task focusedTask) {
- // The provided task is the task on this display with focus, so if WindowManagerService's
- // focused app is not on this display, focusedTask will be null.
+ void updateTouchExcludeRegion() {
+ final Task focusedTask = (mFocusedApp != null ? mFocusedApp.getTask() : null);
if (focusedTask == null) {
mTouchExcludeRegion.setEmpty();
} else {
mTouchExcludeRegion.set(mBaseDisplayRect);
final int delta = dipToPixel(RESIZE_HANDLE_WIDTH_IN_DP, mDisplayMetrics);
mTmpRect2.setEmpty();
- for (int stackNdx = mTaskStackContainers.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+ for (int stackNdx = mTaskStackContainers.getChildCount() - 1; stackNdx >= 0;
+ --stackNdx) {
final TaskStack stack = mTaskStackContainers.getChildAt(stackNdx);
stack.setTouchExcludeRegion(focusedTask, delta, mTouchExcludeRegion,
mDisplayFrames.mContent, mTmpRect2);
}
// If we removed the focused task above, add it back and only leave its
- // outside touch area in the exclusion. TapDectector is not interested in
+ // outside touch area in the exclusion. TapDetector is not interested in
// any touch inside the focused task itself.
if (!mTmpRect2.isEmpty()) {
mTouchExcludeRegion.op(mTmpRect2, Region.Op.UNION);
@@ -2120,12 +2123,7 @@
// events to be intercepted and used to change focus. This would likely cause a
// disappearance of the input method.
mInputMethodWindow.getTouchableRegion(mTmpRegion);
- if (mInputMethodWindow.getDisplayId() == mDisplayId) {
- mTouchExcludeRegion.op(mTmpRegion, Op.UNION);
- } else {
- // IME is on a different display, so we need to update its tap detector.
- setTouchExcludeRegion(null /* focusedTask */);
- }
+ mTouchExcludeRegion.op(mTmpRegion, Op.UNION);
}
for (int i = mTapExcludedWindows.size() - 1; i >= 0; i--) {
final WindowState win = mTapExcludedWindows.get(i);
@@ -2307,21 +2305,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);
@@ -2411,6 +2394,9 @@
}
mDisplayFrames.writeToProto(proto, DISPLAY_FRAMES);
proto.write(SURFACE_SIZE, mSurfaceSize);
+ if (mFocusedApp != null) {
+ mFocusedApp.writeNameToProto(proto, FOCUSED_APP);
+ }
proto.end(token);
}
@@ -2451,6 +2437,27 @@
pw.print(prefix);
pw.print("mDeferredRotationPauseCount="); pw.println(mDeferredRotationPauseCount);
+ pw.print(" mCurrentFocus="); pw.println(mCurrentFocus);
+ if (mLastFocus != mCurrentFocus) {
+ pw.print(" mLastFocus="); pw.println(mLastFocus);
+ }
+ if (mLosingFocus.size() > 0) {
+ pw.println();
+ pw.println(" Windows losing focus:");
+ for (int i = mLosingFocus.size() - 1; i >= 0; i--) {
+ final WindowState w = mLosingFocus.get(i);
+ pw.print(" Losing #"); pw.print(i); pw.print(' ');
+ pw.print(w);
+ if (dumpAll) {
+ pw.println(":");
+ w.dump(pw, " ", true);
+ } else {
+ pw.println();
+ }
+ }
+ }
+ pw.print(" mFocusedApp="); pw.println(mFocusedApp);
+
pw.println();
pw.println(prefix + "Application tokens in top down Z order:");
for (int stackNdx = mTaskStackContainers.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
@@ -2582,6 +2589,132 @@
return mTmpWindow;
}
+
+ /**
+ * Update the focused window and make some adjustments if the focus has changed.
+ *
+ * @param mode Indicates the situation we are in. Possible modes are:
+ * {@link WindowManagerService#UPDATE_FOCUS_NORMAL},
+ * {@link WindowManagerService#UPDATE_FOCUS_PLACING_SURFACES},
+ * {@link WindowManagerService#UPDATE_FOCUS_WILL_PLACE_SURFACES},
+ * {@link WindowManagerService#UPDATE_FOCUS_REMOVING_FOCUS}
+ * @param updateInputWindows Whether to sync the window information to the input module.
+ * @return {@code true} if the focused window has changed.
+ */
+ boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows, boolean focusFound) {
+ final WindowState newFocus = findFocusedWindow();
+ if (mCurrentFocus == newFocus) {
+ return false;
+ }
+ mService.mH.obtainMessage(REPORT_FOCUS_CHANGE, this).sendToTarget();
+ boolean imWindowChanged = false;
+ // TODO (b/111080190): Multi-Session IME
+ if (!focusFound) {
+ final WindowState imWindow = mInputMethodWindow;
+ if (imWindow != null) {
+ final WindowState prevTarget = mService.mInputMethodTarget;
+
+ final WindowState newTarget = computeImeTarget(true /* updateImeTarget*/);
+ imWindowChanged = prevTarget != newTarget;
+
+ if (mode != UPDATE_FOCUS_WILL_ASSIGN_LAYERS
+ && mode != UPDATE_FOCUS_WILL_PLACE_SURFACES) {
+ assignWindowLayers(false /* setLayoutNeeded */);
+ }
+ }
+ }
+
+ if (imWindowChanged) {
+ mService.mWindowsChanged = true;
+ setLayoutNeeded();
+ }
+
+ if (DEBUG_FOCUS_LIGHT || mService.localLOGV) Slog.v(TAG_WM, "Changing focus from "
+ + mCurrentFocus + " to " + newFocus + " displayId=" + getDisplayId()
+ + " Callers=" + Debug.getCallers(4));
+ final WindowState oldFocus = mCurrentFocus;
+ mCurrentFocus = newFocus;
+ mLosingFocus.remove(newFocus);
+
+ if (newFocus != null) {
+ mWinAddedSinceNullFocus.clear();
+ mWinRemovedSinceNullFocus.clear();
+
+ if (newFocus.canReceiveKeys()) {
+ // Displaying a window implicitly causes dispatching to be unpaused.
+ // This is to protect against bugs if someone pauses dispatching but
+ // forgets to resume.
+ newFocus.mToken.paused = false;
+ }
+ }
+
+ // System UI is only shown on the default display.
+ int focusChanged = isDefaultDisplay
+ ? mService.mPolicy.focusChangedLw(oldFocus, newFocus) : 0;
+
+ if (imWindowChanged && oldFocus != mInputMethodWindow) {
+ // Focus of the input method window changed. Perform layout if needed.
+ if (mode == UPDATE_FOCUS_PLACING_SURFACES) {
+ performLayout(true /*initial*/, updateInputWindows);
+ focusChanged &= ~FINISH_LAYOUT_REDO_LAYOUT;
+ } else if (mode == UPDATE_FOCUS_WILL_PLACE_SURFACES) {
+ // Client will do the layout, but we need to assign layers
+ // for handleNewWindowLocked() below.
+ assignWindowLayers(false /* setLayoutNeeded */);
+ }
+ }
+
+ if ((focusChanged & FINISH_LAYOUT_REDO_LAYOUT) != 0) {
+ // The change in focus caused us to need to do a layout. Okay.
+ setLayoutNeeded();
+ if (mode == UPDATE_FOCUS_PLACING_SURFACES) {
+ performLayout(true /*initial*/, updateInputWindows);
+ } else if (mode == UPDATE_FOCUS_REMOVING_FOCUS) {
+ mService.mRoot.performSurfacePlacement(false);
+ }
+ }
+
+ if (mode != UPDATE_FOCUS_WILL_ASSIGN_LAYERS) {
+ // If we defer assigning layers, then the caller is responsible for doing this part.
+ getInputMonitor().setInputFocusLw(newFocus, updateInputWindows);
+ }
+
+ adjustForImeIfNeeded();
+
+ // We may need to schedule some toast windows to be removed. The toasts for an app that
+ // does not have input focus are removed within a timeout to prevent apps to redress
+ // other apps' UI.
+ scheduleToastWindowsTimeoutIfNeededLocked(oldFocus, newFocus);
+
+ if (mode == UPDATE_FOCUS_PLACING_SURFACES) {
+ pendingLayoutChanges |= FINISH_LAYOUT_REDO_ANIM;
+ }
+ return true;
+ }
+
+ /**
+ * Set the new focused app to this display.
+ *
+ * @param newFocus the new focused AppWindowToken.
+ * @return true if the focused app is changed.
+ */
+ boolean setFocusedApp(AppWindowToken newFocus) {
+ if (newFocus != null) {
+ final DisplayContent appDisplay = newFocus.getDisplayContent();
+ if (appDisplay != this) {
+ throw new IllegalStateException(newFocus + " is not on " + getName()
+ + " but " + ((appDisplay != null) ? appDisplay.getName() : "none"));
+ }
+ }
+ if (mFocusedApp == newFocus) {
+ return false;
+ }
+ mFocusedApp = newFocus;
+ getInputMonitor().setFocusedAppLw(newFocus);
+ updateTouchExcludeRegion();
+ return true;
+ }
+
/** Updates the layer assignment of windows on this display. */
void assignWindowLayers(boolean setLayoutNeeded) {
Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "assignWindowLayers");
@@ -2746,22 +2879,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;
}
}
}
@@ -2934,26 +3058,16 @@
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 isInputMethodClientFocus(int uid, int pid) {
@@ -2964,8 +3078,8 @@
if (DEBUG_INPUT_METHOD) {
Slog.i(TAG_WM, "Desired input method target: " + imFocus);
- Slog.i(TAG_WM, "Current focus: " + mService.mCurrentFocus);
- Slog.i(TAG_WM, "Last focus: " + mService.mLastFocus);
+ Slog.i(TAG_WM, "Current focus: " + mCurrentFocus + " displayId=" + mDisplayId);
+ Slog.i(TAG_WM, "Last focus: " + mLastFocus + " displayId=" + mDisplayId);
}
if (DEBUG_INPUT_METHOD) {
@@ -3033,7 +3147,7 @@
}
// TODO: Super crazy long method that should be broken down...
- boolean applySurfaceChangesTransaction(boolean recoveringMemory) {
+ void applySurfaceChangesTransaction(boolean recoveringMemory) {
final int dw = mDisplayInfo.logicalWidth;
final int dh = mDisplayInfo.logicalHeight;
@@ -3117,8 +3231,6 @@
// can now be shown.
atoken.updateAllDrawn();
}
-
- return mTmpApplySurfaceChangesTransactionState.focusDisplayed;
}
private void updateBounds() {
@@ -3372,7 +3484,6 @@
boolean displayHasContent;
boolean obscured;
boolean syswin;
- boolean focusDisplayed;
float preferredRefreshRate;
int preferredModeId;
@@ -3380,7 +3491,6 @@
displayHasContent = false;
obscured = false;
syswin = false;
- focusDisplayed = false;
preferredRefreshRate = 0;
preferredModeId = 0;
}
diff --git a/services/core/java/com/android/server/wm/DisplayWindowController.java b/services/core/java/com/android/server/wm/DisplayWindowController.java
index 76b6dbe..ab87759 100644
--- a/services/core/java/com/android/server/wm/DisplayWindowController.java
+++ b/services/core/java/com/android/server/wm/DisplayWindowController.java
@@ -17,11 +17,14 @@
package com.android.server.wm;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DISPLAY;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS_LIGHT;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
import android.content.res.Configuration;
import android.os.Binder;
+import android.os.IBinder;
import android.util.Slog;
import android.view.Display;
@@ -124,6 +127,42 @@
}
}
+ /**
+ * Sets a focused app on this display.
+ *
+ * @param token Specifies which app should be focused.
+ * @param moveFocusNow Specifies if we should update the focused window immediately.
+ */
+ public void setFocusedApp(IBinder token, boolean moveFocusNow) {
+ synchronized (mWindowMap) {
+ if (mContainer == null) {
+ if (DEBUG_FOCUS_LIGHT) Slog.i(TAG_WM, "setFocusedApp: could not find displayId="
+ + mDisplayId);
+ return;
+ }
+ final AppWindowToken newFocus;
+ if (token == null) {
+ if (DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM, "Clearing focused app, displayId="
+ + mDisplayId);
+ newFocus = null;
+ } else {
+ newFocus = mRoot.getAppWindowToken(token);
+ if (newFocus == null) {
+ Slog.w(TAG_WM, "Attempted to set focus to non-existing app token: " + token
+ + ", displayId=" + mDisplayId);
+ }
+ if (DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM, "Set focused app to: " + newFocus
+ + " moveFocusNow=" + moveFocusNow + " displayId=" + mDisplayId);
+ }
+
+ final boolean changed = mContainer.setFocusedApp(newFocus);
+ if (moveFocusNow && changed) {
+ mService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL,
+ true /*updateInputWindows*/);
+ }
+ }
+ }
+
@Override
public String toString() {
return "{DisplayWindowController displayId=" + mDisplayId + "}";
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index ef3a770..15f6938 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -64,7 +64,6 @@
// Array of window handles to provide to the input dispatcher.
private InputWindowHandle[] mInputWindowHandles;
private int mInputWindowHandleCount;
- private InputWindowHandle mFocusedInputWindowHandle;
private boolean mDisableWallpaperTouchEvents;
private final Rect mTmpRect = new Rect();
@@ -229,16 +228,12 @@
+ child + ", " + inputWindowHandle);
}
addInputWindowHandle(inputWindowHandle);
- if (hasFocus) {
- mFocusedInputWindowHandle = inputWindowHandle;
- }
}
private void clearInputWindowHandlesLw() {
while (mInputWindowHandleCount != 0) {
mInputWindowHandles[--mInputWindowHandleCount] = null;
}
- mFocusedInputWindowHandle = null;
}
void setUpdateInputWindowsNeededLw() {
@@ -325,13 +320,13 @@
public void setFocusedAppLw(AppWindowToken newApp) {
// Focused app has changed.
if (newApp == null) {
- mService.mInputManager.setFocusedApplication(null);
+ mService.mInputManager.setFocusedApplication(mDisplayId, null);
} else {
final InputApplicationHandle handle = newApp.mInputApplicationHandle;
handle.name = newApp.toString();
handle.dispatchingTimeoutNanos = newApp.mInputDispatchingTimeoutNanos;
- mService.mInputManager.setFocusedApplication(handle);
+ mService.mInputManager.setFocusedApplication(mDisplayId, handle);
}
}
@@ -370,8 +365,7 @@
void onRemoved() {
// If DisplayContent removed, we need find a way to remove window handles of this display
// from InputDispatcher, so pass an empty InputWindowHandles to remove them.
- mService.mInputManager.setInputWindows(mInputWindowHandles, mFocusedInputWindowHandle,
- mDisplayId);
+ mService.mInputManager.setInputWindows(mInputWindowHandles, mDisplayId);
}
private final class UpdateInputForAllWindowsConsumer implements Consumer<WindowState> {
@@ -414,8 +408,7 @@
}
// Send windows to native code.
- mService.mInputManager.setInputWindows(mInputWindowHandles, mFocusedInputWindowHandle,
- mDisplayId);
+ mService.mInputManager.setInputWindows(mInputWindowHandles, mDisplayId);
clearInputWindowHandlesLw();
@@ -435,7 +428,6 @@
final int flags = w.mAttrs.flags;
final int privateFlags = w.mAttrs.privateFlags;
final int type = w.mAttrs.type;
- // TODO(b/111361570): multi-display focus, one focus window per display.
final boolean hasFocus = w.isFocused();
final boolean isVisible = w.isVisibleLw();
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 a9571be..3fef87d 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -17,19 +17,20 @@
package com.android.server.wm;
import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.INVALID_DISPLAY;
import static android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SUSTAINED_PERFORMANCE_MODE;
import static android.view.WindowManager.LayoutParams.TYPE_DREAM;
import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG;
-import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
import static com.android.server.wm.RootWindowContainerProto.DISPLAYS;
import static com.android.server.wm.RootWindowContainerProto.WINDOWS;
import static com.android.server.wm.RootWindowContainerProto.WINDOW_CONTAINER;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DISPLAY;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS_LIGHT;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_KEEP_SCREEN_ON;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT_REPEATS;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION;
@@ -41,9 +42,9 @@
import static com.android.server.wm.WindowManagerDebugConfig.TAG_KEEP_SCREEN_ON;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import static com.android.server.wm.WindowManagerService.H.REPORT_LOSING_FOCUS;
import static com.android.server.wm.WindowManagerService.H.SEND_NEW_CONFIGURATION;
import static com.android.server.wm.WindowManagerService.H.WINDOW_FREEZE_TIMEOUT;
+import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_PLACING_SURFACES;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES;
import static com.android.server.wm.WindowManagerService.WINDOWS_FREEZING_SCREENS_NONE;
@@ -124,6 +125,10 @@
private String mCloseSystemDialogsReason;
+ // The ID of the display which is responsible for receiving display-unspecified key and pointer
+ // events.
+ private int mTopFocusedDisplayId = INVALID_DISPLAY;
+
// Only a seperate transaction until we seperate the apply surface changes
// transaction from the global transaction.
private final SurfaceControl.Transaction mDisplayTransaction = new SurfaceControl.Transaction();
@@ -153,23 +158,40 @@
mWallpaperController = new WallpaperController(mService);
}
- WindowState computeFocusedWindow() {
- // While the keyguard is showing, we must focus anything besides the main display.
- // Otherwise we risk input not going to the keyguard when the user expects it to.
- final boolean forceDefaultDisplay = mService.isKeyguardShowingAndNotOccluded();
-
+ boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows) {
+ boolean changed = false;
+ int topFocusedDisplayId = INVALID_DISPLAY;
for (int i = mChildren.size() - 1; i >= 0; i--) {
final DisplayContent dc = mChildren.get(i);
- final WindowState win = dc.findFocusedWindow();
- if (win != null) {
- if (forceDefaultDisplay && !dc.isDefaultDisplay) {
- EventLog.writeEvent(0x534e4554, "71786287", win.mOwnerUid, "");
- continue;
- }
- return win;
+ changed |= dc.updateFocusedWindowLocked(mode, updateInputWindows,
+ topFocusedDisplayId != INVALID_DISPLAY /* focusFound */);
+ if (topFocusedDisplayId == INVALID_DISPLAY && dc.mCurrentFocus != null) {
+ topFocusedDisplayId = dc.getDisplayId();
}
}
- return null;
+ if (topFocusedDisplayId == INVALID_DISPLAY) {
+ topFocusedDisplayId = DEFAULT_DISPLAY;
+ }
+ if (mTopFocusedDisplayId != topFocusedDisplayId) {
+ mTopFocusedDisplayId = topFocusedDisplayId;
+ mService.mInputManager.setFocusedDisplay(topFocusedDisplayId);
+ if (DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM, "New topFocusedDisplayId="
+ + topFocusedDisplayId);
+ }
+ final WindowState topFocusedWindow = getTopFocusedDisplayContent().mCurrentFocus;
+ mService.mInputManager.setFocusedWindow(
+ topFocusedWindow != null ? topFocusedWindow.mInputWindowHandle : null);
+ return changed;
+ }
+
+ DisplayContent getTopFocusedDisplayContent() {
+ return getDisplayContent(mTopFocusedDisplayId == INVALID_DISPLAY
+ ? DEFAULT_DISPLAY : mTopFocusedDisplayId);
+ }
+
+ @Override
+ void onChildPositionChanged() {
+ mService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, false /* updateInputWindows */);
}
DisplayContent getDisplayContent(int displayId) {
@@ -636,7 +658,6 @@
if (mService.updateFocusedWindowLocked(UPDATE_FOCUS_PLACING_SURFACES,
false /*updateInputWindows*/)) {
updateInputWindowsNeeded = true;
- defaultDisplay.pendingLayoutChanges |= FINISH_LAYOUT_REDO_ANIM;
}
}
@@ -646,7 +667,7 @@
defaultDisplay.pendingLayoutChanges);
}
- final ArraySet<DisplayContent> touchExcludeRegionUpdateDisplays = handleResizingWindows();
+ handleResizingWindows();
if (DEBUG_ORIENTATION && mService.mDisplayFrozen) Slog.v(TAG,
"With display frozen, orientationChangeComplete=" + mOrientationChangeComplete);
@@ -765,17 +786,7 @@
dc.getInputMonitor().updateInputWindowsLw(false /*force*/);
});
}
- mService.setFocusTaskRegionLocked(null);
- if (touchExcludeRegionUpdateDisplays != null) {
- final DisplayContent focusedDc = mService.mFocusedApp != null
- ? mService.mFocusedApp.getDisplayContent() : null;
- for (DisplayContent dc : touchExcludeRegionUpdateDisplays) {
- // The focused DisplayContent was recalcuated in setFocusTaskRegionLocked
- if (focusedDc != dc) {
- dc.setTouchExcludeRegion(null /* focusedTask */);
- }
- }
- }
+ forAllDisplays(DisplayContent::updateTouchExcludeRegion);
// Check to see if we are now in a state where the screen should
// be enabled, because the window obscured flags have changed.
@@ -808,16 +819,10 @@
mService.getDefaultDisplayRotation());
}
- boolean focusDisplayed = false;
-
final int count = mChildren.size();
for (int j = 0; j < count; ++j) {
final DisplayContent dc = mChildren.get(j);
- focusDisplayed |= dc.applySurfaceChangesTransaction(recoveringMemory);
- }
-
- if (focusDisplayed) {
- mService.mH.sendEmptyMessage(REPORT_LOSING_FOCUS);
+ dc.applySurfaceChangesTransaction(recoveringMemory);
}
// Give the display manager a chance to adjust properties like display rotation if it needs
@@ -828,12 +833,8 @@
/**
* Handles resizing windows during surface placement.
- *
- * @return A set of any DisplayContent whose touch exclude region needs to be recalculated due
- * to a tap-exclude window resizing, or null if no such DisplayContents were found.
*/
- private ArraySet<DisplayContent> handleResizingWindows() {
- ArraySet<DisplayContent> touchExcludeRegionUpdateSet = null;
+ private void handleResizingWindows() {
for (int i = mService.mResizingWindows.size() - 1; i >= 0; i--) {
WindowState win = mService.mResizingWindows.get(i);
if (win.mAppFreezing) {
@@ -842,15 +843,7 @@
}
win.reportResized();
mService.mResizingWindows.remove(i);
- if (WindowManagerService.excludeWindowTypeFromTapOutTask(win.mAttrs.type)) {
- final DisplayContent dc = win.getDisplayContent();
- if (touchExcludeRegionUpdateSet == null) {
- touchExcludeRegionUpdateSet = new ArraySet<>();
- }
- touchExcludeRegionUpdateSet.add(dc);
- }
}
- return touchExcludeRegionUpdateSet;
}
/**
@@ -1004,6 +997,10 @@
}
}
+ void dumpTopFocusedDisplayId(PrintWriter pw) {
+ pw.print(" mTopFocusedDisplayId="); pw.println(mTopFocusedDisplayId);
+ }
+
void dumpLayoutNeededDisplayIds(PrintWriter pw) {
if (!isLayoutNeeded()) {
return;
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index cc23ab6..6aa0e01 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -561,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() {
diff --git a/services/core/java/com/android/server/wm/TaskPositioningController.java b/services/core/java/com/android/server/wm/TaskPositioningController.java
index 33416f6..b7e37b2 100644
--- a/services/core/java/com/android/server/wm/TaskPositioningController.java
+++ b/services/core/java/com/android/server/wm/TaskPositioningController.java
@@ -131,9 +131,9 @@
// of the app, it may not have focus since there might be other windows
// on top (eg. a dialog window).
WindowState transferFocusFromWin = win;
- if (mService.mCurrentFocus != null && mService.mCurrentFocus != win
- && mService.mCurrentFocus.mAppToken == win.mAppToken) {
- transferFocusFromWin = mService.mCurrentFocus;
+ if (displayContent.mCurrentFocus != null && displayContent.mCurrentFocus != win
+ && displayContent.mCurrentFocus.mAppToken == win.mAppToken) {
+ transferFocusFromWin = displayContent.mCurrentFocus;
}
if (!mInputManager.transferTouchFocus(
transferFocusFromWin.mInputChannel, mTaskPositioner.mServerChannel)) {
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/TaskTapPointerEventListener.java b/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java
index f1e1592..52f8510 100644
--- a/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java
+++ b/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java
@@ -19,12 +19,12 @@
import android.graphics.Rect;
import android.graphics.Region;
import android.hardware.input.InputManager;
+import android.os.Handler;
import android.view.MotionEvent;
import android.view.WindowManagerPolicyConstants.PointerEventListener;
import com.android.server.wm.WindowManagerService.H;
-import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.PointerIcon.TYPE_NOT_SPECIFIED;
import static android.view.PointerIcon.TYPE_HORIZONTAL_DOUBLE_ARROW;
import static android.view.PointerIcon.TYPE_VERTICAL_DOUBLE_ARROW;
@@ -36,6 +36,8 @@
final private Region mTouchExcludeRegion = new Region();
private final WindowManagerService mService;
private final DisplayContent mDisplayContent;
+ private final Handler mHandler;
+ private final Runnable mMoveDisplayToTop;
private final Rect mTmpRect = new Rect();
private int mPointerIconType = TYPE_NOT_SPECIFIED;
@@ -43,6 +45,13 @@
DisplayContent displayContent) {
mService = service;
mDisplayContent = displayContent;
+ mHandler = new Handler(mService.mH.getLooper());
+ mMoveDisplayToTop = () -> {
+ synchronized (mService.mWindowMap) {
+ mDisplayContent.getParent().positionChildAt(WindowContainer.POSITION_TOP,
+ mDisplayContent, true /* includingParents */);
+ }
+ };
}
@Override
@@ -61,6 +70,7 @@
mService.mTaskPositioningController.handleTapOutsideTask(
mDisplayContent, x, y);
}
+ mHandler.post(mMoveDisplayToTop);
}
}
break;
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 a1d6ffd..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();
}
@@ -314,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));
}
@@ -324,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 e860939..46999a2 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -42,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;
@@ -71,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;
@@ -274,6 +273,7 @@
parent.mTreeWeight += child.mTreeWeight;
parent = parent.getParent();
}
+ onChildPositionChanged();
}
/**
@@ -299,6 +299,7 @@
parent.mTreeWeight -= child.mTreeWeight;
parent = parent.getParent();
}
+ onChildPositionChanged();
}
/**
@@ -456,9 +457,15 @@
mChildren.remove(child);
mChildren.add(position, child);
}
+ onChildPositionChanged();
}
/**
+ * Notify that a child's position has changed. Possible changes are adding or removing a child.
+ */
+ void onChildPositionChanged() { }
+
+ /**
* Update override configuration and recalculate full config.
* @see #mOverrideConfiguration
* @see #mFullConfiguration
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 7caa7ae..10ba63e 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -227,6 +227,7 @@
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;
@@ -498,12 +499,6 @@
final ArrayList<WindowState> mDestroyPreservedSurface = new ArrayList<>();
/**
- * Windows that have lost input focus and are waiting for the new
- * focus window to be displayed before they are told about this.
- */
- ArrayList<WindowState> mLosingFocus = new ArrayList<>();
-
- /**
* This is set when we have run out of memory, and will either be an empty
* list or contain windows that need to be force removed.
*/
@@ -638,14 +633,6 @@
*/
final Handler mAnimationHandler = new Handler(AnimationThread.getHandler().getLooper());
- WindowState mCurrentFocus = null;
- WindowState mLastFocus = null;
-
- /** Windows added since {@link #mCurrentFocus} was set to null. Used for ANR blaming. */
- private final ArrayList<WindowState> mWinAddedSinceNullFocus = new ArrayList<>();
- /** Windows removed since {@link #mCurrentFocus} was set to null. Used for ANR blaming. */
- private final ArrayList<WindowState> mWinRemovedSinceNullFocus = new ArrayList<>();
-
/** This just indicates the window the input method is on top of, not
* necessarily the window its input is going to. */
WindowState mInputMethodTarget = null;
@@ -720,9 +707,6 @@
}
}
- // TODO: Move to RootWindowContainer
- AppWindowToken mFocusedApp = null;
-
PowerManager mPowerManager;
PowerManagerInternal mPowerManagerInternal;
@@ -1370,8 +1354,8 @@
// the screen after the activity goes away.
if (addToastWindowRequiresToken
|| (attrs.flags & LayoutParams.FLAG_NOT_FOCUSABLE) == 0
- || mCurrentFocus == null
- || mCurrentFocus.mOwnerUid != callingUid) {
+ || displayContent.mCurrentFocus == null
+ || displayContent.mCurrentFocus.mOwnerUid != callingUid) {
mH.sendMessageDelayed(
mH.obtainMessage(H.WINDOW_HIDE_TIMEOUT, win),
win.mAttrs.hideTimeoutMilliseconds);
@@ -1381,8 +1365,8 @@
// From now on, no exceptions or errors allowed!
res = WindowManagerGlobal.ADD_OKAY;
- if (mCurrentFocus == null) {
- mWinAddedSinceNullFocus.add(win);
+ if (displayContent.mCurrentFocus == null) {
+ displayContent.mWinAddedSinceNullFocus.add(win);
}
if (excludeWindowTypeFromTapOutTask(type)) {
@@ -1503,7 +1487,7 @@
win.getParent().assignChildLayers();
if (focusChanged) {
- displayContent.getInputMonitor().setInputFocusLw(mCurrentFocus,
+ displayContent.getInputMonitor().setInputFocusLw(displayContent.mCurrentFocus,
false /*updateInputWindows*/);
}
displayContent.getInputMonitor().updateInputWindowsLw(false /*force*/);
@@ -1678,8 +1662,9 @@
win.resetAppOpsState();
- if (mCurrentFocus == null) {
- mWinRemovedSinceNullFocus.add(win);
+ final DisplayContent dc = win.getDisplayContent();
+ if (dc.mCurrentFocus == null) {
+ dc.mWinRemovedSinceNullFocus.add(win);
}
mPendingRemove.remove(win);
mResizingWindows.remove(win);
@@ -1715,7 +1700,6 @@
atoken.postWindowRemoveStartingWindowCleanup(win);
}
- final DisplayContent dc = win.getDisplayContent();
if (win.mAttrs.type == TYPE_WALLPAPER) {
dc.mWallpaperController.clearLastWallpaperTimeoutTime();
dc.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
@@ -1977,9 +1961,9 @@
boolean imMayMove = (flagChanges & (FLAG_ALT_FOCUSABLE_IM | FLAG_NOT_FOCUSABLE)) != 0
|| becameVisible;
final boolean isDefaultDisplay = win.isDefaultDisplay();
- boolean focusMayChange = isDefaultDisplay && (win.mViewVisibility != viewVisibility
+ boolean focusMayChange = win.mViewVisibility != viewVisibility
|| ((flagChanges & FLAG_NOT_FOCUSABLE) != 0)
- || (!win.mRelayoutCalled));
+ || (!win.mRelayoutCalled);
boolean wallpaperMayMove = win.mViewVisibility != viewVisibility
&& (win.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0;
@@ -2024,8 +2008,7 @@
}
result |= RELAYOUT_RES_SURFACE_CHANGED;
if (!win.mWillReplaceWindow) {
- focusMayChange = tryStartExitingAnimation(win, winAnimator, isDefaultDisplay,
- focusMayChange);
+ focusMayChange = tryStartExitingAnimation(win, winAnimator, focusMayChange);
}
}
@@ -2050,7 +2033,7 @@
return 0;
}
if ((result & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {
- focusMayChange = isDefaultDisplay;
+ focusMayChange = true;
}
final DisplayContent displayContent = win.getDisplayContent();
if (win.mAttrs.type == TYPE_INPUT_METHOD
@@ -2198,7 +2181,7 @@
}
private boolean tryStartExitingAnimation(WindowState win, WindowStateAnimator winAnimator,
- boolean isDefaultDisplay, boolean focusMayChange) {
+ boolean focusMayChange) {
// Try starting an animation; if there isn't one, we
// can destroy the surface right away.
int transit = WindowManagerPolicy.TRANSIT_EXIT;
@@ -2206,9 +2189,9 @@
transit = WindowManagerPolicy.TRANSIT_PREVIEW_DONE;
}
if (win.isWinVisibleLw() && winAnimator.applyAnimationLocked(transit, false)) {
- focusMayChange = isDefaultDisplay;
+ focusMayChange = true;
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;
@@ -2503,57 +2486,6 @@
}
}
- void setFocusTaskRegionLocked(AppWindowToken previousFocus) {
- final Task focusedTask = mFocusedApp != null ? mFocusedApp.getTask() : null;
- final Task previousTask = previousFocus != null ? previousFocus.getTask() : null;
- final DisplayContent focusedDisplayContent =
- focusedTask != null ? focusedTask.getDisplayContent() : null;
- final DisplayContent previousDisplayContent =
- previousTask != null ? previousTask.getDisplayContent() : null;
- if (previousDisplayContent != null && previousDisplayContent != focusedDisplayContent) {
- previousDisplayContent.setTouchExcludeRegion(null);
- }
- if (focusedDisplayContent != null) {
- focusedDisplayContent.setTouchExcludeRegion(focusedTask);
- }
- }
-
- @Override
- public void setFocusedApp(IBinder token, boolean moveFocusNow) {
- if (!checkCallingPermission(MANAGE_APP_TOKENS, "setFocusedApp()")) {
- throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
- }
-
- synchronized(mWindowMap) {
- final AppWindowToken newFocus;
- if (token == null) {
- if (DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM, "Clearing focused app, was " + mFocusedApp);
- newFocus = null;
- } else {
- newFocus = mRoot.getAppWindowToken(token);
- if (newFocus == null) {
- Slog.w(TAG_WM, "Attempted to set focus to non-existing app token: " + token);
- }
- if (DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM, "Set focused app to: " + newFocus
- + " old focus=" + mFocusedApp + " moveFocusNow=" + moveFocusNow);
- }
-
- final boolean changed = mFocusedApp != newFocus;
- if (changed) {
- AppWindowToken prev = mFocusedApp;
- mFocusedApp = newFocus;
- mFocusedApp.getDisplayContent().getInputMonitor().setFocusedAppLw(newFocus);
- setFocusTaskRegionLocked(prev);
- }
-
- if (moveFocusNow && changed) {
- final long origId = Binder.clearCallingIdentity();
- updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, true /*updateInputWindows*/);
- Binder.restoreCallingIdentity(origId);
- }
- }
- }
-
@Override
public void prepareAppTransition(@TransitionType int transit, boolean alwaysKeepCurrent) {
prepareAppTransition(transit, alwaysKeepCurrent, 0 /* flags */, false /* forceOverride */);
@@ -2714,6 +2646,11 @@
}
}
+ @VisibleForTesting
+ void setRecentsAnimationController(RecentsAnimationController controller) {
+ mRecentsAnimationController = controller;
+ }
+
public RecentsAnimationController getRecentsAnimationController() {
return mRecentsAnimationController;
}
@@ -4389,7 +4326,8 @@
}
private WindowState getFocusedWindowLocked() {
- return mCurrentFocus;
+ // Return the focused window in the focused display.
+ return mRoot.getTopFocusedDisplayContent().mCurrentFocus;
}
TaskStack getImeFocusStackLocked() {
@@ -4397,8 +4335,11 @@
// Also don't use mInputMethodTarget's stack, because some window with FLAG_NOT_FOCUSABLE
// and FLAG_ALT_FOCUSABLE_IM flags both set might be set to IME target so they're moved
// to make room for IME, but the window is not the focused window that's taking input.
- return (mFocusedApp != null && mFocusedApp.getTask() != null) ?
- mFocusedApp.getTask().mStack : null;
+ // TODO (b/111080190): Consider the case of multiple IMEs on multi-display.
+ final DisplayContent topFocusedDisplay = mRoot.getTopFocusedDisplayContent();
+ final AppWindowToken focusedApp = topFocusedDisplay.mFocusedApp;
+ return (focusedApp != null && focusedApp.getTask() != null)
+ ? focusedApp.getTask().mStack : null;
}
public boolean detectSafeMode() {
@@ -4536,7 +4477,6 @@
public static final int REPORT_LOSING_FOCUS = 3;
public static final int WINDOW_FREEZE_TIMEOUT = 11;
- public static final int APP_TRANSITION_TIMEOUT = 13;
public static final int PERSIST_ANIMATION_SCALE = 14;
public static final int FORCE_GC = 15;
public static final int ENABLE_SCREEN = 16;
@@ -4548,7 +4488,6 @@
public static final int BOOT_TIMEOUT = 23;
public static final int WAITING_FOR_DRAWN_TIMEOUT = 24;
public static final int SHOW_STRICT_MODE_VIOLATION = 25;
- public static final int DO_ANIMATION_CALLBACK = 26;
public static final int CLIENT_FREEZE_TIMEOUT = 30;
public static final int NOTIFY_ACTIVITY_DRAWN = 32;
@@ -4595,6 +4534,7 @@
}
switch (msg.what) {
case REPORT_FOCUS_CHANGE: {
+ final DisplayContent displayContent = (DisplayContent) msg.obj;
WindowState lastFocus;
WindowState newFocus;
@@ -4602,24 +4542,22 @@
synchronized(mWindowMap) {
// TODO(multidisplay): Accessibility supported only of default desiplay.
- if (mAccessibilityController != null && getDefaultDisplayContentLocked()
- .getDisplayId() == DEFAULT_DISPLAY) {
+ if (mAccessibilityController != null && displayContent.isDefaultDisplay) {
accessibilityController = mAccessibilityController;
}
- lastFocus = mLastFocus;
- newFocus = mCurrentFocus;
+ lastFocus = displayContent.mLastFocus;
+ newFocus = displayContent.mCurrentFocus;
if (lastFocus == newFocus) {
// Focus is not changing, so nothing to do.
return;
}
- mLastFocus = newFocus;
+ displayContent.mLastFocus = newFocus;
if (DEBUG_FOCUS_LIGHT) Slog.i(TAG_WM, "Focus moving from " + lastFocus +
- " to " + newFocus);
- if (newFocus != null && lastFocus != null
- && !newFocus.isDisplayedLw()) {
- //Slog.i(TAG_WM, "Delaying loss of focus...");
- mLosingFocus.add(lastFocus);
+ " to " + newFocus + " displayId=" + displayContent.getDisplayId());
+ if (newFocus != null && lastFocus != null && !newFocus.isDisplayedLw()) {
+ if (DEBUG_FOCUS_LIGHT) Slog.i(TAG_WM, "Delaying loss of focus...");
+ displayContent.mLosingFocus.add(lastFocus);
lastFocus = null;
}
}
@@ -4630,8 +4568,6 @@
accessibilityController.onWindowFocusChangedNotLocked();
}
- //System.out.println("Changing focus from " + lastFocus
- // + " to " + newFocus);
if (newFocus != null) {
if (DEBUG_FOCUS_LIGHT) Slog.i(TAG_WM, "Gaining focus: " + newFocus);
newFocus.reportFocusChangedSerialized(true, mInTouchMode);
@@ -4645,15 +4581,16 @@
} break;
case REPORT_LOSING_FOCUS: {
+ final DisplayContent displayContent = (DisplayContent) msg.obj;
ArrayList<WindowState> losers;
synchronized(mWindowMap) {
- losers = mLosingFocus;
- mLosingFocus = new ArrayList<WindowState>();
+ losers = displayContent.mLosingFocus;
+ displayContent.mLosingFocus = new ArrayList<>();
}
final int N = losers.size();
- for (int i=0; i<N; i++) {
+ for (int i = 0; i < N; i++) {
if (DEBUG_FOCUS_LIGHT) Slog.i(TAG_WM, "Losing delayed focus: " +
losers.get(i));
losers.get(i).reportFocusChangedSerialized(false, mInTouchMode);
@@ -4668,21 +4605,6 @@
break;
}
- case APP_TRANSITION_TIMEOUT: {
- synchronized (mWindowMap) {
- if (mAppTransition.isTransitionSet() || !mOpeningApps.isEmpty()
- || !mClosingApps.isEmpty()) {
- if (DEBUG_APP_TRANSITIONS) Slog.v(TAG_WM, "*** APP TRANSITION TIMEOUT."
- + " isTransitionSet()=" + mAppTransition.isTransitionSet()
- + " mOpeningApps.size()=" + mOpeningApps.size()
- + " mClosingApps.size()=" + mClosingApps.size());
- mAppTransition.setTimeout();
- mWindowPlacerLocked.performSurfacePlacement();
- }
- }
- break;
- }
-
case PERSIST_ANIMATION_SCALE: {
Settings.Global.putFloat(mContext.getContentResolver(),
Settings.Global.WINDOW_ANIMATION_SCALE, mWindowAnimationScaleSetting);
@@ -4835,14 +4757,6 @@
break;
}
- case DO_ANIMATION_CALLBACK: {
- try {
- ((IRemoteCallback)msg.obj).sendResult(null);
- } catch (RemoteException e) {
- }
- break;
- }
-
case NOTIFY_ACTIVITY_DRAWN:
try {
mActivityTaskManager.notifyActivityDrawn((IBinder) msg.obj);
@@ -5547,93 +5461,11 @@
}
}
- // TODO: Move to DisplayContent
boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows) {
- WindowState newFocus = mRoot.computeFocusedWindow();
- if (mCurrentFocus != newFocus) {
- Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "wmUpdateFocus");
- // This check makes sure that we don't already have the focus
- // change message pending.
- mH.removeMessages(H.REPORT_FOCUS_CHANGE);
- mH.sendEmptyMessage(H.REPORT_FOCUS_CHANGE);
- final DisplayContent displayContent = (newFocus != null) ? newFocus.getDisplayContent()
- : getDefaultDisplayContentLocked();
- boolean imWindowChanged = false;
- if (displayContent.mInputMethodWindow != null) {
- final WindowState prevTarget = mInputMethodTarget;
-
- final WindowState newTarget =
- displayContent.computeImeTarget(true /* updateImeTarget*/);
- imWindowChanged = prevTarget != newTarget;
-
- 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;
- }
- }
-
- if (imWindowChanged) {
- mWindowsChanged = true;
- displayContent.setLayoutNeeded();
- newFocus = mRoot.computeFocusedWindow();
- }
-
- if (DEBUG_FOCUS_LIGHT || localLOGV) Slog.v(TAG_WM, "Changing focus from " +
- mCurrentFocus + " to " + newFocus + " Callers=" + Debug.getCallers(4));
- final WindowState oldFocus = mCurrentFocus;
- mCurrentFocus = newFocus;
- mLosingFocus.remove(newFocus);
-
- if (mCurrentFocus != null) {
- mWinAddedSinceNullFocus.clear();
- mWinRemovedSinceNullFocus.clear();
- }
-
- int focusChanged = mPolicy.focusChangedLw(oldFocus, newFocus);
-
- if (imWindowChanged && oldFocus != displayContent.mInputMethodWindow) {
- // Focus of the input method window changed. Perform layout if needed.
- if (mode == UPDATE_FOCUS_PLACING_SURFACES) {
- displayContent.performLayout(true /*initial*/, updateInputWindows);
- focusChanged &= ~FINISH_LAYOUT_REDO_LAYOUT;
- } else if (mode == UPDATE_FOCUS_WILL_PLACE_SURFACES) {
- // Client will do the layout, but we need to assign layers
- // for handleNewWindowLocked() below.
- displayContent.assignWindowLayers(false /* setLayoutNeeded */);
- }
- }
-
- if ((focusChanged & FINISH_LAYOUT_REDO_LAYOUT) != 0) {
- // The change in focus caused us to need to do a layout. Okay.
- displayContent.setLayoutNeeded();
- if (mode == UPDATE_FOCUS_PLACING_SURFACES) {
- displayContent.performLayout(true /*initial*/, updateInputWindows);
- } else if (mode == UPDATE_FOCUS_REMOVING_FOCUS) {
- mRoot.performSurfacePlacement(false);
- }
- }
-
- if (mode != UPDATE_FOCUS_WILL_ASSIGN_LAYERS) {
- // If we defer assigning layers, then the caller is responsible for
- // doing this part.
- displayContent.getInputMonitor().setInputFocusLw(mCurrentFocus, updateInputWindows);
- }
-
- displayContent.adjustForImeIfNeeded();
-
- // We may need to schedule some toast windows to be removed. The toasts for an app that
- // does not have input focus are removed within a timeout to prevent apps to redress
- // other apps' UI.
- displayContent.scheduleToastWindowsTimeoutIfNeededLocked(oldFocus, newFocus);
-
- Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
- return true;
- }
- return false;
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "wmUpdateFocus");
+ boolean changed = mRoot.updateFocusedWindowLocked(mode, updateInputWindows);
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+ return changed;
}
void startFreezingDisplayLocked(int exitAnim, int enterAnim) {
@@ -6195,11 +6027,12 @@
void writeToProtoLocked(ProtoOutputStream proto, boolean trim) {
mPolicy.writeToProto(proto, POLICY);
mRoot.writeToProto(proto, ROOT_WINDOW_CONTAINER, trim);
- if (mCurrentFocus != null) {
- mCurrentFocus.writeIdentifierToProto(proto, FOCUSED_WINDOW);
+ final DisplayContent topFocusedDisplayContent = mRoot.getTopFocusedDisplayContent();
+ if (topFocusedDisplayContent.mCurrentFocus != null) {
+ topFocusedDisplayContent.mCurrentFocus.writeIdentifierToProto(proto, FOCUSED_WINDOW);
}
- if (mFocusedApp != null) {
- mFocusedApp.writeNameToProto(proto, FOCUSED_APP);
+ if (topFocusedDisplayContent.mFocusedApp != null) {
+ topFocusedDisplayContent.mFocusedApp.writeNameToProto(proto, FOCUSED_APP);
}
final WindowState imeWindow = mRoot.getCurrentInputMethodWindow();
if (imeWindow != null) {
@@ -6297,23 +6130,6 @@
}
}
}
- if (mLosingFocus.size() > 0) {
- pw.println();
- pw.println(" Windows losing focus:");
- for (int i=mLosingFocus.size()-1; i>=0; i--) {
- WindowState w = mLosingFocus.get(i);
- if (windows == null || windows.contains(w)) {
- pw.print(" Losing #"); pw.print(i); pw.print(' ');
- pw.print(w);
- if (dumpAll) {
- pw.println(":");
- w.dump(pw, " ", true);
- } else {
- pw.println();
- }
- }
- }
- }
if (mResizingWindows.size() > 0) {
pw.println();
pw.println(" Windows waiting to resize:");
@@ -6342,11 +6158,7 @@
pw.println();
pw.print(" mGlobalConfiguration="); pw.println(mRoot.getConfiguration());
pw.print(" mHasPermanentDpad="); pw.println(mHasPermanentDpad);
- pw.print(" mCurrentFocus="); pw.println(mCurrentFocus);
- if (mLastFocus != mCurrentFocus) {
- pw.print(" mLastFocus="); pw.println(mLastFocus);
- }
- pw.print(" mFocusedApp="); pw.println(mFocusedApp);
+ mRoot.dumpTopFocusedDisplayId(pw);
if (mInputMethodTarget != null) {
pw.print(" mInputMethodTarget="); pw.println(mInputMethodTarget);
}
@@ -6477,11 +6289,17 @@
if (reason != null) {
pw.println(" Reason: " + reason);
}
- if (!mWinAddedSinceNullFocus.isEmpty()) {
- pw.println(" Windows added since null focus: " + mWinAddedSinceNullFocus);
- }
- if (!mWinRemovedSinceNullFocus.isEmpty()) {
- pw.println(" Windows removed since null focus: " + mWinRemovedSinceNullFocus);
+ for (int i = mRoot.getChildCount() - 1; i >= 0; i--) {
+ final DisplayContent dc = mRoot.getChildAt(i);
+ final int displayId = dc.getDisplayId();
+ if (!dc.mWinAddedSinceNullFocus.isEmpty()) {
+ pw.println(" Windows added in display #" + displayId + " since null focus: "
+ + dc.mWinAddedSinceNullFocus);
+ }
+ if (!dc.mWinRemovedSinceNullFocus.isEmpty()) {
+ pw.println(" Windows removed in display #" + displayId + " since null focus: "
+ + dc.mWinRemovedSinceNullFocus);
+ }
}
pw.println();
dumpWindowsNoHeaderLocked(pw, true, null);
@@ -6816,9 +6634,9 @@
@Override
public void setDockedStackDividerTouchRegion(Rect touchRegion) {
synchronized (mWindowMap) {
- getDefaultDisplayContentLocked().getDockedDividerController()
- .setTouchRegion(touchRegion);
- setFocusTaskRegionLocked(null);
+ final DisplayContent dc = getDefaultDisplayContentLocked();
+ dc.getDockedDividerController().setTouchRegion(touchRegion);
+ dc.updateTouchExcludeRegion();
}
}
@@ -7371,7 +7189,14 @@
@Override
public boolean isUidFocused(int uid) {
synchronized (mWindowMap) {
- return mCurrentFocus != null ? uid == mCurrentFocus.getOwningUid() : false;
+ for (int i = mRoot.getChildCount() - 1; i >= 0; i--) {
+ final DisplayContent displayContent = mRoot.getChildAt(i);
+ if (displayContent.mCurrentFocus != null
+ && uid == displayContent.mCurrentFocus.getOwningUid()) {
+ return true;
+ }
+ }
+ return false;
}
}
@@ -7394,8 +7219,9 @@
// 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.mUid == uid
- && mCurrentFocus.mSession.mPid == pid) {
+ final WindowState currentFocus = mRoot.getTopFocusedDisplayContent().mCurrentFocus;
+ if (currentFocus != null && currentFocus.mSession.mUid == uid
+ && currentFocus.mSession.mPid == pid) {
return true;
}
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index f7c6d77..8276952 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1364,7 +1364,7 @@
@Override
boolean hasContentToDisplay() {
if (!mAppFreezing && isDrawnLw() && (mViewVisibility == View.VISIBLE
- || (mWinAnimator.isAnimationSet() && !mService.mAppTransition.isTransitionSet()))) {
+ || (isAnimating() && !mService.mAppTransition.isTransitionSet()))) {
return true;
}
@@ -1443,9 +1443,9 @@
final AppWindowToken atoken = mAppToken;
if (atoken != null) {
return ((!isParentWindowHidden() && !atoken.hiddenRequested)
- || mWinAnimator.isAnimationSet());
+ || isAnimating());
}
- return !isParentWindowHidden() || mWinAnimator.isAnimationSet();
+ return !isParentWindowHidden() || isAnimating();
}
/**
@@ -1476,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.
@@ -1508,7 +1509,7 @@
final AppWindowToken atoken = mAppToken;
return isDrawnLw() && mPolicyVisibility
&& ((!isParentWindowHidden() && (atoken == null || !atoken.hiddenRequested))
- || mWinAnimator.isAnimationSet());
+ || isAnimating());
}
/**
@@ -1562,7 +1563,7 @@
// to determine if it's occluding apps.
return ((!mIsWallpaper && mAttrs.format == PixelFormat.OPAQUE)
|| (mIsWallpaper && mWallpaperVisible))
- && isDrawnLw() && !mWinAnimator.isAnimationSet();
+ && isDrawnLw() && !isAnimating();
}
@Override
@@ -1832,7 +1833,7 @@
if (startingWindow && DEBUG_STARTING_WINDOW) Slog.d(TAG_WM,
"Starting window removed " + this);
- if (localLOGV || DEBUG_FOCUS || DEBUG_FOCUS_LIGHT && this == mService.mCurrentFocus)
+ if (localLOGV || DEBUG_FOCUS || DEBUG_FOCUS_LIGHT && isFocused())
Slog.v(TAG_WM, "Remove " + this + " client="
+ Integer.toHexString(System.identityHashCode(mClient.asBinder()))
+ ", surfaceController=" + mWinAnimator.mSurfaceController + " Callers="
@@ -1849,7 +1850,7 @@
+ " mRemoveOnExit=" + mRemoveOnExit
+ " mHasSurface=" + mHasSurface
+ " surfaceShowing=" + mWinAnimator.getShown()
- + " isAnimationSet=" + mWinAnimator.isAnimationSet()
+ + " animating=" + isAnimating()
+ " app-animation="
+ (mAppToken != null ? mAppToken.isSelfAnimating() : "false")
+ " mWillReplaceWindow=" + mWillReplaceWindow
@@ -1916,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);
@@ -1944,7 +1945,7 @@
if (wasVisible && mService.updateOrientationFromAppTokensLocked(displayId)) {
mService.mH.obtainMessage(SEND_NEW_CONFIGURATION, displayId).sendToTarget();
}
- mService.updateFocusedWindowLocked(mService.mCurrentFocus == this
+ mService.updateFocusedWindowLocked(isFocused()
? UPDATE_FOCUS_REMOVING_FOCUS
: UPDATE_FOCUS_NORMAL,
true /*updateInputWindows*/);
@@ -2179,7 +2180,7 @@
mPolicyVisibility = mPolicyVisibilityAfterAnim;
if (!mPolicyVisibility) {
mWinAnimator.hide("checkPolicyVisibilityChange");
- if (mService.mCurrentFocus == this) {
+ if (isFocused()) {
if (DEBUG_FOCUS_LIGHT) Slog.i(TAG,
"setAnimationLocked: setting mFocusMayChange true");
mService.mFocusMayChange = true;
@@ -2434,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.
@@ -2476,11 +2477,12 @@
}
if (doAnimation) {
mWinAnimator.applyAnimationLocked(TRANSIT_EXIT, false);
- if (!mWinAnimator.isAnimationSet()) {
+ if (!isAnimating()) {
doAnimation = false;
}
}
mPolicyVisibilityAfterAnim = false;
+ final boolean isFocused = isFocused();
if (!doAnimation) {
if (DEBUG_VISIBILITY) Slog.v(TAG, "Policy visibility false: " + this);
mPolicyVisibility = false;
@@ -2488,7 +2490,7 @@
// for it to be displayed before enabling the display, that
// we allow the display to be enabled now.
mService.enableScreenIfNeededLocked();
- if (mService.mCurrentFocus == this) {
+ if (isFocused) {
if (DEBUG_FOCUS_LIGHT) Slog.i(TAG,
"WindowState.hideLw: setting mFocusMayChange true");
mService.mFocusMayChange = true;
@@ -2497,7 +2499,7 @@
if (requestAnim) {
mService.scheduleAnimationLocked();
}
- if (mService.mCurrentFocus == this) {
+ if (isFocused) {
mService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, false /* updateImWindows */);
}
return true;
@@ -2993,10 +2995,8 @@
}
}
- public boolean isFocused() {
- synchronized(mService.mWindowMap) {
- return mService.mCurrentFocus == this;
- }
+ boolean isFocused() {
+ return getDisplayContent().mCurrentFocus == this;
}
@Override
@@ -3216,10 +3216,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);
@@ -3697,7 +3695,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));
@@ -3749,18 +3747,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()) {
@@ -4110,25 +4096,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;
}
}
@@ -4448,7 +4434,12 @@
@Override
public boolean isFocused() {
final WindowState outer = mOuter.get();
- return outer != null && outer.isFocused();
+ if (outer != null) {
+ synchronized (outer.mService.mWindowMap) {
+ return outer.isFocused();
+ }
+ }
+ return false;
}
}
@@ -4676,10 +4667,7 @@
mTapExcludeRegionHolder.updateRegion(regionId, left, top, width, height);
// Trigger touch exclude region update on current display.
- final boolean isAppFocusedOnDisplay = mService.mFocusedApp != null
- && mService.mFocusedApp.getDisplayContent() == currentDisplay;
- currentDisplay.setTouchExcludeRegion(isAppFocusedOnDisplay ? mService.mFocusedApp.getTask()
- : null);
+ currentDisplay.updateTouchExcludeRegion();
}
/** Union the region with current tap exclude region that this window provides. */
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 c8d1a8b..e13a70a 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -69,7 +69,6 @@
import android.view.animation.Animation;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.wm.WindowManagerService.H;
import java.io.PrintWriter;
import java.util.function.Predicate;
@@ -98,12 +97,6 @@
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;
@@ -258,7 +251,7 @@
mService.mSkipAppTransitionAnimation = false;
mService.mNoAnimationNotifyOnTransitionFinished.clear();
- mService.mH.removeMessages(H.APP_TRANSITION_TIMEOUT);
+ mService.mAppTransition.removeAppTransitionTimeoutCallbacks();
final DisplayContent displayContent = mService.getDefaultDisplayContentLocked();
@@ -298,10 +291,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);
@@ -313,17 +312,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();
@@ -450,10 +446,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);
@@ -478,24 +471,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++) {
@@ -518,13 +502,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();
}
@@ -785,6 +762,7 @@
private void processApplicationsAnimatingInPlace(int transit) {
if (transit == TRANSIT_TASK_IN_PLACE) {
+ // TODO (b/111362605): non-default-display transition.
// Find the focused window
final WindowState win = mService.getDefaultDisplayContentLocked().findFocusedWindow();
if (win != null) {
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index 8972c38..0cf79b6 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -163,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();
@@ -235,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!
@@ -267,6 +255,7 @@
super.removeImmediately();
}
+ @Override
void onDisplayChanged(DisplayContent dc) {
dc.reParentWindowToken(this);
mDisplayContent = dc;
diff --git a/services/core/java/com/android/server/wm/utils/DisplayRotationUtil.java b/services/core/java/com/android/server/wm/utils/DisplayRotationUtil.java
new file mode 100644
index 0000000..9f307bb
--- /dev/null
+++ b/services/core/java/com/android/server/wm/utils/DisplayRotationUtil.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.utils;
+
+import static android.view.DisplayCutout.BOUNDS_POSITION_LENGTH;
+import static android.view.Surface.ROTATION_0;
+import static android.view.Surface.ROTATION_180;
+import static android.view.Surface.ROTATION_270;
+import static android.view.Surface.ROTATION_90;
+
+import static com.android.server.wm.utils.CoordinateTransforms.transformPhysicalToLogicalCoordinates;
+
+import android.graphics.Matrix;
+import android.graphics.Rect;
+import android.graphics.RectF;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * Utility to compute bounds after rotating the screen.
+ */
+public class DisplayRotationUtil {
+ private final Matrix mTmpMatrix = new Matrix();
+
+ private static int getRotationToBoundsOffset(int rotation) {
+ switch (rotation) {
+ case ROTATION_0:
+ return 0;
+ case ROTATION_90:
+ return -1;
+ case ROTATION_180:
+ return 2;
+ case ROTATION_270:
+ return 1;
+ default:
+ // should not happen
+ return 0;
+ }
+ }
+
+ @VisibleForTesting
+ static int getBoundIndexFromRotation(int i, int rotation) {
+ return Math.floorMod(i + getRotationToBoundsOffset(rotation),
+ BOUNDS_POSITION_LENGTH);
+ }
+
+ /**
+ * Compute bounds after rotating teh screen.
+ *
+ * @param bounds Bounds before the rotation. The array must contain exactly 4 non-null elements.
+ * @param rotation rotation constant defined in android.view.Surface.
+ * @param initialDisplayWidth width of the display before the rotation.
+ * @param initialDisplayHeight height of the display before the rotation.
+ * @return Bounds after the rotation.
+ *
+ * @hide
+ */
+ public Rect[] getRotatedBounds(
+ Rect[] bounds, int rotation, int initialDisplayWidth, int initialDisplayHeight) {
+ if (bounds.length != BOUNDS_POSITION_LENGTH) {
+ throw new IllegalArgumentException(
+ "bounds must have exactly 4 elements: bounds=" + bounds);
+ }
+ if (rotation == ROTATION_0) {
+ return bounds;
+ }
+ transformPhysicalToLogicalCoordinates(rotation, initialDisplayWidth, initialDisplayHeight,
+ mTmpMatrix);
+ Rect[] newBounds = new Rect[BOUNDS_POSITION_LENGTH];
+ for (int i = 0; i < bounds.length; i++) {
+
+ final Rect rect = bounds[i];
+ if (!rect.isEmpty()) {
+ final RectF rectF = new RectF(rect);
+ mTmpMatrix.mapRect(rectF);
+ rectF.round(rect);
+ }
+ newBounds[getBoundIndexFromRotation(i, rotation)] = rect;
+ }
+ return newBounds;
+ }
+}
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index becde73..045f4eb 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -8,6 +8,7 @@
"-Wall",
"-Werror",
"-Wno-unused-parameter",
+ "-Wthread-safety",
"-DEGL_EGLEXT_PROTOTYPES",
"-DGL_GLEXT_PROTOTYPES",
@@ -29,14 +30,13 @@
"com_android_server_devicepolicy_CryptoTestHelper.cpp",
"com_android_server_HardwarePropertiesManagerService.cpp",
"com_android_server_hdmi_HdmiCecController.cpp",
- "com_android_server_input_InputApplicationHandle.cpp",
"com_android_server_input_InputManagerService.cpp",
- "com_android_server_input_InputWindowHandle.cpp",
"com_android_server_lights_LightsService.cpp",
"com_android_server_location_GnssLocationProvider.cpp",
"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 +89,6 @@
"libsensorservicehidl",
"libgui",
"libusbhost",
- "libsuspend",
"libtinyalsa",
"libEGL",
"libGLESv2",
@@ -121,6 +120,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_SystemServer.cpp b/services/core/jni/com_android_server_SystemServer.cpp
index 3901ceb..dc0d53b 100644
--- a/services/core/jni/com_android_server_SystemServer.cpp
+++ b/services/core/jni/com_android_server_SystemServer.cpp
@@ -17,6 +17,7 @@
#include <jni.h>
#include <nativehelper/JNIHelp.h>
+#include <binder/IServiceManager.h>
#include <hidl/HidlTransportSupport.h>
#include <schedulerservice/SchedulingPolicyService.h>
@@ -34,7 +35,8 @@
char propBuf[PROPERTY_VALUE_MAX];
property_get("system_init.startsensorservice", propBuf, "1");
if (strcmp(propBuf, "1") == 0) {
- SensorService::instantiate();
+ SensorService::publish(false /* allowIsolated */,
+ IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL);
}
}
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_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 42ade38..c66d03c 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -60,10 +60,12 @@
#include <nativehelper/ScopedUtfChars.h>
#include "com_android_server_power_PowerManagerService.h"
-#include "com_android_server_input_InputApplicationHandle.h"
-#include "com_android_server_input_InputWindowHandle.h"
+#include "android_hardware_input_InputApplicationHandle.h"
+#include "android_hardware_input_InputWindowHandle.h"
#include "android_hardware_display_DisplayViewport.h"
+#include <vector>
+
#define INDENT " "
using android::base::StringPrintf;
@@ -144,8 +146,8 @@
static jobject getInputApplicationHandleObjLocalRef(JNIEnv* env,
const sp<InputApplicationHandle>& inputApplicationHandle) {
- if (inputApplicationHandle == NULL) {
- return NULL;
+ if (inputApplicationHandle == nullptr) {
+ return nullptr;
}
return static_cast<NativeInputApplicationHandle*>(inputApplicationHandle.get())->
getInputApplicationHandleObjLocalRef(env);
@@ -153,8 +155,8 @@
static jobject getInputWindowHandleObjLocalRef(JNIEnv* env,
const sp<InputWindowHandle>& inputWindowHandle) {
- if (inputWindowHandle == NULL) {
- return NULL;
+ if (inputWindowHandle == nullptr) {
+ return nullptr;
}
return static_cast<NativeInputWindowHandle*>(inputWindowHandle.get())->
getInputWindowHandleObjLocalRef(env);
@@ -182,6 +184,15 @@
loadSystemIconAsSpriteWithPointerIcon(env, contextObj, style, &pointerIcon, outSpriteIcon);
}
+static void updatePointerControllerFromViewport(
+ sp<PointerController> controller, const DisplayViewport* const viewport) {
+ if (controller != nullptr && viewport != nullptr) {
+ const int32_t width = viewport->logicalRight - viewport->logicalLeft;
+ const int32_t height = viewport->logicalBottom - viewport->logicalTop;
+ controller->setDisplayViewport(width, height, viewport->orientation);
+ }
+}
+
enum {
WM_ACTION_PASS_TO_USER = 1,
};
@@ -203,15 +214,15 @@
void dump(std::string& dump);
- void setVirtualDisplayViewports(JNIEnv* env, jobjectArray viewportObjArray);
- void setDisplayViewport(int32_t viewportType, const DisplayViewport& viewport);
+ void setDisplayViewports(JNIEnv* env, jobjectArray viewportObjArray);
status_t registerInputChannel(JNIEnv* env, const sp<InputChannel>& inputChannel,
const sp<InputWindowHandle>& inputWindowHandle, bool monitor);
status_t unregisterInputChannel(JNIEnv* env, const sp<InputChannel>& inputChannel);
void setInputWindows(JNIEnv* env, jobjectArray windowHandleObjArray, int32_t displayId);
- void setFocusedApplication(JNIEnv* env, jobject applicationHandleObj);
+ void setFocusedApplication(JNIEnv* env, int32_t displayId, jobject applicationHandleObj);
+ void setFocusedDisplay(JNIEnv* env, int32_t displayId);
void setInputDispatchMode(bool enabled, bool frozen);
void setSystemUiVisibility(int32_t visibility);
void setPointerSpeed(int32_t speed);
@@ -277,9 +288,7 @@
Mutex mLock;
struct Locked {
// Display size information.
- DisplayViewport internalViewport;
- DisplayViewport externalViewport;
- Vector<DisplayViewport> virtualViewports;
+ std::vector<DisplayViewport> viewports;
// System UI visibility.
int32_t systemUiVisibility;
@@ -304,7 +313,7 @@
// Input devices to be disabled
SortedVector<int32_t> disabledInputDevices;
- } mLocked;
+ } mLocked GUARDED_BY(mLock);
std::atomic<bool> mInteractive;
@@ -384,8 +393,17 @@
return false;
}
-void NativeInputManager::setVirtualDisplayViewports(JNIEnv* env, jobjectArray viewportObjArray) {
- Vector<DisplayViewport> viewports;
+static const DisplayViewport* findInternalViewport(const std::vector<DisplayViewport>& viewports) {
+ for (const DisplayViewport& v : viewports) {
+ if (v.type == ViewportType::VIEWPORT_INTERNAL) {
+ return &v;
+ }
+ }
+ return nullptr;
+}
+
+void NativeInputManager::setDisplayViewports(JNIEnv* env, jobjectArray viewportObjArray) {
+ std::vector<DisplayViewport> viewports;
if (viewportObjArray) {
jsize length = env->GetArrayLength(viewportObjArray);
@@ -397,57 +415,32 @@
DisplayViewport viewport;
android_hardware_display_DisplayViewport_toNative(env, viewportObj, &viewport);
- ALOGI("Viewport [%d] to add: %s", (int) length, viewport.uniqueId.c_str());
- viewports.push(viewport);
+ ALOGI("Viewport [%d] to add: %s", (int) i, viewport.uniqueId.c_str());
+ viewports.push_back(viewport);
env->DeleteLocalRef(viewportObj);
}
}
+ const DisplayViewport* newInternalViewport = findInternalViewport(viewports);
{
AutoMutex _l(mLock);
- mLocked.virtualViewports = viewports;
+ const DisplayViewport* oldInternalViewport = findInternalViewport(mLocked.viewports);
+ // Internal viewport has changed if there wasn't one earlier, and there is one now, or,
+ // if they are different.
+ const bool internalViewportChanged = (newInternalViewport != nullptr) &&
+ (oldInternalViewport == nullptr || (*oldInternalViewport != *newInternalViewport));
+ if (internalViewportChanged) {
+ sp<PointerController> controller = mLocked.pointerController.promote();
+ updatePointerControllerFromViewport(controller, newInternalViewport);
+ }
+ mLocked.viewports = viewports;
}
mInputManager->getReader()->requestRefreshConfiguration(
InputReaderConfiguration::CHANGE_DISPLAY_INFO);
}
-void NativeInputManager::setDisplayViewport(int32_t type, const DisplayViewport& viewport) {
- bool changed = false;
- {
- AutoMutex _l(mLock);
-
- ViewportType viewportType = static_cast<ViewportType>(type);
- DisplayViewport* v = NULL;
- if (viewportType == ViewportType::VIEWPORT_EXTERNAL) {
- v = &mLocked.externalViewport;
- } else if (viewportType == ViewportType::VIEWPORT_INTERNAL) {
- v = &mLocked.internalViewport;
- }
-
- if (v != NULL && *v != viewport) {
- changed = true;
- *v = viewport;
-
- if (viewportType == ViewportType::VIEWPORT_INTERNAL) {
- sp<PointerController> controller = mLocked.pointerController.promote();
- if (controller != NULL) {
- controller->setDisplayViewport(
- viewport.logicalRight - viewport.logicalLeft,
- viewport.logicalBottom - viewport.logicalTop,
- viewport.orientation);
- }
- }
- }
- }
-
- if (changed) {
- mInputManager->getReader()->requestRefreshConfiguration(
- InputReaderConfiguration::CHANGE_DISPLAY_INFO);
- }
-}
-
status_t NativeInputManager::registerInputChannel(JNIEnv* /* env */,
const sp<InputChannel>& inputChannel,
const sp<InputWindowHandle>& inputWindowHandle, bool monitor) {
@@ -479,7 +472,7 @@
jsize length = env->GetArrayLength(excludedDeviceNames);
for (jsize i = 0; i < length; i++) {
jstring item = jstring(env->GetObjectArrayElement(excludedDeviceNames, i));
- const char* deviceNameChars = env->GetStringUTFChars(item, NULL);
+ const char* deviceNameChars = env->GetStringUTFChars(item, nullptr);
outConfig->excludedDeviceNames.push_back(deviceNameChars);
env->ReleaseStringUTFChars(item, deviceNameChars);
env->DeleteLocalRef(item);
@@ -526,11 +519,7 @@
outConfig->pointerCapture = mLocked.pointerCapture;
- outConfig->setPhysicalDisplayViewport(ViewportType::VIEWPORT_INTERNAL,
- mLocked.internalViewport);
- outConfig->setPhysicalDisplayViewport(ViewportType::VIEWPORT_EXTERNAL,
- mLocked.externalViewport);
- outConfig->setVirtualDisplayViewports(mLocked.virtualViewports);
+ outConfig->setDisplayViewports(mLocked.viewports);
outConfig->disabledDevices = mLocked.disabledInputDevices;
} // release lock
@@ -541,25 +530,22 @@
AutoMutex _l(mLock);
sp<PointerController> controller = mLocked.pointerController.promote();
- if (controller == NULL) {
+ if (controller == nullptr) {
ensureSpriteControllerLocked();
controller = new PointerController(this, mLooper, mLocked.spriteController);
mLocked.pointerController = controller;
- DisplayViewport& v = mLocked.internalViewport;
- controller->setDisplayViewport(
- v.logicalRight - v.logicalLeft,
- v.logicalBottom - v.logicalTop,
- v.orientation);
+ const DisplayViewport* internalViewport = findInternalViewport(mLocked.viewports);
+ updatePointerControllerFromViewport(controller, internalViewport);
updateInactivityTimeoutLocked(controller);
}
return controller;
}
-void NativeInputManager::ensureSpriteControllerLocked() {
- if (mLocked.spriteController == NULL) {
+void NativeInputManager::ensureSpriteControllerLocked() REQUIRES(mLock) {
+ if (mLocked.spriteController == nullptr) {
JNIEnv* env = jniEnv();
jint layer = env->CallIntMethod(mServiceObj, gServiceClassInfo.getPointerLayer);
if (checkAndClearExceptionFromCallback(env, "getPointerLayer")) {
@@ -575,7 +561,7 @@
size_t count = inputDevices.size();
jobjectArray inputDevicesObjArray = env->NewObjectArray(
- count, gInputDeviceClassInfo.clazz, NULL);
+ count, gInputDeviceClassInfo.clazz, nullptr);
if (inputDevicesObjArray) {
bool error = false;
for (size_t i = 0; i < count; i++) {
@@ -750,7 +736,7 @@
sp<InputWindowHandle> windowHandle =
android_server_InputWindowHandle_getHandle(env, windowHandleObj);
- if (windowHandle != NULL) {
+ if (windowHandle != nullptr) {
windowHandles.push(windowHandle);
}
env->DeleteLocalRef(windowHandleObj);
@@ -786,10 +772,15 @@
}
}
-void NativeInputManager::setFocusedApplication(JNIEnv* env, jobject applicationHandleObj) {
+void NativeInputManager::setFocusedApplication(JNIEnv* env, int32_t displayId,
+ jobject applicationHandleObj) {
sp<InputApplicationHandle> applicationHandle =
android_server_InputApplicationHandle_getHandle(env, applicationHandleObj);
- mInputManager->getDispatcher()->setFocusedApplication(applicationHandle);
+ mInputManager->getDispatcher()->setFocusedApplication(displayId, applicationHandle);
+}
+
+void NativeInputManager::setFocusedDisplay(JNIEnv* env, int32_t displayId) {
+ mInputManager->getDispatcher()->setFocusedDisplay(displayId);
}
void NativeInputManager::setInputDispatchMode(bool enabled, bool frozen) {
@@ -803,13 +794,14 @@
mLocked.systemUiVisibility = visibility;
sp<PointerController> controller = mLocked.pointerController.promote();
- if (controller != NULL) {
+ if (controller != nullptr) {
updateInactivityTimeoutLocked(controller);
}
}
}
-void NativeInputManager::updateInactivityTimeoutLocked(const sp<PointerController>& controller) {
+void NativeInputManager::updateInactivityTimeoutLocked(const sp<PointerController>& controller)
+ REQUIRES(mLock) {
bool lightsOut = mLocked.systemUiVisibility & ASYSTEM_UI_VISIBILITY_STATUS_BAR_HIDDEN;
controller->setInactivityTimeout(lightsOut
? PointerController::INACTIVITY_TIMEOUT_SHORT
@@ -894,7 +886,7 @@
void NativeInputManager::setPointerIconType(int32_t iconId) {
AutoMutex _l(mLock);
sp<PointerController> controller = mLocked.pointerController.promote();
- if (controller != NULL) {
+ if (controller != nullptr) {
controller->updatePointerIcon(iconId);
}
}
@@ -902,7 +894,7 @@
void NativeInputManager::reloadPointerIcons() {
AutoMutex _l(mLock);
sp<PointerController> controller = mLocked.pointerController.promote();
- if (controller != NULL) {
+ if (controller != nullptr) {
controller->reloadPointerResources();
}
}
@@ -910,7 +902,7 @@
void NativeInputManager::setCustomPointerIcon(const SpriteIcon& icon) {
AutoMutex _l(mLock);
sp<PointerController> controller = mLocked.pointerController.promote();
- if (controller != NULL) {
+ if (controller != nullptr) {
controller->setCustomPointerIcon(icon);
}
}
@@ -1122,7 +1114,7 @@
gServiceClassInfo.dispatchUnhandledKey,
inputWindowHandleObj, keyEventObj, policyFlags);
if (checkAndClearExceptionFromCallback(env, "dispatchUnhandledKey")) {
- fallbackKeyEventObj = NULL;
+ fallbackKeyEventObj = nullptr;
}
android_view_KeyEvent_recycle(env, keyEventObj);
env->DeleteLocalRef(keyEventObj);
@@ -1235,7 +1227,7 @@
static jlong nativeInit(JNIEnv* env, jclass /* clazz */,
jobject serviceObj, jobject contextObj, jobject messageQueueObj) {
sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
- if (messageQueue == NULL) {
+ if (messageQueue == nullptr) {
jniThrowRuntimeException(env, "MessageQueue is not initialized.");
return 0;
}
@@ -1255,37 +1247,10 @@
}
}
-static void nativeSetVirtualDisplayViewports(JNIEnv* env, jclass /* clazz */, jlong ptr,
+static void nativeSetDisplayViewports(JNIEnv* env, jclass /* clazz */, jlong ptr,
jobjectArray viewportObjArray) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
- im->setVirtualDisplayViewports(env, viewportObjArray);
-}
-
-static void nativeSetDisplayViewport(JNIEnv* env, jclass /* clazz */, jlong ptr,
- jint viewportType, jint displayId, jint orientation,
- jint logicalLeft, jint logicalTop, jint logicalRight, jint logicalBottom,
- jint physicalLeft, jint physicalTop, jint physicalRight, jint physicalBottom,
- jint deviceWidth, jint deviceHeight, jstring uniqueId) {
- NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
-
- DisplayViewport v;
- v.displayId = displayId;
- v.orientation = orientation;
- v.logicalLeft = logicalLeft;
- v.logicalTop = logicalTop;
- v.logicalRight = logicalRight;
- v.logicalBottom = logicalBottom;
- v.physicalLeft = physicalLeft;
- v.physicalTop = physicalTop;
- v.physicalRight = physicalRight;
- v.physicalBottom = physicalBottom;
- v.deviceWidth = deviceWidth;
- v.deviceHeight = deviceHeight;
- if (uniqueId != nullptr) {
- v.uniqueId = ScopedUtfChars(env, uniqueId).c_str();
- }
-
- im->setDisplayViewport(viewportType, v);
+ im->setDisplayViewports(env, viewportObjArray);
}
static jint nativeGetScanCodeState(JNIEnv* /* env */, jclass /* clazz */,
@@ -1316,8 +1281,8 @@
jlong ptr, jint deviceId, jint sourceMask, jintArray keyCodes, jbooleanArray outFlags) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
- int32_t* codes = env->GetIntArrayElements(keyCodes, NULL);
- uint8_t* flags = env->GetBooleanArrayElements(outFlags, NULL);
+ int32_t* codes = env->GetIntArrayElements(keyCodes, nullptr);
+ uint8_t* flags = env->GetBooleanArrayElements(outFlags, nullptr);
jsize numCodes = env->GetArrayLength(keyCodes);
jboolean result;
if (numCodes == env->GetArrayLength(keyCodes)) {
@@ -1356,7 +1321,7 @@
sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
inputChannelObj);
- if (inputChannel == NULL) {
+ if (inputChannel == nullptr) {
throwInputChannelNotInitialized(env);
return;
}
@@ -1385,12 +1350,12 @@
sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
inputChannelObj);
- if (inputChannel == NULL) {
+ if (inputChannel == nullptr) {
throwInputChannelNotInitialized(env);
return;
}
- android_view_InputChannel_setDisposeCallback(env, inputChannelObj, NULL, NULL);
+ android_view_InputChannel_setDisposeCallback(env, inputChannelObj, nullptr, nullptr);
status_t status = im->unregisterInputChannel(env, inputChannel);
if (status && status != BAD_VALUE) { // ignore already unregistered channel
@@ -1454,10 +1419,17 @@
}
static void nativeSetFocusedApplication(JNIEnv* env, jclass /* clazz */,
- jlong ptr, jobject applicationHandleObj) {
+ jlong ptr, jint displayId, jobject applicationHandleObj) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
- im->setFocusedApplication(env, applicationHandleObj);
+ im->setFocusedApplication(env, displayId, applicationHandleObj);
+}
+
+static void nativeSetFocusedDisplay(JNIEnv* env, jclass /* clazz */,
+ jlong ptr, jint displayId) {
+ NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
+
+ im->setFocusedDisplay(env, displayId);
}
static void nativeSetPointerCapture(JNIEnv* env, jclass /* clazz */, jlong ptr,
@@ -1490,7 +1462,7 @@
sp<InputChannel> toChannel =
android_view_InputChannel_getInputChannel(env, toChannelObj);
- if (fromChannel == NULL || toChannel == NULL) {
+ if (fromChannel == nullptr || toChannel == nullptr) {
return JNI_FALSE;
}
@@ -1543,7 +1515,7 @@
}
jlong* patternMillis = static_cast<jlong*>(env->GetPrimitiveArrayCritical(
- patternObj, NULL));
+ patternObj, nullptr));
nsecs_t pattern[patternSize];
for (size_t i = 0; i < patternSize; i++) {
pattern[i] = max(jlong(0), min(patternMillis[i],
@@ -1656,10 +1628,8 @@
(void*) nativeInit },
{ "nativeStart", "(J)V",
(void*) nativeStart },
- { "nativeSetVirtualDisplayViewports", "(J[Landroid/hardware/display/DisplayViewport;)V",
- (void*) nativeSetVirtualDisplayViewports },
- { "nativeSetDisplayViewport", "(JIIIIIIIIIIIIILjava/lang/String;)V",
- (void*) nativeSetDisplayViewport },
+ { "nativeSetDisplayViewports", "(J[Landroid/hardware/display/DisplayViewport;)V",
+ (void*) nativeSetDisplayViewports },
{ "nativeGetScanCodeState", "(JIII)I",
(void*) nativeGetScanCodeState },
{ "nativeGetKeyCodeState", "(JIII)I",
@@ -1681,8 +1651,10 @@
(void*) nativeToggleCapsLock },
{ "nativeSetInputWindows", "(J[Lcom/android/server/input/InputWindowHandle;I)V",
(void*) nativeSetInputWindows },
- { "nativeSetFocusedApplication", "(JLcom/android/server/input/InputApplicationHandle;)V",
+ { "nativeSetFocusedApplication", "(JILcom/android/server/input/InputApplicationHandle;)V",
(void*) nativeSetFocusedApplication },
+ { "nativeSetFocusedDisplay", "(JI)V",
+ (void*) nativeSetFocusedDisplay },
{ "nativeSetPointerCapture", "(JZ)V",
(void*) nativeSetPointerCapture },
{ "nativeSetInputDispatchMode", "(JZZ)V",
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/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/Android.bp b/services/devicepolicy/Android.bp
index 0505204..47790ce 100644
--- a/services/devicepolicy/Android.bp
+++ b/services/devicepolicy/Android.bp
@@ -3,7 +3,6 @@
srcs: ["java/**/*.java"],
libs: [
- "conscrypt",
"services.core",
],
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
index 84de6b4..66cf48c 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
@@ -15,20 +15,10 @@
*/
package com.android.server.devicepolicy;
-import android.annotation.UserIdInt;
import android.app.admin.IDevicePolicyManager;
-import android.content.ComponentName;
-import android.os.PersistableBundle;
-import android.security.keymaster.KeymasterCertificateChain;
-import android.security.keystore.ParcelableKeyGenParameterSpec;
-import android.telephony.data.ApnSetting;
import com.android.server.SystemService;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
/**
* Defines the required interface for IDevicePolicyManager implemenation.
*
@@ -64,94 +54,10 @@
*/
abstract void handleStopUser(int userId);
- public void setSystemSetting(ComponentName who, String setting, String value){}
-
- public void transferOwnership(ComponentName admin, ComponentName target, PersistableBundle bundle) {}
-
- public PersistableBundle getTransferOwnershipBundle() {
- return null;
- }
-
- public boolean generateKeyPair(ComponentName who, String callerPackage, String algorithm,
- ParcelableKeyGenParameterSpec keySpec, int idAttestationFlags,
- KeymasterCertificateChain attestationChain) {
- return false;
- }
-
- public boolean isUsingUnifiedPassword(ComponentName who) {
- return true;
- }
-
- public boolean setKeyPairCertificate(ComponentName who, String callerPackage, String alias,
- byte[] cert, byte[] chain, boolean isUserSelectable) {
- return false;
- }
-
- @Override
- public void setStartUserSessionMessage(
- ComponentName admin, CharSequence startUserSessionMessage) {}
-
- @Override
- public void setEndUserSessionMessage(ComponentName admin, CharSequence endUserSessionMessage) {}
-
- @Override
- public String getStartUserSessionMessage(ComponentName admin) {
- return null;
- }
-
- @Override
- public String getEndUserSessionMessage(ComponentName admin) {
- return null;
- }
-
- @Override
- public List<String> setMeteredDataDisabledPackages(ComponentName admin, List<String> packageNames) {
- return packageNames;
- }
-
- @Override
- public List<String> getMeteredDataDisabledPackages(ComponentName admin) {
- return new ArrayList<>();
- }
-
- @Override
- public int addOverrideApn(ComponentName admin, ApnSetting apnSetting) {
- return -1;
- }
-
- @Override
- public boolean updateOverrideApn(ComponentName admin, int apnId, ApnSetting apnSetting) {
- return false;
- }
-
- @Override
- public boolean removeOverrideApn(ComponentName admin, int apnId) {
- return false;
- }
-
- @Override
- public List<ApnSetting> getOverrideApns(ComponentName admin) {
- return Collections.emptyList();
- }
-
- @Override
- public void setOverrideApnsEnabled(ComponentName admin, boolean enabled) {}
-
- @Override
- public boolean isOverrideApnEnabled(ComponentName admin) {
- return false;
- }
-
public void clearSystemUpdatePolicyFreezePeriodRecord() {
}
@Override
- public boolean isMeteredDataDisabledPackageForUser(ComponentName admin,
- String packageName, int userId) {
- return false;
- }
-
- @Override
public long forceNetworkLogs() {
return 0;
}
@@ -160,8 +66,4 @@
public long forceSecurityLogs() {
return 0;
}
-
- @Override
- public void setDefaultSmsApplication(ComponentName admin, String packageName) {
- }
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DeviceAdminServiceController.java b/services/devicepolicy/java/com/android/server/devicepolicy/DeviceAdminServiceController.java
index 0c0ce8d..85ca52e 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.
@@ -70,7 +66,13 @@
super(TAG, mContext, mHandler, userId, componentName,
mConstants.DAS_DIED_SERVICE_RECONNECT_BACKOFF_SEC,
mConstants.DAS_DIED_SERVICE_RECONNECT_BACKOFF_INCREASE,
- mConstants.DAS_DIED_SERVICE_RECONNECT_MAX_BACKOFF_SEC);
+ mConstants.DAS_DIED_SERVICE_RECONNECT_MAX_BACKOFF_SEC,
+ mConstants.DAS_DIED_SERVICE_STABLE_CONNECTION_THRESHOLD_SEC);
+ }
+
+ @Override
+ protected int getBindFlags() {
+ return Context.BIND_FOREGROUND_SERVICE;
}
@Override
@@ -100,40 +102,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/DevicePolicyConstants.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyConstants.java
index 616c669..71fea02 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyConstants.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyConstants.java
@@ -30,14 +30,17 @@
public class DevicePolicyConstants {
private static final String TAG = DevicePolicyManagerService.LOG_TAG;
- private static final String DAS_DIED_SERVICE_RECONNECT_BACKOFF_SEC_KEY
- = "das_died_service_reconnect_backoff_sec";
+ private static final String DAS_DIED_SERVICE_RECONNECT_BACKOFF_SEC_KEY =
+ "das_died_service_reconnect_backoff_sec";
- private static final String DAS_DIED_SERVICE_RECONNECT_BACKOFF_INCREASE_KEY
- = "das_died_service_reconnect_backoff_increase";
+ private static final String DAS_DIED_SERVICE_RECONNECT_BACKOFF_INCREASE_KEY =
+ "das_died_service_reconnect_backoff_increase";
- private static final String DAS_DIED_SERVICE_RECONNECT_MAX_BACKOFF_SEC_KEY
- = "das_died_service_reconnect_max_backoff_sec";
+ private static final String DAS_DIED_SERVICE_RECONNECT_MAX_BACKOFF_SEC_KEY =
+ "das_died_service_reconnect_max_backoff_sec";
+
+ private static final String DAS_DIED_SERVICE_STABLE_CONNECTION_THRESHOLD_SEC_KEY =
+ "das_died_service_stable_connection_threshold_sec";
/**
* The back-off before re-connecting, when a service binding died, due to the owner
@@ -55,6 +58,11 @@
*/
public final long DAS_DIED_SERVICE_RECONNECT_MAX_BACKOFF_SEC;
+ /**
+ * If a connection lasts more than this duration, we reset the re-connect back-off time.
+ */
+ public final long DAS_DIED_SERVICE_STABLE_CONNECTION_THRESHOLD_SEC;
+
private DevicePolicyConstants(String settings) {
final KeyValueListParser parser = new KeyValueListParser(',');
@@ -75,6 +83,10 @@
long dasDiedServiceReconnectMaxBackoffSec = parser.getLong(
DAS_DIED_SERVICE_RECONNECT_MAX_BACKOFF_SEC_KEY, TimeUnit.DAYS.toSeconds(1));
+ long dasDiedServiceStableConnectionThresholdSec = parser.getLong(
+ DAS_DIED_SERVICE_STABLE_CONNECTION_THRESHOLD_SEC_KEY,
+ TimeUnit.MINUTES.toSeconds(2));
+
// Set minimum: 5 seconds.
dasDiedServiceReconnectBackoffSec = Math.max(5, dasDiedServiceReconnectBackoffSec);
@@ -89,7 +101,8 @@
DAS_DIED_SERVICE_RECONNECT_BACKOFF_SEC = dasDiedServiceReconnectBackoffSec;
DAS_DIED_SERVICE_RECONNECT_BACKOFF_INCREASE = dasDiedServiceReconnectBackoffIncrease;
DAS_DIED_SERVICE_RECONNECT_MAX_BACKOFF_SEC = dasDiedServiceReconnectMaxBackoffSec;
-
+ DAS_DIED_SERVICE_STABLE_CONNECTION_THRESHOLD_SEC =
+ dasDiedServiceStableConnectionThresholdSec;
}
public static DevicePolicyConstants loadFromString(String settings) {
@@ -102,14 +115,18 @@
pw.print(prefix);
pw.print(" DAS_DIED_SERVICE_RECONNECT_BACKOFF_SEC: ");
- pw.println( DAS_DIED_SERVICE_RECONNECT_BACKOFF_SEC);
+ pw.println(DAS_DIED_SERVICE_RECONNECT_BACKOFF_SEC);
pw.print(prefix);
pw.print(" DAS_DIED_SERVICE_RECONNECT_BACKOFF_INCREASE: ");
- pw.println( DAS_DIED_SERVICE_RECONNECT_BACKOFF_INCREASE);
+ pw.println(DAS_DIED_SERVICE_RECONNECT_BACKOFF_INCREASE);
pw.print(prefix);
pw.print(" DAS_DIED_SERVICE_RECONNECT_MAX_BACKOFF_SEC: ");
- pw.println( DAS_DIED_SERVICE_RECONNECT_MAX_BACKOFF_SEC);
+ pw.println(DAS_DIED_SERVICE_RECONNECT_MAX_BACKOFF_SEC);
+
+ pw.print(prefix);
+ pw.print(" DAS_DIED_SERVICE_STABLE_CONNECTION_THRESHOLD_SEC: ");
+ pw.println(DAS_DIED_SERVICE_STABLE_CONNECTION_THRESHOLD_SEC);
}
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index e76afa3..eeb4ad3 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -10106,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/net/java/android/net/dhcp/DhcpPacket.java b/services/net/java/android/net/dhcp/DhcpPacket.java
index 77a3e21..6ba7d94 100644
--- a/services/net/java/android/net/dhcp/DhcpPacket.java
+++ b/services/net/java/android/net/dhcp/DhcpPacket.java
@@ -1,5 +1,8 @@
package android.net.dhcp;
+import static android.net.util.NetworkConstants.IPV4_MAX_MTU;
+import static android.net.util.NetworkConstants.IPV4_MIN_MTU;
+
import android.annotation.Nullable;
import android.net.DhcpResults;
import android.net.LinkAddress;
@@ -381,6 +384,26 @@
}
/**
+ * Returns whether a parameter is included in the parameter request list option of this packet.
+ *
+ * <p>If there is no parameter request list option in the packet, false is returned.
+ *
+ * @param paramId ID of the parameter, such as {@link #DHCP_MTU} or {@link #DHCP_HOST_NAME}.
+ */
+ public boolean hasRequestedParam(byte paramId) {
+ if (mRequestedParams == null) {
+ return false;
+ }
+
+ for (byte reqParam : mRequestedParams) {
+ if (reqParam == paramId) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
* Creates a new L3 packet (including IP header) containing the
* DHCP udp packet. This method relies upon the delegated method
* finishPacket() to insert the per-packet contents.
@@ -696,7 +719,11 @@
addTlv(buf, DHCP_ROUTER, mGateways);
addTlv(buf, DHCP_DNS_SERVER, mDnsServers);
addTlv(buf, DHCP_DOMAIN_NAME, mDomainName);
+ addTlv(buf, DHCP_HOST_NAME, mHostName);
addTlv(buf, DHCP_VENDOR_INFO, mVendorInfo);
+ if (mMtu != null && Short.toUnsignedInt(mMtu) >= IPV4_MIN_MTU) {
+ addTlv(buf, DHCP_MTU, mMtu);
+ }
}
/**
@@ -1259,7 +1286,8 @@
boolean broadcast, Inet4Address serverIpAddr, Inet4Address relayIp,
Inet4Address yourIp, byte[] mac, Integer timeout, Inet4Address netMask,
Inet4Address bcAddr, List<Inet4Address> gateways, List<Inet4Address> dnsServers,
- Inet4Address dhcpServerIdentifier, String domainName, boolean metered) {
+ Inet4Address dhcpServerIdentifier, String domainName, String hostname, boolean metered,
+ short mtu) {
DhcpPacket pkt = new DhcpOfferPacket(
transactionId, (short) 0, broadcast, serverIpAddr, relayIp,
INADDR_ANY /* clientIp */, yourIp, mac);
@@ -1267,9 +1295,11 @@
pkt.mDnsServers = dnsServers;
pkt.mLeaseTime = timeout;
pkt.mDomainName = domainName;
+ pkt.mHostName = hostname;
pkt.mServerIdentifier = dhcpServerIdentifier;
pkt.mSubnetMask = netMask;
pkt.mBroadcastAddress = bcAddr;
+ pkt.mMtu = mtu;
if (metered) {
pkt.mVendorInfo = VENDOR_INFO_ANDROID_METERED;
}
@@ -1283,7 +1313,8 @@
boolean broadcast, Inet4Address serverIpAddr, Inet4Address relayIp, Inet4Address yourIp,
Inet4Address requestClientIp, byte[] mac, Integer timeout, Inet4Address netMask,
Inet4Address bcAddr, List<Inet4Address> gateways, List<Inet4Address> dnsServers,
- Inet4Address dhcpServerIdentifier, String domainName, boolean metered) {
+ Inet4Address dhcpServerIdentifier, String domainName, String hostname, boolean metered,
+ short mtu) {
DhcpPacket pkt = new DhcpAckPacket(
transactionId, (short) 0, broadcast, serverIpAddr, relayIp, requestClientIp, yourIp,
mac);
@@ -1291,9 +1322,11 @@
pkt.mDnsServers = dnsServers;
pkt.mLeaseTime = timeout;
pkt.mDomainName = domainName;
+ pkt.mHostName = hostname;
pkt.mSubnetMask = netMask;
pkt.mServerIdentifier = dhcpServerIdentifier;
pkt.mBroadcastAddress = bcAddr;
+ pkt.mMtu = mtu;
if (metered) {
pkt.mVendorInfo = VENDOR_INFO_ANDROID_METERED;
}
diff --git a/services/net/java/android/net/dhcp/DhcpServer.java b/services/net/java/android/net/dhcp/DhcpServer.java
index 2b3d577..cee6fa9 100644
--- a/services/net/java/android/net/dhcp/DhcpServer.java
+++ b/services/net/java/android/net/dhcp/DhcpServer.java
@@ -20,6 +20,7 @@
import static android.net.NetworkUtils.getPrefixMaskAsInet4Address;
import static android.net.TrafficStats.TAG_SYSTEM_DHCP_SERVER;
import static android.net.dhcp.DhcpPacket.DHCP_CLIENT;
+import static android.net.dhcp.DhcpPacket.DHCP_HOST_NAME;
import static android.net.dhcp.DhcpPacket.DHCP_SERVER;
import static android.net.dhcp.DhcpPacket.ENCAP_BOOTP;
import static android.net.dhcp.DhcpPacket.INFINITE_LEASE;
@@ -46,6 +47,7 @@
import android.os.SystemClock;
import android.system.ErrnoException;
import android.system.Os;
+import android.text.TextUtils;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.HexDump;
@@ -350,6 +352,19 @@
return isEmpty(request.mClientIp) && (request.mBroadcast || isEmpty(lease.getNetAddr()));
}
+ /**
+ * Get the hostname from a lease if non-empty and requested in the incoming request.
+ * @param request The incoming request.
+ * @return The hostname, or null if not requested or empty.
+ */
+ @Nullable
+ private static String getHostnameIfRequested(@NonNull DhcpPacket request,
+ @NonNull DhcpLease lease) {
+ return request.hasRequestedParam(DHCP_HOST_NAME) && !TextUtils.isEmpty(lease.getHostname())
+ ? lease.getHostname()
+ : null;
+ }
+
private boolean transmitOffer(@NonNull DhcpPacket request, @NonNull DhcpLease lease,
@NonNull MacAddress clientMac) {
final boolean broadcastFlag = getBroadcastFlag(request, lease);
@@ -358,12 +373,14 @@
getPrefixMaskAsInet4Address(mServingParams.serverAddr.getPrefixLength());
final Inet4Address broadcastAddr = getBroadcastAddress(
mServingParams.getServerInet4Addr(), mServingParams.serverAddr.getPrefixLength());
+ final String hostname = getHostnameIfRequested(request, lease);
final ByteBuffer offerPacket = DhcpPacket.buildOfferPacket(
ENCAP_BOOTP, request.mTransId, broadcastFlag, mServingParams.getServerInet4Addr(),
request.mRelayIp, lease.getNetAddr(), request.mClientMac, timeout, prefixMask,
broadcastAddr, new ArrayList<>(mServingParams.defaultRouters),
new ArrayList<>(mServingParams.dnsServers),
- mServingParams.getServerInet4Addr(), null /* domainName */, mServingParams.metered);
+ mServingParams.getServerInet4Addr(), null /* domainName */, hostname,
+ mServingParams.metered, (short) mServingParams.linkMtu);
return transmitOfferOrAckPacket(offerPacket, request, lease, clientMac, broadcastFlag);
}
@@ -374,13 +391,15 @@
// transmitOffer above
final boolean broadcastFlag = getBroadcastFlag(request, lease);
final int timeout = getLeaseTimeout(lease);
+ final String hostname = getHostnameIfRequested(request, lease);
final ByteBuffer ackPacket = DhcpPacket.buildAckPacket(ENCAP_BOOTP, request.mTransId,
broadcastFlag, mServingParams.getServerInet4Addr(), request.mRelayIp,
lease.getNetAddr(), request.mClientIp, request.mClientMac, timeout,
mServingParams.getPrefixMaskAsAddress(), mServingParams.getBroadcastAddress(),
new ArrayList<>(mServingParams.defaultRouters),
new ArrayList<>(mServingParams.dnsServers),
- mServingParams.getServerInet4Addr(), null /* domainName */, mServingParams.metered);
+ mServingParams.getServerInet4Addr(), null /* domainName */, hostname,
+ mServingParams.metered, (short) mServingParams.linkMtu);
return transmitOfferOrAckPacket(ackPacket, request, lease, clientMac, broadcastFlag);
}
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/src/com/android/server/backup/fullbackup/AppMetadataBackupWriterTest.java b/services/robotests/src/com/android/server/backup/fullbackup/AppMetadataBackupWriterTest.java
index 112e1e3..b771039 100644
--- a/services/robotests/src/com/android/server/backup/fullbackup/AppMetadataBackupWriterTest.java
+++ b/services/robotests/src/com/android/server/backup/fullbackup/AppMetadataBackupWriterTest.java
@@ -183,7 +183,6 @@
new Signature[] {new Signature("1234"), new Signature("5678")},
SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
- null,
null));
File manifestFile = createFile(BACKUP_MANIFEST_FILENAME);
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/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
new file mode 100644
index 0000000..6665070
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.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;
+
+import static androidx.test.InstrumentationRegistry.getContext;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+import static com.android.server.DeviceIdleController.LIGHT_STATE_ACTIVE;
+import static com.android.server.DeviceIdleController.LIGHT_STATE_IDLE;
+import static com.android.server.DeviceIdleController.LIGHT_STATE_IDLE_MAINTENANCE;
+import static com.android.server.DeviceIdleController.LIGHT_STATE_INACTIVE;
+import static com.android.server.DeviceIdleController.LIGHT_STATE_OVERRIDE;
+import static com.android.server.DeviceIdleController.LIGHT_STATE_PRE_IDLE;
+import static com.android.server.DeviceIdleController.LIGHT_STATE_WAITING_FOR_NETWORK;
+import static com.android.server.DeviceIdleController.STATE_ACTIVE;
+import static com.android.server.DeviceIdleController.STATE_IDLE;
+import static com.android.server.DeviceIdleController.STATE_IDLE_MAINTENANCE;
+import static com.android.server.DeviceIdleController.STATE_IDLE_PENDING;
+import static com.android.server.DeviceIdleController.STATE_INACTIVE;
+import static com.android.server.DeviceIdleController.STATE_LOCATING;
+import static com.android.server.DeviceIdleController.STATE_SENSING;
+import static com.android.server.DeviceIdleController.lightStateToString;
+import static com.android.server.DeviceIdleController.stateToString;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+
+import android.app.ActivityManagerInternal;
+import android.app.AlarmManager;
+import android.app.IActivityManager;
+import android.content.Context;
+import android.hardware.SensorManager;
+import android.location.LocationManager;
+import android.location.LocationProvider;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.PowerManager;
+import android.os.PowerManagerInternal;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.net.NetworkPolicyManagerInternal;
+import com.android.server.wm.ActivityTaskManagerInternal;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
+
+/**
+ * Tests for {@link com.android.server.DeviceIdleController}.
+ */
+@RunWith(AndroidJUnit4.class)
+public class DeviceIdleControllerTest {
+ private DeviceIdleController mDeviceIdleController;
+ private AnyMotionDetectorForTest mAnyMotionDetector;
+ private AppStateTrackerForTest mAppStateTracker;
+
+ private MockitoSession mMockingSession;
+ @Mock
+ private PowerManager mPowerManager;
+ @Mock
+ private PowerManager.WakeLock mWakeLock;
+ @Mock
+ private AlarmManager mAlarmManager;
+ @Mock
+ private LocationManager mLocationManager;
+ @Mock
+ private IActivityManager mIActivityManager;
+
+ class InjectorForTest extends DeviceIdleController.Injector {
+
+ InjectorForTest(Context ctx) {
+ super(ctx);
+ }
+
+ @Override
+ AlarmManager getAlarmManager() {
+ return mAlarmManager;
+ }
+
+ @Override
+ AnyMotionDetector getAnyMotionDetector(Handler handler, SensorManager sm,
+ AnyMotionDetector.DeviceIdleCallback callback, float angleThreshold) {
+ return mAnyMotionDetector;
+ }
+
+ @Override
+ AppStateTracker getAppStateTracker(Context ctx, Looper loop) {
+ return mAppStateTracker;
+ }
+
+ @Override
+ ConnectivityService getConnectivityService() {
+ return null;
+ }
+
+ @Override
+ LocationManager getLocationManager() {
+ return mLocationManager;
+ }
+
+ @Override
+ DeviceIdleController.MyHandler getHandler(DeviceIdleController ctlr) {
+ return mock(DeviceIdleController.MyHandler.class);
+ }
+
+ @Override
+ PowerManager getPowerManager() {
+ return mPowerManager;
+ }
+ }
+
+ private class AnyMotionDetectorForTest extends AnyMotionDetector {
+ boolean isMonitoring = false;
+
+ AnyMotionDetectorForTest() {
+ super(mPowerManager, mock(Handler.class), mock(SensorManager.class),
+ mock(DeviceIdleCallback.class), 0.5f);
+ }
+
+ @Override
+ public void checkForAnyMotion() {
+ isMonitoring = true;
+ }
+
+ @Override
+ public void stop() {
+ isMonitoring = false;
+ }
+ }
+
+ private class AppStateTrackerForTest extends AppStateTracker {
+ AppStateTrackerForTest(Context ctx, Looper looper) {
+ super(ctx, looper);
+ }
+
+ @Override
+ public void onSystemServicesReady() {
+ // Do nothing.
+ }
+
+ @Override
+ IActivityManager injectIActivityManager() {
+ return mIActivityManager;
+ }
+ }
+
+ @Before
+ public void setUp() {
+ mMockingSession = mockitoSession()
+ .initMocks(this)
+ .strictness(Strictness.LENIENT)
+ .mockStatic(LocalServices.class)
+ .startMocking();
+ doReturn(mock(ActivityManagerInternal.class))
+ .when(() -> LocalServices.getService(ActivityManagerInternal.class));
+ doReturn(mock(ActivityTaskManagerInternal.class))
+ .when(() -> LocalServices.getService(ActivityTaskManagerInternal.class));
+ doReturn(mock(PowerManagerInternal.class))
+ .when(() -> LocalServices.getService(PowerManagerInternal.class));
+ doReturn(mock(NetworkPolicyManagerInternal.class))
+ .when(() -> LocalServices.getService(NetworkPolicyManagerInternal.class));
+ when(mPowerManager.newWakeLock(anyInt(), anyString())).thenReturn(mWakeLock);
+ doNothing().when(mWakeLock).acquire();
+ mAppStateTracker = new AppStateTrackerForTest(getContext(), Looper.getMainLooper());
+ mAnyMotionDetector = new AnyMotionDetectorForTest();
+ mDeviceIdleController = new DeviceIdleController(getContext(),
+ new InjectorForTest(getContext()));
+ spyOn(mDeviceIdleController);
+ doNothing().when(mDeviceIdleController).publishBinderService(any(), any());
+ mDeviceIdleController.onStart();
+ mDeviceIdleController.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
+ mDeviceIdleController.setDeepEnabledForTest(true);
+ mDeviceIdleController.setLightEnabledForTest(true);
+ }
+
+ @After
+ public void tearDown() {
+ if (mMockingSession != null) {
+ mMockingSession.finishMocking();
+ }
+ // DeviceIdleController adds this to LocalServices in the constructor, so we have to remove
+ // it after each test, otherwise, subsequent tests will fail.
+ LocalServices.removeServiceForTest(AppStateTracker.class);
+ }
+
+ @Test
+ public void testUpdateInteractivityLocked() {
+ doReturn(false).when(mPowerManager).isInteractive();
+ mDeviceIdleController.updateInteractivityLocked();
+ assertFalse(mDeviceIdleController.isScreenOn());
+
+ // Make sure setting false when screen is already off doesn't change anything.
+ doReturn(false).when(mPowerManager).isInteractive();
+ mDeviceIdleController.updateInteractivityLocked();
+ assertFalse(mDeviceIdleController.isScreenOn());
+
+ // Test changing from screen off to screen on.
+ doReturn(true).when(mPowerManager).isInteractive();
+ mDeviceIdleController.updateInteractivityLocked();
+ assertTrue(mDeviceIdleController.isScreenOn());
+
+ // Make sure setting true when screen is already on doesn't change anything.
+ doReturn(true).when(mPowerManager).isInteractive();
+ mDeviceIdleController.updateInteractivityLocked();
+ assertTrue(mDeviceIdleController.isScreenOn());
+
+ // Test changing from screen on to screen off.
+ doReturn(false).when(mPowerManager).isInteractive();
+ mDeviceIdleController.updateInteractivityLocked();
+ assertFalse(mDeviceIdleController.isScreenOn());
+ }
+
+ @Test
+ public void testUpdateChargingLocked() {
+ mDeviceIdleController.updateChargingLocked(false);
+ assertFalse(mDeviceIdleController.isCharging());
+
+ // Make sure setting false when charging is already off doesn't change anything.
+ mDeviceIdleController.updateChargingLocked(false);
+ assertFalse(mDeviceIdleController.isCharging());
+
+ // Test changing from charging off to charging on.
+ mDeviceIdleController.updateChargingLocked(true);
+ assertTrue(mDeviceIdleController.isCharging());
+
+ // Make sure setting true when charging is already on doesn't change anything.
+ mDeviceIdleController.updateChargingLocked(true);
+ assertTrue(mDeviceIdleController.isCharging());
+
+ // Test changing from charging on to charging off.
+ mDeviceIdleController.updateChargingLocked(false);
+ assertFalse(mDeviceIdleController.isCharging());
+ }
+
+ @Test
+ public void testStateActiveToStateInactive_ConditionsNotMet() {
+ mDeviceIdleController.becomeActiveLocked("testing", 0);
+ verifyStateConditions(STATE_ACTIVE);
+
+ // State should stay ACTIVE with screen on and charging.
+ setChargingOn(true);
+ setScreenOn(true);
+
+ mDeviceIdleController.becomeInactiveIfAppropriateLocked();
+ verifyStateConditions(STATE_ACTIVE);
+
+ // State should stay ACTIVE with charging on.
+ setChargingOn(true);
+ setScreenOn(false);
+
+ mDeviceIdleController.becomeInactiveIfAppropriateLocked();
+ verifyStateConditions(STATE_ACTIVE);
+
+ // State should stay ACTIVE with screen on.
+ // Note the different operation order here makes sure the state doesn't change before test.
+ setScreenOn(true);
+ setChargingOn(false);
+
+ mDeviceIdleController.becomeInactiveIfAppropriateLocked();
+ verifyStateConditions(STATE_ACTIVE);
+ }
+
+ @Test
+ public void testLightStateActiveToLightStateInactive_ConditionsNotMet() {
+ mDeviceIdleController.becomeActiveLocked("testing", 0);
+ verifyLightStateConditions(LIGHT_STATE_ACTIVE);
+
+ // State should stay ACTIVE with screen on and charging.
+ setChargingOn(true);
+ setScreenOn(true);
+
+ mDeviceIdleController.becomeInactiveIfAppropriateLocked();
+ verifyLightStateConditions(LIGHT_STATE_ACTIVE);
+
+ // State should stay ACTIVE with charging on.
+ setChargingOn(true);
+ setScreenOn(false);
+
+ mDeviceIdleController.becomeInactiveIfAppropriateLocked();
+ verifyLightStateConditions(LIGHT_STATE_ACTIVE);
+
+ // State should stay ACTIVE with screen on.
+ // Note the different operation order here makes sure the state doesn't change before test.
+ setScreenOn(true);
+ setChargingOn(false);
+
+ mDeviceIdleController.becomeInactiveIfAppropriateLocked();
+ verifyLightStateConditions(LIGHT_STATE_ACTIVE);
+ }
+
+ @Test
+ public void testStateActiveToStateInactive_ConditionsMet() {
+ mDeviceIdleController.becomeActiveLocked("testing", 0);
+ verifyStateConditions(STATE_ACTIVE);
+
+ setChargingOn(false);
+ setScreenOn(false);
+
+ mDeviceIdleController.becomeInactiveIfAppropriateLocked();
+ verifyStateConditions(STATE_INACTIVE);
+ }
+
+ @Test
+ public void testLightStateActiveToLightStateInactive_ConditionsMet() {
+ mDeviceIdleController.becomeActiveLocked("testing", 0);
+ verifyLightStateConditions(LIGHT_STATE_ACTIVE);
+
+ setChargingOn(false);
+ setScreenOn(false);
+
+ mDeviceIdleController.becomeInactiveIfAppropriateLocked();
+ verifyLightStateConditions(LIGHT_STATE_INACTIVE);
+ }
+
+ @Test
+ public void testStepIdleStateLocked_InvalidStates() {
+ mDeviceIdleController.becomeActiveLocked("testing", 0);
+ mDeviceIdleController.stepIdleStateLocked("testing");
+ // mDeviceIdleController.stepIdleStateLocked doesn't handle the ACTIVE case, so the state
+ // should stay as ACTIVE.
+ verifyStateConditions(STATE_ACTIVE);
+ }
+
+ @Test
+ public void testStepIdleStateLocked_ValidStates_NoLocationManager() {
+ mDeviceIdleController.setLocationManagerForTest(null);
+ // Make sure the controller doesn't think there's a wake-from-idle alarm coming soon.
+ doReturn(Long.MAX_VALUE).when(mAlarmManager).getNextWakeFromIdleTime();
+ // Set state to INACTIVE.
+ mDeviceIdleController.becomeActiveLocked("testing", 0);
+ setChargingOn(false);
+ setScreenOn(false);
+ verifyStateConditions(STATE_INACTIVE);
+
+ mDeviceIdleController.stepIdleStateLocked("testing");
+ verifyStateConditions(STATE_IDLE_PENDING);
+
+ mDeviceIdleController.stepIdleStateLocked("testing");
+ verifyStateConditions(STATE_SENSING);
+
+ mDeviceIdleController.stepIdleStateLocked("testing");
+ // No location manager, so SENSING should go straight to IDLE.
+ verifyStateConditions(STATE_IDLE);
+
+ // Should just alternate between IDLE and IDLE_MAINTENANCE now.
+
+ mDeviceIdleController.stepIdleStateLocked("testing");
+ verifyStateConditions(STATE_IDLE_MAINTENANCE);
+
+ mDeviceIdleController.stepIdleStateLocked("testing");
+ verifyStateConditions(STATE_IDLE);
+
+ mDeviceIdleController.stepIdleStateLocked("testing");
+ verifyStateConditions(STATE_IDLE_MAINTENANCE);
+ }
+
+ @Test
+ public void testStepIdleStateLocked_ValidStates_WithLocationManager_NoProviders() {
+ // Make sure the controller doesn't think there's a wake-from-idle alarm coming soon.
+ doReturn(Long.MAX_VALUE).when(mAlarmManager).getNextWakeFromIdleTime();
+ // Set state to INACTIVE.
+ mDeviceIdleController.becomeActiveLocked("testing", 0);
+ setChargingOn(false);
+ setScreenOn(false);
+ verifyStateConditions(STATE_INACTIVE);
+
+ mDeviceIdleController.stepIdleStateLocked("testing");
+ verifyStateConditions(STATE_IDLE_PENDING);
+
+ mDeviceIdleController.stepIdleStateLocked("testing");
+ verifyStateConditions(STATE_SENSING);
+
+ mDeviceIdleController.stepIdleStateLocked("testing");
+ // Location manager exists but there isn't a network or GPS provider,
+ // so SENSING should go straight to IDLE.
+ verifyStateConditions(STATE_IDLE);
+
+ // Should just alternate between IDLE and IDLE_MAINTENANCE now.
+
+ mDeviceIdleController.stepIdleStateLocked("testing");
+ verifyStateConditions(STATE_IDLE_MAINTENANCE);
+
+ mDeviceIdleController.stepIdleStateLocked("testing");
+ verifyStateConditions(STATE_IDLE);
+
+ mDeviceIdleController.stepIdleStateLocked("testing");
+ verifyStateConditions(STATE_IDLE_MAINTENANCE);
+ }
+
+ @Test
+ public void testStepIdleStateLocked_ValidStates_WithLocationManager_WithProviders() {
+ doReturn(mock(LocationProvider.class)).when(mLocationManager).getProvider(anyString());
+ // Make sure the controller doesn't think there's a wake-from-idle alarm coming soon.
+ // TODO: add tests for when there's a wake-from-idle alarm coming soon.
+ doReturn(Long.MAX_VALUE).when(mAlarmManager).getNextWakeFromIdleTime();
+ // Set state to INACTIVE.
+ mDeviceIdleController.becomeActiveLocked("testing", 0);
+ setChargingOn(false);
+ setScreenOn(false);
+ verifyStateConditions(STATE_INACTIVE);
+
+ mDeviceIdleController.stepIdleStateLocked("testing");
+ verifyStateConditions(STATE_IDLE_PENDING);
+
+ mDeviceIdleController.stepIdleStateLocked("testing");
+ verifyStateConditions(STATE_SENSING);
+
+ mDeviceIdleController.stepIdleStateLocked("testing");
+ // Location manager exists with a provider, so SENSING should go to LOCATING.
+ verifyStateConditions(STATE_LOCATING);
+
+ mDeviceIdleController.stepIdleStateLocked("testing");
+ verifyStateConditions(STATE_IDLE);
+
+ // Should just alternate between IDLE and IDLE_MAINTENANCE now.
+
+ mDeviceIdleController.stepIdleStateLocked("testing");
+ verifyStateConditions(STATE_IDLE_MAINTENANCE);
+
+ mDeviceIdleController.stepIdleStateLocked("testing");
+ verifyStateConditions(STATE_IDLE);
+
+ mDeviceIdleController.stepIdleStateLocked("testing");
+ verifyStateConditions(STATE_IDLE_MAINTENANCE);
+ }
+
+ private void setChargingOn(boolean on) {
+ mDeviceIdleController.updateChargingLocked(on);
+ }
+
+ private void setScreenOn(boolean on) {
+ doReturn(on).when(mPowerManager).isInteractive();
+ mDeviceIdleController.updateInteractivityLocked();
+ }
+
+ private void verifyStateConditions(int expectedState) {
+ int curState = mDeviceIdleController.getState();
+ assertEquals(
+ "Expected " + stateToString(expectedState) + " but was " + stateToString(curState),
+ expectedState, curState);
+
+ switch (expectedState) {
+ case STATE_ACTIVE:
+ assertFalse(mDeviceIdleController.mMotionListener.isActive());
+ assertFalse(mAnyMotionDetector.isMonitoring);
+ break;
+ case STATE_INACTIVE:
+ assertFalse(mDeviceIdleController.mMotionListener.isActive());
+ assertFalse(mAnyMotionDetector.isMonitoring);
+ assertFalse(mDeviceIdleController.isCharging());
+ assertFalse(mDeviceIdleController.isScreenOn());
+ break;
+ case STATE_IDLE_PENDING:
+ assertTrue(mDeviceIdleController.mMotionListener.isActive());
+ assertFalse(mAnyMotionDetector.isMonitoring);
+ assertFalse(mDeviceIdleController.isCharging());
+ assertFalse(mDeviceIdleController.isScreenOn());
+ break;
+ case STATE_SENSING:
+ assertTrue(mDeviceIdleController.mMotionListener.isActive());
+ assertTrue(mAnyMotionDetector.isMonitoring);
+ assertFalse(mDeviceIdleController.isCharging());
+ assertFalse(mDeviceIdleController.isScreenOn());
+ break;
+ case STATE_LOCATING:
+ assertTrue(mDeviceIdleController.mMotionListener.isActive());
+ assertTrue(mAnyMotionDetector.isMonitoring);
+ assertFalse(mDeviceIdleController.isCharging());
+ assertFalse(mDeviceIdleController.isScreenOn());
+ break;
+ case STATE_IDLE:
+ assertTrue(mDeviceIdleController.mMotionListener.isActive());
+ assertFalse(mAnyMotionDetector.isMonitoring);
+ assertFalse(mDeviceIdleController.isCharging());
+ assertFalse(mDeviceIdleController.isScreenOn());
+ // Light state should be OVERRIDE at this point.
+ verifyLightStateConditions(LIGHT_STATE_OVERRIDE);
+ break;
+ case STATE_IDLE_MAINTENANCE:
+ assertTrue(mDeviceIdleController.mMotionListener.isActive());
+ assertFalse(mAnyMotionDetector.isMonitoring);
+ assertFalse(mDeviceIdleController.isCharging());
+ assertFalse(mDeviceIdleController.isScreenOn());
+ break;
+ default:
+ fail("Conditions for " + stateToString(expectedState) + " unknown.");
+ }
+ }
+
+ private void verifyLightStateConditions(int expectedLightState) {
+ int curLightState = mDeviceIdleController.getLightState();
+ assertEquals(
+ "Expected " + lightStateToString(expectedLightState)
+ + " but was " + lightStateToString(curLightState),
+ expectedLightState, curLightState);
+
+ switch (expectedLightState) {
+ case LIGHT_STATE_ACTIVE:
+ assertTrue(
+ mDeviceIdleController.isCharging() || mDeviceIdleController.isScreenOn());
+ break;
+ case LIGHT_STATE_INACTIVE:
+ case LIGHT_STATE_PRE_IDLE:
+ case LIGHT_STATE_IDLE:
+ case LIGHT_STATE_WAITING_FOR_NETWORK:
+ case LIGHT_STATE_IDLE_MAINTENANCE:
+ case LIGHT_STATE_OVERRIDE:
+ assertFalse(mDeviceIdleController.isCharging());
+ assertFalse(mDeviceIdleController.isScreenOn());
+ break;
+ default:
+ fail("Conditions for " + lightStateToString(expectedLightState) + " unknown.");
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/am/PersistentConnectionTest.java b/services/tests/mockingservicestests/src/com/android/server/am/PersistentConnectionTest.java
similarity index 78%
rename from services/tests/servicestests/src/com/android/server/am/PersistentConnectionTest.java
rename to services/tests/mockingservicestests/src/com/android/server/am/PersistentConnectionTest.java
index 54f93a8..26e77eb 100644
--- a/services/tests/servicestests/src/com/android/server/am/PersistentConnectionTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/PersistentConnectionTest.java
@@ -43,6 +43,8 @@
@SmallTest
public class PersistentConnectionTest extends AndroidTestCase {
+ private static final String TAG = "PersistentConnectionTest";
+
private static class MyConnection extends PersistentConnection<IDeviceAdminService> {
public long uptimeMillis = 12345;
@@ -50,9 +52,16 @@
public MyConnection(String tag, Context context, Handler handler, int userId,
ComponentName componentName, long rebindBackoffSeconds,
- double rebindBackoffIncrease, long rebindMaxBackoffSeconds) {
+ double rebindBackoffIncrease, long rebindMaxBackoffSeconds,
+ long resetBackoffDelay) {
super(tag, context, handler, userId, componentName,
- rebindBackoffSeconds, rebindBackoffIncrease, rebindMaxBackoffSeconds);
+ rebindBackoffSeconds, rebindBackoffIncrease, rebindMaxBackoffSeconds,
+ resetBackoffDelay);
+ }
+
+ @Override
+ protected int getBindFlags() {
+ return Context.BIND_FOREGROUND_SERVICE;
}
@Override
@@ -108,10 +117,11 @@
final ComponentName cn = ComponentName.unflattenFromString("a.b.c/def");
final Handler handler = new Handler(Looper.getMainLooper());
- final MyConnection conn = new MyConnection("tag", context, handler, userId, cn,
+ final MyConnection conn = new MyConnection(TAG, context, handler, userId, cn,
/* rebindBackoffSeconds= */ 5,
/* rebindBackoffIncrease= */ 1.5,
- /* rebindMaxBackoffSeconds= */ 11);
+ /* rebindMaxBackoffSeconds= */ 11,
+ /* resetBackoffDelay= */ 999);
assertFalse(conn.isBound());
assertFalse(conn.isConnected());
@@ -310,10 +320,11 @@
final ComponentName cn = ComponentName.unflattenFromString("a.b.c/def");
final Handler handler = new Handler(Looper.getMainLooper());
- final MyConnection conn = new MyConnection("tag", context, handler, userId, cn,
+ final MyConnection conn = new MyConnection(TAG, context, handler, userId, cn,
/* rebindBackoffSeconds= */ 5,
/* rebindBackoffIncrease= */ 1.5,
- /* rebindMaxBackoffSeconds= */ 11);
+ /* rebindMaxBackoffSeconds= */ 11,
+ /* resetBackoffDelay= */ 999);
when(context.bindServiceAsUser(any(Intent.class), any(ServiceConnection.class), anyInt(),
any(Handler.class), any(UserHandle.class)))
@@ -351,4 +362,78 @@
assertFalse(conn.isBound());
assertFalse(conn.shouldBeBoundForTest());
}
+
+ public void testResetBackoff() {
+ final Context context = mock(Context.class);
+ final int userId = 11;
+ final ComponentName cn = ComponentName.unflattenFromString("a.b.c/def");
+ final Handler handler = new Handler(Looper.getMainLooper());
+
+ final MyConnection conn = new MyConnection(TAG, context, handler, userId, cn,
+ /* rebindBackoffSeconds= */ 5,
+ /* rebindBackoffIncrease= */ 1.5,
+ /* rebindMaxBackoffSeconds= */ 11,
+ /* resetBackoffDelay= */ 20);
+
+ when(context.bindServiceAsUser(any(Intent.class), any(ServiceConnection.class), anyInt(),
+ any(Handler.class), any(UserHandle.class)))
+ .thenReturn(true);
+
+ // Bind.
+ conn.bind();
+
+ assertTrue(conn.isBound());
+ assertTrue(conn.shouldBeBoundForTest());
+ assertFalse(conn.isRebindScheduled());
+
+ conn.elapse(1000);
+
+ // Then the binding is "died"...
+ conn.getServiceConnectionForTest().onBindingDied(cn);
+
+ assertFalse(conn.isBound());
+ assertTrue(conn.shouldBeBoundForTest());
+ assertFalse(conn.isConnected());
+ assertNull(conn.getServiceBinder());
+ assertTrue(conn.isRebindScheduled());
+
+ assertEquals(7500, conn.getNextBackoffMsForTest());
+
+ assertEquals(
+ Arrays.asList(Pair.create(conn.getBindForBackoffRunnableForTest(),
+ conn.uptimeMillis + 5000)),
+ conn.scheduledRunnables);
+
+ // 5000 ms later...
+ conn.elapse(5000);
+
+ assertTrue(conn.isBound());
+ assertTrue(conn.shouldBeBoundForTest());
+ assertFalse(conn.isConnected());
+ assertNull(conn.getServiceBinder());
+ assertFalse(conn.isRebindScheduled());
+
+ assertEquals(7500, conn.getNextBackoffMsForTest());
+
+ // Connected.
+ conn.getServiceConnectionForTest().onServiceConnected(cn,
+ new IDeviceAdminService.Stub() {});
+
+ assertTrue(conn.isBound());
+ assertTrue(conn.shouldBeBoundForTest());
+ assertTrue(conn.isConnected());
+ assertNotNull(conn.getServiceBinder());
+ assertFalse(conn.isRebindScheduled());
+
+ assertEquals(7500, conn.getNextBackoffMsForTest());
+
+ assertEquals(
+ Arrays.asList(Pair.create(conn.getStableCheckRunnableForTest(),
+ conn.uptimeMillis + 20000)),
+ conn.scheduledRunnables);
+
+ conn.elapse(20000);
+
+ assertEquals(5000, conn.getNextBackoffMsForTest());
+ }
}
diff --git a/services/tests/servicestests/Android.mk b/services/tests/servicestests/Android.mk
index 80307ee..2957267 100644
--- a/services/tests/servicestests/Android.mk
+++ b/services/tests/servicestests/Android.mk
@@ -58,6 +58,7 @@
libbacktrace \
libbase \
libbinder \
+ libbinderthreadstate \
libc++ \
libcutils \
liblog \
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/certs/ec-p256-lineage-2-signers b/services/tests/servicestests/assets/PackageSignaturesTest/certs/ec-p256-lineage-2-signers
new file mode 100644
index 0000000..509ea3b
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/certs/ec-p256-lineage-2-signers
Binary files differ
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/certs/ec-p256-lineage-3-signers b/services/tests/servicestests/assets/PackageSignaturesTest/certs/ec-p256-lineage-3-signers
new file mode 100644
index 0000000..bee71c0
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/certs/ec-p256-lineage-3-signers
Binary files differ
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/certs/ec-p256.pk8 b/services/tests/servicestests/assets/PackageSignaturesTest/certs/ec-p256.pk8
new file mode 100644
index 0000000..f781c30
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/certs/ec-p256.pk8
Binary files differ
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/certs/ec-p256.x509.der b/services/tests/servicestests/assets/PackageSignaturesTest/certs/ec-p256.x509.der
new file mode 100644
index 0000000..e611e3d
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/certs/ec-p256.x509.der
Binary files differ
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/certs/ec-p256_2.pk8 b/services/tests/servicestests/assets/PackageSignaturesTest/certs/ec-p256_2.pk8
new file mode 100644
index 0000000..5e73f27
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/certs/ec-p256_2.pk8
Binary files differ
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/certs/ec-p256_2.x509.der b/services/tests/servicestests/assets/PackageSignaturesTest/certs/ec-p256_2.x509.der
new file mode 100644
index 0000000..7723bea
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/certs/ec-p256_2.x509.der
Binary files differ
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/certs/ec-p256_3.pk8 b/services/tests/servicestests/assets/PackageSignaturesTest/certs/ec-p256_3.pk8
new file mode 100644
index 0000000..d7309dd
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/certs/ec-p256_3.pk8
Binary files differ
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/certs/ec-p256_3.x509.der b/services/tests/servicestests/assets/PackageSignaturesTest/certs/ec-p256_3.x509.der
new file mode 100644
index 0000000..cc82af9
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/certs/ec-p256_3.x509.der
Binary files differ
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/xml/README b/services/tests/servicestests/assets/PackageSignaturesTest/xml/README
new file mode 100644
index 0000000..43d5bb8
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/xml/README
@@ -0,0 +1,58 @@
+The XML files in this directory are taken from the packages tag of a test APK signed with the
+certificates and keys under the certs/ directory. To recreate the XML files run the following:
+
+1. Build the test APK:
+mmm -j cts/hostsidetests/appsecurity/test-apps/tinyapp/
+
+2. Sign the APK with the first signer:
+apksigner sign --in ${OUT}/data/app/CtsPkgInstallTinyApp/CtsPkgInstallTinyApp.apk --out test.apk \
+ --cert certs/ec-p256.x509.der --key certs/ec-p256.pk8
+
+3. Install the APK on a device:
+adb install test.apk
+
+4. Pull the packages.xml file containing the new entry for the APK from the device:
+adb pull /data/system/packages.xml
+
+5. Search the packages.xml file for the package name 'android.appsecurity.cts.tinyapp'. Following is
+ the full entry when the APK is signed as above:
+
+ <package name="android.appsecurity.cts.tinyapp" codePath="/data/app/android.appsecurity.cts.tiny
+ app-4ix3umoWct_iD26jQ03Z_g==" nativeLibraryPath="/data/app/android.appsecurity.cts.tinyapp-4ix3u
+ moWct_iD26jQ03Z_g==/lib" publicFlags="805879364" privateFlags="0" ft="1663710dd00" it="1663710de
+ 41" ut="1663710de41" version="10" userId="10051">
+ <sigs count="1" schemeVersion="3">
+ <cert index="16" key="3082016c30820111a003020102020900ca0fb64dfb66e772300a06082a8648ce3d
+ 04030230123110300e06035504030c0765632d70323536301e170d3136303333313134353830365a170d3433
+ 303831373134353830365a30123110300e06035504030c0765632d703235363059301306072a8648ce3d0201
+ 06082a8648ce3d03010703420004a65f113d22cb4913908307ac31ee2ba0e9138b785fac6536d14ea2ce90d2
+ b4bfe194b50cdc8e169f54a73a991ef0fa76329825be078cc782740703da44b4d7eba350304e301d0603551d
+ 0e04160414d4133568b95b30158b322071ea8c43ff5b05ccc8301f0603551d23041830168014d4133568b95b
+ 30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d04030203490030
+ 46022100f504a0866caef029f417142c5cb71354c79ffcd1d640618dfca4f19e16db78d6022100f8eea48297
+ 99c06cad08c6d3d2d2ec05e0574154e747ea0fdbb8042cb655aadd" />
+ </sigs>
+ <proper-signing-keyset identifier="480" />
+ </package>
+
+The PackageSignatures#readXml and writeXml methods read and write everything within the sigs tag.
+The tags and attributes within the sigs tag can be modified and used to verify various good and
+error paths for the PackageSignaturesTest.
+
+Step 2 can be modified to sign with multiple signers by running one of the following commands:
+
+- To sign with two signers in the lineage (after the signing key has been rotated once):
+apksigner sign --in ${OUT}/data/app/CtsPkgInstallTinyApp/CtsPkgInstallTinyApp.apk --out test.apk \
+ --cert certs/ec-p256.x509.der --key certs/ec-p256.pk8 --next-signer --cert \
+ certs/ec-p256_2.x509.der --key certs/ec-p256_2.pk8 --lineage certs/ec-p256-lineage-2-signers
+
+- To sign with three signers in the lineage (after the second key rotation):
+apksigner sign --in ${OUT}/data/app/CtsPkgInstallTinyApp/CtsPkgInstallTinyApp.apk --out test.apk \
+ --cert certs/ec-p256.x509.der --key certs/ec-p256.pk8 --next-signer --cert \
+ certs/ec-p256_3.x509.der --key certs/ec-p256_3.pk8 --lineage certs/ec-p256-lineage-3-signers
+
+- To sign with two distinct signers (NOTE: The V3 signature scheme only supports a single signer,
+ so this method can only be used with signature schemes V1 and V2):
+apksigner sign --in ${OUT}/data/app/CtsPkgInstallTinyApp/CtsPkgInstallTinyApp.apk --out test.apk \
+ --cert certs/ec-p256.x509.der --key certs/ec-p256.pk8 --next-signer --cert \
+ certs/ec-p256_3.x509.der --key certs/ec-p256_3.pk8 --v3-signing-enabled false
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-extra-cert-tag.xml b/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-extra-cert-tag.xml
new file mode 100644
index 0000000..4d55bad
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-extra-cert-tag.xml
@@ -0,0 +1,5 @@
+ <sigs count="1" schemeVersion="3">
+ <cert index="0" key="3082016c30820111a003020102020900ca0fb64dfb66e772300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3136303333313134353830365a170d3433303831373134353830365a30123110300e06035504030c0765632d703235363059301306072a8648ce3d020106082a8648ce3d03010703420004a65f113d22cb4913908307ac31ee2ba0e9138b785fac6536d14ea2ce90d2b4bfe194b50cdc8e169f54a73a991ef0fa76329825be078cc782740703da44b4d7eba350304e301d0603551d0e04160414d4133568b95b30158b322071ea8c43ff5b05ccc8301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d0403020349003046022100f504a0866caef029f417142c5cb71354c79ffcd1d640618dfca4f19e16db78d6022100f8eea4829799c06cad08c6d3d2d2ec05e0574154e747ea0fdbb8042cb655aadd" />
+ <cert index="0" key="3082016c30820111a003020102020900ca0fb64dfb66e772300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3136303333313134353830365a170d3433303831373134353830365a30123110300e06035504030c0765632d703235363059301306072a8648ce3d020106082a8648ce3d03010703420004a65f113d22cb4913908307ac31ee2ba0e9138b785fac6536d14ea2ce90d2b4bfe194b50cdc8e169f54a73a991ef0fa76329825be078cc782740703da44b4d7eba350304e301d0603551d0e04160414d4133568b95b30158b322071ea8c43ff5b05ccc8301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d0403020349003046022100f504a0866caef029f417142c5cb71354c79ffcd1d640618dfca4f19e16db78d6022100f8eea4829799c06cad08c6d3d2d2ec05e0574154e747ea0fdbb8042cb655aadd" />
+ </sigs>
+
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-invalid-cert-index.xml b/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-invalid-cert-index.xml
new file mode 100644
index 0000000..f7882b1
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-invalid-cert-index.xml
@@ -0,0 +1,4 @@
+ <sigs count="1" schemeVersion="3">
+ <cert index="x" key="3082016c30820111a003020102020900ca0fb64dfb66e772300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3136303333313134353830365a170d3433303831373134353830365a30123110300e06035504030c0765632d703235363059301306072a8648ce3d020106082a8648ce3d03010703420004a65f113d22cb4913908307ac31ee2ba0e9138b785fac6536d14ea2ce90d2b4bfe194b50cdc8e169f54a73a991ef0fa76329825be078cc782740703da44b4d7eba350304e301d0603551d0e04160414d4133568b95b30158b322071ea8c43ff5b05ccc8301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d0403020349003046022100f504a0866caef029f417142c5cb71354c79ffcd1d640618dfca4f19e16db78d6022100f8eea4829799c06cad08c6d3d2d2ec05e0574154e747ea0fdbb8042cb655aadd" />
+ </sigs>
+
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-invalid-cert-key.xml b/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-invalid-cert-key.xml
new file mode 100644
index 0000000..af2c293
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-invalid-cert-key.xml
@@ -0,0 +1,4 @@
+ <sigs count="1" schemeVersion="3">
+ <cert index="0" key="082016c30820111a003020102020900ca0fb64dfb66e772300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3136303333313134353830365a170d3433303831373134353830365a30123110300e06035504030c0765632d703235363059301306072a8648ce3d020106082a8648ce3d03010703420004a65f113d22cb4913908307ac31ee2ba0e9138b785fac6536d14ea2ce90d2b4bfe194b50cdc8e169f54a73a991ef0fa76329825be078cc782740703da44b4d7eba350304e301d0603551d0e04160414d4133568b95b30158b322071ea8c43ff5b05ccc8301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d0403020349003046022100f504a0866caef029f417142c5cb71354c79ffcd1d640618dfca4f19e16db78d6022100f8eea4829799c06cad08c6d3d2d2ec05e0574154e747ea0fdbb8042cb655aadd" />
+ </sigs>
+
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-invalid-public-key-cert-key.xml b/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-invalid-public-key-cert-key.xml
new file mode 100644
index 0000000..893402d
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-invalid-public-key-cert-key.xml
@@ -0,0 +1,4 @@
+ <sigs count="1" schemeVersion="1">
+ <cert index="0" key="4082016c30820111a003020102020900ca0fb64dfb66e772300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3136303333313134353830365a170d3433303831373134353830365a30123110300e06035504030c0765632d703235363059301306072a8648ce3d020106082a8648ce3d03010703420004a65f113d22cb4913908307ac31ee2ba0e9138b785fac6536d14ea2ce90d2b4bfe194b50cdc8e169f54a73a991ef0fa76329825be078cc782740703da44b4d7eba350304e301d0603551d0e04160414d4133568b95b30158b322071ea8c43ff5b05ccc8301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d0403020349003046022100f504a0866caef029f417142c5cb71354c79ffcd1d640618dfca4f19e16db78d6022100f8eea4829799c06cad08c6d3d2d2ec05e0574154e747ea0fdbb8042cb655aadd" />
+ </sigs>
+
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-invalid-tag.xml b/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-invalid-tag.xml
new file mode 100644
index 0000000..1f81dac
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-invalid-tag.xml
@@ -0,0 +1,5 @@
+ <sigs count="1" schemeVersion="3">
+ <cert index="0" key="3082016c30820111a003020102020900ca0fb64dfb66e772300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3136303333313134353830365a170d3433303831373134353830365a30123110300e06035504030c0765632d703235363059301306072a8648ce3d020106082a8648ce3d03010703420004a65f113d22cb4913908307ac31ee2ba0e9138b785fac6536d14ea2ce90d2b4bfe194b50cdc8e169f54a73a991ef0fa76329825be078cc782740703da44b4d7eba350304e301d0603551d0e04160414d4133568b95b30158b322071ea8c43ff5b05ccc8301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d0403020349003046022100f504a0866caef029f417142c5cb71354c79ffcd1d640618dfca4f19e16db78d6022100f8eea4829799c06cad08c6d3d2d2ec05e0574154e747ea0fdbb8042cb655aadd" />
+ <invalid />
+ </sigs>
+
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-missing-cert-index.xml b/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-missing-cert-index.xml
new file mode 100644
index 0000000..c38e4d9
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-missing-cert-index.xml
@@ -0,0 +1,4 @@
+ <sigs count="1" schemeVersion="3">
+ <cert key="3082016c30820111a003020102020900ca0fb64dfb66e772300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3136303333313134353830365a170d3433303831373134353830365a30123110300e06035504030c0765632d703235363059301306072a8648ce3d020106082a8648ce3d03010703420004a65f113d22cb4913908307ac31ee2ba0e9138b785fac6536d14ea2ce90d2b4bfe194b50cdc8e169f54a73a991ef0fa76329825be078cc782740703da44b4d7eba350304e301d0603551d0e04160414d4133568b95b30158b322071ea8c43ff5b05ccc8301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d0403020349003046022100f504a0866caef029f417142c5cb71354c79ffcd1d640618dfca4f19e16db78d6022100f8eea4829799c06cad08c6d3d2d2ec05e0574154e747ea0fdbb8042cb655aadd" />
+ </sigs>
+
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-missing-cert-key.xml b/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-missing-cert-key.xml
new file mode 100644
index 0000000..8e8cbcf
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-missing-cert-key.xml
@@ -0,0 +1,4 @@
+ <sigs count="1" schemeVersion="3">
+ <cert index="0" />
+ </sigs>
+
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-missing-cert-tag.xml b/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-missing-cert-tag.xml
new file mode 100644
index 0000000..57e96a8
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-missing-cert-tag.xml
@@ -0,0 +1,3 @@
+ <sigs count="1" schemeVersion="3">
+ </sigs>
+
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-missing-scheme-version.xml b/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-missing-scheme-version.xml
new file mode 100644
index 0000000..d9f7a5f
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-missing-scheme-version.xml
@@ -0,0 +1,4 @@
+ <sigs count="1">
+ <cert index="0" key="3082016c30820111a003020102020900ca0fb64dfb66e772300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3136303333313134353830365a170d3433303831373134353830365a30123110300e06035504030c0765632d703235363059301306072a8648ce3d020106082a8648ce3d03010703420004a65f113d22cb4913908307ac31ee2ba0e9138b785fac6536d14ea2ce90d2b4bfe194b50cdc8e169f54a73a991ef0fa76329825be078cc782740703da44b4d7eba350304e301d0603551d0e04160414d4133568b95b30158b322071ea8c43ff5b05ccc8301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d0403020349003046022100f504a0866caef029f417142c5cb71354c79ffcd1d640618dfca4f19e16db78d6022100f8eea4829799c06cad08c6d3d2d2ec05e0574154e747ea0fdbb8042cb655aadd" />
+ </sigs>
+
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-missing-sigs-count.xml b/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-missing-sigs-count.xml
new file mode 100644
index 0000000..4eefdd9
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-missing-sigs-count.xml
@@ -0,0 +1,4 @@
+ <sigs schemeVersion="3">
+ <cert index="0" key="3082016c30820111a003020102020900ca0fb64dfb66e772300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3136303333313134353830365a170d3433303831373134353830365a30123110300e06035504030c0765632d703235363059301306072a8648ce3d020106082a8648ce3d03010703420004a65f113d22cb4913908307ac31ee2ba0e9138b785fac6536d14ea2ce90d2b4bfe194b50cdc8e169f54a73a991ef0fa76329825be078cc782740703da44b4d7eba350304e301d0603551d0e04160414d4133568b95b30158b322071ea8c43ff5b05ccc8301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d0403020349003046022100f504a0866caef029f417142c5cb71354c79ffcd1d640618dfca4f19e16db78d6022100f8eea4829799c06cad08c6d3d2d2ec05e0574154e747ea0fdbb8042cb655aadd" />
+ </sigs>
+
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-previous-cert.xml b/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-previous-cert.xml
new file mode 100644
index 0000000..2aeeb71
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-previous-cert.xml
@@ -0,0 +1,4 @@
+ <sigs count="1" schemeVersion="2">
+ <cert index="0" />
+ </sigs>
+
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer.xml b/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer.xml
new file mode 100644
index 0000000..14471f8
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer.xml
@@ -0,0 +1,4 @@
+ <sigs count="1" schemeVersion="1">
+ <cert index="0" key="3082016c30820111a003020102020900ca0fb64dfb66e772300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3136303333313134353830365a170d3433303831373134353830365a30123110300e06035504030c0765632d703235363059301306072a8648ce3d020106082a8648ce3d03010703420004a65f113d22cb4913908307ac31ee2ba0e9138b785fac6536d14ea2ce90d2b4bfe194b50cdc8e169f54a73a991ef0fa76329825be078cc782740703da44b4d7eba350304e301d0603551d0e04160414d4133568b95b30158b322071ea8c43ff5b05ccc8301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d0403020349003046022100f504a0866caef029f417142c5cb71354c79ffcd1d640618dfca4f19e16db78d6022100f8eea4829799c06cad08c6d3d2d2ec05e0574154e747ea0fdbb8042cb655aadd" />
+ </sigs>
+
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/xml/three-signers-in-lineage-invalid-pastSigs-count.xml b/services/tests/servicestests/assets/PackageSignaturesTest/xml/three-signers-in-lineage-invalid-pastSigs-count.xml
new file mode 100644
index 0000000..2b2e383
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/xml/three-signers-in-lineage-invalid-pastSigs-count.xml
@@ -0,0 +1,9 @@
+ <sigs count="1" schemeVersion="3">
+ <cert index="0" key="3082016e30820115a0030201020209008394f5cad16a89a7300a06082a8648ce3d04030230143112301006035504030c0965632d703235365f32301e170d3138303731343030303532365a170d3238303731313030303532365a30143112301006035504030c0965632d703235365f333059301306072a8648ce3d020106082a8648ce3d03010703420004f31e62430e9db6fc5928d975fc4e47419bacfcb2e07c89299e6cd7e344dd21adfd308d58cb49a1a2a3fecacceea4862069f30be1643bcc255040d8089dfb3743a350304e301d0603551d0e041604146f8d0828b13efaf577fc86b0e99fa3e54bcbcff0301f0603551d230418301680147991d92b0208fc448bf506d4efc9fff428cb5e5f300c0603551d13040530030101ff300a06082a8648ce3d04030203470030440220256bdaa2784c273e4cc291a595a46779dee9de9044dc9f7ab820309567df9fe902201a4ad8c69891b5a8c47434fe9540ed1f4979b5fad3483f3fa04d5677355a579e" />
+ <pastSigs count="x">
+ <cert index="1" key="3082016c30820111a003020102020900ca0fb64dfb66e772300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3136303333313134353830365a170d3433303831373134353830365a30123110300e06035504030c0765632d703235363059301306072a8648ce3d020106082a8648ce3d03010703420004a65f113d22cb4913908307ac31ee2ba0e9138b785fac6536d14ea2ce90d2b4bfe194b50cdc8e169f54a73a991ef0fa76329825be078cc782740703da44b4d7eba350304e301d0603551d0e04160414d4133568b95b30158b322071ea8c43ff5b05ccc8301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d0403020349003046022100f504a0866caef029f417142c5cb71354c79ffcd1d640618dfca4f19e16db78d6022100f8eea4829799c06cad08c6d3d2d2ec05e0574154e747ea0fdbb8042cb655aadd" flags="3" />
+ <cert index="2" key="3082016d30820113a0030201020209008855bd1dd2b2b225300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3138303731333137343135315a170d3238303731303137343135315a30143112301006035504030c0965632d703235365f323059301306072a8648ce3d020106082a8648ce3d030107034200041d4cca0472ad97ee3cecef0da93d62b450c6788333b36e7553cde9f74ab5df00bbba6ba950e68461d70bbc271b62151dad2de2bf6203cd2076801c7a9d4422e1a350304e301d0603551d0e041604147991d92b0208fc448bf506d4efc9fff428cb5e5f301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d040302034800304502202769abb1b49fc2f53479c4ae92a6631dabfd522c9acb0bba2b43ebeb99c63011022100d260fb1d1f176cf9b7fa60098bfd24319f4905a3e5fda100a6fe1a2ab19ff09e" flags="7" />
+ <cert index="0" flags="23" />
+ </pastSigs>
+ </sigs>
+
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/xml/three-signers-in-lineage-missing-pastSigs-cert-tag.xml b/services/tests/servicestests/assets/PackageSignaturesTest/xml/three-signers-in-lineage-missing-pastSigs-cert-tag.xml
new file mode 100644
index 0000000..f992104
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/xml/three-signers-in-lineage-missing-pastSigs-cert-tag.xml
@@ -0,0 +1,8 @@
+ <sigs count="1" schemeVersion="3">
+ <cert index="0" key="3082016e30820115a0030201020209008394f5cad16a89a7300a06082a8648ce3d04030230143112301006035504030c0965632d703235365f32301e170d3138303731343030303532365a170d3238303731313030303532365a30143112301006035504030c0965632d703235365f333059301306072a8648ce3d020106082a8648ce3d03010703420004f31e62430e9db6fc5928d975fc4e47419bacfcb2e07c89299e6cd7e344dd21adfd308d58cb49a1a2a3fecacceea4862069f30be1643bcc255040d8089dfb3743a350304e301d0603551d0e041604146f8d0828b13efaf577fc86b0e99fa3e54bcbcff0301f0603551d230418301680147991d92b0208fc448bf506d4efc9fff428cb5e5f300c0603551d13040530030101ff300a06082a8648ce3d04030203470030440220256bdaa2784c273e4cc291a595a46779dee9de9044dc9f7ab820309567df9fe902201a4ad8c69891b5a8c47434fe9540ed1f4979b5fad3483f3fa04d5677355a579e" />
+ <pastSigs count="3">
+ <cert index="1" key="3082016c30820111a003020102020900ca0fb64dfb66e772300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3136303333313134353830365a170d3433303831373134353830365a30123110300e06035504030c0765632d703235363059301306072a8648ce3d020106082a8648ce3d03010703420004a65f113d22cb4913908307ac31ee2ba0e9138b785fac6536d14ea2ce90d2b4bfe194b50cdc8e169f54a73a991ef0fa76329825be078cc782740703da44b4d7eba350304e301d0603551d0e04160414d4133568b95b30158b322071ea8c43ff5b05ccc8301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d0403020349003046022100f504a0866caef029f417142c5cb71354c79ffcd1d640618dfca4f19e16db78d6022100f8eea4829799c06cad08c6d3d2d2ec05e0574154e747ea0fdbb8042cb655aadd" flags="3" />
+ <cert index="0" flags="23" />
+ </pastSigs>
+ </sigs>
+
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/xml/three-signers-in-lineage-missing-pastSigs-count.xml b/services/tests/servicestests/assets/PackageSignaturesTest/xml/three-signers-in-lineage-missing-pastSigs-count.xml
new file mode 100644
index 0000000..6ef0fe5
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/xml/three-signers-in-lineage-missing-pastSigs-count.xml
@@ -0,0 +1,9 @@
+ <sigs count="1" schemeVersion="3">
+ <cert index="0" key="3082016e30820115a0030201020209008394f5cad16a89a7300a06082a8648ce3d04030230143112301006035504030c0965632d703235365f32301e170d3138303731343030303532365a170d3238303731313030303532365a30143112301006035504030c0965632d703235365f333059301306072a8648ce3d020106082a8648ce3d03010703420004f31e62430e9db6fc5928d975fc4e47419bacfcb2e07c89299e6cd7e344dd21adfd308d58cb49a1a2a3fecacceea4862069f30be1643bcc255040d8089dfb3743a350304e301d0603551d0e041604146f8d0828b13efaf577fc86b0e99fa3e54bcbcff0301f0603551d230418301680147991d92b0208fc448bf506d4efc9fff428cb5e5f300c0603551d13040530030101ff300a06082a8648ce3d04030203470030440220256bdaa2784c273e4cc291a595a46779dee9de9044dc9f7ab820309567df9fe902201a4ad8c69891b5a8c47434fe9540ed1f4979b5fad3483f3fa04d5677355a579e" />
+ <pastSigs>
+ <cert index="1" key="3082016c30820111a003020102020900ca0fb64dfb66e772300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3136303333313134353830365a170d3433303831373134353830365a30123110300e06035504030c0765632d703235363059301306072a8648ce3d020106082a8648ce3d03010703420004a65f113d22cb4913908307ac31ee2ba0e9138b785fac6536d14ea2ce90d2b4bfe194b50cdc8e169f54a73a991ef0fa76329825be078cc782740703da44b4d7eba350304e301d0603551d0e04160414d4133568b95b30158b322071ea8c43ff5b05ccc8301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d0403020349003046022100f504a0866caef029f417142c5cb71354c79ffcd1d640618dfca4f19e16db78d6022100f8eea4829799c06cad08c6d3d2d2ec05e0574154e747ea0fdbb8042cb655aadd" flags="3" />
+ <cert index="2" key="3082016d30820113a0030201020209008855bd1dd2b2b225300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3138303731333137343135315a170d3238303731303137343135315a30143112301006035504030c0965632d703235365f323059301306072a8648ce3d020106082a8648ce3d030107034200041d4cca0472ad97ee3cecef0da93d62b450c6788333b36e7553cde9f74ab5df00bbba6ba950e68461d70bbc271b62151dad2de2bf6203cd2076801c7a9d4422e1a350304e301d0603551d0e041604147991d92b0208fc448bf506d4efc9fff428cb5e5f301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d040302034800304502202769abb1b49fc2f53479c4ae92a6631dabfd522c9acb0bba2b43ebeb99c63011022100d260fb1d1f176cf9b7fa60098bfd24319f4905a3e5fda100a6fe1a2ab19ff09e" flags="7" />
+ <cert index="0" flags="23" />
+ </pastSigs>
+ </sigs>
+
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/xml/three-signers-in-lineage-missing-scheme-version.xml b/services/tests/servicestests/assets/PackageSignaturesTest/xml/three-signers-in-lineage-missing-scheme-version.xml
new file mode 100644
index 0000000..d98573d
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/xml/three-signers-in-lineage-missing-scheme-version.xml
@@ -0,0 +1,9 @@
+ <sigs count="1">
+ <cert index="0" key="3082016e30820115a0030201020209008394f5cad16a89a7300a06082a8648ce3d04030230143112301006035504030c0965632d703235365f32301e170d3138303731343030303532365a170d3238303731313030303532365a30143112301006035504030c0965632d703235365f333059301306072a8648ce3d020106082a8648ce3d03010703420004f31e62430e9db6fc5928d975fc4e47419bacfcb2e07c89299e6cd7e344dd21adfd308d58cb49a1a2a3fecacceea4862069f30be1643bcc255040d8089dfb3743a350304e301d0603551d0e041604146f8d0828b13efaf577fc86b0e99fa3e54bcbcff0301f0603551d230418301680147991d92b0208fc448bf506d4efc9fff428cb5e5f300c0603551d13040530030101ff300a06082a8648ce3d04030203470030440220256bdaa2784c273e4cc291a595a46779dee9de9044dc9f7ab820309567df9fe902201a4ad8c69891b5a8c47434fe9540ed1f4979b5fad3483f3fa04d5677355a579e" />
+ <pastSigs count="3">
+ <cert index="1" key="3082016c30820111a003020102020900ca0fb64dfb66e772300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3136303333313134353830365a170d3433303831373134353830365a30123110300e06035504030c0765632d703235363059301306072a8648ce3d020106082a8648ce3d03010703420004a65f113d22cb4913908307ac31ee2ba0e9138b785fac6536d14ea2ce90d2b4bfe194b50cdc8e169f54a73a991ef0fa76329825be078cc782740703da44b4d7eba350304e301d0603551d0e04160414d4133568b95b30158b322071ea8c43ff5b05ccc8301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d0403020349003046022100f504a0866caef029f417142c5cb71354c79ffcd1d640618dfca4f19e16db78d6022100f8eea4829799c06cad08c6d3d2d2ec05e0574154e747ea0fdbb8042cb655aadd" flags="3" />
+ <cert index="2" key="3082016d30820113a0030201020209008855bd1dd2b2b225300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3138303731333137343135315a170d3238303731303137343135315a30143112301006035504030c0965632d703235365f323059301306072a8648ce3d020106082a8648ce3d030107034200041d4cca0472ad97ee3cecef0da93d62b450c6788333b36e7553cde9f74ab5df00bbba6ba950e68461d70bbc271b62151dad2de2bf6203cd2076801c7a9d4422e1a350304e301d0603551d0e041604147991d92b0208fc448bf506d4efc9fff428cb5e5f301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d040302034800304502202769abb1b49fc2f53479c4ae92a6631dabfd522c9acb0bba2b43ebeb99c63011022100d260fb1d1f176cf9b7fa60098bfd24319f4905a3e5fda100a6fe1a2ab19ff09e" flags="7" />
+ <cert index="0" flags="23" />
+ </pastSigs>
+ </sigs>
+
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/xml/three-signers-in-lineage.xml b/services/tests/servicestests/assets/PackageSignaturesTest/xml/three-signers-in-lineage.xml
new file mode 100644
index 0000000..2ccf5060
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/xml/three-signers-in-lineage.xml
@@ -0,0 +1,9 @@
+ <sigs count="1" schemeVersion="3">
+ <cert index="0" key="3082016e30820115a0030201020209008394f5cad16a89a7300a06082a8648ce3d04030230143112301006035504030c0965632d703235365f32301e170d3138303731343030303532365a170d3238303731313030303532365a30143112301006035504030c0965632d703235365f333059301306072a8648ce3d020106082a8648ce3d03010703420004f31e62430e9db6fc5928d975fc4e47419bacfcb2e07c89299e6cd7e344dd21adfd308d58cb49a1a2a3fecacceea4862069f30be1643bcc255040d8089dfb3743a350304e301d0603551d0e041604146f8d0828b13efaf577fc86b0e99fa3e54bcbcff0301f0603551d230418301680147991d92b0208fc448bf506d4efc9fff428cb5e5f300c0603551d13040530030101ff300a06082a8648ce3d04030203470030440220256bdaa2784c273e4cc291a595a46779dee9de9044dc9f7ab820309567df9fe902201a4ad8c69891b5a8c47434fe9540ed1f4979b5fad3483f3fa04d5677355a579e" />
+ <pastSigs count="3">
+ <cert index="1" key="3082016c30820111a003020102020900ca0fb64dfb66e772300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3136303333313134353830365a170d3433303831373134353830365a30123110300e06035504030c0765632d703235363059301306072a8648ce3d020106082a8648ce3d03010703420004a65f113d22cb4913908307ac31ee2ba0e9138b785fac6536d14ea2ce90d2b4bfe194b50cdc8e169f54a73a991ef0fa76329825be078cc782740703da44b4d7eba350304e301d0603551d0e04160414d4133568b95b30158b322071ea8c43ff5b05ccc8301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d0403020349003046022100f504a0866caef029f417142c5cb71354c79ffcd1d640618dfca4f19e16db78d6022100f8eea4829799c06cad08c6d3d2d2ec05e0574154e747ea0fdbb8042cb655aadd" flags="3" />
+ <cert index="2" key="3082016d30820113a0030201020209008855bd1dd2b2b225300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3138303731333137343135315a170d3238303731303137343135315a30143112301006035504030c0965632d703235365f323059301306072a8648ce3d020106082a8648ce3d030107034200041d4cca0472ad97ee3cecef0da93d62b450c6788333b36e7553cde9f74ab5df00bbba6ba950e68461d70bbc271b62151dad2de2bf6203cd2076801c7a9d4422e1a350304e301d0603551d0e041604147991d92b0208fc448bf506d4efc9fff428cb5e5f301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d040302034800304502202769abb1b49fc2f53479c4ae92a6631dabfd522c9acb0bba2b43ebeb99c63011022100d260fb1d1f176cf9b7fa60098bfd24319f4905a3e5fda100a6fe1a2ab19ff09e" flags="7" />
+ <cert index="0" flags="23" />
+ </pastSigs>
+ </sigs>
+
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/xml/two-signers-in-lineage-invalid-certs-flags.xml b/services/tests/servicestests/assets/PackageSignaturesTest/xml/two-signers-in-lineage-invalid-certs-flags.xml
new file mode 100644
index 0000000..6d567e9
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/xml/two-signers-in-lineage-invalid-certs-flags.xml
@@ -0,0 +1,8 @@
+ <sigs count="1" schemeVersion="3">
+ <cert index="0" key="3082016d30820113a0030201020209008855bd1dd2b2b225300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3138303731333137343135315a170d3238303731303137343135315a30143112301006035504030c0965632d703235365f323059301306072a8648ce3d020106082a8648ce3d030107034200041d4cca0472ad97ee3cecef0da93d62b450c6788333b36e7553cde9f74ab5df00bbba6ba950e68461d70bbc271b62151dad2de2bf6203cd2076801c7a9d4422e1a350304e301d0603551d0e041604147991d92b0208fc448bf506d4efc9fff428cb5e5f301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d040302034800304502202769abb1b49fc2f53479c4ae92a6631dabfd522c9acb0bba2b43ebeb99c63011022100d260fb1d1f176cf9b7fa60098bfd24319f4905a3e5fda100a6fe1a2ab19ff09e" />
+ <pastSigs count="2">
+ <cert index="1" key="3082016c30820111a003020102020900ca0fb64dfb66e772300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3136303333313134353830365a170d3433303831373134353830365a30123110300e06035504030c0765632d703235363059301306072a8648ce3d020106082a8648ce3d03010703420004a65f113d22cb4913908307ac31ee2ba0e9138b785fac6536d14ea2ce90d2b4bfe194b50cdc8e169f54a73a991ef0fa76329825be078cc782740703da44b4d7eba350304e301d0603551d0e04160414d4133568b95b30158b322071ea8c43ff5b05ccc8301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d0403020349003046022100f504a0866caef029f417142c5cb71354c79ffcd1d640618dfca4f19e16db78d6022100f8eea4829799c06cad08c6d3d2d2ec05e0574154e747ea0fdbb8042cb655aadd" flags="x" />
+ <cert index="0" flags="23" />
+ </pastSigs>
+ </sigs>
+
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/xml/two-signers-in-lineage-invalid-pastSigs-cert-index.xml b/services/tests/servicestests/assets/PackageSignaturesTest/xml/two-signers-in-lineage-invalid-pastSigs-cert-index.xml
new file mode 100644
index 0000000..a2146b7
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/xml/two-signers-in-lineage-invalid-pastSigs-cert-index.xml
@@ -0,0 +1,8 @@
+ <sigs count="1" schemeVersion="3">
+ <cert index="0" key="3082016d30820113a0030201020209008855bd1dd2b2b225300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3138303731333137343135315a170d3238303731303137343135315a30143112301006035504030c0965632d703235365f323059301306072a8648ce3d020106082a8648ce3d030107034200041d4cca0472ad97ee3cecef0da93d62b450c6788333b36e7553cde9f74ab5df00bbba6ba950e68461d70bbc271b62151dad2de2bf6203cd2076801c7a9d4422e1a350304e301d0603551d0e041604147991d92b0208fc448bf506d4efc9fff428cb5e5f301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d040302034800304502202769abb1b49fc2f53479c4ae92a6631dabfd522c9acb0bba2b43ebeb99c63011022100d260fb1d1f176cf9b7fa60098bfd24319f4905a3e5fda100a6fe1a2ab19ff09e" />
+ <pastSigs count="2">
+ <cert index="x" key="3082016c30820111a003020102020900ca0fb64dfb66e772300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3136303333313134353830365a170d3433303831373134353830365a30123110300e06035504030c0765632d703235363059301306072a8648ce3d020106082a8648ce3d03010703420004a65f113d22cb4913908307ac31ee2ba0e9138b785fac6536d14ea2ce90d2b4bfe194b50cdc8e169f54a73a991ef0fa76329825be078cc782740703da44b4d7eba350304e301d0603551d0e04160414d4133568b95b30158b322071ea8c43ff5b05ccc8301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d0403020349003046022100f504a0866caef029f417142c5cb71354c79ffcd1d640618dfca4f19e16db78d6022100f8eea4829799c06cad08c6d3d2d2ec05e0574154e747ea0fdbb8042cb655aadd" flags="7" />
+ <cert index="0" flags="0" />
+ </pastSigs>
+ </sigs>
+
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/xml/two-signers-in-lineage-missing-certs-flags.xml b/services/tests/servicestests/assets/PackageSignaturesTest/xml/two-signers-in-lineage-missing-certs-flags.xml
new file mode 100644
index 0000000..90a4a84
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/xml/two-signers-in-lineage-missing-certs-flags.xml
@@ -0,0 +1,8 @@
+ <sigs count="1" schemeVersion="3">
+ <cert index="0" key="3082016d30820113a0030201020209008855bd1dd2b2b225300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3138303731333137343135315a170d3238303731303137343135315a30143112301006035504030c0965632d703235365f323059301306072a8648ce3d020106082a8648ce3d030107034200041d4cca0472ad97ee3cecef0da93d62b450c6788333b36e7553cde9f74ab5df00bbba6ba950e68461d70bbc271b62151dad2de2bf6203cd2076801c7a9d4422e1a350304e301d0603551d0e041604147991d92b0208fc448bf506d4efc9fff428cb5e5f301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d040302034800304502202769abb1b49fc2f53479c4ae92a6631dabfd522c9acb0bba2b43ebeb99c63011022100d260fb1d1f176cf9b7fa60098bfd24319f4905a3e5fda100a6fe1a2ab19ff09e" />
+ <pastSigs count="2">
+ <cert index="1" key="3082016c30820111a003020102020900ca0fb64dfb66e772300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3136303333313134353830365a170d3433303831373134353830365a30123110300e06035504030c0765632d703235363059301306072a8648ce3d020106082a8648ce3d03010703420004a65f113d22cb4913908307ac31ee2ba0e9138b785fac6536d14ea2ce90d2b4bfe194b50cdc8e169f54a73a991ef0fa76329825be078cc782740703da44b4d7eba350304e301d0603551d0e04160414d4133568b95b30158b322071ea8c43ff5b05ccc8301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d0403020349003046022100f504a0866caef029f417142c5cb71354c79ffcd1d640618dfca4f19e16db78d6022100f8eea4829799c06cad08c6d3d2d2ec05e0574154e747ea0fdbb8042cb655aadd" />
+ <cert index="0" flags="23" />
+ </pastSigs>
+ </sigs>
+
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/xml/two-signers-in-lineage-missing-pastSigs-cert-index.xml b/services/tests/servicestests/assets/PackageSignaturesTest/xml/two-signers-in-lineage-missing-pastSigs-cert-index.xml
new file mode 100644
index 0000000..6525e48
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/xml/two-signers-in-lineage-missing-pastSigs-cert-index.xml
@@ -0,0 +1,8 @@
+ <sigs count="1" schemeVersion="3">
+ <cert index="0" key="3082016d30820113a0030201020209008855bd1dd2b2b225300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3138303731333137343135315a170d3238303731303137343135315a30143112301006035504030c0965632d703235365f323059301306072a8648ce3d020106082a8648ce3d030107034200041d4cca0472ad97ee3cecef0da93d62b450c6788333b36e7553cde9f74ab5df00bbba6ba950e68461d70bbc271b62151dad2de2bf6203cd2076801c7a9d4422e1a350304e301d0603551d0e041604147991d92b0208fc448bf506d4efc9fff428cb5e5f301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d040302034800304502202769abb1b49fc2f53479c4ae92a6631dabfd522c9acb0bba2b43ebeb99c63011022100d260fb1d1f176cf9b7fa60098bfd24319f4905a3e5fda100a6fe1a2ab19ff09e" />
+ <pastSigs count="2">
+ <cert key="3082016c30820111a003020102020900ca0fb64dfb66e772300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3136303333313134353830365a170d3433303831373134353830365a30123110300e06035504030c0765632d703235363059301306072a8648ce3d020106082a8648ce3d03010703420004a65f113d22cb4913908307ac31ee2ba0e9138b785fac6536d14ea2ce90d2b4bfe194b50cdc8e169f54a73a991ef0fa76329825be078cc782740703da44b4d7eba350304e301d0603551d0e04160414d4133568b95b30158b322071ea8c43ff5b05ccc8301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d0403020349003046022100f504a0866caef029f417142c5cb71354c79ffcd1d640618dfca4f19e16db78d6022100f8eea4829799c06cad08c6d3d2d2ec05e0574154e747ea0fdbb8042cb655aadd" flags="7" />
+ <cert flags="0" />
+ </pastSigs>
+ </sigs>
+
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/xml/two-signers-in-lineage-multiple-pastSigs-tags.xml b/services/tests/servicestests/assets/PackageSignaturesTest/xml/two-signers-in-lineage-multiple-pastSigs-tags.xml
new file mode 100644
index 0000000..e06892c
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/xml/two-signers-in-lineage-multiple-pastSigs-tags.xml
@@ -0,0 +1,12 @@
+ <sigs count="1" schemeVersion="3">
+ <cert index="0" key="3082016d30820113a0030201020209008855bd1dd2b2b225300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3138303731333137343135315a170d3238303731303137343135315a30143112301006035504030c0965632d703235365f323059301306072a8648ce3d020106082a8648ce3d030107034200041d4cca0472ad97ee3cecef0da93d62b450c6788333b36e7553cde9f74ab5df00bbba6ba950e68461d70bbc271b62151dad2de2bf6203cd2076801c7a9d4422e1a350304e301d0603551d0e041604147991d92b0208fc448bf506d4efc9fff428cb5e5f301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d040302034800304502202769abb1b49fc2f53479c4ae92a6631dabfd522c9acb0bba2b43ebeb99c63011022100d260fb1d1f176cf9b7fa60098bfd24319f4905a3e5fda100a6fe1a2ab19ff09e" />
+ <pastSigs count="2">
+ <cert index="1" key="3082016c30820111a003020102020900ca0fb64dfb66e772300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3136303333313134353830365a170d3433303831373134353830365a30123110300e06035504030c0765632d703235363059301306072a8648ce3d020106082a8648ce3d03010703420004a65f113d22cb4913908307ac31ee2ba0e9138b785fac6536d14ea2ce90d2b4bfe194b50cdc8e169f54a73a991ef0fa76329825be078cc782740703da44b4d7eba350304e301d0603551d0e04160414d4133568b95b30158b322071ea8c43ff5b05ccc8301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d0403020349003046022100f504a0866caef029f417142c5cb71354c79ffcd1d640618dfca4f19e16db78d6022100f8eea4829799c06cad08c6d3d2d2ec05e0574154e747ea0fdbb8042cb655aadd" flags="23" />
+ <cert index="0" flags="23" />
+ <pastSigs count="2">
+ <cert index="1" key="3082016c30820111a003020102020900ca0fb64dfb66e772300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3136303333313134353830365a170d3433303831373134353830365a30123110300e06035504030c0765632d703235363059301306072a8648ce3d020106082a8648ce3d03010703420004a65f113d22cb4913908307ac31ee2ba0e9138b785fac6536d14ea2ce90d2b4bfe194b50cdc8e169f54a73a991ef0fa76329825be078cc782740703da44b4d7eba350304e301d0603551d0e04160414d4133568b95b30158b322071ea8c43ff5b05ccc8301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d0403020349003046022100f504a0866caef029f417142c5cb71354c79ffcd1d640618dfca4f19e16db78d6022100f8eea4829799c06cad08c6d3d2d2ec05e0574154e747ea0fdbb8042cb655aadd" flags="23" />
+ <cert index="0" flags="23" />
+ </pastSigs>
+ </pastSigs>
+ </sigs>
+
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/xml/two-signers-in-lineage-no-caps.xml b/services/tests/servicestests/assets/PackageSignaturesTest/xml/two-signers-in-lineage-no-caps.xml
new file mode 100644
index 0000000..8081d2e
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/xml/two-signers-in-lineage-no-caps.xml
@@ -0,0 +1,8 @@
+ <sigs count="1" schemeVersion="3">
+ <cert index="0" key="3082016d30820113a0030201020209008855bd1dd2b2b225300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3138303731333137343135315a170d3238303731303137343135315a30143112301006035504030c0965632d703235365f323059301306072a8648ce3d020106082a8648ce3d030107034200041d4cca0472ad97ee3cecef0da93d62b450c6788333b36e7553cde9f74ab5df00bbba6ba950e68461d70bbc271b62151dad2de2bf6203cd2076801c7a9d4422e1a350304e301d0603551d0e041604147991d92b0208fc448bf506d4efc9fff428cb5e5f301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d040302034800304502202769abb1b49fc2f53479c4ae92a6631dabfd522c9acb0bba2b43ebeb99c63011022100d260fb1d1f176cf9b7fa60098bfd24319f4905a3e5fda100a6fe1a2ab19ff09e" />
+ <pastSigs count="2">
+ <cert index="1" key="3082016c30820111a003020102020900ca0fb64dfb66e772300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3136303333313134353830365a170d3433303831373134353830365a30123110300e06035504030c0765632d703235363059301306072a8648ce3d020106082a8648ce3d03010703420004a65f113d22cb4913908307ac31ee2ba0e9138b785fac6536d14ea2ce90d2b4bfe194b50cdc8e169f54a73a991ef0fa76329825be078cc782740703da44b4d7eba350304e301d0603551d0e04160414d4133568b95b30158b322071ea8c43ff5b05ccc8301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d0403020349003046022100f504a0866caef029f417142c5cb71354c79ffcd1d640618dfca4f19e16db78d6022100f8eea4829799c06cad08c6d3d2d2ec05e0574154e747ea0fdbb8042cb655aadd" flags="0" />
+ <cert index="0" flags="7" />
+ </pastSigs>
+ </sigs>
+
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/xml/two-signers-in-lineage-undefined-pastSigs-index.xml b/services/tests/servicestests/assets/PackageSignaturesTest/xml/two-signers-in-lineage-undefined-pastSigs-index.xml
new file mode 100644
index 0000000..127000a
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/xml/two-signers-in-lineage-undefined-pastSigs-index.xml
@@ -0,0 +1,8 @@
+ <sigs count="2" schemeVersion="3">
+ <cert index="1" key="3082016c30820111a003020102020900ca0fb64dfb66e772300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3136303333313134353830365a170d3433303831373134353830365a30123110300e06035504030c0765632d703235363059301306072a8648ce3d020106082a8648ce3d03010703420004a65f113d22cb4913908307ac31ee2ba0e9138b785fac6536d14ea2ce90d2b4bfe194b50cdc8e169f54a73a991ef0fa76329825be078cc782740703da44b4d7eba350304e301d0603551d0e04160414d4133568b95b30158b322071ea8c43ff5b05ccc8301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d0403020349003046022100f504a0866caef029f417142c5cb71354c79ffcd1d640618dfca4f19e16db78d6022100f8eea4829799c06cad08c6d3d2d2ec05e0574154e747ea0fdbb8042cb655aadd" />
+ <pastSigs count="2">
+ <cert index="1" flags="23" />
+ <cert index="0" flags="23" />
+ </pastSigs>
+ </sigs>
+
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/xml/two-signers-in-lineage.xml b/services/tests/servicestests/assets/PackageSignaturesTest/xml/two-signers-in-lineage.xml
new file mode 100644
index 0000000..6097ea6
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/xml/two-signers-in-lineage.xml
@@ -0,0 +1,8 @@
+ <sigs count="1" schemeVersion="3">
+ <cert index="0" key="3082016d30820113a0030201020209008855bd1dd2b2b225300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3138303731333137343135315a170d3238303731303137343135315a30143112301006035504030c0965632d703235365f323059301306072a8648ce3d020106082a8648ce3d030107034200041d4cca0472ad97ee3cecef0da93d62b450c6788333b36e7553cde9f74ab5df00bbba6ba950e68461d70bbc271b62151dad2de2bf6203cd2076801c7a9d4422e1a350304e301d0603551d0e041604147991d92b0208fc448bf506d4efc9fff428cb5e5f301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d040302034800304502202769abb1b49fc2f53479c4ae92a6631dabfd522c9acb0bba2b43ebeb99c63011022100d260fb1d1f176cf9b7fa60098bfd24319f4905a3e5fda100a6fe1a2ab19ff09e" />
+ <pastSigs count="2">
+ <cert index="1" key="3082016c30820111a003020102020900ca0fb64dfb66e772300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3136303333313134353830365a170d3433303831373134353830365a30123110300e06035504030c0765632d703235363059301306072a8648ce3d020106082a8648ce3d03010703420004a65f113d22cb4913908307ac31ee2ba0e9138b785fac6536d14ea2ce90d2b4bfe194b50cdc8e169f54a73a991ef0fa76329825be078cc782740703da44b4d7eba350304e301d0603551d0e04160414d4133568b95b30158b322071ea8c43ff5b05ccc8301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d0403020349003046022100f504a0866caef029f417142c5cb71354c79ffcd1d640618dfca4f19e16db78d6022100f8eea4829799c06cad08c6d3d2d2ec05e0574154e747ea0fdbb8042cb655aadd" flags="7" />
+ <cert index="0" flags="3" />
+ </pastSigs>
+ </sigs>
+
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/xml/two-signers-v1v2-missing-cert-tag.xml b/services/tests/servicestests/assets/PackageSignaturesTest/xml/two-signers-v1v2-missing-cert-tag.xml
new file mode 100644
index 0000000..6ed3be8
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/xml/two-signers-v1v2-missing-cert-tag.xml
@@ -0,0 +1,4 @@
+ <sigs count="2" schemeVersion="1">
+ <cert index="0" key="3082016c30820111a003020102020900ca0fb64dfb66e772300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3136303333313134353830365a170d3433303831373134353830365a30123110300e06035504030c0765632d703235363059301306072a8648ce3d020106082a8648ce3d03010703420004a65f113d22cb4913908307ac31ee2ba0e9138b785fac6536d14ea2ce90d2b4bfe194b50cdc8e169f54a73a991ef0fa76329825be078cc782740703da44b4d7eba350304e301d0603551d0e04160414d4133568b95b30158b322071ea8c43ff5b05ccc8301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d0403020349003046022100f504a0866caef029f417142c5cb71354c79ffcd1d640618dfca4f19e16db78d6022100f8eea4829799c06cad08c6d3d2d2ec05e0574154e747ea0fdbb8042cb655aadd" />
+ </sigs>
+
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/xml/two-signers-v1v2.xml b/services/tests/servicestests/assets/PackageSignaturesTest/xml/two-signers-v1v2.xml
new file mode 100644
index 0000000..ee4c4eb
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/xml/two-signers-v1v2.xml
@@ -0,0 +1,5 @@
+ <sigs count="2" schemeVersion="2">
+ <cert index="0" key="3082016c30820111a003020102020900ca0fb64dfb66e772300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3136303333313134353830365a170d3433303831373134353830365a30123110300e06035504030c0765632d703235363059301306072a8648ce3d020106082a8648ce3d03010703420004a65f113d22cb4913908307ac31ee2ba0e9138b785fac6536d14ea2ce90d2b4bfe194b50cdc8e169f54a73a991ef0fa76329825be078cc782740703da44b4d7eba350304e301d0603551d0e04160414d4133568b95b30158b322071ea8c43ff5b05ccc8301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d0403020349003046022100f504a0866caef029f417142c5cb71354c79ffcd1d640618dfca4f19e16db78d6022100f8eea4829799c06cad08c6d3d2d2ec05e0574154e747ea0fdbb8042cb655aadd" />
+ <cert index="1" key="3082016d30820113a0030201020209008855bd1dd2b2b225300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3138303731333137343135315a170d3238303731303137343135315a30143112301006035504030c0965632d703235365f323059301306072a8648ce3d020106082a8648ce3d030107034200041d4cca0472ad97ee3cecef0da93d62b450c6788333b36e7553cde9f74ab5df00bbba6ba950e68461d70bbc271b62151dad2de2bf6203cd2076801c7a9d4422e1a350304e301d0603551d0e041604147991d92b0208fc448bf506d4efc9fff428cb5e5f301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d040302034800304502202769abb1b49fc2f53479c4ae92a6631dabfd522c9acb0bba2b43ebeb99c63011022100d260fb1d1f176cf9b7fa60098bfd24319f4905a3e5fda100a6fe1a2ab19ff09e" />
+ </sigs>
+
diff --git a/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java b/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java
index 2ec6830..4fbc587 100644
--- a/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java
@@ -73,6 +73,9 @@
private static final int IGNORED_ACTION = 13;
private static final int IGNORED_CODE = 1999;
private static final int IGNORED_REPEAT = 42;
+ private static final int IGNORED_META_STATE = 0;
+ private static final int IGNORED_DEVICE_ID = 0;
+ private static final int IGNORED_SCANCODE = 0;
private @Mock Context mContext;
private @Mock Resources mResources;
@@ -369,6 +372,50 @@
}
@Test
+ public void testInterceptPowerKeyDown_longpress() {
+ withCameraDoubleTapPowerEnableConfigValue(true);
+ withCameraDoubleTapPowerDisableSettingValue(0);
+ mGestureLauncherService.updateCameraDoubleTapPowerEnabled();
+ withUserSetupCompleteValue(true);
+
+ long eventTime = INITIAL_EVENT_TIME_MILLIS;
+ KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
+ IGNORED_REPEAT);
+ boolean interactive = true;
+ MutableBoolean outLaunched = new MutableBoolean(true);
+ boolean intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
+ outLaunched);
+ assertFalse(intercepted);
+ assertFalse(outLaunched.value);
+
+ final long interval = GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
+ eventTime += interval;
+ keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
+ IGNORED_REPEAT, IGNORED_META_STATE, IGNORED_DEVICE_ID, IGNORED_SCANCODE,
+ KeyEvent.FLAG_LONG_PRESS);
+ outLaunched.value = false;
+ intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
+ outLaunched);
+ assertFalse(intercepted);
+ assertFalse(outLaunched.value);
+
+ verify(mMetricsLogger, never())
+ .action(eq(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE), anyInt());
+
+ final ArgumentCaptor<Integer> intervalCaptor = ArgumentCaptor.forClass(Integer.class);
+ verify(mMetricsLogger, times(1)).histogram(
+ eq("power_double_tap_interval"), intervalCaptor.capture());
+ List<Integer> intervals = intervalCaptor.getAllValues();
+ assertEquals((int) INITIAL_EVENT_TIME_MILLIS, intervals.get(0).intValue());
+
+ final ArgumentCaptor<Integer> tapCountCaptor = ArgumentCaptor.forClass(Integer.class);
+ verify(mMetricsLogger, times(1)).histogram(
+ eq("power_consecutive_short_tap_count"), tapCountCaptor.capture());
+ List<Integer> tapCounts = tapCountCaptor.getAllValues();
+ assertEquals(1, tapCounts.get(0).intValue());
+ }
+
+ @Test
public void
testInterceptPowerKeyDown_intervalInBoundsCameraPowerGestureOnInteractiveSetupIncomplete() {
withCameraDoubleTapPowerEnableConfigValue(true);
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityDisplayTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityDisplayTests.java
new file mode 100644
index 0000000..44981b3
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityDisplayTests.java
@@ -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.
+ */
+
+package com.android.server.am;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE;
+
+import static com.android.server.am.ActivityStackSupervisor.ON_TOP;
+
+import static org.junit.Assert.assertTrue;
+
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests for the {@link ActivityDisplay} class.
+ *
+ * Build/Install/Run:
+ * atest WmTests:ActivityDisplayTests
+ */
+@SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class ActivityDisplayTests extends ActivityTestsBase {
+
+ @Before
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ setupActivityTaskManagerService();
+ }
+
+ /**
+ * This test simulates the picture-in-picture menu activity launches an activity to fullscreen
+ * stack. The fullscreen stack should be the top focused for resuming correctly.
+ */
+ @Test
+ public void testFullscreenStackCanBeFocusedWhenFocusablePinnedStackExists() {
+ // Create a pinned stack and move to front.
+ final ActivityStack pinnedStack = mSupervisor.getDefaultDisplay().createStack(
+ WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, ON_TOP);
+ final TaskRecord pinnedTask = new TaskBuilder(mService.mStackSupervisor)
+ .setStack(pinnedStack).build();
+ new ActivityBuilder(mService).setActivityFlags(FLAG_ALWAYS_FOCUSABLE)
+ .setTask(pinnedTask).build();
+ pinnedStack.moveToFront("movePinnedStackToFront");
+
+ // The focused stack should be the pinned stack.
+ assertTrue(pinnedStack.isFocusedStackOnDisplay());
+
+ // Create a fullscreen stack and move to front.
+ final ActivityStack fullscreenStack = createFullscreenStackWithSimpleActivityAt(
+ mSupervisor.getDefaultDisplay());
+ fullscreenStack.moveToFront("moveFullscreenStackToFront");
+
+ // The focused stack should be the fullscreen stack.
+ assertTrue(fullscreenStack.isFocusedStackOnDisplay());
+ }
+
+ /**
+ * Test {@link ActivityDisplay#mPreferredTopFocusableStack} will be cleared when the stack is
+ * removed or moved to back, and the focused stack will be according to z-order.
+ */
+ @Test
+ public void testStackShouldNotBeFocusedAfterMovingToBackOrRemoving() {
+ // Create a display which only contains 2 stacks.
+ final ActivityDisplay display = addNewActivityDisplayAt(ActivityDisplay.POSITION_TOP);
+ final ActivityStack stack1 = createFullscreenStackWithSimpleActivityAt(display);
+ final ActivityStack stack2 = createFullscreenStackWithSimpleActivityAt(display);
+
+ // Put stack1 and stack2 on top.
+ stack1.moveToFront("moveStack1ToFront");
+ stack2.moveToFront("moveStack2ToFront");
+ assertTrue(stack2.isFocusedStackOnDisplay());
+
+ // Stack1 should be focused after moving stack2 to back.
+ stack2.moveToBack("moveStack2ToBack", null /* task */);
+ assertTrue(stack1.isFocusedStackOnDisplay());
+
+ // Stack2 should be focused after removing stack1.
+ display.removeChild(stack1);
+ assertTrue(stack2.isFocusedStackOnDisplay());
+ }
+
+ private ActivityStack createFullscreenStackWithSimpleActivityAt(ActivityDisplay display) {
+ final ActivityStack fullscreenStack = display.createStack(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, ON_TOP);
+ final TaskRecord fullscreenTask = new TaskBuilder(mService.mStackSupervisor)
+ .setStack(fullscreenStack).build();
+ new ActivityBuilder(mService).setTask(fullscreenTask).build();
+ return fullscreenStack;
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
index 0345a81..81a0934 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
@@ -33,10 +33,12 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
-import static org.mockito.Matchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -232,6 +234,7 @@
doReturn(displaySleeping).when(display).isSleeping();
doReturn(keyguardShowing).when(keyguard).isKeyguardOrAodShowing(anyInt());
+ doReturn(isFocusedStack).when(stack).isFocusedStackOnDisplay();
doReturn(isFocusedStack ? stack : null).when(display).getFocusedStack();
mSupervisor.applySleepTokensLocked(true);
verify(stack, times(expectWakeFromSleep ? 1 : 0)).awakeFromSleepingLocked();
@@ -402,4 +405,32 @@
assertEquals(primaryStack.getBounds(), STACK_SIZE);
assertEquals(task.getBounds(), TASK_SIZE);
}
+
+ /**
+ * Verify if a stack is not at the topmost position, it should be able to resume its activity if
+ * the stack is the top focused.
+ */
+ @Test
+ public void testResumeActivityWhenNonTopmostStackIsTopFocused() throws Exception {
+ // Create a stack at bottom.
+ final ActivityDisplay display = mSupervisor.getDefaultDisplay();
+ final ActivityStack targetStack = spy(display.createStack(WINDOWING_MODE_FULLSCREEN,
+ ACTIVITY_TYPE_STANDARD, false /* onTop */));
+ final TaskRecord task = new TaskBuilder(mSupervisor).setStack(targetStack).build();
+ final ActivityRecord activity = new ActivityBuilder(mService).setTask(task).build();
+ display.positionChildAtBottom(targetStack);
+
+ // Assume the stack is not at the topmost position (e.g. behind always-on-top stacks) but it
+ // is the current top focused stack.
+ assertFalse(targetStack.isTopStackOnDisplay());
+ doReturn(targetStack).when(mSupervisor).getTopDisplayFocusedStack();
+
+ // Use the stack as target to resume.
+ mSupervisor.resumeFocusedStacksTopActivitiesLocked(
+ targetStack, activity, null /* targetOptions */);
+
+ // Verify the target stack should resume its activity.
+ verify(targetStack, times(1)).resumeTopActivityUncheckedLocked(
+ eq(activity), eq(null /* targetOptions */));
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
index ab814ee..5fcd2aa 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
@@ -38,6 +38,7 @@
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
import android.content.pm.ActivityInfo;
import android.os.UserHandle;
@@ -71,8 +72,8 @@
setupActivityTaskManagerService();
mDefaultDisplay = mSupervisor.getDefaultDisplay();
- mStack = mDefaultDisplay.createStack(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD,
- true /* onTop */);
+ mStack = spy(mDefaultDisplay.createStack(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD,
+ true /* onTop */));
mTask = new TaskBuilder(mSupervisor).setStack(mStack).build();
}
@@ -720,7 +721,7 @@
doReturn(display).when(mSupervisor).getActivityDisplay(anyInt());
doReturn(keyguardGoingAway).when(keyguardController).isKeyguardGoingAway();
doReturn(displaySleeping).when(display).isSleeping();
- doReturn(focusedStack ? mStack : null).when(mSupervisor).getTopDisplayFocusedStack();
+ doReturn(focusedStack).when(mStack).isFocusedStackOnDisplay();
assertEquals(expected, mStack.shouldSleepActivities());
}
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 22add01..20088619 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
@@ -168,6 +168,9 @@
// Makes sure the supervisor is using with the spy object.
atm.mStackSupervisor.setService(atm);
doReturn(mock(IPackageManager.class)).when(am).getPackageManager();
+ PackageManagerInternal mockPackageManager = mock(PackageManagerInternal.class);
+ doReturn(mockPackageManager).when(am).getPackageManagerInternalLocked();
+ doReturn(null).when(mockPackageManager).getDefaultHomeActivity(anyInt());
doNothing().when(am).grantEphemeralAccessLocked(anyInt(), any(), anyInt(), anyInt());
am.mWindowManager = prepareMockWindowManager();
atm.setWindowManager(am.mWindowManager);
@@ -175,10 +178,9 @@
// Put a home stack on the default display, so that we'll always have something focusable.
final TestActivityStackSupervisor supervisor =
(TestActivityStackSupervisor) atm.mStackSupervisor;
- supervisor.mHomeStack = supervisor.mDisplay.createStack(WINDOWING_MODE_FULLSCREEN,
- ACTIVITY_TYPE_HOME, ON_TOP);
+ supervisor.mDisplay.createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
final TaskRecord task = new TaskBuilder(atm.mStackSupervisor)
- .setStack(supervisor.mHomeStack).build();
+ .setStack(supervisor.getDefaultDisplay().getHomeStack()).build();
new ActivityBuilder(atm).setTask(task).build();
}
@@ -447,9 +449,6 @@
final ActivityStackSupervisor supervisor = spy(createTestSupervisor());
final KeyguardController keyguardController = mock(KeyguardController.class);
- // No home stack is set.
- doNothing().when(supervisor).moveHomeStackToFront(any());
- doReturn(true).when(supervisor).moveHomeStackTaskToTop(any());
// Invoked during {@link ActivityStack} creation.
doNothing().when(supervisor).updateUIDsPresentOnDisplay();
// Always keep things awake.
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 e8a824a..9a283fe 100644
--- a/services/tests/servicestests/src/com/android/server/am/MemoryStatUtilTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/MemoryStatUtilTest.java
@@ -18,6 +18,7 @@
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;
@@ -32,6 +33,8 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.Collections;
+
@RunWith(AndroidJUnit4.class)
@SmallTest
public class MemoryStatUtilTest {
@@ -95,7 +98,7 @@
"0",
"2206",
"1257177088",
- "3", // this is rss in bytes
+ "3", // this is RSS (number of pages)
"4294967295",
"2936971264",
"2936991289",
@@ -173,7 +176,7 @@
+ "nonvoluntary_ctxt_switches:\t104\n";
@Test
- public void testParseMemoryStatFromMemcg_parsesCorrectValues() throws Exception {
+ public void testParseMemoryStatFromMemcg_parsesCorrectValues() {
MemoryStat stat = parseMemoryStatFromMemcg(MEMORY_STAT_CONTENTS);
assertEquals(1, stat.pgfault);
assertEquals(2, stat.pgmajfault);
@@ -183,7 +186,7 @@
}
@Test
- public void testParseMemoryStatFromMemcg_emptyMemoryStatContents() throws Exception {
+ public void testParseMemoryStatFromMemcg_emptyMemoryStatContents() {
MemoryStat stat = parseMemoryStatFromMemcg("");
assertNull(stat);
@@ -204,17 +207,22 @@
}
@Test
- public void testParseMemoryStatFromProcfs_parsesCorrectValues() throws Exception {
+ 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);
@@ -223,6 +231,12 @@
}
@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);
}
diff --git a/services/tests/servicestests/src/com/android/server/am/PersisterQueueTests.java b/services/tests/servicestests/src/com/android/server/am/PersisterQueueTests.java
new file mode 100644
index 0000000..d7794b0
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/am/PersisterQueueTests.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 com.android.server.am;
+
+import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertSame;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.os.SystemClock;
+import android.platform.test.annotations.Presubmit;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Predicate;
+
+import androidx.test.filters.FlakyTest;
+import androidx.test.filters.MediumTest;
+import androidx.test.runner.AndroidJUnit4;
+
+/**
+ * atest PersisterQueueTests
+ */
+@RunWith(AndroidJUnit4.class)
+@MediumTest
+@Presubmit
+@FlakyTest(detail = "Confirm stable in post-submit before removing")
+public class PersisterQueueTests implements PersisterQueue.Listener {
+ private static final long INTER_WRITE_DELAY_MS = 50;
+ private static final long PRE_TASK_DELAY_MS = 300;
+ // We allow at most 1s more than the expected timeout.
+ private static final long TIMEOUT_ALLOWANCE = 100;
+
+ private static final Predicate<MatchingTestItem> TEST_ITEM_PREDICATE = item -> item.mMatching;
+
+ private AtomicInteger mItemCount;
+ private CountDownLatch mSetUpLatch;
+ private volatile CountDownLatch mLatch;
+ private List<Boolean> mProbablyDoneResults;
+
+ private PersisterQueue mTarget;
+
+ @Before
+ public void setUp() throws Exception {
+ mItemCount = new AtomicInteger(0);
+ mProbablyDoneResults = new ArrayList<>();
+ mSetUpLatch = new CountDownLatch(1);
+
+ mTarget = new PersisterQueue(INTER_WRITE_DELAY_MS, PRE_TASK_DELAY_MS);
+ mTarget.addListener(this);
+ mTarget.startPersisting();
+
+ assertTrue("Target didn't call callback on start up.",
+ mSetUpLatch.await(TIMEOUT_ALLOWANCE, TimeUnit.MILLISECONDS));
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mTarget.stopPersisting();
+ }
+
+ @Test
+ public void testCallCallbackOnStartUp() throws Exception {
+ // The onPreProcessItem() must be called on start up.
+ assertEquals(1, mProbablyDoneResults.size());
+ // The last one must be called with probably done being true.
+ assertTrue("The last probablyDone must be true.", mProbablyDoneResults.get(0));
+ }
+
+ @Test
+ public void testProcessOneItem() throws Exception {
+ mLatch = new CountDownLatch(1);
+
+ final long dispatchTime = SystemClock.uptimeMillis();
+ mTarget.addItem(new TestItem(), false);
+ assertTrue("Target didn't call callback enough times.",
+ mLatch.await(PRE_TASK_DELAY_MS + TIMEOUT_ALLOWANCE, TimeUnit.MILLISECONDS));
+ assertEquals("Target didn't process item.", 1, mItemCount.get());
+ final long processDuration = SystemClock.uptimeMillis() - dispatchTime;
+ assertTrue("Target didn't wait enough time before processing item. duration: "
+ + processDuration + "ms pretask delay: " + PRE_TASK_DELAY_MS + "ms",
+ processDuration >= PRE_TASK_DELAY_MS);
+
+ // Once before processing this item, once after that.
+ assertEquals(2, mProbablyDoneResults.size());
+ // The last one must be called with probably done being true.
+ assertTrue("The last probablyDone must be true.", mProbablyDoneResults.get(1));
+ }
+
+ @Test
+ public void testProcessOneItem_Flush() throws Exception {
+ mLatch = new CountDownLatch(1);
+
+ final long dispatchTime = SystemClock.uptimeMillis();
+ mTarget.addItem(new TestItem(), true);
+ assertTrue("Target didn't call callback enough times.",
+ mLatch.await(TIMEOUT_ALLOWANCE, TimeUnit.MILLISECONDS));
+ assertEquals("Target didn't process item.", 1, mItemCount.get());
+ final long processDuration = SystemClock.uptimeMillis() - dispatchTime;
+ assertTrue("Target didn't process item immediately when flushing. duration: "
+ + processDuration + "ms pretask delay: "
+ + PRE_TASK_DELAY_MS + "ms",
+ processDuration < PRE_TASK_DELAY_MS);
+
+ // Once before processing this item, once after that.
+ assertEquals(2, mProbablyDoneResults.size());
+ // The last one must be called with probably done being true.
+ assertTrue("The last probablyDone must be true.", mProbablyDoneResults.get(1));
+ }
+
+ @Test
+ public void testProcessTwoItems() throws Exception {
+ mLatch = new CountDownLatch(2);
+
+ final long dispatchTime = SystemClock.uptimeMillis();
+ mTarget.addItem(new TestItem(), false);
+ mTarget.addItem(new TestItem(), false);
+ assertTrue("Target didn't call callback enough times.",
+ mLatch.await(PRE_TASK_DELAY_MS + INTER_WRITE_DELAY_MS + TIMEOUT_ALLOWANCE,
+ TimeUnit.MILLISECONDS));
+ assertEquals("Target didn't process all items.", 2, mItemCount.get());
+ final long processDuration = SystemClock.uptimeMillis() - dispatchTime;
+ assertTrue("Target didn't wait enough time before processing item. duration: "
+ + processDuration + "ms pretask delay: " + PRE_TASK_DELAY_MS
+ + "ms inter write delay: " + INTER_WRITE_DELAY_MS + "ms",
+ processDuration >= PRE_TASK_DELAY_MS + INTER_WRITE_DELAY_MS);
+
+ // Once before processing this item, once after that.
+ assertEquals(3, mProbablyDoneResults.size());
+ // The first one must be called with probably done being false.
+ assertFalse("The first probablyDone must be false.", mProbablyDoneResults.get(1));
+ // The last one must be called with probably done being true.
+ assertTrue("The last probablyDone must be true.", mProbablyDoneResults.get(2));
+ }
+
+ @Test
+ public void testProcessTwoItems_OneAfterAnother() throws Exception {
+ // First item
+ mLatch = new CountDownLatch(1);
+ long dispatchTime = SystemClock.uptimeMillis();
+ mTarget.addItem(new TestItem(), false);
+ assertTrue("Target didn't call callback enough times.",
+ mLatch.await(PRE_TASK_DELAY_MS + TIMEOUT_ALLOWANCE, TimeUnit.MILLISECONDS));
+ long processDuration = SystemClock.uptimeMillis() - dispatchTime;
+ assertTrue("Target didn't wait enough time before processing item."
+ + processDuration + "ms pretask delay: "
+ + PRE_TASK_DELAY_MS + "ms",
+ processDuration >= PRE_TASK_DELAY_MS);
+ assertEquals("Target didn't process item.", 1, mItemCount.get());
+
+ // Second item
+ mLatch = new CountDownLatch(1);
+ dispatchTime = SystemClock.uptimeMillis();
+ // Synchronize on the instance to make sure we schedule the item after it starts to wait for
+ // task indefinitely.
+ synchronized (mTarget) {
+ mTarget.addItem(new TestItem(), false);
+ }
+ assertTrue("Target didn't call callback enough times.",
+ mLatch.await(PRE_TASK_DELAY_MS + TIMEOUT_ALLOWANCE, TimeUnit.MILLISECONDS));
+ assertEquals("Target didn't process all items.", 2, mItemCount.get());
+ processDuration = SystemClock.uptimeMillis() - dispatchTime;
+ assertTrue("Target didn't wait enough time before processing item."
+ + processDuration + "ms pre task delay: "
+ + PRE_TASK_DELAY_MS + "ms",
+ processDuration >= PRE_TASK_DELAY_MS);
+
+ // Once before processing this item, once after that.
+ assertEquals(3, mProbablyDoneResults.size());
+ // The last one must be called with probably done being true.
+ assertTrue("The last probablyDone must be true.", mProbablyDoneResults.get(2));
+ }
+
+ @Test
+ public void testFindLastItemNotReturnDifferentType() throws Exception {
+ synchronized (mTarget) {
+ mTarget.addItem(new TestItem(), false);
+ assertNull(mTarget.findLastItem(TEST_ITEM_PREDICATE, MatchingTestItem.class));
+ }
+ }
+
+ @Test
+ public void testFindLastItemNotReturnMismatchItem() throws Exception {
+ synchronized (mTarget) {
+ mTarget.addItem(new MatchingTestItem(false), false);
+ assertNull(mTarget.findLastItem(TEST_ITEM_PREDICATE, MatchingTestItem.class));
+ }
+ }
+
+ @Test
+ public void testFindLastItemReturnMatchedItem() throws Exception {
+ synchronized (mTarget) {
+ final MatchingTestItem item = new MatchingTestItem(true);
+ mTarget.addItem(item, false);
+ assertSame(item, mTarget.findLastItem(TEST_ITEM_PREDICATE, MatchingTestItem.class));
+ }
+ }
+
+ @Test
+ public void testRemoveItemsNotRemoveDifferentType() throws Exception {
+ mLatch = new CountDownLatch(1);
+ synchronized (mTarget) {
+ mTarget.addItem(new TestItem(), false);
+ mTarget.removeItems(TEST_ITEM_PREDICATE, MatchingTestItem.class);
+ }
+ assertTrue("Target didn't call callback enough times.",
+ mLatch.await(PRE_TASK_DELAY_MS + TIMEOUT_ALLOWANCE, TimeUnit.MILLISECONDS));
+ assertEquals("Target didn't process item.", 1, mItemCount.get());
+ }
+
+ @Test
+ public void testRemoveItemsNotRemoveMismatchedItem() throws Exception {
+ mLatch = new CountDownLatch(1);
+ synchronized (mTarget) {
+ mTarget.addItem(new MatchingTestItem(false), false);
+ mTarget.removeItems(TEST_ITEM_PREDICATE, MatchingTestItem.class);
+ }
+ assertTrue("Target didn't call callback enough times.",
+ mLatch.await(PRE_TASK_DELAY_MS + TIMEOUT_ALLOWANCE, TimeUnit.MILLISECONDS));
+ assertEquals("Target didn't process item.", 1, mItemCount.get());
+ }
+
+ @Test
+ public void testRemoveItemsRemoveMatchedItem() throws Exception {
+ mLatch = new CountDownLatch(1);
+ synchronized (mTarget) {
+ mTarget.addItem(new TestItem(), false);
+ mTarget.addItem(new MatchingTestItem(true), false);
+ mTarget.removeItems(TEST_ITEM_PREDICATE, MatchingTestItem.class);
+ }
+ assertTrue("Target didn't call callback enough times.",
+ mLatch.await(PRE_TASK_DELAY_MS + TIMEOUT_ALLOWANCE, TimeUnit.MILLISECONDS));
+ assertEquals("Target didn't process item.", 1, mItemCount.get());
+ }
+
+ @Test
+ public void testFlushWaitSynchronously() {
+ final long dispatchTime = SystemClock.uptimeMillis();
+ mTarget.addItem(new TestItem(), false);
+ mTarget.addItem(new TestItem(), false);
+ mTarget.flush();
+ assertEquals("Flush should wait until all items are processed before return.",
+ 2, mItemCount.get());
+ final long processTime = SystemClock.uptimeMillis() - dispatchTime;
+ assertTrue("Flush should trigger immediate flush without delays. processTime: "
+ + processTime, processTime < TIMEOUT_ALLOWANCE);
+ }
+
+ @Override
+ public void onPreProcessItem(boolean queueEmpty) {
+ mProbablyDoneResults.add(queueEmpty);
+
+ final CountDownLatch latch = mLatch;
+ if (latch != null) {
+ latch.countDown();
+ }
+
+ mSetUpLatch.countDown();
+ }
+
+ private class TestItem implements PersisterQueue.WriteQueueItem {
+ @Override
+ public void process() {
+ mItemCount.getAndIncrement();
+ }
+ }
+
+ private class MatchingTestItem extends TestItem {
+ private boolean mMatching;
+
+ private MatchingTestItem(boolean matching) {
+ mMatching = matching;
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java b/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
index 70cfad1..1276f65 100644
--- a/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
@@ -125,7 +125,6 @@
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
mStack = mService.mStackSupervisor.getDefaultDisplay().createStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
- ((MyTestActivityStackSupervisor) mService.mStackSupervisor).setHomeStack(mHomeStack);
mCallbacksRecorder = new CallbacksRecorder();
mRecentTasks.registerCallback(mCallbacksRecorder);
QUIET_USER_INFO.flags = UserInfo.FLAG_MANAGED_PROFILE | UserInfo.FLAG_QUIET_MODE;
@@ -558,9 +557,8 @@
final MyTestActivityStackSupervisor supervisor =
(MyTestActivityStackSupervisor) mService.mStackSupervisor;
- final ActivityStack homeStack = new MyTestActivityStack(mDisplay, supervisor);
+ final ActivityStack homeStack = mDisplay.getHomeStack();
final ActivityStack aboveHomeStack = new MyTestActivityStack(mDisplay, supervisor);
- supervisor.setHomeStack(homeStack);
// Add a number of tasks (beyond the max) but ensure that nothing is trimmed because all
// the tasks belong in stacks above the home stack
@@ -579,9 +577,8 @@
final MyTestActivityStackSupervisor supervisor =
(MyTestActivityStackSupervisor) mService.mStackSupervisor;
final ActivityStack behindHomeStack = new MyTestActivityStack(mDisplay, supervisor);
- final ActivityStack homeStack = new MyTestActivityStack(mDisplay, supervisor);
+ final ActivityStack homeStack = mDisplay.getHomeStack();
final ActivityStack aboveHomeStack = new MyTestActivityStack(mDisplay, supervisor);
- supervisor.setHomeStack(homeStack);
// Add a number of tasks (beyond the max) but ensure that only the task in the stack behind
// the home stack is trimmed once a new task is added
@@ -601,9 +598,8 @@
final MyTestActivityStackSupervisor supervisor =
(MyTestActivityStackSupervisor) mService.mStackSupervisor;
- final ActivityStack homeStack = new MyTestActivityStack(mDisplay, supervisor);
+ final ActivityStack homeStack = mDisplay.getHomeStack();
final ActivityStack otherDisplayStack = new MyTestActivityStack(mOtherDisplay, supervisor);
- supervisor.setHomeStack(homeStack);
// Add a number of tasks (beyond the max) on each display, ensure that the tasks are not
// removed
@@ -870,7 +866,7 @@
@Override
public void initialize() {
super.initialize();
- mDisplay = TestActivityDisplay.create(this, DEFAULT_DISPLAY);
+ mDisplay = getActivityDisplay(DEFAULT_DISPLAY);
mOtherDisplay = TestActivityDisplay.create(this, DEFAULT_DISPLAY + 1);
addChild(mOtherDisplay, ActivityDisplay.POSITION_TOP);
addChild(mDisplay, ActivityDisplay.POSITION_TOP);
@@ -881,10 +877,6 @@
mRunningTasks = new TestRunningTasks();
return mRunningTasks;
}
-
- void setHomeStack(ActivityStack stack) {
- mHomeStack = stack;
- }
}
private class MyTestActivityStack extends TestActivityStack {
diff --git a/services/tests/servicestests/src/com/android/server/backup/utils/AppBackupUtilsTest.java b/services/tests/servicestests/src/com/android/server/backup/utils/AppBackupUtilsTest.java
index 9fcdf2d..d52051eec 100644
--- a/services/tests/servicestests/src/com/android/server/backup/utils/AppBackupUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/backup/utils/AppBackupUtilsTest.java
@@ -436,7 +436,6 @@
new Signature[] {SIGNATURE_1},
PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
- null,
null));
packageInfo.applicationInfo = new ApplicationInfo();
@@ -456,7 +455,6 @@
new Signature[] {SIGNATURE_1},
PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
- null,
null));
packageInfo.applicationInfo = new ApplicationInfo();
@@ -537,7 +535,6 @@
new Signature[] {SIGNATURE_1, SIGNATURE_2, SIGNATURE_3},
PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
- null,
null));
packageInfo.applicationInfo = new ApplicationInfo();
@@ -560,7 +557,6 @@
new Signature[] {SIGNATURE_1, SIGNATURE_2, SIGNATURE_3},
PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
- null,
null));
packageInfo.applicationInfo = new ApplicationInfo();
@@ -583,7 +579,6 @@
new Signature[] {signature1Copy, signature2Copy},
PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
- null,
null));
packageInfo.applicationInfo = new ApplicationInfo();
@@ -606,7 +601,6 @@
new Signature[] {SIGNATURE_1, SIGNATURE_2, SIGNATURE_3},
PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
- null,
null));
packageInfo.applicationInfo = new ApplicationInfo();
@@ -629,7 +623,6 @@
new Signature[] {SIGNATURE_1},
PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
- null,
null));
packageInfo.applicationInfo = new ApplicationInfo();
@@ -654,8 +647,7 @@
new Signature[] {SIGNATURE_2},
PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
- new Signature[] {SIGNATURE_1, SIGNATURE_2},
- new int[] {0, 0}));
+ new Signature[] {SIGNATURE_1, SIGNATURE_2}));
packageInfo.applicationInfo = new ApplicationInfo();
// we know signature1Copy is in history, and we want to assume it has
@@ -682,8 +674,7 @@
new Signature[] {SIGNATURE_2},
PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
- new Signature[] {SIGNATURE_1, SIGNATURE_2},
- new int[] {0, 0}));
+ new Signature[] {SIGNATURE_1, SIGNATURE_2}));
packageInfo.applicationInfo = new ApplicationInfo();
// we know signature1Copy is in history, but we want to assume it does not have
diff --git a/services/tests/servicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java b/services/tests/servicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java
index 12f2991..4774985 100644
--- a/services/tests/servicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java
+++ b/services/tests/servicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java
@@ -377,7 +377,6 @@
new Signature[] {FAKE_SIGNATURE_2},
PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
- null,
null));
PackageManagerStub.sPackageInfo = packageInfo;
@@ -414,7 +413,6 @@
new Signature[] {FAKE_SIGNATURE_1},
PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
- null,
null));
PackageManagerStub.sPackageInfo = packageInfo;
@@ -452,7 +450,6 @@
new Signature[] {FAKE_SIGNATURE_1},
PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
- null,
null));
PackageManagerStub.sPackageInfo = packageInfo;
@@ -493,7 +490,6 @@
new Signature[] {FAKE_SIGNATURE_1},
PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
- null,
null));
packageInfo.versionCode = 2;
PackageManagerStub.sPackageInfo = packageInfo;
@@ -537,7 +533,6 @@
new Signature[] {FAKE_SIGNATURE_1},
PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
- null,
null));
packageInfo.versionCode = 1;
PackageManagerStub.sPackageInfo = packageInfo;
@@ -577,7 +572,6 @@
new Signature[] {FAKE_SIGNATURE_1},
PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
- null,
null));
packageInfo.versionCode = 1;
PackageManagerStub.sPackageInfo = packageInfo;
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
index ceee60c..b421280 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -16,6 +16,8 @@
package com.android.server.display;
+import static com.android.server.display.VirtualDisplayAdapter.UNIQUE_ID_PREFIX;
+
import android.content.Context;
import android.hardware.display.BrightnessConfiguration;
import android.hardware.display.Curve;
@@ -25,35 +27,48 @@
import android.hardware.input.InputManagerInternal;
import android.os.Handler;
import android.os.IBinder;
-import android.os.UserHandle;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
+import android.view.Display;
+import android.view.DisplayInfo;
import android.view.SurfaceControl;
+import androidx.test.runner.AndroidJUnit4;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.display.DisplayDeviceInfo;
import com.android.server.display.DisplayManagerService.SyncRoot;
-import com.android.server.display.VirtualDisplayAdapter.SurfaceControlDisplayFactory;
import com.android.server.lights.LightsManager;
import com.android.server.wm.WindowManagerInternal;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.Arrays;
import java.util.List;
-import static org.mockito.Matchers.any;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.mock;
@SmallTest
-public class DisplayManagerServiceTest extends AndroidTestCase {
+@RunWith(AndroidJUnit4.class)
+public class DisplayManagerServiceTest {
private static final int MSG_REGISTER_DEFAULT_DISPLAY_ADAPTERS = 1;
private static final long SHORT_DEFAULT_DISPLAY_TIMEOUT_MILLIS = 10;
+ private Context mContext;
+
private final DisplayManagerService.Injector mShortMockedInjector =
new DisplayManagerService.Injector() {
@Override
@@ -86,8 +101,8 @@
@Mock VirtualDisplayAdapter mMockVirtualDisplayAdapter;
@Mock IBinder mMockDisplayToken;
- @Override
- protected void setUp() throws Exception {
+ @Before
+ public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
LocalServices.removeServiceForTest(InputManagerInternal.class);
@@ -96,15 +111,12 @@
LocalServices.addService(WindowManagerInternal.class, mMockWindowManagerInternal);
LocalServices.removeServiceForTest(LightsManager.class);
LocalServices.addService(LightsManager.class, mMockLightsManager);
- super.setUp();
+
+ mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
}
- @Override
- protected void tearDown() throws Exception {
- super.tearDown();
- }
-
- public void testCreateVirtualDisplay_sentToInputManager() throws Exception {
+ @Test
+ public void testCreateVirtualDisplay_sentToInputManager() {
DisplayManagerService displayManager =
new DisplayManagerService(mContext, mBasicInjector);
registerDefaultDisplays(displayManager);
@@ -115,7 +127,7 @@
DisplayManagerService.BinderService bs = displayManager.new BinderService();
String uniqueId = "uniqueId --- Test";
- String uniqueIdPrefix = "virtual:" + mContext.getPackageName() + ":";
+ String uniqueIdPrefix = UNIQUE_ID_PREFIX + mContext.getPackageName() + ":";
int width = 600;
int height = 800;
int dpi = 320;
@@ -132,19 +144,113 @@
// flush the handler
displayManager.getDisplayHandler().runWithScissors(() -> {}, 0 /* now */);
- ArgumentCaptor<List<DisplayViewport>> virtualViewportCaptor =
- ArgumentCaptor.forClass(List.class);
- verify(mMockInputManagerInternal).setDisplayViewports(
- any(), any(), virtualViewportCaptor.capture());
+ ArgumentCaptor<List<DisplayViewport>> viewportCaptor = ArgumentCaptor.forClass(List.class);
+ verify(mMockInputManagerInternal).setDisplayViewports(viewportCaptor.capture());
+ List<DisplayViewport> viewports = viewportCaptor.getValue();
- assertEquals(1, virtualViewportCaptor.getValue().size());
- DisplayViewport dv = virtualViewportCaptor.getValue().get(0);
- assertEquals(height, dv.deviceHeight);
- assertEquals(width, dv.deviceWidth);
- assertEquals(uniqueIdPrefix + uniqueId, dv.uniqueId);
- assertEquals(displayId, dv.displayId);
+ // Expect to receive 3 viewports: internal, external, and virtual
+ assertEquals(3, viewports.size());
+
+ DisplayViewport virtualViewport = null;
+ DisplayViewport internalViewport = null;
+ DisplayViewport externalViewport = null;
+ for (int i = 0; i < viewports.size(); i++) {
+ DisplayViewport v = viewports.get(i);
+ switch (v.type) {
+ case DisplayViewport.VIEWPORT_INTERNAL: {
+ internalViewport = v;
+ break;
+ }
+ case DisplayViewport.VIEWPORT_EXTERNAL: {
+ externalViewport = v;
+ break;
+ }
+ case DisplayViewport.VIEWPORT_VIRTUAL: {
+ virtualViewport = v;
+ break;
+ }
+ }
+ }
+ // INTERNAL and EXTERNAL viewports get created upon access
+ assertNotNull(internalViewport);
+ assertNotNull(externalViewport);
+ assertNotNull(virtualViewport);
+
+ // INTERNAL and EXTERNAL
+ assertTrue(internalViewport.valid);
+ assertTrue(externalViewport.valid);
+
+ // VIRTUAL
+ assertEquals(height, virtualViewport.deviceHeight);
+ assertEquals(width, virtualViewport.deviceWidth);
+ assertEquals(uniqueIdPrefix + uniqueId, virtualViewport.uniqueId);
+ assertEquals(displayId, virtualViewport.displayId);
}
+ @Test
+ public void testPhysicalViewports() {
+ DisplayManagerService displayManager =
+ new DisplayManagerService(mContext, mBasicInjector);
+ registerDefaultDisplays(displayManager);
+ displayManager.systemReady(false /* safeMode */, true /* onlyCore */);
+ displayManager.windowManagerAndInputReady();
+
+ // This is effectively the DisplayManager service published to ServiceManager.
+ DisplayManagerService.BinderService bs = displayManager.new BinderService();
+
+ when(mMockAppToken.asBinder()).thenReturn(mMockAppToken);
+
+ final int displayIds[] = bs.getDisplayIds();
+ assertEquals(1, displayIds.length);
+ final int displayId = displayIds[0];
+ DisplayInfo info = bs.getDisplayInfo(displayId);
+ assertEquals(info.type, Display.TYPE_BUILT_IN);
+
+ displayManager.performTraversalInternal(mock(SurfaceControl.Transaction.class));
+
+ // flush the handler
+ displayManager.getDisplayHandler().runWithScissors(() -> {}, 0 /* now */);
+
+ ArgumentCaptor<List<DisplayViewport>> viewportCaptor = ArgumentCaptor.forClass(List.class);
+ verify(mMockInputManagerInternal).setDisplayViewports(viewportCaptor.capture());
+ List<DisplayViewport> viewports = viewportCaptor.getValue();
+
+ // Expect to receive 2 viewports: 1 internal, 1 external
+ assertEquals(2, viewports.size());
+
+ DisplayViewport internalViewport = null;
+ DisplayViewport externalViewport = null;
+ for (int i = 0; i < viewports.size(); i++) {
+ DisplayViewport v = viewports.get(i);
+ switch (v.type) {
+ case DisplayViewport.VIEWPORT_INTERNAL: {
+ internalViewport = v;
+ break;
+ }
+ case DisplayViewport.VIEWPORT_EXTERNAL: {
+ externalViewport = v;
+ break;
+ }
+ default: {
+ fail("Unexpected viewport type: " + DisplayViewport.typeToString(v.type));
+ break;
+ }
+ }
+ }
+ // INTERNAL and EXTERNAL viewports get created upon access
+ assertNotNull(internalViewport);
+ assertNotNull(externalViewport);
+ assertTrue(internalViewport.valid);
+ assertEquals(displayId, internalViewport.displayId);
+
+ // To simplify comparison, override the type for external Viewport
+ // TODO (b/116850516) remove this
+ externalViewport.type = internalViewport.type;
+ assertEquals(internalViewport, externalViewport);
+ externalViewport.type = DisplayViewport.VIEWPORT_EXTERNAL; // undo the changes above
+ }
+
+ @Test
public void testCreateVirtualDisplayRotatesWithContent() throws Exception {
DisplayManagerService displayManager =
new DisplayManagerService(mContext, mBasicInjector);
@@ -178,6 +284,7 @@
/**
* Tests that the virtual display is created along-side the default display.
*/
+ @Test
public void testStartVirtualDisplayWithDefaultDisplay_Succeeds() throws Exception {
DisplayManagerService displayManager =
new DisplayManagerService(mContext, mShortMockedInjector);
@@ -188,6 +295,7 @@
/**
* Tests that we get a Runtime exception when we cannot initialize the default display.
*/
+ @Test
public void testStartVirtualDisplayWithDefDisplay_NoDefaultDisplay() throws Exception {
DisplayManagerService displayManager =
new DisplayManagerService(mContext, mShortMockedInjector);
@@ -206,6 +314,7 @@
/**
* Tests that we get a Runtime exception when we cannot initialize the virtual display.
*/
+ @Test
public void testStartVirtualDisplayWithDefDisplay_NoVirtualDisplayAdapter() throws Exception {
DisplayManagerService displayManager = new DisplayManagerService(mContext,
new DisplayManagerService.Injector() {
@@ -232,6 +341,7 @@
/**
* Tests that an exception is raised for too dark a brightness configuration.
*/
+ @Test
public void testTooDarkBrightnessConfigurationThrowException() {
DisplayManagerService displayManager =
new DisplayManagerService(mContext, mShortMockedInjector);
@@ -266,6 +376,7 @@
/**
* Tests that no exception is raised for not too dark a brightness configuration.
*/
+ @Test
public void testBrightEnoughBrightnessConfigurationDoesNotThrowException() {
DisplayManagerService displayManager =
new DisplayManagerService(mContext, mShortMockedInjector);
@@ -279,6 +390,7 @@
/**
* Tests that null brightness configurations are alright.
*/
+ @Test
public void testNullBrightnessConfiguration() {
DisplayManagerService displayManager =
new DisplayManagerService(mContext, mShortMockedInjector);
diff --git a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
index 2de5d87..a3348c2 100644
--- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
@@ -1052,7 +1052,6 @@
genSignatures(signatures),
PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
- null,
null));
return pi;
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
index 318ed3a..9af7b1c 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
@@ -504,7 +504,6 @@
new Signature[] { new Signature(new byte[16]) },
2,
new ArraySet<>(),
- null,
null);
pkg.mExtras = new Bundle();
pkg.mRestrictedAccountType = "foo19";
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageSignaturesTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageSignaturesTest.java
new file mode 100644
index 0000000..d3a77d3
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageSignaturesTest.java
@@ -0,0 +1,474 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.pm;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.content.Context;
+import android.content.pm.PackageParser;
+import android.content.pm.Signature;
+import android.util.Xml;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.util.HexDump;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.xmlpull.v1.XmlPullParser;
+
+import java.io.File;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+@RunWith(AndroidJUnit4.class)
+public class PackageSignaturesTest {
+ private static final String TEST_RESOURCES_FOLDER = "PackageSignaturesTest";
+
+ private Context mContext;
+
+ private PackageSetting mPackageSetting;
+
+ // These signatures are the DER encoding of the ec-p256[_X] X509 certificates in the certs/
+ // directory. The apksigner tool was used to sign a test APK with these certificates and the
+ // corresponding ec-p256{_X].pk8 private key file. For the lineage tests the
+ // ec-p256-lineage-X-signers file was provided as the parameter to the --lineage option when
+ // signing the APK. The APK was then installed on a test device, the packages.xml file was
+ // pulled from the device, and the APK's <sig> tag was used as the basis for these tests.
+ // For more details see the README under the xml/ directory.
+ private static final String FIRST_EXPECTED_SIGNATURE =
+ "3082016c30820111a003020102020900ca0fb64dfb66e772300a06082a8648ce3d04030230123110300e06"
+ + "035504030c0765632d70323536301e170d3136303333313134353830365a170d34333038313731343538"
+ + "30365a30123110300e06035504030c0765632d703235363059301306072a8648ce3d020106082a8648ce"
+ + "3d03010703420004a65f113d22cb4913908307ac31ee2ba0e9138b785fac6536d14ea2ce90d2b4bfe194"
+ + "b50cdc8e169f54a73a991ef0fa76329825be078cc782740703da44b4d7eba350304e301d0603551d0e04"
+ + "160414d4133568b95b30158b322071ea8c43ff5b05ccc8301f0603551d23041830168014d4133568b95b"
+ + "30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d0403020349"
+ + "003046022100f504a0866caef029f417142c5cb71354c79ffcd1d640618dfca4f19e16db78d6022100f8"
+ + "eea4829799c06cad08c6d3d2d2ec05e0574154e747ea0fdbb8042cb655aadd";
+ private static final String SECOND_EXPECTED_SIGNATURE =
+ "3082016d30820113a0030201020209008855bd1dd2b2b225300a06082a8648ce3d04030230123110300e06"
+ + "035504030c0765632d70323536301e170d3138303731333137343135315a170d32383037313031373431"
+ + "35315a30143112301006035504030c0965632d703235365f323059301306072a8648ce3d020106082a86"
+ + "48ce3d030107034200041d4cca0472ad97ee3cecef0da93d62b450c6788333b36e7553cde9f74ab5df00"
+ + "bbba6ba950e68461d70bbc271b62151dad2de2bf6203cd2076801c7a9d4422e1a350304e301d0603551d"
+ + "0e041604147991d92b0208fc448bf506d4efc9fff428cb5e5f301f0603551d23041830168014d4133568"
+ + "b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d040302"
+ + "034800304502202769abb1b49fc2f53479c4ae92a6631dabfd522c9acb0bba2b43ebeb99c63011022100"
+ + "d260fb1d1f176cf9b7fa60098bfd24319f4905a3e5fda100a6fe1a2ab19ff09e";
+ private static final String THIRD_EXPECTED_SIGNATURE =
+ "3082016e30820115a0030201020209008394f5cad16a89a7300a06082a8648ce3d04030230143112301006"
+ + "035504030c0965632d703235365f32301e170d3138303731343030303532365a170d3238303731313030"
+ + "303532365a30143112301006035504030c0965632d703235365f333059301306072a8648ce3d02010608"
+ + "2a8648ce3d03010703420004f31e62430e9db6fc5928d975fc4e47419bacfcb2e07c89299e6cd7e344dd"
+ + "21adfd308d58cb49a1a2a3fecacceea4862069f30be1643bcc255040d8089dfb3743a350304e301d0603"
+ + "551d0e041604146f8d0828b13efaf577fc86b0e99fa3e54bcbcff0301f0603551d230418301680147991"
+ + "d92b0208fc448bf506d4efc9fff428cb5e5f300c0603551d13040530030101ff300a06082a8648ce3d04"
+ + "030203470030440220256bdaa2784c273e4cc291a595a46779dee9de9044dc9f7ab820309567df9fe902"
+ + "201a4ad8c69891b5a8c47434fe9540ed1f4979b5fad3483f3fa04d5677355a579e";
+
+ // When running tests using the pastSigs tag / lineage the past signers and their capabilities
+ // should be returned in the SigningDetails. The flags attribute of the cert tag under the
+ // pastSigs tag contains these capabilities; for tests that verify the lineage the capabilities
+ // of the signers should be set to the values in this Map.
+ private static final Map<String, Integer> SIGNATURE_TO_CAPABILITY_MAP;
+
+ static {
+ SIGNATURE_TO_CAPABILITY_MAP = new HashMap<>();
+ SIGNATURE_TO_CAPABILITY_MAP.put(FIRST_EXPECTED_SIGNATURE, 3);
+ SIGNATURE_TO_CAPABILITY_MAP.put(SECOND_EXPECTED_SIGNATURE, 7);
+ SIGNATURE_TO_CAPABILITY_MAP.put(THIRD_EXPECTED_SIGNATURE, 23);
+ }
+
+ private static final int[] CAPABILITIES =
+ {PackageParser.SigningDetails.CertCapabilities.INSTALLED_DATA,
+ PackageParser.SigningDetails.CertCapabilities.SHARED_USER_ID,
+ PackageParser.SigningDetails.CertCapabilities.PERMISSION,
+ PackageParser.SigningDetails.CertCapabilities.ROLLBACK};
+
+ @Before
+ public void setUp() throws Exception {
+ mContext = InstrumentationRegistry.getContext();
+ mPackageSetting = createPackageSetting();
+ }
+
+ @Test
+ public void testReadXmlWithOneSignerCompletesSuccessfully() throws Exception {
+ // Verifies the good path of reading a single sigs tag with one signer returns the
+ // expected signature and scheme version.
+ verifyReadXmlReturnsExpectedSignatures("xml/one-signer.xml", 1, FIRST_EXPECTED_SIGNATURE);
+ }
+
+ @Test
+ public void testReadXmlWithTwoV1V2Signers() throws Exception {
+ // Verifies the good path of reading a single sigs tag with multiple signers returns the
+ // expected signatures and scheme version.
+ verifyReadXmlReturnsExpectedSignatures("xml/two-signers-v1v2.xml", 2,
+ FIRST_EXPECTED_SIGNATURE, SECOND_EXPECTED_SIGNATURE);
+ }
+
+ @Test
+ public void testReadXmlFromTwoSigsTagsWithSameSigner() throws Exception {
+ // Verifies the good path of reading two separate packages tags from the same signer. The
+ // first call to readXml should return the list with the expected signature, then the second
+ // call should reference this signature and complete successfully with no new entries in the
+ // List.
+ XmlPullParser parser = getXMLFromResources("xml/one-signer.xml");
+ ArrayList<Signature> signatures = new ArrayList<>();
+ mPackageSetting.signatures.readXml(parser, signatures);
+ Set<String> expectedSignatures = createSetOfSignatures(FIRST_EXPECTED_SIGNATURE);
+ verifySignaturesContainExpectedValues(signatures, expectedSignatures);
+ parser = getXMLFromResources("xml/one-signer-previous-cert.xml");
+ mPackageSetting.signatures.readXml(parser, signatures);
+ expectedSignatures = createSetOfSignatures(FIRST_EXPECTED_SIGNATURE);
+ verifySignaturesContainExpectedValues(signatures, expectedSignatures);
+ }
+
+ @Test
+ public void testReadXmlWithSigningLineage() throws Exception {
+ // Verifies the good path of reading a single sigs tag including pastSigs with the
+ // signing lineage returns the expected signatures and lineage for two and three signers
+ // in the lineage.
+ verifyReadXmlReturnsExpectedSignaturesAndLineage("xml/two-signers-in-lineage.xml", 3,
+ FIRST_EXPECTED_SIGNATURE, SECOND_EXPECTED_SIGNATURE);
+ verifyReadXmlReturnsExpectedSignaturesAndLineage("xml/three-signers-in-lineage.xml", 3,
+ FIRST_EXPECTED_SIGNATURE, SECOND_EXPECTED_SIGNATURE, THIRD_EXPECTED_SIGNATURE);
+ }
+
+ @Test
+ public void testReadXmlWithInvalidPublicKeyInCertKey() throws Exception {
+ // If the cert tag key attribute does not contain a valid public key then a
+ // CertificateException should be thrown when attempting to build the SigningDetails; in
+ // this case the signing details should be set to UNKNOWN.
+ XmlPullParser parser = getXMLFromResources(
+ "xml/one-signer-invalid-public-key-cert-key.xml");
+ ArrayList<Signature> signatures = new ArrayList<>();
+ mPackageSetting.signatures.readXml(parser, signatures);
+ assertEquals(
+ "The signing details was not UNKNOWN after parsing an invalid public key cert key"
+ + " attribute",
+ PackageParser.SigningDetails.UNKNOWN, mPackageSetting.signatures.mSigningDetails);
+ }
+
+ @Test
+ public void testReadXmlWithMissingSigsCount() throws Exception {
+ // Verifies if the sigs count attribute is missing then the signature cannot be read but the
+ // method does not throw an exception.
+ verifyReadXmlReturnsExpectedSignatures("xml/one-signer-missing-sigs-count.xml",
+ PackageParser.SigningDetails.SignatureSchemeVersion.UNKNOWN);
+ }
+
+ @Test
+ public void testReadXmlWithMissingSchemeVersion() throws Exception {
+ // Verifies if the schemeVersion is an invalid value the signature can still be obtained.
+ verifyReadXmlReturnsExpectedSignatures("xml/one-signer-missing-scheme-version.xml",
+ PackageParser.SigningDetails.SignatureSchemeVersion.UNKNOWN,
+ FIRST_EXPECTED_SIGNATURE);
+ }
+
+ @Test
+ public void testReadXmlWithSigningLineageWithMissingSchemeVersion() throws Exception {
+ // Verifies if the scheme version cannot be read the signers in the lineage can still be
+ // obtained.
+ verifyReadXmlReturnsExpectedSignaturesAndLineage(
+ "xml/three-signers-in-lineage-missing-scheme-version.xml",
+ PackageParser.SigningDetails.SignatureSchemeVersion.UNKNOWN,
+ FIRST_EXPECTED_SIGNATURE, SECOND_EXPECTED_SIGNATURE, THIRD_EXPECTED_SIGNATURE);
+ }
+
+ @Test
+ public void testReadXmlWithInvalidCertIndex() throws Exception {
+ // If the cert index attribute is invalid the signature will not be read but the call
+ // should exit gracefully.
+ verifyReadXmlReturnsExpectedSignatures("xml/one-signer-invalid-cert-index.xml", 3);
+ }
+
+ @Test
+ public void testReadXmlWithMissingCertIndex() throws Exception {
+ // If the cert index attribute is missing the signature will not be read but the call should
+ // exit gracefully.
+ verifyReadXmlReturnsExpectedSignatures("xml/one-signer-missing-cert-index.xml", 3);
+ }
+
+ @Test
+ public void testReadXmlWithInvalidCertKey() throws Exception {
+ // If the cert key value is invalid the signature cannot be read but the call should exit
+ // gracefully.
+ verifyReadXmlReturnsExpectedSignatures("xml/one-signer-invalid-cert-key.xml", 3);
+ }
+
+ @Test
+ public void testReadXmlWithMissingCertKey() throws Exception {
+ // If the cert key is missing the signature cannot be read but the call should exit
+ // gracefully.
+ verifyReadXmlReturnsExpectedSignatures("xml/one-signer-missing-cert-key.xml", 3);
+ }
+
+ @Test
+ public void testReadXmlWithMissingCertTag() throws Exception {
+ // If the cert tag is missing there is no signature to read but the call should exit
+ // gracefully.
+ verifyReadXmlReturnsExpectedSignatures("xml/one-signer-missing-cert-tag.xml", 3);
+ }
+
+ @Test
+ public void testReadXmlWithTooFewCertTags() throws Exception {
+ // If the number of cert tags is less than that specified in the count attribute then the
+ // signatures that could be read are copied to a smaller array to be used when building
+ // the SigningDetails object. This test verifies if there are too few cert tags the
+ // available signatures can still be obtained.
+ verifyReadXmlReturnsExpectedSignatures("xml/two-signers-v1v2-missing-cert-tag.xml", 1,
+ FIRST_EXPECTED_SIGNATURE);
+ }
+
+ @Test
+ public void testReadXmlWithExtraCertTag() throws Exception {
+ // Verifies if there are more cert tags than specified by the count attribute the extra cert
+ // tag is ignored and the expected signature from the first cert tag is returned.
+ verifyReadXmlReturnsExpectedSignatures("xml/one-signer-extra-cert-tag.xml", 3,
+ FIRST_EXPECTED_SIGNATURE);
+ }
+
+ @Test
+ public void testReadXmlWithInvalidTag() throws Exception {
+ // Verifies an invalid tag under sigs is ignored and the expected signature is returned.
+ verifyReadXmlReturnsExpectedSignatures("xml/one-signer-invalid-tag.xml", 3,
+ FIRST_EXPECTED_SIGNATURE);
+ }
+
+ @Test
+ public void testReadXmlWithInvalidPastSigsCount() throws Exception {
+ // Verifies if the pastSigs tag contains an invalid count attribute the current signature
+ // is still returned; in this case the third expected signature is the most recent signer.
+ verifyReadXmlReturnsExpectedSignatures(
+ "xml/three-signers-in-lineage-invalid-pastSigs-count.xml", 3,
+ THIRD_EXPECTED_SIGNATURE);
+ }
+
+ @Test
+ public void testReadXmlWithMissingPastSigsCount() throws Exception {
+ // Verifies if the pastSigs tag is missing the count attribute the current signature is
+ // still returned; in this case the third expected signature is the most recent signer.
+ verifyReadXmlReturnsExpectedSignaturesAndLineage(
+ "xml/three-signers-in-lineage-missing-pastSigs-count.xml", 3,
+ THIRD_EXPECTED_SIGNATURE);
+ }
+
+ @Test
+ public void testReadXmlWithInvalidCertFlags() throws Exception {
+ // Verifies if the cert tag contains an invalid flags attribute the expected signatures
+ // are still returned, although since the flags could not be read these signatures will not
+ // include the capabilities of the previous signers in the lineage.
+ verifyReadXmlReturnsExpectedSignatures("xml/two-signers-in-lineage-invalid-certs-flags.xml",
+ 3, FIRST_EXPECTED_SIGNATURE, SECOND_EXPECTED_SIGNATURE);
+ }
+
+ @Test
+ public void testReadXmlWithMissingCertFlags() throws Exception {
+ // Verifies if the cert tag does not contain a flags attribute the expected signatures are
+ // still returned, although since there are no flags to read these signatures will not
+ // include the capabilities of the previous signers in the lineage.
+ verifyReadXmlReturnsExpectedSignatures("xml/two-signers-in-lineage-missing-certs-flags.xml",
+ 3, FIRST_EXPECTED_SIGNATURE, SECOND_EXPECTED_SIGNATURE);
+ }
+
+ @Test
+ public void testReadXmlWithMultiplePastSigsTags() throws Exception {
+ // Verifies if multiple pastSigs tags are found under the sigs tag the additional pastSigs
+ // tag is ignored and the expected signatures are returned along with the previous signer in
+ // the lineage.
+ verifyReadXmlReturnsExpectedSignaturesAndLineage(
+ "xml/two-signers-in-lineage-multiple-pastSigs-tags.xml", 3,
+ FIRST_EXPECTED_SIGNATURE, SECOND_EXPECTED_SIGNATURE);
+ }
+
+ @Test
+ public void testReadXmlWithInvalidPastSigsCertIndex() throws Exception {
+ // If the pastSigs cert tag contains an invalid index attribute that signature cannot be
+ // read but the current signature should still be returned.
+ verifyReadXmlReturnsExpectedSignaturesAndLineage(
+ "xml/two-signers-in-lineage-invalid-pastSigs-cert-index.xml", 3,
+ SECOND_EXPECTED_SIGNATURE);
+ }
+
+ @Test
+ public void testReadXmlWithMissingPastSigsCertIndex() throws Exception {
+ // If the pastSigs cert tag does not contain an index attribute that signature cannot be
+ // read but the current signature should still be returned.
+ verifyReadXmlReturnsExpectedSignaturesAndLineage(
+ "xml/two-signers-in-lineage-missing-pastSigs-cert-index.xml", 3,
+ SECOND_EXPECTED_SIGNATURE);
+ }
+
+ @Test
+ public void testReadXmlWithUndefinedPastSigsIndex() throws Exception {
+ // If a cert tag does not contain a key attribute it is assumed that the index attribute
+ // refers to a previously seen signature. If a signature does not yet exist at this index
+ // then the current signature cannot be read but any other signatures should still be
+ // returned.
+ verifyReadXmlReturnsExpectedSignatures(
+ "xml/two-signers-in-lineage-undefined-pastSigs-index.xml", 3,
+ FIRST_EXPECTED_SIGNATURE, null);
+ }
+
+ @Test
+ public void testReadXmlWithTooFewPastSigsCertTags() throws Exception {
+ // If the number of cert tags is less than that specified in the count attribute of the
+ // pastSigs tag then the signatures that could be read are copied to a smaller array to be
+ // used when building the SigningDetails object. This test verifies if there are too few
+ // cert tags the available signatures and lineage can still be obtained.
+ verifyReadXmlReturnsExpectedSignaturesAndLineage(
+ "xml/three-signers-in-lineage-missing-pastSigs-cert-tag.xml", 3,
+ FIRST_EXPECTED_SIGNATURE, THIRD_EXPECTED_SIGNATURE);
+ }
+
+ @Test
+ public void testReadXmlWithPastSignerWithNoCapabilities() throws Exception {
+ // When rotating the signing key a developer is able to specify the capabilities granted to
+ // the apps signed with the previous key. This test verifies a previous signing certificate
+ // with the flags set to 0 does not have any capabilities.
+ XmlPullParser parser = getXMLFromResources("xml/two-signers-in-lineage-no-caps.xml");
+ ArrayList<Signature> signatures = new ArrayList<>();
+ mPackageSetting.signatures.readXml(parser, signatures);
+ // obtain the Signature in the list matching the previous signing certificate
+ Signature previousSignature = null;
+ for (Signature signature : signatures) {
+ String signatureValue = HexDump.toHexString(signature.toByteArray(), false);
+ if (signatureValue.equals(FIRST_EXPECTED_SIGNATURE)) {
+ previousSignature = signature;
+ break;
+ }
+ }
+ assertNotNull("Unable to find the expected previous signer", previousSignature);
+ for (int capability : CAPABILITIES) {
+ assertFalse("The previous signer should not have the " + capability + " capability",
+ mPackageSetting.signatures.mSigningDetails.hasCertificate(previousSignature,
+ capability));
+ }
+ }
+
+ /**
+ * Verifies reading the sigs tag of the provided XML file returns the specified signature scheme
+ * version and the provided signatures.
+ */
+ private void verifyReadXmlReturnsExpectedSignatures(String xmlFile, int expectedSchemeVersion,
+ String... expectedSignatureValues) throws Exception {
+ XmlPullParser parser = getXMLFromResources(xmlFile);
+ ArrayList<Signature> signatures = new ArrayList<>();
+ mPackageSetting.signatures.readXml(parser, signatures);
+ Set<String> expectedSignatures = createSetOfSignatures(expectedSignatureValues);
+ verifySignaturesContainExpectedValues(signatures, expectedSignatures);
+ assertEquals("The returned signature scheme is not the expected value",
+ expectedSchemeVersion,
+ mPackageSetting.signatures.mSigningDetails.signatureSchemeVersion);
+ }
+
+ /**
+ * Verifies reading the sigs tag of the provided XML file returns the specified signature scheme
+ * version, the provided signatures, and that the previous signers have the expected
+ * capabilities.
+ */
+ private void verifyReadXmlReturnsExpectedSignaturesAndLineage(String xmlFile,
+ int schemeVersion, String... expectedSignatureValues) throws Exception {
+ XmlPullParser parser = getXMLFromResources(xmlFile);
+ ArrayList<Signature> signatures = new ArrayList<>();
+ mPackageSetting.signatures.readXml(parser, signatures);
+ Set<String> expectedSignatures = createSetOfSignatures(expectedSignatureValues);
+ verifySignaturesContainExpectedValues(signatures, expectedSignatures);
+ assertEquals("The returned signature scheme is not the expected value", schemeVersion,
+ mPackageSetting.signatures.mSigningDetails.signatureSchemeVersion);
+ for (Signature signature : signatures) {
+ String signatureValue = HexDump.toHexString(signature.toByteArray(), false);
+ int expectedCapabilities = SIGNATURE_TO_CAPABILITY_MAP.get(signatureValue);
+ assertTrue("The signature " + signatureValue
+ + " was not found with the expected capabilities of " +
+ expectedCapabilities
+ + " in the signing details",
+ mPackageSetting.signatures.mSigningDetails.hasCertificate(signature,
+ expectedCapabilities));
+ }
+ }
+
+ /**
+ * Verifies the provided {@code List} contains Signatures that match the provided hex encoded
+ * signature values.
+ *
+ * The provided {@code Set} will be modified by this method as elements will be removed to
+ * ensure duplicate expected Signatures are not in the {@code List}.
+ */
+ private static void verifySignaturesContainExpectedValues(ArrayList<Signature> signatures,
+ Set<String> expectedSignatures) {
+ assertEquals("The number of signatures does not equal the expected number of signatures",
+ expectedSignatures.size(), signatures.size());
+ for (Signature signature : signatures) {
+ String signatureString = null;
+ if (signature != null) {
+ signatureString = HexDump.toHexString(signature.toByteArray(), false);
+ }
+ // If the signature is in the expected set then remove it so that duplicate matching
+ // signatures are reported.
+ if (expectedSignatures.contains(signatureString)) {
+ expectedSignatures.remove(signatureString);
+ } else {
+ fail("The following unexpected signature was returned: " + signatureString);
+ }
+ }
+ }
+
+ private static Set<String> createSetOfSignatures(String... signatures) {
+ Set<String> result = new HashSet<String>();
+ for (String signature : signatures) {
+ result.add(signature);
+ }
+ return result;
+ }
+
+ private XmlPullParser getXMLFromResources(String xmlFile) throws Exception {
+ InputStream xmlStream = mContext.getResources().getAssets().open(
+ TEST_RESOURCES_FOLDER + "/" + xmlFile);
+ XmlPullParser result = Xml.newPullParser();
+ result.setInput(xmlStream, StandardCharsets.UTF_8.name());
+ int type;
+ // advance the parser to the first tag
+ while ((type = result.next()) != XmlPullParser.START_TAG
+ && type != XmlPullParser.END_DOCUMENT) {
+ ;
+ }
+ return result;
+ }
+
+ private static PackageSetting createPackageSetting() {
+ // Generic PackageSetting object with values from a test app installed on a device to be
+ // used to test the methods under the PackageSignatures signatures data member.
+ File appPath = new File("/data/app/app");
+ PackageSetting result = new PackageSetting("test.app", null, appPath, appPath,
+ "/data/app/app", null, null, null,
+ 1, 940097092, 0, null,
+ null, 0 /*userId*/, null, null);
+ return result;
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/pm/backup/BackupUtilsTest.java b/services/tests/servicestests/src/com/android/server/pm/backup/BackupUtilsTest.java
index 13612a1..182760b 100644
--- a/services/tests/servicestests/src/com/android/server/pm/backup/BackupUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/backup/BackupUtilsTest.java
@@ -99,7 +99,6 @@
new Signature[] {SIGNATURE_1},
PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
- null,
null));
packageInfo.applicationInfo = new ApplicationInfo();
@@ -119,7 +118,6 @@
new Signature[] {SIGNATURE_1},
PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
- null,
null));
packageInfo.applicationInfo = new ApplicationInfo();
@@ -203,7 +201,6 @@
new Signature[] {SIGNATURE_1, SIGNATURE_2, SIGNATURE_3},
PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
- null,
null));
packageInfo.applicationInfo = new ApplicationInfo();
@@ -226,7 +223,6 @@
new Signature[] {SIGNATURE_1, SIGNATURE_2, SIGNATURE_3},
PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
- null,
null));
packageInfo.applicationInfo = new ApplicationInfo();
@@ -248,7 +244,6 @@
new Signature[] {SIGNATURE_1, SIGNATURE_2},
PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
- null,
null));
packageInfo.applicationInfo = new ApplicationInfo();
@@ -271,7 +266,6 @@
new Signature[] {SIGNATURE_1, SIGNATURE_2, SIGNATURE_3},
PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
- null,
null));
packageInfo.applicationInfo = new ApplicationInfo();
@@ -295,7 +289,6 @@
new Signature[] {SIGNATURE_1},
PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
- null,
null));
packageInfo.applicationInfo = new ApplicationInfo();
@@ -320,7 +313,6 @@
new Signature[] {SIGNATURE_1, SIGNATURE_2},
PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
- null,
null));
packageInfo.applicationInfo = new ApplicationInfo();
@@ -348,7 +340,6 @@
new Signature[] {SIGNATURE_1, SIGNATURE_2},
PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
- null,
null));
packageInfo.applicationInfo = new ApplicationInfo();
diff --git a/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerTestBase.java b/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerTestBase.java
index acd065e..e16f118 100644
--- a/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerTestBase.java
+++ b/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerTestBase.java
@@ -16,6 +16,10 @@
package com.android.server.policy;
+import static android.view.DisplayCutout.BOUNDS_POSITION_BOTTOM;
+import static android.view.DisplayCutout.BOUNDS_POSITION_LEFT;
+import static android.view.DisplayCutout.BOUNDS_POSITION_RIGHT;
+import static android.view.DisplayCutout.BOUNDS_POSITION_TOP;
import static android.view.Surface.ROTATION_0;
import static android.view.Surface.ROTATION_180;
import static android.view.Surface.ROTATION_270;
@@ -179,8 +183,25 @@
transformPhysicalToLogicalCoordinates(rotation, DISPLAY_WIDTH, DISPLAY_HEIGHT, m);
m.mapRect(rectF);
+ int pos = -1;
+ switch (rotation) {
+ case ROTATION_0:
+ pos = BOUNDS_POSITION_TOP;
+ break;
+ case ROTATION_90:
+ pos = BOUNDS_POSITION_LEFT;
+ break;
+ case ROTATION_180:
+ pos = BOUNDS_POSITION_BOTTOM;
+ break;
+ case ROTATION_270:
+ pos = BOUNDS_POSITION_RIGHT;
+ break;
+ }
+
+
return DisplayCutout.fromBoundingRect((int) rectF.left, (int) rectF.top,
- (int) rectF.right, (int) rectF.bottom);
+ (int) rectF.right, (int) rectF.bottom, pos);
}
static class TestContextWrapper extends ContextWrapper {
diff --git a/services/tests/servicestests/src/com/android/server/power/BatterySaverPolicyTest.java b/services/tests/servicestests/src/com/android/server/power/BatterySaverPolicyTest.java
index 54ac6fc..bd4a356 100644
--- a/services/tests/servicestests/src/com/android/server/power/BatterySaverPolicyTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/BatterySaverPolicyTest.java
@@ -84,9 +84,6 @@
}
@Mock
- Handler mHandler;
-
- @Mock
MetricsLogger mMetricsLogger = mock(MetricsLogger.class);
private BatterySaverPolicyForTest mBatterySaverPolicy;
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/BoundsAnimationControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java
index 12be0b3..e6e08bb 100644
--- a/services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java
@@ -90,7 +90,7 @@
private AppTransitionListener mListener;
MockAppTransition(Context context) {
- super(context, null);
+ super(context, sWm);
}
@Override
diff --git a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
index b330304..3dcdd23 100644
--- a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
@@ -24,6 +24,8 @@
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.DisplayCutout.BOUNDS_POSITION_LEFT;
+import static android.view.DisplayCutout.BOUNDS_POSITION_TOP;
import static android.view.DisplayCutout.fromBoundingRect;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
@@ -32,6 +34,7 @@
import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
+import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertEquals;
@@ -303,7 +306,8 @@
createTapEvent(dm0.widthPixels / 2, dm0.heightPixels / 2, false));
// Check focus is on primary display.
- assertEquals(sWm.mCurrentFocus, dc0.findFocusedWindow());
+ assertEquals(sWm.mRoot.getTopFocusedDisplayContent().mCurrentFocus,
+ dc0.findFocusedWindow());
// Tap on secondary display
DisplayMetrics dm1 = dc1.getDisplayMetrics();
@@ -313,7 +317,8 @@
createTapEvent(dm1.widthPixels / 2, dm1.heightPixels / 2, false));
// Check focus is on secondary.
- assertEquals(sWm.mCurrentFocus, dc1.findFocusedWindow());
+ assertEquals(sWm.mRoot.getTopFocusedDisplayContent().mCurrentFocus,
+ dc1.findFocusedWindow());
}
@Test
@@ -321,34 +326,29 @@
// Create a focusable window and check that focus is calculated correctly
final WindowState window1 =
createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "window1");
- assertEquals(window1, sWm.mRoot.computeFocusedWindow());
+ updateFocusedWindow();
+ assertTrue(window1.isFocused());
+ assertEquals(window1, sWm.mRoot.getTopFocusedDisplayContent().mCurrentFocus);
// Check that a new display doesn't affect focus
final DisplayContent dc = createNewDisplay();
- assertEquals(window1, sWm.mRoot.computeFocusedWindow());
+ updateFocusedWindow();
+ assertTrue(window1.isFocused());
+ assertEquals(window1, sWm.mRoot.getTopFocusedDisplayContent().mCurrentFocus);
// Add a window to the second display, and it should be focused
final WindowState window2 = createWindow(null, TYPE_BASE_APPLICATION, dc, "window2");
- assertEquals(window2, sWm.mRoot.computeFocusedWindow());
+ updateFocusedWindow();
+ assertTrue(window1.isFocused());
+ assertTrue(window2.isFocused());
+ assertEquals(window2, sWm.mRoot.getTopFocusedDisplayContent().mCurrentFocus);
// Move the first window to the to including parents, and make sure focus is updated
window1.getParent().positionChildAt(POSITION_TOP, window1, true);
- assertEquals(window1, sWm.mRoot.computeFocusedWindow());
- }
-
- @Test
- public void testKeyguard_preventsSecondaryDisplayFocus() throws Exception {
- final WindowState keyguard = createWindow(null, TYPE_STATUS_BAR,
- sWm.getDefaultDisplayContentLocked(), "keyguard");
- assertEquals(keyguard, sWm.mRoot.computeFocusedWindow());
-
- // Add a window to a second display, and it should be focused
- final DisplayContent dc = createNewDisplay();
- final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, dc, "win");
- assertEquals(win, sWm.mRoot.computeFocusedWindow());
-
- mWmRule.getWindowManagerPolicy().keyguardShowingAndNotOccluded = true;
- assertEquals(keyguard, sWm.mRoot.computeFocusedWindow());
+ updateFocusedWindow();
+ assertTrue(window1.isFocused());
+ assertTrue(window2.isFocused());
+ assertEquals(window1, sWm.mRoot.getTopFocusedDisplayContent().mCurrentFocus);
}
/**
@@ -454,7 +454,7 @@
dc.mInitialDisplayHeight = 400;
Rect r = new Rect(80, 0, 120, 10);
final DisplayCutout cutout = new WmDisplayCutout(
- fromBoundingRect(r.left, r.top, r.right, r.bottom), null)
+ fromBoundingRect(r.left, r.top, r.right, r.bottom, BOUNDS_POSITION_TOP), null)
.computeSafeInsets(200, 400).getDisplayCutout();
dc.mInitialDisplayCutout = cutout;
@@ -484,7 +484,7 @@
final Rect r1 = new Rect(left, top, right, bottom);
final DisplayCutout cutout = new WmDisplayCutout(
- fromBoundingRect(r1.left, r1.top, r1.right, r1.bottom), null)
+ fromBoundingRect(r1.left, r1.top, r1.right, r1.bottom, BOUNDS_POSITION_TOP), null)
.computeSafeInsets(displayWidth, displayHeight).getDisplayCutout();
dc.mInitialDisplayCutout = cutout;
@@ -501,7 +501,7 @@
// | | -------------
final Rect r = new Rect(top, left, bottom, right);
assertEquals(new WmDisplayCutout(
- fromBoundingRect(r.left, r.top, r.right, r.bottom), null)
+ fromBoundingRect(r.left, r.top, r.right, r.bottom, BOUNDS_POSITION_LEFT), null)
.computeSafeInsets(displayHeight, displayWidth)
.getDisplayCutout(), dc.getDisplayInfo().displayCutout);
}
@@ -590,6 +590,12 @@
assertEquals(displayContent.mBaseDisplayDensity, expectedBaseDensity);
}
+ private void updateFocusedWindow() {
+ synchronized (sWm.mWindowMap) {
+ sWm.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, false);
+ }
+ }
+
/**
* Create DisplayContent that does not update display base/initial values from device to keep
* the values set by test.
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/WindowFrameTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
index 0886729..7cd1314 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
@@ -16,12 +16,12 @@
package com.android.server.wm;
+import static android.view.DisplayCutout.BOUNDS_POSITION_TOP;
import static android.view.DisplayCutout.fromBoundingRect;
import static android.view.WindowManager.LayoutParams.FILL_PARENT;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
import android.app.ActivityManager.TaskDescription;
import android.content.res.Configuration;
@@ -474,7 +474,8 @@
final Rect pf = new Rect(0, 0, 1000, 2000);
// Create a display cutout of size 50x50, aligned top-center
final WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
- fromBoundingRect(500, 0, 550, 50), pf.width(), pf.height());
+ fromBoundingRect(500, 0, 550, 50, BOUNDS_POSITION_TOP),
+ pf.width(), pf.height());
final WindowFrames windowFrames = w.getWindowFrames();
windowFrames.setFrames(pf, pf, pf, pf, pf, pf, pf, pf);
@@ -499,7 +500,8 @@
final Rect pf = new Rect(0, -500, 1000, 1500);
// Create a display cutout of size 50x50, aligned top-center
final WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
- fromBoundingRect(500, 0, 550, 50), pf.width(), pf.height());
+ fromBoundingRect(500, 0, 550, 50, BOUNDS_POSITION_TOP),
+ pf.width(), pf.height());
final WindowFrames windowFrames = w.getWindowFrames();
windowFrames.setFrames(pf, pf, pf, pf, pf, pf, pf, pf);
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 b7cc9ce..3637baf 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
@@ -49,6 +49,7 @@
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
+import android.graphics.Insets;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
@@ -404,8 +405,12 @@
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)));
+ final DisplayCutout cutout = new DisplayCutout(
+ Insets.of(0, 15, 0, 22) /* safeInset */,
+ null /* boundLeft */,
+ new Rect(95, 0, 105, 15),
+ null /* boundRight */,
+ new Rect(95, 378, 105, 400));
wf.setDisplayCutout(new WmDisplayCutout(cutout, new Size(200, 400)));
app.computeFrameLw();
diff --git a/services/tests/servicestests/src/com/android/server/wm/utils/DisplayRotationUtilTest.java b/services/tests/servicestests/src/com/android/server/wm/utils/DisplayRotationUtilTest.java
new file mode 100644
index 0000000..ba8869b
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/wm/utils/DisplayRotationUtilTest.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.wm.utils;
+
+import static android.view.DisplayCutout.BOUNDS_POSITION_BOTTOM;
+import static android.view.DisplayCutout.BOUNDS_POSITION_LEFT;
+import static android.view.DisplayCutout.BOUNDS_POSITION_RIGHT;
+import static android.view.DisplayCutout.BOUNDS_POSITION_TOP;
+import static android.view.Surface.ROTATION_0;
+import static android.view.Surface.ROTATION_180;
+import static android.view.Surface.ROTATION_270;
+import static android.view.Surface.ROTATION_90;
+import static com.android.server.wm.utils.DisplayRotationUtil.getBoundIndexFromRotation;
+
+import static org.hamcrest.Matchers.equalTo;
+import static org.junit.Assert.assertThat;
+
+import android.graphics.Rect;
+import android.platform.test.annotations.Presubmit;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+
+
+/**
+ * Tests for {@link DisplayRotationUtil}
+ *
+ * Run with: atest DisplayRotationUtilTest
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@Presubmit
+public class DisplayRotationUtilTest {
+ private static Rect ZERO_RECT = new Rect();
+
+ @Test
+ public void testGetBoundIndexFromRotation_rot0() {
+ assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_LEFT, ROTATION_0),
+ equalTo(BOUNDS_POSITION_LEFT));
+ assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_TOP, ROTATION_0),
+ equalTo(BOUNDS_POSITION_TOP));
+ assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_RIGHT, ROTATION_0),
+ equalTo(BOUNDS_POSITION_RIGHT));
+ assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_BOTTOM, ROTATION_0),
+ equalTo(BOUNDS_POSITION_BOTTOM));
+ }
+
+ @Test
+ public void testGetBoundIndexFromRotation_rot90() {
+ assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_LEFT, ROTATION_90),
+ equalTo(BOUNDS_POSITION_BOTTOM));
+ assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_TOP, ROTATION_90),
+ equalTo(BOUNDS_POSITION_LEFT));
+ assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_RIGHT, ROTATION_90),
+ equalTo(BOUNDS_POSITION_TOP));
+ assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_BOTTOM, ROTATION_90),
+ equalTo(BOUNDS_POSITION_RIGHT));
+ }
+
+ @Test
+ public void testGetBoundIndexFromRotation_rot180() {
+ assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_LEFT, ROTATION_180),
+ equalTo(BOUNDS_POSITION_RIGHT));
+ assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_TOP, ROTATION_180),
+ equalTo(BOUNDS_POSITION_BOTTOM));
+ assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_RIGHT, ROTATION_180),
+ equalTo(BOUNDS_POSITION_LEFT));
+ assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_BOTTOM, ROTATION_180),
+ equalTo(BOUNDS_POSITION_TOP));
+ }
+
+ @Test
+ public void testGetBoundIndexFromRotation_rot270() {
+ assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_LEFT, ROTATION_270),
+ equalTo(BOUNDS_POSITION_TOP));
+ assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_TOP, ROTATION_270),
+ equalTo(BOUNDS_POSITION_RIGHT));
+ assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_RIGHT, ROTATION_270),
+ equalTo(BOUNDS_POSITION_BOTTOM));
+ assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_BOTTOM, ROTATION_270),
+ equalTo(BOUNDS_POSITION_LEFT));
+
+ }
+
+ @Test
+ public void testGetRotatedBounds_top_rot0() {
+ DisplayRotationUtil util = new DisplayRotationUtil();
+ Rect[] bounds = new Rect[] { ZERO_RECT, new Rect(50,0,150,10), ZERO_RECT, ZERO_RECT };
+ assertThat(util.getRotatedBounds(bounds, ROTATION_0, 200, 300),
+ equalTo(bounds));
+ }
+
+ @Test
+ public void testGetRotatedBounds_top_rot90() {
+ DisplayRotationUtil util = new DisplayRotationUtil();
+ Rect[] bounds = new Rect[] { ZERO_RECT, new Rect(50,0,150,10), ZERO_RECT, ZERO_RECT };
+ assertThat(util.getRotatedBounds(bounds, ROTATION_90, 200, 300),
+ equalTo(new Rect[] { new Rect(0, 50, 10, 150), ZERO_RECT, ZERO_RECT, ZERO_RECT }));
+ }
+
+ @Test
+ public void testGetRotatedBounds_top_rot180() {
+ DisplayRotationUtil util = new DisplayRotationUtil();
+ Rect[] bounds = new Rect[] { ZERO_RECT, new Rect(50,0,150,10), ZERO_RECT, ZERO_RECT };
+ assertThat(util.getRotatedBounds(bounds, ROTATION_180, 200, 300),
+ equalTo(new Rect[] { ZERO_RECT, ZERO_RECT, ZERO_RECT, new Rect(50, 290, 150, 300) }));
+ }
+
+ @Test
+ public void testGetRotatedBounds_top_rot270() {
+ DisplayRotationUtil util = new DisplayRotationUtil();
+ Rect[] bounds = new Rect[] { ZERO_RECT, new Rect(50,0,150,10), ZERO_RECT, ZERO_RECT };
+ assertThat(util.getRotatedBounds(bounds, ROTATION_270, 200, 300),
+ equalTo(new Rect[] { ZERO_RECT, ZERO_RECT, new Rect(290, 50, 300, 150), ZERO_RECT }));
+ }
+
+ @Test
+ public void testGetRotatedBounds_left_rot0() {
+ DisplayRotationUtil util = new DisplayRotationUtil();
+ Rect[] bounds = new Rect[] { new Rect(0, 50, 10, 150), ZERO_RECT, ZERO_RECT, ZERO_RECT };
+ assertThat(util.getRotatedBounds(bounds, ROTATION_0, 300, 200),
+ equalTo(bounds));
+ }
+
+ @Test
+ public void testGetRotatedBounds_left_rot90() {
+ DisplayRotationUtil util = new DisplayRotationUtil();
+ Rect[] bounds = new Rect[] { new Rect(0, 50, 10, 150), ZERO_RECT, ZERO_RECT, ZERO_RECT };
+ assertThat(util.getRotatedBounds(bounds, ROTATION_90, 300, 200),
+ equalTo(new Rect[]{ ZERO_RECT, ZERO_RECT, ZERO_RECT, new Rect(50, 290, 150, 300) }));
+ }
+
+ @Test
+ public void testGetRotatedBounds_left_rot180() {
+ DisplayRotationUtil util = new DisplayRotationUtil();
+ Rect[] bounds = new Rect[] { new Rect(0, 50, 10, 150), ZERO_RECT, ZERO_RECT, ZERO_RECT };
+ assertThat(util.getRotatedBounds(bounds, ROTATION_180, 300, 200),
+ equalTo(new Rect[]{ ZERO_RECT, ZERO_RECT, new Rect(290, 50, 300, 150), ZERO_RECT }));
+ }
+
+ @Test
+ public void testGetRotatedBounds_left_rot270() {
+ DisplayRotationUtil util = new DisplayRotationUtil();
+ Rect[] bounds = new Rect[] { new Rect(0, 50, 10, 150), ZERO_RECT, ZERO_RECT, ZERO_RECT };
+ assertThat(util.getRotatedBounds(bounds, ROTATION_270, 300, 200),
+ equalTo(new Rect[]{ ZERO_RECT, new Rect(50, 0, 150, 10), ZERO_RECT, ZERO_RECT }));
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/wm/utils/WmDisplayCutoutTest.java b/services/tests/servicestests/src/com/android/server/wm/utils/WmDisplayCutoutTest.java
index 9ce3dca..c5e35e7 100644
--- a/services/tests/servicestests/src/com/android/server/wm/utils/WmDisplayCutoutTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/utils/WmDisplayCutoutTest.java
@@ -18,11 +18,19 @@
import static android.view.DisplayCutout.NO_CUTOUT;
+import static android.view.DisplayCutout.BOUNDS_POSITION_BOTTOM;
+import static android.view.DisplayCutout.BOUNDS_POSITION_LEFT;
+import static android.view.DisplayCutout.BOUNDS_POSITION_RIGHT;
+import static android.view.DisplayCutout.BOUNDS_POSITION_TOP;
import static android.view.DisplayCutout.fromBoundingRect;
+import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertThat;
+
+import android.graphics.Insets;
import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
import android.util.Size;
@@ -45,15 +53,17 @@
@SmallTest
@Presubmit
public class WmDisplayCutoutTest {
+ private static final Rect ZERO_RECT = new Rect();
private final DisplayCutout mCutoutTop = new DisplayCutout(
- new Rect(0, 100, 0, 0),
- Arrays.asList(new Rect(50, 0, 75, 100)));
+ Insets.of(0, 100, 0, 0),
+ null /* boundLeft */, new Rect(50, 0, 75, 100) /* boundTop */,
+ null /* boundRight */, null /* boundBottom */);
@Test
public void calculateRelativeTo_top() {
WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
- fromBoundingRect(0, 0, 100, 20), 200, 400)
+ fromBoundingRect(0, 0, 100, 20, BOUNDS_POSITION_TOP), 200, 400)
.calculateRelativeTo(new Rect(5, 5, 95, 195));
assertEquals(new Rect(0, 15, 0, 0), cutout.getDisplayCutout().getSafeInsets());
@@ -62,7 +72,7 @@
@Test
public void calculateRelativeTo_left() {
WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
- fromBoundingRect(0, 0, 20, 100), 400, 200)
+ fromBoundingRect(0, 0, 20, 100, BOUNDS_POSITION_LEFT), 400, 200)
.calculateRelativeTo(new Rect(5, 5, 195, 95));
assertEquals(new Rect(15, 0, 0, 0), cutout.getDisplayCutout().getSafeInsets());
@@ -71,7 +81,7 @@
@Test
public void calculateRelativeTo_bottom() {
WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
- fromBoundingRect(0, 180, 100, 200), 100, 200)
+ fromBoundingRect(0, 180, 100, 200, BOUNDS_POSITION_BOTTOM), 100, 200)
.calculateRelativeTo(new Rect(5, 5, 95, 195));
assertEquals(new Rect(0, 0, 0, 15), cutout.getDisplayCutout().getSafeInsets());
@@ -80,7 +90,7 @@
@Test
public void calculateRelativeTo_right() {
WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
- fromBoundingRect(180, 0, 200, 100), 200, 100)
+ fromBoundingRect(180, 0, 200, 100, BOUNDS_POSITION_RIGHT), 200, 100)
.calculateRelativeTo(new Rect(5, 5, 195, 95));
assertEquals(new Rect(0, 0, 15, 0), cutout.getDisplayCutout().getSafeInsets());
@@ -89,16 +99,17 @@
@Test
public void calculateRelativeTo_bounds() {
WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
- fromBoundingRect(0, 0, 100, 20), 200, 400)
+ fromBoundingRect(0, 0, 100, 20, BOUNDS_POSITION_TOP), 200, 400)
.calculateRelativeTo(new Rect(5, 10, 95, 180));
- assertEquals(new Rect(-5, -10, 95, 10), cutout.getDisplayCutout().getBounds().getBounds());
+ assertThat(cutout.getDisplayCutout().getBoundingRectTop(),
+ equalTo(new Rect(-5, -10, 95, 10)));
}
@Test
public void computeSafeInsets_top() {
WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
- fromBoundingRect(0, 0, 100, 20), 200, 400);
+ fromBoundingRect(0, 0, 100, 20, BOUNDS_POSITION_TOP), 200, 400);
assertEquals(new Rect(0, 20, 0, 0), cutout.getDisplayCutout().getSafeInsets());
}
@@ -106,7 +117,7 @@
@Test
public void computeSafeInsets_left() {
WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
- fromBoundingRect(0, 0, 20, 100), 400, 200);
+ fromBoundingRect(0, 0, 20, 100, BOUNDS_POSITION_LEFT), 400, 200);
assertEquals(new Rect(20, 0, 0, 0), cutout.getDisplayCutout().getSafeInsets());
}
@@ -114,7 +125,7 @@
@Test
public void computeSafeInsets_bottom() {
WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
- fromBoundingRect(0, 180, 100, 200), 100, 200);
+ fromBoundingRect(0, 180, 100, 200, BOUNDS_POSITION_BOTTOM), 100, 200);
assertEquals(new Rect(0, 0, 0, 20), cutout.getDisplayCutout().getSafeInsets());
}
@@ -122,7 +133,7 @@
@Test
public void computeSafeInsets_right() {
WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
- fromBoundingRect(180, 0, 200, 100), 200, 100);
+ fromBoundingRect(180, 0, 200, 100, BOUNDS_POSITION_RIGHT), 200, 100);
assertEquals(new Rect(0, 0, 20, 0), cutout.getDisplayCutout().getSafeInsets());
}
@@ -132,8 +143,7 @@
DisplayCutout cutout = WmDisplayCutout.computeSafeInsets(mCutoutTop, 1000,
2000).getDisplayCutout();
- assertEquals(mCutoutTop.getBounds().getBounds(),
- cutout.getBounds().getBounds());
+ assertEquals(mCutoutTop.getBoundingRects(), cutout.getBoundingRects());
}
@Test
diff --git a/services/tests/uiservicestests/Android.mk b/services/tests/uiservicestests/Android.mk
index 8405179..f3f4355 100644
--- a/services/tests/uiservicestests/Android.mk
+++ b/services/tests/uiservicestests/Android.mk
@@ -45,6 +45,7 @@
libbacktrace \
libbase \
libbinder \
+ libbinderthreadstate \
libc++ \
libcutils \
liblog \
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 45d2fa2..4e007c2d 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -16,6 +16,8 @@
package com.android.server.notification;
+import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
+import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE;
import static android.app.Notification.FLAG_FOREGROUND_SERVICE;
import static android.app.NotificationManager.EXTRA_BLOCKED_STATE;
import static android.app.NotificationManager.IMPORTANCE_HIGH;
@@ -74,6 +76,7 @@
import android.app.NotificationChannel;
import android.app.NotificationChannelGroup;
import android.app.NotificationManager;
+import android.app.ITransientNotification;
import android.app.IUriGrantsManager;
import android.app.admin.DevicePolicyManagerInternal;
import android.app.usage.UsageStatsManagerInternal;
@@ -118,12 +121,14 @@
import com.android.internal.R;
import com.android.internal.statusbar.NotificationVisibility;
import com.android.server.LocalServices;
+import com.android.server.SystemService;
import com.android.server.UiServiceTestCase;
import com.android.server.lights.Light;
import com.android.server.lights.LightsManager;
import com.android.server.notification.NotificationManagerService.NotificationAssistants;
import com.android.server.notification.NotificationManagerService.NotificationListeners;
import com.android.server.uri.UriGrantsManagerInternal;
+import com.android.server.wm.WindowManagerInternal;
import org.junit.After;
import org.junit.Before;
@@ -160,6 +165,8 @@
private IPackageManager mPackageManager;
@Mock
private PackageManager mPackageManagerClient;
+ @Mock
+ private WindowManagerInternal mWindowManagerInternal;
private TestableContext mContext = spy(getContext());
private final String PKG = mContext.getPackageName();
private TestableLooper mTestableLooper;
@@ -238,6 +245,16 @@
}
}
+ private class TestableToastCallback extends ITransientNotification.Stub {
+ @Override
+ public void show(IBinder windowToken) {
+ }
+
+ @Override
+ public void hide() {
+ }
+ }
+
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
@@ -249,6 +266,8 @@
LocalServices.removeServiceForTest(UriGrantsManagerInternal.class);
LocalServices.addService(UriGrantsManagerInternal.class, mUgmInternal);
+ LocalServices.removeServiceForTest(WindowManagerInternal.class);
+ LocalServices.addService(WindowManagerInternal.class, mWindowManagerInternal);
mService = new TestableNotificationManagerService(mContext);
@@ -302,6 +321,7 @@
mGroupHelper, mAm, mAppUsageStats,
mock(DevicePolicyManagerInternal.class), mUgm, mUgmInternal,
mAppOpsManager);
+ mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
} catch (SecurityException e) {
if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) {
throw e;
@@ -2192,6 +2212,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,
@@ -3428,17 +3468,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
@@ -3531,4 +3568,93 @@
assertEquals(0, captor.getValue().getNotification().flags);
}
+
+ @Test
+ public void testAllowForegroundToasts() throws Exception {
+ final String testPackage = "testPackageName";
+ assertEquals(0, mService.mToastQueue.size());
+ mService.isSystemUid = false;
+
+ // package is not suspended
+ when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid)))
+ .thenReturn(false);
+
+ // notifications from this package are blocked by the user
+ mService.setPreferencesHelper(mPreferencesHelper);
+ when(mPreferencesHelper.getImportance(testPackage, mUid)).thenReturn(IMPORTANCE_NONE);
+
+ // this app is in the foreground
+ when(mActivityManager.getUidImportance(mUid)).thenReturn(IMPORTANCE_FOREGROUND);
+
+ // enqueue toast -> toast should still enqueue
+ ((INotificationManager)mService.mService).enqueueToast(testPackage,
+ new TestableToastCallback(), 2000, 0);
+ assertEquals(1, mService.mToastQueue.size());
+ }
+
+ @Test
+ public void testDisallowToastsFromSuspendedPackages() throws Exception {
+ final String testPackage = "testPackageName";
+ assertEquals(0, mService.mToastQueue.size());
+ mService.isSystemUid = false;
+
+ // package is suspended
+ when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid)))
+ .thenReturn(true);
+
+ // notifications from this package are NOT blocked by the user
+ mService.setPreferencesHelper(mPreferencesHelper);
+ when(mPreferencesHelper.getImportance(testPackage, mUid)).thenReturn(IMPORTANCE_LOW);
+
+ // enqueue toast -> no toasts enqueued
+ ((INotificationManager)mService.mService).enqueueToast(testPackage,
+ new TestableToastCallback(), 2000, 0);
+ assertEquals(0, mService.mToastQueue.size());
+ }
+
+ @Test
+ public void testDisallowToastsFromBlockedApps() throws Exception {
+ final String testPackage = "testPackageName";
+ assertEquals(0, mService.mToastQueue.size());
+ mService.isSystemUid = false;
+
+ // package is not suspended
+ when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid)))
+ .thenReturn(false);
+
+ // notifications from this package are blocked by the user
+ mService.setPreferencesHelper(mPreferencesHelper);
+ when(mPreferencesHelper.getImportance(testPackage, mUid)).thenReturn(IMPORTANCE_NONE);
+
+ // this app is NOT in the foreground
+ when(mActivityManager.getUidImportance(mUid)).thenReturn(IMPORTANCE_GONE);
+
+ // enqueue toast -> no toasts enqueued
+ ((INotificationManager)mService.mService).enqueueToast(testPackage,
+ new TestableToastCallback(), 2000, 0);
+ assertEquals(0, mService.mToastQueue.size());
+ }
+
+ @Test
+ public void testAlwaysAllowSystemToasts() throws Exception {
+ final String testPackage = "testPackageName";
+ assertEquals(0, mService.mToastQueue.size());
+ mService.isSystemUid = true;
+
+ // package is suspended
+ when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid)))
+ .thenReturn(true);
+
+ // notifications from this package ARE blocked by the user
+ mService.setPreferencesHelper(mPreferencesHelper);
+ when(mPreferencesHelper.getImportance(testPackage, mUid)).thenReturn(IMPORTANCE_NONE);
+
+ // this app is NOT in the foreground
+ when(mActivityManager.getUidImportance(mUid)).thenReturn(IMPORTANCE_GONE);
+
+ // enqueue toast -> system toast can still be enqueued
+ ((INotificationManager)mService.mService).enqueueToast(testPackage,
+ new TestableToastCallback(), 2000, 0);
+ assertEquals(1, mService.mToastQueue.size());
+ }
}
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 702161e..13f3e5e 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -1030,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;
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..8946d25 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 = 3;
// 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,225 @@
}
}
+
+ 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;
+ }
+ // If old version, don't bother sanity checking
+ if (version < 4) return;
+
+ // 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 +1005,7 @@
mSortedStatFiles[intervalType].put(stats.beginTime, f);
}
- UsageStatsXml.write(f, stats);
+ writeLocked(f, stats);
stats.lastTimeSaved = f.getLastModifiedTime();
}
}
@@ -730,7 +1139,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 +1165,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 +1178,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/startop/iorap/Android.bp b/startop/iorap/Android.bp
new file mode 100644
index 0000000..b3b0900
--- /dev/null
+++ b/startop/iorap/Android.bp
@@ -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.
+
+java_library_static {
+ name: "libiorap-java",
+
+ aidl: {
+ include_dirs: [
+ "system/iorap/binder",
+ ],
+ },
+
+ srcs: [
+ ":iorap-aidl",
+ "**/*.java",
+ ],
+}
diff --git a/startop/iorap/src/com/google/android/startop/iorap/ActivityHintEvent.java b/startop/iorap/src/com/google/android/startop/iorap/ActivityHintEvent.java
new file mode 100644
index 0000000..1d38f4c
--- /dev/null
+++ b/startop/iorap/src/com/google/android/startop/iorap/ActivityHintEvent.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.startop.iorap;
+
+import android.os.Parcelable;
+import android.os.Parcel;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * Provide a hint to iorapd that an activity has transitioned state.<br /><br />
+ *
+ * Knowledge of when an activity starts/stops can be used by iorapd to increase system
+ * performance (e.g. by launching perfetto tracing to record an io profile, or by
+ * playing back an ioprofile via readahead) over the long run.<br /><br />
+ *
+ * /@see com.google.android.startop.iorap.IIorap#onActivityHintEvent<br /><br />
+ *
+ * Once an activity hint is in {@link #TYPE_STARTED} it must transition to another type.
+ * All other states could be terminal, see below: <br /><br />
+ *
+ * <pre>
+ *
+ * ┌──────────────────────────────────────┐
+ * │ ▼
+ * ┌─────────┐ ╔════════════════╗ ╔═══════════╗
+ * ──▶ │ STARTED │ ──▶ ║ COMPLETED ║ ──▶ ║ CANCELLED ║
+ * └─────────┘ ╚════════════════╝ ╚═══════════╝
+ * │
+ * │
+ * ▼
+ * ╔════════════════╗
+ * ║ POST_COMPLETED ║
+ * ╚════════════════╝
+ *
+ * </pre> <!-- system/iorap/docs/binder/ActivityHint.dot -->
+ *
+ * @hide
+ */
+public class ActivityHintEvent implements Parcelable {
+
+ public static final int TYPE_STARTED = 0;
+ public static final int TYPE_CANCELLED = 1;
+ public static final int TYPE_COMPLETED = 2;
+ public static final int TYPE_POST_COMPLETED = 3;
+ private static final int TYPE_MAX = TYPE_POST_COMPLETED;
+
+ /** @hide */
+ @IntDef(flag = true, prefix = { "TYPE_" }, value = {
+ TYPE_STARTED,
+ TYPE_CANCELLED,
+ TYPE_COMPLETED,
+ TYPE_POST_COMPLETED,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Type {}
+
+ @Type public final int type;
+ public final ActivityInfo activityInfo;
+
+ public ActivityHintEvent(@Type int type, ActivityInfo activityInfo) {
+ this.type = type;
+ this.activityInfo = activityInfo;
+ checkConstructorArguments();
+ }
+
+ private void checkConstructorArguments() {
+ CheckHelpers.checkTypeInRange(type, TYPE_MAX);
+ Objects.requireNonNull(activityInfo, "activityInfo");
+ }
+
+ @Override
+ public String toString() {
+ return String.format("{type: %d, activityInfo: %s}", type, activityInfo);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ } else if (other instanceof ActivityHintEvent) {
+ return equals((ActivityHintEvent) other);
+ }
+ return false;
+ }
+
+ private boolean equals(ActivityHintEvent other) {
+ return type == other.type &&
+ Objects.equals(activityInfo, other.activityInfo);
+ }
+
+ //<editor-fold desc="Binder boilerplate">
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(type);
+ activityInfo.writeToParcel(out, flags);
+ }
+
+ private ActivityHintEvent(Parcel in) {
+ this.type = in.readInt();
+ this.activityInfo = ActivityInfo.CREATOR.createFromParcel(in);
+ checkConstructorArguments();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final Parcelable.Creator<ActivityHintEvent> CREATOR
+ = new Parcelable.Creator<ActivityHintEvent>() {
+ public ActivityHintEvent createFromParcel(Parcel in) {
+ return new ActivityHintEvent(in);
+ }
+
+ public ActivityHintEvent[] newArray(int size) {
+ return new ActivityHintEvent[size];
+ }
+ };
+ //</editor-fold>
+}
diff --git a/startop/iorap/src/com/google/android/startop/iorap/ActivityInfo.java b/startop/iorap/src/com/google/android/startop/iorap/ActivityInfo.java
new file mode 100644
index 0000000..f47a42c
--- /dev/null
+++ b/startop/iorap/src/com/google/android/startop/iorap/ActivityInfo.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.startop.iorap;
+
+import java.util.Objects;
+
+import android.os.Parcelable;
+import android.os.Parcel;
+
+/**
+ * Provide minimal information for launched activities to iorap.<br /><br />
+ *
+ * This uniquely identifies a system-wide activity by providing the {@link #packageName} and
+ * {@link #activityName}.
+ *
+ * @see ActivityHintEvent
+ * @see AppIntentEvent
+ *
+ * @hide
+ */
+public class ActivityInfo implements Parcelable {
+
+ /** The name of the package, for example {@code com.android.calculator}. */
+ public final String packageName;
+ /** The name of the activity, for example {@code .activities.activity.MainActivity} */
+ public final String activityName;
+
+ public ActivityInfo(String packageName, String activityName) {
+ this.packageName = packageName;
+ this.activityName = activityName;
+
+ checkConstructorArguments();
+ }
+
+ private void checkConstructorArguments() {
+ Objects.requireNonNull(packageName, "packageName");
+ Objects.requireNonNull(activityName, "activityName");
+ }
+
+ @Override
+ public String toString() {
+ return String.format("{packageName: %s, activityName: %s}", packageName, activityName);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ } else if (other instanceof ActivityInfo) {
+ return equals((ActivityInfo) other);
+ }
+ return false;
+ }
+
+ private boolean equals(ActivityInfo other) {
+ return Objects.equals(packageName, other.packageName) &&
+ Objects.equals(activityName, other.activityName);
+ }
+
+ //<editor-fold desc="Binder boilerplate">
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeString(packageName);
+ out.writeString(activityName);
+ }
+
+ private ActivityInfo(Parcel in) {
+ packageName = in.readString();
+ activityName = in.readString();
+
+ checkConstructorArguments();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final Parcelable.Creator<ActivityInfo> CREATOR
+ = new Parcelable.Creator<ActivityInfo>() {
+ public ActivityInfo createFromParcel(Parcel in) {
+ return new ActivityInfo(in);
+ }
+
+ public ActivityInfo[] newArray(int size) {
+ return new ActivityInfo[size];
+ }
+ };
+ //</editor-fold>
+}
diff --git a/startop/iorap/src/com/google/android/startop/iorap/AppIntentEvent.java b/startop/iorap/src/com/google/android/startop/iorap/AppIntentEvent.java
new file mode 100644
index 0000000..1cd37b5
--- /dev/null
+++ b/startop/iorap/src/com/google/android/startop/iorap/AppIntentEvent.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.startop.iorap;
+
+import android.os.Parcelable;
+import android.os.Parcel;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * Notifications for iorapd specifying when a system-wide intent defaults change.<br /><br />
+ *
+ * Intent defaults provide a mechanism for an app to register itself as an automatic handler.
+ * For example the camera app might be registered as the default handler for
+ * {@link android.provider.MediaStore#INTENT_ACTION_STILL_IMAGE_CAMERA} intent. Subsequently,
+ * if an arbitrary other app requests for a still image camera photo to be taken, the system
+ * will launch the respective default camera app to be launched to handle that request.<br /><br />
+ *
+ * In some cases iorapd might need to know default intents, e.g. for boot-time pinning of
+ * applications that resolve from the default intent. If the application would now be resolved
+ * differently, iorapd would unpin the old application and pin the new application.<br /><br />
+ *
+ * @hide
+ */
+public class AppIntentEvent implements Parcelable {
+
+ /** @see android.content.Intent#CATEGORY_DEFAULT */
+ public static final int TYPE_DEFAULT_INTENT_CHANGED = 0;
+ private static final int TYPE_MAX = 0;
+
+ /** @hide */
+ @IntDef(flag = true, prefix = { "TYPE_" }, value = {
+ TYPE_DEFAULT_INTENT_CHANGED,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Type {}
+
+ @Type public final int type;
+
+ public final ActivityInfo oldActivityInfo;
+ public final ActivityInfo newActivityInfo;
+
+ // TODO: Probably need the corresponding action here as well.
+
+ public static AppIntentEvent createDefaultIntentChanged(ActivityInfo oldActivityInfo,
+ ActivityInfo newActivityInfo) {
+ return new AppIntentEvent(TYPE_DEFAULT_INTENT_CHANGED, oldActivityInfo,
+ newActivityInfo);
+ }
+
+ private AppIntentEvent(@Type int type, ActivityInfo oldActivityInfo,
+ ActivityInfo newActivityInfo) {
+ this.type = type;
+ this.oldActivityInfo = oldActivityInfo;
+ this.newActivityInfo = newActivityInfo;
+
+ checkConstructorArguments();
+ }
+
+ private void checkConstructorArguments() {
+ CheckHelpers.checkTypeInRange(type, TYPE_MAX);
+ Objects.requireNonNull(oldActivityInfo, "oldActivityInfo");
+ Objects.requireNonNull(oldActivityInfo, "newActivityInfo");
+ }
+
+ @Override
+ public String toString() {
+ return String.format("{oldActivityInfo: %s, newActivityInfo: %s}", oldActivityInfo,
+ newActivityInfo);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ } else if (other instanceof AppIntentEvent) {
+ return equals((AppIntentEvent) other);
+ }
+ return false;
+ }
+
+ private boolean equals(AppIntentEvent other) {
+ return type == other.type &&
+ Objects.equals(oldActivityInfo, other.oldActivityInfo) &&
+ Objects.equals(newActivityInfo, other.newActivityInfo);
+ }
+
+ //<editor-fold desc="Binder boilerplate">
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(type);
+ oldActivityInfo.writeToParcel(out, flags);
+ newActivityInfo.writeToParcel(out, flags);
+ }
+
+ private AppIntentEvent(Parcel in) {
+ this.type = in.readInt();
+ this.oldActivityInfo = ActivityInfo.CREATOR.createFromParcel(in);
+ this.newActivityInfo = ActivityInfo.CREATOR.createFromParcel(in);
+
+ checkConstructorArguments();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final Parcelable.Creator<AppIntentEvent> CREATOR
+ = new Parcelable.Creator<AppIntentEvent>() {
+ public AppIntentEvent createFromParcel(Parcel in) {
+ return new AppIntentEvent(in);
+ }
+
+ public AppIntentEvent[] newArray(int size) {
+ return new AppIntentEvent[size];
+ }
+ };
+ //</editor-fold>
+}
diff --git a/startop/iorap/src/com/google/android/startop/iorap/CheckHelpers.java b/startop/iorap/src/com/google/android/startop/iorap/CheckHelpers.java
new file mode 100644
index 0000000..34aedd7
--- /dev/null
+++ b/startop/iorap/src/com/google/android/startop/iorap/CheckHelpers.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.google.android.startop.iorap;
+
+/**
+ * Convenience short-hand to throw {@link IllegalAccessException} when the arguments
+ * are out-of-range.
+ */
+public class CheckHelpers {
+ /** @throws IllegalAccessException if {@param type} is not in {@code [0..maxValue]} */
+ public static void checkTypeInRange(int type, int maxValue) {
+ if (type < 0) {
+ throw new IllegalArgumentException(
+ String.format("type must be non-negative (value=%d)", type));
+ }
+ if (type > maxValue) {
+ throw new IllegalArgumentException(
+ String.format("type out of range (value=%d, max=%d)", type, maxValue));
+ }
+ }
+
+ /** @throws IllegalAccessException if {@param state} is not in {@code [0..maxValue]} */
+ public static void checkStateInRange(int state, int maxValue) {
+ if (state < 0) {
+ throw new IllegalArgumentException(
+ String.format("state must be non-negative (value=%d)", state));
+ }
+ if (state > maxValue) {
+ throw new IllegalArgumentException(
+ String.format("state out of range (value=%d, max=%d)", state, maxValue));
+ }
+ }
+}
diff --git a/startop/iorap/src/com/google/android/startop/iorap/PackageEvent.java b/startop/iorap/src/com/google/android/startop/iorap/PackageEvent.java
new file mode 100644
index 0000000..aa4eea7
--- /dev/null
+++ b/startop/iorap/src/com/google/android/startop/iorap/PackageEvent.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.startop.iorap;
+
+import android.annotation.NonNull;
+import android.os.Parcelable;
+import android.os.Parcel;
+import android.net.Uri;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * Forward package manager events to iorapd. <br /><br />
+ *
+ * Knowing when packages are modified by the system are a useful tidbit to help with performance:
+ * for example when a package is replaced, it could be a hint used to invalidate any collected
+ * io profiles used for prefetching or pinning.
+ *
+ * @hide
+ */
+public class PackageEvent implements Parcelable {
+
+ /** @see android.content.Intent#ACTION_PACKAGE_REPLACED */
+ public static final int TYPE_REPLACED = 0;
+ private static final int TYPE_MAX = 0;
+
+ /** @hide */
+ @IntDef(flag = true, prefix = { "TYPE_" }, value = {
+ TYPE_REPLACED,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Type {}
+
+ @Type public final int type;
+
+ /** The path that a package is installed in, for example {@code /data/app/.../base.apk}. */
+ public final Uri packageUri;
+ /** The name of the package, for example {@code com.android.calculator}. */
+ public final String packageName;
+
+ @NonNull
+ public static PackageEvent createReplaced(Uri packageUri, String packageName) {
+ return new PackageEvent(TYPE_REPLACED, packageUri, packageName);
+ }
+
+ private PackageEvent(@Type int type, Uri packageUri, String packageName) {
+ this.type = type;
+ this.packageUri = packageUri;
+ this.packageName = packageName;
+
+ checkConstructorArguments();
+ }
+
+ private void checkConstructorArguments() {
+ CheckHelpers.checkTypeInRange(type, TYPE_MAX);
+ Objects.requireNonNull(packageUri, "packageUri");
+ Objects.requireNonNull(packageName, "packageName");
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ } else if (other instanceof PackageEvent) {
+ return equals((PackageEvent) other);
+ }
+ return false;
+ }
+
+ private boolean equals(PackageEvent other) {
+ return type == other.type &&
+ Objects.equals(packageUri, other.packageUri) &&
+ Objects.equals(packageName, other.packageName);
+ }
+
+ @Override
+ public String toString() {
+ return String.format("{packageUri: %s, packageName: %s}", packageUri, packageName);
+ }
+
+ //<editor-fold desc="Binder boilerplate">
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(type);
+ packageUri.writeToParcel(out, flags);
+ out.writeString(packageName);
+ }
+
+ private PackageEvent(Parcel in) {
+ this.type = in.readInt();
+ this.packageUri = Uri.CREATOR.createFromParcel(in);
+ this.packageName = in.readString();
+
+ checkConstructorArguments();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final Parcelable.Creator<PackageEvent> CREATOR
+ = new Parcelable.Creator<PackageEvent>() {
+ public PackageEvent createFromParcel(Parcel in) {
+ return new PackageEvent(in);
+ }
+
+ public PackageEvent[] newArray(int size) {
+ return new PackageEvent[size];
+ }
+ };
+ //</editor-fold>
+}
diff --git a/startop/iorap/src/com/google/android/startop/iorap/RequestId.java b/startop/iorap/src/com/google/android/startop/iorap/RequestId.java
new file mode 100644
index 0000000..2c79319
--- /dev/null
+++ b/startop/iorap/src/com/google/android/startop/iorap/RequestId.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.google.android.startop.iorap;
+
+import android.os.Parcelable;
+import android.os.Parcel;
+
+import android.annotation.NonNull;
+
+/**
+ * Uniquely identify an {@link com.google.android.startop.iorap.IIorap} method invocation,
+ * used for asynchronous callbacks by the server. <br /><br />
+ *
+ * As all system server binder calls must be {@code oneway}, this means all invocations
+ * into {@link com.google.android.startop.iorap.IIorap} are non-blocking. The request ID
+ * exists to associate all calls with their respective callbacks in
+ * {@link com.google.android.startop.iorap.ITaskListener}.
+ *
+ * @see com.google.android.startop.iorap.IIorap
+ *
+ * @hide
+ */
+public class RequestId implements Parcelable {
+
+ public final long requestId;
+
+ private static Object mLock = new Object();
+ private static long mNextRequestId = 0;
+
+ /**
+ * Create a monotonically increasing request ID.<br /><br />
+ *
+ * It is invalid to re-use the same request ID for multiple method calls on
+ * {@link com.google.android.startop.iorap.IIorap}; a new request ID must be created
+ * each time.
+ */
+ @NonNull public static RequestId nextValueForSequence() {
+ long currentRequestId;
+ synchronized (mLock) {
+ currentRequestId = mNextRequestId;
+ ++mNextRequestId;
+ }
+ return new RequestId(currentRequestId);
+ }
+
+ private RequestId(long requestId) {
+ this.requestId = requestId;
+
+ checkConstructorArguments();
+ }
+
+ private void checkConstructorArguments() {
+ if (requestId < 0) {
+ throw new IllegalArgumentException("request id must be non-negative");
+ }
+ }
+
+ @Override
+ public String toString() {
+ return String.format("{requestId: %ld}", requestId);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ } else if (other instanceof RequestId) {
+ return equals((RequestId) other);
+ }
+ return false;
+ }
+
+ private boolean equals(RequestId other) {
+ return requestId == other.requestId;
+ }
+
+
+ //<editor-fold desc="Binder boilerplate">
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeLong(requestId);
+ }
+
+ private RequestId(Parcel in) {
+ requestId = in.readLong();
+
+ checkConstructorArguments();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final Parcelable.Creator<RequestId> CREATOR
+ = new Parcelable.Creator<RequestId>() {
+ public RequestId createFromParcel(Parcel in) {
+ return new RequestId(in);
+ }
+
+ public RequestId[] newArray(int size) {
+ return new RequestId[size];
+ }
+ };
+ //</editor-fold>
+}
diff --git a/startop/iorap/src/com/google/android/startop/iorap/SystemServiceEvent.java b/startop/iorap/src/com/google/android/startop/iorap/SystemServiceEvent.java
new file mode 100644
index 0000000..75d47f9
--- /dev/null
+++ b/startop/iorap/src/com/google/android/startop/iorap/SystemServiceEvent.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.startop.iorap;
+
+import android.os.Parcelable;
+import android.os.Parcel;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Forward system service events to iorapd.
+ *
+ * @see com.android.server.SystemService
+ *
+ * @hide
+ */
+public class SystemServiceEvent implements Parcelable {
+
+ /** @see com.android.server.SystemService#onBootPhase */
+ public static final int TYPE_BOOT_PHASE = 0;
+ /** @see com.android.server.SystemService#onStart */
+ public static final int TYPE_START = 1;
+ private static final int TYPE_MAX = TYPE_START;
+
+ /** @hide */
+ @IntDef(flag = true, prefix = { "TYPE_" }, value = {
+ TYPE_BOOT_PHASE,
+ TYPE_START,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Type {}
+
+ @Type public final int type;
+
+ // TODO: do we want to pass the exact build phase enum?
+
+ public SystemServiceEvent(@Type int type) {
+ this.type = type;
+ checkConstructorArguments();
+ }
+
+ private void checkConstructorArguments() {
+ CheckHelpers.checkTypeInRange(type, TYPE_MAX);
+ }
+
+ @Override
+ public String toString() {
+ return String.format("{type: %d}", type);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ } else if (other instanceof SystemServiceEvent) {
+ return equals((SystemServiceEvent) other);
+ }
+ return false;
+ }
+
+ private boolean equals(SystemServiceEvent other) {
+ return type == other.type;
+ }
+
+ //<editor-fold desc="Binder boilerplate">
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(type);
+ }
+
+ private SystemServiceEvent(Parcel in) {
+ this.type = in.readInt();
+ checkConstructorArguments();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final Parcelable.Creator<SystemServiceEvent> CREATOR
+ = new Parcelable.Creator<SystemServiceEvent>() {
+ public SystemServiceEvent createFromParcel(Parcel in) {
+ return new SystemServiceEvent(in);
+ }
+
+ public SystemServiceEvent[] newArray(int size) {
+ return new SystemServiceEvent[size];
+ }
+ };
+ //</editor-fold>
+}
diff --git a/startop/iorap/src/com/google/android/startop/iorap/SystemServiceUserEvent.java b/startop/iorap/src/com/google/android/startop/iorap/SystemServiceUserEvent.java
new file mode 100644
index 0000000..b77c03c
--- /dev/null
+++ b/startop/iorap/src/com/google/android/startop/iorap/SystemServiceUserEvent.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.startop.iorap;
+
+import android.os.Parcelable;
+import android.os.Parcel;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Forward user events to iorapd.<br /><br />
+ *
+ * Knowledge of the logged-in user is reserved to be used to set-up appropriate policies
+ * by iorapd (e.g. to handle user default pinned applications changing).
+ *
+ * @see com.android.server.SystemService
+ *
+ * @hide
+ */
+public class SystemServiceUserEvent implements Parcelable {
+
+ /** @see com.android.server.SystemService#onStartUser */
+ public static final int TYPE_START_USER = 0;
+ /** @see com.android.server.SystemService#onUnlockUser */
+ public static final int TYPE_UNLOCK_USER = 1;
+ /** @see com.android.server.SystemService#onSwitchUser*/
+ public static final int TYPE_SWITCH_USER = 2;
+ /** @see com.android.server.SystemService#onStopUser */
+ public static final int TYPE_STOP_USER = 3;
+ /** @see com.android.server.SystemService#onCleanupUser */
+ public static final int TYPE_CLEANUP_USER = 4;
+ private static final int TYPE_MAX = TYPE_CLEANUP_USER;
+
+ /** @hide */
+ @IntDef(flag = true, prefix = { "TYPE_" }, value = {
+ TYPE_START_USER,
+ TYPE_UNLOCK_USER,
+ TYPE_SWITCH_USER,
+ TYPE_STOP_USER,
+ TYPE_CLEANUP_USER,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Type {}
+
+ @Type public final int type;
+ public final int userHandle;
+
+ public SystemServiceUserEvent(@Type int type, int userHandle) {
+ this.type = type;
+ this.userHandle = userHandle;
+ checkConstructorArguments();
+ }
+
+ private void checkConstructorArguments() {
+ CheckHelpers.checkTypeInRange(type, TYPE_MAX);
+ if (userHandle < 0) {
+ throw new IllegalArgumentException("userHandle must be non-negative");
+ }
+ }
+
+ @Override
+ public String toString() {
+ return String.format("{type: %d, userHandle: %d}", type, userHandle);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ } else if (other instanceof SystemServiceUserEvent) {
+ return equals((SystemServiceUserEvent) other);
+ }
+ return false;
+ }
+
+ private boolean equals(SystemServiceUserEvent other) {
+ return type == other.type &&
+ userHandle == other.userHandle;
+ }
+
+ //<editor-fold desc="Binder boilerplate">
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(type);
+ out.writeInt(userHandle);
+ }
+
+ private SystemServiceUserEvent(Parcel in) {
+ this.type = in.readInt();
+ this.userHandle = in.readInt();
+ checkConstructorArguments();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final Parcelable.Creator<SystemServiceUserEvent> CREATOR
+ = new Parcelable.Creator<SystemServiceUserEvent>() {
+ public SystemServiceUserEvent createFromParcel(Parcel in) {
+ return new SystemServiceUserEvent(in);
+ }
+
+ public SystemServiceUserEvent[] newArray(int size) {
+ return new SystemServiceUserEvent[size];
+ }
+ };
+ //</editor-fold>
+}
diff --git a/startop/iorap/src/com/google/android/startop/iorap/TaskResult.java b/startop/iorap/src/com/google/android/startop/iorap/TaskResult.java
new file mode 100644
index 0000000..b5fd6d8
--- /dev/null
+++ b/startop/iorap/src/com/google/android/startop/iorap/TaskResult.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.startop.iorap;
+
+import android.os.Parcelable;
+import android.os.Parcel;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Result data accompanying a request for {@link com.google.android.startop.iorap.ITaskListener}
+ * callbacks.<br /><br />
+ *
+ * Following {@link com.google.android.startop.iorap.IIorap} method invocation,
+ * iorapd will issue in-order callbacks for that corresponding {@link RequestId}.<br /><br />
+ *
+ * State transitions are as follows: <br /><br />
+ *
+ * <pre>
+ * ┌─────────────────────────────┐
+ * │ ▼
+ * ┌───────┐ ┌─────────┐ ╔═══════════╗
+ * ──▶ │ BEGAN │ ──▶ │ ONGOING │ ──▶ ║ COMPLETED ║
+ * └───────┘ └─────────┘ ╚═══════════╝
+ * │ │
+ * │ │
+ * ▼ │
+ * ╔═══════╗ │
+ * ──▶ ║ ERROR ║ ◀─────┘
+ * ╚═══════╝
+ *
+ * </pre> <!-- system/iorap/docs/binder/TaskResult.dot -->
+ *
+ * @hide
+ */
+public class TaskResult implements Parcelable {
+
+ public static final int STATE_BEGAN = 0;
+ public static final int STATE_ONGOING = 1;
+ public static final int STATE_COMPLETED = 2;
+ public static final int STATE_ERROR = 3;
+ private static final int STATE_MAX = STATE_ERROR;
+
+ /** @hide */
+ @IntDef(flag = true, prefix = { "STATE_" }, value = {
+ STATE_BEGAN,
+ STATE_ONGOING,
+ STATE_COMPLETED,
+ STATE_ERROR,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface State {}
+
+ @State public final int state;
+
+ @Override
+ public String toString() {
+ return String.format("{state: %d}", state);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ } else if (other instanceof TaskResult) {
+ return equals((TaskResult) other);
+ }
+ return false;
+ }
+
+ private boolean equals(TaskResult other) {
+ return state == other.state;
+ }
+
+ public TaskResult(@State int state) {
+ this.state = state;
+
+ checkConstructorArguments();
+ }
+
+ private void checkConstructorArguments() {
+ CheckHelpers.checkStateInRange(state, STATE_MAX);
+ }
+
+ //<editor-fold desc="Binder boilerplate">
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(state);
+ }
+
+ private TaskResult(Parcel in) {
+ state = in.readInt();
+
+ checkConstructorArguments();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final Parcelable.Creator<TaskResult> CREATOR
+ = new Parcelable.Creator<TaskResult>() {
+ public TaskResult createFromParcel(Parcel in) {
+ return new TaskResult(in);
+ }
+
+ public TaskResult[] newArray(int size) {
+ return new TaskResult[size];
+ }
+ };
+ //</editor-fold>
+}
diff --git a/startop/iorap/tests/Android.bp b/startop/iorap/tests/Android.bp
new file mode 100644
index 0000000..7605784
--- /dev/null
+++ b/startop/iorap/tests/Android.bp
@@ -0,0 +1,40 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// TODO: once b/80095087 is fixed, rewrite this back to android_test
+java_library {
+ name: "libiorap-java-test-lib",
+ srcs: ["src/**/*.kt"],
+
+ static_libs: [
+ // non-test dependencies
+ "libiorap-java",
+ // test android dependencies
+ "platform-test-annotations",
+ "android-support-test",
+ // test framework dependencies
+ "mockito-target-inline-minus-junit4",
+ // "mockito-target-minus-junit4",
+ // Mockito also requires JNI (see Android.mk)
+ // and android:debuggable=true (see AndroidManifest.xml)
+ "truth-prebuilt",
+ ],
+
+ // sdk_version: "current",
+ // certificate: "platform",
+
+ libs: ["android.test.base", "android.test.runner"],
+
+ // test_suites: ["device-tests"],
+}
diff --git a/startop/iorap/tests/Android.mk b/startop/iorap/tests/Android.mk
new file mode 100644
index 0000000..1b2aa46
--- /dev/null
+++ b/startop/iorap/tests/Android.mk
@@ -0,0 +1,46 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# android_test does not support JNI libraries
+# TODO: once b/80095087 is fixed, rewrite this back to android_test
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_JACK_FLAGS := --multi-dex native
+LOCAL_DX_FLAGS := --multi-dex
+
+LOCAL_PACKAGE_NAME := libiorap-java-tests
+LOCAL_COMPATIBILITY_SUITE := device-tests
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ libiorap-java-test-lib
+
+LOCAL_MULTILIB := both
+
+LOCAL_JNI_SHARED_LIBRARIES := \
+ libdexmakerjvmtiagent \
+ libstaticjvmtiagent \
+ libmultiplejvmtiagentsinterferenceagent
+
+LOCAL_JAVA_LIBRARIES := \
+ android.test.base \
+ android.test.runner
+
+# Use private APIs
+LOCAL_CERTIFICATE := platform
+LOCAL_PRIVATE_PLATFORM_APIS := true
+
+include $(BUILD_PACKAGE)
diff --git a/startop/iorap/tests/AndroidManifest.xml b/startop/iorap/tests/AndroidManifest.xml
new file mode 100644
index 0000000..99f4add
--- /dev/null
+++ b/startop/iorap/tests/AndroidManifest.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<!--suppress AndroidUnknownAttribute -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.google.android.startop.iorap.tests"
+ android:sharedUserId="com.google.android.startop.iorap.tests"
+ android:versionCode="1"
+ android:versionName="1.0" >
+
+ <!--suppress AndroidDomInspection -->
+ <instrumentation
+ android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.google.android.startop.iorap.tests" />
+
+ <!--
+ 'debuggable=true' is required to properly load mockito jvmti dependencies,
+ otherwise it gives the following error at runtime:
+
+ Openjdkjvmti plugin was loaded on a non-debuggable Runtime.
+ Plugin was loaded too late to change runtime state to DEBUGGABLE. -->
+ <application android:debuggable="true">
+ <uses-library android:name="android.test.runner" />
+ </application>
+</manifest>
diff --git a/startop/iorap/tests/src/com/google/android/startop/iorap/IIorapIntegrationTest.kt b/startop/iorap/tests/src/com/google/android/startop/iorap/IIorapIntegrationTest.kt
new file mode 100644
index 0000000..4ba44a9
--- /dev/null
+++ b/startop/iorap/tests/src/com/google/android/startop/iorap/IIorapIntegrationTest.kt
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.google.android.startop.iorap
+
+import android.net.Uri
+import android.os.ServiceManager
+import android.support.test.filters.MediumTest
+import org.junit.Test
+import org.junit.Ignore
+import org.mockito.Mockito.*
+
+// @Ignore("Test is disabled until iorapd is added to init and there's selinux policies for it")
+@MediumTest
+class IIorapIntegrationTest {
+ /**
+ * @throws ServiceManager.ServiceNotFoundException if iorapd service could not be found
+ */
+ private val iorapService : IIorap by lazy {
+ // TODO: connect to 'iorapd.stub' which doesn't actually do any work other than reply.
+ IIorap.Stub.asInterface(ServiceManager.getServiceOrThrow("iorapd"))
+
+ // Use 'adb shell setenforce 0' otherwise this whole test fails,
+ // because the servicemanager is not allowed to hand out the binder token for iorapd.
+
+ // TODO: implement the selinux policies for iorapd.
+ }
+
+ // A dummy binder stub implementation is required to use with mockito#spy.
+ // Mockito overrides the methods at runtime and tracks how methods were invoked.
+ open class DummyTaskListener : ITaskListener.Stub() {
+ // Note: make parameters nullable to avoid the kotlin IllegalStateExceptions
+ // from using the mockito matchers (eq, argThat, etc).
+ override fun onProgress(requestId: RequestId?, result: TaskResult?) {
+ }
+
+ override fun onComplete(requestId: RequestId?, result: TaskResult?) {
+ }
+ }
+
+ private fun testAnyMethod(func : (RequestId) -> Unit) {
+ val taskListener = spy(DummyTaskListener())!!
+
+ try {
+ iorapService.setTaskListener(taskListener)
+ // Note: Binder guarantees total order for oneway messages sent to the same binder
+ // interface, so we don't need any additional blocking here before sending later calls.
+
+ // Every new method call should have a unique request id.
+ val requestId = RequestId.nextValueForSequence()!!
+
+ // Apply the specific function under test.
+ func(requestId)
+
+ // Typical mockito behavior is to allow any-order callbacks, but we want to test order.
+ val inOrder = inOrder(taskListener)
+
+ // The "stub" behavior of iorapd is that every request immediately gets a response of
+ // BEGAN,ONGOING,COMPLETED
+ inOrder.verify(taskListener, timeout(100)).
+ onProgress(eq(requestId), argThat { it!!.state == TaskResult.STATE_BEGAN })
+ inOrder.verify(taskListener, timeout(100)).
+ onProgress(eq(requestId), argThat { it!!.state == TaskResult.STATE_ONGOING })
+ inOrder.verify(taskListener, timeout(100)).
+ onComplete(eq(requestId), argThat { it!!.state == TaskResult.STATE_COMPLETED })
+ inOrder.verifyNoMoreInteractions()
+
+ } finally {
+ iorapService.setTaskListener(null)
+ }
+ }
+
+ @Test
+ fun testOnPackageEvent() {
+ testAnyMethod { requestId : RequestId ->
+ iorapService.onPackageEvent(requestId,
+ PackageEvent.createReplaced(
+ Uri.parse("https://www.google.com"), "com.fake.package"))
+ }
+ }
+
+ @Test
+ fun testOnAppIntentEvent() {
+ testAnyMethod { requestId : RequestId ->
+ iorapService.onAppIntentEvent(requestId, AppIntentEvent.createDefaultIntentChanged(
+ ActivityInfo("dont care", "dont care"),
+ ActivityInfo("dont care 2", "dont care 2")))
+ }
+ }
+
+ @Test
+ fun testOnSystemServiceEvent() {
+ testAnyMethod { requestId : RequestId ->
+ iorapService.onSystemServiceEvent(requestId,
+ SystemServiceEvent(SystemServiceEvent.TYPE_START))
+ }
+ }
+
+ @Test
+ fun testOnSystemServiceUserEvent() {
+ testAnyMethod { requestId : RequestId ->
+ iorapService.onSystemServiceUserEvent(requestId,
+ SystemServiceUserEvent(SystemServiceUserEvent.TYPE_START_USER,0))
+ }
+ }
+}
diff --git a/startop/iorap/tests/src/com/google/android/startop/iorap/ParcelablesTest.kt b/startop/iorap/tests/src/com/google/android/startop/iorap/ParcelablesTest.kt
new file mode 100644
index 0000000..4abbb3e
--- /dev/null
+++ b/startop/iorap/tests/src/com/google/android/startop/iorap/ParcelablesTest.kt
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.google.android.startop.iorap
+
+import android.net.Uri
+import android.os.Parcel
+import android.os.Parcelable
+import android.support.test.filters.SmallTest
+import org.junit.Test
+import org.junit.runner.RunWith
+import com.google.common.truth.Truth.assertThat
+import org.junit.runners.Parameterized
+
+/**
+ * Basic unit tests to ensure that all of the [Parcelable]s in [com.google.android.startop.iorap]
+ * have a valid-conforming interface implementation.
+ */
+@SmallTest
+@RunWith(Parameterized::class)
+class ParcelablesTest<T : Parcelable>(private val inputData : InputData<T>) {
+ companion object {
+ private val initialRequestId = RequestId.nextValueForSequence()!!
+
+ @JvmStatic
+ @Parameterized.Parameters
+ fun data() = listOf(
+ InputData(
+ newActivityInfo(),
+ newActivityInfo(),
+ ActivityInfo("some package", "some other activity")),
+ InputData(
+ ActivityHintEvent(ActivityHintEvent.TYPE_COMPLETED, newActivityInfo()),
+ ActivityHintEvent(ActivityHintEvent.TYPE_COMPLETED, newActivityInfo()),
+ ActivityHintEvent(ActivityHintEvent.TYPE_POST_COMPLETED,
+ newActivityInfo())),
+ InputData(
+ AppIntentEvent.createDefaultIntentChanged(newActivityInfo(),
+ newActivityInfoOther()),
+ AppIntentEvent.createDefaultIntentChanged(newActivityInfo(),
+ newActivityInfoOther()),
+ AppIntentEvent.createDefaultIntentChanged(newActivityInfoOther(),
+ newActivityInfo())),
+ InputData(
+ PackageEvent.createReplaced(newUri(), "some package"),
+ PackageEvent.createReplaced(newUri(), "some package"),
+ PackageEvent.createReplaced(newUri(), "some other package")
+ ),
+ InputData(initialRequestId, cloneRequestId(initialRequestId),
+ RequestId.nextValueForSequence()),
+ InputData(
+ SystemServiceEvent(SystemServiceEvent.TYPE_BOOT_PHASE),
+ SystemServiceEvent(SystemServiceEvent.TYPE_BOOT_PHASE),
+ SystemServiceEvent(SystemServiceEvent.TYPE_START)),
+ InputData(
+ SystemServiceUserEvent(SystemServiceUserEvent.TYPE_START_USER, 12345),
+ SystemServiceUserEvent(SystemServiceUserEvent.TYPE_START_USER, 12345),
+ SystemServiceUserEvent(SystemServiceUserEvent.TYPE_CLEANUP_USER, 12345)),
+ InputData(
+ TaskResult(TaskResult.STATE_COMPLETED),
+ TaskResult(TaskResult.STATE_COMPLETED),
+ TaskResult(TaskResult.STATE_ONGOING))
+ )
+
+ private fun newActivityInfo() : ActivityInfo {
+ return ActivityInfo("some package", "some activity")
+ }
+
+ private fun newActivityInfoOther() : ActivityInfo {
+ return ActivityInfo("some package 2", "some activity 2")
+ }
+
+ private fun newUri() : Uri {
+ return Uri.parse("https://www.google.com")
+ }
+
+ private fun cloneRequestId(requestId: RequestId) : RequestId {
+ val constructor = requestId::class.java.declaredConstructors[0]
+ constructor.isAccessible = true
+ return constructor.newInstance(requestId.requestId) as RequestId
+ }
+ }
+
+ /**
+ * Test for [Object.equals] implementation.
+ */
+ @Test
+ fun testEquality() {
+ assertThat(inputData.valid).isEqualTo(inputData.valid)
+ assertThat(inputData.valid).isEqualTo(inputData.validCopy)
+ assertThat(inputData.valid).isNotEqualTo(inputData.validOther)
+ }
+
+ /**
+ * Test for [Parcelable] implementation.
+ */
+ @Test
+ fun testParcelRoundTrip() {
+ // calling writeToParcel and then T::CREATOR.createFromParcel would return the same data.
+ val assertParcels = { it : T, data : InputData<T> ->
+ val parcel = Parcel.obtain()
+ it.writeToParcel(parcel, 0)
+ parcel.setDataPosition(0) // future reads will see all previous writes.
+ assertThat(it).isEqualTo(data.createFromParcel(parcel))
+ parcel.recycle()
+ }
+
+ assertParcels(inputData.valid, inputData)
+ assertParcels(inputData.validCopy, inputData)
+ assertParcels(inputData.validOther, inputData)
+ }
+
+ data class InputData<T : Parcelable>(val valid : T, val validCopy : T, val validOther : T) {
+ val kls = valid.javaClass
+ init {
+ assertThat(valid).isNotSameAs(validCopy)
+ // Don't use isInstanceOf because of phantom warnings in intellij about Class!
+ assertThat(validCopy.javaClass).isEqualTo(valid.javaClass)
+ assertThat(validOther.javaClass).isEqualTo(valid.javaClass)
+ }
+
+ fun createFromParcel(parcel : Parcel) : T {
+ val field = kls.getDeclaredField("CREATOR")
+ val creator = field.get(null) as Parcelable.Creator<T>
+
+ return creator.createFromParcel(parcel)
+ }
+ }
+}
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/src/com/android/systemui/stackdivider/events/StoppedDragingEvent.java b/startop/tools/view_compiler/util.h
similarity index 62%
copy from packages/SystemUI/src/com/android/systemui/stackdivider/events/StoppedDragingEvent.java
copy to startop/tools/view_compiler/util.h
index c50d6d6..03e0939 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/events/StoppedDragingEvent.java
+++ b/startop/tools/view_compiler/util.h
@@ -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.
*/
+#ifndef UTIL_H_
+#define UTIL_H_
-package com.android.systemui.stackdivider.events;
+#include <string>
-import com.android.systemui.recents.events.EventBus;
+std::string FindLayoutNameFromFilename(const std::string& filename);
-/**
- * Sent when the divider isn't draging anymore.
- */
-public class StoppedDragingEvent extends EventBus.Event {
-}
+#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/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 8c37a21..d33a537 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -1905,6 +1905,22 @@
return false;
}
+ /**
+ * Handles {@link Intent#ACTION_CALL} intents trampolined from UserCallActivity.
+ * @param intent The {@link Intent#ACTION_CALL} intent to handle.
+ * @hide
+ */
+ public void handleCallIntent(Intent intent) {
+ try {
+ if (isServiceConnected()) {
+ getTelecomService().handleCallIntent(intent);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException handleCallIntent: " + e);
+ }
+
+ }
+
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 38247bc..df7d683 100644
--- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
@@ -284,4 +284,9 @@
* @see TelecomServiceImpl#isInEmergencyCall
*/
boolean isInEmergencyCall();
+
+ /**
+ * @see TelecomServiceImpl#handleCallIntent
+ */
+ void handleCallIntent(in Intent intent);
}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 995418e..9b5b700 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.
*/
@@ -1625,11 +1633,21 @@
* When {@code false}, use default title for Enhanced 4G LTE Mode settings.
* When {@code true}, use the variant.
* @hide
+ * @deprecated use {@link #KEY_ENHANCED_4G_LTE_TITLE_VARIANT_INT}.
*/
+ @Deprecated
public static final String KEY_ENHANCED_4G_LTE_TITLE_VARIANT_BOOL =
"enhanced_4g_lte_title_variant_bool";
/**
+ * The index indicates the carrier specified title string of Enahnce 4G LTE Mode settings.
+ * Default value is 0, which indicates the default title string.
+ * @hide
+ */
+ public static final String KEY_ENHANCED_4G_LTE_TITLE_VARIANT_INT =
+ "enhanced_4g_lte_title_variant_int";
+
+ /**
* Indicates whether the carrier wants to notify the user when handover of an LTE video call to
* WIFI fails.
* <p>
@@ -2181,6 +2199,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);
@@ -2410,6 +2429,7 @@
sDefaults.putStringArray(KEY_IMS_REASONINFO_MAPPING_STRING_ARRAY, null);
sDefaults.putBoolean(KEY_ENHANCED_4G_LTE_TITLE_VARIANT_BOOL, false);
+ sDefaults.putInt(KEY_ENHANCED_4G_LTE_TITLE_VARIANT_INT, 0);
sDefaults.putBoolean(KEY_NOTIFY_VT_HANDOVER_TO_WIFI_FAILURE_BOOL, false);
sDefaults.putStringArray(KEY_FILTERED_CNAP_NAMES_STRING_ARRAY, null);
sDefaults.putBoolean(KEY_EDITABLE_WFC_ROAMING_MODE_BOOL, false);
diff --git a/telephony/java/android/telephony/NeighboringCellInfo.java b/telephony/java/android/telephony/NeighboringCellInfo.java
index 8e99518..79298fd 100644
--- a/telephony/java/android/telephony/NeighboringCellInfo.java
+++ b/telephony/java/android/telephony/NeighboringCellInfo.java
@@ -32,7 +32,12 @@
/**
* Represents the neighboring cell information, including
* Received Signal Strength and Cell ID location.
+ *
+ * @deprecated This class should not be used by any app targeting
+ * {@link Build.VERSION_CODES.Q Android Q} or higher. Instead callers should use
+ * {@Link android.telephony.CellInfo 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/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index f2b73dc..7469186 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -24,6 +24,7 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.telephony.AccessNetworkConstants.AccessNetworkType;
+import android.telephony.NetworkRegistrationState.Domain;
import android.text.TextUtils;
import java.lang.annotation.Retention;
@@ -1595,7 +1596,7 @@
/**
* Get all of the available network registration states.
*
- * @return List of registration states
+ * @return List of {@link NetworkRegistrationState}
* @hide
*/
@SystemApi
@@ -1606,14 +1607,30 @@
}
/**
- * Get the network registration states with given transport type.
+ * Get the network registration states for the transport type.
*
- * @param transportType The transport type. See {@link AccessNetworkConstants.TransportType}
- * @return List of registration states.
+ * @param transportType The {@link AccessNetworkConstants.TransportType transport type}
+ * @return List of {@link NetworkRegistrationState}
+ * @hide
+ *
+ * @deprecated Use {@link #getNetworkRegistrationStatesFromTransportType(int)}
+ */
+ @Deprecated
+ @SystemApi
+ public List<NetworkRegistrationState> getNetworkRegistrationStates(int transportType) {
+ return getNetworkRegistrationStatesForTransportType(transportType);
+ }
+
+ /**
+ * Get the network registration states for the transport type.
+ *
+ * @param transportType The {@link AccessNetworkConstants.TransportType transport type}
+ * @return List of {@link NetworkRegistrationState}
* @hide
*/
@SystemApi
- public List<NetworkRegistrationState> getNetworkRegistrationStates(int transportType) {
+ public List<NetworkRegistrationState> getNetworkRegistrationStatesForTransportType(
+ int transportType) {
List<NetworkRegistrationState> list = new ArrayList<>();
synchronized (mNetworkRegistrationStates) {
@@ -1628,16 +1645,57 @@
}
/**
- * Get the network registration states with given transport type and domain.
+ * Get the network registration states for the network domain.
*
- * @param domain The network domain. Must be {@link NetworkRegistrationState#DOMAIN_CS} or
- * {@link NetworkRegistrationState#DOMAIN_PS}.
- * @param transportType The transport type. See {@link AccessNetworkConstants.TransportType}
- * @return The matching NetworkRegistrationState.
+ * @param domain The network {@link NetworkRegistrationState.Domain domain}
+ * @return List of {@link NetworkRegistrationState}
* @hide
*/
@SystemApi
- public NetworkRegistrationState getNetworkRegistrationStates(int domain, int transportType) {
+ public List<NetworkRegistrationState> getNetworkRegistrationStatesForDomain(
+ @Domain int domain) {
+ List<NetworkRegistrationState> list = new ArrayList<>();
+
+ synchronized (mNetworkRegistrationStates) {
+ for (NetworkRegistrationState networkRegistrationState : mNetworkRegistrationStates) {
+ if (networkRegistrationState.getDomain() == domain) {
+ list.add(networkRegistrationState);
+ }
+ }
+ }
+
+ return list;
+ }
+
+ /**
+ * Get the network registration state for the transport type and network domain.
+ *
+ * @param domain The network {@link NetworkRegistrationState.Domain domain}
+ * @param transportType The {@link AccessNetworkConstants.TransportType transport type}
+ * @return The matching {@link NetworkRegistrationState}
+ * @hide
+ *
+ * @deprecated Use {@link #getNetworkRegistrationState(int, int)}
+ */
+ @Deprecated
+ @SystemApi
+ public NetworkRegistrationState getNetworkRegistrationStates(@Domain int domain,
+ int transportType) {
+ return getNetworkRegistrationState(domain, transportType);
+ }
+
+ /**
+ * Get the network registration state for the transport type and network domain.
+ *
+ * @param domain The network {@link NetworkRegistrationState.Domain domain}
+ * @param transportType The {@link AccessNetworkConstants.TransportType transport type}
+ * @return The matching {@link NetworkRegistrationState}
+ * @hide
+ *
+ */
+ @SystemApi
+ public NetworkRegistrationState getNetworkRegistrationState(@Domain int domain,
+ int transportType) {
synchronized (mNetworkRegistrationStates) {
for (NetworkRegistrationState networkRegistrationState : mNetworkRegistrationStates) {
if (networkRegistrationState.getTransportType() == transportType
@@ -1669,5 +1727,4 @@
mNetworkRegistrationStates.add(regState);
}
}
-
}
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 38ee79f..cc143d6 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -2153,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 824533d..80b6ead 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -1590,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.
*/
@@ -8380,6 +8381,29 @@
}
/**
+ * Checks if manual network selection is allowed.
+ *
+ * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
+ * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}.
+ *
+ * @return {@code true} if manual network selection is allowed, otherwise return {@code false}.
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ public boolean isManualNetworkSelectionAllowed() {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ return telephony.isManualNetworkSelectionAllowed(getSubId());
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelephony#isManualNetworkSelectionAllowed", e);
+ }
+ return true;
+ }
+
+ /**
* Get the most recently available signal strength information.
*
* Get the most recent SignalStrength information reported by the modem. Due
diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java
index c2c93da..8379f8c 100644
--- a/telephony/java/android/telephony/data/ApnSetting.java
+++ b/telephony/java/android/telephony/data/ApnSetting.java
@@ -1206,7 +1206,8 @@
/** @hide */
public static int getMvnoTypeIntFromString(String mvnoType) {
- Integer mvnoTypeInt = MVNO_TYPE_STRING_MAP.get(mvnoType);
+ String mvnoTypeString = TextUtils.isEmpty(mvnoType) ? mvnoType : mvnoType.toLowerCase();
+ Integer mvnoTypeInt = MVNO_TYPE_STRING_MAP.get(mvnoTypeString);
return mvnoTypeInt == null ? UNSPECIFIED_INT : mvnoTypeInt;
}
diff --git a/telephony/java/android/telephony/ims/ImsCallProfile.java b/telephony/java/android/telephony/ims/ImsCallProfile.java
index 5d6a8c1..89ef339 100644
--- a/telephony/java/android/telephony/ims/ImsCallProfile.java
+++ b/telephony/java/android/telephony/ims/ImsCallProfile.java
@@ -117,12 +117,14 @@
* @hide
*/
public static final String EXTRA_CONFERENCE = "conference";
+
/**
* 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";
+ public static final String EXTRA_EMERGENCY_CALL = "e_call";
+
/**
* @hide
*/
diff --git a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
index 31381804..cecf2e2 100644
--- a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
@@ -21,12 +21,11 @@
import android.net.Uri;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
+import android.telephony.ims.ImsReasonInfo;
import android.telephony.ims.aidl.IImsRegistration;
import android.telephony.ims.aidl.IImsRegistrationCallback;
import android.util.Log;
-import android.telephony.ims.ImsReasonInfo;
-
import com.android.internal.annotations.VisibleForTesting;
import java.lang.annotation.Retention;
@@ -81,13 +80,14 @@
* Callback class for receiving Registration callback events.
* @hide
*/
- public static class Callback {
+ public static class Callback extends IImsRegistrationCallback.Stub {
/**
* Notifies the framework when the IMS Provider is connected to the IMS network.
*
* @param imsRadioTech the radio access technology. Valid values are defined in
* {@link ImsRegistrationTech}.
*/
+ @Override
public void onRegistered(@ImsRegistrationTech int imsRadioTech) {
}
@@ -97,6 +97,7 @@
* @param imsRadioTech the radio access technology. Valid values are defined in
* {@link ImsRegistrationTech}.
*/
+ @Override
public void onRegistering(@ImsRegistrationTech int imsRadioTech) {
}
@@ -105,6 +106,7 @@
*
* @param info the {@link ImsReasonInfo} associated with why registration was disconnected.
*/
+ @Override
public void onDeregistered(ImsReasonInfo info) {
}
@@ -115,6 +117,7 @@
* @param imsRadioTech The {@link ImsRegistrationTech} type that has failed
* @param info A {@link ImsReasonInfo} that identifies the reason for failure.
*/
+ @Override
public void onTechnologyChangeFailed(@ImsRegistrationTech int imsRadioTech,
ImsReasonInfo info) {
}
@@ -125,6 +128,7 @@
* @param uris new array of subscriber {@link Uri}s that are associated with this IMS
* subscription.
*/
+ @Override
public void onSubscriberAssociatedUriChanged(Uri[] uris) {
}
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/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index ca2bcff..32eb12b 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -808,6 +808,13 @@
*/
boolean isDataEnabled(int subId);
+ /**
+ * Checks if manual network selection is allowed.
+ *
+ * @return {@code true} if manual network selection is allowed, otherwise return {@code false}.
+ */
+ boolean isManualNetworkSelectionAllowed(int subId);
+
/**
* Get P-CSCF address from PCO after data connection is established or modified.
* @param apnType the apnType, "ims" for IMS APN, "emergency" for EMERGENCY APN
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/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 39722c6..5ed283e 100644
--- a/telephony/java/com/android/internal/telephony/SmsApplication.java
+++ b/telephony/java/com/android/internal/telephony/SmsApplication.java
@@ -584,7 +584,8 @@
// We only make the change if the new package is valid
PackageManager packageManager = context.getPackageManager();
- Collection<SmsApplicationData> applications = getApplicationCollection(context);
+ Collection<SmsApplicationData> applications = getApplicationCollectionInternal(
+ context, userId);
SmsApplicationData oldAppData = oldPackageName != null ?
getApplicationForPackage(applications, oldPackageName) : null;
SmsApplicationData applicationData = getApplicationForPackage(applications, packageName);
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 1f60b71..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";
@@ -814,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*/
@@ -831,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*/
@@ -842,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/NativeProcessesMemoryTest/src/com/android/tests/nativeprocesses/NativeProcessesMemoryTest.java b/tests/NativeProcessesMemoryTest/src/com/android/tests/nativeprocesses/NativeProcessesMemoryTest.java
index ae011a0..c86f06e 100644
--- a/tests/NativeProcessesMemoryTest/src/com/android/tests/nativeprocesses/NativeProcessesMemoryTest.java
+++ b/tests/NativeProcessesMemoryTest/src/com/android/tests/nativeprocesses/NativeProcessesMemoryTest.java
@@ -21,6 +21,7 @@
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;
@@ -84,7 +85,10 @@
// showmap requires root, we enable it here for the rest of the test
mTestDevice.enableAdbRoot();
- listener.testRunStarted(RUN_NAME, 0 /* testCount */);
+ 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();
@@ -94,7 +98,8 @@
mNativeProcessToMemory.put(
NUM_NATIVE_PROCESSES_KEY, Integer.toString(nativeProcesses.size()));
- listener.testRunEnded(0, mNativeProcessToMemory);
+ listener.testEnded(testDescription, mNativeProcessToMemory);
+ listener.testRunEnded(0, new HashMap<String, String>());
}
/** Samples memory of all processes and logs the memory use. */
diff --git a/tests/NetworkSecurityConfigTest/Android.mk b/tests/NetworkSecurityConfigTest/Android.mk
index fe65ecc..c225e17 100644
--- a/tests/NetworkSecurityConfigTest/Android.mk
+++ b/tests/NetworkSecurityConfigTest/Android.mk
@@ -7,7 +7,6 @@
LOCAL_JAVA_LIBRARIES := \
android.test.runner \
- bouncycastle \
conscrypt \
android.test.base \
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/Android.mk b/tests/net/Android.mk
index e529b93..750e2fb 100644
--- a/tests/net/Android.mk
+++ b/tests/net/Android.mk
@@ -38,6 +38,7 @@
libbacktrace \
libbase \
libbinder \
+ libbinderthreadstate \
libc++ \
libcrypto \
libcutils \
diff --git a/tests/net/java/android/net/dhcp/DhcpPacketTest.java b/tests/net/java/android/net/dhcp/DhcpPacketTest.java
index 312b3d1..a592809 100644
--- a/tests/net/java/android/net/dhcp/DhcpPacketTest.java
+++ b/tests/net/java/android/net/dhcp/DhcpPacketTest.java
@@ -25,6 +25,7 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import android.annotation.Nullable;
import android.net.DhcpResults;
import android.net.LinkAddress;
import android.net.NetworkUtils;
@@ -37,6 +38,7 @@
import java.io.ByteArrayOutputStream;
import java.net.Inet4Address;
import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -56,6 +58,8 @@
private static final Inet4Address NETMASK = getPrefixMaskAsInet4Address(PREFIX_LENGTH);
private static final Inet4Address BROADCAST_ADDR = getBroadcastAddress(
SERVER_ADDR, PREFIX_LENGTH);
+ private static final String HOSTNAME = "testhostname";
+ private static final short MTU = 1500;
// Use our own empty address instead of Inet4Address.ANY or INADDR_ANY to ensure that the code
// doesn't use == instead of equals when comparing addresses.
private static final Inet4Address ANY = (Inet4Address) v4Address("0.0.0.0");
@@ -960,7 +964,8 @@
assertTrue(msg, Arrays.equals(expected, actual));
}
- public void checkBuildOfferPacket(int leaseTimeSecs) throws Exception {
+ public void checkBuildOfferPacket(int leaseTimeSecs, @Nullable String hostname)
+ throws Exception {
final int renewalTime = (int) (Integer.toUnsignedLong(leaseTimeSecs) / 2);
final int rebindingTime = (int) (Integer.toUnsignedLong(leaseTimeSecs) * 875 / 1000);
final int transactionId = 0xdeadbeef;
@@ -971,7 +976,8 @@
CLIENT_MAC, leaseTimeSecs, NETMASK /* netMask */,
BROADCAST_ADDR /* bcAddr */, Collections.singletonList(SERVER_ADDR) /* gateways */,
Collections.singletonList(SERVER_ADDR) /* dnsServers */,
- SERVER_ADDR /* dhcpServerIdentifier */, null /* domainName */, false /* metered */);
+ SERVER_ADDR /* dhcpServerIdentifier */, null /* domainName */, hostname,
+ false /* metered */, MTU);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
// BOOTP headers
@@ -1027,12 +1033,22 @@
// Nameserver
bos.write(new byte[] { (byte) 0x06, (byte) 0x04 });
bos.write(SERVER_ADDR.getAddress());
+ // Hostname
+ if (hostname != null) {
+ bos.write(new byte[]{(byte) 0x0c, (byte) hostname.length()});
+ bos.write(hostname.getBytes(Charset.forName("US-ASCII")));
+ }
+ // MTU
+ bos.write(new byte[] { (byte) 0x1a, (byte) 0x02 });
+ bos.write(shortToByteArray(MTU));
// End options.
bos.write(0xff);
- final byte[] expected = bos.toByteArray();
- assertTrue((expected.length & 1) == 0);
+ if ((bos.size() & 1) != 0) {
+ bos.write(0x00);
+ }
+ final byte[] expected = bos.toByteArray();
final byte[] actual = new byte[packet.limit()];
packet.get(actual);
final String msg = "Expected:\n " + HexDump.dumpHexString(expected) +
@@ -1042,13 +1058,18 @@
@Test
public void testOfferPacket() throws Exception {
- checkBuildOfferPacket(3600);
- checkBuildOfferPacket(Integer.MAX_VALUE);
- checkBuildOfferPacket(0x80000000);
- checkBuildOfferPacket(INFINITE_LEASE);
+ checkBuildOfferPacket(3600, HOSTNAME);
+ checkBuildOfferPacket(Integer.MAX_VALUE, HOSTNAME);
+ checkBuildOfferPacket(0x80000000, HOSTNAME);
+ checkBuildOfferPacket(INFINITE_LEASE, HOSTNAME);
+ checkBuildOfferPacket(3600, null);
}
private static byte[] intToByteArray(int val) {
return ByteBuffer.allocate(4).putInt(val).array();
}
+
+ private static byte[] shortToByteArray(short val) {
+ return ByteBuffer.allocate(2).putShort(val).array();
+ }
}
diff --git a/tests/net/java/android/net/dhcp/DhcpServerTest.java b/tests/net/java/android/net/dhcp/DhcpServerTest.java
index 45a50d9..df34c73 100644
--- a/tests/net/java/android/net/dhcp/DhcpServerTest.java
+++ b/tests/net/java/android/net/dhcp/DhcpServerTest.java
@@ -17,6 +17,7 @@
package android.net.dhcp;
import static android.net.dhcp.DhcpPacket.DHCP_CLIENT;
+import static android.net.dhcp.DhcpPacket.DHCP_HOST_NAME;
import static android.net.dhcp.DhcpPacket.ENCAP_BOOTP;
import static android.net.dhcp.DhcpPacket.INADDR_ANY;
import static android.net.dhcp.DhcpPacket.INADDR_BROADCAST;
@@ -87,6 +88,7 @@
Arrays.asList(parseAddr("192.168.0.200"), parseAddr("192.168.0.201")));
private static final long TEST_LEASE_TIME_SECS = 3600L;
private static final int TEST_MTU = 1500;
+ private static final String TEST_HOSTNAME = "testhostname";
private static final int TEST_TRANSACTION_ID = 123;
private static final byte[] TEST_CLIENT_MAC_BYTES = new byte [] { 1, 2, 3, 4, 5, 6 };
@@ -96,7 +98,10 @@
private static final long TEST_CLOCK_TIME = 1234L;
private static final int TEST_LEASE_EXPTIME_SECS = 3600;
private static final DhcpLease TEST_LEASE = new DhcpLease(null, TEST_CLIENT_MAC,
- TEST_CLIENT_ADDR, TEST_LEASE_EXPTIME_SECS*1000L + TEST_CLOCK_TIME, null /* hostname */);
+ TEST_CLIENT_ADDR, TEST_LEASE_EXPTIME_SECS * 1000L + TEST_CLOCK_TIME,
+ null /* hostname */);
+ private static final DhcpLease TEST_LEASE_WITH_HOSTNAME = new DhcpLease(null, TEST_CLIENT_MAC,
+ TEST_CLIENT_ADDR, TEST_LEASE_EXPTIME_SECS * 1000L + TEST_CLOCK_TIME, TEST_HOSTNAME);
@NonNull @Mock
private Dependencies mDeps;
@@ -217,15 +222,17 @@
public void testRequest_Selecting_Ack() throws Exception {
when(mRepository.requestLease(isNull() /* clientId */, eq(TEST_CLIENT_MAC),
eq(INADDR_ANY) /* clientAddr */, eq(INADDR_ANY) /* relayAddr */,
- eq(TEST_CLIENT_ADDR) /* reqAddr */, eq(true) /* sidSet */, isNull() /* hostname */))
- .thenReturn(TEST_LEASE);
+ eq(TEST_CLIENT_ADDR) /* reqAddr */, eq(true) /* sidSet */, eq(TEST_HOSTNAME)))
+ .thenReturn(TEST_LEASE_WITH_HOSTNAME);
final DhcpRequestPacket request = makeRequestSelectingPacket();
+ request.mHostName = TEST_HOSTNAME;
+ request.mRequestedParams = new byte[] { DHCP_HOST_NAME };
mServer.processPacket(request, DHCP_CLIENT);
assertResponseSentTo(TEST_CLIENT_ADDR);
final DhcpAckPacket packet = assertAck(getPacket());
- assertMatchesTestLease(packet);
+ assertMatchesTestLease(packet, TEST_HOSTNAME);
}
@Test
@@ -270,14 +277,18 @@
* - other request states (init-reboot/renewing/rebinding)
*/
- private void assertMatchesTestLease(@NonNull DhcpPacket packet) {
+ private void assertMatchesTestLease(@NonNull DhcpPacket packet, @Nullable String hostname) {
assertMatchesClient(packet);
assertFalse(packet.hasExplicitClientId());
assertEquals(TEST_SERVER_ADDR, packet.mServerIdentifier);
assertEquals(TEST_CLIENT_ADDR, packet.mYourIp);
assertNotNull(packet.mLeaseTime);
assertEquals(TEST_LEASE_EXPTIME_SECS, (int) packet.mLeaseTime);
- assertNull(packet.mHostName);
+ assertEquals(hostname, packet.mHostName);
+ }
+
+ private void assertMatchesTestLease(@NonNull DhcpPacket packet) {
+ assertMatchesTestLease(packet, null);
}
private void assertMatchesClient(@NonNull DhcpPacket packet) {
diff --git a/tests/net/java/android/net/netlink/InetDiagSocketTest.java b/tests/net/java/android/net/netlink/InetDiagSocketTest.java
index 39ecb7e5a..122edba 100644
--- a/tests/net/java/android/net/netlink/InetDiagSocketTest.java
+++ b/tests/net/java/android/net/netlink/InetDiagSocketTest.java
@@ -69,17 +69,12 @@
private ConnectivityManager mCm;
private Context mContext;
private final static int SOCKET_TIMEOUT_MS = 100;
- private boolean mInetDiagUdpEnabled;
@Before
public void setUp() throws Exception {
Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
mContext = instrumentation.getTargetContext();
mCm = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
- int expectedUid = Process.myUid();
- UdpConnection udp = new UdpConnection("127.0.0.1", "127.0.0.2");
- int uid = mCm.getConnectionOwnerUid(udp.protocol, udp.local, udp.remote);
- mInetDiagUdpEnabled = (uid == expectedUid);
}
private class Connection {
@@ -188,11 +183,6 @@
tcp.close();
/**
- * TODO: STOPSHIP: Always test for UDP, do not allow opt-out.
- */
- if (!mInetDiagUdpEnabled) return;
-
- /**
* For UDP connections, either a complete match {protocol, local, remote} or a
* partial match {protocol, local} should return a valid UID.
*/
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..1c77fcc 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));
@@ -1067,13 +1070,13 @@
// Ensure that the default setting for Captive Portals is used for most tests
setCaptivePortalMode(Settings.Global.CAPTIVE_PORTAL_MODE_PROMPT);
- setMobileDataAlwaysOn(false);
+ setAlwaysOnNetworks(false);
setPrivateDnsSettings(PRIVATE_DNS_MODE_OFF, "ignored.example.com");
}
@After
public void tearDown() throws Exception {
- setMobileDataAlwaysOn(false);
+ setAlwaysOnNetworks(false);
if (mCellNetworkAgent != null) {
mCellNetworkAgent.disconnect();
mCellNetworkAgent = null;
@@ -1086,6 +1089,7 @@
mEthernetNetworkAgent.disconnect();
mEthernetNetworkAgent = null;
}
+ FakeSettingsProvider.clearSettingsProvider();
}
private static int transportToLegacyType(int transport) {
@@ -2023,7 +2027,7 @@
@Test
public void testNetworkGoesIntoBackgroundAfterLinger() {
- setMobileDataAlwaysOn(true);
+ setAlwaysOnNetworks(true);
NetworkRequest request = new NetworkRequest.Builder()
.clearCapabilities()
.build();
@@ -2768,10 +2772,10 @@
Settings.Global.putInt(cr, Settings.Global.CAPTIVE_PORTAL_MODE, mode);
}
- private void setMobileDataAlwaysOn(boolean enable) {
+ private void setAlwaysOnNetworks(boolean enable) {
ContentResolver cr = mServiceContext.getContentResolver();
Settings.Global.putInt(cr, Settings.Global.MOBILE_DATA_ALWAYS_ON, enable ? 1 : 0);
- mService.updateMobileDataAlwaysOn();
+ mService.updateAlwaysOnNetworks();
waitForIdle();
}
@@ -2793,7 +2797,7 @@
public void testBackgroundNetworks() throws Exception {
// Create a background request. We can't do this ourselves because ConnectivityService
// doesn't have an API for it. So just turn on mobile data always on.
- setMobileDataAlwaysOn(true);
+ setAlwaysOnNetworks(true);
final NetworkRequest request = new NetworkRequest.Builder().build();
final NetworkRequest fgRequest = new NetworkRequest.Builder()
.addCapability(NET_CAPABILITY_FOREGROUND).build();
@@ -2991,7 +2995,7 @@
// Turn on mobile data always on. The factory starts looking again.
testFactory.expectAddRequests(1);
- setMobileDataAlwaysOn(true);
+ setAlwaysOnNetworks(true);
testFactory.waitForNetworkRequests(2);
assertTrue(testFactory.getMyStartRequested());
@@ -3011,7 +3015,7 @@
// Turn off mobile data always on and expect the request to disappear...
testFactory.expectRemoveRequests(1);
- setMobileDataAlwaysOn(false);
+ setAlwaysOnNetworks(false);
testFactory.waitForNetworkRequests(1);
// ... and cell data to be torn down.
@@ -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/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
index 9c4da1f..14312cf 100644
--- a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
+++ b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
@@ -83,16 +83,6 @@
}
try {
- mWm.setFocusedApp(null, false);
- fail("IWindowManager.setFocusedApp did not throw SecurityException as"
- + " expected");
- } catch (SecurityException e) {
- // expected
- } catch (RemoteException e) {
- fail("Unexpected remote exception");
- }
-
- try {
mWm.prepareAppTransition(0, false);
fail("IWindowManager.prepareAppTransition did not throw SecurityException as"
+ " expected");
diff --git a/tests/testables/src/android/testing/TestableContext.java b/tests/testables/src/android/testing/TestableContext.java
index cf84c79..fff9635 100644
--- a/tests/testables/src/android/testing/TestableContext.java
+++ b/tests/testables/src/android/testing/TestableContext.java
@@ -53,7 +53,7 @@
* Like the following:</p>
* <pre class="prettyprint">
* @Rule
- * private final TestableContext mContext = new TestableContext(InstrumentationRegister.getContext());
+ * public final TestableContext mContext = new TestableContext(InstrumentationRegister.getContext());
* </pre>
*/
public class TestableContext extends ContextWrapper implements TestRule {
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/ResourceUtils.cpp b/tools/aapt2/ResourceUtils.cpp
index c48765b..de00fca 100644
--- a/tools/aapt2/ResourceUtils.cpp
+++ b/tools/aapt2/ResourceUtils.cpp
@@ -38,6 +38,8 @@
namespace aapt {
namespace ResourceUtils {
+constexpr int32_t kNonBreakingSpace = 0xa0;
+
Maybe<ResourceName> ToResourceName(
const android::ResTable::resource_name& name_in) {
ResourceName name_out;
@@ -810,7 +812,7 @@
Utf8Iterator iter(text);
while (iter.HasNext()) {
char32_t codepoint = iter.Next();
- if (!preserve_spaces && !quote_ && iswspace(codepoint)) {
+ if (!preserve_spaces && !quote_ && codepoint != kNonBreakingSpace && iswspace(codepoint)) {
if (!last_codepoint_was_space_) {
// Emit a space if it's the first.
xml_string_.text += ' ';
diff --git a/tools/aapt2/cmd/Compile.cpp b/tools/aapt2/cmd/Compile.cpp
index 62c19fb..8060a8de 100644
--- a/tools/aapt2/cmd/Compile.cpp
+++ b/tools/aapt2/cmd/Compile.cpp
@@ -73,9 +73,9 @@
};
// Resource file paths are expected to look like: [--/res/]type[-config]/name
-static Maybe<ResourcePathData> ExtractResourcePathData(const std::string& path,
+static Maybe<ResourcePathData> ExtractResourcePathData(const std::string& path, const char dir_sep,
std::string* out_error) {
- std::vector<std::string> parts = util::Split(path, file::sDirSep);
+ std::vector<std::string> parts = util::Split(path, dir_sep);
if (parts.size() < 2) {
if (out_error) *out_error = "bad resource path";
return {};
@@ -656,7 +656,7 @@
// Extract resource type information from the full path
std::string err_str;
ResourcePathData path_data;
- if (auto maybe_path_data = ExtractResourcePathData(path, &err_str)) {
+ if (auto maybe_path_data = ExtractResourcePathData(path, inputs->GetDirSeparator(), &err_str)) {
path_data = maybe_path_data.value();
} else {
context->GetDiagnostics()->Error(DiagMessage(file->GetSource()) << err_str);
@@ -731,7 +731,7 @@
// Collect the resources files to compile
if (options_.res_dir && options_.res_zip) {
context.GetDiagnostics()->Error(DiagMessage()
- << "only one of --dir and --zip can be specified");
+ << "only one of --dir and --zip can be specified");
return 1;
} else if (options_.res_dir) {
if (!args.empty()) {
diff --git a/tools/aapt2/cmd/Compile_test.cpp b/tools/aapt2/cmd/Compile_test.cpp
index dd5198c..c0c05cd 100644
--- a/tools/aapt2/cmd/Compile_test.cpp
+++ b/tools/aapt2/cmd/Compile_test.cpp
@@ -17,6 +17,9 @@
#include "Compile.h"
#include "android-base/file.h"
+#include "android-base/stringprintf.h"
+#include "android-base/utf8.h"
+
#include "io/StringStream.h"
#include "io/ZipArchive.h"
#include "java/AnnotationProcessor.h"
@@ -24,8 +27,20 @@
namespace aapt {
+std::string BuildPath(std::vector<std::string> args) {
+ std::string out;
+ if (args.empty()) {
+ return out;
+ }
+ out = args[0];
+ for (int i = 1; i < args.size(); i++) {
+ file::AppendPath(&out, args[i]);
+ }
+ return out;
+}
+
int TestCompile(const std::string& path, const std::string& outDir, bool legacy,
- StdErrDiagnostics& diag) {
+ StdErrDiagnostics& diag) {
std::vector<android::StringPiece> args;
args.push_back(path);
args.push_back("-o");
@@ -39,95 +54,101 @@
TEST(CompilerTest, MultiplePeriods) {
StdErrDiagnostics diag;
std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
- const std::string kResDir = android::base::Dirname(android::base::GetExecutablePath())
- + "/integration-tests/CompileTest/res";
+ const std::string kResDir = BuildPath({android::base::Dirname(android::base::GetExecutablePath()),
+ "integration-tests", "CompileTest", "res"});
// Resource files without periods in the file name should not throw errors
- const std::string path0 = kResDir + "/values/values.xml";
- const std::string path0_out = kResDir + "/values_values.arsc.flat";
-
- remove(path0_out.c_str());
+ const std::string path0 = BuildPath({kResDir, "values", "values.xml"});
+ const std::string path0_out = BuildPath({kResDir, "values_values.arsc.flat"});
+ ::android::base::utf8::unlink(path0_out.c_str());
ASSERT_EQ(TestCompile(path0, kResDir, /** legacy */ false, diag), 0);
- ASSERT_EQ(remove(path0_out.c_str()), 0);
+ ASSERT_EQ(::android::base::utf8::unlink(path0_out.c_str()), 0);
ASSERT_EQ(TestCompile(path0, kResDir, /** legacy */ true, diag), 0);
- ASSERT_EQ(remove(path0_out.c_str()), 0);
+ ASSERT_EQ(::android::base::utf8::unlink(path0_out.c_str()), 0);
- const std::string path1 = kResDir + "/drawable/image.png";
- const std::string path1_out = kResDir + "/drawable_image.png.flat";
- remove(path1_out.c_str());
+ const std::string path1 = BuildPath({kResDir, "drawable", "image.png"});
+ const std::string path1_out = BuildPath({kResDir, "drawable_image.png.flat"});
+ ::android::base::utf8::unlink(path1_out.c_str());
ASSERT_EQ(TestCompile(path1, kResDir, /** legacy */ false, diag), 0);
- ASSERT_EQ(remove(path1_out.c_str()), 0);
+ ASSERT_EQ(::android::base::utf8::unlink(path1_out.c_str()), 0);
ASSERT_EQ(TestCompile(path1, kResDir, /** legacy */ true, diag), 0);
- ASSERT_EQ(remove(path1_out.c_str()), 0);
+ ASSERT_EQ(::android::base::utf8::unlink(path1_out.c_str()), 0);
- const std::string path2 = kResDir + "/drawable/image.9.png";
- const std::string path2_out = kResDir + "/drawable_image.9.png.flat";
- remove(path2_out.c_str());
+ const std::string path2 = BuildPath({kResDir, "drawable", "image.9.png"});
+ const std::string path2_out = BuildPath({kResDir, "drawable_image.9.png.flat"});
+ ::android::base::utf8::unlink(path2_out.c_str());
ASSERT_EQ(TestCompile(path2, kResDir, /** legacy */ false, diag), 0);
- ASSERT_EQ(remove(path2_out.c_str()), 0);
+ ASSERT_EQ(::android::base::utf8::unlink(path2_out.c_str()), 0);
ASSERT_EQ(TestCompile(path2, kResDir, /** legacy */ true, diag), 0);
- ASSERT_EQ(remove(path2_out.c_str()), 0);
+ ASSERT_EQ(::android::base::utf8::unlink(path2_out.c_str()), 0);
// Resource files with periods in the file name should fail on non-legacy compilations
- const std::string path3 = kResDir + "/values/values.all.xml";
- const std::string path3_out = kResDir + "/values_values.all.arsc.flat";
- remove(path3_out.c_str());
+ const std::string path3 = BuildPath({kResDir, "values", "values.all.xml"});
+ const std::string path3_out = BuildPath({kResDir, "values_values.all.arsc.flat"});
+ ::android::base::utf8::unlink(path3_out.c_str());
ASSERT_NE(TestCompile(path3, kResDir, /** legacy */ false, diag), 0);
- ASSERT_NE(remove(path3_out.c_str()), 0);
+ ASSERT_NE(::android::base::utf8::unlink(path3_out.c_str()), 0);
ASSERT_EQ(TestCompile(path3, kResDir, /** legacy */ true, diag), 0);
- ASSERT_EQ(remove(path3_out.c_str()), 0);
+ ASSERT_EQ(::android::base::utf8::unlink(path3_out.c_str()), 0);
- const std::string path4 = kResDir + "/drawable/image.small.png";
- const std::string path4_out = (kResDir + std::string("/drawable_image.small.png.flat")).c_str();
- remove(path4_out.c_str());
+ const std::string path4 = BuildPath({kResDir, "drawable", "image.small.png"});
+ const std::string path4_out = BuildPath({kResDir, "drawable_image.small.png.flat"});
+ ::android::base::utf8::unlink(path4_out.c_str());
ASSERT_NE(TestCompile(path4, kResDir, /** legacy */ false, diag), 0);
- ASSERT_NE(remove(path4_out.c_str()), 0);
+ ASSERT_NE(::android::base::utf8::unlink(path4_out.c_str()), 0);
ASSERT_EQ(TestCompile(path4, kResDir, /** legacy */ true, diag), 0);
- ASSERT_EQ(remove(path4_out.c_str()), 0);
+ ASSERT_EQ(::android::base::utf8::unlink(path4_out.c_str()), 0);
- const std::string path5 = kResDir + "/drawable/image.small.9.png";
- const std::string path5_out = (kResDir + std::string("/drawable_image.small.9.png.flat")).c_str();
- remove(path5_out.c_str());
+ const std::string path5 = BuildPath({kResDir, "drawable", "image.small.9.png"});
+ const std::string path5_out = BuildPath({kResDir, "drawable_image.small.9.png.flat"});
+ ::android::base::utf8::unlink(path5_out.c_str());
ASSERT_NE(TestCompile(path5, kResDir, /** legacy */ false, diag), 0);
- ASSERT_NE(remove(path5_out.c_str()), 0);
+ ASSERT_NE(::android::base::utf8::unlink(path5_out.c_str()), 0);
ASSERT_EQ(TestCompile(path5, kResDir, /** legacy */ true, diag), 0);
- ASSERT_EQ(remove(path5_out.c_str()), 0);
+ ASSERT_EQ(::android::base::utf8::unlink(path5_out.c_str()), 0);
}
TEST(CompilerTest, DirInput) {
StdErrDiagnostics diag;
std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
- const std::string kResDir = android::base::Dirname(android::base::GetExecutablePath())
- + "/integration-tests/CompileTest/DirInput/res";
- const std::string kOutputFlata = android::base::Dirname(android::base::GetExecutablePath())
- + "/integration-tests/CompileTest/DirInput/compiled.flata";
- remove(kOutputFlata.c_str());
+ const std::string kResDir = BuildPath({android::base::Dirname(android::base::GetExecutablePath()),
+ "integration-tests", "CompileTest", "DirInput", "res"});
+ const std::string kOutputFlata =
+ BuildPath({android::base::Dirname(android::base::GetExecutablePath()), "integration-tests",
+ "CompileTest", "DirInput", "compiled.flata"});
+ ::android::base::utf8::unlink(kOutputFlata.c_str());
std::vector<android::StringPiece> args;
args.push_back("--dir");
args.push_back(kResDir);
args.push_back("-o");
args.push_back(kOutputFlata);
+ args.push_back("-v");
ASSERT_EQ(CompileCommand(&diag).Execute(args, &std::cerr), 0);
- // Check for the presence of the compiled files
- std::string err;
- std::unique_ptr<io::ZipFileCollection> zip = io::ZipFileCollection::Create(kOutputFlata, &err);
- ASSERT_NE(zip, nullptr) << err;
- ASSERT_NE(zip->FindFile("drawable_image.png.flat"), nullptr);
- ASSERT_NE(zip->FindFile("layout_layout.xml.flat"), nullptr);
- ASSERT_NE(zip->FindFile("values_values.arsc.flat"), nullptr);
- ASSERT_EQ(remove(kOutputFlata.c_str()), 0);
+ {
+ // Check for the presence of the compiled files
+ std::string err;
+ std::unique_ptr<io::ZipFileCollection> zip = io::ZipFileCollection::Create(kOutputFlata, &err);
+ ASSERT_NE(zip, nullptr) << err;
+ ASSERT_NE(zip->FindFile("drawable_image.png.flat"), nullptr);
+ ASSERT_NE(zip->FindFile("layout_layout.xml.flat"), nullptr);
+ ASSERT_NE(zip->FindFile("values_values.arsc.flat"), nullptr);
+ }
+ ASSERT_EQ(::android::base::utf8::unlink(kOutputFlata.c_str()), 0);
}
TEST(CompilerTest, ZipInput) {
StdErrDiagnostics diag;
std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
- const std::string kResZip = android::base::Dirname(android::base::GetExecutablePath())
- + "/integration-tests/CompileTest/ZipInput/res.zip";
- const std::string kOutputFlata = android::base::Dirname(android::base::GetExecutablePath())
- + "/integration-tests/CompileTest/ZipInput/compiled.flata";
- remove(kOutputFlata.c_str());
+ const std::string kResZip =
+ BuildPath({android::base::Dirname(android::base::GetExecutablePath()), "integration-tests",
+ "CompileTest", "ZipInput", "res.zip"});
+ const std::string kOutputFlata =
+ BuildPath({android::base::Dirname(android::base::GetExecutablePath()), "integration-tests",
+ "CompileTest", "ZipInput", "compiled.flata"});
+
+ ::android::base::utf8::unlink(kOutputFlata.c_str());
std::vector<android::StringPiece> args;
args.push_back("--zip");
@@ -136,14 +157,16 @@
args.push_back(kOutputFlata);
ASSERT_EQ(CompileCommand(&diag).Execute(args, &std::cerr), 0);
- // Check for the presence of the compiled files
- std::string err;
- std::unique_ptr<io::ZipFileCollection> zip = io::ZipFileCollection::Create(kOutputFlata, &err);
- ASSERT_NE(zip, nullptr) << err;
- ASSERT_NE(zip->FindFile("drawable_image.png.flat"), nullptr);
- ASSERT_NE(zip->FindFile("layout_layout.xml.flat"), nullptr);
- ASSERT_NE(zip->FindFile("values_values.arsc.flat"), nullptr);
- ASSERT_EQ(remove(kOutputFlata.c_str()), 0);
+ {
+ // Check for the presence of the compiled files
+ std::string err;
+ std::unique_ptr<io::ZipFileCollection> zip = io::ZipFileCollection::Create(kOutputFlata, &err);
+ ASSERT_NE(zip, nullptr) << err;
+ ASSERT_NE(zip->FindFile("drawable_image.png.flat"), nullptr);
+ ASSERT_NE(zip->FindFile("layout_layout.xml.flat"), nullptr);
+ ASSERT_NE(zip->FindFile("values_values.arsc.flat"), nullptr);
+ }
+ ASSERT_EQ(::android::base::utf8::unlink(kOutputFlata.c_str()), 0);
}
-} // namespace aapt
\ No newline at end of file
+} // namespace aapt
\ No newline at end of file
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index 13c1047..20ea3cb6 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -1260,7 +1260,7 @@
return false;
}
- proguard::WriteKeepSet(keep_set, &fout);
+ proguard::WriteKeepSet(keep_set, &fout, options_.generate_minimal_proguard_rules);
fout.Flush();
if (fout.HadError()) {
diff --git a/tools/aapt2/cmd/Link.h b/tools/aapt2/cmd/Link.h
index e58a93e..a42c0f2 100644
--- a/tools/aapt2/cmd/Link.h
+++ b/tools/aapt2/cmd/Link.h
@@ -49,6 +49,7 @@
Maybe<std::string> generate_proguard_rules_path;
Maybe<std::string> generate_main_dex_proguard_rules_path;
bool generate_conditional_proguard_rules = false;
+ bool generate_minimal_proguard_rules = false;
bool generate_non_final_ids = false;
std::vector<std::string> javadoc_annotations;
Maybe<std::string> private_symbols;
@@ -119,6 +120,9 @@
AddOptionalSwitch("--proguard-conditional-keep-rules",
"Generate conditional Proguard keep rules.",
&options_.generate_conditional_proguard_rules);
+ AddOptionalSwitch("--proguard-minimal-keep-rules",
+ "Generate a minimal set of Proguard keep rules.",
+ &options_.generate_minimal_proguard_rules);
AddOptionalSwitch("--no-auto-version", "Disables automatic style and layout SDK versioning.",
&options_.no_auto_version);
AddOptionalSwitch("--no-version-vectors",
diff --git a/tools/aapt2/cmd/Util_test.cpp b/tools/aapt2/cmd/Util_test.cpp
index 158ef29..6aeff08 100644
--- a/tools/aapt2/cmd/Util_test.cpp
+++ b/tools/aapt2/cmd/Util_test.cpp
@@ -16,12 +16,22 @@
#include "Util.h"
+#include "android-base/stringprintf.h"
+
#include "AppInfo.h"
#include "split/TableSplitter.h"
#include "test/Builders.h"
#include "test/Test.h"
+#include "util/Files.h"
namespace aapt {
+
+#ifdef _WIN32
+#define CREATE_PATH(path) android::base::StringPrintf(";%s", path)
+#else
+#define CREATE_PATH(path) android::base::StringPrintf(":%s", path)
+#endif
+
#define EXPECT_CONFIG_EQ(constraints, config) \
EXPECT_EQ(constraints.configs.size(), 1); \
EXPECT_EQ(*constraints.configs.begin(), config); \
@@ -89,7 +99,7 @@
}
-TEST (UtilTest, ParseSplitParameter) {
+TEST (UtilTest, ParseSplitParameters) {
IDiagnostics* diagnostics = test::ContextBuilder().Build().get()->GetDiagnostics();
std::string path;
SplitConstraints constraints;
@@ -98,14 +108,14 @@
// ========== Test IMSI ==========
// mcc: 'mcc[0-9]{3}'
// mnc: 'mnc[0-9]{1,3}'
- ASSERT_TRUE(ParseSplitParameter(":mcc310",
+ ASSERT_TRUE(ParseSplitParameter(CREATE_PATH("mcc310"),
diagnostics, &path, &constraints));
expected_configuration = test::ConfigDescriptionBuilder()
.setMcc(0x0136)
.Build();
EXPECT_CONFIG_EQ(constraints, expected_configuration);
- ASSERT_TRUE(ParseSplitParameter(":mcc310-mnc004",
+ ASSERT_TRUE(ParseSplitParameter(CREATE_PATH("mcc310-mnc004"),
diagnostics, &path, &constraints));
expected_configuration = test::ConfigDescriptionBuilder()
.setMcc(0x0136)
@@ -113,7 +123,7 @@
.Build();
EXPECT_CONFIG_EQ(constraints, expected_configuration);
- ASSERT_TRUE(ParseSplitParameter(":mcc310-mnc000",
+ ASSERT_TRUE(ParseSplitParameter(CREATE_PATH("mcc310-mnc000"),
diagnostics, &path, &constraints));
expected_configuration = test::ConfigDescriptionBuilder()
.setMcc(0x0136)
@@ -124,14 +134,14 @@
// ========== Test LOCALE ==========
// locale: '[a-z]{2,3}(-r[a-z]{2})?'
// locale: 'b+[a-z]{2,3}(+[a-z[0-9]]{2})?'
- ASSERT_TRUE(ParseSplitParameter(":es",
+ ASSERT_TRUE(ParseSplitParameter(CREATE_PATH("es"),
diagnostics, &path, &constraints));
expected_configuration = test::ConfigDescriptionBuilder()
.setLanguage(0x6573)
.Build();
EXPECT_CONFIG_EQ(constraints, expected_configuration);
- ASSERT_TRUE(ParseSplitParameter(":fr-rCA",
+ ASSERT_TRUE(ParseSplitParameter(CREATE_PATH("fr-rCA"),
diagnostics, &path, &constraints));
expected_configuration = test::ConfigDescriptionBuilder()
.setLanguage(0x6672)
@@ -139,7 +149,7 @@
.Build();
EXPECT_CONFIG_EQ(constraints, expected_configuration);
- ASSERT_TRUE(ParseSplitParameter(":b+es+419",
+ ASSERT_TRUE(ParseSplitParameter(CREATE_PATH("b+es+419"),
diagnostics, &path, &constraints));
expected_configuration = test::ConfigDescriptionBuilder()
.setLanguage(0x6573)
@@ -151,21 +161,21 @@
// orientation: '(port|land|square)'
// touchscreen: '(notouch|stylus|finger)'
// density" '(anydpi|nodpi|ldpi|mdpi|tvdpi|hdpi|xhdpi|xxhdpi|xxxhdpi|[0-9]*dpi)'
- ASSERT_TRUE(ParseSplitParameter(":square",
+ ASSERT_TRUE(ParseSplitParameter(CREATE_PATH("square"),
diagnostics, &path, &constraints));
expected_configuration = test::ConfigDescriptionBuilder()
.setOrientation(0x03)
.Build();
EXPECT_CONFIG_EQ(constraints, expected_configuration);
- ASSERT_TRUE(ParseSplitParameter(":stylus",
+ ASSERT_TRUE(ParseSplitParameter(CREATE_PATH("stylus"),
diagnostics, &path, &constraints));
expected_configuration = test::ConfigDescriptionBuilder()
.setTouchscreen(0x02)
.Build();
EXPECT_CONFIG_EQ(constraints, expected_configuration);
- ASSERT_TRUE(ParseSplitParameter(":xxxhdpi",
+ ASSERT_TRUE(ParseSplitParameter(CREATE_PATH("xxxhdpi"),
diagnostics, &path, &constraints));
expected_configuration = test::ConfigDescriptionBuilder()
.setDensity(0x0280)
@@ -173,7 +183,7 @@
.Build();
EXPECT_CONFIG_EQ(constraints, expected_configuration);
- ASSERT_TRUE(ParseSplitParameter(":land-xhdpi-finger",
+ ASSERT_TRUE(ParseSplitParameter(CREATE_PATH("land-xhdpi-finger"),
diagnostics, &path, &constraints));
expected_configuration = test::ConfigDescriptionBuilder()
.setOrientation(0x02)
@@ -188,28 +198,28 @@
// navigation: '(nonav|dpad|trackball|wheel)'
// inputFlags: '(keysexposed|keyshidden|keyssoft)'
// inputFlags: '(navexposed|navhidden)'
- ASSERT_TRUE(ParseSplitParameter(":qwerty",
+ ASSERT_TRUE(ParseSplitParameter(CREATE_PATH("qwerty"),
diagnostics, &path, &constraints));
expected_configuration = test::ConfigDescriptionBuilder()
.setKeyboard(0x02)
.Build();
EXPECT_CONFIG_EQ(constraints, expected_configuration);
- ASSERT_TRUE(ParseSplitParameter(":dpad",
+ ASSERT_TRUE(ParseSplitParameter(CREATE_PATH("dpad"),
diagnostics, &path, &constraints));
expected_configuration = test::ConfigDescriptionBuilder()
.setNavigation(0x02)
.Build();
EXPECT_CONFIG_EQ(constraints, expected_configuration);
- ASSERT_TRUE(ParseSplitParameter(":keyssoft-navhidden",
+ ASSERT_TRUE(ParseSplitParameter(CREATE_PATH("keyssoft-navhidden"),
diagnostics, &path, &constraints));
expected_configuration = test::ConfigDescriptionBuilder()
.setInputFlags(0x0B)
.Build();
EXPECT_CONFIG_EQ(constraints, expected_configuration);
- ASSERT_TRUE(ParseSplitParameter(":keyshidden-nokeys-navexposed-trackball",
+ ASSERT_TRUE(ParseSplitParameter(CREATE_PATH("keyshidden-nokeys-navexposed-trackball"),
diagnostics, &path, &constraints));
expected_configuration = test::ConfigDescriptionBuilder()
.setKeyboard(0x01)
@@ -220,7 +230,7 @@
// ========== Test SCREEN_SIZE ==========
// screenWidth/screenHeight: '[0-9]+x[0-9]+'
- ASSERT_TRUE(ParseSplitParameter(":1920x1080",
+ ASSERT_TRUE(ParseSplitParameter(CREATE_PATH("1920x1080"),
diagnostics, &path, &constraints));
expected_configuration = test::ConfigDescriptionBuilder()
.setScreenWidth(0x0780)
@@ -238,14 +248,14 @@
// uiMode [type]: '(desk|car|television|appliance|watch|vrheadset)'
// uiMode [night]: '(night|notnight)'
// smallestScreenWidthDp: 'sw[0-9]dp'
- ASSERT_TRUE(ParseSplitParameter(":ldrtl",
+ ASSERT_TRUE(ParseSplitParameter(CREATE_PATH("ldrtl"),
diagnostics, &path, &constraints));
expected_configuration = test::ConfigDescriptionBuilder()
.setScreenLayout(0x80)
.Build();
EXPECT_CONFIG_EQ(constraints, expected_configuration);
- ASSERT_TRUE(ParseSplitParameter(":small",
+ ASSERT_TRUE(ParseSplitParameter(CREATE_PATH("small"),
diagnostics, &path, &constraints));
expected_configuration = test::ConfigDescriptionBuilder()
.setScreenLayout(0x01)
@@ -253,7 +263,7 @@
.Build();
EXPECT_CONFIG_EQ(constraints, expected_configuration);
- ASSERT_TRUE(ParseSplitParameter(":notlong",
+ ASSERT_TRUE(ParseSplitParameter(CREATE_PATH("notlong"),
diagnostics, &path, &constraints));
expected_configuration = test::ConfigDescriptionBuilder()
.setScreenLayout(0x10)
@@ -261,15 +271,15 @@
.Build();
EXPECT_CONFIG_EQ(constraints, expected_configuration);
- ASSERT_TRUE(ParseSplitParameter(":ldltr-normal-long",
- diagnostics, &path, &constraints));
+ ASSERT_TRUE(ParseSplitParameter(CREATE_PATH("ldltr-normal-long"),
+ diagnostics, &path, &constraints));
expected_configuration = test::ConfigDescriptionBuilder()
.setScreenLayout(0x62)
.setSdkVersion(0x0004) // screenLayout (size|long) requires donut
.Build();
EXPECT_CONFIG_EQ(constraints, expected_configuration);
- ASSERT_TRUE(ParseSplitParameter(":car",
+ ASSERT_TRUE(ParseSplitParameter(CREATE_PATH("car"),
diagnostics, &path, &constraints));
expected_configuration = test::ConfigDescriptionBuilder()
.setUiMode(0x03)
@@ -277,7 +287,7 @@
.Build();
EXPECT_CONFIG_EQ(constraints, expected_configuration);
- ASSERT_TRUE(ParseSplitParameter(":vrheadset",
+ ASSERT_TRUE(ParseSplitParameter(CREATE_PATH("vrheadset"),
diagnostics, &path, &constraints));
expected_configuration = test::ConfigDescriptionBuilder()
.setUiMode(0x07)
@@ -285,7 +295,7 @@
.Build();
EXPECT_CONFIG_EQ(constraints, expected_configuration);
- ASSERT_TRUE(ParseSplitParameter(":television-night",
+ ASSERT_TRUE(ParseSplitParameter(CREATE_PATH("television-night"),
diagnostics, &path, &constraints));
expected_configuration = test::ConfigDescriptionBuilder()
.setUiMode(0x24)
@@ -293,7 +303,7 @@
.Build();
EXPECT_CONFIG_EQ(constraints, expected_configuration);
- ASSERT_TRUE(ParseSplitParameter(":sw1920dp",
+ ASSERT_TRUE(ParseSplitParameter(CREATE_PATH("sw1920dp"),
diagnostics, &path, &constraints));
expected_configuration = test::ConfigDescriptionBuilder()
.setSmallestScreenWidthDp(0x0780)
@@ -304,7 +314,7 @@
// ========== Test SCREEN_SIZE_DP ==========
// screenWidthDp: 'w[0-9]dp'
// screenHeightDp: 'h[0-9]dp'
- ASSERT_TRUE(ParseSplitParameter(":w1920dp",
+ ASSERT_TRUE(ParseSplitParameter(CREATE_PATH("w1920dp"),
diagnostics, &path, &constraints));
expected_configuration = test::ConfigDescriptionBuilder()
.setScreenWidthDp(0x0780)
@@ -312,7 +322,7 @@
.Build();
EXPECT_CONFIG_EQ(constraints, expected_configuration);
- ASSERT_TRUE(ParseSplitParameter(":h1080dp",
+ ASSERT_TRUE(ParseSplitParameter(CREATE_PATH("h1080dp"),
diagnostics, &path, &constraints));
expected_configuration = test::ConfigDescriptionBuilder()
.setScreenHeightDp(0x0438)
@@ -324,7 +334,7 @@
// screenLayout2: '(round|notround)'
// colorMode: '(widecg|nowidecg)'
// colorMode: '(highhdr|lowdr)'
- ASSERT_TRUE(ParseSplitParameter(":round",
+ ASSERT_TRUE(ParseSplitParameter(CREATE_PATH("round"),
diagnostics, &path, &constraints));
expected_configuration = test::ConfigDescriptionBuilder()
.setScreenLayout2(0x02)
@@ -332,7 +342,7 @@
.Build();
EXPECT_CONFIG_EQ(constraints, expected_configuration);
- ASSERT_TRUE(ParseSplitParameter(":widecg-highdr",
+ ASSERT_TRUE(ParseSplitParameter(CREATE_PATH("widecg-highdr"),
diagnostics, &path, &constraints));
expected_configuration = test::ConfigDescriptionBuilder()
.setColorMode(0x0A)
@@ -349,10 +359,10 @@
std::string path;
test_constraints.push_back({});
- ASSERT_TRUE(ParseSplitParameter(":v7",
+ ASSERT_TRUE(ParseSplitParameter(CREATE_PATH("v7"),
diagnostics, &path, &test_constraints.back()));
test_constraints.push_back({});
- ASSERT_TRUE(ParseSplitParameter(":xhdpi",
+ ASSERT_TRUE(ParseSplitParameter(CREATE_PATH("xhdpi"),
diagnostics, &path, &test_constraints.back()));
EXPECT_EQ(test_constraints.size(), 2);
EXPECT_EQ(test_constraints[0].name, "v7");
diff --git a/tools/aapt2/dump/DumpManifest.cpp b/tools/aapt2/dump/DumpManifest.cpp
index 2c356d1..b6a984f 100644
--- a/tools/aapt2/dump/DumpManifest.cpp
+++ b/tools/aapt2/dump/DumpManifest.cpp
@@ -16,6 +16,8 @@
#include "DumpManifest.h"
+#include <algorithm>
+
#include "LoadedApk.h"
#include "SdkConstants.h"
#include "ValueVisitor.h"
@@ -70,10 +72,14 @@
CATEGORY_ATTR = 0x010103e8,
BANNER_ATTR = 0x10103f2,
ISGAME_ATTR = 0x10103f4,
+ VERSION_ATTR = 0x01010519,
+ CERT_DIGEST_ATTR = 0x01010548,
REQUIRED_FEATURE_ATTR = 0x1010557,
REQUIRED_NOT_FEATURE_ATTR = 0x1010558,
COMPILE_SDK_VERSION_ATTR = 0x01010572,
COMPILE_SDK_VERSION_CODENAME_ATTR = 0x01010573,
+ VERSION_MAJOR_ATTR = 0x01010577,
+ PACKAGE_TYPE_ATTR = 0x01010587,
};
const std::string& kAndroidNamespace = "http://schemas.android.com/apk/res/android";
@@ -1318,6 +1324,70 @@
}
};
+/** Represents <static-library> elements. **/
+class StaticLibrary : public ManifestExtractor::Element {
+ public:
+ StaticLibrary() = default;
+ std::string name;
+ int version;
+ int versionMajor;
+
+ void Extract(xml::Element* element) override {
+ auto parent_stack = extractor()->parent_stack();
+ if (parent_stack.size() > 0 && ElementCast<Application>(parent_stack[0])) {
+ name = GetAttributeStringDefault(FindAttribute(element, NAME_ATTR), "");
+ version = GetAttributeIntegerDefault(FindAttribute(element, VERSION_ATTR), 0);
+ versionMajor = GetAttributeIntegerDefault(FindAttribute(element, VERSION_MAJOR_ATTR), 0);
+ }
+ }
+
+ void Print(text::Printer& printer) override {
+ printer.Print(StringPrintf(
+ "static-library: name='%s' version='%d' versionMajor='%d'\n",
+ name.data(), version, versionMajor));
+ }
+};
+
+/** Represents <uses-static-library> elements. **/
+class UsesStaticLibrary : public ManifestExtractor::Element {
+ public:
+ UsesStaticLibrary() = default;
+ std::string name;
+ int version;
+ int versionMajor;
+ std::vector<std::string> certDigests;
+
+ void Extract(xml::Element* element) override {
+ auto parent_stack = extractor()->parent_stack();
+ if (parent_stack.size() > 0 && ElementCast<Application>(parent_stack[0])) {
+ name = GetAttributeStringDefault(FindAttribute(element, NAME_ATTR), "");
+ version = GetAttributeIntegerDefault(FindAttribute(element, VERSION_ATTR), 0);
+ versionMajor = GetAttributeIntegerDefault(FindAttribute(element, VERSION_MAJOR_ATTR), 0);
+ AddCertDigest(element);
+ }
+ }
+
+ void AddCertDigest(xml::Element* element) {
+ std::string digest = GetAttributeStringDefault(FindAttribute(element, CERT_DIGEST_ATTR), "");
+ // We allow ":" delimiters in the SHA declaration as this is the format
+ // emitted by the certtool making it easy for developers to copy/paste.
+ digest.erase(std::remove(digest.begin(), digest.end(), ':'), digest.end());
+ if (!digest.empty()) {
+ certDigests.push_back(digest);
+ }
+ }
+
+ void Print(text::Printer& printer) override {
+ printer.Print(StringPrintf(
+ "uses-static-library: name='%s' version='%d' versionMajor='%d'",
+ name.data(), version, versionMajor));
+ for (size_t i = 0; i < certDigests.size(); i++) {
+ printer.Print(StringPrintf(" certDigest='%s'", certDigests[i].data()));
+ }
+ printer.Print("\n");
+ }
+};
+
/**
* Represents <meta-data> elements. These tags are only printed when a flag is passed in to
* explicitly enable meta data printing.
@@ -1326,29 +1396,29 @@
public:
MetaData() = default;
std::string name;
- const std::string* value;
+ std::string value;
const int* value_int;
- const std::string* resource;
+ std::string resource;
const int* resource_int;
void Extract(xml::Element* element) override {
name = GetAttributeStringDefault(FindAttribute(element, NAME_ATTR), "");
- value = GetAttributeString(FindAttribute(element, VALUE_ATTR));
+ value = GetAttributeStringDefault(FindAttribute(element, VALUE_ATTR), "");
value_int = GetAttributeInteger(FindAttribute(element, VALUE_ATTR));
- resource = GetAttributeString(FindAttribute(element, RESOURCE_ATTR));
+ resource = GetAttributeStringDefault(FindAttribute(element, RESOURCE_ATTR), "");
resource_int = GetAttributeInteger(FindAttribute(element, RESOURCE_ATTR));
}
void Print(text::Printer& printer) override {
if (extractor()->options_.include_meta_data && !name.empty()) {
printer.Print(StringPrintf("meta-data: name='%s' ", name.data()));
- if (value) {
- printer.Print(StringPrintf("value='%s' ", value->data()));
+ if (!value.empty()) {
+ printer.Print(StringPrintf("value='%s' ", value.data()));
} else if (value_int) {
printer.Print(StringPrintf("value='%d' ", *value_int));
} else {
- if (resource) {
- printer.Print(StringPrintf("resource='%s' ", resource->data()));
+ if (!resource.empty()) {
+ printer.Print(StringPrintf("resource='%s' ", resource.data()));
} else if (resource_int) {
printer.Print(StringPrintf("resource='%d' ", *resource_int));
}
@@ -1544,15 +1614,65 @@
class UsesPackage : public ManifestExtractor::Element {
public:
UsesPackage() = default;
+ const std::string* packageType = nullptr;
const std::string* name = nullptr;
+ int version;
+ int versionMajor;
+ std::vector<std::string> certDigests;
void Extract(xml::Element* element) override {
- name = GetAttributeString(FindAttribute(element, NAME_ATTR));
+ auto parent_stack = extractor()->parent_stack();
+ if (parent_stack.size() > 0 && ElementCast<Application>(parent_stack[0])) {
+ packageType = GetAttributeString(FindAttribute(element, PACKAGE_TYPE_ATTR));
+ name = GetAttributeString(FindAttribute(element, NAME_ATTR));
+ version = GetAttributeIntegerDefault(FindAttribute(element, VERSION_ATTR), 0);
+ versionMajor = GetAttributeIntegerDefault(FindAttribute(element, VERSION_MAJOR_ATTR), 0);
+ AddCertDigest(element);
+ }
+ }
+
+ void AddCertDigest(xml::Element* element) {
+ std::string digest = GetAttributeStringDefault(FindAttribute(element, CERT_DIGEST_ATTR), "");
+ // We allow ":" delimiters in the SHA declaration as this is the format
+ // emitted by the certtool making it easy for developers to copy/paste.
+ digest.erase(std::remove(digest.begin(), digest.end(), ':'), digest.end());
+ if (!digest.empty()) {
+ certDigests.push_back(digest);
+ }
}
void Print(text::Printer& printer) override {
if (name) {
- printer.Print(StringPrintf("uses-package:'%s'\n", name->data()));
+ if (packageType) {
+ printer.Print(StringPrintf(
+ "uses-typed-package: type='%s' name='%s' version='%d' versionMajor='%d'",
+ packageType->data(), name->data(), version, versionMajor));
+ for (size_t i = 0; i < certDigests.size(); i++) {
+ printer.Print(StringPrintf(" certDigest='%s'", certDigests[i].data()));
+ }
+ printer.Print("\n");
+ } else {
+ printer.Print(StringPrintf("uses-package:'%s'\n", name->data()));
+ }
+ }
+ }
+};
+
+/** Represents <additional-certificate> elements. **/
+class AdditionalCertificate : public ManifestExtractor::Element {
+ public:
+ AdditionalCertificate() = default;
+
+ void Extract(xml::Element* element) override {
+ auto parent_stack = extractor()->parent_stack();
+ if (parent_stack.size() > 0) {
+ if (ElementCast<UsesPackage>(parent_stack[0])) {
+ UsesPackage* uses = ElementCast<UsesPackage>(parent_stack[0]);
+ uses->AddCertDigest(element);
+ } else if (ElementCast<UsesStaticLibrary>(parent_stack[0])) {
+ UsesStaticLibrary* uses = ElementCast<UsesStaticLibrary>(parent_stack[0]);
+ uses->AddCertDigest(element);
+ }
}
}
};
@@ -1837,10 +1957,10 @@
&& offhost_apdu_action)) {
// Attempt to load the resource file
- if (!meta_data->resource) {
+ if (!meta_data->resource.empty()) {
return;
}
- auto resource = apk->LoadXml(*meta_data->resource, diag);
+ auto resource = apk->LoadXml(meta_data->resource, diag);
if (!resource) {
return;
}
@@ -2065,6 +2185,9 @@
{"uses-permission-sdk-23", std::is_base_of<UsesPermissionSdk23, T>::value},
{"uses-library", std::is_base_of<UsesLibrary, T>::value},
{"uses-package", std::is_base_of<UsesPackage, T>::value},
+ {"static-library", std::is_base_of<StaticLibrary, T>::value},
+ {"uses-static-library", std::is_base_of<UsesStaticLibrary, T>::value},
+ {"additional-certificate", std::is_base_of<AdditionalCertificate, T>::value},
{"uses-sdk", std::is_base_of<UsesSdkBadging, T>::value},
};
@@ -2110,7 +2233,10 @@
{"uses-permission", &CreateType<UsesPermission>},
{"uses-permission-sdk-23", &CreateType<UsesPermissionSdk23>},
{"uses-library", &CreateType<UsesLibrary>},
+ {"static-library", &CreateType<StaticLibrary>},
+ {"uses-static-library", &CreateType<UsesStaticLibrary>},
{"uses-package", &CreateType<UsesPackage>},
+ {"additional-certificate", &CreateType<AdditionalCertificate>},
{"uses-sdk", &CreateType<UsesSdkBadging>},
};
diff --git a/tools/aapt2/format/proto/ProtoSerialize_test.cpp b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
index 21fdbd8..74295ab 100644
--- a/tools/aapt2/format/proto/ProtoSerialize_test.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
@@ -33,6 +33,7 @@
public:
MOCK_METHOD1(FindFile, io::IFile*(const StringPiece& path));
MOCK_METHOD0(Iterator, std::unique_ptr<io::IFileCollectionIterator>());
+ MOCK_METHOD0(GetDirSeparator, char());
};
TEST(ProtoSerializeTest, SerializeSinglePackage) {
diff --git a/tools/aapt2/io/File.h b/tools/aapt2/io/File.h
index f06e28c..565aad6 100644
--- a/tools/aapt2/io/File.h
+++ b/tools/aapt2/io/File.h
@@ -25,6 +25,7 @@
#include "Source.h"
#include "io/Data.h"
+#include "util/Files.h"
#include "util/Util.h"
namespace aapt {
@@ -103,6 +104,7 @@
virtual IFile* FindFile(const android::StringPiece& path) = 0;
virtual std::unique_ptr<IFileCollectionIterator> Iterator() = 0;
+ virtual char GetDirSeparator() = 0;
};
} // namespace io
diff --git a/tools/aapt2/io/FileSystem.cpp b/tools/aapt2/io/FileSystem.cpp
index 16a20f4c..51cc903 100644
--- a/tools/aapt2/io/FileSystem.cpp
+++ b/tools/aapt2/io/FileSystem.cpp
@@ -128,5 +128,9 @@
return util::make_unique<FileCollectionIterator>(this);
}
+char FileCollection::GetDirSeparator() {
+ return file::sDirSep;
+}
+
} // namespace io
} // namespace aapt
diff --git a/tools/aapt2/io/FileSystem.h b/tools/aapt2/io/FileSystem.h
index fb6bf6e..04c6fa1 100644
--- a/tools/aapt2/io/FileSystem.h
+++ b/tools/aapt2/io/FileSystem.h
@@ -67,6 +67,7 @@
IFile* InsertFile(const android::StringPiece& path);
IFile* FindFile(const android::StringPiece& path) override;
std::unique_ptr<IFileCollectionIterator> Iterator() override;
+ char GetDirSeparator() override;
private:
DISALLOW_COPY_AND_ASSIGN(FileCollection);
diff --git a/tools/aapt2/io/ZipArchive.cpp b/tools/aapt2/io/ZipArchive.cpp
index 8e6d713..427dc92 100644
--- a/tools/aapt2/io/ZipArchive.cpp
+++ b/tools/aapt2/io/ZipArchive.cpp
@@ -33,6 +33,11 @@
: zip_handle_(handle), zip_entry_(entry), source_(source) {}
std::unique_ptr<IData> ZipFile::OpenAsData() {
+ // The file will fail to be mmaped if it is empty
+ if (zip_entry_.uncompressed_length == 0) {
+ return util::make_unique<EmptyData>();
+ }
+
if (zip_entry_.method == kCompressStored) {
int fd = GetFileDescriptor(zip_handle_);
@@ -154,6 +159,13 @@
return util::make_unique<ZipFileCollectionIterator>(this);
}
+char ZipFileCollection::GetDirSeparator() {
+ // According to the zip file specification, section 4.4.17.1:
+ // "All slashes MUST be forward slashes '/' as opposed to backwards slashes '\' for compatibility
+ // with Amiga and UNIX file systems etc."
+ return '/';
+}
+
ZipFileCollection::~ZipFileCollection() {
if (handle_) {
CloseArchive(handle_);
diff --git a/tools/aapt2/io/ZipArchive.h b/tools/aapt2/io/ZipArchive.h
index 8381259..b283e57 100644
--- a/tools/aapt2/io/ZipArchive.h
+++ b/tools/aapt2/io/ZipArchive.h
@@ -66,6 +66,7 @@
io::IFile* FindFile(const android::StringPiece& path) override;
std::unique_ptr<IFileCollectionIterator> Iterator() override;
+ char GetDirSeparator() override;
~ZipFileCollection() override;
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/java/ProguardRules.cpp b/tools/aapt2/java/ProguardRules.cpp
index d40795a..52e168e 100644
--- a/tools/aapt2/java/ProguardRules.cpp
+++ b/tools/aapt2/java/ProguardRules.cpp
@@ -384,7 +384,7 @@
return true;
}
-void WriteKeepSet(const KeepSet& keep_set, OutputStream* out) {
+void WriteKeepSet(const KeepSet& keep_set, OutputStream* out, bool minimal_keep) {
Printer printer(out);
for (const auto& entry : keep_set.manifest_class_set_) {
for (const UsageLocation& location : entry.second) {
@@ -406,15 +406,19 @@
printer.Print("-if class **.R$layout { int ")
.Print(JavaClassGenerator::TransformToFieldName(location.name.entry))
.Println("; }");
- printer.Print("-keep class ").Print(entry.first.name).Print(" { <init>(")
- .Print(entry.first.signature).Println("); }");
+
+ printer.Print("-keep class ").Print(entry.first.name).Print(" { <init>(");
+ printer.Print((minimal_keep) ? entry.first.signature : "...");
+ printer.Println("); }");
}
} else {
for (const UsageLocation& location : entry.second) {
printer.Print("# Referenced at ").Println(location.source.to_string());
}
- printer.Print("-keep class ").Print(entry.first.name).Print(" { <init>(")
- .Print(entry.first.signature).Println("); }");
+
+ printer.Print("-keep class ").Print(entry.first.name).Print(" { <init>(");
+ printer.Print((minimal_keep) ? entry.first.signature : "...");
+ printer.Println("); }");
}
printer.Println();
}
diff --git a/tools/aapt2/java/ProguardRules.h b/tools/aapt2/java/ProguardRules.h
index 01dad0b..38b4860 100644
--- a/tools/aapt2/java/ProguardRules.h
+++ b/tools/aapt2/java/ProguardRules.h
@@ -70,7 +70,7 @@
}
private:
- friend void WriteKeepSet(const KeepSet& keep_set, io::OutputStream* out);
+ friend void WriteKeepSet(const KeepSet& keep_set, io::OutputStream* out, bool minimal_keep);
friend bool CollectLocations(const UsageLocation& location, const KeepSet& keep_set,
std::set<UsageLocation>* locations);
@@ -89,7 +89,7 @@
bool CollectResourceReferences(IAaptContext* context, ResourceTable* table, KeepSet* keep_set);
-void WriteKeepSet(const KeepSet& keep_set, io::OutputStream* out);
+void WriteKeepSet(const KeepSet& keep_set, io::OutputStream* out, bool minimal_keep);
bool CollectLocations(const UsageLocation& location, const KeepSet& keep_set,
std::set<UsageLocation>* locations);
diff --git a/tools/aapt2/java/ProguardRules_test.cpp b/tools/aapt2/java/ProguardRules_test.cpp
index 83c72d8..3d93cb1 100644
--- a/tools/aapt2/java/ProguardRules_test.cpp
+++ b/tools/aapt2/java/ProguardRules_test.cpp
@@ -26,10 +26,10 @@
namespace aapt {
-std::string GetKeepSetString(const proguard::KeepSet& set) {
+std::string GetKeepSetString(const proguard::KeepSet& set, bool minimal_rules) {
std::string out;
StringOutputStream sout(&out);
- proguard::WriteKeepSet(set, &sout);
+ proguard::WriteKeepSet(set, &sout, minimal_rules);
sout.Flush();
return out;
}
@@ -53,8 +53,17 @@
proguard::KeepSet set;
ASSERT_TRUE(proguard::CollectProguardRulesForManifest(manifest.get(), &set, false));
- std::string actual = GetKeepSetString(set);
+ std::string actual = GetKeepSetString(set, /** minimal_rules */ false);
+ EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarAppComponentFactory { <init>(); }"));
+ EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarBackupAgent { <init>(); }"));
+ EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarApplication { <init>(); }"));
+ EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarActivity { <init>(); }"));
+ EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarService { <init>(); }"));
+ EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarReceiver { <init>(); }"));
+ EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarProvider { <init>(); }"));
+ EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarInstrumentation { <init>(); }"));
+ actual = GetKeepSetString(set, /** minimal_rules */ true);
EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarAppComponentFactory { <init>(); }"));
EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarBackupAgent { <init>(); }"));
EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarApplication { <init>(); }"));
@@ -75,8 +84,10 @@
proguard::KeepSet set;
ASSERT_TRUE(proguard::CollectProguardRules(context.get(), layout.get(), &set));
- std::string actual = GetKeepSetString(set);
+ std::string actual = GetKeepSetString(set, /** minimal_rules */ false);
+ EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(...); }"));
+ actual = GetKeepSetString(set, /** minimal_rules */ true);
EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(); }"));
}
@@ -89,8 +100,10 @@
proguard::KeepSet set;
ASSERT_TRUE(proguard::CollectProguardRules(context.get(), layout.get(), &set));
- std::string actual = GetKeepSetString(set);
+ std::string actual = GetKeepSetString(set, /** minimal_rules */ false);
+ EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(...); }"));
+ actual = GetKeepSetString(set, /** minimal_rules */ true);
EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(); }"));
}
@@ -105,8 +118,11 @@
proguard::KeepSet set;
ASSERT_TRUE(proguard::CollectProguardRules(context.get(), layout.get(), &set));
- std::string actual = GetKeepSetString(set);
+ std::string actual = GetKeepSetString(set, /** minimal_rules */ false);
+ EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(...); }"));
+ EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Baz { <init>(...); }"));
+ actual = GetKeepSetString(set, /** minimal_rules */ true);
EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(); }"));
EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Baz { <init>(); }"));
}
@@ -133,7 +149,12 @@
proguard::KeepSet set;
ASSERT_TRUE(proguard::CollectProguardRules(context.get(), navigation.get(), &set));
- std::string actual = GetKeepSetString(set);
+ std::string actual = GetKeepSetString(set, /** minimal_rules */ false);
+ EXPECT_THAT(actual, HasSubstr("-keep class com.package.Foo { <init>(...); }"));
+ EXPECT_THAT(actual, HasSubstr("-keep class com.package.Bar { <init>(...); }"));
+ EXPECT_THAT(actual, HasSubstr("-keep class com.base.Nested { <init>(...); }"));
+
+ actual = GetKeepSetString(set, /** minimal_rules */ true);
EXPECT_THAT(actual, HasSubstr("-keep class com.package.Foo { <init>(...); }"));
EXPECT_THAT(actual, HasSubstr("-keep class com.package.Bar { <init>(...); }"));
EXPECT_THAT(actual, HasSubstr("-keep class com.base.Nested { <init>(...); }"));
@@ -150,8 +171,10 @@
proguard::KeepSet set;
ASSERT_TRUE(proguard::CollectProguardRules(context.get(), layout.get(), &set));
- std::string actual = GetKeepSetString(set);
+ std::string actual = GetKeepSetString(set, /** minimal_rules */ false);
+ EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(...); }"));
+ actual = GetKeepSetString(set, /** minimal_rules */ true);
EXPECT_THAT(actual, HasSubstr(
"-keep class com.foo.Bar { <init>(android.content.Context, android.util.AttributeSet); }"));
}
@@ -188,11 +211,16 @@
ASSERT_TRUE(proguard::CollectProguardRules(context.get(), bar_layout.get(), &set));
ASSERT_TRUE(proguard::CollectProguardRules(context.get(), foo_layout.get(), &set));
- std::string actual = GetKeepSetString(set);
+ std::string actual = GetKeepSetString(set, /** minimal_rules */ false);
+ EXPECT_THAT(actual, HasSubstr("-if class **.R$layout"));
+ EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(...); }"));
+ EXPECT_THAT(actual, HasSubstr("int foo"));
+ EXPECT_THAT(actual, HasSubstr("int bar"));
+ actual = GetKeepSetString(set, /** minimal_rules */ true);
EXPECT_THAT(actual, HasSubstr("-if class **.R$layout"));
EXPECT_THAT(actual, HasSubstr(
- "-keep class com.foo.Bar { <init>(android.content.Context, android.util.AttributeSet); }"));
+ "-keep class com.foo.Bar { <init>(android.content.Context, android.util.AttributeSet); }"));
EXPECT_THAT(actual, HasSubstr("int foo"));
EXPECT_THAT(actual, HasSubstr("int bar"));
}
@@ -209,10 +237,16 @@
set.AddReference({test::ParseNameOrDie("layout/bar"), {}}, layout->file.name);
ASSERT_TRUE(proguard::CollectProguardRules(context.get(), layout.get(), &set));
- std::string actual = GetKeepSetString(set);
-
+ std::string actual = GetKeepSetString(set, /** minimal_rules */ false);
EXPECT_THAT(actual, HasSubstr(
- "-keep class com.foo.Bar { <init>(android.content.Context, android.util.AttributeSet); }"));
+ "-keep class com.foo.Bar { <init>(...); }"));
+ EXPECT_THAT(actual, HasSubstr("-if class **.R$layout"));
+ EXPECT_THAT(actual, HasSubstr("int foo"));
+ EXPECT_THAT(actual, HasSubstr("int bar"));
+
+ actual = GetKeepSetString(set, /** minimal_rules */ true);
+ EXPECT_THAT(actual, HasSubstr(
+ "-keep class com.foo.Bar { <init>(android.content.Context, android.util.AttributeSet); }"));
EXPECT_THAT(actual, HasSubstr("-if class **.R$layout"));
EXPECT_THAT(actual, HasSubstr("int foo"));
EXPECT_THAT(actual, HasSubstr("int bar"));
@@ -230,11 +264,14 @@
set.AddReference({test::ParseNameOrDie("style/MyStyle"), {}}, layout->file.name);
ASSERT_TRUE(proguard::CollectProguardRules(context.get(), layout.get(), &set));
- std::string actual = GetKeepSetString(set);
+ std::string actual = GetKeepSetString(set, /** minimal_rules */ false);
+ EXPECT_THAT(actual, Not(HasSubstr("-if")));
+ EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(...); }"));
+ actual = GetKeepSetString(set, /** minimal_rules */ true);
EXPECT_THAT(actual, Not(HasSubstr("-if")));
EXPECT_THAT(actual, HasSubstr(
- "-keep class com.foo.Bar { <init>(android.content.Context, android.util.AttributeSet); }"));
+ "-keep class com.foo.Bar { <init>(android.content.Context, android.util.AttributeSet); }"));
}
TEST(ProguardRulesTest, ViewOnClickRuleIsEmitted) {
@@ -247,10 +284,13 @@
proguard::KeepSet set;
ASSERT_TRUE(proguard::CollectProguardRules(context.get(), layout.get(), &set));
- std::string actual = GetKeepSetString(set);
-
+ std::string actual = GetKeepSetString(set, /** minimal_rules */ false);
EXPECT_THAT(actual, HasSubstr(
"-keepclassmembers class * { *** bar_method(android.view.View); }"));
+
+ actual = GetKeepSetString(set, /** minimal_rules */ true);
+ EXPECT_THAT(actual, HasSubstr(
+ "-keepclassmembers class * { *** bar_method(android.view.View); }"));
}
TEST(ProguardRulesTest, MenuRulesAreEmitted) {
@@ -267,10 +307,16 @@
proguard::KeepSet set;
ASSERT_TRUE(proguard::CollectProguardRules(context.get(), menu.get(), &set));
- std::string actual = GetKeepSetString(set);
-
+ std::string actual = GetKeepSetString(set, /** minimal_rules */ false);
EXPECT_THAT(actual, HasSubstr(
- "-keepclassmembers class * { *** on_click(android.view.MenuItem); }"));
+ "-keepclassmembers class * { *** on_click(android.view.MenuItem); }"));
+ EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(...); }"));
+ EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Baz { <init>(...); }"));
+ EXPECT_THAT(actual, Not(HasSubstr("com.foo.Bat")));
+
+ actual = GetKeepSetString(set, /** minimal_rules */ true);
+ EXPECT_THAT(actual, HasSubstr(
+ "-keepclassmembers class * { *** on_click(android.view.MenuItem); }"));
EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(android.content.Context); }"));
EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Baz { <init>(android.content.Context); }"));
EXPECT_THAT(actual, Not(HasSubstr("com.foo.Bat")));
@@ -287,10 +333,12 @@
proguard::KeepSet set;
ASSERT_TRUE(proguard::CollectProguardRules(context.get(), transition.get(), &set));
- std::string actual = GetKeepSetString(set);
+ std::string actual = GetKeepSetString(set, /** minimal_rules */ false);
+ EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(...); }"));
+ actual = GetKeepSetString(set, /** minimal_rules */ true);
EXPECT_THAT(actual, HasSubstr(
- "-keep class com.foo.Bar { <init>(android.content.Context, android.util.AttributeSet); }"));
+ "-keep class com.foo.Bar { <init>(android.content.Context, android.util.AttributeSet); }"));
}
TEST(ProguardRulesTest, TransitionRulesAreEmitted) {
@@ -304,10 +352,12 @@
proguard::KeepSet set;
ASSERT_TRUE(proguard::CollectProguardRules(context.get(), transitionSet.get(), &set));
- std::string actual = GetKeepSetString(set);
+ std::string actual = GetKeepSetString(set, /** minimal_rules */ false);
+ EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(...); }"));
+ actual = GetKeepSetString(set, /** minimal_rules */ true);
EXPECT_THAT(actual, HasSubstr(
- "-keep class com.foo.Bar { <init>(android.content.Context, android.util.AttributeSet); }"));
+ "-keep class com.foo.Bar { <init>(android.content.Context, android.util.AttributeSet); }"));
}
} // namespace aapt
diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp
index fa6538d..85bf6f2 100644
--- a/tools/aapt2/link/ManifestFixer.cpp
+++ b/tools/aapt2/link/ManifestFixer.cpp
@@ -393,6 +393,10 @@
uses_static_library_action.Action(RequiredAndroidAttribute("certDigest"));
uses_static_library_action["additional-certificate"];
+ xml::XmlNodeAction& uses_package_action = application_action["uses-package"];
+ uses_package_action.Action(RequiredNameIsJavaPackage);
+ uses_package_action["additional-certificate"];
+
if (options_.debug_mode) {
application_action.Action([&](xml::Element* el) -> bool {
xml::Attribute *attr = el->FindOrCreateAttribute(xml::kSchemaAndroid, "debuggable");
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/Files_test.cpp b/tools/aapt2/util/Files_test.cpp
index 219c183..202cc26 100644
--- a/tools/aapt2/util/Files_test.cpp
+++ b/tools/aapt2/util/Files_test.cpp
@@ -18,11 +18,21 @@
#include <sstream>
+#include "android-base/stringprintf.h"
+
#include "test/Test.h"
+using ::android::base::StringPrintf;
+
namespace aapt {
namespace file {
+#ifdef _WIN32
+constexpr const char sTestDirSep = '\\';
+#else
+constexpr const char sTestDirSep = '/';
+#endif
+
class FilesTest : public ::testing::Test {
public:
void SetUp() override {
@@ -42,16 +52,16 @@
}
TEST_F(FilesTest, AppendPathWithLeadingOrTrailingSeparators) {
- std::string base = "hello/";
+ std::string base = StringPrintf("hello%c", sTestDirSep);
AppendPath(&base, "there");
EXPECT_EQ(expected_path_, base);
base = "hello";
- AppendPath(&base, "/there");
+ AppendPath(&base, StringPrintf("%cthere", sTestDirSep));
EXPECT_EQ(expected_path_, base);
- base = "hello/";
- AppendPath(&base, "/there");
+ base = StringPrintf("hello%c", sTestDirSep);
+ AppendPath(&base, StringPrintf("%cthere", sTestDirSep));
EXPECT_EQ(expected_path_, base);
}
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/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..7472278 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
@@ -512,7 +513,7 @@
/**
* @hide
* Universal name for app creating the configuration
- * see {#link {@link PackageManager#getNameForUid(int)}
+ * see {@link PackageManager#getNameForUid(int)}
*/
@SystemApi
public String creatorName;
@@ -520,7 +521,7 @@
/**
* @hide
* Universal name for app updating the configuration
- * see {#link {@link PackageManager#getNameForUid(int)}
+ * see {@link PackageManager#getNameForUid(int)}
*/
@SystemApi
public String lastUpdateName;
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 {