Merge "[RenderScript] Remove @hide for setAutoPadding(boolean)."
diff --git a/Android.mk b/Android.mk
index 1df2af3..70e965b 100644
--- a/Android.mk
+++ b/Android.mk
@@ -45,6 +45,7 @@
core/java/android/content/EventLogTags.logtags \
core/java/android/speech/tts/EventLogTags.logtags \
core/java/android/webkit/EventLogTags.logtags \
+ core/java/com/android/internal/logging/EventLogTags.logtags \
## READ ME: ########################################################
##
@@ -244,6 +245,7 @@
core/java/android/view/accessibility/IAccessibilityManagerClient.aidl \
core/java/android/view/IApplicationToken.aidl \
core/java/android/view/IAssetAtlas.aidl \
+ core/java/android/view/IGraphicsStats.aidl \
core/java/android/view/IInputFilter.aidl \
core/java/android/view/IInputFilterHost.aidl \
core/java/android/view/IOnKeyguardExitResult.aidl \
diff --git a/api/current.txt b/api/current.txt
index 32b1d5e..eac30c1 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -304,8 +304,8 @@
field public static final int alphabeticShortcut = 16843235; // 0x10101e3
field public static final int alwaysDrawnWithCache = 16842991; // 0x10100ef
field public static final int alwaysRetainTaskState = 16843267; // 0x1010203
- field public static final int amPmBackgroundColor = 16843941; // 0x10104a5
- field public static final int amPmTextColor = 16843940; // 0x10104a4
+ field public static final deprecated int amPmBackgroundColor = 16843941; // 0x10104a5
+ field public static final deprecated int amPmTextColor = 16843940; // 0x10104a4
field public static final int ambientShadowAlpha = 16843966; // 0x10104be
field public static final int angle = 16843168; // 0x10101a0
field public static final int animateFirstView = 16843477; // 0x10102d5
@@ -330,6 +330,7 @@
field public static final int autoStart = 16843445; // 0x10102b5
field public static final deprecated int autoText = 16843114; // 0x101016a
field public static final int autoUrlDetect = 16843404; // 0x101028c
+ field public static final int autoVerify = 16844010; // 0x10104ea
field public static final int background = 16842964; // 0x10100d4
field public static final int backgroundDimAmount = 16842802; // 0x1010032
field public static final int backgroundDimEnabled = 16843295; // 0x101021f
@@ -651,13 +652,13 @@
field public static final int hapticFeedbackEnabled = 16843358; // 0x101025e
field public static final int hardwareAccelerated = 16843475; // 0x10102d3
field public static final int hasCode = 16842764; // 0x101000c
- field public static final int headerAmPmTextAppearance = 16843936; // 0x10104a0
+ field public static final deprecated int headerAmPmTextAppearance = 16843936; // 0x10104a0
field public static final int headerBackground = 16843055; // 0x101012f
field public static final deprecated int headerDayOfMonthTextAppearance = 16843927; // 0x1010497
field public static final int headerDividersEnabled = 16843310; // 0x101022e
field public static final deprecated int headerMonthTextAppearance = 16843926; // 0x1010496
- field public static final int headerTimeTextAppearance = 16843935; // 0x101049f
- field public static final int headerYearTextAppearance = 16843928; // 0x1010498
+ field public static final deprecated int headerTimeTextAppearance = 16843935; // 0x101049f
+ field public static final deprecated int headerYearTextAppearance = 16843928; // 0x1010498
field public static final int height = 16843093; // 0x1010155
field public static final int hideOnContentScroll = 16843843; // 0x1010443
field public static final int hint = 16843088; // 0x1010150
@@ -1361,6 +1362,7 @@
field public static final int useIntrinsicSizeAsMinimum = 16843536; // 0x1010310
field public static final int useLevel = 16843167; // 0x101019f
field public static final int userVisible = 16843409; // 0x1010291
+ field public static final int usesCleartextTraffic = 16844009; // 0x10104e9
field public static final int value = 16842788; // 0x1010024
field public static final int valueFrom = 16843486; // 0x10102de
field public static final int valueTo = 16843487; // 0x10102df
@@ -1455,7 +1457,7 @@
field public static final int x = 16842924; // 0x10100ac
field public static final int xlargeScreens = 16843455; // 0x10102bf
field public static final int y = 16842925; // 0x10100ad
- field public static final int yearListItemTextAppearance = 16843929; // 0x1010499
+ field public static final deprecated int yearListItemTextAppearance = 16843929; // 0x1010499
field public static final deprecated int yearListSelectorColor = 16843930; // 0x101049a
field public static final int yesNoPreferenceStyle = 16842896; // 0x1010090
field public static final int zAdjustment = 16843201; // 0x10101c1
@@ -2734,6 +2736,7 @@
}
public class AccountManager {
+ method public boolean accountAuthenticated(android.accounts.Account);
method public android.accounts.AccountManagerFuture<android.os.Bundle> addAccount(java.lang.String, java.lang.String, java.lang.String[], android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler);
method public boolean addAccountExplicitly(android.accounts.Account, java.lang.String, android.os.Bundle);
method public void addOnAccountsUpdatedListener(android.accounts.OnAccountsUpdateListener, android.os.Handler, boolean);
@@ -2794,6 +2797,7 @@
field public static final java.lang.String KEY_ERROR_CODE = "errorCode";
field public static final java.lang.String KEY_ERROR_MESSAGE = "errorMessage";
field public static final java.lang.String KEY_INTENT = "intent";
+ field public static final java.lang.String KEY_LAST_AUTHENTICATE_TIME_MILLIS_EPOCH = "lastAuthenticatedTimeMillisEpoch";
field public static final java.lang.String KEY_PASSWORD = "password";
field public static final java.lang.String KEY_USERDATA = "userdata";
field public static final java.lang.String LOGIN_ACCOUNTS_CHANGED_ACTION = "android.accounts.LOGIN_ACCOUNTS_CHANGED";
@@ -8252,6 +8256,8 @@
field public static final int NO_MATCH_CATEGORY = -4; // 0xfffffffc
field public static final int NO_MATCH_DATA = -2; // 0xfffffffe
field public static final int NO_MATCH_TYPE = -1; // 0xffffffff
+ field public static final java.lang.String SCHEME_HTTP = "http";
+ field public static final java.lang.String SCHEME_HTTPS = "https";
field public static final int SYSTEM_HIGH_PRIORITY = 1000; // 0x3e8
field public static final int SYSTEM_LOW_PRIORITY = -1000; // 0xfffffc18
}
@@ -8728,6 +8734,7 @@
field public static final int FLAG_SYSTEM = 1; // 0x1
field public static final int FLAG_TEST_ONLY = 256; // 0x100
field public static final int FLAG_UPDATED_SYSTEM_APP = 128; // 0x80
+ field public static final int FLAG_USES_CLEARTEXT_TRAFFIC = 134217728; // 0x8000000
field public static final int FLAG_VM_SAFE_MODE = 16384; // 0x4000
field public java.lang.String backupAgentName;
field public java.lang.String className;
@@ -8830,6 +8837,25 @@
field public java.lang.String targetPackage;
}
+ public final class IntentFilterVerificationInfo implements android.os.Parcelable {
+ ctor public IntentFilterVerificationInfo();
+ ctor public IntentFilterVerificationInfo(java.lang.String, java.lang.String[]);
+ ctor public IntentFilterVerificationInfo(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+ ctor public IntentFilterVerificationInfo(android.os.Parcel);
+ method public int describeContents();
+ method public java.lang.String[] getDomains();
+ method public java.lang.String getDomainsString();
+ method public java.lang.String getPackageName();
+ method public int getStatus();
+ method public java.lang.String getStatusString();
+ method public static java.lang.String getStatusStringFromValue(int);
+ method public void readFromXml(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+ method public void setStatus(int);
+ method public void writeToParcel(android.os.Parcel, int);
+ method public void writeToXml(org.xmlpull.v1.XmlSerializer) throws java.io.IOException;
+ field public static final android.os.Parcelable.Creator<android.content.pm.IntentFilterVerificationInfo> CREATOR;
+ }
+
public class LabeledIntent extends android.content.Intent {
ctor public LabeledIntent(android.content.Intent, java.lang.String, int, int);
ctor public LabeledIntent(android.content.Intent, java.lang.String, java.lang.CharSequence, int);
@@ -9058,6 +9084,7 @@
method public abstract java.util.List<android.content.pm.PackageInfo> getInstalledPackages(int);
method public abstract java.lang.String getInstallerPackageName(java.lang.String);
method public abstract android.content.pm.InstrumentationInfo getInstrumentationInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
+ method public abstract java.util.List<android.content.pm.IntentFilterVerificationInfo> getIntentFilterVerifications(java.lang.String);
method public abstract android.content.Intent getLaunchIntentForPackage(java.lang.String);
method public abstract android.content.Intent getLeanbackLaunchIntentForPackage(java.lang.String);
method public abstract java.lang.String getNameForUid(int);
@@ -9144,6 +9171,7 @@
field public static final java.lang.String FEATURE_LOCATION_NETWORK = "android.hardware.location.network";
field public static final java.lang.String FEATURE_MANAGED_USERS = "android.software.managed_users";
field public static final java.lang.String FEATURE_MICROPHONE = "android.hardware.microphone";
+ field public static final java.lang.String FEATURE_MIDI = "android.software.midi";
field public static final java.lang.String FEATURE_NFC = "android.hardware.nfc";
field public static final java.lang.String FEATURE_NFC_HOST_CARD_EMULATION = "android.hardware.nfc.hce";
field public static final java.lang.String FEATURE_OPENGLES_EXTENSION_PACK = "android.hardware.opengles.aep";
@@ -11135,7 +11163,9 @@
field public static final int JPEG = 256; // 0x100
field public static final int NV16 = 16; // 0x10
field public static final int NV21 = 17; // 0x11
+ field public static final int PRIVATE = 34; // 0x22
field public static final int RAW10 = 37; // 0x25
+ field public static final int RAW12 = 38; // 0x26
field public static final int RAW_SENSOR = 32; // 0x20
field public static final int RGB_565 = 4; // 0x4
field public static final int UNKNOWN = 0; // 0x0
@@ -22508,6 +22538,7 @@
field public static java.lang.String DIRECTORY_RINGTONES;
field public static final java.lang.String MEDIA_BAD_REMOVAL = "bad_removal";
field public static final java.lang.String MEDIA_CHECKING = "checking";
+ field public static final java.lang.String MEDIA_EJECTING = "ejecting";
field public static final java.lang.String MEDIA_MOUNTED = "mounted";
field public static final java.lang.String MEDIA_MOUNTED_READ_ONLY = "mounted_ro";
field public static final java.lang.String MEDIA_NOFS = "nofs";
@@ -26922,6 +26953,10 @@
method public static android.renderscript.Element A_8(android.renderscript.RenderScript);
method public static android.renderscript.Element BOOLEAN(android.renderscript.RenderScript);
method public static android.renderscript.Element ELEMENT(android.renderscript.RenderScript);
+ method public static android.renderscript.Element F16(android.renderscript.RenderScript);
+ method public static android.renderscript.Element F16_2(android.renderscript.RenderScript);
+ method public static android.renderscript.Element F16_3(android.renderscript.RenderScript);
+ method public static android.renderscript.Element F16_4(android.renderscript.RenderScript);
method public static android.renderscript.Element F32(android.renderscript.RenderScript);
method public static android.renderscript.Element F32_2(android.renderscript.RenderScript);
method public static android.renderscript.Element F32_3(android.renderscript.RenderScript);
@@ -27020,6 +27055,7 @@
method public static android.renderscript.Element.DataType valueOf(java.lang.String);
method public static final android.renderscript.Element.DataType[] values();
enum_constant public static final android.renderscript.Element.DataType BOOLEAN;
+ enum_constant public static final android.renderscript.Element.DataType FLOAT_16;
enum_constant public static final android.renderscript.Element.DataType FLOAT_32;
enum_constant public static final android.renderscript.Element.DataType FLOAT_64;
enum_constant public static final android.renderscript.Element.DataType MATRIX_2X2;
@@ -27293,11 +27329,14 @@
method public static android.renderscript.RenderScript create(android.content.Context);
method public static android.renderscript.RenderScript create(android.content.Context, android.renderscript.RenderScript.ContextType);
method public static android.renderscript.RenderScript create(android.content.Context, android.renderscript.RenderScript.ContextType, int);
+ method public static android.renderscript.RenderScript createMultiContext(android.content.Context, android.renderscript.RenderScript.ContextType, int, int);
method public void destroy();
method public void finish();
method public final android.content.Context getApplicationContext();
method public android.renderscript.RenderScript.RSErrorHandler getErrorHandler();
method public android.renderscript.RenderScript.RSMessageHandler getMessageHandler();
+ method public static long getMinorID();
+ method public static void releaseAllContexts();
method public void sendMessage(int, int[]);
method public void setErrorHandler(android.renderscript.RenderScript.RSErrorHandler);
method public void setMessageHandler(android.renderscript.RenderScript.RSMessageHandler);
@@ -27379,9 +27418,12 @@
public class Script extends android.renderscript.BaseObj {
method public void bindAllocation(android.renderscript.Allocation, int);
method protected android.renderscript.Script.FieldID createFieldID(int, android.renderscript.Element);
+ method protected android.renderscript.Script.InvokeID createInvokeID(int);
method protected android.renderscript.Script.KernelID createKernelID(int, int, android.renderscript.Element, android.renderscript.Element);
method protected void forEach(int, android.renderscript.Allocation, android.renderscript.Allocation, android.renderscript.FieldPacker);
method protected void forEach(int, android.renderscript.Allocation, android.renderscript.Allocation, android.renderscript.FieldPacker, android.renderscript.Script.LaunchOptions);
+ method protected void forEach(int, android.renderscript.Allocation[], android.renderscript.Allocation, android.renderscript.FieldPacker);
+ method protected void forEach(int, android.renderscript.Allocation[], android.renderscript.Allocation, android.renderscript.FieldPacker, android.renderscript.Script.LaunchOptions);
method public boolean getVarB(int);
method public double getVarD(int);
method public float getVarF(int);
@@ -27419,6 +27461,9 @@
public static final class Script.FieldID extends android.renderscript.BaseObj {
}
+ public static final class Script.InvokeID extends android.renderscript.BaseObj {
+ }
+
public static final class Script.KernelID extends android.renderscript.BaseObj {
}
@@ -27629,6 +27674,8 @@
method public static android.renderscript.Type createX(android.renderscript.RenderScript, android.renderscript.Element, int);
method public static android.renderscript.Type createXY(android.renderscript.RenderScript, android.renderscript.Element, int, int);
method public static android.renderscript.Type createXYZ(android.renderscript.RenderScript, android.renderscript.Element, int, int, int);
+ method public int getArray(int);
+ method public int getArrayCount();
method public int getCount();
method public android.renderscript.Element getElement();
method public int getX();
@@ -27642,6 +27689,7 @@
public static class Type.Builder {
ctor public Type.Builder(android.renderscript.RenderScript, android.renderscript.Element);
method public android.renderscript.Type create();
+ method public android.renderscript.Type.Builder setArray(int, int);
method public android.renderscript.Type.Builder setFaces(boolean);
method public android.renderscript.Type.Builder setMipmaps(boolean);
method public android.renderscript.Type.Builder setX(int);
@@ -27771,6 +27819,11 @@
method public android.security.KeyStoreParameter.Builder setEncryptionRequired(boolean);
}
+ public class NetworkSecurityPolicy {
+ method public static android.security.NetworkSecurityPolicy getInstance();
+ method public boolean isCleartextTrafficPermitted();
+ }
+
}
package android.service.carrier {
@@ -29789,6 +29842,7 @@
method public boolean setOperatorBrandOverride(java.lang.String);
method public boolean setPreferredNetworkTypeToGlobal();
method public boolean setVoiceMailNumber(java.lang.String, java.lang.String);
+ field public static final java.lang.String ACTION_EMERGENCY_ASSISTANCE = "android.telephony.action.EMERGENCY_ASSISTANCE";
field public static final java.lang.String ACTION_PHONE_STATE_CHANGED = "android.intent.action.PHONE_STATE";
field public static final java.lang.String ACTION_RESPOND_VIA_MESSAGE = "android.intent.action.RESPOND_VIA_MESSAGE";
field public static final int CALL_STATE_IDLE = 0; // 0x0
@@ -30460,6 +30514,7 @@
method public java.util.List<android.content.pm.PackageInfo> getInstalledPackages(int);
method public java.lang.String getInstallerPackageName(java.lang.String);
method public android.content.pm.InstrumentationInfo getInstrumentationInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
+ method public java.util.List<android.content.pm.IntentFilterVerificationInfo> getIntentFilterVerifications(java.lang.String);
method public android.content.Intent getLaunchIntentForPackage(java.lang.String);
method public android.content.Intent getLeanbackLaunchIntentForPackage(java.lang.String);
method public java.lang.String getNameForUid(int);
@@ -34238,8 +34293,10 @@
method public long getTimeDelta();
method public boolean isInProgress();
method public boolean isQuickScaleEnabled();
+ method public boolean isSecondaryButtonScaleEnabled();
method public boolean onTouchEvent(android.view.MotionEvent);
method public void setQuickScaleEnabled(boolean);
+ method public void setSecondaryButtonScaleEnabled(boolean);
}
public static abstract interface ScaleGestureDetector.OnScaleGestureListener {
@@ -37768,7 +37825,6 @@
method public boolean shouldOverrideUrlLoading(android.webkit.WebView, java.lang.String);
field public static final int ERROR_AUTHENTICATION = -4; // 0xfffffffc
field public static final int ERROR_BAD_URL = -12; // 0xfffffff4
- field public static final int ERROR_BLOCKED = -16; // 0xfffffff0
field public static final int ERROR_CONNECT = -6; // 0xfffffffa
field public static final int ERROR_FAILED_SSL_HANDSHAKE = -11; // 0xfffffff5
field public static final int ERROR_FILE = -13; // 0xfffffff3
@@ -38131,7 +38187,7 @@
field protected android.database.Cursor mDataCursor;
}
- public class AnalogClock extends android.view.View {
+ public deprecated class AnalogClock extends android.view.View {
ctor public AnalogClock(android.content.Context);
ctor public AnalogClock(android.content.Context, android.util.AttributeSet);
ctor public AnalogClock(android.content.Context, android.util.AttributeSet, int);
@@ -40192,12 +40248,16 @@
ctor public TimePicker(android.content.Context, android.util.AttributeSet);
ctor public TimePicker(android.content.Context, android.util.AttributeSet, int);
ctor public TimePicker(android.content.Context, android.util.AttributeSet, int, int);
- method public java.lang.Integer getCurrentHour();
- method public java.lang.Integer getCurrentMinute();
+ method public deprecated java.lang.Integer getCurrentHour();
+ method public deprecated java.lang.Integer getCurrentMinute();
+ method public int getHour();
+ method public int getMinute();
method public boolean is24HourView();
- method public void setCurrentHour(java.lang.Integer);
- method public void setCurrentMinute(java.lang.Integer);
+ method public deprecated void setCurrentHour(java.lang.Integer);
+ method public deprecated void setCurrentMinute(java.lang.Integer);
+ method public void setHour(int);
method public void setIs24HourView(java.lang.Boolean);
+ method public void setMinute(int);
method public void setOnTimeChangedListener(android.widget.TimePicker.OnTimeChangedListener);
}
diff --git a/api/system-current.txt b/api/system-current.txt
index 2a10f0a..5e4c5f2 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -103,6 +103,7 @@
field public static final java.lang.String INSTALL_LOCATION_PROVIDER = "android.permission.INSTALL_LOCATION_PROVIDER";
field public static final java.lang.String INSTALL_PACKAGES = "android.permission.INSTALL_PACKAGES";
field public static final java.lang.String INSTALL_SHORTCUT = "com.android.launcher.permission.INSTALL_SHORTCUT";
+ field public static final java.lang.String INTENT_FILTER_VERIFICATION_AGENT = "android.permission.INTENT_FILTER_VERIFICATION_AGENT";
field public static final java.lang.String INTERACT_ACROSS_USERS = "android.permission.INTERACT_ACROSS_USERS";
field public static final java.lang.String INTERNAL_SYSTEM_WINDOW = "android.permission.INTERNAL_SYSTEM_WINDOW";
field public static final java.lang.String INTERNET = "android.permission.INTERNET";
@@ -375,8 +376,8 @@
field public static final int alphabeticShortcut = 16843235; // 0x10101e3
field public static final int alwaysDrawnWithCache = 16842991; // 0x10100ef
field public static final int alwaysRetainTaskState = 16843267; // 0x1010203
- field public static final int amPmBackgroundColor = 16843941; // 0x10104a5
- field public static final int amPmTextColor = 16843940; // 0x10104a4
+ field public static final deprecated int amPmBackgroundColor = 16843941; // 0x10104a5
+ field public static final deprecated int amPmTextColor = 16843940; // 0x10104a4
field public static final int ambientShadowAlpha = 16843966; // 0x10104be
field public static final int angle = 16843168; // 0x10101a0
field public static final int animateFirstView = 16843477; // 0x10102d5
@@ -401,6 +402,7 @@
field public static final int autoStart = 16843445; // 0x10102b5
field public static final deprecated int autoText = 16843114; // 0x101016a
field public static final int autoUrlDetect = 16843404; // 0x101028c
+ field public static final int autoVerify = 16844010; // 0x10104ea
field public static final int background = 16842964; // 0x10100d4
field public static final int backgroundDimAmount = 16842802; // 0x1010032
field public static final int backgroundDimEnabled = 16843295; // 0x101021f
@@ -722,13 +724,13 @@
field public static final int hapticFeedbackEnabled = 16843358; // 0x101025e
field public static final int hardwareAccelerated = 16843475; // 0x10102d3
field public static final int hasCode = 16842764; // 0x101000c
- field public static final int headerAmPmTextAppearance = 16843936; // 0x10104a0
+ field public static final deprecated int headerAmPmTextAppearance = 16843936; // 0x10104a0
field public static final int headerBackground = 16843055; // 0x101012f
field public static final deprecated int headerDayOfMonthTextAppearance = 16843927; // 0x1010497
field public static final int headerDividersEnabled = 16843310; // 0x101022e
field public static final deprecated int headerMonthTextAppearance = 16843926; // 0x1010496
- field public static final int headerTimeTextAppearance = 16843935; // 0x101049f
- field public static final int headerYearTextAppearance = 16843928; // 0x1010498
+ field public static final deprecated int headerTimeTextAppearance = 16843935; // 0x101049f
+ field public static final deprecated int headerYearTextAppearance = 16843928; // 0x1010498
field public static final int height = 16843093; // 0x1010155
field public static final int hideOnContentScroll = 16843843; // 0x1010443
field public static final int hint = 16843088; // 0x1010150
@@ -1436,6 +1438,7 @@
field public static final int useIntrinsicSizeAsMinimum = 16843536; // 0x1010310
field public static final int useLevel = 16843167; // 0x101019f
field public static final int userVisible = 16843409; // 0x1010291
+ field public static final int usesCleartextTraffic = 16844009; // 0x10104e9
field public static final int value = 16842788; // 0x1010024
field public static final int valueFrom = 16843486; // 0x10102de
field public static final int valueTo = 16843487; // 0x10102df
@@ -1530,7 +1533,7 @@
field public static final int x = 16842924; // 0x10100ac
field public static final int xlargeScreens = 16843455; // 0x10102bf
field public static final int y = 16842925; // 0x10100ad
- field public static final int yearListItemTextAppearance = 16843929; // 0x1010499
+ field public static final deprecated int yearListItemTextAppearance = 16843929; // 0x1010499
field public static final deprecated int yearListSelectorColor = 16843930; // 0x101049a
field public static final int yesNoPreferenceStyle = 16842896; // 0x1010090
field public static final int zAdjustment = 16843201; // 0x10101c1
@@ -2812,6 +2815,7 @@
}
public class AccountManager {
+ method public boolean accountAuthenticated(android.accounts.Account);
method public android.accounts.AccountManagerFuture<android.os.Bundle> addAccount(java.lang.String, java.lang.String, java.lang.String[], android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler);
method public boolean addAccountExplicitly(android.accounts.Account, java.lang.String, android.os.Bundle);
method public void addOnAccountsUpdatedListener(android.accounts.OnAccountsUpdateListener, android.os.Handler, boolean);
@@ -2872,6 +2876,7 @@
field public static final java.lang.String KEY_ERROR_CODE = "errorCode";
field public static final java.lang.String KEY_ERROR_MESSAGE = "errorMessage";
field public static final java.lang.String KEY_INTENT = "intent";
+ field public static final java.lang.String KEY_LAST_AUTHENTICATE_TIME_MILLIS_EPOCH = "lastAuthenticatedTimeMillisEpoch";
field public static final java.lang.String KEY_PASSWORD = "password";
field public static final java.lang.String KEY_USERDATA = "userdata";
field public static final java.lang.String LOGIN_ACCOUNTS_CHANGED_ACTION = "android.accounts.LOGIN_ACCOUNTS_CHANGED";
@@ -5909,6 +5914,7 @@
ctor public BackupTransport();
method public int abortFullRestore();
method public void cancelFullBackup();
+ method public int checkFullBackupSize(long);
method public int clearBackupData(android.content.pm.PackageInfo);
method public android.content.Intent configurationIntent();
method public java.lang.String currentDestinationString();
@@ -8173,6 +8179,7 @@
field public static final java.lang.String ACTION_INSERT = "android.intent.action.INSERT";
field public static final java.lang.String ACTION_INSERT_OR_EDIT = "android.intent.action.INSERT_OR_EDIT";
field public static final java.lang.String ACTION_INSTALL_PACKAGE = "android.intent.action.INSTALL_PACKAGE";
+ field public static final java.lang.String ACTION_INTENT_FILTER_NEEDS_VERIFICATION = "android.intent.action.INTENT_FILTER_NEEDS_VERIFICATION";
field public static final java.lang.String ACTION_LOCALE_CHANGED = "android.intent.action.LOCALE_CHANGED";
field public static final java.lang.String ACTION_MAIN = "android.intent.action.MAIN";
field public static final java.lang.String ACTION_MANAGED_PROFILE_ADDED = "android.intent.action.MANAGED_PROFILE_ADDED";
@@ -8470,6 +8477,8 @@
field public static final int NO_MATCH_CATEGORY = -4; // 0xfffffffc
field public static final int NO_MATCH_DATA = -2; // 0xfffffffe
field public static final int NO_MATCH_TYPE = -1; // 0xffffffff
+ field public static final java.lang.String SCHEME_HTTP = "http";
+ field public static final java.lang.String SCHEME_HTTPS = "https";
field public static final int SYSTEM_HIGH_PRIORITY = 1000; // 0x3e8
field public static final int SYSTEM_LOW_PRIORITY = -1000; // 0xfffffc18
}
@@ -8946,6 +8955,7 @@
field public static final int FLAG_SYSTEM = 1; // 0x1
field public static final int FLAG_TEST_ONLY = 256; // 0x100
field public static final int FLAG_UPDATED_SYSTEM_APP = 128; // 0x80
+ field public static final int FLAG_USES_CLEARTEXT_TRAFFIC = 134217728; // 0x8000000
field public static final int FLAG_VM_SAFE_MODE = 16384; // 0x4000
field public java.lang.String backupAgentName;
field public java.lang.String className;
@@ -9067,6 +9077,25 @@
field public java.lang.String targetPackage;
}
+ public final class IntentFilterVerificationInfo implements android.os.Parcelable {
+ ctor public IntentFilterVerificationInfo();
+ ctor public IntentFilterVerificationInfo(java.lang.String, java.lang.String[]);
+ ctor public IntentFilterVerificationInfo(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+ ctor public IntentFilterVerificationInfo(android.os.Parcel);
+ method public int describeContents();
+ method public java.lang.String[] getDomains();
+ method public java.lang.String getDomainsString();
+ method public java.lang.String getPackageName();
+ method public int getStatus();
+ method public java.lang.String getStatusString();
+ method public static java.lang.String getStatusStringFromValue(int);
+ method public void readFromXml(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+ method public void setStatus(int);
+ method public void writeToParcel(android.os.Parcel, int);
+ method public void writeToXml(org.xmlpull.v1.XmlSerializer) throws java.io.IOException;
+ field public static final android.os.Parcelable.Creator<android.content.pm.IntentFilterVerificationInfo> CREATOR;
+ }
+
public class LabeledIntent extends android.content.Intent {
ctor public LabeledIntent(android.content.Intent, java.lang.String, int, int);
ctor public LabeledIntent(android.content.Intent, java.lang.String, java.lang.CharSequence, int);
@@ -9301,6 +9330,7 @@
method public abstract java.util.List<android.content.pm.PackageInfo> getInstalledPackages(int);
method public abstract java.lang.String getInstallerPackageName(java.lang.String);
method public abstract android.content.pm.InstrumentationInfo getInstrumentationInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
+ method public abstract java.util.List<android.content.pm.IntentFilterVerificationInfo> getIntentFilterVerifications(java.lang.String);
method public abstract android.content.Intent getLaunchIntentForPackage(java.lang.String);
method public abstract android.content.Intent getLeanbackLaunchIntentForPackage(java.lang.String);
method public abstract java.lang.String getNameForUid(int);
@@ -9392,6 +9422,7 @@
field public static final java.lang.String FEATURE_LOCATION_NETWORK = "android.hardware.location.network";
field public static final java.lang.String FEATURE_MANAGED_USERS = "android.software.managed_users";
field public static final java.lang.String FEATURE_MICROPHONE = "android.hardware.microphone";
+ field public static final java.lang.String FEATURE_MIDI = "android.software.midi";
field public static final java.lang.String FEATURE_NFC = "android.hardware.nfc";
field public static final java.lang.String FEATURE_NFC_HOST_CARD_EMULATION = "android.hardware.nfc.hce";
field public static final java.lang.String FEATURE_OPENGLES_EXTENSION_PACK = "android.hardware.opengles.aep";
@@ -11418,7 +11449,9 @@
field public static final int JPEG = 256; // 0x100
field public static final int NV16 = 16; // 0x10
field public static final int NV21 = 17; // 0x11
+ field public static final int PRIVATE = 34; // 0x22
field public static final int RAW10 = 37; // 0x25
+ field public static final int RAW12 = 38; // 0x26
field public static final int RAW_SENSOR = 32; // 0x20
field public static final int RGB_565 = 4; // 0x4
field public static final int UNKNOWN = 0; // 0x0
@@ -14168,6 +14201,7 @@
method public void setHdmiMhlVendorCommandListener(android.hardware.hdmi.HdmiTvClient.HdmiMhlVendorCommandListener);
method public void setInputChangeListener(android.hardware.hdmi.HdmiTvClient.InputChangeListener);
method public void setRecordListener(android.hardware.hdmi.HdmiRecordListener);
+ method public void setSystemAudioMode(boolean, android.hardware.hdmi.HdmiTvClient.SelectCallback);
method public void setSystemAudioMute(boolean);
method public void setSystemAudioVolume(int, int, int);
method public void startOneTouchRecord(int, android.hardware.hdmi.HdmiRecordSources.RecordSource);
@@ -24368,6 +24402,7 @@
field public static java.lang.String DIRECTORY_RINGTONES;
field public static final java.lang.String MEDIA_BAD_REMOVAL = "bad_removal";
field public static final java.lang.String MEDIA_CHECKING = "checking";
+ field public static final java.lang.String MEDIA_EJECTING = "ejecting";
field public static final java.lang.String MEDIA_MOUNTED = "mounted";
field public static final java.lang.String MEDIA_MOUNTED_READ_ONLY = "mounted_ro";
field public static final java.lang.String MEDIA_NOFS = "nofs";
@@ -27884,6 +27919,108 @@
field public static final java.lang.String SIZE = "_size";
}
+ public abstract class SearchIndexableData {
+ ctor public SearchIndexableData();
+ ctor public SearchIndexableData(android.content.Context);
+ field public java.lang.String className;
+ field public android.content.Context context;
+ field public boolean enabled;
+ field public int iconResId;
+ field public java.lang.String intentAction;
+ field public java.lang.String intentTargetClass;
+ field public java.lang.String intentTargetPackage;
+ field public java.lang.String key;
+ field public java.util.Locale locale;
+ field public java.lang.String packageName;
+ field public int rank;
+ field public int userId;
+ }
+
+ public class SearchIndexableResource extends android.provider.SearchIndexableData {
+ ctor public SearchIndexableResource(int, int, java.lang.String, int);
+ ctor public SearchIndexableResource(android.content.Context);
+ field public int xmlResId;
+ }
+
+ public class SearchIndexablesContract {
+ ctor public SearchIndexablesContract();
+ field public static final int COLUMN_INDEX_NON_INDEXABLE_KEYS_KEY_VALUE = 0; // 0x0
+ field public static final int COLUMN_INDEX_RAW_CLASS_NAME = 7; // 0x7
+ field public static final int COLUMN_INDEX_RAW_ENTRIES = 4; // 0x4
+ field public static final int COLUMN_INDEX_RAW_ICON_RESID = 8; // 0x8
+ field public static final int COLUMN_INDEX_RAW_INTENT_ACTION = 9; // 0x9
+ field public static final int COLUMN_INDEX_RAW_INTENT_TARGET_CLASS = 11; // 0xb
+ field public static final int COLUMN_INDEX_RAW_INTENT_TARGET_PACKAGE = 10; // 0xa
+ field public static final int COLUMN_INDEX_RAW_KEY = 12; // 0xc
+ field public static final int COLUMN_INDEX_RAW_KEYWORDS = 5; // 0x5
+ field public static final int COLUMN_INDEX_RAW_RANK = 0; // 0x0
+ field public static final int COLUMN_INDEX_RAW_SCREEN_TITLE = 6; // 0x6
+ field public static final int COLUMN_INDEX_RAW_SUMMARY_OFF = 3; // 0x3
+ field public static final int COLUMN_INDEX_RAW_SUMMARY_ON = 2; // 0x2
+ field public static final int COLUMN_INDEX_RAW_TITLE = 1; // 0x1
+ field public static final int COLUMN_INDEX_RAW_USER_ID = 13; // 0xd
+ field public static final int COLUMN_INDEX_XML_RES_CLASS_NAME = 2; // 0x2
+ field public static final int COLUMN_INDEX_XML_RES_ICON_RESID = 3; // 0x3
+ field public static final int COLUMN_INDEX_XML_RES_INTENT_ACTION = 4; // 0x4
+ field public static final int COLUMN_INDEX_XML_RES_INTENT_TARGET_CLASS = 6; // 0x6
+ field public static final int COLUMN_INDEX_XML_RES_INTENT_TARGET_PACKAGE = 5; // 0x5
+ field public static final int COLUMN_INDEX_XML_RES_RANK = 0; // 0x0
+ field public static final int COLUMN_INDEX_XML_RES_RESID = 1; // 0x1
+ field public static final java.lang.String INDEXABLES_RAW = "indexables_raw";
+ field public static final java.lang.String[] INDEXABLES_RAW_COLUMNS;
+ field public static final java.lang.String INDEXABLES_RAW_PATH = "settings/indexables_raw";
+ field public static final java.lang.String INDEXABLES_XML_RES = "indexables_xml_res";
+ field public static final java.lang.String[] INDEXABLES_XML_RES_COLUMNS;
+ field public static final java.lang.String INDEXABLES_XML_RES_PATH = "settings/indexables_xml_res";
+ field public static final java.lang.String NON_INDEXABLES_KEYS = "non_indexables_key";
+ field public static final java.lang.String[] NON_INDEXABLES_KEYS_COLUMNS;
+ field public static final java.lang.String NON_INDEXABLES_KEYS_PATH = "settings/non_indexables_key";
+ field public static final java.lang.String PROVIDER_INTERFACE = "android.content.action.SEARCH_INDEXABLES_PROVIDER";
+ }
+
+ public static class SearchIndexablesContract.BaseColumns {
+ field public static final java.lang.String COLUMN_CLASS_NAME = "className";
+ field public static final java.lang.String COLUMN_ICON_RESID = "iconResId";
+ field public static final java.lang.String COLUMN_INTENT_ACTION = "intentAction";
+ field public static final java.lang.String COLUMN_INTENT_TARGET_CLASS = "intentTargetClass";
+ field public static final java.lang.String COLUMN_INTENT_TARGET_PACKAGE = "intentTargetPackage";
+ field public static final java.lang.String COLUMN_RANK = "rank";
+ }
+
+ public static final class SearchIndexablesContract.NonIndexableKey extends android.provider.SearchIndexablesContract.BaseColumns {
+ field public static final java.lang.String COLUMN_KEY_VALUE = "key";
+ field public static final java.lang.String MIME_TYPE = "vnd.android.cursor.dir/non_indexables_key";
+ }
+
+ public static final class SearchIndexablesContract.RawData extends android.provider.SearchIndexablesContract.BaseColumns {
+ field public static final java.lang.String COLUMN_ENTRIES = "entries";
+ field public static final java.lang.String COLUMN_KEY = "key";
+ field public static final java.lang.String COLUMN_KEYWORDS = "keywords";
+ field public static final java.lang.String COLUMN_SCREEN_TITLE = "screenTitle";
+ field public static final java.lang.String COLUMN_SUMMARY_OFF = "summaryOff";
+ field public static final java.lang.String COLUMN_SUMMARY_ON = "summaryOn";
+ field public static final java.lang.String COLUMN_TITLE = "title";
+ field public static final java.lang.String COLUMN_USER_ID = "user_id";
+ field public static final java.lang.String MIME_TYPE = "vnd.android.cursor.dir/indexables_raw";
+ }
+
+ public static final class SearchIndexablesContract.XmlResource extends android.provider.SearchIndexablesContract.BaseColumns {
+ field public static final java.lang.String COLUMN_XML_RESID = "xmlResId";
+ field public static final java.lang.String MIME_TYPE = "vnd.android.cursor.dir/indexables_xml_res";
+ }
+
+ public abstract class SearchIndexablesProvider extends android.content.ContentProvider {
+ ctor public SearchIndexablesProvider();
+ method public final int delete(android.net.Uri, java.lang.String, java.lang.String[]);
+ method public java.lang.String getType(android.net.Uri);
+ method public final android.net.Uri insert(android.net.Uri, android.content.ContentValues);
+ method public android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String);
+ method public abstract android.database.Cursor queryNonIndexableKeys(java.lang.String[]);
+ method public abstract android.database.Cursor queryRawData(java.lang.String[]);
+ method public abstract android.database.Cursor queryXmlResources(java.lang.String[]);
+ method public final int update(android.net.Uri, android.content.ContentValues, java.lang.String, java.lang.String[]);
+ }
+
public class SearchRecentSuggestions {
ctor public SearchRecentSuggestions(android.content.Context, java.lang.String, int);
method public void clearHistory();
@@ -28795,6 +28932,10 @@
method public static android.renderscript.Element A_8(android.renderscript.RenderScript);
method public static android.renderscript.Element BOOLEAN(android.renderscript.RenderScript);
method public static android.renderscript.Element ELEMENT(android.renderscript.RenderScript);
+ method public static android.renderscript.Element F16(android.renderscript.RenderScript);
+ method public static android.renderscript.Element F16_2(android.renderscript.RenderScript);
+ method public static android.renderscript.Element F16_3(android.renderscript.RenderScript);
+ method public static android.renderscript.Element F16_4(android.renderscript.RenderScript);
method public static android.renderscript.Element F32(android.renderscript.RenderScript);
method public static android.renderscript.Element F32_2(android.renderscript.RenderScript);
method public static android.renderscript.Element F32_3(android.renderscript.RenderScript);
@@ -28893,6 +29034,7 @@
method public static android.renderscript.Element.DataType valueOf(java.lang.String);
method public static final android.renderscript.Element.DataType[] values();
enum_constant public static final android.renderscript.Element.DataType BOOLEAN;
+ enum_constant public static final android.renderscript.Element.DataType FLOAT_16;
enum_constant public static final android.renderscript.Element.DataType FLOAT_32;
enum_constant public static final android.renderscript.Element.DataType FLOAT_64;
enum_constant public static final android.renderscript.Element.DataType MATRIX_2X2;
@@ -29166,11 +29308,14 @@
method public static android.renderscript.RenderScript create(android.content.Context);
method public static android.renderscript.RenderScript create(android.content.Context, android.renderscript.RenderScript.ContextType);
method public static android.renderscript.RenderScript create(android.content.Context, android.renderscript.RenderScript.ContextType, int);
+ method public static android.renderscript.RenderScript createMultiContext(android.content.Context, android.renderscript.RenderScript.ContextType, int, int);
method public void destroy();
method public void finish();
method public final android.content.Context getApplicationContext();
method public android.renderscript.RenderScript.RSErrorHandler getErrorHandler();
method public android.renderscript.RenderScript.RSMessageHandler getMessageHandler();
+ method public static long getMinorID();
+ method public static void releaseAllContexts();
method public void sendMessage(int, int[]);
method public void setErrorHandler(android.renderscript.RenderScript.RSErrorHandler);
method public void setMessageHandler(android.renderscript.RenderScript.RSMessageHandler);
@@ -29252,9 +29397,12 @@
public class Script extends android.renderscript.BaseObj {
method public void bindAllocation(android.renderscript.Allocation, int);
method protected android.renderscript.Script.FieldID createFieldID(int, android.renderscript.Element);
+ method protected android.renderscript.Script.InvokeID createInvokeID(int);
method protected android.renderscript.Script.KernelID createKernelID(int, int, android.renderscript.Element, android.renderscript.Element);
method protected void forEach(int, android.renderscript.Allocation, android.renderscript.Allocation, android.renderscript.FieldPacker);
method protected void forEach(int, android.renderscript.Allocation, android.renderscript.Allocation, android.renderscript.FieldPacker, android.renderscript.Script.LaunchOptions);
+ method protected void forEach(int, android.renderscript.Allocation[], android.renderscript.Allocation, android.renderscript.FieldPacker);
+ method protected void forEach(int, android.renderscript.Allocation[], android.renderscript.Allocation, android.renderscript.FieldPacker, android.renderscript.Script.LaunchOptions);
method public boolean getVarB(int);
method public double getVarD(int);
method public float getVarF(int);
@@ -29292,6 +29440,9 @@
public static final class Script.FieldID extends android.renderscript.BaseObj {
}
+ public static final class Script.InvokeID extends android.renderscript.BaseObj {
+ }
+
public static final class Script.KernelID extends android.renderscript.BaseObj {
}
@@ -29502,6 +29653,8 @@
method public static android.renderscript.Type createX(android.renderscript.RenderScript, android.renderscript.Element, int);
method public static android.renderscript.Type createXY(android.renderscript.RenderScript, android.renderscript.Element, int, int);
method public static android.renderscript.Type createXYZ(android.renderscript.RenderScript, android.renderscript.Element, int, int, int);
+ method public int getArray(int);
+ method public int getArrayCount();
method public int getCount();
method public android.renderscript.Element getElement();
method public int getX();
@@ -29515,6 +29668,7 @@
public static class Type.Builder {
ctor public Type.Builder(android.renderscript.RenderScript, android.renderscript.Element);
method public android.renderscript.Type create();
+ method public android.renderscript.Type.Builder setArray(int, int);
method public android.renderscript.Type.Builder setFaces(boolean);
method public android.renderscript.Type.Builder setMipmaps(boolean);
method public android.renderscript.Type.Builder setX(int);
@@ -29644,6 +29798,11 @@
method public android.security.KeyStoreParameter.Builder setEncryptionRequired(boolean);
}
+ public class NetworkSecurityPolicy {
+ method public static android.security.NetworkSecurityPolicy getInstance();
+ method public boolean isCleartextTrafficPermitted();
+ }
+
}
package android.service.carrier {
@@ -32223,6 +32382,7 @@
method public int[] supplyPukReportResult(java.lang.String, java.lang.String);
method public void toggleRadioOnOff();
method public void updateServiceLocation();
+ field public static final java.lang.String ACTION_EMERGENCY_ASSISTANCE = "android.telephony.action.EMERGENCY_ASSISTANCE";
field public static final java.lang.String ACTION_PHONE_STATE_CHANGED = "android.intent.action.PHONE_STATE";
field public static final java.lang.String ACTION_RESPOND_VIA_MESSAGE = "android.intent.action.RESPOND_VIA_MESSAGE";
field public static final int CALL_STATE_IDLE = 0; // 0x0
@@ -32894,6 +33054,7 @@
method public java.util.List<android.content.pm.PackageInfo> getInstalledPackages(int);
method public java.lang.String getInstallerPackageName(java.lang.String);
method public android.content.pm.InstrumentationInfo getInstrumentationInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
+ method public java.util.List<android.content.pm.IntentFilterVerificationInfo> getIntentFilterVerifications(java.lang.String);
method public android.content.Intent getLaunchIntentForPackage(java.lang.String);
method public android.content.Intent getLeanbackLaunchIntentForPackage(java.lang.String);
method public java.lang.String getNameForUid(int);
@@ -36674,8 +36835,10 @@
method public long getTimeDelta();
method public boolean isInProgress();
method public boolean isQuickScaleEnabled();
+ method public boolean isSecondaryButtonScaleEnabled();
method public boolean onTouchEvent(android.view.MotionEvent);
method public void setQuickScaleEnabled(boolean);
+ method public void setSecondaryButtonScaleEnabled(boolean);
}
public static abstract interface ScaleGestureDetector.OnScaleGestureListener {
@@ -40310,7 +40473,6 @@
method public boolean shouldOverrideUrlLoading(android.webkit.WebView, java.lang.String);
field public static final int ERROR_AUTHENTICATION = -4; // 0xfffffffc
field public static final int ERROR_BAD_URL = -12; // 0xfffffff4
- field public static final int ERROR_BLOCKED = -16; // 0xfffffff0
field public static final int ERROR_CONNECT = -6; // 0xfffffffa
field public static final int ERROR_FAILED_SSL_HANDSHAKE = -11; // 0xfffffff5
field public static final int ERROR_FILE = -13; // 0xfffffff3
@@ -40868,7 +41030,7 @@
field protected android.database.Cursor mDataCursor;
}
- public class AnalogClock extends android.view.View {
+ public deprecated class AnalogClock extends android.view.View {
ctor public AnalogClock(android.content.Context);
ctor public AnalogClock(android.content.Context, android.util.AttributeSet);
ctor public AnalogClock(android.content.Context, android.util.AttributeSet, int);
@@ -42929,12 +43091,16 @@
ctor public TimePicker(android.content.Context, android.util.AttributeSet);
ctor public TimePicker(android.content.Context, android.util.AttributeSet, int);
ctor public TimePicker(android.content.Context, android.util.AttributeSet, int, int);
- method public java.lang.Integer getCurrentHour();
- method public java.lang.Integer getCurrentMinute();
+ method public deprecated java.lang.Integer getCurrentHour();
+ method public deprecated java.lang.Integer getCurrentMinute();
+ method public int getHour();
+ method public int getMinute();
method public boolean is24HourView();
- method public void setCurrentHour(java.lang.Integer);
- method public void setCurrentMinute(java.lang.Integer);
+ method public deprecated void setCurrentHour(java.lang.Integer);
+ method public deprecated void setCurrentMinute(java.lang.Integer);
+ method public void setHour(int);
method public void setIs24HourView(java.lang.Boolean);
+ method public void setMinute(int);
method public void setOnTimeChangedListener(android.widget.TimePicker.OnTimeChangedListener);
}
diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java
index 6957435c..480d171 100644
--- a/core/java/android/accounts/AccountManager.java
+++ b/core/java/android/accounts/AccountManager.java
@@ -203,6 +203,14 @@
public static final String KEY_USERDATA = "userdata";
/**
+ * Bundle key used to supply the last time the credentials of the account
+ * were authenticated successfully. Time is specified in milliseconds since
+ * epoch.
+ */
+ public static final String KEY_LAST_AUTHENTICATE_TIME_MILLIS_EPOCH =
+ "lastAuthenticatedTimeMillisEpoch";
+
+ /**
* Authenticators using 'customTokens' option will also get the UID of the
* caller
*/
@@ -663,6 +671,31 @@
}
/**
+ * Informs the system that the account has been authenticated recently. This
+ * recency may be used by other applications to verify the account. This
+ * should be called only when the user has entered correct credentials for
+ * the account.
+ * <p>
+ * It is not safe to call this method from the main thread. As such, call it
+ * from another thread.
+ * <p>
+ * This method requires the caller to hold the permission
+ * {@link android.Manifest.permission#AUTHENTICATE_ACCOUNTS} and should be
+ * called from the account's authenticator.
+ *
+ * @param account The {@link Account} to be updated.
+ */
+ public boolean accountAuthenticated(Account account) {
+ if (account == null)
+ throw new IllegalArgumentException("account is null");
+ try {
+ return mService.accountAuthenticated(account);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
* Rename the specified {@link Account}. This is equivalent to removing
* the existing account and adding a new renamed account with the old
* account's user data.
@@ -1544,15 +1577,20 @@
* with these fields if activity or password was supplied and
* the account was successfully verified:
* <ul>
- * <li> {@link #KEY_ACCOUNT_NAME} - the name of the account created
+ * <li> {@link #KEY_ACCOUNT_NAME} - the name of the account verified
* <li> {@link #KEY_ACCOUNT_TYPE} - the type of the account
* <li> {@link #KEY_BOOLEAN_RESULT} - true to indicate success
* </ul>
*
* If no activity or password was specified, the returned Bundle contains
- * only {@link #KEY_INTENT} with the {@link Intent} needed to launch the
- * password prompt. If an error occurred,
- * {@link AccountManagerFuture#getResult()} throws:
+ * {@link #KEY_INTENT} with the {@link Intent} needed to launch the
+ * password prompt.
+ *
+ * <p>Also the returning Bundle may contain {@link
+ * #KEY_LAST_AUTHENTICATE_TIME_MILLIS_EPOCH} indicating the last time the
+ * credential was validated/created.
+ *
+ * If an error occurred,{@link AccountManagerFuture#getResult()} throws:
* <ul>
* <li> {@link AuthenticatorException} if the authenticator failed to respond
* <li> {@link OperationCanceledException} if the operation was canceled for
@@ -1625,9 +1663,9 @@
* <li> {@link #KEY_ACCOUNT_TYPE} - the type of the account
* </ul>
*
- * If no activity was specified, the returned Bundle contains only
+ * If no activity was specified, the returned Bundle contains
* {@link #KEY_INTENT} with the {@link Intent} needed to launch the
- * password prompt. If an error occurred,
+ * password prompt. If an error occurred,
* {@link AccountManagerFuture#getResult()} throws:
* <ul>
* <li> {@link AuthenticatorException} if the authenticator failed to respond
diff --git a/core/java/android/accounts/IAccountManager.aidl b/core/java/android/accounts/IAccountManager.aidl
index aa41161..04b3c88 100644
--- a/core/java/android/accounts/IAccountManager.aidl
+++ b/core/java/android/accounts/IAccountManager.aidl
@@ -67,6 +67,7 @@
boolean expectActivityLaunch);
void confirmCredentialsAsUser(in IAccountManagerResponse response, in Account account,
in Bundle options, boolean expectActivityLaunch, int userId);
+ boolean accountAuthenticated(in Account account);
void getAuthTokenLabel(in IAccountManagerResponse response, String accountType,
String authTokenType);
diff --git a/core/java/android/animation/FloatKeyframeSet.java b/core/java/android/animation/FloatKeyframeSet.java
index 56da940..0173079 100644
--- a/core/java/android/animation/FloatKeyframeSet.java
+++ b/core/java/android/animation/FloatKeyframeSet.java
@@ -118,13 +118,14 @@
FloatKeyframe nextKeyframe = (FloatKeyframe) mKeyframes.get(i);
if (fraction < nextKeyframe.getFraction()) {
final TimeInterpolator interpolator = nextKeyframe.getInterpolator();
- if (interpolator != null) {
- fraction = interpolator.getInterpolation(fraction);
- }
float intervalFraction = (fraction - prevKeyframe.getFraction()) /
(nextKeyframe.getFraction() - prevKeyframe.getFraction());
float prevValue = prevKeyframe.getFloatValue();
float nextValue = nextKeyframe.getFloatValue();
+ // Apply interpolator on the proportional duration.
+ if (interpolator != null) {
+ intervalFraction = interpolator.getInterpolation(intervalFraction);
+ }
return mEvaluator == null ?
prevValue + intervalFraction * (nextValue - prevValue) :
((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)).
diff --git a/core/java/android/animation/IntKeyframeSet.java b/core/java/android/animation/IntKeyframeSet.java
index 12a4bf9..73f9af1 100644
--- a/core/java/android/animation/IntKeyframeSet.java
+++ b/core/java/android/animation/IntKeyframeSet.java
@@ -117,13 +117,14 @@
IntKeyframe nextKeyframe = (IntKeyframe) mKeyframes.get(i);
if (fraction < nextKeyframe.getFraction()) {
final TimeInterpolator interpolator = nextKeyframe.getInterpolator();
- if (interpolator != null) {
- fraction = interpolator.getInterpolation(fraction);
- }
float intervalFraction = (fraction - prevKeyframe.getFraction()) /
(nextKeyframe.getFraction() - prevKeyframe.getFraction());
int prevValue = prevKeyframe.getIntValue();
int nextValue = nextKeyframe.getIntValue();
+ // Apply interpolator on the proportional duration.
+ if (interpolator != null) {
+ intervalFraction = interpolator.getInterpolation(intervalFraction);
+ }
return mEvaluator == null ?
prevValue + (int)(intervalFraction * (nextValue - prevValue)) :
((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)).
diff --git a/core/java/android/animation/KeyframeSet.java b/core/java/android/animation/KeyframeSet.java
index c80e162..32edd4d 100644
--- a/core/java/android/animation/KeyframeSet.java
+++ b/core/java/android/animation/KeyframeSet.java
@@ -201,7 +201,6 @@
* @return The animated value.
*/
public Object getValue(float fraction) {
-
// Special-case optimization for the common case of only two keyframes
if (mNumKeyframes == 2) {
if (mInterpolator != null) {
@@ -238,12 +237,13 @@
Keyframe nextKeyframe = mKeyframes.get(i);
if (fraction < nextKeyframe.getFraction()) {
final TimeInterpolator interpolator = nextKeyframe.getInterpolator();
- if (interpolator != null) {
- fraction = interpolator.getInterpolation(fraction);
- }
final float prevFraction = prevKeyframe.getFraction();
float intervalFraction = (fraction - prevFraction) /
(nextKeyframe.getFraction() - prevFraction);
+ // Apply interpolator on the proportional duration.
+ if (interpolator != null) {
+ intervalFraction = interpolator.getInterpolation(intervalFraction);
+ }
return mEvaluator.evaluate(intervalFraction, prevKeyframe.getValue(),
nextKeyframe.getValue());
}
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 6d74905..6ec5457 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -36,6 +36,7 @@
import android.content.pm.IPackageMoveObserver;
import android.content.pm.IPackageStatsObserver;
import android.content.pm.InstrumentationInfo;
+import android.content.pm.IntentFilterVerificationInfo;
import android.content.pm.KeySet;
import android.content.pm.ManifestDigest;
import android.content.pm.PackageInfo;
@@ -1309,6 +1310,45 @@
}
@Override
+ public void verifyIntentFilter(int id, int verificationCode, List<String> outFailedDomains) {
+ try {
+ mPM.verifyIntentFilter(id, verificationCode, outFailedDomains);
+ } catch (RemoteException e) {
+ // Should never happen!
+ }
+ }
+
+ @Override
+ public int getIntentVerificationStatus(String packageName, int userId) {
+ try {
+ return mPM.getIntentVerificationStatus(packageName, userId);
+ } catch (RemoteException e) {
+ // Should never happen!
+ return PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
+ }
+ }
+
+ @Override
+ public boolean updateIntentVerificationStatus(String packageName, int status, int userId) {
+ try {
+ return mPM.updateIntentVerificationStatus(packageName, status, userId);
+ } catch (RemoteException e) {
+ // Should never happen!
+ return false;
+ }
+ }
+
+ @Override
+ public List<IntentFilterVerificationInfo> getIntentFilterVerifications(String packageName) {
+ try {
+ return mPM.getIntentFilterVerifications(packageName);
+ } catch (RemoteException e) {
+ // Should never happen!
+ return null;
+ }
+ }
+
+ @Override
public void setInstallerPackageName(String targetPackage,
String installerPackageName) {
try {
diff --git a/core/java/android/app/IBackupAgent.aidl b/core/java/android/app/IBackupAgent.aidl
index 451af99..fe8e228 100644
--- a/core/java/android/app/IBackupAgent.aidl
+++ b/core/java/android/app/IBackupAgent.aidl
@@ -100,6 +100,11 @@
void doFullBackup(in ParcelFileDescriptor data, int token, IBackupManager callbackBinder);
/**
+ * Estimate how much data a full backup will deliver
+ */
+ void doMeasureFullBackup(int token, IBackupManager callbackBinder);
+
+ /**
* Restore a single "file" to the application. The file was typically obtained from
* a full-backup dataset. The agent reads 'size' bytes of file content
* from the provided file descriptor.
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 59fe490..a0f40f6 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -402,13 +402,7 @@
new CachedServiceFetcher<StorageManager>() {
@Override
public StorageManager createService(ContextImpl ctx) {
- try {
- return new StorageManager(
- ctx.getContentResolver(), ctx.mMainThread.getHandler().getLooper());
- } catch (RemoteException rex) {
- Log.e(TAG, "Failed to create StorageManager", rex);
- return null;
- }
+ return new StorageManager(ctx, ctx.mMainThread.getHandler().getLooper());
}});
registerService(Context.TELEPHONY_SERVICE, TelephonyManager.class,
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index cf6619f..cbb0f51 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -3165,6 +3165,22 @@
}
/**
+ * Start Quick Contact on the managed profile for the current user, if the policy allows.
+ * @hide
+ */
+ public void startManagedQuickContact(String actualLookupKey, long actualContactId,
+ Intent originalIntent) {
+ if (mService != null) {
+ try {
+ mService.startManagedQuickContact(
+ actualLookupKey, actualContactId, originalIntent);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed talking with device policy service", e);
+ }
+ }
+ }
+
+ /**
* Called by the profile owner of a managed profile so that some intents sent in the managed
* profile can also be resolved in the parent, or vice versa.
* Only activity intents are supported.
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 9ca52e5..73b0684 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -189,6 +189,7 @@
void setCrossProfileCallerIdDisabled(in ComponentName who, boolean disabled);
boolean getCrossProfileCallerIdDisabled(in ComponentName who);
boolean getCrossProfileCallerIdDisabledForUser(int userId);
+ void startManagedQuickContact(String lookupKey, long contactId, in Intent originalIntent);
void setTrustAgentConfiguration(in ComponentName admin, in ComponentName agent,
in PersistableBundle args);
diff --git a/core/java/android/app/backup/BackupAgent.java b/core/java/android/app/backup/BackupAgent.java
index 7f89100..2bf267a 100644
--- a/core/java/android/app/backup/BackupAgent.java
+++ b/core/java/android/app/backup/BackupAgent.java
@@ -424,10 +424,12 @@
}
// And now that we know where it lives, semantically, back it up appropriately
- Log.i(TAG, "backupFile() of " + filePath + " => domain=" + domain
+ // In the measurement case, backupToTar() updates the size in output and returns
+ // without transmitting any file data.
+ if (DEBUG) Log.i(TAG, "backupFile() of " + filePath + " => domain=" + domain
+ " rootpath=" + rootpath);
- FullBackup.backupToTar(getPackageName(), domain, null, rootpath, filePath,
- output.getData());
+
+ FullBackup.backupToTar(getPackageName(), domain, null, rootpath, filePath, output);
}
/**
@@ -477,9 +479,8 @@
continue;
}
- // Finally, back this file up before proceeding
- FullBackup.backupToTar(packageName, domain, null, rootPath, filePath,
- output.getData());
+ // Finally, back this file up (or measure it) before proceeding
+ FullBackup.backupToTar(packageName, domain, null, rootPath, filePath, output);
}
}
}
@@ -640,7 +641,7 @@
Binder.restoreCallingIdentity(ident);
try {
- callbackBinder.opComplete(token);
+ callbackBinder.opComplete(token, 0);
} catch (RemoteException e) {
// we'll time out anyway, so we're safe
}
@@ -670,7 +671,7 @@
Binder.restoreCallingIdentity(ident);
try {
- callbackBinder.opComplete(token);
+ callbackBinder.opComplete(token, 0);
} catch (RemoteException e) {
// we'll time out anyway, so we're safe
}
@@ -692,10 +693,10 @@
try {
BackupAgent.this.onFullBackup(new FullBackupDataOutput(data));
} catch (IOException ex) {
- Log.d(TAG, "onBackup (" + BackupAgent.this.getClass().getName() + ") threw", ex);
+ Log.d(TAG, "onFullBackup (" + BackupAgent.this.getClass().getName() + ") threw", ex);
throw new RuntimeException(ex);
} catch (RuntimeException ex) {
- Log.d(TAG, "onBackup (" + BackupAgent.this.getClass().getName() + ") threw", ex);
+ Log.d(TAG, "onFullBackup (" + BackupAgent.this.getClass().getName() + ") threw", ex);
throw ex;
} finally {
// ... and then again after, as in the doBackup() case
@@ -713,13 +714,37 @@
Binder.restoreCallingIdentity(ident);
try {
- callbackBinder.opComplete(token);
+ callbackBinder.opComplete(token, 0);
} catch (RemoteException e) {
// we'll time out anyway, so we're safe
}
}
}
+ public void doMeasureFullBackup(int token, IBackupManager callbackBinder) {
+ // Ensure that we're running with the app's normal permission level
+ final long ident = Binder.clearCallingIdentity();
+ FullBackupDataOutput measureOutput = new FullBackupDataOutput();
+
+ waitForSharedPrefs();
+ try {
+ BackupAgent.this.onFullBackup(measureOutput);
+ } catch (IOException ex) {
+ Log.d(TAG, "onFullBackup[M] (" + BackupAgent.this.getClass().getName() + ") threw", ex);
+ throw new RuntimeException(ex);
+ } catch (RuntimeException ex) {
+ Log.d(TAG, "onFullBackup[M] (" + BackupAgent.this.getClass().getName() + ") threw", ex);
+ throw ex;
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ try {
+ callbackBinder.opComplete(token, measureOutput.getSize());
+ } catch (RemoteException e) {
+ // timeout, so we're safe
+ }
+ }
+ }
+
@Override
public void doRestoreFile(ParcelFileDescriptor data, long size,
int type, String domain, String path, long mode, long mtime,
@@ -728,6 +753,7 @@
try {
BackupAgent.this.onRestoreFile(data, size, type, domain, path, mode, mtime);
} catch (IOException e) {
+ Log.d(TAG, "onRestoreFile (" + BackupAgent.this.getClass().getName() + ") threw", e);
throw new RuntimeException(e);
} finally {
// Ensure that any side-effect SharedPreferences writes have landed
@@ -735,7 +761,7 @@
Binder.restoreCallingIdentity(ident);
try {
- callbackBinder.opComplete(token);
+ callbackBinder.opComplete(token, 0);
} catch (RemoteException e) {
// we'll time out anyway, so we're safe
}
@@ -747,13 +773,16 @@
long ident = Binder.clearCallingIdentity();
try {
BackupAgent.this.onRestoreFinished();
+ } catch (Exception e) {
+ Log.d(TAG, "onRestoreFinished (" + BackupAgent.this.getClass().getName() + ") threw", e);
+ throw e;
} finally {
// Ensure that any side-effect SharedPreferences writes have landed
waitForSharedPrefs();
Binder.restoreCallingIdentity(ident);
try {
- callbackBinder.opComplete(token);
+ callbackBinder.opComplete(token, 0);
} catch (RemoteException e) {
// we'll time out anyway, so we're safe
}
diff --git a/core/java/android/app/backup/BackupTransport.java b/core/java/android/app/backup/BackupTransport.java
index e853540..ca6dc69 100644
--- a/core/java/android/app/backup/BackupTransport.java
+++ b/core/java/android/app/backup/BackupTransport.java
@@ -393,6 +393,26 @@
}
/**
+ * Called after {@link #performFullBackup} to make sure that the transport is willing to
+ * handle a full-data backup operation of the specified size on the current package.
+ * If the transport returns anything other than TRANSPORT_OK, the package's backup
+ * operation will be skipped (and {@link #finishBackup() invoked} with no data for that
+ * package being passed to {@link #sendBackupData}.
+ *
+ * Added in MNC (API 23).
+ *
+ * @param size The estimated size of the full-data payload for this app. This includes
+ * manifest and archive format overhead, but is not guaranteed to be precise.
+ * @return TRANSPORT_OK if the platform is to proceed with the full-data backup,
+ * TRANSPORT_PACKAGE_REJECTED if the proposed payload size is too large for
+ * the transport to handle, or TRANSPORT_ERROR to indicate a fatal error
+ * condition that means the platform cannot perform a backup at this time.
+ */
+ public int checkFullBackupSize(long size) {
+ return BackupTransport.TRANSPORT_OK;
+ }
+
+ /**
* Tells the transport to read {@code numBytes} bytes of data from the socket file
* descriptor provided in the {@link #performFullBackup(PackageInfo, ParcelFileDescriptor)}
* call, and deliver those bytes to the datastore.
@@ -588,6 +608,11 @@
}
@Override
+ public int checkFullBackupSize(long size) {
+ return BackupTransport.this.checkFullBackupSize(size);
+ }
+
+ @Override
public int sendBackupData(int numBytes) throws RemoteException {
return BackupTransport.this.sendBackupData(numBytes);
}
diff --git a/core/java/android/app/backup/FullBackup.java b/core/java/android/app/backup/FullBackup.java
index e5b47c6..259884e 100644
--- a/core/java/android/app/backup/FullBackup.java
+++ b/core/java/android/app/backup/FullBackup.java
@@ -58,7 +58,7 @@
* @hide
*/
static public native int backupToTar(String packageName, String domain,
- String linkdomain, String rootpath, String path, BackupDataOutput output);
+ String linkdomain, String rootpath, String path, FullBackupDataOutput output);
/**
* Copy data from a socket to the given File location on permanent storage. The
diff --git a/core/java/android/app/backup/FullBackupDataOutput.java b/core/java/android/app/backup/FullBackupDataOutput.java
index 99dab1f..94704b9 100644
--- a/core/java/android/app/backup/FullBackupDataOutput.java
+++ b/core/java/android/app/backup/FullBackupDataOutput.java
@@ -9,7 +9,14 @@
*/
public class FullBackupDataOutput {
// Currently a name-scoping shim around BackupDataOutput
- private BackupDataOutput mData;
+ private final BackupDataOutput mData;
+ private long mSize;
+
+ /** @hide - used only in measure operation */
+ public FullBackupDataOutput() {
+ mData = null;
+ mSize = 0;
+ }
/** @hide */
public FullBackupDataOutput(ParcelFileDescriptor fd) {
@@ -18,4 +25,14 @@
/** @hide */
public BackupDataOutput getData() { return mData; }
+
+ /** @hide - used for measurement pass */
+ public void addSize(long size) {
+ if (size > 0) {
+ mSize += size;
+ }
+ }
+
+ /** @hide - used for measurement pass */
+ public long getSize() { return mSize; }
}
diff --git a/core/java/android/app/backup/IBackupManager.aidl b/core/java/android/app/backup/IBackupManager.aidl
index 41ad936..8f36dc4 100644
--- a/core/java/android/app/backup/IBackupManager.aidl
+++ b/core/java/android/app/backup/IBackupManager.aidl
@@ -286,11 +286,14 @@
* Notify the backup manager that a BackupAgent has completed the operation
* corresponding to the given token.
*
- * @param token The transaction token passed to a BackupAgent's doBackup() or
- * doRestore() method.
+ * @param token The transaction token passed to the BackupAgent method being
+ * invoked.
+ * @param result In the case of a full backup measure operation, the estimated
+ * total file size that would result from the operation. Unused in all other
+ * cases.
* {@hide}
*/
- void opComplete(int token);
+ void opComplete(int token, long result);
/**
* Make the device's backup and restore machinery (in)active. When it is inactive,
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 030b770..7a99a79 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1911,6 +1911,19 @@
public static final String ACTION_PACKAGE_VERIFIED = "android.intent.action.PACKAGE_VERIFIED";
/**
+ * Broadcast Action: Sent to the system intent filter verifier when an intent filter
+ * needs to be verified. The data contains the filter data hosts to be verified against.
+ * <p class="note">
+ * This is a protected intent that can only be sent by the system.
+ * </p>
+ *
+ * @hide
+ */
+ @SystemApi
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_INTENT_FILTER_NEEDS_VERIFICATION = "android.intent.action.INTENT_FILTER_NEEDS_VERIFICATION";
+
+ /**
* Broadcast Action: Resources for a set of packages (which were
* previously unavailable) are currently
* available since the media on which they exist is available.
diff --git a/core/java/android/content/IntentFilter.java b/core/java/android/content/IntentFilter.java
index 1240a23..590d791 100644
--- a/core/java/android/content/IntentFilter.java
+++ b/core/java/android/content/IntentFilter.java
@@ -16,10 +16,12 @@
package android.content;
+import android.content.pm.PackageParser;
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.PatternMatcher;
+import android.text.TextUtils;
import android.util.AndroidException;
import android.util.Log;
import android.util.Printer;
@@ -150,6 +152,7 @@
private static final String CAT_STR = "cat";
private static final String NAME_STR = "name";
private static final String ACTION_STR = "action";
+ private static final String AUTO_VERIFY_STR = "autoVerify";
/**
* The filter {@link #setPriority} value at which system high-priority
@@ -247,6 +250,19 @@
*/
public static final int NO_MATCH_CATEGORY = -4;
+ /**
+ * HTTP scheme.
+ *
+ * @see #addDataScheme(String)
+ */
+ public static final String SCHEME_HTTP = "http";
+ /**
+ * HTTPS scheme.
+ *
+ * @see #addDataScheme(String)
+ */
+ public static final String SCHEME_HTTPS = "https";
+
private int mPriority;
private final ArrayList<String> mActions;
private ArrayList<String> mCategories = null;
@@ -257,6 +273,13 @@
private ArrayList<String> mDataTypes = null;
private boolean mHasPartialTypes = false;
+ private static final int STATE_VERIFY_AUTO = 0x00000001;
+ private static final int STATE_NEED_VERIFY = 0x00000010;
+ private static final int STATE_NEED_VERIFY_CHECKED = 0x00000100;
+ private static final int STATE_VERIFIED = 0x00001000;
+
+ private int mVerifyState;
+
// These functions are the start of more optimized code for managing
// the string sets... not yet implemented.
@@ -326,7 +349,7 @@
public MalformedMimeTypeException(String name) {
super(name);
}
- };
+ }
/**
* Create a new IntentFilter instance with a specified action and MIME
@@ -421,6 +444,7 @@
mDataPaths = new ArrayList<PatternMatcher>(o.mDataPaths);
}
mHasPartialTypes = o.mHasPartialTypes;
+ mVerifyState = o.mVerifyState;
}
/**
@@ -452,6 +476,94 @@
}
/**
+ * Set whether this filter will needs to be automatically verified against its data URIs or not.
+ * The default is false.
+ *
+ * The verification would need to happen only and only if the Intent action is
+ * {@link android.content.Intent#ACTION_VIEW} and the Intent category is
+ * {@link android.content.Intent#CATEGORY_BROWSABLE} and the Intent data scheme
+ * is "http" or "https".
+ *
+ * True means that the filter will need to use its data URIs to be verified.
+ *
+ * @param autoVerify The new autoVerify value.
+ *
+ * @see #getAutoVerify()
+ * @see #addAction(String)
+ * @see #getAction(int)
+ * @see #addCategory(String)
+ * @see #getCategory(int)
+ * @see #addDataScheme(String)
+ * @see #getDataScheme(int)
+ *
+ * @hide
+ */
+ public final void setAutoVerify(boolean autoVerify) {
+ mVerifyState &= ~STATE_VERIFY_AUTO;
+ if (autoVerify) mVerifyState |= STATE_VERIFY_AUTO;
+ }
+
+ /**
+ * Return if this filter will needs to be automatically verified again its data URIs or not.
+ *
+ * @return True if the filter will needs to be automatically verified. False otherwise.
+ *
+ * @see #setAutoVerify(boolean)
+ *
+ * @hide
+ */
+ public final boolean getAutoVerify() {
+ return ((mVerifyState & STATE_VERIFY_AUTO) == 1);
+ }
+
+ /**
+ * Return if this filter needs to be automatically verified again its data URIs or not.
+ *
+ * @return True if the filter needs to be automatically verified. False otherwise.
+ *
+ * This will check if if the Intent action is {@link android.content.Intent#ACTION_VIEW} and
+ * the Intent category is {@link android.content.Intent#CATEGORY_BROWSABLE} and the Intent
+ * data scheme is "http" or "https".
+ *
+ * @see #setAutoVerify(boolean)
+ *
+ * @hide
+ */
+ public final boolean needsVerification() {
+ return hasAction(Intent.ACTION_VIEW) &&
+ hasCategory(Intent.CATEGORY_BROWSABLE) &&
+ (hasDataScheme(SCHEME_HTTP) || hasDataScheme(SCHEME_HTTPS)) &&
+ getAutoVerify();
+ }
+
+ /**
+ * Return if this filter has been verified
+ *
+ * @return true if the filter has been verified or if autoVerify is false.
+ *
+ * @hide
+ */
+ public final boolean isVerified() {
+ if ((mVerifyState & STATE_NEED_VERIFY_CHECKED) == STATE_NEED_VERIFY_CHECKED) {
+ return ((mVerifyState & STATE_NEED_VERIFY) == STATE_NEED_VERIFY);
+ }
+ return false;
+ }
+
+ /**
+ * Set if this filter has been verified
+ *
+ * @param verified true if this filter has been verified. False otherwise.
+ *
+ * @hide
+ */
+ public void setVerified(boolean verified) {
+ mVerifyState |= STATE_NEED_VERIFY_CHECKED;
+ mVerifyState &= ~STATE_VERIFIED;
+ if (verified) mVerifyState |= STATE_VERIFIED;
+ }
+
+ /**
* Add a new Intent action to match against. If any actions are included
* in the filter, then an Intent's action must be one of those values for
* it to match. If no actions are included, the Intent action is ignored.
@@ -1333,6 +1445,7 @@
* Write the contents of the IntentFilter as an XML stream.
*/
public void writeToXml(XmlSerializer serializer) throws IOException {
+ serializer.attribute(null, AUTO_VERIFY_STR, Boolean.toString(getAutoVerify()));
int N = countActions();
for (int i=0; i<N; i++) {
serializer.startTag(null, ACTION_STR);
@@ -1407,6 +1520,9 @@
public void readFromXml(XmlPullParser parser) throws XmlPullParserException,
IOException {
+ String autoVerify = parser.getAttributeValue(null, AUTO_VERIFY_STR);
+ setAutoVerify(TextUtils.isEmpty(autoVerify) ? false : Boolean.getBoolean(autoVerify));
+
int outerDepth = parser.getDepth();
int type;
while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
@@ -1548,6 +1664,11 @@
sb.append(", mHasPartialTypes="); sb.append(mHasPartialTypes);
du.println(sb.toString());
}
+ {
+ sb.setLength(0);
+ sb.append(prefix); sb.append("AutoVerify="); sb.append(getAutoVerify());
+ du.println(sb.toString());
+ }
}
public static final Parcelable.Creator<IntentFilter> CREATOR
@@ -1614,6 +1735,7 @@
}
dest.writeInt(mPriority);
dest.writeInt(mHasPartialTypes ? 1 : 0);
+ dest.writeInt(getAutoVerify() ? 1 : 0);
}
/**
@@ -1680,6 +1802,7 @@
}
mPriority = source.readInt();
mHasPartialTypes = source.readInt() > 0;
+ setAutoVerify(source.readInt() > 0);
}
private final boolean findMimeType(String type) {
@@ -1724,4 +1847,27 @@
return false;
}
+
+ /**
+ * @hide
+ */
+ public ArrayList<String> getHostsList() {
+ ArrayList<String> result = new ArrayList<>();
+ Iterator<IntentFilter.AuthorityEntry> it = authoritiesIterator();
+ if (it != null) {
+ while (it.hasNext()) {
+ IntentFilter.AuthorityEntry entry = it.next();
+ result.add(entry.getHost());
+ }
+ }
+ return result;
+ }
+
+ /**
+ * @hide
+ */
+ public String[] getHosts() {
+ ArrayList<String> list = getHostsList();
+ return list.toArray(new String[list.size()]);
+ }
}
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 8f17845..2496e45 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -340,8 +340,6 @@
* cleartext network traffic, in which case platform components (e.g., HTTP stacks,
* {@code WebView}, {@code MediaPlayer}) will refuse app's requests to use cleartext traffic.
* Third-party libraries are encouraged to honor this flag as well.
- *
- * @hide
*/
public static final int FLAG_USES_CLEARTEXT_TRAFFIC = 1<<27;
@@ -379,7 +377,8 @@
* {@link #FLAG_LARGE_HEAP}, {@link #FLAG_STOPPED},
* {@link #FLAG_SUPPORTS_RTL}, {@link #FLAG_INSTALLED},
* {@link #FLAG_IS_DATA_ONLY}, {@link #FLAG_IS_GAME},
- * {@link #FLAG_FULL_BACKUP_ONLY}, {@link #FLAG_MULTIARCH}.
+ * {@link #FLAG_FULL_BACKUP_ONLY}, {@link #FLAG_USES_CLEARTEXT_TRAFFIC},
+ * {@link #FLAG_MULTIARCH}.
*/
public int flags = 0;
@@ -655,7 +654,7 @@
}
pw.println(prefix + "dataDir=" + dataDir);
if (sharedLibraryFiles != null) {
- pw.println(prefix + "sharedLibraryFiles=" + sharedLibraryFiles);
+ pw.println(prefix + "sharedLibraryFiles=" + Arrays.toString(sharedLibraryFiles));
}
pw.println(prefix + "enabled=" + enabled + " targetSdkVersion=" + targetSdkVersion
+ " versionCode=" + versionCode);
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index c6d97f1..66b0d1a 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -31,6 +31,7 @@
import android.content.pm.IPackageDataObserver;
import android.content.pm.IPackageMoveObserver;
import android.content.pm.IPackageStatsObserver;
+import android.content.pm.IntentFilterVerificationInfo;
import android.content.pm.InstrumentationInfo;
import android.content.pm.KeySet;
import android.content.pm.PackageInfo;
@@ -436,6 +437,11 @@
void verifyPendingInstall(int id, int verificationCode);
void extendVerificationTimeout(int id, int verificationCodeAtTimeout, long millisecondsToDelay);
+ void verifyIntentFilter(int id, int verificationCode, in List<String> outFailedDomains);
+ int getIntentVerificationStatus(String packageName, int userId);
+ boolean updateIntentVerificationStatus(String packageName, int status, int userId);
+ List<IntentFilterVerificationInfo> getIntentFilterVerifications(String packageName);
+
VerifierDeviceIdentity getVerifierDeviceIdentity();
boolean isFirstBoot();
diff --git a/core/java/android/content/pm/IntentFilterVerificationInfo.aidl b/core/java/android/content/pm/IntentFilterVerificationInfo.aidl
new file mode 100644
index 0000000..00220e5
--- /dev/null
+++ b/core/java/android/content/pm/IntentFilterVerificationInfo.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm;
+
+parcelable IntentFilterVerificationInfo;
diff --git a/core/java/android/content/pm/IntentFilterVerificationInfo.java b/core/java/android/content/pm/IntentFilterVerificationInfo.java
new file mode 100644
index 0000000..60cb4a8
--- /dev/null
+++ b/core/java/android/content/pm/IntentFilterVerificationInfo.java
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm;
+
+import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
+import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK;
+import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
+import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+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.util.ArrayList;
+
+/**
+ * The {@link com.android.server.pm.PackageManagerService} maintains some
+ * {@link IntentFilterVerificationInfo}s for each domain / package / class name per user.
+ *
+ * @hide
+ */
+public final class IntentFilterVerificationInfo implements Parcelable {
+ private static final String TAG = IntentFilterVerificationInfo.class.getName();
+
+ private static final String TAG_DOMAIN = "domain";
+ private static final String ATTR_DOMAIN_NAME = "name";
+ private static final String ATTR_PACKAGE_NAME = "packageName";
+ private static final String ATTR_STATUS = "status";
+
+ private String[] mDomains;
+ private String mPackageName;
+ private int mMainStatus;
+
+ public IntentFilterVerificationInfo() {
+ mPackageName = null;
+ mDomains = new String[0];
+ mMainStatus = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
+ }
+
+ public IntentFilterVerificationInfo(String packageName, String[] domains) {
+ mPackageName = packageName;
+ mDomains = domains;
+ mMainStatus = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
+ }
+
+ public IntentFilterVerificationInfo(XmlPullParser parser)
+ throws IOException, XmlPullParserException {
+ readFromXml(parser);
+ }
+
+ public IntentFilterVerificationInfo(Parcel source) {
+ readFromParcel(source);
+ }
+
+ public String[] getDomains() {
+ return mDomains;
+ }
+
+ public String getPackageName() {
+ return mPackageName;
+ }
+
+ public int getStatus() {
+ return mMainStatus;
+ }
+
+ public void setStatus(int s) {
+ if (s >= INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED &&
+ s <= INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER) {
+ mMainStatus = s;
+ } else {
+ Log.w(TAG, "Trying to set a non supported status: " + s);
+ }
+ }
+
+ public String getDomainsString() {
+ StringBuilder sb = new StringBuilder();
+ for (String str : mDomains) {
+ if (sb.length() > 0) {
+ sb.append(" ");
+ }
+ sb.append(str);
+ }
+ return sb.toString();
+ }
+
+ String getStringFromXml(XmlPullParser parser, String attribute, String defaultValue) {
+ String value = parser.getAttributeValue(null, attribute);
+ if (value == null) {
+ String msg = "Missing element under " + TAG +": " + attribute + " at " +
+ parser.getPositionDescription();
+ Log.w(TAG, msg);
+ return defaultValue;
+ } else {
+ return value;
+ }
+ }
+
+ int getIntFromXml(XmlPullParser parser, String attribute, int defaultValue) {
+ String value = parser.getAttributeValue(null, attribute);
+ if (TextUtils.isEmpty(value)) {
+ String msg = "Missing element under " + TAG +": " + attribute + " at " +
+ parser.getPositionDescription();
+ Log.w(TAG, msg);
+ return defaultValue;
+ } else {
+ return Integer.parseInt(value);
+ }
+ }
+
+ public void readFromXml(XmlPullParser parser) throws XmlPullParserException,
+ IOException {
+ mPackageName = getStringFromXml(parser, ATTR_PACKAGE_NAME, null);
+ if (mPackageName == null) {
+ Log.e(TAG, "Package name cannot be null!");
+ }
+ int status = getIntFromXml(parser, ATTR_STATUS, -1);
+ if (status == -1) {
+ Log.e(TAG, "Unknown status value: " + status);
+ }
+ mMainStatus = status;
+
+ ArrayList<String> list = new ArrayList<>();
+ int outerDepth = parser.getDepth();
+ int type;
+ while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG
+ || parser.getDepth() > outerDepth)) {
+ if (type == XmlPullParser.END_TAG
+ || type == XmlPullParser.TEXT) {
+ continue;
+ }
+
+ String tagName = parser.getName();
+ if (tagName.equals(TAG_DOMAIN)) {
+ String name = getStringFromXml(parser, ATTR_DOMAIN_NAME, null);
+ if (!TextUtils.isEmpty(name)) {
+ if (list == null) {
+ list = new ArrayList<>();
+ }
+ list.add(name);
+ }
+ } else {
+ Log.w(TAG, "Unknown tag parsing IntentFilter: " + tagName);
+ }
+ XmlUtils.skipCurrentTag(parser);
+ }
+
+ mDomains = list.toArray(new String[list.size()]);
+ }
+
+ public void writeToXml(XmlSerializer serializer) throws IOException {
+ serializer.attribute(null, ATTR_PACKAGE_NAME, mPackageName);
+ serializer.attribute(null, ATTR_STATUS, String.valueOf(mMainStatus));
+ for (String str : mDomains) {
+ serializer.startTag(null, TAG_DOMAIN);
+ serializer.attribute(null, ATTR_DOMAIN_NAME, str);
+ serializer.endTag(null, TAG_DOMAIN);
+ }
+ }
+
+ public String getStatusString() {
+ return getStatusStringFromValue(mMainStatus);
+ }
+
+ public static String getStatusStringFromValue(int val) {
+ switch (val) {
+ case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK : return "ask";
+ case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS : return "always";
+ case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER : return "never";
+ default:
+ case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED : return "undefined";
+ }
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ private void readFromParcel(Parcel source) {
+ mPackageName = source.readString();
+ mMainStatus = source.readInt();
+ mDomains = source.readStringArray();
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(mPackageName);
+ dest.writeInt(mMainStatus);
+ dest.writeStringArray(mDomains);
+ }
+
+ public static final Creator<IntentFilterVerificationInfo> CREATOR =
+ new Creator<IntentFilterVerificationInfo>() {
+ public IntentFilterVerificationInfo createFromParcel(Parcel source) {
+ return new IntentFilterVerificationInfo(source);
+ }
+ public IntentFilterVerificationInfo[] newArray(int size) {
+ return new IntentFilterVerificationInfo[size];
+ }
+ };
+
+}
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 59a16da..46d6ffb3 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -969,6 +969,60 @@
public static final int VERIFICATION_REJECT = -1;
/**
+ * Used as the {@code verificationCode} argument for
+ * {@link PackageManager#verifyIntentFilter} to indicate that the calling
+ * IntentFilter Verifier confirms that the IntentFilter is verified.
+ *
+ * @hide
+ */
+ public static final int INTENT_FILTER_VERIFICATION_SUCCESS = 1;
+
+ /**
+ * Used as the {@code verificationCode} argument for
+ * {@link PackageManager#verifyIntentFilter} to indicate that the calling
+ * IntentFilter Verifier confirms that the IntentFilter is NOT verified.
+ *
+ * @hide
+ */
+ public static final int INTENT_FILTER_VERIFICATION_FAILURE = -1;
+
+ /**
+ * Internal status code to indicate that an IntentFilter verification result is not specified.
+ *
+ * @hide
+ */
+ public static final int INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED = 0;
+
+ /**
+ * Used as the {@code status} argument for {@link PackageManager#updateIntentVerificationStatus}
+ * to indicate that the User will always be prompted the Intent Disambiguation Dialog if there
+ * are two or more Intent resolved for the IntentFilter's domain(s).
+ *
+ * @hide
+ */
+ public static final int INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK = 1;
+
+ /**
+ * Used as the {@code status} argument for {@link PackageManager#updateIntentVerificationStatus}
+ * to indicate that the User will never be prompted the Intent Disambiguation Dialog if there
+ * are two or more resolution of the Intent. The default App for the domain(s) specified in the
+ * IntentFilter will also ALWAYS be used.
+ *
+ * @hide
+ */
+ public static final int INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS = 2;
+
+ /**
+ * Used as the {@code status} argument for {@link PackageManager#updateIntentVerificationStatus}
+ * to indicate that the User may be prompted the Intent Disambiguation Dialog if there
+ * are two or more Intent resolved. The default App for the domain(s) specified in the
+ * IntentFilter will also NEVER be presented to the User.
+ *
+ * @hide
+ */
+ public static final int INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER = 3;
+
+ /**
* Can be used as the {@code millisecondsToDelay} argument for
* {@link PackageManager#extendVerificationTimeout}. This is the
* maximum time {@code PackageManager} waits for the verification
@@ -1600,6 +1654,12 @@
@SdkConstant(SdkConstantType.FEATURE)
public static final String FEATURE_GAMEPAD = "android.hardware.gamepad";
+ /**
+ * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
+ * The device has a full implementation of the android.media.midi.* APIs.
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_MIDI = "android.software.midi";
/**
* Action to external storage service to clean out removed apps.
@@ -1674,8 +1734,52 @@
= "android.content.pm.extra.VERIFICATION_VERSION_CODE";
/**
- * The action used to request that the user approve a grant permissions
- * request from the application.
+ * Extra field name for the ID of a intent filter pending verification. Passed to
+ * an intent filter verifier and is used to call back to
+ * {@link PackageManager#verifyIntentFilter(int, int)}
+ *
+ * @hide
+ */
+ public static final String EXTRA_INTENT_FILTER_VERIFICATION_ID
+ = "android.content.pm.extra.INTENT_FILTER_VERIFICATION_ID";
+
+ /**
+ * Extra field name for the scheme used for an intent filter pending verification. Passed to
+ * an intent filter verifier and is used to construct the URI to verify against.
+ *
+ * Usually this is "https"
+ *
+ * @hide
+ */
+ public static final String EXTRA_INTENT_FILTER_VERIFICATION_URI_SCHEME
+ = "android.content.pm.extra.INTENT_FILTER_VERIFICATION_URI_SCHEME";
+
+ /**
+ * Extra field name for the host names to be used for an intent filter pending verification.
+ * Passed to an intent filter verifier and is used to construct the URI to verify the
+ * intent filter.
+ *
+ * This is a space delimited list of hosts.
+ *
+ * @hide
+ */
+ public static final String EXTRA_INTENT_FILTER_VERIFICATION_HOSTS
+ = "android.content.pm.extra.INTENT_FILTER_VERIFICATION_HOSTS";
+
+ /**
+ * Extra field name for the package name to be used for an intent filter pending verification.
+ * Passed to an intent filter verifier and is used to check the verification responses coming
+ * from the hosts. Each host response will need to include the package name of APK containing
+ * the intent filter.
+ *
+ * @hide
+ */
+ public static final String EXTRA_INTENT_FILTER_VERIFICATION_PACKAGE_NAME
+ = "android.content.pm.extra.INTENT_FILTER_VERIFICATION_PACKAGE_NAME";
+
+ /**
+ * The action used to request that the user approve a permission request
+ * from the application.
*
* @hide
*/
@@ -3455,6 +3559,85 @@
int verificationCodeAtTimeout, long millisecondsToDelay);
/**
+ * Allows a package listening to the
+ * {@link Intent#ACTION_INTENT_FILTER_NEEDS_VERIFICATION intent filter verification
+ * broadcast} to respond to the package manager. The response must include
+ * the {@code verificationCode} which is one of
+ * {@link PackageManager#INTENT_FILTER_VERIFICATION_SUCCESS} or
+ * {@link PackageManager#INTENT_FILTER_VERIFICATION_FAILURE}.
+ *
+ * @param verificationId pending package identifier as passed via the
+ * {@link PackageManager#EXTRA_VERIFICATION_ID} Intent extra.
+ * @param verificationCode either {@link PackageManager#INTENT_FILTER_VERIFICATION_SUCCESS}
+ * or {@link PackageManager#INTENT_FILTER_VERIFICATION_FAILURE}.
+ * @param outFailedDomains a list of failed domains if the verificationCode is
+ * {@link PackageManager#INTENT_FILTER_VERIFICATION_FAILURE}, otherwise null;
+ * @throws SecurityException if the caller does not have the
+ * INTENT_FILTER_VERIFICATION_AGENT permission.
+ *
+ * @hide
+ */
+ public abstract void verifyIntentFilter(int verificationId, int verificationCode,
+ List<String> outFailedDomains);
+
+ /**
+ * Get the status of a Domain Verification Result for an IntentFilter. This is
+ * related to the {@link android.content.IntentFilter#setAutoVerify(boolean)} and
+ * {@link android.content.IntentFilter#getAutoVerify()}
+ *
+ * This is used by the ResolverActivity to change the status depending on what the User select
+ * in the Disambiguation Dialog and also used by the Settings App for changing the default App
+ * for a domain.
+ *
+ * @param packageName The package name of the Activity associated with the IntentFilter.
+ * @param userId The user id.
+ *
+ * @return The status to set to. This can be
+ * {@link #INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK} or
+ * {@link #INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS} or
+ * {@link #INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER} or
+ * {@link #INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED}
+ *
+ * @hide
+ */
+ public abstract int getIntentVerificationStatus(String packageName, int userId);
+
+ /**
+ * Allow to change the status of a Intent Verification status for all IntentFilter of an App.
+ * This is related to the {@link android.content.IntentFilter#setAutoVerify(boolean)} and
+ * {@link android.content.IntentFilter#getAutoVerify()}
+ *
+ * This is used by the ResolverActivity to change the status depending on what the User select
+ * in the Disambiguation Dialog and also used by the Settings App for changing the default App
+ * for a domain.
+ *
+ * @param packageName The package name of the Activity associated with the IntentFilter.
+ * @param status The status to set to. This can be
+ * {@link #INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK} or
+ * {@link #INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS} or
+ * {@link #INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER}
+ * @param userId The user id.
+ *
+ * @return true if the status has been set. False otherwise.
+ *
+ * @hide
+ */
+ public abstract boolean updateIntentVerificationStatus(String packageName, int status,
+ int userId);
+
+ /**
+ * Get the list of IntentFilterVerificationInfo for a specific package and User.
+ *
+ * @param packageName the package name. When this parameter is set to a non null value,
+ * the results will be filtered by the package name provided.
+ * Otherwise, there will be no filtering and it will return a list
+ * corresponding for all packages for the provided userId.
+ * @return a list of IntentFilterVerificationInfo for a specific package and User.
+ */
+ public abstract List<IntentFilterVerificationInfo> getIntentFilterVerifications(
+ String packageName);
+
+ /**
* Change the installer associated with a given package. There are limitations
* on how the installer package can be changed; in particular:
* <ul>
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 212cf6d..4b81fd4 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -2750,15 +2750,21 @@
}
}
- addSharedLibrariesForBackwardCompatibility(owner);
+ modifySharedLibrariesForBackwardCompatibility(owner);
return true;
}
- private static void addSharedLibrariesForBackwardCompatibility(Package owner) {
- if (owner.applicationInfo.targetSdkVersion <= Build.VERSION_CODES.LOLLIPOP_MR1) {
- owner.usesLibraries = ArrayUtils.add(owner.usesLibraries, "org.apache.http.legacy");
- }
+ private static void modifySharedLibrariesForBackwardCompatibility(Package owner) {
+ // "org.apache.http.legacy" is now a part of the boot classpath so it doesn't need
+ // to be an explicit dependency.
+ //
+ // A future change will remove this library from the boot classpath, at which point
+ // all apps that target SDK 21 and earlier will have it automatically added to their
+ // dependency lists.
+ owner.usesLibraries = ArrayUtils.remove(owner.usesLibraries, "org.apache.http.legacy");
+ owner.usesOptionalLibraries = ArrayUtils.remove(owner.usesOptionalLibraries,
+ "org.apache.http.legacy");
}
/**
@@ -3149,7 +3155,7 @@
if (parser.getName().equals("intent-filter")) {
ActivityIntentInfo intent = new ActivityIntentInfo(a);
- if (!parseIntent(res, parser, attrs, true, intent, outError)) {
+ if (!parseIntent(res, parser, attrs, true, true, intent, outError)) {
return null;
}
if (intent.countActions() == 0) {
@@ -3161,7 +3167,7 @@
}
} else if (!receiver && parser.getName().equals("preferred")) {
ActivityIntentInfo intent = new ActivityIntentInfo(a);
- if (!parseIntent(res, parser, attrs, false, intent, outError)) {
+ if (!parseIntent(res, parser, attrs, false, false, intent, outError)) {
return null;
}
if (intent.countActions() == 0) {
@@ -3341,7 +3347,7 @@
if (parser.getName().equals("intent-filter")) {
ActivityIntentInfo intent = new ActivityIntentInfo(a);
- if (!parseIntent(res, parser, attrs, true, intent, outError)) {
+ if (!parseIntent(res, parser, attrs, true, true, intent, outError)) {
return null;
}
if (intent.countActions() == 0) {
@@ -3521,7 +3527,7 @@
if (parser.getName().equals("intent-filter")) {
ProviderIntentInfo intent = new ProviderIntentInfo(outInfo);
- if (!parseIntent(res, parser, attrs, true, intent, outError)) {
+ if (!parseIntent(res, parser, attrs, true, false, intent, outError)) {
return false;
}
outInfo.intents.add(intent);
@@ -3780,7 +3786,7 @@
if (parser.getName().equals("intent-filter")) {
ServiceIntentInfo intent = new ServiceIntentInfo(s);
- if (!parseIntent(res, parser, attrs, true, intent, outError)) {
+ if (!parseIntent(res, parser, attrs, true, false, intent, outError)) {
return null;
}
@@ -3981,7 +3987,7 @@
= "http://schemas.android.com/apk/res/android";
private boolean parseIntent(Resources res, XmlPullParser parser, AttributeSet attrs,
- boolean allowGlobs, IntentInfo outInfo, String[] outError)
+ boolean allowGlobs, boolean allowAutoVerify, IntentInfo outInfo, String[] outError)
throws XmlPullParserException, IOException {
TypedArray sa = res.obtainAttributes(attrs,
@@ -4006,6 +4012,12 @@
outInfo.banner = sa.getResourceId(
com.android.internal.R.styleable.AndroidManifestIntentFilter_banner, 0);
+ if (allowAutoVerify) {
+ outInfo.setAutoVerify(sa.getBoolean(
+ com.android.internal.R.styleable.AndroidManifestIntentFilter_autoVerify,
+ false));
+ }
+
sa.recycle();
int outerDepth = parser.getDepth();
diff --git a/core/java/android/content/pm/PackageUserState.java b/core/java/android/content/pm/PackageUserState.java
index a9c7be3..92b8055 100644
--- a/core/java/android/content/pm/PackageUserState.java
+++ b/core/java/android/content/pm/PackageUserState.java
@@ -37,10 +37,14 @@
public ArraySet<String> disabledComponents;
public ArraySet<String> enabledComponents;
+ public int domainVerificationStatus;
+
public PackageUserState() {
installed = true;
hidden = false;
enabled = COMPONENT_ENABLED_STATE_DEFAULT;
+ domainVerificationStatus =
+ PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
}
public PackageUserState(PackageUserState o) {
@@ -51,9 +55,10 @@
hidden = o.hidden;
lastDisableAppCaller = o.lastDisableAppCaller;
disabledComponents = o.disabledComponents != null
- ? new ArraySet<String>(o.disabledComponents) : null;
+ ? new ArraySet<>(o.disabledComponents) : null;
enabledComponents = o.enabledComponents != null
- ? new ArraySet<String>(o.enabledComponents) : null;
+ ? new ArraySet<>(o.enabledComponents) : null;
blockUninstall = o.blockUninstall;
+ domainVerificationStatus = o.domainVerificationStatus;
}
}
diff --git a/core/java/android/content/pm/ResolveInfo.java b/core/java/android/content/pm/ResolveInfo.java
index fe3aec9..7b141f0 100644
--- a/core/java/android/content/pm/ResolveInfo.java
+++ b/core/java/android/content/pm/ResolveInfo.java
@@ -143,6 +143,11 @@
*/
public boolean system;
+ /**
+ * @hide Does the associated IntentFilter needs verification ?
+ */
+ public boolean filterNeedsVerification;
+
private ComponentInfo getComponentInfo() {
if (activityInfo != null) return activityInfo;
if (serviceInfo != null) return serviceInfo;
@@ -283,6 +288,7 @@
resolvePackageName = orig.resolvePackageName;
system = orig.system;
targetUserId = orig.targetUserId;
+ filterNeedsVerification = orig.filterNeedsVerification;
}
public String toString() {
@@ -344,6 +350,7 @@
dest.writeInt(targetUserId);
dest.writeInt(system ? 1 : 0);
dest.writeInt(noResourceId ? 1 : 0);
+ dest.writeInt(filterNeedsVerification ? 1 : 0);
}
public static final Creator<ResolveInfo> CREATOR
@@ -389,6 +396,7 @@
targetUserId = source.readInt();
system = source.readInt() != 0;
noResourceId = source.readInt() != 0;
+ filterNeedsVerification = source.readInt() != 0;
}
public static class DisplayNameComparator
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index 14af584..b5eeb30 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -1314,7 +1314,8 @@
* {@link View#LAYOUT_DIRECTION_LTR}. If not null will set it to the layout direction
* corresponding to the Locale.
*
- * @see {@link View#LAYOUT_DIRECTION_LTR} and {@link View#LAYOUT_DIRECTION_RTL}
+ * @see View#LAYOUT_DIRECTION_LTR
+ * @see View#LAYOUT_DIRECTION_RTL
*/
public void setLayoutDirection(Locale locale) {
// There is a "1" difference between the configuration values for
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index 95ad57e..44018ff 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -111,12 +111,12 @@
// single-threaded, and after that these are immutable.
private static final LongSparseArray<ConstantState>[] sPreloadedDrawables;
private static final LongSparseArray<ConstantState> sPreloadedColorDrawables
- = new LongSparseArray<ConstantState>();
+ = new LongSparseArray<>();
private static final LongSparseArray<ColorStateListFactory> sPreloadedColorStateLists
- = new LongSparseArray<ColorStateListFactory>();
+ = new LongSparseArray<>();
// Pool of TypedArrays targeted to this Resources object.
- final SynchronizedPool<TypedArray> mTypedArrayPool = new SynchronizedPool<TypedArray>(5);
+ final SynchronizedPool<TypedArray> mTypedArrayPool = new SynchronizedPool<>(5);
// Used by BridgeResources in layoutlib
static Resources mSystem = null;
@@ -128,21 +128,19 @@
private final Object mAccessLock = new Object();
private final Configuration mTmpConfig = new Configuration();
private final ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>> mDrawableCache =
- new ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>>();
+ new ArrayMap<>();
private final ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>> mColorDrawableCache =
- new ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>>();
+ new ArrayMap<>();
private final ConfigurationBoundResourceCache<ColorStateList> mColorStateListCache =
- new ConfigurationBoundResourceCache<ColorStateList>(this);
+ new ConfigurationBoundResourceCache<>(this);
private final ConfigurationBoundResourceCache<Animator> mAnimatorCache =
- new ConfigurationBoundResourceCache<Animator>(this);
+ new ConfigurationBoundResourceCache<>(this);
private final ConfigurationBoundResourceCache<StateListAnimator> mStateListAnimatorCache =
- new ConfigurationBoundResourceCache<StateListAnimator>(this);
+ new ConfigurationBoundResourceCache<>(this);
private TypedValue mTmpValue = new TypedValue();
private boolean mPreloading;
- private TypedArray mCachedStyledAttributes = null;
-
private int mLastCachedXmlBlockIndex = -1;
private final int[] mCachedXmlBlockIds = { 0, 0, 0, 0 };
private final XmlBlock[] mCachedXmlBlocks = new XmlBlock[4];
@@ -157,8 +155,8 @@
static {
sPreloadedDrawables = new LongSparseArray[2];
- sPreloadedDrawables[0] = new LongSparseArray<ConstantState>();
- sPreloadedDrawables[1] = new LongSparseArray<ConstantState>();
+ sPreloadedDrawables[0] = new LongSparseArray<>();
+ sPreloadedDrawables[1] = new LongSparseArray<>();
}
/**
@@ -1876,7 +1874,7 @@
// the framework.
mCompatibilityInfo.applyToDisplayMetrics(mMetrics);
- int configChanges = calcConfigChanges(config);
+ final int configChanges = calcConfigChanges(config);
if (mConfiguration.locale == null) {
mConfiguration.locale = Locale.getDefault();
mConfiguration.setLayoutDirection(mConfiguration.locale);
@@ -1891,7 +1889,8 @@
if (mConfiguration.locale != null) {
locale = adjustLanguageTag(mConfiguration.locale.toLanguageTag());
}
- int width, height;
+
+ final int width, height;
if (mMetrics.widthPixels >= mMetrics.heightPixels) {
width = mMetrics.widthPixels;
height = mMetrics.heightPixels;
@@ -1901,12 +1900,15 @@
//noinspection SuspiciousNameCombination
height = mMetrics.widthPixels;
}
- int keyboardHidden = mConfiguration.keyboardHidden;
- if (keyboardHidden == Configuration.KEYBOARDHIDDEN_NO
- && mConfiguration.hardKeyboardHidden
- == Configuration.HARDKEYBOARDHIDDEN_YES) {
+
+ final int keyboardHidden;
+ if (mConfiguration.keyboardHidden == Configuration.KEYBOARDHIDDEN_NO
+ && mConfiguration.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_YES) {
keyboardHidden = Configuration.KEYBOARDHIDDEN_SOFT;
+ } else {
+ keyboardHidden = mConfiguration.keyboardHidden;
}
+
mAssets.setConfiguration(mConfiguration.mcc, mConfiguration.mnc,
locale, mConfiguration.orientation,
mConfiguration.touchscreen,
@@ -2508,10 +2510,10 @@
// Clean out the caches before we add more. This shouldn't
// happen very often.
pruneCaches(caches);
- themedCache = new LongSparseArray<WeakReference<ConstantState>>(1);
+ themedCache = new LongSparseArray<>(1);
caches.put(themeKey, themedCache);
}
- themedCache.put(key, new WeakReference<ConstantState>(cs));
+ themedCache.put(key, new WeakReference<>(cs));
}
}
}
@@ -2830,15 +2832,6 @@
+ Integer.toHexString(id));
}
- /*package*/ void recycleCachedStyledAttributes(TypedArray attrs) {
- synchronized (mAccessLock) {
- final TypedArray cached = mCachedStyledAttributes;
- if (cached == null || cached.mData.length < attrs.mData.length) {
- mCachedStyledAttributes = attrs;
- }
- }
- }
-
/**
* Obtains styled attributes from the theme, if available, or unstyled
* resources if the theme is null.
diff --git a/core/java/android/database/DatabaseErrorHandler.java b/core/java/android/database/DatabaseErrorHandler.java
index f0c5452..55ad921 100644
--- a/core/java/android/database/DatabaseErrorHandler.java
+++ b/core/java/android/database/DatabaseErrorHandler.java
@@ -19,13 +19,12 @@
import android.database.sqlite.SQLiteDatabase;
/**
- * An interface to let the apps define the actions to take when the following errors are detected
- * database corruption
+ * An interface to let apps define an action to take when database corruption is detected.
*/
public interface DatabaseErrorHandler {
/**
- * defines the method to be invoked when database corruption is detected.
+ * The method invoked when database corruption is detected.
* @param dbObj the {@link SQLiteDatabase} object representing the database on which corruption
* is detected.
*/
diff --git a/core/java/android/database/DefaultDatabaseErrorHandler.java b/core/java/android/database/DefaultDatabaseErrorHandler.java
index b234e34..7fa2b40 100755
--- a/core/java/android/database/DefaultDatabaseErrorHandler.java
+++ b/core/java/android/database/DefaultDatabaseErrorHandler.java
@@ -24,7 +24,7 @@
import android.util.Pair;
/**
- * Default class used to define the actions to take when the database corruption is reported
+ * Default class used to define the action to take when database corruption is reported
* by sqlite.
* <p>
* An application can specify an implementation of {@link DatabaseErrorHandler} on the
@@ -38,7 +38,7 @@
* The specified {@link DatabaseErrorHandler} is used to handle database corruption errors, if they
* occur.
* <p>
- * If null is specified for DatabaeErrorHandler param in the above calls, then this class is used
+ * If null is specified for the DatabaseErrorHandler param in the above calls, this class is used
* as the default {@link DatabaseErrorHandler}.
*/
public final class DefaultDatabaseErrorHandler implements DatabaseErrorHandler {
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index a0217c2..1503bf5 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -991,7 +991,8 @@
* <ul>
* <li>Processed (but stalling): any non-RAW format with a stallDurations > 0.
* Typically JPEG format (ImageFormat#JPEG).</li>
- * <li>Raw formats: ImageFormat#RAW_SENSOR, ImageFormat#RAW10 and ImageFormat#RAW_OPAQUE.</li>
+ * <li>Raw formats: ImageFormat#RAW_SENSOR, ImageFormat#RAW10, ImageFormat#RAW12,
+ * and ImageFormat#RAW_OPAQUE.</li>
* <li>Processed (but not-stalling): any non-RAW format without a stall duration.
* Typically ImageFormat#YUV_420_888, ImageFormat#NV21, ImageFormat#YV12.</li>
* </ul>
@@ -1023,6 +1024,7 @@
* <ul>
* <li>ImageFormat#RAW_SENSOR</li>
* <li>ImageFormat#RAW10</li>
+ * <li>ImageFormat#RAW12</li>
* <li>Opaque <code>RAW</code></li>
* </ul>
* <p>LEGACY mode devices ({@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} <code>==</code> LEGACY)
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index 7569ea5..b8fb8e7 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -1541,7 +1541,8 @@
* to the camera, that the JPEG picture needs to be rotated by, to be viewed
* upright.</p>
* <p>Camera devices may either encode this value into the JPEG EXIF header, or
- * rotate the image data to match this orientation.</p>
+ * rotate the image data to match this orientation. When the image data is rotated,
+ * the thumbnail data will also be rotated.</p>
* <p>Note that this orientation is relative to the orientation of the camera sensor, given
* by {@link CameraCharacteristics#SENSOR_ORIENTATION android.sensor.orientation}.</p>
* <p>To translate from the device orientation given by the Android sensor APIs, the following
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index b84dc2e..e346dc2 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -2230,7 +2230,8 @@
* to the camera, that the JPEG picture needs to be rotated by, to be viewed
* upright.</p>
* <p>Camera devices may either encode this value into the JPEG EXIF header, or
- * rotate the image data to match this orientation.</p>
+ * rotate the image data to match this orientation. When the image data is rotated,
+ * the thumbnail data will also be rotated.</p>
* <p>Note that this orientation is relative to the orientation of the camera sensor, given
* by {@link CameraCharacteristics#SENSOR_ORIENTATION android.sensor.orientation}.</p>
* <p>To translate from the device orientation given by the Android sensor APIs, the following
diff --git a/core/java/android/hardware/hdmi/HdmiTvClient.java b/core/java/android/hardware/hdmi/HdmiTvClient.java
index a94c1da..a336e5c 100644
--- a/core/java/android/hardware/hdmi/HdmiTvClient.java
+++ b/core/java/android/hardware/hdmi/HdmiTvClient.java
@@ -168,7 +168,22 @@
}
/**
- * Sets system audio volume
+ * Sets system audio mode.
+ *
+ * @param enabled set to {@code true} to enable the mode; otherwise {@code false}
+ * @param callback callback to get the result with
+ * @throws {@link IllegalArgumentException} if the {@code callback} is null
+ */
+ public void setSystemAudioMode(boolean enabled, SelectCallback callback) {
+ try {
+ mService.setSystemAudioMode(enabled, getCallbackWrapper(callback));
+ } catch (RemoteException e) {
+ Log.e(TAG, "failed to set system audio mode:", e);
+ }
+ }
+
+ /**
+ * Sets system audio volume.
*
* @param oldIndex current volume index
* @param newIndex volume index to be set
@@ -183,7 +198,7 @@
}
/**
- * Sets system audio mute status
+ * Sets system audio mute status.
*
* @param mute {@code true} if muted; otherwise, {@code false}
*/
@@ -196,7 +211,7 @@
}
/**
- * Sets record listener
+ * Sets record listener.
*
* @param listener
*/
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index a0e2bf8..3abccbc 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -40,6 +40,7 @@
import android.util.ArrayMap;
import android.util.Log;
+import com.android.internal.net.VpnConfig;
import com.android.internal.telephony.ITelephony;
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.util.Protocol;
@@ -2447,6 +2448,38 @@
}
/**
+ * Resets all connectivity manager settings back to factory defaults.
+ * @hide
+ */
+ public void factoryReset() {
+ // Turn airplane mode off
+ setAirplaneMode(false);
+
+ // Untether
+ for (String tether : getTetheredIfaces()) {
+ untether(tether);
+ }
+
+ // Turn VPN off
+ try {
+ VpnConfig vpnConfig = mService.getVpnConfig();
+ if (vpnConfig != null) {
+ if (vpnConfig.legacy) {
+ mService.prepareVpn(VpnConfig.LEGACY_VPN, VpnConfig.LEGACY_VPN);
+ } else {
+ // Prevent this app from initiating VPN connections in the future without
+ // user intervention.
+ mService.setVpnPackageAuthorization(false);
+
+ mService.prepareVpn(vpnConfig.user, VpnConfig.LEGACY_VPN);
+ }
+ }
+ } catch (RemoteException e) {
+ // Well, we tried
+ }
+ }
+
+ /**
* Binds the current process to {@code network}. All Sockets created in the future
* (and not explicitly bound via a bound SocketFactory from
* {@link Network#getSocketFactory() Network.getSocketFactory()}) will be bound to
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index a8e7757..a7ffee9 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -180,6 +180,33 @@
}
/**
+ * Resets network policy settings back to factory defaults.
+ *
+ * @hide
+ */
+ public void factoryReset(String subscriber) {
+ // Turn mobile data limit off
+ NetworkPolicy[] policies = getNetworkPolicies();
+ NetworkTemplate template = NetworkTemplate.buildTemplateMobileAll(subscriber);
+ for (NetworkPolicy policy : policies) {
+ if (policy.template.equals(template)) {
+ policy.limitBytes = NetworkPolicy.LIMIT_DISABLED;
+ policy.inferred = false;
+ policy.clearSnooze();
+ }
+ }
+ setNetworkPolicies(policies);
+
+ // Turn restrict background data off
+ setRestrictBackground(false);
+
+ // Remove app's "restrict background data" flag
+ for (int uid : getUidsWithPolicy(POLICY_REJECT_METERED_BACKGROUND)) {
+ setUidPolicy(uid, NetworkPolicyManager.POLICY_NONE);
+ }
+ }
+
+ /**
* Compute the last cycle boundary for the given {@link NetworkPolicy}. For
* example, if cycle day is 20th, and today is June 15th, it will return May
* 20th. When cycle day doesn't exist in current month, it snaps to the 1st
diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java
index 0766253..77d7e0c 100644
--- a/core/java/android/net/NetworkStats.java
+++ b/core/java/android/net/NetworkStats.java
@@ -50,12 +50,19 @@
public static final int UID_ALL = -1;
/** {@link #tag} value matching any tag. */
public static final int TAG_ALL = -1;
- /** {@link #set} value when all sets combined. */
+ /** {@link #set} value when all sets combined, not including debug sets. */
public static final int SET_ALL = -1;
/** {@link #set} value where background data is accounted. */
public static final int SET_DEFAULT = 0;
/** {@link #set} value where foreground data is accounted. */
public static final int SET_FOREGROUND = 1;
+ /** All {@link #set} value greater than SET_DEBUG_START are debug {@link #set} values. */
+ public static final int SET_DEBUG_START = 1000;
+ /** Debug {@link #set} value when the VPN stats are moved in. */
+ public static final int SET_DBG_VPN_IN = 1001;
+ /** Debug {@link #set} value when the VPN stats are moved out of a vpn UID. */
+ public static final int SET_DBG_VPN_OUT = 1002;
+
/** {@link #tag} value for total data across all tags. */
public static final int TAG_NONE = 0;
@@ -729,6 +736,10 @@
return "DEFAULT";
case SET_FOREGROUND:
return "FOREGROUND";
+ case SET_DBG_VPN_IN:
+ return "DBG_VPN_IN";
+ case SET_DBG_VPN_OUT:
+ return "DBG_VPN_OUT";
default:
return "UNKNOWN";
}
@@ -745,12 +756,27 @@
return "def";
case SET_FOREGROUND:
return "fg";
+ case SET_DBG_VPN_IN:
+ return "vpnin";
+ case SET_DBG_VPN_OUT:
+ return "vpnout";
default:
return "unk";
}
}
/**
+ * @return true if the querySet matches the dataSet.
+ */
+ public static boolean setMatches(int querySet, int dataSet) {
+ if (querySet == dataSet) {
+ return true;
+ }
+ // SET_ALL matches all non-debugging sets.
+ return querySet == SET_ALL && dataSet < SET_DEBUG_START;
+ }
+
+ /**
* Return text description of {@link #tag} value.
*/
public static String tagToString(int tag) {
@@ -843,6 +869,9 @@
if (recycle.uid == UID_ALL) {
throw new IllegalStateException(
"Cannot adjust VPN accounting on an iface aggregated NetworkStats.");
+ } if (recycle.set == SET_DBG_VPN_IN || recycle.set == SET_DBG_VPN_OUT) {
+ throw new IllegalStateException(
+ "Cannot adjust VPN accounting on a NetworkStats containing SET_DBG_VPN_*");
}
if (recycle.uid == tunUid && recycle.tag == TAG_NONE
@@ -906,6 +935,9 @@
combineValues(tmpEntry);
if (tag[i] == TAG_NONE) {
moved.add(tmpEntry);
+ // Add debug info
+ tmpEntry.set = SET_DBG_VPN_IN;
+ combineValues(tmpEntry);
}
}
}
@@ -913,6 +945,13 @@
}
private void deductTrafficFromVpnApp(int tunUid, String underlyingIface, Entry moved) {
+ // Add debug info
+ moved.uid = tunUid;
+ moved.set = SET_DBG_VPN_OUT;
+ moved.tag = TAG_NONE;
+ moved.iface = underlyingIface;
+ combineValues(moved);
+
// Caveat: if the vpn software uses tag, the total tagged traffic may be greater than
// the TAG_NONE traffic.
int idxVpnBackground = findIndex(underlyingIface, tunUid, SET_DEFAULT, TAG_NONE);
diff --git a/core/java/android/net/TrafficStats.java b/core/java/android/net/TrafficStats.java
index 3f18519..ff3de2b 100644
--- a/core/java/android/net/TrafficStats.java
+++ b/core/java/android/net/TrafficStats.java
@@ -51,6 +51,8 @@
public static final long MB_IN_BYTES = KB_IN_BYTES * 1024;
/** @hide */
public static final long GB_IN_BYTES = MB_IN_BYTES * 1024;
+ /** @hide */
+ public static final long TB_IN_BYTES = GB_IN_BYTES * 1024;
/**
* Special UID value used when collecting {@link NetworkStatsHistory} for
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 1a06e0a..8b3ecae 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -3233,9 +3233,9 @@
if (!didOne) sb.append(" (no activity)");
pw.println(sb.toString());
- final long wifiIdleTimeMs = getBluetoothControllerActivity(CONTROLLER_IDLE_TIME, which);
- final long wifiRxTimeMs = getBluetoothControllerActivity(CONTROLLER_RX_TIME, which);
- final long wifiTxTimeMs = getBluetoothControllerActivity(CONTROLLER_TX_TIME, which);
+ final long wifiIdleTimeMs = getWifiControllerActivity(CONTROLLER_IDLE_TIME, which);
+ final long wifiRxTimeMs = getWifiControllerActivity(CONTROLLER_RX_TIME, which);
+ final long wifiTxTimeMs = getWifiControllerActivity(CONTROLLER_TX_TIME, which);
final long wifiTotalTimeMs = wifiIdleTimeMs + wifiRxTimeMs + wifiTxTimeMs;
sb.setLength(0);
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index 975bfc2..2db976e 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -18,16 +18,11 @@
import android.app.admin.DevicePolicyManager;
import android.content.Context;
-import android.os.storage.IMountService;
+import android.os.storage.StorageManager;
import android.os.storage.StorageVolume;
-import android.text.TextUtils;
import android.util.Log;
-import com.google.android.collect.Lists;
-
import java.io.File;
-import java.io.IOException;
-import java.util.ArrayList;
/**
* Provides access to environment variables.
@@ -36,11 +31,9 @@
private static final String TAG = "Environment";
private static final String ENV_EXTERNAL_STORAGE = "EXTERNAL_STORAGE";
- private static final String ENV_EMULATED_STORAGE_SOURCE = "EMULATED_STORAGE_SOURCE";
- private static final String ENV_EMULATED_STORAGE_TARGET = "EMULATED_STORAGE_TARGET";
- private static final String ENV_MEDIA_STORAGE = "MEDIA_STORAGE";
- private static final String ENV_SECONDARY_STORAGE = "SECONDARY_STORAGE";
private static final String ENV_ANDROID_ROOT = "ANDROID_ROOT";
+ private static final String ENV_ANDROID_DATA = "ANDROID_DATA";
+ private static final String ENV_ANDROID_STORAGE = "ANDROID_STORAGE";
private static final String ENV_OEM_ROOT = "OEM_ROOT";
private static final String ENV_VENDOR_ROOT = "VENDOR_ROOT";
@@ -57,12 +50,10 @@
public static final String DIRECTORY_ANDROID = DIR_ANDROID;
private static final File DIR_ANDROID_ROOT = getDirectory(ENV_ANDROID_ROOT, "/system");
+ private static final File DIR_ANDROID_DATA = getDirectory(ENV_ANDROID_DATA, "/data");
+ private static final File DIR_ANDROID_STORAGE = getDirectory(ENV_ANDROID_STORAGE, "/storage");
private static final File DIR_OEM_ROOT = getDirectory(ENV_OEM_ROOT, "/oem");
private static final File DIR_VENDOR_ROOT = getDirectory(ENV_VENDOR_ROOT, "/vendor");
- private static final File DIR_MEDIA_STORAGE = getDirectory(ENV_MEDIA_STORAGE, "/data/media");
-
- private static final String CANONCIAL_EMULATED_STORAGE_TARGET = getCanonicalPathOrNull(
- ENV_EMULATED_STORAGE_TARGET);
private static final String SYSTEM_PROPERTY_EFS_ENABLED = "persist.security.efs.enabled";
@@ -81,73 +72,24 @@
/** {@hide} */
public static class UserEnvironment {
- // TODO: generalize further to create package-specific environment
-
- /** External storage dirs, as visible to vold */
- private final File[] mExternalDirsForVold;
- /** External storage dirs, as visible to apps */
- private final File[] mExternalDirsForApp;
- /** Primary emulated storage dir for direct access */
- private final File mEmulatedDirForDirect;
+ private final int mUserId;
public UserEnvironment(int userId) {
- // See storage config details at http://source.android.com/tech/storage/
- String rawExternalStorage = System.getenv(ENV_EXTERNAL_STORAGE);
- String rawEmulatedSource = System.getenv(ENV_EMULATED_STORAGE_SOURCE);
- String rawEmulatedTarget = System.getenv(ENV_EMULATED_STORAGE_TARGET);
+ mUserId = userId;
+ }
- String rawMediaStorage = System.getenv(ENV_MEDIA_STORAGE);
- if (TextUtils.isEmpty(rawMediaStorage)) {
- rawMediaStorage = "/data/media";
+ public File[] getExternalDirs() {
+ final StorageVolume[] volumes = StorageManager.getVolumeList(mUserId);
+ final File[] dirs = new File[volumes.length];
+ for (int i = 0; i < volumes.length; i++) {
+ dirs[i] = volumes[i].getPathFile();
}
-
- ArrayList<File> externalForVold = Lists.newArrayList();
- ArrayList<File> externalForApp = Lists.newArrayList();
-
- if (!TextUtils.isEmpty(rawEmulatedTarget)) {
- // Device has emulated storage; external storage paths should have
- // userId burned into them.
- final String rawUserId = Integer.toString(userId);
- final File emulatedSourceBase = new File(rawEmulatedSource);
- final File emulatedTargetBase = new File(rawEmulatedTarget);
- final File mediaBase = new File(rawMediaStorage);
-
- // /storage/emulated/0
- externalForVold.add(buildPath(emulatedSourceBase, rawUserId));
- externalForApp.add(buildPath(emulatedTargetBase, rawUserId));
- // /data/media/0
- mEmulatedDirForDirect = buildPath(mediaBase, rawUserId);
-
- } else {
- // Device has physical external storage; use plain paths.
- if (TextUtils.isEmpty(rawExternalStorage)) {
- Log.w(TAG, "EXTERNAL_STORAGE undefined; falling back to default");
- rawExternalStorage = "/storage/sdcard0";
- }
-
- // /storage/sdcard0
- externalForVold.add(new File(rawExternalStorage));
- externalForApp.add(new File(rawExternalStorage));
- // /data/media
- mEmulatedDirForDirect = new File(rawMediaStorage);
- }
-
- // Splice in any secondary storage paths, but only for owner
- final String rawSecondaryStorage = System.getenv(ENV_SECONDARY_STORAGE);
- if (!TextUtils.isEmpty(rawSecondaryStorage) && userId == UserHandle.USER_OWNER) {
- for (String secondaryPath : rawSecondaryStorage.split(":")) {
- externalForVold.add(new File(secondaryPath));
- externalForApp.add(new File(secondaryPath));
- }
- }
-
- mExternalDirsForVold = externalForVold.toArray(new File[externalForVold.size()]);
- mExternalDirsForApp = externalForApp.toArray(new File[externalForApp.size()]);
+ return dirs;
}
@Deprecated
public File getExternalStorageDirectory() {
- return mExternalDirsForApp[0];
+ return getExternalDirs()[0];
}
@Deprecated
@@ -155,60 +97,36 @@
return buildExternalStoragePublicDirs(type)[0];
}
- public File[] getExternalDirsForVold() {
- return mExternalDirsForVold;
- }
-
- public File[] getExternalDirsForApp() {
- return mExternalDirsForApp;
- }
-
- public File getMediaDir() {
- return mEmulatedDirForDirect;
- }
-
public File[] buildExternalStoragePublicDirs(String type) {
- return buildPaths(mExternalDirsForApp, type);
+ return buildPaths(getExternalDirs(), type);
}
public File[] buildExternalStorageAndroidDataDirs() {
- return buildPaths(mExternalDirsForApp, DIR_ANDROID, DIR_DATA);
+ return buildPaths(getExternalDirs(), DIR_ANDROID, DIR_DATA);
}
public File[] buildExternalStorageAndroidObbDirs() {
- return buildPaths(mExternalDirsForApp, DIR_ANDROID, DIR_OBB);
+ return buildPaths(getExternalDirs(), DIR_ANDROID, DIR_OBB);
}
public File[] buildExternalStorageAppDataDirs(String packageName) {
- return buildPaths(mExternalDirsForApp, DIR_ANDROID, DIR_DATA, packageName);
- }
-
- public File[] buildExternalStorageAppDataDirsForVold(String packageName) {
- return buildPaths(mExternalDirsForVold, DIR_ANDROID, DIR_DATA, packageName);
+ return buildPaths(getExternalDirs(), DIR_ANDROID, DIR_DATA, packageName);
}
public File[] buildExternalStorageAppMediaDirs(String packageName) {
- return buildPaths(mExternalDirsForApp, DIR_ANDROID, DIR_MEDIA, packageName);
- }
-
- public File[] buildExternalStorageAppMediaDirsForVold(String packageName) {
- return buildPaths(mExternalDirsForVold, DIR_ANDROID, DIR_MEDIA, packageName);
+ return buildPaths(getExternalDirs(), DIR_ANDROID, DIR_MEDIA, packageName);
}
public File[] buildExternalStorageAppObbDirs(String packageName) {
- return buildPaths(mExternalDirsForApp, DIR_ANDROID, DIR_OBB, packageName);
- }
-
- public File[] buildExternalStorageAppObbDirsForVold(String packageName) {
- return buildPaths(mExternalDirsForVold, DIR_ANDROID, DIR_OBB, packageName);
+ return buildPaths(getExternalDirs(), DIR_ANDROID, DIR_OBB, packageName);
}
public File[] buildExternalStorageAppFilesDirs(String packageName) {
- return buildPaths(mExternalDirsForApp, DIR_ANDROID, DIR_DATA, packageName, DIR_FILES);
+ return buildPaths(getExternalDirs(), DIR_ANDROID, DIR_DATA, packageName, DIR_FILES);
}
public File[] buildExternalStorageAppCacheDirs(String packageName) {
- return buildPaths(mExternalDirsForApp, DIR_ANDROID, DIR_DATA, packageName, DIR_CACHE);
+ return buildPaths(getExternalDirs(), DIR_ANDROID, DIR_DATA, packageName, DIR_CACHE);
}
}
@@ -220,6 +138,11 @@
return DIR_ANDROID_ROOT;
}
+ /** {@hide} */
+ public static File getStorageDirectory() {
+ return DIR_ANDROID_STORAGE;
+ }
+
/**
* Return root directory of the "oem" partition holding OEM customizations,
* if any. If present, the partition is mounted read-only.
@@ -270,17 +193,6 @@
}
/**
- * Return directory used for internal media storage, which is protected by
- * {@link android.Manifest.permission#WRITE_MEDIA_STORAGE}.
- *
- * @hide
- */
- public static File getMediaStorageDirectory() {
- throwIfUserRequired();
- return sCurrentUser.getMediaDir();
- }
-
- /**
* Return the system directory for a user. This is for use by system services to store
* files relating to the user. This directory will be automatically deleted when the user
* is removed.
@@ -389,7 +301,7 @@
*/
public static File getExternalStorageDirectory() {
throwIfUserRequired();
- return sCurrentUser.getExternalDirsForApp()[0];
+ return sCurrentUser.getExternalDirs()[0];
}
/** {@hide} */
@@ -402,18 +314,6 @@
return buildPath(getLegacyExternalStorageDirectory(), DIR_ANDROID, DIR_OBB);
}
- /** {@hide} */
- public static File getEmulatedStorageSource(int userId) {
- // /mnt/shell/emulated/0
- return new File(System.getenv(ENV_EMULATED_STORAGE_SOURCE), String.valueOf(userId));
- }
-
- /** {@hide} */
- public static File getEmulatedStorageObbSource() {
- // /mnt/shell/emulated/obb
- return new File(System.getenv(ENV_EMULATED_STORAGE_SOURCE), DIR_OBB);
- }
-
/**
* Standard directory in which to place any audio files that should be
* in the regular list of music for the user.
@@ -683,6 +583,13 @@
public static final String MEDIA_UNMOUNTABLE = "unmountable";
/**
+ * Storage state if the media is in the process of being ejected.
+ *
+ * @see #getExternalStorageState(File)
+ */
+ public static final String MEDIA_EJECTING = "ejecting";
+
+ /**
* Returns the current state of the primary "external" storage device.
*
* @see #getExternalStorageDirectory()
@@ -693,7 +600,7 @@
* {@link #MEDIA_BAD_REMOVAL}, or {@link #MEDIA_UNMOUNTABLE}.
*/
public static String getExternalStorageState() {
- final File externalDir = sCurrentUser.getExternalDirsForApp()[0];
+ final File externalDir = sCurrentUser.getExternalDirs()[0];
return getExternalStorageState(externalDir);
}
@@ -716,17 +623,12 @@
* {@link #MEDIA_BAD_REMOVAL}, or {@link #MEDIA_UNMOUNTABLE}.
*/
public static String getExternalStorageState(File path) {
- final StorageVolume volume = getStorageVolume(path);
+ final StorageVolume volume = StorageManager.getStorageVolume(path, UserHandle.myUserId());
if (volume != null) {
- final IMountService mountService = IMountService.Stub.asInterface(
- ServiceManager.getService("mount"));
- try {
- return mountService.getVolumeState(volume.getPath());
- } catch (RemoteException e) {
- }
+ return volume.getState();
+ } else {
+ return MEDIA_UNKNOWN;
}
-
- return Environment.MEDIA_UNKNOWN;
}
/**
@@ -738,7 +640,7 @@
*/
public static boolean isExternalStorageRemovable() {
if (isStorageDisabled()) return false;
- final File externalDir = sCurrentUser.getExternalDirsForApp()[0];
+ final File externalDir = sCurrentUser.getExternalDirs()[0];
return isExternalStorageRemovable(externalDir);
}
@@ -753,7 +655,7 @@
* device.
*/
public static boolean isExternalStorageRemovable(File path) {
- final StorageVolume volume = getStorageVolume(path);
+ final StorageVolume volume = StorageManager.getStorageVolume(path, UserHandle.myUserId());
if (volume != null) {
return volume.isRemovable();
} else {
@@ -771,7 +673,7 @@
*/
public static boolean isExternalStorageEmulated() {
if (isStorageDisabled()) return false;
- final File externalDir = sCurrentUser.getExternalDirsForApp()[0];
+ final File externalDir = sCurrentUser.getExternalDirs()[0];
return isExternalStorageEmulated(externalDir);
}
@@ -784,7 +686,7 @@
* device.
*/
public static boolean isExternalStorageEmulated(File path) {
- final StorageVolume volume = getStorageVolume(path);
+ final StorageVolume volume = StorageManager.getStorageVolume(path, UserHandle.myUserId());
if (volume != null) {
return volume.isEmulated();
} else {
@@ -797,19 +699,6 @@
return path == null ? new File(defaultPath) : new File(path);
}
- private static String getCanonicalPathOrNull(String variableName) {
- String path = System.getenv(variableName);
- if (path == null) {
- return null;
- }
- try {
- return new File(path).getCanonicalPath();
- } catch (IOException e) {
- Log.w(TAG, "Unable to resolve canonical path for " + path);
- return null;
- }
- }
-
/** {@hide} */
public static void setUserRequired(boolean userRequired) {
sUserRequired = userRequired;
@@ -856,28 +745,6 @@
return SystemProperties.getBoolean("config.disable_storage", false);
}
- private static StorageVolume getStorageVolume(File path) {
- try {
- path = path.getCanonicalFile();
- } catch (IOException e) {
- return null;
- }
-
- try {
- final IMountService mountService = IMountService.Stub.asInterface(
- ServiceManager.getService("mount"));
- final StorageVolume[] volumes = mountService.getVolumeList();
- for (StorageVolume volume : volumes) {
- if (FileUtils.contains(volume.getPathFile(), path)) {
- return volume;
- }
- }
- } catch (RemoteException e) {
- }
-
- return null;
- }
-
/**
* If the given path exists on emulated external storage, return the
* translated backing path hosted on internal storage. This bypasses any
@@ -891,26 +758,7 @@
* @hide
*/
public static File maybeTranslateEmulatedPathToInternal(File path) {
- // Fast return if not emulated, or missing variables
- if (!Environment.isExternalStorageEmulated()
- || CANONCIAL_EMULATED_STORAGE_TARGET == null) {
- return path;
- }
-
- try {
- final String rawPath = path.getCanonicalPath();
- if (rawPath.startsWith(CANONCIAL_EMULATED_STORAGE_TARGET)) {
- final File internalPath = new File(DIR_MEDIA_STORAGE,
- rawPath.substring(CANONCIAL_EMULATED_STORAGE_TARGET.length()));
- if (internalPath.exists()) {
- return internalPath;
- }
- }
- } catch (IOException e) {
- Log.w(TAG, "Failed to resolve canonical path for " + path);
- }
-
- // Unable to translate to internal path; use original
+ // TODO: bring back this optimization
return path;
}
}
diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java
index 0a724a1..b302f95 100644
--- a/core/java/android/os/FileUtils.java
+++ b/core/java/android/os/FileUtils.java
@@ -369,6 +369,23 @@
* {@link File#getCanonicalFile()} to avoid symlink or path traversal
* attacks.
*/
+ public static boolean contains(File[] dirs, File file) {
+ for (File dir : dirs) {
+ if (contains(dir, file)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Test if a file lives under the given directory, either as a direct child
+ * or a distant grandchild.
+ * <p>
+ * Both files <em>must</em> have been resolved using
+ * {@link File#getCanonicalFile()} to avoid symlink or path traversal
+ * attacks.
+ */
public static boolean contains(File dir, File file) {
if (file == null) return false;
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 0de9c70..355ec8c 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -637,10 +637,8 @@
if ((debugFlags & Zygote.DEBUG_ENABLE_ASSERT) != 0) {
argsForZygote.add("--enable-assert");
}
- if (mountExternal == Zygote.MOUNT_EXTERNAL_MULTIUSER) {
- argsForZygote.add("--mount-external-multiuser");
- } else if (mountExternal == Zygote.MOUNT_EXTERNAL_MULTIUSER_ALL) {
- argsForZygote.add("--mount-external-multiuser-all");
+ if (mountExternal == Zygote.MOUNT_EXTERNAL_DEFAULT) {
+ argsForZygote.add("--mount-external-default");
}
argsForZygote.add("--target-sdk-version=" + targetSdkVersion);
diff --git a/core/java/android/os/storage/IMountService.java b/core/java/android/os/storage/IMountService.java
index 6209c2a..fef12d1 100644
--- a/core/java/android/os/storage/IMountService.java
+++ b/core/java/android/os/storage/IMountService.java
@@ -757,12 +757,13 @@
return _result;
}
- public StorageVolume[] getVolumeList() throws RemoteException {
+ public StorageVolume[] getVolumeList(int userId) throws RemoteException {
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
StorageVolume[] _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
+ _data.writeInt(userId);
mRemote.transact(Stub.TRANSACTION_getVolumeList, _data, _reply, 0);
_reply.readException();
_result = _reply.createTypedArray(StorageVolume.CREATOR);
@@ -1308,7 +1309,8 @@
}
case TRANSACTION_getVolumeList: {
data.enforceInterface(DESCRIPTOR);
- StorageVolume[] result = getVolumeList();
+ int userId = data.readInt();
+ StorageVolume[] result = getVolumeList(userId);
reply.writeNoException();
reply.writeTypedArray(result, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
return true;
@@ -1630,7 +1632,7 @@
/**
* Returns list of all mountable volumes.
*/
- public StorageVolume[] getVolumeList() throws RemoteException;
+ public StorageVolume[] getVolumeList(int userId) throws RemoteException;
/**
* Gets the path on the filesystem for the ASEC container itself.
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 2785ee8..532bf2c 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -18,19 +18,23 @@
import static android.net.TrafficStats.MB_IN_BYTES;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.ContentResolver;
import android.content.Context;
import android.os.Environment;
+import android.os.FileUtils;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
-import android.os.Parcelable;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.provider.Settings;
import android.util.Log;
import android.util.SparseArray;
+import libcore.util.EmptyArray;
+
import com.android.internal.util.Preconditions;
import java.io.File;
@@ -60,6 +64,7 @@
public class StorageManager {
private static final String TAG = "StorageManager";
+ private final Context mContext;
private final ContentResolver mResolver;
/*
@@ -311,8 +316,9 @@
*
* @hide
*/
- public StorageManager(ContentResolver resolver, Looper tgtLooper) throws RemoteException {
- mResolver = resolver;
+ public StorageManager(Context context, Looper tgtLooper) {
+ mContext = context;
+ mResolver = context.getContentResolver();
mTgtLooper = tgtLooper;
mMountService = IMountService.Stub.asInterface(ServiceManager.getService("mount"));
if (mMountService == null) {
@@ -548,17 +554,46 @@
return null;
}
+ /** {@hide} */
+ public @Nullable StorageVolume getStorageVolume(File file) {
+ return getStorageVolume(getVolumeList(), file);
+ }
+
+ /** {@hide} */
+ public static @Nullable StorageVolume getStorageVolume(File file, int userId) {
+ return getStorageVolume(getVolumeList(userId), file);
+ }
+
+ /** {@hide} */
+ private static @Nullable StorageVolume getStorageVolume(StorageVolume[] volumes, File file) {
+ File canonicalFile = null;
+ try {
+ canonicalFile = file.getCanonicalFile();
+ } catch (IOException ignored) {
+ canonicalFile = null;
+ }
+ for (StorageVolume volume : volumes) {
+ if (volume.getPathFile().equals(file)) {
+ return volume;
+ }
+ if (FileUtils.contains(volume.getPathFile(), canonicalFile)) {
+ return volume;
+ }
+ }
+ return null;
+ }
+
/**
* Gets the state of a volume via its mountpoint.
* @hide
*/
- public String getVolumeState(String mountPoint) {
- if (mMountService == null) return Environment.MEDIA_REMOVED;
- try {
- return mMountService.getVolumeState(mountPoint);
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to get volume state", e);
- return null;
+ @Deprecated
+ public @NonNull String getVolumeState(String mountPoint) {
+ final StorageVolume vol = getStorageVolume(new File(mountPoint));
+ if (vol != null) {
+ return vol.getState();
+ } else {
+ return Environment.MEDIA_UNKNOWN;
}
}
@@ -566,20 +601,22 @@
* Returns list of all mountable volumes.
* @hide
*/
- public StorageVolume[] getVolumeList() {
- if (mMountService == null) return new StorageVolume[0];
+ public @NonNull StorageVolume[] getVolumeList() {
try {
- Parcelable[] list = mMountService.getVolumeList();
- if (list == null) return new StorageVolume[0];
- int length = list.length;
- StorageVolume[] result = new StorageVolume[length];
- for (int i = 0; i < length; i++) {
- result[i] = (StorageVolume)list[i];
- }
- return result;
+ return mMountService.getVolumeList(mContext.getUserId());
} catch (RemoteException e) {
- Log.e(TAG, "Failed to get volume list", e);
- return null;
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /** {@hide} */
+ public static @NonNull StorageVolume[] getVolumeList(int userId) {
+ final IMountService mountService = IMountService.Stub.asInterface(
+ ServiceManager.getService("mount"));
+ try {
+ return mountService.getVolumeList(userId);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
}
}
@@ -587,9 +624,9 @@
* Returns list of paths for all mountable volumes.
* @hide
*/
- public String[] getVolumePaths() {
+ @Deprecated
+ public @NonNull String[] getVolumePaths() {
StorageVolume[] volumes = getVolumeList();
- if (volumes == null) return null;
int count = volumes.length;
String[] paths = new String[count];
for (int i = 0; i < count; i++) {
@@ -599,21 +636,21 @@
}
/** {@hide} */
- public StorageVolume getPrimaryVolume() {
+ public @NonNull StorageVolume getPrimaryVolume() {
return getPrimaryVolume(getVolumeList());
}
/** {@hide} */
- public static StorageVolume getPrimaryVolume(StorageVolume[] volumes) {
+ public static @NonNull StorageVolume getPrimaryVolume(StorageVolume[] volumes) {
for (StorageVolume volume : volumes) {
if (volume.isPrimary()) {
return volume;
}
}
- Log.w(TAG, "No primary storage defined");
- return null;
+ throw new IllegalStateException("Missing primary storage");
}
+ /** {@hide} */
private static final int DEFAULT_THRESHOLD_PERCENTAGE = 10;
private static final long DEFAULT_THRESHOLD_MAX_BYTES = 500 * MB_IN_BYTES;
private static final long DEFAULT_FULL_THRESHOLD_BYTES = MB_IN_BYTES;
diff --git a/core/java/android/os/storage/StorageVolume.java b/core/java/android/os/storage/StorageVolume.java
index 06565f1..0c391ca 100644
--- a/core/java/android/os/storage/StorageVolume.java
+++ b/core/java/android/os/storage/StorageVolume.java
@@ -17,6 +17,7 @@
package android.os.storage;
import android.content.Context;
+import android.net.TrafficStats;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.UserHandle;
@@ -34,52 +35,58 @@
*/
public class StorageVolume implements Parcelable {
- // TODO: switch to more durable token
- private int mStorageId;
+ private final String mId;
+ private final int mStorageId;
private final File mPath;
private final int mDescriptionId;
private final boolean mPrimary;
private final boolean mRemovable;
private final boolean mEmulated;
- private final int mMtpReserveSpace;
+ private final long mMtpReserveSize;
private final boolean mAllowMassStorage;
/** Maximum file size for the storage, or zero for no limit */
private final long mMaxFileSize;
/** When set, indicates exclusive ownership of this volume */
private final UserHandle mOwner;
- private String mUuid;
- private String mUserLabel;
- private String mState;
+ private final String mUuid;
+ private final String mUserLabel;
+ private final String mState;
// StorageVolume extra for ACTION_MEDIA_REMOVED, ACTION_MEDIA_UNMOUNTED, ACTION_MEDIA_CHECKING,
// ACTION_MEDIA_NOFS, ACTION_MEDIA_MOUNTED, ACTION_MEDIA_SHARED, ACTION_MEDIA_UNSHARED,
// ACTION_MEDIA_BAD_REMOVAL, ACTION_MEDIA_UNMOUNTABLE and ACTION_MEDIA_EJECT broadcasts.
public static final String EXTRA_STORAGE_VOLUME = "storage_volume";
- public StorageVolume(File path, int descriptionId, boolean primary, boolean removable,
- boolean emulated, int mtpReserveSpace, boolean allowMassStorage, long maxFileSize,
- UserHandle owner) {
+ public StorageVolume(String id, int storageId, File path, int descriptionId, boolean primary,
+ boolean removable, boolean emulated, long mtpReserveSize, boolean allowMassStorage,
+ long maxFileSize, UserHandle owner, String uuid, String userLabel, String state) {
+ mId = id;
+ mStorageId = storageId;
mPath = path;
mDescriptionId = descriptionId;
mPrimary = primary;
mRemovable = removable;
mEmulated = emulated;
- mMtpReserveSpace = mtpReserveSpace;
+ mMtpReserveSize = mtpReserveSize;
mAllowMassStorage = allowMassStorage;
mMaxFileSize = maxFileSize;
mOwner = owner;
+ mUuid = uuid;
+ mUserLabel = userLabel;
+ mState = state;
}
private StorageVolume(Parcel in) {
+ mId = in.readString();
mStorageId = in.readInt();
mPath = new File(in.readString());
mDescriptionId = in.readInt();
mPrimary = in.readInt() != 0;
mRemovable = in.readInt() != 0;
mEmulated = in.readInt() != 0;
- mMtpReserveSpace = in.readInt();
+ mMtpReserveSize = in.readLong();
mAllowMassStorage = in.readInt() != 0;
mMaxFileSize = in.readLong();
mOwner = in.readParcelable(null);
@@ -88,10 +95,8 @@
mState = in.readString();
}
- public static StorageVolume fromTemplate(StorageVolume template, File path, UserHandle owner) {
- return new StorageVolume(path, template.mDescriptionId, template.mPrimary,
- template.mRemovable, template.mEmulated, template.mMtpReserveSpace,
- template.mAllowMassStorage, template.mMaxFileSize, owner);
+ public String getId() {
+ return mId;
}
/**
@@ -153,15 +158,6 @@
}
/**
- * Do not call this unless you are MountService
- */
- public void setStorageId(int index) {
- // storage ID is 0x00010001 for primary storage,
- // then 0x00020001, 0x00030001, etc. for secondary storages
- mStorageId = ((index + 1) << 16) + 1;
- }
-
- /**
* Number of megabytes of space to leave unallocated by MTP.
* MTP will subtract this value from the free space it reports back
* to the host via GetStorageInfo, and will not allow new files to
@@ -174,7 +170,7 @@
* @return MTP reserve space
*/
public int getMtpReserveSpace() {
- return mMtpReserveSpace;
+ return (int) (mMtpReserveSize / TrafficStats.MB_IN_BYTES);
}
/**
@@ -199,10 +195,6 @@
return mOwner;
}
- public void setUuid(String uuid) {
- mUuid = uuid;
- }
-
public String getUuid() {
return mUuid;
}
@@ -222,18 +214,10 @@
}
}
- public void setUserLabel(String userLabel) {
- mUserLabel = userLabel;
- }
-
public String getUserLabel() {
return mUserLabel;
}
- public void setState(String state) {
- mState = state;
- }
-
public String getState() {
return mState;
}
@@ -262,13 +246,14 @@
public void dump(IndentingPrintWriter pw) {
pw.println("StorageVolume:");
pw.increaseIndent();
+ pw.printPair("mId", mId);
pw.printPair("mStorageId", mStorageId);
pw.printPair("mPath", mPath);
pw.printPair("mDescriptionId", mDescriptionId);
pw.printPair("mPrimary", mPrimary);
pw.printPair("mRemovable", mRemovable);
pw.printPair("mEmulated", mEmulated);
- pw.printPair("mMtpReserveSpace", mMtpReserveSpace);
+ pw.printPair("mMtpReserveSize", mMtpReserveSize);
pw.printPair("mAllowMassStorage", mAllowMassStorage);
pw.printPair("mMaxFileSize", mMaxFileSize);
pw.printPair("mOwner", mOwner);
@@ -297,13 +282,14 @@
@Override
public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeString(mId);
parcel.writeInt(mStorageId);
parcel.writeString(mPath.toString());
parcel.writeInt(mDescriptionId);
parcel.writeInt(mPrimary ? 1 : 0);
parcel.writeInt(mRemovable ? 1 : 0);
parcel.writeInt(mEmulated ? 1 : 0);
- parcel.writeInt(mMtpReserveSpace);
+ parcel.writeLong(mMtpReserveSize);
parcel.writeInt(mAllowMassStorage ? 1 : 0);
parcel.writeLong(mMaxFileSize);
parcel.writeParcelable(mOwner, flags);
diff --git a/core/java/android/preference/Preference.java b/core/java/android/preference/Preference.java
index f32e8cf..3b482eb 100644
--- a/core/java/android/preference/Preference.java
+++ b/core/java/android/preference/Preference.java
@@ -1438,7 +1438,7 @@
protected boolean persistString(String value) {
if (shouldPersist()) {
// Shouldn't store null
- if (value == getPersistedString(null)) {
+ if (TextUtils.equals(value, getPersistedString(null))) {
// It's already there, so the same as persisting
return true;
}
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index e4a6f07..bf7f3cb 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -18,6 +18,7 @@
import android.accounts.Account;
import android.app.Activity;
+import android.app.admin.DevicePolicyManager;
import android.content.ActivityNotFoundException;
import android.content.ContentProviderClient;
import android.content.ContentProviderOperation;
@@ -1628,7 +1629,6 @@
*/
public static final String CONTENT_VCARD_TYPE = "text/x-vcard";
-
/**
* Mimimal ID for corp contacts returned from
* {@link PhoneLookup#ENTERPRISE_CONTENT_FILTER_URI}.
@@ -1638,6 +1638,14 @@
public static long ENTERPRISE_CONTACT_ID_BASE = 1000000000; // slightly smaller than 2 ** 30
/**
+ * Prefix for corp contacts returned from
+ * {@link PhoneLookup#ENTERPRISE_CONTENT_FILTER_URI}.
+ *
+ * @hide
+ */
+ public static String ENTERPRISE_CONTACT_LOOKUP_PREFIX = "c-";
+
+ /**
* Return TRUE if a contact ID is from the contacts provider on the enterprise profile.
*
* {@link PhoneLookup#ENTERPRISE_CONTENT_FILTER_URI} may return such a contact.
@@ -5032,9 +5040,17 @@
* is from the corp profile, use
* {@link ContactsContract.Contacts#isEnterpriseContactId(long)}.
* </li>
+ * <li>
+ * Corp contacts will get artificial {@link #LOOKUP_KEY}s too.
+ * </li>
* </ul>
* <p>
- * This URI does NOT support selection nor order-by.
+ * A contact lookup URL built by
+ * {@link ContactsContract.Contacts#getLookupUri(long, String)}
+ * with an {@link #_ID} and a {@link #LOOKUP_KEY} returned by this API can be passed to
+ * {@link ContactsContract.QuickContact#showQuickContact} even if a contact is from the
+ * corp profile.
+ * </p>
*
* <pre>
* Uri lookupUri = Uri.withAppendedPath(PhoneLookup.ENTERPRISE_CONTENT_FILTER_URI,
@@ -6025,10 +6041,18 @@
* a contact
* is from the corp profile, use
* {@link ContactsContract.Contacts#isEnterpriseContactId(long)}.
- * </li>
- * </ul>
- * <p>
- * This URI does NOT support selection nor order-by.
+ * </li>
+ * <li>
+ * Corp contacts will get artificial {@link #LOOKUP_KEY}s too.
+ * </li>
+ * </ul>
+ * <p>
+ * A contact lookup URL built by
+ * {@link ContactsContract.Contacts#getLookupUri(long, String)}
+ * with an {@link #_ID} and a {@link #LOOKUP_KEY} returned by this API can be passed to
+ * {@link ContactsContract.QuickContact#showQuickContact} even if a contact is from the
+ * corp profile.
+ * </p>
*
* <pre>
* Uri lookupUri = Uri.withAppendedPath(Email.ENTERPRISE_CONTENT_LOOKUP_URI,
@@ -8182,6 +8206,9 @@
*/
public static final int MODE_LARGE = 3;
+ /** @hide */
+ public static final int MODE_DEFAULT = MODE_LARGE;
+
/**
* Constructs the QuickContacts intent with a view's rect.
* @hide
@@ -8224,6 +8251,7 @@
// Launch pivot dialog through intent for now
final Intent intent = new Intent(ACTION_QUICK_CONTACT).addFlags(intentFlags);
+ // NOTE: This logic and rebuildManagedQuickContactsIntent() must be in sync.
intent.setData(lookupUri);
intent.setSourceBounds(target);
intent.putExtra(EXTRA_MODE, mode);
@@ -8232,6 +8260,30 @@
}
/**
+ * Constructs a QuickContacts intent based on an incoming intent for DevicePolicyManager
+ * to strip off anything not necessary.
+ *
+ * @hide
+ */
+ public static Intent rebuildManagedQuickContactsIntent(String lookupKey, long contactId,
+ Intent originalIntent) {
+ final Intent intent = new Intent(ACTION_QUICK_CONTACT);
+ // Rebuild the URI from a lookup key and a contact ID.
+ intent.setData(Contacts.getLookupUri(contactId, lookupKey));
+
+ // Copy flags and always set NEW_TASK because it won't have a parent activity.
+ intent.setFlags(originalIntent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK);
+
+ // Copy extras.
+ intent.setSourceBounds(originalIntent.getSourceBounds());
+ intent.putExtra(EXTRA_MODE, originalIntent.getIntExtra(EXTRA_MODE, MODE_DEFAULT));
+ intent.putExtra(EXTRA_EXCLUDE_MIMES,
+ originalIntent.getStringArrayExtra(EXTRA_EXCLUDE_MIMES));
+ return intent;
+ }
+
+
+ /**
* Trigger a dialog that lists the various methods of interacting with
* the requested {@link Contacts} entry. This may be based on available
* {@link ContactsContract.Data} rows under that contact, and may also
@@ -8259,7 +8311,7 @@
// Trigger with obtained rectangle
Intent intent = composeQuickContactsIntent(context, target, lookupUri, mode,
excludeMimes);
- startActivityWithErrorToast(context, intent);
+ ContactsInternal.startQuickContactWithErrorToast(context, intent);
}
/**
@@ -8292,7 +8344,7 @@
String[] excludeMimes) {
Intent intent = composeQuickContactsIntent(context, target, lookupUri, mode,
excludeMimes);
- startActivityWithErrorToast(context, intent);
+ ContactsInternal.startQuickContactWithErrorToast(context, intent);
}
/**
@@ -8325,10 +8377,10 @@
// Use MODE_LARGE instead of accepting mode as a parameter. The different mode
// values defined in ContactsContract only affect very old implementations
// of QuickContacts.
- Intent intent = composeQuickContactsIntent(context, target, lookupUri, MODE_LARGE,
+ Intent intent = composeQuickContactsIntent(context, target, lookupUri, MODE_DEFAULT,
excludeMimes);
intent.putExtra(EXTRA_PRIORITIZED_MIMETYPE, prioritizedMimeType);
- startActivityWithErrorToast(context, intent);
+ ContactsInternal.startQuickContactWithErrorToast(context, intent);
}
/**
@@ -8363,19 +8415,10 @@
// Use MODE_LARGE instead of accepting mode as a parameter. The different mode
// values defined in ContactsContract only affect very old implementations
// of QuickContacts.
- Intent intent = composeQuickContactsIntent(context, target, lookupUri, MODE_LARGE,
+ Intent intent = composeQuickContactsIntent(context, target, lookupUri, MODE_DEFAULT,
excludeMimes);
intent.putExtra(EXTRA_PRIORITIZED_MIMETYPE, prioritizedMimeType);
- startActivityWithErrorToast(context, intent);
- }
-
- private static void startActivityWithErrorToast(Context context, Intent intent) {
- try {
- context.startActivity(intent);
- } catch (ActivityNotFoundException e) {
- Toast.makeText(context, com.android.internal.R.string.quick_contacts_not_available,
- Toast.LENGTH_SHORT).show();
- }
+ ContactsInternal.startQuickContactWithErrorToast(context, intent);
}
}
diff --git a/core/java/android/provider/ContactsInternal.java b/core/java/android/provider/ContactsInternal.java
new file mode 100644
index 0000000..059a603
--- /dev/null
+++ b/core/java/android/provider/ContactsInternal.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+package android.provider;
+
+import android.app.admin.DevicePolicyManager;
+import android.content.ActivityNotFoundException;
+import android.content.ContentUris;
+import android.content.Context;
+import android.content.Intent;
+import android.content.UriMatcher;
+import android.net.Uri;
+import android.os.Process;
+import android.os.UserHandle;
+import android.text.TextUtils;
+import android.widget.Toast;
+
+import java.util.List;
+
+/**
+ * Contacts related internal methods.
+ *
+ * @hide
+ */
+public class ContactsInternal {
+ private ContactsInternal() {
+ }
+
+ /** URI matcher used to parse contact URIs. */
+ private static final UriMatcher sContactsUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
+
+ private static final int CONTACTS_URI_LOOKUP_ID = 1000;
+
+ static {
+ // Contacts URI matching table
+ final UriMatcher matcher = sContactsUriMatcher;
+ matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#", CONTACTS_URI_LOOKUP_ID);
+ }
+
+ /**
+ * Called by {@link ContactsContract} to star Quick Contact, possibly on the managed profile.
+ */
+ public static void startQuickContactWithErrorToast(Context context, Intent intent) {
+ final Uri uri = intent.getData();
+
+ final int match = sContactsUriMatcher.match(uri);
+ switch (match) {
+ case CONTACTS_URI_LOOKUP_ID: {
+ if (maybeStartManagedQuickContact(context, intent)) {
+ return; // Request handled by DPM. Just return here.
+ }
+ break;
+ }
+ }
+ // Launch on the current profile.
+ startQuickContactWithErrorToastForUser(context, intent, Process.myUserHandle());
+ }
+
+ public static void startQuickContactWithErrorToastForUser(Context context, Intent intent,
+ UserHandle user) {
+ try {
+ context.startActivityAsUser(intent, user);
+ } catch (ActivityNotFoundException e) {
+ Toast.makeText(context, com.android.internal.R.string.quick_contacts_not_available,
+ Toast.LENGTH_SHORT).show();
+ }
+ }
+
+ /**
+ * If the URI in {@code intent} is of a corp contact, launch quick contact on the managed
+ * profile.
+ *
+ * @return the URI in {@code intent} is of a corp contact thus launched on the managed profile.
+ */
+ private static boolean maybeStartManagedQuickContact(Context context, Intent originalIntent) {
+ final Uri uri = originalIntent.getData();
+
+ // Decompose into an ID and a lookup key.
+ final List<String> pathSegments = uri.getPathSegments();
+ final long contactId = ContentUris.parseId(uri);
+ final String lookupKey = pathSegments.get(2);
+
+ // See if it has a corp lookupkey.
+ if (TextUtils.isEmpty(lookupKey)
+ || !lookupKey.startsWith(
+ ContactsContract.Contacts.ENTERPRISE_CONTACT_LOOKUP_PREFIX)) {
+ return false; // It's not a corp lookup key.
+ }
+
+ // Launch Quick Contact on the managed profile, if the policy allows.
+ final DevicePolicyManager dpm = context.getSystemService(DevicePolicyManager.class);
+ final String actualLookupKey = lookupKey.substring(
+ ContactsContract.Contacts.ENTERPRISE_CONTACT_LOOKUP_PREFIX.length());
+ final long actualContactId =
+ (contactId - ContactsContract.Contacts.ENTERPRISE_CONTACT_ID_BASE);
+
+ dpm.startManagedQuickContact(actualLookupKey, actualContactId, originalIntent);
+ return true;
+ }
+}
diff --git a/core/java/android/provider/SearchIndexableData.java b/core/java/android/provider/SearchIndexableData.java
index 7b9d1ea..5e0a76d 100644
--- a/core/java/android/provider/SearchIndexableData.java
+++ b/core/java/android/provider/SearchIndexableData.java
@@ -16,6 +16,7 @@
package android.provider;
+import android.annotation.SystemApi;
import android.content.Context;
import java.util.Locale;
@@ -27,6 +28,7 @@
*
* @hide
*/
+@SystemApi
public abstract class SearchIndexableData {
/**
diff --git a/core/java/android/provider/SearchIndexableResource.java b/core/java/android/provider/SearchIndexableResource.java
index c807df2..1eb1734 100644
--- a/core/java/android/provider/SearchIndexableResource.java
+++ b/core/java/android/provider/SearchIndexableResource.java
@@ -16,6 +16,7 @@
package android.provider;
+import android.annotation.SystemApi;
import android.content.Context;
/**
@@ -31,6 +32,7 @@
*
* @hide
*/
+@SystemApi
public class SearchIndexableResource extends SearchIndexableData {
/**
diff --git a/core/java/android/provider/SearchIndexablesContract.java b/core/java/android/provider/SearchIndexablesContract.java
index 1b5f72a..93ac7f6 100644
--- a/core/java/android/provider/SearchIndexablesContract.java
+++ b/core/java/android/provider/SearchIndexablesContract.java
@@ -16,6 +16,7 @@
package android.provider;
+import android.annotation.SystemApi;
import android.content.ContentResolver;
/**
@@ -23,6 +24,7 @@
*
* @hide
*/
+@SystemApi
public class SearchIndexablesContract {
/**
@@ -234,7 +236,7 @@
/**
* The base columns.
*/
- private static class BaseColumns {
+ public static class BaseColumns {
private BaseColumns() {
}
diff --git a/core/java/android/provider/SearchIndexablesProvider.java b/core/java/android/provider/SearchIndexablesProvider.java
index 9c8f6d0..3120e54 100644
--- a/core/java/android/provider/SearchIndexablesProvider.java
+++ b/core/java/android/provider/SearchIndexablesProvider.java
@@ -16,6 +16,7 @@
package android.provider;
+import android.annotation.SystemApi;
import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.Context;
@@ -61,6 +62,7 @@
*
* @hide
*/
+@SystemApi
public abstract class SearchIndexablesProvider extends ContentProvider {
private static final String TAG = "IndexablesProvider";
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index de536bd..8e5d245 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -5255,6 +5255,15 @@
public static final String SMS_DEFAULT_APPLICATION = "sms_default_application";
/**
+ * Specifies the package name currently configured to be the emergency assistance application
+ *
+ * @see android.telephony.TelephonyManager#ACTION_EMERGENCY_ASSISTANCE
+ *
+ * @hide
+ */
+ public static final String EMERGENCY_ASSISTANCE_APPLICATION = "emergency_assistance_application";
+
+ /**
* Names of the packages that the current user has explicitly allowed to
* see all of the user's notifications, separated by ':'.
*
@@ -6088,7 +6097,7 @@
public static final String PACKAGE_VERIFIER_SETTING_VISIBLE = "verifier_setting_visible";
/**
- * Run package verificaiton on apps installed through ADB/ADT/USB
+ * Run package verification on apps installed through ADB/ADT/USB
* 1 = perform package verification on ADB installs (default)
* 0 = bypass package verification on ADB installs
* @hide
diff --git a/core/java/android/security/IKeystoreService.aidl b/core/java/android/security/IKeystoreService.aidl
index d24bc13..579cdbe 100644
--- a/core/java/android/security/IKeystoreService.aidl
+++ b/core/java/android/security/IKeystoreService.aidl
@@ -60,8 +60,8 @@
// Keymaster 0.4 methods
int addRngEntropy(in byte[] data);
- int generateKey(String alias, in KeymasterArguments arguments, int uid, int flags,
- out KeyCharacteristics characteristics);
+ int generateKey(String alias, in KeymasterArguments arguments, in byte[] entropy, int uid,
+ int flags, out KeyCharacteristics characteristics);
int getKeyCharacteristics(String alias, in KeymasterBlob clientId, in KeymasterBlob appId,
out KeyCharacteristics characteristics);
int importKey(String alias, in KeymasterArguments arguments, int format,
@@ -69,8 +69,10 @@
ExportResult exportKey(String alias, int format, in KeymasterBlob clientId,
in KeymasterBlob appId);
OperationResult begin(IBinder appToken, String alias, int purpose, boolean pruneable,
- in KeymasterArguments params, out KeymasterArguments operationParams);
+ in KeymasterArguments params, in byte[] entropy, out KeymasterArguments operationParams);
OperationResult update(IBinder token, in KeymasterArguments params, in byte[] input);
OperationResult finish(IBinder token, in KeymasterArguments params, in byte[] signature);
int abort(IBinder handle);
+ boolean isOperationAuthorized(IBinder token);
+ int addAuthToken(in byte[] authToken);
}
diff --git a/core/java/android/security/NetworkSecurityPolicy.java b/core/java/android/security/NetworkSecurityPolicy.java
index 0626bbc..0b3bf453 100644
--- a/core/java/android/security/NetworkSecurityPolicy.java
+++ b/core/java/android/security/NetworkSecurityPolicy.java
@@ -24,8 +24,6 @@
*
* <p>The policy currently consists of a single flag: whether cleartext network traffic is
* permitted. See {@link #isCleartextTrafficPermitted()}.
- *
- * @hide
*/
public class NetworkSecurityPolicy {
@@ -48,9 +46,9 @@
* without TLS or STARTTLS) is permitted for this process.
*
* <p>When cleartext network traffic is not permitted, the platform's components (e.g. HTTP and
- * FTP stacks, {@code WebView}, {@code MediaPlayer}) will refuse this process's requests to use
- * cleartext traffic. Third-party libraries are strongly encouraged to honor this setting as
- * well.
+ * FTP stacks, {@link android.webkit.WebView}, {@link android.media.MediaPlayer}) will refuse
+ * this process's requests to use cleartext traffic. Third-party libraries are strongly
+ * encouraged to honor this setting as well.
*
* <p>This flag is honored on a best effort basis because it's impossible to prevent all
* cleartext traffic from Android applications given the level of access provided to them. For
diff --git a/core/java/android/security/keymaster/KeymasterDefs.java b/core/java/android/security/keymaster/KeymasterDefs.java
index e653b74..c2ebbc6 100644
--- a/core/java/android/security/keymaster/KeymasterDefs.java
+++ b/core/java/android/security/keymaster/KeymasterDefs.java
@@ -16,6 +16,9 @@
package android.security.keymaster;
+import java.util.HashMap;
+import java.util.Map;
+
/**
* Class tracking all the keymaster enum values needed for the binder API to keystore.
* This must be kept in sync with hardware/libhardware/include/hardware/keymaster_defs.h
@@ -224,7 +227,53 @@
public static final int KM_ERROR_VERSION_MISMATCH = -101;
public static final int KM_ERROR_UNKNOWN_ERROR = -1000;
+ public static final Map<Integer, String> sErrorCodeToString = new HashMap<Integer, String>();
+ static {
+ sErrorCodeToString.put(KM_ERROR_OK, "OK");
+ sErrorCodeToString.put(KM_ERROR_UNSUPPORTED_PURPOSE, "Unsupported purpose");
+ sErrorCodeToString.put(KM_ERROR_INCOMPATIBLE_PURPOSE, "Incompatible purpose");
+ sErrorCodeToString.put(KM_ERROR_UNSUPPORTED_ALGORITHM, "Unsupported algorithm");
+ sErrorCodeToString.put(KM_ERROR_INCOMPATIBLE_ALGORITHM, "Incompatible algorithm");
+ sErrorCodeToString.put(KM_ERROR_UNSUPPORTED_KEY_SIZE, "Unsupported key size");
+ sErrorCodeToString.put(KM_ERROR_UNSUPPORTED_BLOCK_MODE, "Unsupported block mode");
+ sErrorCodeToString.put(KM_ERROR_INCOMPATIBLE_BLOCK_MODE, "Incompatible block mode");
+ sErrorCodeToString.put(KM_ERROR_UNSUPPORTED_TAG_LENGTH,
+ "Unsupported authentication tag length");
+ sErrorCodeToString.put(KM_ERROR_UNSUPPORTED_PADDING_MODE, "Unsupported padding mode");
+ sErrorCodeToString.put(KM_ERROR_INCOMPATIBLE_PADDING_MODE, "Incompatible padding mode");
+ sErrorCodeToString.put(KM_ERROR_UNSUPPORTED_DIGEST, "Unsupported digest");
+ sErrorCodeToString.put(KM_ERROR_INCOMPATIBLE_DIGEST, "Incompatible digest");
+ sErrorCodeToString.put(KM_ERROR_INVALID_EXPIRATION_TIME, "Invalid expiration time");
+ sErrorCodeToString.put(KM_ERROR_INVALID_USER_ID, "Invalid user ID");
+ sErrorCodeToString.put(KM_ERROR_INVALID_AUTHORIZATION_TIMEOUT,
+ "Invalid user authorization timeout");
+ sErrorCodeToString.put(KM_ERROR_UNSUPPORTED_KEY_FORMAT, "Unsupported key format");
+ sErrorCodeToString.put(KM_ERROR_INCOMPATIBLE_KEY_FORMAT, "Incompatible key format");
+ sErrorCodeToString.put(KM_ERROR_INVALID_INPUT_LENGTH, "Invalid input length");
+ sErrorCodeToString.put(KM_ERROR_KEY_NOT_YET_VALID, "Key not yet valid");
+ sErrorCodeToString.put(KM_ERROR_KEY_EXPIRED, "Key expired");
+ sErrorCodeToString.put(KM_ERROR_KEY_USER_NOT_AUTHENTICATED, "Key user not authenticated");
+ sErrorCodeToString.put(KM_ERROR_INVALID_OPERATION_HANDLE, "Invalid operation handle");
+ sErrorCodeToString.put(KM_ERROR_VERIFICATION_FAILED, "Signature/MAC verification failed");
+ sErrorCodeToString.put(KM_ERROR_TOO_MANY_OPERATIONS, "Too many operations");
+ sErrorCodeToString.put(KM_ERROR_INVALID_KEY_BLOB, "Invalid key blob");
+ sErrorCodeToString.put(KM_ERROR_INVALID_ARGUMENT, "Invalid argument");
+ sErrorCodeToString.put(KM_ERROR_UNSUPPORTED_TAG, "Unsupported tag");
+ sErrorCodeToString.put(KM_ERROR_INVALID_TAG, "Invalid tag");
+ sErrorCodeToString.put(KM_ERROR_MEMORY_ALLOCATION_FAILED, "Memory allocation failed");
+ sErrorCodeToString.put(KM_ERROR_UNIMPLEMENTED, "Not implemented");
+ sErrorCodeToString.put(KM_ERROR_UNKNOWN_ERROR, "Unknown error");
+ }
+
public static int getTagType(int tag) {
return tag & (0xF << 28);
}
+
+ public static String getErrorMessage(int errorCode) {
+ String result = sErrorCodeToString.get(errorCode);
+ if (result != null) {
+ return result;
+ }
+ return String.valueOf(errorCode);
+ }
}
diff --git a/core/java/android/security/keymaster/OperationResult.java b/core/java/android/security/keymaster/OperationResult.java
index 4fc9d24..9b46ad3 100644
--- a/core/java/android/security/keymaster/OperationResult.java
+++ b/core/java/android/security/keymaster/OperationResult.java
@@ -28,6 +28,7 @@
public class OperationResult implements Parcelable {
public final int resultCode;
public final IBinder token;
+ public final long operationHandle;
public final int inputConsumed;
public final byte[] output;
@@ -45,6 +46,7 @@
protected OperationResult(Parcel in) {
resultCode = in.readInt();
token = in.readStrongBinder();
+ operationHandle = in.readLong();
inputConsumed = in.readInt();
output = in.createByteArray();
}
@@ -58,6 +60,7 @@
public void writeToParcel(Parcel out, int flags) {
out.writeInt(resultCode);
out.writeStrongBinder(token);
+ out.writeLong(operationHandle);
out.writeInt(inputConsumed);
out.writeByteArray(output);
}
diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java
index 7a5bb90..3245f55 100644
--- a/core/java/android/service/voice/VoiceInteractionSession.java
+++ b/core/java/android/service/voice/VoiceInteractionSession.java
@@ -512,11 +512,7 @@
Request removeRequest(IBinder reqInterface) {
synchronized (this) {
- Request req = mActiveRequests.get(reqInterface);
- if (req != null) {
- mActiveRequests.remove(req);
- }
- return req;
+ return mActiveRequests.remove(reqInterface);
}
}
diff --git a/core/java/android/text/DynamicLayout.java b/core/java/android/text/DynamicLayout.java
index 1bdaef0..7d2e1ef 100644
--- a/core/java/android/text/DynamicLayout.java
+++ b/core/java/android/text/DynamicLayout.java
@@ -356,6 +356,8 @@
ints[DESCENT] = desc;
objects[0] = reflowed.getLineDirections(i);
+ ints[HYPHEN] = reflowed.getHyphen(i);
+
if (mEllipsize) {
ints[ELLIPSIS_START] = reflowed.getEllipsisStart(i);
ints[ELLIPSIS_COUNT] = reflowed.getEllipsisCount(i);
@@ -631,6 +633,14 @@
return mBottomPadding;
}
+ /**
+ * @hide
+ */
+ @Override
+ public int getHyphen(int line) {
+ return mInts.getValue(line, HYPHEN);
+ }
+
@Override
public int getEllipsizedWidth() {
return mEllipsizedWidth;
@@ -739,11 +749,12 @@
private static final int TAB = START;
private static final int TOP = 1;
private static final int DESCENT = 2;
- private static final int COLUMNS_NORMAL = 3;
+ private static final int HYPHEN = 3;
+ private static final int COLUMNS_NORMAL = 4;
- private static final int ELLIPSIS_START = 3;
- private static final int ELLIPSIS_COUNT = 4;
- private static final int COLUMNS_ELLIPSIZE = 5;
+ private static final int ELLIPSIS_START = 4;
+ private static final int ELLIPSIS_COUNT = 5;
+ private static final int COLUMNS_ELLIPSIZE = 6;
private static final int START_MASK = 0x1FFFFFFF;
private static final int DIR_SHIFT = 30;
diff --git a/core/java/android/text/Hyphenator.java b/core/java/android/text/Hyphenator.java
new file mode 100644
index 0000000..f4dff9b
--- /dev/null
+++ b/core/java/android/text/Hyphenator.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.text;
+
+import android.util.Log;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.util.HashMap;
+import java.util.Locale;
+
+/**
+ * Hyphenator is a wrapper class for a native implementation of automatic hyphenation,
+ * in essence finding valid hyphenation opportunities in a word.
+ *
+ * @hide
+ */
+/* package */ class Hyphenator {
+ // This class has deliberately simple lifetime management (no finalizer) because in
+ // the common case a process will use a very small number of locales.
+
+ private static String TAG = "Hyphenator";
+
+ static HashMap<Locale, Hyphenator> sMap = new HashMap<Locale, Hyphenator>();
+
+ private long mNativePtr;
+
+ private Hyphenator(long nativePtr) {
+ mNativePtr = nativePtr;
+ }
+
+ public static long get(Locale locale) {
+ synchronized (sMap) {
+ Hyphenator result = sMap.get(locale);
+ if (result == null) {
+ result = loadHyphenator(locale);
+ sMap.put(locale, result);
+ }
+ return result == null ? 0 : result.mNativePtr;
+ }
+ }
+
+ private static Hyphenator loadHyphenator(Locale locale) {
+ // TODO: find pattern dictionary (from system location) that best matches locale
+ if (Locale.US.equals(locale)) {
+ File f = new File("/data/local/tmp/hyph-en-us.pat.txt");
+ try {
+ RandomAccessFile rf = new RandomAccessFile(f, "r");
+ byte[] buf = new byte[(int)rf.length()];
+ rf.read(buf);
+ rf.close();
+ String patternData = new String(buf);
+ long nativePtr = StaticLayout.nLoadHyphenator(patternData);
+ return new Hyphenator(nativePtr);
+ } catch (IOException e) {
+ Log.e(TAG, "error loading hyphenation " + f);
+ }
+ }
+ return null;
+ }
+}
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index fcf1828..22abb18 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -225,17 +225,17 @@
// Draw the lines, one at a time.
// The baseline is the top of the following line minus the current line's descent.
- for (int i = firstLine; i <= lastLine; i++) {
+ for (int lineNum = firstLine; lineNum <= lastLine; lineNum++) {
int start = previousLineEnd;
- previousLineEnd = getLineStart(i + 1);
- int end = getLineVisibleEnd(i, start, previousLineEnd);
+ previousLineEnd = getLineStart(lineNum + 1);
+ int end = getLineVisibleEnd(lineNum, start, previousLineEnd);
int ltop = previousLineBottom;
- int lbottom = getLineTop(i+1);
+ int lbottom = getLineTop(lineNum + 1);
previousLineBottom = lbottom;
- int lbaseline = lbottom - getLineDescent(i);
+ int lbaseline = lbottom - getLineDescent(lineNum);
- int dir = getParagraphDirection(i);
+ int dir = getParagraphDirection(lineNum);
int left = 0;
int right = mWidth;
@@ -254,7 +254,7 @@
// just collect the ones present at the start of the paragraph.
// If spanEnd is before the end of the paragraph, that's not
// our problem.
- if (start >= spanEnd && (i == firstLine || isFirstParaLine)) {
+ if (start >= spanEnd && (lineNum == firstLine || isFirstParaLine)) {
spanEnd = sp.nextSpanTransition(start, textLength,
ParagraphStyle.class);
spans = getParagraphSpans(sp, start, spanEnd, ParagraphStyle.class);
@@ -280,7 +280,7 @@
int startLine = getLineForOffset(sp.getSpanStart(spans[n]));
// if there is more than one LeadingMarginSpan2, use
// the count that is greatest
- if (i < startLine + count) {
+ if (lineNum < startLine + count) {
useFirstLineMargin = true;
break;
}
@@ -304,7 +304,7 @@
}
}
- boolean hasTabOrEmoji = getLineContainsTab(i);
+ boolean hasTabOrEmoji = getLineContainsTab(lineNum);
// Can't tell if we have tabs for sure, currently
if (hasTabOrEmoji && !tabStopsIsInitialized) {
if (tabStops == null) {
@@ -333,7 +333,7 @@
x = right;
}
} else {
- int max = (int)getLineExtent(i, tabStops, false);
+ int max = (int)getLineExtent(lineNum, tabStops, false);
if (align == Alignment.ALIGN_OPPOSITE) {
if (dir == DIR_LEFT_TO_RIGHT) {
x = right - max;
@@ -346,7 +346,8 @@
}
}
- Directions directions = getLineDirections(i);
+ paint.setHyphenEdit(getHyphen(lineNum));
+ Directions directions = getLineDirections(lineNum);
if (directions == DIRS_ALL_LEFT_TO_RIGHT && !mSpannedText && !hasTabOrEmoji) {
// XXX: assumes there's nothing additional to be done
canvas.drawText(buf, start, end, x, lbaseline, paint);
@@ -677,6 +678,15 @@
*/
public abstract int getBottomPadding();
+ /**
+ * Returns the hyphen edit for a line.
+ *
+ * @hide
+ */
+ public int getHyphen(int line) {
+ return 0;
+ }
+
/**
* Returns true if the character at offset and the preceding character
@@ -1153,7 +1163,10 @@
return end - 1;
}
- if (ch != ' ' && ch != '\t') {
+ // Note: keep this in sync with Minikin LineBreaker::isLineEndSpace()
+ if (!(ch == ' ' || ch == '\t' || ch == 0x1680 ||
+ (0x2000 <= ch && ch <= 0x200A && ch != 0x2007) ||
+ ch == 0x205F || ch == 0x3000)) {
break;
}
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index ee39e27..4174df0 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -170,8 +170,10 @@
* Measurement and break iteration is done in native code. The protocol for using
* the native code is as follows.
*
- * For each paragraph, do a nSetText of the paragraph text. Then, for each run within the
- * paragraph:
+ * For each paragraph, do a nSetupParagraph, which sets paragraph text, line width, tab
+ * stops, break strategy (and possibly other parameters in the future).
+ *
+ * Then, for each run within the paragraph:
* - setLocale (this must be done at least for the first run, optional afterwards)
* - one of the following, depending on the type of run:
* + addStyleRun (a text run, to be measured in native code)
@@ -186,7 +188,7 @@
private void setLocale(Locale locale) {
if (!locale.equals(mLocale)) {
- nSetLocale(mNativePtr, locale.toLanguageTag());
+ nSetLocale(mNativePtr, locale.toLanguageTag(), Hyphenator.get(locale));
mLocale = locale;
}
}
@@ -459,7 +461,26 @@
byte[] chdirs = measured.mLevels;
int dir = measured.mDir;
boolean easy = measured.mEasy;
- nSetText(b.mNativePtr, chs, paraEnd - paraStart);
+
+ // tab stop locations
+ int[] variableTabStops = null;
+ if (spanned != null) {
+ TabStopSpan[] spans = getParagraphSpans(spanned, paraStart,
+ paraEnd, TabStopSpan.class);
+ if (spans.length > 0) {
+ int[] stops = new int[spans.length];
+ for (int i = 0; i < spans.length; i++) {
+ stops[i] = spans[i].getTabStop();
+ }
+ Arrays.sort(stops, 0, stops.length);
+ variableTabStops = stops;
+ }
+ }
+
+ int breakStrategy = 0; // 0 = kBreakStrategy_Greedy
+ nSetupParagraph(b.mNativePtr, chs, paraEnd - paraStart,
+ firstWidth, firstWidthLineCount, restWidth,
+ variableTabStops, TAB_INCREMENT, breakStrategy);
// measurement has to be done before performing line breaking
// but we don't want to recompute fontmetrics or span ranges the
@@ -505,29 +526,13 @@
spanEndCacheCount++;
}
- // tab stop locations
- int[] variableTabStops = null;
- if (spanned != null) {
- TabStopSpan[] spans = getParagraphSpans(spanned, paraStart,
- paraEnd, TabStopSpan.class);
- if (spans.length > 0) {
- int[] stops = new int[spans.length];
- for (int i = 0; i < spans.length; i++) {
- stops[i] = spans[i].getTabStop();
- }
- Arrays.sort(stops, 0, stops.length);
- variableTabStops = stops;
- }
- }
-
nGetWidths(b.mNativePtr, widths);
- int breakCount = nComputeLineBreaks(b.mNativePtr, paraEnd - paraStart, firstWidth,
- firstWidthLineCount, restWidth, variableTabStops, TAB_INCREMENT, false, lineBreaks,
- lineBreaks.breaks, lineBreaks.widths, lineBreaks.flags, lineBreaks.breaks.length);
+ int breakCount = nComputeLineBreaks(b.mNativePtr, lineBreaks, lineBreaks.breaks,
+ lineBreaks.widths, lineBreaks.flags, lineBreaks.breaks.length);
int[] breaks = lineBreaks.breaks;
float[] lineWidths = lineBreaks.widths;
- boolean[] flags = lineBreaks.flags;
+ int[] flags = lineBreaks.flags;
// here is the offset of the starting character of the line we are currently measuring
int here = paraStart;
@@ -613,7 +618,7 @@
fm.top, fm.bottom,
v,
spacingmult, spacingadd, null,
- null, fm, false,
+ null, fm, 0,
needMultiply, measured.mLevels, measured.mDir, measured.mEasy, bufEnd,
includepad, trackpad, null,
null, bufStart, ellipsize,
@@ -625,7 +630,7 @@
int above, int below, int top, int bottom, int v,
float spacingmult, float spacingadd,
LineHeightSpan[] chooseHt, int[] chooseHtv,
- Paint.FontMetricsInt fm, boolean hasTabOrEmoji,
+ Paint.FontMetricsInt fm, int flags,
boolean needMultiply, byte[] chdirs, int dir,
boolean easy, int bufEnd, boolean includePad,
boolean trackPad, char[] chs,
@@ -718,8 +723,10 @@
lines[off + mColumns + START] = end;
lines[off + mColumns + TOP] = v;
- if (hasTabOrEmoji)
- lines[off + TAB] |= TAB_MASK;
+ // 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 + DIR] |= dir << DIR_SHIFT;
Directions linedirs = DIRS_ALL_LEFT_TO_RIGHT;
@@ -938,6 +945,14 @@
return mBottomPadding;
}
+ /**
+ * @hide
+ */
+ @Override
+ public int getHyphen(int line) {
+ return mLines[mColumns * line + HYPHEN] & 0xff;
+ }
+
@Override
public int getEllipsisCount(int line) {
if (mColumns < COLUMNS_ELLIPSIZE) {
@@ -964,9 +979,15 @@
private static native long nNewBuilder();
private static native void nFreeBuilder(long nativePtr);
private static native void nFinishBuilder(long nativePtr);
- private static native void nSetLocale(long nativePtr, String locale);
- private static native void nSetText(long nativePtr, char[] text, int length);
+ /* package */ static native long nLoadHyphenator(String patternData);
+
+ private static native void nSetLocale(long nativePtr, String locale, long nativeHyphenator);
+
+ // Set up paragraph text and settings; done as one big method to minimize jni crossings
+ private static native void nSetupParagraph(long nativePtr, char[] text, int length,
+ float firstWidth, int firstWidthLineCount, float restWidth,
+ int[] variableTabStops, int defaultTabStop, int breakStrategy);
private static native float nAddStyleRun(long nativePtr, long nativePaint,
long nativeTypeface, int start, int end, boolean isRtl);
@@ -983,25 +1004,24 @@
// the arrays inside the LineBreaks objects are passed in as well
// to reduce the number of JNI calls in the common case where the
// arrays do not have to be resized
- private static native int nComputeLineBreaks(long nativePtr,
- int length, float firstWidth, int firstWidthLineCount, float restWidth,
- int[] variableTabStops, int defaultTabStop, boolean optimize, LineBreaks recycle,
- int[] recycleBreaks, float[] recycleWidths, boolean[] recycleFlags, int recycleLength);
+ private static native int nComputeLineBreaks(long nativePtr, LineBreaks recycle,
+ int[] recycleBreaks, float[] recycleWidths, int[] recycleFlags, int recycleLength);
private int mLineCount;
private int mTopPadding, mBottomPadding;
private int mColumns;
private int mEllipsizedWidth;
- private static final int COLUMNS_NORMAL = 3;
- private static final int COLUMNS_ELLIPSIZE = 5;
+ private static final int COLUMNS_NORMAL = 4;
+ private static final int COLUMNS_ELLIPSIZE = 6;
private static final int START = 0;
private static final int DIR = START;
private static final int TAB = START;
private static final int TOP = 1;
private static final int DESCENT = 2;
- private static final int ELLIPSIS_START = 3;
- private static final int ELLIPSIS_COUNT = 4;
+ private static final int HYPHEN = 3;
+ private static final int ELLIPSIS_START = 4;
+ private static final int ELLIPSIS_COUNT = 5;
private int[] mLines;
private Directions[] mLineDirections;
@@ -1023,7 +1043,7 @@
private static final int INITIAL_SIZE = 16;
public int[] breaks = new int[INITIAL_SIZE];
public float[] widths = new float[INITIAL_SIZE];
- public boolean[] flags = new boolean[INITIAL_SIZE]; // hasTabOrEmoji
+ public int[] flags = new int[INITIAL_SIZE]; // hasTabOrEmoji
// breaks, widths, and flags should all have the same length
}
diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java
index 4725581..479242c 100644
--- a/core/java/android/text/TextLine.java
+++ b/core/java/android/text/TextLine.java
@@ -955,6 +955,10 @@
span.updateDrawState(wp);
}
+ // Only draw hyphen on last run in line
+ if (jnext < mLen) {
+ wp.setHyphenEdit(0);
+ }
x += handleText(wp, j, jnext, i, inext, runIsRtl, c, x,
top, y, bottom, fmi, needWidth || jnext < measureLimit);
}
diff --git a/core/java/android/text/format/Time.java b/core/java/android/text/format/Time.java
index 0c66709..d567d90 100644
--- a/core/java/android/text/format/Time.java
+++ b/core/java/android/text/format/Time.java
@@ -47,6 +47,7 @@
* before 1st Jan 1970 UTC).</li>
* <li>Much of the formatting / parsing assumes ASCII text and is therefore not suitable for
* use with non-ASCII scripts.</li>
+ * <li>No support for pseudo-zones like "GMT-07:00".</li>
* </ul>
*
* @deprecated Use {@link java.util.GregorianCalendar} instead.
diff --git a/core/java/android/util/DebugUtils.java b/core/java/android/util/DebugUtils.java
index 84d9ce8..c44f42b 100644
--- a/core/java/android/util/DebugUtils.java
+++ b/core/java/android/util/DebugUtils.java
@@ -17,8 +17,10 @@
package android.util;
import java.io.PrintWriter;
-import java.lang.reflect.Method;
+import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
import java.util.Locale;
/**
@@ -203,4 +205,57 @@
outBuilder.append(suffix);
return outBuilder.toString();
}
+
+ /**
+ * Use prefixed constants (static final values) on given class to turn value
+ * into human-readable string.
+ *
+ * @hide
+ */
+ public static String valueToString(Class<?> clazz, String prefix, int value) {
+ for (Field field : clazz.getDeclaredFields()) {
+ final int modifiers = field.getModifiers();
+ if (Modifier.isStatic(modifiers) && Modifier.isFinal(modifiers)
+ && field.getType().equals(int.class) && field.getName().startsWith(prefix)) {
+ try {
+ if (value == field.getInt(null)) {
+ return field.getName().substring(prefix.length());
+ }
+ } catch (IllegalAccessException ignored) {
+ }
+ }
+ }
+ return Integer.toString(value);
+ }
+
+ /**
+ * Use prefixed constants (static final values) on given class to turn flags
+ * into human-readable string.
+ *
+ * @hide
+ */
+ public static String flagsToString(Class<?> clazz, String prefix, int flags) {
+ final StringBuilder res = new StringBuilder();
+
+ for (Field field : clazz.getDeclaredFields()) {
+ final int modifiers = field.getModifiers();
+ if (Modifier.isStatic(modifiers) && Modifier.isFinal(modifiers)
+ && field.getType().equals(int.class) && field.getName().startsWith(prefix)) {
+ try {
+ final int value = field.getInt(null);
+ if ((flags & value) != 0) {
+ flags &= ~value;
+ res.append(field.getName().substring(prefix.length())).append('|');
+ }
+ } catch (IllegalAccessException ignored) {
+ }
+ }
+ }
+ if (flags != 0 || res.length() == 0) {
+ res.append(Integer.toHexString(flags));
+ } else {
+ res.deleteCharAt(res.length() - 1);
+ }
+ return res.toString();
+ }
}
diff --git a/core/java/android/view/IGraphicsStats.aidl b/core/java/android/view/IGraphicsStats.aidl
new file mode 100644
index 0000000..c235eb2
--- /dev/null
+++ b/core/java/android/view/IGraphicsStats.aidl
@@ -0,0 +1,26 @@
+/**
+ * Copyright (c) 2015, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.os.ParcelFileDescriptor;
+
+/**
+ * @hide
+ */
+interface IGraphicsStats {
+ ParcelFileDescriptor requestBufferForProcess(String packageName, IBinder token);
+}
diff --git a/core/java/android/view/ScaleGestureDetector.java b/core/java/android/view/ScaleGestureDetector.java
index 6508cca..5cf2c5c 100644
--- a/core/java/android/view/ScaleGestureDetector.java
+++ b/core/java/android/view/ScaleGestureDetector.java
@@ -130,6 +130,7 @@
private float mFocusY;
private boolean mQuickScaleEnabled;
+ private boolean mButtonScaleEnabled;
private float mCurrSpan;
private float mPrevSpan;
@@ -151,14 +152,17 @@
private int mTouchHistoryDirection;
private long mTouchHistoryLastAcceptedTime;
private int mTouchMinMajor;
- private MotionEvent mDoubleTapEvent;
- private int mDoubleTapMode = DOUBLE_TAP_MODE_NONE;
private final Handler mHandler;
+ private float mAnchoredScaleStartX;
+ private float mAnchoredScaleStartY;
+ private int mAnchoredScaleMode = ANCHORED_SCALE_MODE_NONE;
+
private static final long TOUCH_STABILIZE_TIME = 128; // ms
- private static final int DOUBLE_TAP_MODE_NONE = 0;
- private static final int DOUBLE_TAP_MODE_IN_PROGRESS = 1;
private static final float SCALE_FACTOR = .5f;
+ private static final int ANCHORED_SCALE_MODE_NONE = 0;
+ private static final int ANCHORED_SCALE_MODE_DOUBLE_TAP = 1;
+ private static final int ANCHORED_SCALE_MODE_BUTTON = 2;
/**
@@ -310,8 +314,17 @@
mGestureDetector.onTouchEvent(event);
}
+ final int count = event.getPointerCount();
+ final int toolType = event.getToolType(0);
+ final boolean isButtonTool = toolType == MotionEvent.TOOL_TYPE_STYLUS
+ || toolType == MotionEvent.TOOL_TYPE_MOUSE;
+ final boolean isAnchoredScaleButtonDown = isButtonTool && (count == 1)
+ && (event.getButtonState() & MotionEvent.BUTTON_SECONDARY) != 0;
+
+ final boolean anchoredScaleCancelled =
+ mAnchoredScaleMode == ANCHORED_SCALE_MODE_BUTTON && !isAnchoredScaleButtonDown;
final boolean streamComplete = action == MotionEvent.ACTION_UP ||
- action == MotionEvent.ACTION_CANCEL;
+ action == MotionEvent.ACTION_CANCEL || anchoredScaleCancelled;
if (action == MotionEvent.ACTION_DOWN || streamComplete) {
// Reset any scale in progress with the listener.
@@ -321,11 +334,11 @@
mListener.onScaleEnd(this);
mInProgress = false;
mInitialSpan = 0;
- mDoubleTapMode = DOUBLE_TAP_MODE_NONE;
- } else if (mDoubleTapMode == DOUBLE_TAP_MODE_IN_PROGRESS && streamComplete) {
+ mAnchoredScaleMode = ANCHORED_SCALE_MODE_NONE;
+ } else if (inAnchoredScaleMode() && streamComplete) {
mInProgress = false;
mInitialSpan = 0;
- mDoubleTapMode = DOUBLE_TAP_MODE_NONE;
+ mAnchoredScaleMode = ANCHORED_SCALE_MODE_NONE;
}
if (streamComplete) {
@@ -334,25 +347,32 @@
}
}
+ if (!mInProgress && mButtonScaleEnabled && !inAnchoredScaleMode()
+ && !streamComplete && isAnchoredScaleButtonDown) {
+ // Start of a button scale gesture
+ mAnchoredScaleStartX = event.getX();
+ mAnchoredScaleStartY = event.getY();
+ mAnchoredScaleMode = ANCHORED_SCALE_MODE_BUTTON;
+ mInitialSpan = 0;
+ }
+
final boolean configChanged = action == MotionEvent.ACTION_DOWN ||
action == MotionEvent.ACTION_POINTER_UP ||
- action == MotionEvent.ACTION_POINTER_DOWN;
-
+ action == MotionEvent.ACTION_POINTER_DOWN || anchoredScaleCancelled;
final boolean pointerUp = action == MotionEvent.ACTION_POINTER_UP;
final int skipIndex = pointerUp ? event.getActionIndex() : -1;
// Determine focal point
float sumX = 0, sumY = 0;
- final int count = event.getPointerCount();
final int div = pointerUp ? count - 1 : count;
final float focusX;
final float focusY;
- if (mDoubleTapMode == DOUBLE_TAP_MODE_IN_PROGRESS) {
- // In double tap mode, the focal pt is always where the double tap
- // gesture started
- focusX = mDoubleTapEvent.getX();
- focusY = mDoubleTapEvent.getY();
+ if (inAnchoredScaleMode()) {
+ // In anchored scale mode, the focal pt is always where the double tap
+ // or button down gesture started
+ focusX = mAnchoredScaleStartX;
+ focusY = mAnchoredScaleStartY;
if (event.getY() < focusY) {
mEventBeforeOrAboveStartingGestureEvent = true;
} else {
@@ -390,7 +410,7 @@
final float spanX = devX * 2;
final float spanY = devY * 2;
final float span;
- if (inDoubleTapMode()) {
+ if (inAnchoredScaleMode()) {
span = spanY;
} else {
span = (float) Math.hypot(spanX, spanY);
@@ -402,11 +422,10 @@
final boolean wasInProgress = mInProgress;
mFocusX = focusX;
mFocusY = focusY;
- if (!inDoubleTapMode() && mInProgress && (span < mMinSpan || configChanged)) {
+ if (!inAnchoredScaleMode() && mInProgress && (span < mMinSpan || configChanged)) {
mListener.onScaleEnd(this);
mInProgress = false;
mInitialSpan = span;
- mDoubleTapMode = DOUBLE_TAP_MODE_NONE;
}
if (configChanged) {
mPrevSpanX = mCurrSpanX = spanX;
@@ -414,7 +433,7 @@
mInitialSpan = mPrevSpan = mCurrSpan = span;
}
- final int minSpan = inDoubleTapMode() ? mSpanSlop : mMinSpan;
+ final int minSpan = inAnchoredScaleMode() ? mSpanSlop : mMinSpan;
if (!mInProgress && span >= minSpan &&
(wasInProgress || Math.abs(span - mInitialSpan) > mSpanSlop)) {
mPrevSpanX = mCurrSpanX = spanX;
@@ -447,9 +466,8 @@
return true;
}
-
- private boolean inDoubleTapMode() {
- return mDoubleTapMode == DOUBLE_TAP_MODE_IN_PROGRESS;
+ private boolean inAnchoredScaleMode() {
+ return mAnchoredScaleMode != ANCHORED_SCALE_MODE_NONE;
}
/**
@@ -466,8 +484,9 @@
@Override
public boolean onDoubleTap(MotionEvent e) {
// Double tap: start watching for a swipe
- mDoubleTapEvent = e;
- mDoubleTapMode = DOUBLE_TAP_MODE_IN_PROGRESS;
+ mAnchoredScaleStartX = e.getX();
+ mAnchoredScaleStartY = e.getY();
+ mAnchoredScaleMode = ANCHORED_SCALE_MODE_DOUBLE_TAP;
return true;
}
};
@@ -484,6 +503,27 @@
}
/**
+ * Sets whether the associates {@link OnScaleGestureListener} should receive onScale callbacks
+ * when the user presses a {@value MotionEvent#BUTTON_SECONDARY} (right mouse button, stylus
+ * first button) and drags the pointer on the screen. Note that this is enabled by default if
+ * the app targets API 23 and newer.
+ *
+ * @param scales true to enable stylus or mouse scaling, false to disable.
+ */
+ public void setSecondaryButtonScaleEnabled(boolean scales) {
+ mButtonScaleEnabled = scales;
+ }
+
+ /**
+ * Return whether the button scale gesture, in which the user presses a
+ * {@value MotionEvent#BUTTON_SECONDARY} (right mouse button, stylus first button) and drags the
+ * pointer on the screen, should perform scaling. {@see #setButtonScaleEnabled(boolean)}.
+ */
+ public boolean isSecondaryButtonScaleEnabled() {
+ return mButtonScaleEnabled;
+ }
+
+ /**
* Returns {@code true} if a scale gesture is in progress.
*/
public boolean isInProgress() {
@@ -586,7 +626,7 @@
* @return The current scaling factor.
*/
public float getScaleFactor() {
- if (inDoubleTapMode()) {
+ if (inAnchoredScaleMode()) {
// Drag is moving up; the further away from the gesture
// start, the smaller the span should be, the closer,
// the larger the span, and therefore the larger the scale
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index 031be07..87d5d9a 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -23,7 +23,9 @@
import android.graphics.Bitmap;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
+import android.os.Binder;
import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.Trace;
@@ -124,7 +126,7 @@
mRootNode.setClipToBounds(false);
mNativeProxy = nCreateProxy(translucent, rootNodePtr);
- AtlasInitializer.sInstance.init(context, mNativeProxy);
+ ProcessInitializer.sInstance.init(context, mNativeProxy);
loadSystemProperties();
}
@@ -410,15 +412,44 @@
nTrimMemory(level);
}
- private static class AtlasInitializer {
- static AtlasInitializer sInstance = new AtlasInitializer();
+ public static void dumpProfileData(byte[] data, FileDescriptor fd) {
+ nDumpProfileData(data, fd);
+ }
+
+ private static class ProcessInitializer {
+ static ProcessInitializer sInstance = new ProcessInitializer();
+ static IGraphicsStats sGraphicsStatsService;
+ private static IBinder sProcToken;
private boolean mInitialized = false;
- private AtlasInitializer() {}
+ private ProcessInitializer() {}
synchronized void init(Context context, long renderProxy) {
if (mInitialized) return;
+ mInitialized = true;
+ initGraphicsStats(context, renderProxy);
+ initAssetAtlas(context, renderProxy);
+ }
+
+ private static void initGraphicsStats(Context context, long renderProxy) {
+ IBinder binder = ServiceManager.getService("graphicsstats");
+ if (binder == null) return;
+
+ sGraphicsStatsService = IGraphicsStats.Stub.asInterface(binder);
+ sProcToken = new Binder();
+ try {
+ final String pkg = context.getApplicationInfo().packageName;
+ ParcelFileDescriptor pfd = sGraphicsStatsService.
+ requestBufferForProcess(pkg, sProcToken);
+ nSetProcessStatsBuffer(renderProxy, pfd.getFd());
+ pfd.close();
+ } catch (Exception e) {
+ Log.w(LOG_TAG, "Could not acquire gfx stats buffer", e);
+ }
+ }
+
+ private static void initAssetAtlas(Context context, long renderProxy) {
IBinder binder = ServiceManager.getService("assetatlas");
if (binder == null) return;
@@ -432,7 +463,6 @@
// TODO Remove after fixing b/15425820
validateMap(context, map);
nSetAtlas(renderProxy, buffer, map);
- mInitialized = true;
}
// If IAssetAtlas is not the same class as the IBinder
// we are using a remote service and we can safely
@@ -477,6 +507,7 @@
static native void setupShadersDiskCache(String cacheFile);
private static native void nSetAtlas(long nativeProxy, GraphicBuffer buffer, long[] map);
+ private static native void nSetProcessStatsBuffer(long nativeProxy, int fd);
private static native long nCreateRootRenderNode();
private static native long nCreateProxy(boolean translucent, long rootRenderNode);
@@ -514,4 +545,5 @@
private static native void nDumpProfileInfo(long nativeProxy, FileDescriptor fd,
@DumpFlags int dumpFlags);
+ private static native void nDumpProfileData(byte[] data, FileDescriptor fd);
}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index cfcc6fe..a69384a 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -9959,6 +9959,8 @@
* @param oldt Previous vertical scroll origin.
*/
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
+ notifySubtreeAccessibilityStateChangedIfNeeded();
+
if (AccessibilityManager.getInstance(mContext).isEnabled()) {
postSendViewScrolledAccessibilityEventCallback();
}
@@ -11162,6 +11164,7 @@
invalidateViewProperty(false, true);
invalidateParentIfNeededAndWasQuickRejected();
+ notifySubtreeAccessibilityStateChangedIfNeeded();
}
}
diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java
index 50e64c6..a237afd 100644
--- a/core/java/android/view/ViewDebug.java
+++ b/core/java/android/view/ViewDebug.java
@@ -1005,31 +1005,23 @@
return fields;
}
- final ArrayList<Field> declaredFields = new ArrayList();
- klass.getDeclaredFieldsUnchecked(false, declaredFields);
-
- final ArrayList<Field> foundFields = new ArrayList<Field>();
- final int count = declaredFields.size();
- for (int i = 0; i < count; i++) {
- final Field field = declaredFields.get(i);
-
- // Ensure the field type can be resolved.
- try {
- field.getType();
- } catch (NoClassDefFoundError e) {
- continue;
+ try {
+ final Field[] declaredFields = klass.getDeclaredFieldsUnchecked(false);
+ final ArrayList<Field> foundFields = new ArrayList<Field>();
+ for (final Field field : declaredFields) {
+ // Fields which can't be resolved have a null type.
+ if (field.getType() != null && field.isAnnotationPresent(ExportedProperty.class)) {
+ field.setAccessible(true);
+ foundFields.add(field);
+ sAnnotations.put(field, field.getAnnotation(ExportedProperty.class));
+ }
}
-
- if (field.isAnnotationPresent(ExportedProperty.class)) {
- field.setAccessible(true);
- foundFields.add(field);
- sAnnotations.put(field, field.getAnnotation(ExportedProperty.class));
- }
+ fields = foundFields.toArray(new Field[foundFields.size()]);
+ map.put(klass, fields);
+ } catch (NoClassDefFoundError e) {
+ throw new AssertionError(e);
}
- fields = foundFields.toArray(new Field[foundFields.size()]);
- map.put(klass, fields);
-
return fields;
}
@@ -1651,4 +1643,4 @@
}
});
}
-}
\ No newline at end of file
+}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 294174a..4158340 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -6268,41 +6268,79 @@
case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED: {
- if (mAccessibilityFocusedHost != null && mAccessibilityFocusedVirtualView != null) {
- // We care only for changes rooted in the focused host.
- final long eventSourceId = event.getSourceNodeId();
- final int hostViewId = AccessibilityNodeInfo.getAccessibilityViewId(
- eventSourceId);
- if (hostViewId != mAccessibilityFocusedHost.getAccessibilityViewId()) {
- break;
- }
-
- // We only care about changes that may change the virtual focused view bounds.
- final int changes = event.getContentChangeTypes();
- if ((changes & AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE) != 0
- || changes == AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED) {
- AccessibilityNodeProvider provider = mAccessibilityFocusedHost
- .getAccessibilityNodeProvider();
- if (provider != null) {
- final int virtualChildId = AccessibilityNodeInfo.getVirtualDescendantId(
- mAccessibilityFocusedVirtualView.getSourceNodeId());
- if (virtualChildId == AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
- mAccessibilityFocusedVirtualView = provider
- .createAccessibilityNodeInfo(
- AccessibilityNodeProvider.HOST_VIEW_ID);
- } else {
- mAccessibilityFocusedVirtualView = provider
- .createAccessibilityNodeInfo(virtualChildId);
- }
- }
- }
- }
+ handleWindowContentChangedEvent(event);
} break;
}
mAccessibilityManager.sendAccessibilityEvent(event);
return true;
}
+ /**
+ * Updates the focused virtual view, when necessary, in response to a
+ * content changed event.
+ * <p>
+ * This is necessary to get updated bounds after a position change.
+ *
+ * @param event an accessibility event of type
+ * {@link AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED}
+ */
+ private void handleWindowContentChangedEvent(AccessibilityEvent event) {
+ // No virtual view focused, nothing to do here.
+ if (mAccessibilityFocusedHost == null || mAccessibilityFocusedVirtualView == null) {
+ return;
+ }
+
+ // If we have a node but no provider, abort.
+ final AccessibilityNodeProvider provider =
+ mAccessibilityFocusedHost.getAccessibilityNodeProvider();
+ if (provider == null) {
+ // TODO: Should we clear the focused virtual view?
+ return;
+ }
+
+ // We only care about change types that may affect the bounds of the
+ // focused virtual view.
+ final int changes = event.getContentChangeTypes();
+ if ((changes & AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE) == 0
+ && changes != AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED) {
+ return;
+ }
+
+ final long eventSourceNodeId = event.getSourceNodeId();
+ final int changedViewId = AccessibilityNodeInfo.getAccessibilityViewId(eventSourceNodeId);
+
+ // Search up the tree for subtree containment.
+ boolean hostInSubtree = false;
+ View root = mAccessibilityFocusedHost;
+ while (root != null && !hostInSubtree) {
+ if (changedViewId == root.getAccessibilityViewId()) {
+ hostInSubtree = true;
+ } else {
+ final ViewParent parent = root.getParent();
+ if (parent instanceof View) {
+ root = (View) parent;
+ } else {
+ root = null;
+ }
+ }
+ }
+
+ // We care only about changes in subtrees containing the host view.
+ if (!hostInSubtree) {
+ return;
+ }
+
+ final long focusedSourceNodeId = mAccessibilityFocusedVirtualView.getSourceNodeId();
+ int focusedChildId = AccessibilityNodeInfo.getVirtualDescendantId(focusedSourceNodeId);
+ if (focusedChildId == AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
+ // TODO: Should we clear the focused virtual view?
+ focusedChildId = AccessibilityNodeProvider.HOST_VIEW_ID;
+ }
+
+ // Refresh the node for the focused virtual view.
+ mAccessibilityFocusedVirtualView = provider.createAccessibilityNodeInfo(focusedChildId);
+ }
+
@Override
public void notifySubtreeAccessibilityStateChanged(View child, View source, int changeType) {
postSendWindowContentChangedCallback(source, changeType);
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index 9a92932..36f047e 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -1856,14 +1856,14 @@
public abstract int getStatusBarColor();
/**
- * Sets the color of the status bar to {@param color}.
+ * Sets the color of the status bar to {@code color}.
*
* For this to take effect,
* the window must be drawing the system bar backgrounds with
* {@link android.view.WindowManager.LayoutParams#FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS} and
* {@link android.view.WindowManager.LayoutParams#FLAG_TRANSLUCENT_STATUS} must not be set.
*
- * If {@param color} is not opaque, consider setting
+ * If {@code color} is not opaque, consider setting
* {@link android.view.View#SYSTEM_UI_FLAG_LAYOUT_STABLE} and
* {@link android.view.View#SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN}.
* <p>
diff --git a/core/java/android/webkit/WebChromeClient.java b/core/java/android/webkit/WebChromeClient.java
index 4737e9b..0b18bb8 100644
--- a/core/java/android/webkit/WebChromeClient.java
+++ b/core/java/android/webkit/WebChromeClient.java
@@ -284,13 +284,19 @@
* currently set for that origin. The host application should invoke the
* specified callback with the desired permission state. See
* {@link GeolocationPermissions} for details.
+ *
+ * If this method isn't overridden, the callback is invoked with permission
+ * denied state.
+ *
* @param origin The origin of the web content attempting to use the
* Geolocation API.
* @param callback The callback to use to set the permission state for the
* origin.
*/
public void onGeolocationPermissionsShowPrompt(String origin,
- GeolocationPermissions.Callback callback) {}
+ GeolocationPermissions.Callback callback) {
+ callback.invoke(origin, false, false);
+ }
/**
* Notify the host application that a request for Geolocation permissions,
diff --git a/core/java/android/webkit/WebViewClient.java b/core/java/android/webkit/WebViewClient.java
index 53c7e04..8a2b3fa 100644
--- a/core/java/android/webkit/WebViewClient.java
+++ b/core/java/android/webkit/WebViewClient.java
@@ -200,8 +200,6 @@
public static final int ERROR_FILE_NOT_FOUND = -14;
/** Too many requests during this load */
public static final int ERROR_TOO_MANY_REQUESTS = -15;
- /** Request blocked by the browser */
- public static final int ERROR_BLOCKED = -16;
/**
* Report an error to the host application. These errors are unrecoverable
diff --git a/core/java/android/widget/AnalogClock.java b/core/java/android/widget/AnalogClock.java
index 1716dbd..45eee34 100644
--- a/core/java/android/widget/AnalogClock.java
+++ b/core/java/android/widget/AnalogClock.java
@@ -40,8 +40,10 @@
* @attr ref android.R.styleable#AnalogClock_dial
* @attr ref android.R.styleable#AnalogClock_hand_hour
* @attr ref android.R.styleable#AnalogClock_hand_minute
+ * @deprecated This widget is no longer supported.
*/
@RemoteView
+@Deprecated
public class AnalogClock extends View {
private Time mCalendar;
diff --git a/core/java/android/widget/DatePickerCalendarDelegate.java b/core/java/android/widget/DatePickerCalendarDelegate.java
index 7b8a979..a157087 100755
--- a/core/java/android/widget/DatePickerCalendarDelegate.java
+++ b/core/java/android/widget/DatePickerCalendarDelegate.java
@@ -49,7 +49,6 @@
* A delegate for picking up a date (day / month / year).
*/
class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate {
-
private static final int USE_LOCALE = 0;
private static final int UNINITIALIZED = -1;
@@ -61,9 +60,9 @@
private static final int ANIMATION_DURATION = 300;
- public static final int[] ATTRS_TEXT_COLOR = new int[]{com.android.internal.R.attr.textColor};
-
- public static final int[] ATTRS_DISABLED_ALPHA = new int[]{
+ private static final int[] ATTRS_TEXT_COLOR = new int[] {
+ com.android.internal.R.attr.textColor};
+ private static final int[] ATTRS_DISABLED_ALPHA = new int[] {
com.android.internal.R.attr.disabledAlpha};
private SimpleDateFormat mYearFormat;
@@ -157,6 +156,8 @@
header.setBackground(a.getDrawable(R.styleable.DatePicker_headerBackground));
}
+ a.recycle();
+
// Set up picker container.
mAnimator = (ViewAnimator) mContainer.findViewById(R.id.animator);
@@ -174,32 +175,10 @@
mYearPickerView.setDate(mCurrentDate.getTimeInMillis());
mYearPickerView.setOnYearSelectedListener(mOnYearSelectedListener);
- final int yearTextAppearanceResId = a.getResourceId(
- R.styleable.DatePicker_yearListItemTextAppearance, 0);
- if (yearTextAppearanceResId != 0) {
- mYearPickerView.setYearTextAppearance(yearTextAppearanceResId);
- }
-
- final int yearActivatedTextAppearanceResId = a.getResourceId(
- R.styleable.DatePicker_yearListItemActivatedTextAppearance, 0);
- if (yearActivatedTextAppearanceResId != 0) {
- mYearPickerView.setYearActivatedTextAppearance(yearActivatedTextAppearanceResId);
- }
-
- a.recycle();
-
// Set up content descriptions.
mSelectDay = res.getString(R.string.select_day);
mSelectYear = res.getString(R.string.select_year);
- final Animation inAnim = new AlphaAnimation(0, 1);
- inAnim.setDuration(ANIMATION_DURATION);
- mAnimator.setInAnimation(inAnim);
-
- final Animation outAnim = new AlphaAnimation(1, 0);
- outAnim.setDuration(ANIMATION_DURATION);
- mAnimator.setOutAnimation(outAnim);
-
// Initialize for current locale. This also initializes the date, so no
// need to call onDateChanged.
onLocaleChanged(mCurrentLocale);
diff --git a/core/java/android/widget/DayPickerAdapter.java b/core/java/android/widget/DayPickerAdapter.java
index 4f9f09e..9a4b6f5 100644
--- a/core/java/android/widget/DayPickerAdapter.java
+++ b/core/java/android/widget/DayPickerAdapter.java
@@ -18,10 +18,15 @@
import com.android.internal.widget.PagerAdapter;
+import android.annotation.IdRes;
+import android.annotation.LayoutRes;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.TypedArray;
import android.util.SparseArray;
+import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.SimpleMonthView.OnDayClickListener;
@@ -37,9 +42,13 @@
private final Calendar mMinDate = Calendar.getInstance();
private final Calendar mMaxDate = Calendar.getInstance();
- private final SparseArray<SimpleMonthView> mItems = new SparseArray<>();
+ private final SparseArray<ViewHolder> mItems = new SparseArray<>();
- private Calendar mSelectedDay = Calendar.getInstance();
+ private final LayoutInflater mInflater;
+ private final int mLayoutResId;
+ private final int mCalendarViewId;
+
+ private Calendar mSelectedDay = null;
private int mMonthTextAppearance;
private int mDayOfWeekTextAppearance;
@@ -51,19 +60,29 @@
private OnDaySelectedListener mOnDaySelectedListener;
+ private int mCount;
private int mFirstDayOfWeek;
- public DayPickerAdapter(Context context) {
+ public DayPickerAdapter(@NonNull Context context, @LayoutRes int layoutResId,
+ @IdRes int calendarViewId) {
+ mInflater = LayoutInflater.from(context);
+ mLayoutResId = layoutResId;
+ mCalendarViewId = calendarViewId;
+
final TypedArray ta = context.obtainStyledAttributes(new int[] {
com.android.internal.R.attr.colorControlHighlight});
mDayHighlightColor = ta.getColorStateList(0);
ta.recycle();
}
- public void setRange(Calendar min, Calendar max) {
+ public void setRange(@NonNull Calendar min, @NonNull Calendar max) {
mMinDate.setTimeInMillis(min.getTimeInMillis());
mMaxDate.setTimeInMillis(max.getTimeInMillis());
+ final int diffYear = mMaxDate.get(Calendar.YEAR) - mMinDate.get(Calendar.YEAR);
+ final int diffMonth = mMaxDate.get(Calendar.MONTH) - mMinDate.get(Calendar.MONTH);
+ mCount = diffMonth + MONTHS_IN_YEAR * diffYear + 1;
+
// Positions are now invalid, clear everything and start over.
notifyDataSetChanged();
}
@@ -80,7 +99,7 @@
// Update displayed views.
final int count = mItems.size();
for (int i = 0; i < count; i++) {
- final SimpleMonthView monthView = mItems.valueAt(i);
+ final SimpleMonthView monthView = mItems.valueAt(i).calendar;
monthView.setFirstDayOfWeek(weekStart);
}
}
@@ -94,23 +113,25 @@
*
* @param day the selected day
*/
- public void setSelectedDay(Calendar day) {
+ public void setSelectedDay(@Nullable Calendar day) {
final int oldPosition = getPositionForDay(mSelectedDay);
final int newPosition = getPositionForDay(day);
// Clear the old position if necessary.
- if (oldPosition != newPosition) {
- final SimpleMonthView oldMonthView = mItems.get(oldPosition, null);
+ if (oldPosition != newPosition && oldPosition >= 0) {
+ final ViewHolder oldMonthView = mItems.get(oldPosition, null);
if (oldMonthView != null) {
- oldMonthView.setSelectedDay(-1);
+ oldMonthView.calendar.setSelectedDay(-1);
}
}
// Set the new position.
- final SimpleMonthView newMonthView = mItems.get(newPosition, null);
- if (newMonthView != null) {
- final int dayOfMonth = day.get(Calendar.DAY_OF_MONTH);
- newMonthView.setSelectedDay(dayOfMonth);
+ if (newPosition >= 0) {
+ final ViewHolder newMonthView = mItems.get(newPosition, null);
+ if (newMonthView != null) {
+ final int dayOfMonth = day.get(Calendar.DAY_OF_MONTH);
+ newMonthView.calendar.setSelectedDay(dayOfMonth);
+ }
}
mSelectedDay = day;
@@ -155,14 +176,13 @@
@Override
public int getCount() {
- final int diffYear = mMaxDate.get(Calendar.YEAR) - mMinDate.get(Calendar.YEAR);
- final int diffMonth = mMaxDate.get(Calendar.MONTH) - mMinDate.get(Calendar.MONTH);
- return diffMonth + MONTHS_IN_YEAR * diffYear + 1;
+ return mCount;
}
@Override
public boolean isViewFromObject(View view, Object object) {
- return view == object;
+ final ViewHolder holder = (ViewHolder) object;
+ return view == holder.container;
}
private int getMonthForPosition(int position) {
@@ -173,7 +193,11 @@
return position / MONTHS_IN_YEAR + mMinDate.get(Calendar.YEAR);
}
- private int getPositionForDay(Calendar day) {
+ private int getPositionForDay(@Nullable Calendar day) {
+ if (day == null) {
+ return -1;
+ }
+
final int yearOffset = (day.get(Calendar.YEAR) - mMinDate.get(Calendar.YEAR));
final int monthOffset = (day.get(Calendar.MONTH) - mMinDate.get(Calendar.MONTH));
return yearOffset * MONTHS_IN_YEAR + monthOffset;
@@ -181,7 +205,9 @@
@Override
public Object instantiateItem(ViewGroup container, int position) {
- final SimpleMonthView v = new SimpleMonthView(container.getContext());
+ final View itemView = mInflater.inflate(mLayoutResId, container, false);
+
+ final SimpleMonthView v = (SimpleMonthView) itemView.findViewById(mCalendarViewId);
v.setOnDayClickListener(mOnDayClickListener);
v.setMonthTextAppearance(mMonthTextAppearance);
v.setDayOfWeekTextAppearance(mDayOfWeekTextAppearance);
@@ -205,7 +231,7 @@
final int year = getYearForPosition(position);
final int selectedDay;
- if (mSelectedDay.get(Calendar.MONTH) == month) {
+ if (mSelectedDay != null && mSelectedDay.get(Calendar.MONTH) == month) {
selectedDay = mSelectedDay.get(Calendar.DAY_OF_MONTH);
} else {
selectedDay = -1;
@@ -227,33 +253,34 @@
v.setMonthParams(selectedDay, month, year, mFirstDayOfWeek,
enabledDayRangeStart, enabledDayRangeEnd);
+ v.setPrevEnabled(position > 0);
+ v.setNextEnabled(position < mCount - 1);
- mItems.put(position, v);
+ final ViewHolder holder = new ViewHolder(position, itemView, v);
+ mItems.put(position, holder);
- container.addView(v);
+ container.addView(itemView);
- return v;
+ return holder;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
- container.removeView(mItems.get(position));
+ final ViewHolder holder = (ViewHolder) object;
+ container.removeView(holder.container);
mItems.remove(position);
}
@Override
public int getItemPosition(Object object) {
- final int index = mItems.indexOfValue((SimpleMonthView) object);
- if (index < 0) {
- return mItems.keyAt(index);
- }
- return -1;
+ final ViewHolder holder = (ViewHolder) object;
+ return holder.position;
}
@Override
public CharSequence getPageTitle(int position) {
- final SimpleMonthView v = mItems.get(position);
+ final SimpleMonthView v = mItems.get(position).calendar;
if (v != null) {
return v.getTitle();
}
@@ -275,9 +302,29 @@
}
}
}
+
+ @Override
+ public void onNavigationClick(SimpleMonthView view, int direction, boolean animate) {
+ if (mOnDaySelectedListener != null) {
+ mOnDaySelectedListener.onNavigationClick(DayPickerAdapter.this, direction, animate);
+ }
+ }
};
+ private static class ViewHolder {
+ public final int position;
+ public final View container;
+ public final SimpleMonthView calendar;
+
+ public ViewHolder(int position, View container, SimpleMonthView calendar) {
+ this.position = position;
+ this.container = container;
+ this.calendar = calendar;
+ }
+ }
+
public interface OnDaySelectedListener {
public void onDaySelected(DayPickerAdapter view, Calendar day);
+ public void onNavigationClick(DayPickerAdapter view, int direction, boolean animate);
}
}
diff --git a/core/java/android/widget/DayPickerView.java b/core/java/android/widget/DayPickerView.java
index a7ae926..e2f8efc 100644
--- a/core/java/android/widget/DayPickerView.java
+++ b/core/java/android/widget/DayPickerView.java
@@ -88,7 +88,8 @@
a.recycle();
// Set up adapter.
- mAdapter = new DayPickerAdapter(context);
+ mAdapter = new DayPickerAdapter(context,
+ R.layout.date_picker_month_item_material, R.id.month_view);
mAdapter.setMonthTextAppearance(monthTextAppearanceResId);
mAdapter.setDayOfWeekTextAppearance(dayOfWeekTextAppearanceResId);
mAdapter.setDayTextAppearance(dayTextAppearanceResId);
@@ -128,6 +129,14 @@
mOnDaySelectedListener.onDaySelected(DayPickerView.this, day);
}
}
+
+ @Override
+ public void onNavigationClick(DayPickerAdapter view, int direction, boolean animate) {
+ // ViewPager clamps input values, so we don't need to worry
+ // about passing invalid indices.
+ final int nextItem = getCurrentItem() + direction;
+ setCurrentItem(nextItem, animate);
+ }
});
}
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 6e24837..32b99a8 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -16,18 +16,6 @@
package android.widget;
-import android.content.UndoManager;
-import android.content.UndoOperation;
-import android.content.UndoOwner;
-import android.os.Build;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.text.InputFilter;
-import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.GrowingArrayUtils;
-import com.android.internal.view.menu.MenuBuilder;
-import com.android.internal.widget.EditableInputConnection;
-
import android.R;
import android.app.PendingIntent;
import android.app.PendingIntent.CanceledException;
@@ -35,6 +23,9 @@
import android.content.ClipData.Item;
import android.content.Context;
import android.content.Intent;
+import android.content.UndoManager;
+import android.content.UndoOperation;
+import android.content.UndoOwner;
import android.content.pm.PackageManager;
import android.content.res.TypedArray;
import android.graphics.Canvas;
@@ -46,13 +37,17 @@
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.inputmethodservice.ExtractEditText;
+import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
+import android.os.Parcel;
+import android.os.Parcelable;
import android.os.ParcelableParcel;
import android.os.SystemClock;
import android.provider.Settings;
import android.text.DynamicLayout;
import android.text.Editable;
+import android.text.InputFilter;
import android.text.InputType;
import android.text.Layout;
import android.text.ParcelableSpan;
@@ -105,6 +100,11 @@
import android.widget.TextView.Drawables;
import android.widget.TextView.OnEditorActionListener;
+import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.GrowingArrayUtils;
+import com.android.internal.view.menu.MenuBuilder;
+import com.android.internal.widget.EditableInputConnection;
+
import java.text.BreakIterator;
import java.util.Arrays;
import java.util.Comparator;
@@ -2939,7 +2939,27 @@
* The default callback provides a subset of Select All, Cut, Copy and Paste actions, depending
* on which of these this TextView supports.
*/
- private class SelectionActionModeCallback implements ActionMode.Callback {
+ private class SelectionActionModeCallback extends ActionMode.Callback2 {
+ private final Path mSelectionPath = new Path();
+ private final RectF mSelectionBounds = new RectF();
+
+ private int mSelectionHandleHeight;
+ private int mInsertionHandleHeight;
+
+ public SelectionActionModeCallback() {
+ SelectionModifierCursorController selectionController = getSelectionController();
+ if (selectionController.mStartHandle == null) {
+ selectionController.initDrawables();
+ selectionController.initHandles();
+ }
+ mSelectionHandleHeight = Math.max(
+ mSelectHandleLeft.getMinimumHeight(), mSelectHandleRight.getMinimumHeight());
+ InsertionPointCursorController insertionController = getInsertionController();
+ if (insertionController != null) {
+ insertionController.getHandle();
+ mInsertionHandleHeight = mSelectHandleCenter.getMinimumHeight();
+ }
+ }
@Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
@@ -2956,13 +2976,6 @@
mode.setSubtitle(null);
mode.setTitleOptionalHint(true);
- menu.add(0, TextView.ID_SELECT_ALL, 0, com.android.internal.R.string.selectAll).
- setIcon(styledAttributes.getResourceId(
- R.styleable.SelectionModeDrawables_actionModeSelectAllDrawable, 0)).
- setAlphabeticShortcut('a').
- setShowAsAction(
- MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT);
-
if (mTextView.canCut()) {
menu.add(0, TextView.ID_CUT, 0, com.android.internal.R.string.cut).
setIcon(styledAttributes.getResourceId(
@@ -2990,6 +3003,13 @@
MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT);
}
+ menu.add(0, TextView.ID_SELECT_ALL, 0, com.android.internal.R.string.selectAll).
+ setIcon(styledAttributes.getResourceId(
+ R.styleable.SelectionModeDrawables_actionModeSelectAllDrawable, 0)).
+ setAlphabeticShortcut('a').
+ setShowAsAction(
+ MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT);
+
if (mTextView.isSuggestionsEnabled() && isCursorInsideSuggestionSpan()) {
menu.add(0, TextView.ID_REPLACE, 0, com.android.internal.R.string.replace).
setShowAsAction(
@@ -3057,6 +3077,40 @@
mSelectionActionMode = null;
}
+
+ @Override
+ public void onGetContentRect(ActionMode mode, View view, Rect outRect) {
+ if (!view.equals(mTextView) || mTextView.getLayout() == null) {
+ super.onGetContentRect(mode, view, outRect);
+ return;
+ }
+ if (mTextView.getSelectionStart() != mTextView.getSelectionEnd()) {
+ // We have a selection.
+ mSelectionPath.reset();
+ mTextView.getLayout().getSelectionPath(
+ mTextView.getSelectionStart(), mTextView.getSelectionEnd(), mSelectionPath);
+ mSelectionPath.computeBounds(mSelectionBounds, true);
+ mSelectionBounds.bottom += mSelectionHandleHeight;
+ } else {
+ // We have a single cursor.
+ int line = mTextView.getLayout().getLineForOffset(mTextView.getSelectionStart());
+ float primaryHorizontal =
+ mTextView.getLayout().getPrimaryHorizontal(mTextView.getSelectionStart());
+ mSelectionBounds.set(
+ primaryHorizontal,
+ mTextView.getLayout().getLineTop(line),
+ primaryHorizontal + 1,
+ mTextView.getLayout().getLineTop(line + 1) + mInsertionHandleHeight);
+ }
+ // Take TextView's padding and scroll into account.
+ int textHorizontalOffset = mTextView.viewportToContentHorizontalOffset();
+ int textVerticalOffset = mTextView.viewportToContentVerticalOffset();
+ outRect.set(
+ (int) Math.floor(mSelectionBounds.left + textHorizontalOffset),
+ (int) Math.floor(mSelectionBounds.top + textVerticalOffset),
+ (int) Math.ceil(mSelectionBounds.right + textHorizontalOffset),
+ (int) Math.ceil(mSelectionBounds.bottom + textVerticalOffset));
+ }
}
private void onReplace() {
@@ -3066,97 +3120,6 @@
showSuggestions();
}
- private class ActionPopupWindow extends PinnedPopupWindow implements OnClickListener {
- private static final int POPUP_TEXT_LAYOUT =
- com.android.internal.R.layout.text_edit_action_popup_text;
- private TextView mPasteTextView;
- private TextView mReplaceTextView;
-
- @Override
- protected void createPopupWindow() {
- mPopupWindow = new PopupWindow(mTextView.getContext(), null,
- com.android.internal.R.attr.textSelectHandleWindowStyle);
- mPopupWindow.setClippingEnabled(true);
- }
-
- @Override
- protected void initContentView() {
- LinearLayout linearLayout = new LinearLayout(mTextView.getContext());
- linearLayout.setOrientation(LinearLayout.HORIZONTAL);
- mContentView = linearLayout;
- mContentView.setBackgroundResource(
- com.android.internal.R.drawable.text_edit_paste_window);
-
- LayoutInflater inflater = (LayoutInflater) mTextView.getContext().
- getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-
- LayoutParams wrapContent = new LayoutParams(
- ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
-
- mPasteTextView = (TextView) inflater.inflate(POPUP_TEXT_LAYOUT, null);
- mPasteTextView.setLayoutParams(wrapContent);
- mContentView.addView(mPasteTextView);
- mPasteTextView.setText(com.android.internal.R.string.paste);
- mPasteTextView.setOnClickListener(this);
-
- mReplaceTextView = (TextView) inflater.inflate(POPUP_TEXT_LAYOUT, null);
- mReplaceTextView.setLayoutParams(wrapContent);
- mContentView.addView(mReplaceTextView);
- mReplaceTextView.setText(com.android.internal.R.string.replace);
- mReplaceTextView.setOnClickListener(this);
- }
-
- @Override
- public void show() {
- boolean canPaste = mTextView.canPaste();
- boolean canSuggest = mTextView.isSuggestionsEnabled() && isCursorInsideSuggestionSpan();
- mPasteTextView.setVisibility(canPaste ? View.VISIBLE : View.GONE);
- mReplaceTextView.setVisibility(canSuggest ? View.VISIBLE : View.GONE);
-
- if (!canPaste && !canSuggest) return;
-
- super.show();
- }
-
- @Override
- public void onClick(View view) {
- if (view == mPasteTextView && mTextView.canPaste()) {
- mTextView.onTextContextMenuItem(TextView.ID_PASTE);
- hide();
- } else if (view == mReplaceTextView) {
- onReplace();
- }
- }
-
- @Override
- protected int getTextOffset() {
- return (mTextView.getSelectionStart() + mTextView.getSelectionEnd()) / 2;
- }
-
- @Override
- protected int getVerticalLocalPosition(int line) {
- return mTextView.getLayout().getLineTop(line) - mContentView.getMeasuredHeight();
- }
-
- @Override
- protected int clipVertically(int positionY) {
- if (positionY < 0) {
- final int offset = getTextOffset();
- final Layout layout = mTextView.getLayout();
- final int line = layout.getLineForOffset(offset);
- positionY += layout.getLineBottom(line) - layout.getLineTop(line);
- positionY += mContentView.getMeasuredHeight();
-
- // Assumes insertion and selection handles share the same height
- final Drawable handle = mTextView.getContext().getDrawable(
- mTextView.mTextSelectHandleRes);
- positionY += handle.getIntrinsicHeight();
- }
-
- return positionY;
- }
- }
-
/**
* A listener to call {@link InputMethodManager#updateCursorAnchorInfo(View, CursorAnchorInfo)}
* while the input method is requesting the cursor/anchor position. Does nothing as long as
@@ -4102,7 +4065,6 @@
}
class SelectionModifierCursorController implements CursorController {
- private static final int DELAY_BEFORE_REPLACE_ACTION = 200; // milliseconds
// The cursor controller handles, lazily created when shown.
private SelectionStartHandleView mStartHandle;
private SelectionEndHandleView mEndHandle;
diff --git a/core/java/android/widget/RadialTimePickerView.java b/core/java/android/widget/RadialTimePickerView.java
index 20aa972..143dea4 100644
--- a/core/java/android/widget/RadialTimePickerView.java
+++ b/core/java/android/widget/RadialTimePickerView.java
@@ -122,8 +122,9 @@
private final Paint mPaintCenter = new Paint();
private final Paint[][] mPaintSelector = new Paint[2][3];
- private final int[][] mColorSelector = new int[2][3];
- private final IntHolder[][] mAlphaSelector = new IntHolder[2][3];
+
+ private final int mSelectorColor;
+ private final int mSelectorDotColor;
private final Paint mPaintBackground = new Paint();
@@ -147,6 +148,8 @@
private final RadialPickerTouchHelper mTouchHelper;
+ private final Path mSelectorPath = new Path();
+
private boolean mIs24HourMode;
private boolean mShowHours;
@@ -316,11 +319,6 @@
for (int i = 0; i < mAlpha.length; i++) {
mAlpha[i] = new IntHolder(ALPHA_OPAQUE);
}
- for (int i = 0; i < mAlphaSelector.length; i++) {
- for (int j = 0; j < mAlphaSelector[i].length; j++) {
- mAlphaSelector[i][j] = new IntHolder(ALPHA_OPAQUE);
- }
- }
mTextColor[HOURS] = a.getColorStateList(R.styleable.TimePicker_numbersTextColor);
mTextColor[HOURS_INNER] = a.getColorStateList(R.styleable.TimePicker_numbersInnerTextColor);
@@ -345,33 +343,28 @@
final int[] activatedStateSet = StateSet.get(
StateSet.VIEW_STATE_ENABLED | StateSet.VIEW_STATE_ACTIVATED);
+ mSelectorColor = selectorActivatedColor;
+ mSelectorDotColor = mTextColor[HOURS].getColorForState(activatedStateSet, 0);
+
mPaintSelector[HOURS][SELECTOR_CIRCLE] = new Paint();
mPaintSelector[HOURS][SELECTOR_CIRCLE].setAntiAlias(true);
- mColorSelector[HOURS][SELECTOR_CIRCLE] = selectorActivatedColor;
mPaintSelector[HOURS][SELECTOR_DOT] = new Paint();
mPaintSelector[HOURS][SELECTOR_DOT].setAntiAlias(true);
- mColorSelector[HOURS][SELECTOR_DOT] =
- mTextColor[HOURS].getColorForState(activatedStateSet, 0);
mPaintSelector[HOURS][SELECTOR_LINE] = new Paint();
mPaintSelector[HOURS][SELECTOR_LINE].setAntiAlias(true);
mPaintSelector[HOURS][SELECTOR_LINE].setStrokeWidth(2);
- mColorSelector[HOURS][SELECTOR_LINE] = selectorActivatedColor;
mPaintSelector[MINUTES][SELECTOR_CIRCLE] = new Paint();
mPaintSelector[MINUTES][SELECTOR_CIRCLE].setAntiAlias(true);
- mColorSelector[MINUTES][SELECTOR_CIRCLE] = selectorActivatedColor;
mPaintSelector[MINUTES][SELECTOR_DOT] = new Paint();
mPaintSelector[MINUTES][SELECTOR_DOT].setAntiAlias(true);
- mColorSelector[MINUTES][SELECTOR_DOT] =
- mTextColor[MINUTES].getColorForState(activatedStateSet, 0);
mPaintSelector[MINUTES][SELECTOR_LINE] = new Paint();
mPaintSelector[MINUTES][SELECTOR_LINE].setAntiAlias(true);
mPaintSelector[MINUTES][SELECTOR_LINE].setStrokeWidth(2);
- mColorSelector[MINUTES][SELECTOR_LINE] = selectorActivatedColor;
mPaintBackground.setColor(a.getColor(R.styleable.TimePicker_numbersBackgroundColor,
context.getColor(R.color.timepicker_default_numbers_background_color_material)));
@@ -600,8 +593,8 @@
// Initialize the hours and minutes numbers.
for (int i = 0; i < 12; i++) {
mHours12Texts[i] = String.format("%d", HOURS_NUMBERS[i]);
- mOuterHours24Texts[i] = String.format("%02d", HOURS_NUMBERS_24[i]);
- mInnerHours24Texts[i] = String.format("%d", HOURS_NUMBERS[i]);
+ mInnerHours24Texts[i] = String.format("%02d", HOURS_NUMBERS_24[i]);
+ mOuterHours24Texts[i] = String.format("%d", HOURS_NUMBERS[i]);
mMinutesTexts[i] = String.format("%02d", MINUTES_NUMBERS[i]);
}
}
@@ -612,22 +605,16 @@
mInnerTextHours = mInnerHours24Texts;
} else {
mOuterTextHours = mHours12Texts;
- mInnerTextHours = null;
+ mInnerTextHours = mHours12Texts;
}
mOuterTextMinutes = mMinutesTexts;
final int hoursAlpha = mShowHours ? ALPHA_OPAQUE : ALPHA_TRANSPARENT;
mAlpha[HOURS].setValue(hoursAlpha);
- mAlphaSelector[HOURS][SELECTOR_CIRCLE].setValue(hoursAlpha);
- mAlphaSelector[HOURS][SELECTOR_DOT].setValue(hoursAlpha);
- mAlphaSelector[HOURS][SELECTOR_LINE].setValue(hoursAlpha);
final int minutesAlpha = mShowHours ? ALPHA_TRANSPARENT : ALPHA_OPAQUE;
mAlpha[MINUTES].setValue(minutesAlpha);
- mAlphaSelector[MINUTES][SELECTOR_CIRCLE].setValue(minutesAlpha);
- mAlphaSelector[MINUTES][SELECTOR_DOT].setValue(minutesAlpha);
- mAlphaSelector[MINUTES][SELECTOR_LINE].setValue(minutesAlpha);
}
@Override
@@ -675,7 +662,7 @@
mOuterTextHours, mOuterTextX[HOURS], mOuterTextY[HOURS], mPaint[HOURS],
hoursAlpha, !mIsOnInnerCircle, mSelectionDegrees[HOURS], false);
- // Draw inner hours (12-23) for 24-hour time.
+ // Draw inner hours (13-00) for 24-hour time.
if (mIs24HourMode && mInnerTextHours != null) {
drawTextElements(canvas, mTextSize[HOURS_INNER], mTypeface, mTextColor[HOURS_INNER],
mInnerTextHours, mInnerTextX, mInnerTextY, mPaint[HOURS], hoursAlpha,
@@ -714,69 +701,61 @@
canvas.drawCircle(mXCenter, mYCenter, mCenterDotRadius, mPaintCenter);
}
+ private int applyAlpha(int argb, int alpha) {
+ final int srcAlpha = (argb >> 24) & 0xFF;
+ final int dstAlpha = (int) (srcAlpha * (alpha / 255.0) + 0.5f);
+ return (0xFFFFFF & argb) | (dstAlpha << 24);
+ }
+
private int getMultipliedAlpha(int argb, int alpha) {
return (int) (Color.alpha(argb) * (alpha / 255.0) + 0.5);
}
- private final Path mSelectorPath = new Path();
-
private void drawSelector(Canvas canvas, int index, Path selectorPath, float alphaMod) {
+ final int alpha = (int) (mAlpha[index % 2].getValue() * alphaMod + 0.5f);
+ final int color = applyAlpha(mSelectorColor, alpha);
+
// Calculate the current radius at which to place the selection circle.
- mLineLength[index] = mCircleRadius - mTextInset[index];
+ final int selRadius = mSelectorRadius;
+ final int selLength = mCircleRadius - mTextInset[index];
+ final double selAngleRad = Math.toRadians(mSelectionDegrees[index]);
+ final float selCenterX = mXCenter + selLength * (float) Math.sin(selAngleRad);
+ final float selCenterY = mYCenter - selLength * (float) Math.cos(selAngleRad);
- final double selectionRadians = Math.toRadians(mSelectionDegrees[index]);
-
- float pointX = mXCenter + (int) (mLineLength[index] * Math.sin(selectionRadians));
- float pointY = mYCenter - (int) (mLineLength[index] * Math.cos(selectionRadians));
-
- int color;
- int alpha;
- Paint paint;
-
- // Draw the selection circle
- color = mColorSelector[index % 2][SELECTOR_CIRCLE];
- alpha = (int) (mAlphaSelector[index % 2][SELECTOR_CIRCLE].getValue() * alphaMod + 0.5f);
- paint = mPaintSelector[index % 2][SELECTOR_CIRCLE];
+ // Draw the selection circle.
+ final Paint paint = mPaintSelector[index % 2][SELECTOR_CIRCLE];
paint.setColor(color);
- paint.setAlpha(getMultipliedAlpha(color, alpha));
- canvas.drawCircle(pointX, pointY, mSelectorRadius, paint);
+ canvas.drawCircle(selCenterX, selCenterY, selRadius, paint);
// If needed, set up the clip path for later.
if (selectorPath != null) {
- mSelectorPath.reset();
- mSelectorPath.addCircle(pointX, pointY, mSelectorRadius, Path.Direction.CCW);
+ selectorPath.reset();
+ selectorPath.addCircle(selCenterX, selCenterY, selRadius, Path.Direction.CCW);
}
- // Draw the dot if needed.
+ // Draw the dot if we're between two items.
final boolean shouldDrawDot = mSelectionDegrees[index] % 30 != 0;
if (shouldDrawDot) {
- // We're not on a direct tick
- color = mColorSelector[index % 2][SELECTOR_DOT];
- alpha = (int) (mAlphaSelector[index % 2][SELECTOR_DOT].getValue() * alphaMod + 0.5f);
- paint = mPaintSelector[index % 2][SELECTOR_DOT];
- paint.setColor(color);
- paint.setAlpha(getMultipliedAlpha(color, alpha));
- canvas.drawCircle(pointX, pointY, mSelectorDotRadius, paint);
+ final Paint dotPaint = mPaintSelector[index % 2][SELECTOR_DOT];
+ dotPaint.setColor(color);
+ canvas.drawCircle(selCenterX, selCenterY, mSelectorDotRadius, dotPaint);
}
// Shorten the line to only go from the edge of the center dot to the
// edge of the selection circle.
- final double sin = Math.sin(selectionRadians);
- final double cos = Math.cos(selectionRadians);
- final int lineLength = mLineLength[index] - mSelectorRadius;
+ final double sin = Math.sin(selAngleRad);
+ final double cos = Math.cos(selAngleRad);
+ final int lineLength = selLength - selRadius;
final int centerX = mXCenter + (int) (mCenterDotRadius * sin);
final int centerY = mYCenter - (int) (mCenterDotRadius * cos);
- pointX = centerX + (int) (lineLength * sin);
- pointY = centerY - (int) (lineLength * cos);
+ final float linePointX = centerX + (int) (lineLength * sin);
+ final float linePointY = centerY - (int) (lineLength * cos);
- // Draw the line
- color = mColorSelector[index % 2][SELECTOR_LINE];
- alpha = (int) (mAlphaSelector[index % 2][SELECTOR_LINE].getValue() * alphaMod + 0.5f);
- paint = mPaintSelector[index % 2][SELECTOR_LINE];
- paint.setColor(color);
- paint.setStrokeWidth(mSelectorStroke);
- paint.setAlpha(getMultipliedAlpha(color, alpha));
- canvas.drawLine(mXCenter, mYCenter, pointX, pointY, paint);
+ // Draw the line.
+ final Paint linePaint = mPaintSelector[index % 2][SELECTOR_LINE];
+ linePaint.setColor(color);
+ linePaint.setStrokeWidth(mSelectorStroke);
+ canvas.drawLine(mXCenter, mYCenter, linePointX, linePointY, linePaint);
}
private void calculatePositionsHours() {
@@ -890,21 +869,8 @@
if (mHoursToMinutesAnims.size() == 0) {
mHoursToMinutesAnims.add(getFadeOutAnimator(mAlpha[HOURS],
ALPHA_OPAQUE, ALPHA_TRANSPARENT, mInvalidateUpdateListener));
- mHoursToMinutesAnims.add(getFadeOutAnimator(mAlphaSelector[HOURS][SELECTOR_CIRCLE],
- ALPHA_OPAQUE, ALPHA_TRANSPARENT, mInvalidateUpdateListener));
- mHoursToMinutesAnims.add(getFadeOutAnimator(mAlphaSelector[HOURS][SELECTOR_DOT],
- ALPHA_OPAQUE, ALPHA_TRANSPARENT, mInvalidateUpdateListener));
- mHoursToMinutesAnims.add(getFadeOutAnimator(mAlphaSelector[HOURS][SELECTOR_LINE],
- ALPHA_OPAQUE, ALPHA_TRANSPARENT, mInvalidateUpdateListener));
-
mHoursToMinutesAnims.add(getFadeInAnimator(mAlpha[MINUTES],
ALPHA_TRANSPARENT, ALPHA_OPAQUE, mInvalidateUpdateListener));
- mHoursToMinutesAnims.add(getFadeInAnimator(mAlphaSelector[MINUTES][SELECTOR_CIRCLE],
- ALPHA_TRANSPARENT, ALPHA_OPAQUE, mInvalidateUpdateListener));
- mHoursToMinutesAnims.add(getFadeInAnimator(mAlphaSelector[MINUTES][SELECTOR_DOT],
- ALPHA_TRANSPARENT, ALPHA_OPAQUE, mInvalidateUpdateListener));
- mHoursToMinutesAnims.add(getFadeInAnimator(mAlphaSelector[MINUTES][SELECTOR_LINE],
- ALPHA_TRANSPARENT, ALPHA_OPAQUE, mInvalidateUpdateListener));
}
if (mTransition != null && mTransition.isRunning()) {
@@ -919,21 +885,8 @@
if (mMinuteToHoursAnims.size() == 0) {
mMinuteToHoursAnims.add(getFadeOutAnimator(mAlpha[MINUTES],
ALPHA_OPAQUE, ALPHA_TRANSPARENT, mInvalidateUpdateListener));
- mMinuteToHoursAnims.add(getFadeOutAnimator(mAlphaSelector[MINUTES][SELECTOR_CIRCLE],
- ALPHA_OPAQUE, ALPHA_TRANSPARENT, mInvalidateUpdateListener));
- mMinuteToHoursAnims.add(getFadeOutAnimator(mAlphaSelector[MINUTES][SELECTOR_DOT],
- ALPHA_OPAQUE, ALPHA_TRANSPARENT, mInvalidateUpdateListener));
- mMinuteToHoursAnims.add(getFadeOutAnimator(mAlphaSelector[MINUTES][SELECTOR_LINE],
- ALPHA_OPAQUE, ALPHA_TRANSPARENT, mInvalidateUpdateListener));
-
mMinuteToHoursAnims.add(getFadeInAnimator(mAlpha[HOURS],
ALPHA_TRANSPARENT, ALPHA_OPAQUE, mInvalidateUpdateListener));
- mMinuteToHoursAnims.add(getFadeInAnimator(mAlphaSelector[HOURS][SELECTOR_CIRCLE],
- ALPHA_TRANSPARENT, ALPHA_OPAQUE, mInvalidateUpdateListener));
- mMinuteToHoursAnims.add(getFadeInAnimator(mAlphaSelector[HOURS][SELECTOR_DOT],
- ALPHA_TRANSPARENT, ALPHA_OPAQUE, mInvalidateUpdateListener));
- mMinuteToHoursAnims.add(getFadeInAnimator(mAlphaSelector[HOURS][SELECTOR_LINE],
- ALPHA_TRANSPARENT, ALPHA_OPAQUE, mInvalidateUpdateListener));
}
if (mTransition != null && mTransition.isRunning()) {
diff --git a/core/java/android/widget/SimpleMonthView.java b/core/java/android/widget/SimpleMonthView.java
index 4e5a39a..3fb096c6 100644
--- a/core/java/android/widget/SimpleMonthView.java
+++ b/core/java/android/widget/SimpleMonthView.java
@@ -26,6 +26,7 @@
import android.graphics.Paint.Style;
import android.graphics.Rect;
import android.graphics.Typeface;
+import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.text.TextPaint;
import android.text.format.DateFormat;
@@ -59,6 +60,12 @@
private static final String DEFAULT_TITLE_FORMAT = "MMMMy";
private static final String DAY_OF_WEEK_FORMAT = "EEEEE";
+ /** Virtual view ID for previous button. */
+ private static final int ITEM_ID_PREV = 0x101;
+
+ /** Virtual view ID for next button. */
+ private static final int ITEM_ID_NEXT = 0x100;
+
private final TextPaint mMonthPaint = new TextPaint();
private final TextPaint mDayOfWeekPaint = new TextPaint();
private final TextPaint mDayPaint = new TextPaint();
@@ -66,13 +73,27 @@
private final Paint mDayHighlightPaint = new Paint();
private final Calendar mCalendar = Calendar.getInstance();
- private final Calendar mDayLabelCalendar = Calendar.getInstance();
+ private final Calendar mDayOfWeekLabelCalendar = Calendar.getInstance();
private final MonthViewTouchHelper mTouchHelper;
private final SimpleDateFormat mTitleFormatter;
private final SimpleDateFormat mDayOfWeekFormatter;
+ private final int mMonthHeight;
+ private final int mDayOfWeekHeight;
+ private final int mDayHeight;
+ private final int mCellWidth;
+ private final int mDaySelectorRadius;
+
+ // Next/previous drawables.
+ private final Drawable mPrevDrawable;
+ private final Drawable mNextDrawable;
+ private final Rect mPrevHitArea;
+ private final Rect mNextHitArea;
+ private final CharSequence mPrevContentDesc;
+ private final CharSequence mNextContentDesc;
+
private CharSequence mTitle;
private int mMonth;
@@ -81,12 +102,6 @@
private int mPaddedWidth;
private int mPaddedHeight;
- private final int mMonthHeight;
- private final int mDayOfWeekHeight;
- private final int mDayHeight;
- private final int mCellWidth;
- private final int mDaySelectorRadius;
-
/** The day of month for the selected day, or -1 if no day is selected. */
private int mActivatedDay = -1;
@@ -122,7 +137,10 @@
private ColorStateList mDayTextColor;
- private int mTouchedDay = -1;
+ private int mTouchedItem = -1;
+
+ private boolean mPrevEnabled;
+ private boolean mNextEnabled;
public SimpleMonthView(Context context) {
this(context, null);
@@ -146,6 +164,13 @@
mCellWidth = res.getDimensionPixelSize(R.dimen.date_picker_day_width);
mDaySelectorRadius = res.getDimensionPixelSize(R.dimen.date_picker_day_selector_radius);
+ mPrevDrawable = context.getDrawable(R.drawable.ic_chevron_left);
+ mNextDrawable = context.getDrawable(R.drawable.ic_chevron_right);
+ mPrevHitArea = mPrevDrawable != null ? new Rect() : null;
+ mNextHitArea = mNextDrawable != null ? new Rect() : null;
+ mPrevContentDesc = res.getText(R.string.date_picker_prev_month_button);
+ mNextContentDesc = res.getText(R.string.date_picker_next_month_button);
+
// Set up accessibility components.
mTouchHelper = new MonthViewTouchHelper(this);
setAccessibilityDelegate(mTouchHelper);
@@ -160,6 +185,18 @@
initPaints(res);
}
+ public void setNextEnabled(boolean enabled) {
+ mNextEnabled = enabled;
+ mTouchHelper.invalidateRoot();
+ invalidate();
+ }
+
+ public void setPrevEnabled(boolean enabled) {
+ mPrevEnabled = enabled;
+ mTouchHelper.invalidateRoot();
+ invalidate();
+ }
+
/**
* Applies the specified text appearance resource to a paint, returning the
* text color if one is set in the text appearance.
@@ -192,7 +229,16 @@
}
public void setMonthTextAppearance(int resId) {
- applyTextAppearance(mMonthPaint, resId);
+ final ColorStateList monthColor = applyTextAppearance(mMonthPaint, resId);
+ if (monthColor != null) {
+ if (mPrevDrawable != null) {
+ mPrevDrawable.setTintList(monthColor);
+ }
+ if (mNextDrawable != null) {
+ mNextDrawable.setTintList(monthColor);
+ }
+ }
+
invalidate();
}
@@ -300,23 +346,26 @@
@Override
public boolean onTouchEvent(MotionEvent event) {
+ final int x = (int) (event.getX() + 0.5f);
+ final int y = (int) (event.getY() + 0.5f);
+
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
- final int touchedDay = getDayAtLocation(event.getX(), event.getY());
- if (mTouchedDay != touchedDay) {
- mTouchedDay = touchedDay;
+ final int touchedItem = getItemAtLocation(x, y);
+ if (mTouchedItem != touchedItem) {
+ mTouchedItem = touchedItem;
invalidate();
}
break;
case MotionEvent.ACTION_UP:
- final int clickedDay = getDayAtLocation(event.getX(), event.getY());
- onDayClicked(clickedDay);
+ final int clickedItem = getItemAtLocation(x, y);
+ onItemClicked(clickedItem, true);
// Fall through.
case MotionEvent.ACTION_CANCEL:
// Reset touched day on stream end.
- mTouchedDay = -1;
+ mTouchedItem = -1;
invalidate();
break;
}
@@ -332,6 +381,7 @@
drawMonth(canvas);
drawDaysOfWeek(canvas);
drawDays(canvas);
+ drawButtons(canvas);
canvas.translate(-paddingLeft, -paddingTop);
}
@@ -347,34 +397,43 @@
}
private void drawDaysOfWeek(Canvas canvas) {
- final float cellWidthHalf = mPaddedWidth / (DAYS_IN_WEEK * 2);
+ final TextPaint p = mDayOfWeekPaint;
+ final int headerHeight = mMonthHeight;
+ final int rowHeight = mDayOfWeekHeight;
+ final int colWidth = mPaddedWidth / DAYS_IN_WEEK;
- // Vertically centered within the cell height.
- final float lineHeight = mDayOfWeekPaint.ascent() + mDayOfWeekPaint.descent();
- final float y = mMonthHeight + (mDayOfWeekHeight - lineHeight) / 2f;
+ // Text is vertically centered within the day of week height.
+ final float halfLineHeight = (p.ascent() + p.descent()) / 2f;
+ final int rowCenter = headerHeight + rowHeight / 2;
- for (int i = 0; i < DAYS_IN_WEEK; i++) {
- final int calendarDay = (i + mWeekStart) % DAYS_IN_WEEK;
- mDayLabelCalendar.set(Calendar.DAY_OF_WEEK, calendarDay);
-
- final String dayLabel = mDayOfWeekFormatter.format(mDayLabelCalendar.getTime());
- final float x = (2 * i + 1) * cellWidthHalf;
- canvas.drawText(dayLabel, x, y, mDayOfWeekPaint);
+ for (int col = 0; col < DAYS_IN_WEEK; col++) {
+ final int colCenter = colWidth * col + colWidth / 2;
+ final int dayOfWeek = (col + mWeekStart) % DAYS_IN_WEEK;
+ final String label = getDayOfWeekLabel(dayOfWeek);
+ canvas.drawText(label, colCenter, rowCenter - halfLineHeight, p);
}
}
+ private String getDayOfWeekLabel(int dayOfWeek) {
+ mDayOfWeekLabelCalendar.set(Calendar.DAY_OF_WEEK, dayOfWeek);
+ return mDayOfWeekFormatter.format(mDayOfWeekLabelCalendar.getTime());
+ }
+
/**
* Draws the month days.
*/
private void drawDays(Canvas canvas) {
- final int cellWidthHalf = mPaddedWidth / (DAYS_IN_WEEK * 2);
+ final TextPaint p = mDayPaint;
+ final int headerHeight = mMonthHeight + mDayOfWeekHeight;
+ final int rowHeight = mDayHeight;
+ final int colWidth = mPaddedWidth / DAYS_IN_WEEK;
- // Vertically centered within the cell height.
- final float halfLineHeight = (mDayPaint.ascent() + mDayPaint.descent()) / 2;
- float centerY = mMonthHeight + mDayOfWeekHeight + mDayHeight / 2f;
+ // Text is vertically centered within the row height.
+ final float halfLineHeight = (p.ascent() + p.descent()) / 2f;
+ int rowCenter = headerHeight + rowHeight / 2;
- for (int day = 1, j = findDayOffset(); day <= mDaysInMonth; day++) {
- final int x = (2 * j + 1) * cellWidthHalf;
+ for (int day = 1, col = findDayOffset(); day <= mDaysInMonth; day++) {
+ final int colCenter = colWidth * col + colWidth / 2;
int stateMask = 0;
if (day >= mEnabledDayStart && day <= mEnabledDayEnd) {
@@ -386,12 +445,12 @@
stateMask |= StateSet.VIEW_STATE_ACTIVATED;
// Adjust the circle to be centered on the row.
- canvas.drawCircle(x, centerY, mDaySelectorRadius, mDaySelectorPaint);
- } else if (mTouchedDay == day) {
+ canvas.drawCircle(colCenter, rowCenter, mDaySelectorRadius, mDaySelectorPaint);
+ } else if (mTouchedItem == day) {
stateMask |= StateSet.VIEW_STATE_PRESSED;
// Adjust the circle to be centered on the row.
- canvas.drawCircle(x, centerY, mDaySelectorRadius, mDayHighlightPaint);
+ canvas.drawCircle(colCenter, rowCenter, mDaySelectorRadius, mDayHighlightPaint);
}
final boolean isDayToday = mToday == day;
@@ -402,19 +461,29 @@
final int[] stateSet = StateSet.get(stateMask);
dayTextColor = mDayTextColor.getColorForState(stateSet, 0);
}
- mDayPaint.setColor(dayTextColor);
+ p.setColor(dayTextColor);
- canvas.drawText("" + day, x, centerY - halfLineHeight, mDayPaint);
+ canvas.drawText(Integer.toString(day), colCenter, rowCenter - halfLineHeight, p);
- j++;
+ col++;
- if (j == DAYS_IN_WEEK) {
- j = 0;
- centerY += mDayHeight;
+ if (col == DAYS_IN_WEEK) {
+ col = 0;
+ rowCenter += rowHeight;
}
}
}
+ private void drawButtons(Canvas canvas) {
+ if (mPrevEnabled && mPrevDrawable != null) {
+ mPrevDrawable.draw(canvas);
+ }
+
+ if (mNextEnabled && mNextDrawable != null) {
+ mNextDrawable.draw(canvas);
+ }
+ }
+
private static boolean isValidDayOfWeek(int day) {
return day >= Calendar.SUNDAY && day <= Calendar.SATURDAY;
}
@@ -569,8 +638,42 @@
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
- mPaddedWidth = w - getPaddingLeft() - getPaddingRight();
- mPaddedHeight = w - getPaddingTop() - getPaddingBottom();
+ final int paddedLeft = getPaddingLeft();
+ final int paddedTop = getPaddingTop();
+ final int paddedRight = w - getPaddingRight();
+ final int paddedBottom = h - getPaddingBottom();
+ mPaddedWidth = paddedRight - paddedLeft;
+ mPaddedHeight = paddedBottom - paddedTop;
+
+ final int monthHeight = mMonthHeight;
+ final int cellWidth = mPaddedWidth / DAYS_IN_WEEK;
+
+ // Vertically center the previous/next drawables within the month
+ // header, horizontally center within the day cell, then expand the
+ // hit area to ensure it's at least 48x48dp.
+ final Drawable prevDrawable = mPrevDrawable;
+ if (prevDrawable != null) {
+ final int dW = prevDrawable.getIntrinsicWidth();
+ final int dH = prevDrawable.getIntrinsicHeight();
+ final int iconTop = (monthHeight - dH) / 2;
+ final int iconLeft = (cellWidth - dW) / 2;
+
+ // Button bounds don't include padding, but hit area does.
+ prevDrawable.setBounds(iconLeft, iconTop, iconLeft + dW, iconTop + dH);
+ mPrevHitArea.set(0, 0, paddedLeft + cellWidth, paddedTop + monthHeight);
+ }
+
+ final Drawable nextDrawable = mNextDrawable;
+ if (nextDrawable != null) {
+ final int dW = nextDrawable.getIntrinsicWidth();
+ final int dH = nextDrawable.getIntrinsicHeight();
+ final int iconTop = (monthHeight - dH) / 2;
+ final int iconRight = mPaddedWidth - (cellWidth - dW) / 2;
+
+ // Button bounds don't include padding, but hit area does.
+ nextDrawable.setBounds(iconRight - dW, iconTop, iconRight, iconTop + dH);
+ mNextHitArea.set(paddedRight - cellWidth, 0, w, paddedTop + monthHeight);
+ }
// Invalidate cached accessibility information.
mTouchHelper.invalidateRoot();
@@ -585,22 +688,29 @@
}
/**
- * Calculates the day of the month at the specified touch position. Returns
- * the day of the month or -1 if the position wasn't in a valid day.
+ * Calculates the day of the month or item identifier at the specified
+ * touch position. Returns the day of the month or -1 if the position
+ * wasn't in a valid day.
*
* @param x the x position of the touch event
* @param y the y position of the touch event
- * @return the day of the month at (x, y) or -1 if the position wasn't in a
- * valid day
+ * @return the day of the month at (x, y), an item identifier, or -1 if the
+ * position wasn't in a valid day or item
*/
- private int getDayAtLocation(float x, float y) {
- final int paddedX = (int) (x - getPaddingLeft() + 0.5f);
+ private int getItemAtLocation(int x, int y) {
+ if (mNextEnabled && mNextDrawable != null && mNextHitArea.contains(x, y)) {
+ return ITEM_ID_NEXT;
+ } else if (mPrevEnabled && mPrevDrawable != null && mPrevHitArea.contains(x, y)) {
+ return ITEM_ID_PREV;
+ }
+
+ final int paddedX = x - getPaddingLeft();
if (paddedX < 0 || paddedX >= mPaddedWidth) {
return -1;
}
final int headerHeight = mMonthHeight + mDayOfWeekHeight;
- final int paddedY = (int) (y - getPaddingTop() + 0.5f);
+ final int paddedY = y - getPaddingTop();
if (paddedY < headerHeight || paddedY >= mPaddedHeight) {
return -1;
}
@@ -619,47 +729,97 @@
/**
* Calculates the bounds of the specified day.
*
- * @param day the day of the month
+ * @param id the day of the month, or an item identifier
* @param outBounds the rect to populate with bounds
*/
- private boolean getBoundsForDay(int day, Rect outBounds) {
- if (day < 1 || day > mDaysInMonth) {
+ private boolean getBoundsForItem(int id, Rect outBounds) {
+ if (mNextEnabled && id == ITEM_ID_NEXT) {
+ if (mNextDrawable != null) {
+ outBounds.set(mNextHitArea);
+ return true;
+ }
+ } else if (mPrevEnabled && id == ITEM_ID_PREV) {
+ if (mPrevDrawable != null) {
+ outBounds.set(mPrevHitArea);
+ return true;
+ }
+ }
+
+ if (id < 1 || id > mDaysInMonth) {
return false;
}
- final int index = day - 1 + findDayOffset();
- final int row = index / DAYS_IN_WEEK;
+ final int index = id - 1 + findDayOffset();
+
+ // Compute left edge.
final int col = index % DAYS_IN_WEEK;
+ final int colWidth = mPaddedWidth / DAYS_IN_WEEK;
+ final int left = getPaddingLeft() + col * colWidth;
+ // Compute top edge.
+ final int row = index / DAYS_IN_WEEK;
+ final int rowHeight = mDayHeight;
final int headerHeight = mMonthHeight + mDayOfWeekHeight;
- final int paddedY = row * mDayHeight + headerHeight;
- final int paddedX = col * mPaddedWidth;
+ final int top = getPaddingTop() + headerHeight + row * rowHeight;
- final int y = paddedY + getPaddingTop();
- final int x = paddedX + getPaddingLeft();
-
- final int cellHeight = mDayHeight;
- final int cellWidth = mPaddedWidth / DAYS_IN_WEEK;
- outBounds.set(x, y, (x + cellWidth), (y + cellHeight));
-
+ outBounds.set(left, top, left + colWidth, top + rowHeight);
return true;
}
/**
+ * Called when an item is clicked.
+ *
+ * @param id the day number or item identifier
+ */
+ private boolean onItemClicked(int id, boolean animate) {
+ return onNavigationClicked(id, animate) || onDayClicked(id);
+ }
+
+ /**
* Called when the user clicks on a day. Handles callbacks to the
* {@link OnDayClickListener} if one is set.
*
* @param day the day that was clicked
*/
- private void onDayClicked(int day) {
+ private boolean onDayClicked(int day) {
+ if (day < 0 || day > mDaysInMonth) {
+ return false;
+ }
+
if (mOnDayClickListener != null) {
- Calendar date = Calendar.getInstance();
+ final Calendar date = Calendar.getInstance();
date.set(mYear, mMonth, day);
mOnDayClickListener.onDayClick(this, date);
}
// This is a no-op if accessibility is turned off.
mTouchHelper.sendEventForVirtualView(day, AccessibilityEvent.TYPE_VIEW_CLICKED);
+ return true;
+ }
+
+ /**
+ * Called when the user clicks on a navigation button. Handles callbacks to
+ * the {@link OnDayClickListener} if one is set.
+ *
+ * @param id the item identifier
+ */
+ private boolean onNavigationClicked(int id, boolean animate) {
+ final int direction;
+ if (id == ITEM_ID_NEXT) {
+ direction = 1;
+ } else if (id == ITEM_ID_PREV) {
+ direction = -1;
+ } else {
+ return false;
+ }
+
+ if (mOnDayClickListener != null) {
+ mOnDayClickListener.onNavigationClick(this, direction, animate);
+ }
+
+ // This is a no-op if accessibility is turned off.
+ mTouchHelper.sendEventForVirtualView(id, AccessibilityEvent.TYPE_VIEW_CLICKED);
+ return true;
}
/**
@@ -678,7 +838,7 @@
@Override
protected int getVirtualViewAt(float x, float y) {
- final int day = getDayAtLocation(x, y);
+ final int day = getItemAtLocation((int) (x + 0.5f), (int) (y + 0.5f));
if (day >= 0) {
return day;
}
@@ -687,6 +847,14 @@
@Override
protected void getVisibleVirtualViews(IntArray virtualViewIds) {
+ if (mNextEnabled && mNextDrawable != null) {
+ virtualViewIds.add(ITEM_ID_PREV);
+ }
+
+ if (mPrevEnabled && mPrevDrawable != null) {
+ virtualViewIds.add(ITEM_ID_NEXT);
+ }
+
for (int day = 1; day <= mDaysInMonth; day++) {
virtualViewIds.add(day);
}
@@ -699,7 +867,7 @@
@Override
protected void onPopulateNodeForVirtualView(int virtualViewId, AccessibilityNodeInfo node) {
- final boolean hasBounds = getBoundsForDay(virtualViewId, mTempRect);
+ final boolean hasBounds = getBoundsForItem(virtualViewId, mTempRect);
if (!hasBounds) {
// The day is invalid, kill the node.
@@ -710,12 +878,14 @@
return;
}
+ node.setText(getItemText(virtualViewId));
node.setContentDescription(getItemDescription(virtualViewId));
node.setBoundsInParent(mTempRect);
node.addAction(AccessibilityAction.ACTION_CLICK);
if (virtualViewId == mActivatedDay) {
- node.setSelected(true);
+ // TODO: This should use activated once that's supported.
+ node.setChecked(true);
}
}
@@ -725,31 +895,45 @@
Bundle arguments) {
switch (action) {
case AccessibilityNodeInfo.ACTION_CLICK:
- onDayClicked(virtualViewId);
- return true;
+ return onItemClicked(virtualViewId, false);
}
return false;
}
/**
- * Generates a description for a given time object. Since this
- * description will be spoken, the components are ordered by descending
- * specificity as DAY MONTH YEAR.
+ * Generates a description for a given virtual view.
*
- * @param day The day to generate a description for
- * @return A description of the time object
+ * @param id the day or item identifier to generate a description for
+ * @return a description of the virtual view
*/
- private CharSequence getItemDescription(int day) {
- mTempCalendar.set(mYear, mMonth, day);
- final CharSequence date = DateFormat.format(DATE_FORMAT,
- mTempCalendar.getTimeInMillis());
-
- if (day == mActivatedDay) {
- return getContext().getString(R.string.item_is_selected, date);
+ private CharSequence getItemDescription(int id) {
+ if (id == ITEM_ID_NEXT) {
+ return mNextContentDesc;
+ } else if (id == ITEM_ID_PREV) {
+ return mPrevContentDesc;
+ } else if (id >= 1 && id <= mDaysInMonth) {
+ mTempCalendar.set(mYear, mMonth, id);
+ return DateFormat.format(DATE_FORMAT, mTempCalendar.getTimeInMillis());
}
- return date;
+ return "";
+ }
+
+ /**
+ * Generates displayed text for a given virtual view.
+ *
+ * @param id the day or item identifier to generate text for
+ * @return the visible text of the virtual view
+ */
+ private CharSequence getItemText(int id) {
+ if (id == ITEM_ID_NEXT || id == ITEM_ID_PREV) {
+ return null;
+ } else if (id >= 1 && id <= mDaysInMonth) {
+ return Integer.toString(id);
+ }
+
+ return null;
}
}
@@ -758,5 +942,6 @@
*/
public interface OnDayClickListener {
public void onDayClick(SimpleMonthView view, Calendar day);
+ public void onNavigationClick(SimpleMonthView view, int direction, boolean animate);
}
}
diff --git a/core/java/android/widget/TimePicker.java b/core/java/android/widget/TimePicker.java
index 944b491..986c0f8 100644
--- a/core/java/android/widget/TimePicker.java
+++ b/core/java/android/widget/TimePicker.java
@@ -16,6 +16,7 @@
package android.widget;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.Widget;
import android.content.Context;
@@ -29,18 +30,13 @@
import java.util.Locale;
/**
- * A view for selecting the time of day, in either 24 hour or AM/PM mode. The
- * hour, each minute digit, and AM/PM (if applicable) can be conrolled by
- * vertical spinners. The hour can be entered by keyboard input. Entering in two
- * digit hours can be accomplished by hitting two digits within a timeout of
- * about a second (e.g. '1' then '2' to select 12). The minutes can be entered
- * by entering single digits. Under AM/PM mode, the user can hit 'a', 'A", 'p'
- * or 'P' to pick. For a dialog using this view, see
- * {@link android.app.TimePickerDialog}.
+ * A widget for selecting the time of day, in either 24-hour or AM/PM mode.
* <p>
- * See the <a href="{@docRoot}guide/topics/ui/controls/pickers.html">Pickers</a>
- * guide.
- * </p>
+ * For a dialog using this view, see {@link android.app.TimePickerDialog}. See
+ * the <a href="{@docRoot}guide/topics/ui/controls/pickers.html">Pickers</a>
+ * guide for more information.
+ *
+ * @attr ref android.R.styleable#TimePicker_timePickerMode
*/
@Widget
public class TimePicker extends FrameLayout {
@@ -96,44 +92,105 @@
}
/**
- * Set the current hour.
+ * Sets the currently selected hour using 24-hour time.
+ *
+ * @param hour the hour to set, in the range (0-23)
+ * @see #getHour()
*/
- public void setCurrentHour(Integer currentHour) {
- mDelegate.setCurrentHour(currentHour);
+ public void setHour(int hour) {
+ mDelegate.setCurrentHour(hour);
}
/**
- * @return The current hour in the range (0-23).
+ * Returns the currently selected hour using 24-hour time.
+ *
+ * @return the currently selected hour, in the range (0-23)
+ * @see #setHour(int)
*/
+ public int getHour() {
+ return mDelegate.getCurrentHour();
+ }
+
+ /**
+ * Sets the currently selected minute..
+ *
+ * @param minute the minute to set, in the range (0-59)
+ * @see #getMinute()
+ */
+ public void setMinute(int minute) {
+ mDelegate.setCurrentMinute(minute);
+ }
+
+ /**
+ * Returns the currently selected minute.
+ *
+ * @return the currently selected minute, in the range (0-59)
+ * @see #setMinute(int)
+ */
+ public int getMinute() {
+ return mDelegate.getCurrentMinute();
+ }
+
+ /**
+ * Sets the current hour.
+ *
+ * @deprecated Use {@link #setHour(int)}
+ */
+ @Deprecated
+ public void setCurrentHour(@NonNull Integer currentHour) {
+ setHour(currentHour);
+ }
+
+ /**
+ * @return the current hour in the range (0-23)
+ * @deprecated Use {@link #getHour()}
+ */
+ @NonNull
+ @Deprecated
public Integer getCurrentHour() {
return mDelegate.getCurrentHour();
}
/**
* Set the current minute (0-59).
+ *
+ * @deprecated Use {@link #setMinute(int)}
*/
- public void setCurrentMinute(Integer currentMinute) {
+ @Deprecated
+ public void setCurrentMinute(@NonNull Integer currentMinute) {
mDelegate.setCurrentMinute(currentMinute);
}
/**
- * @return The current minute.
+ * @return the current minute
+ * @deprecated Use {@link #getMinute()}
*/
+ @NonNull
+ @Deprecated
public Integer getCurrentMinute() {
return mDelegate.getCurrentMinute();
}
/**
- * Set whether in 24 hour or AM/PM mode.
+ * Sets whether this widget displays time in 24-hour mode or 12-hour mode
+ * with an AM/PM picker.
*
- * @param is24HourView True = 24 hour mode. False = AM/PM.
+ * @param is24HourView {@code true} to display in 24-hour mode,
+ * {@code false} for 12-hour mode with AM/PM
+ * @see #is24HourView()
*/
- public void setIs24HourView(Boolean is24HourView) {
+ public void setIs24HourView(@NonNull Boolean is24HourView) {
+ if (is24HourView == null) {
+ return;
+ }
+
mDelegate.setIs24HourView(is24HourView);
}
/**
- * @return true if this is in 24 hour view else false.
+ * @return {@code true} if this widget displays time in 24-hour mode,
+ * {@code false} otherwise}
+ * @see #setIs24HourView(Boolean)
*/
public boolean is24HourView() {
return mDelegate.is24HourView();
@@ -210,13 +267,13 @@
* for the real behavior.
*/
interface TimePickerDelegate {
- void setCurrentHour(Integer currentHour);
- Integer getCurrentHour();
+ void setCurrentHour(int currentHour);
+ int getCurrentHour();
- void setCurrentMinute(Integer currentMinute);
- Integer getCurrentMinute();
+ void setCurrentMinute(int currentMinute);
+ int getCurrentMinute();
- void setIs24HourView(Boolean is24HourView);
+ void setIs24HourView(boolean is24HourView);
boolean is24HourView();
void setOnTimeChangedListener(OnTimeChangedListener onTimeChangedListener);
diff --git a/core/java/android/widget/TimePickerClockDelegate.java b/core/java/android/widget/TimePickerClockDelegate.java
index 9fdd718..c58d5cb 100644
--- a/core/java/android/widget/TimePickerClockDelegate.java
+++ b/core/java/android/widget/TimePickerClockDelegate.java
@@ -16,16 +16,20 @@
package android.widget;
+import android.annotation.Nullable;
import android.content.Context;
+import android.content.res.ColorStateList;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.TypedArray;
+import android.graphics.drawable.Drawable;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.format.DateFormat;
import android.text.format.DateUtils;
import android.util.AttributeSet;
import android.util.Log;
+import android.util.StateSet;
import android.util.TypedValue;
import android.view.HapticFeedbackConstants;
import android.view.KeyCharacterMap;
@@ -48,7 +52,6 @@
*/
class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate implements
RadialTimePickerView.OnValueSelectedListener {
-
private static final String TAG = "TimePickerClockDelegate";
// Index used by RadialPickerLayout
@@ -61,14 +64,16 @@
// Also NOT a real index, just used for keyboard mode.
private static final int ENABLE_PICKER_INDEX = 3;
+ private static final int[] ATTRS_TEXT_COLOR = new int[] {
+ com.android.internal.R.attr.textColor};
+ private static final int[] ATTRS_DISABLED_ALPHA = new int[] {
+ com.android.internal.R.attr.disabledAlpha};
+
// LayoutLib relies on these constants. Change TimePickerClockDelegate_Delegate if
// modifying these.
static final int AM = 0;
static final int PM = 1;
- private static final boolean DEFAULT_ENABLED_STATE = true;
- private boolean mIsEnabled = DEFAULT_ENABLED_STATE;
-
private static final int HOURS_IN_HALF_DAY = 12;
private final View mHeaderView;
@@ -83,8 +88,7 @@
private final String mAmText;
private final String mPmText;
- private final float mDisabledAlpha;
-
+ private boolean mIsEnabled = true;
private boolean mAllowAutoAdvance;
private int mInitialHourOfDay;
private int mInitialMinute;
@@ -134,7 +138,6 @@
final View mainView = inflater.inflate(layoutResourceId, delegator);
mHeaderView = mainView.findViewById(R.id.time_header);
- mHeaderView.setBackground(a.getDrawable(R.styleable.TimePicker_headerBackground));
// Set up hour/minute labels.
mHourView = (TextView) mainView.findViewById(R.id.hours);
@@ -147,14 +150,6 @@
mMinuteView.setAccessibilityDelegate(
new ClickActionDelegate(context, R.string.select_minutes));
- final int headerTimeTextAppearance = a.getResourceId(
- R.styleable.TimePicker_headerTimeTextAppearance, 0);
- if (headerTimeTextAppearance != 0) {
- mHourView.setTextAppearance(context, headerTimeTextAppearance);
- mSeparatorView.setTextAppearance(context, headerTimeTextAppearance);
- mMinuteView.setTextAppearance(context, headerTimeTextAppearance);
- }
-
// Now that we have text appearances out of the way, make sure the hour
// and minute views are correctly sized.
mHourView.setMinWidth(computeStableWidth(mHourView, 24));
@@ -169,20 +164,41 @@
mPmLabel.setText(amPmStrings[1]);
mPmLabel.setOnClickListener(mClickListener);
- final int headerAmPmTextAppearance = a.getResourceId(
- R.styleable.TimePicker_headerAmPmTextAppearance, 0);
- if (headerAmPmTextAppearance != 0) {
- mAmLabel.setTextAppearance(context, headerAmPmTextAppearance);
- mPmLabel.setTextAppearance(context, headerAmPmTextAppearance);
+ // For the sake of backwards compatibility, attempt to extract the text
+ // color from the header time text appearance. If it's set, we'll let
+ // that override the "real" header text color.
+ ColorStateList headerTextColor = null;
+
+ @SuppressWarnings("deprecation")
+ final int timeHeaderTextAppearance = a.getResourceId(
+ R.styleable.TimePicker_headerTimeTextAppearance, 0);
+ if (timeHeaderTextAppearance != 0) {
+ final TypedArray textAppearance = mContext.obtainStyledAttributes(null,
+ ATTRS_TEXT_COLOR, 0, timeHeaderTextAppearance);
+ final ColorStateList legacyHeaderTextColor = textAppearance.getColorStateList(0);
+ headerTextColor = applyLegacyColorFixes(legacyHeaderTextColor);
+ textAppearance.recycle();
+ }
+
+ if (headerTextColor == null) {
+ headerTextColor = a.getColorStateList(R.styleable.TimePicker_headerTextColor);
+ }
+
+ if (headerTextColor != null) {
+ mHourView.setTextColor(headerTextColor);
+ mSeparatorView.setTextColor(headerTextColor);
+ mMinuteView.setTextColor(headerTextColor);
+ mAmLabel.setTextColor(headerTextColor);
+ mPmLabel.setTextColor(headerTextColor);
+ }
+
+ // Set up header background, if available.
+ if (a.hasValueOrEmpty(R.styleable.TimePicker_headerBackground)) {
+ mHeaderView.setBackground(a.getDrawable(R.styleable.TimePicker_headerBackground));
}
a.recycle();
- // Pull disabled alpha from theme.
- final TypedValue outValue = new TypedValue();
- context.getTheme().resolveAttribute(android.R.attr.disabledAlpha, outValue, true);
- mDisabledAlpha = outValue.getFloat();
-
mRadialTimePickerView = (RadialTimePickerView) mainView.findViewById(
R.id.radial_picker);
@@ -204,6 +220,54 @@
initialize(currentHour, currentMinute, false /* 12h */, HOUR_INDEX);
}
+ /**
+ * The legacy text color might have been poorly defined. Ensures that it
+ * has an appropriate activated state, using the selected state if one
+ * exists or modifying the default text color otherwise.
+ *
+ * @param color a legacy text color, or {@code null}
+ * @return a color state list with an appropriate activated state, or
+ * {@code null} if a valid activated state could not be generated
+ */
+ @Nullable
+ private ColorStateList applyLegacyColorFixes(@Nullable ColorStateList color) {
+ if (color == null || color.hasState(R.attr.state_activated)) {
+ return color;
+ }
+
+ final int activatedColor;
+ final int defaultColor;
+ if (color.hasState(R.attr.state_selected)) {
+ activatedColor = color.getColorForState(StateSet.get(
+ StateSet.VIEW_STATE_ENABLED | StateSet.VIEW_STATE_SELECTED), 0);
+ defaultColor = color.getColorForState(StateSet.get(
+ StateSet.VIEW_STATE_ENABLED), 0);
+ } else {
+ activatedColor = color.getDefaultColor();
+
+ // Generate a non-activated color using the disabled alpha.
+ final TypedArray ta = mContext.obtainStyledAttributes(ATTRS_DISABLED_ALPHA);
+ final float disabledAlpha = ta.getFloat(0, 0.30f);
+ defaultColor = multiplyAlphaComponent(activatedColor, disabledAlpha);
+ }
+
+ if (activatedColor == 0 || defaultColor == 0) {
+ // We somehow failed to obtain the colors.
+ return null;
+ }
+
+ final int[][] stateSet = new int[][] {{ R.attr.state_activated }, {}};
+ final int[] colors = new int[] { activatedColor, defaultColor };
+ return new ColorStateList(stateSet, colors);
+ }
+
+ private int multiplyAlphaComponent(int color, float alphaMod) {
+ final int srcRgb = color & 0xFFFFFF;
+ final int srcAlpha = (color >> 24) & 0xFF;
+ final int dstAlpha = (int) (srcAlpha * alphaMod + 0.5f);
+ return srcRgb | (dstAlpha << 24);
+ }
+
private static class ClickActionDelegate extends AccessibilityDelegate {
private final AccessibilityAction mClickAction;
@@ -312,7 +376,7 @@
* Set the current hour.
*/
@Override
- public void setCurrentHour(Integer currentHour) {
+ public void setCurrentHour(int currentHour) {
if (mInitialHourOfDay == currentHour) {
return;
}
@@ -329,7 +393,7 @@
* @return The current hour in the range (0-23).
*/
@Override
- public Integer getCurrentHour() {
+ public int getCurrentHour() {
int currentHour = mRadialTimePickerView.getCurrentHour();
if (mIs24HourView) {
return currentHour;
@@ -348,7 +412,7 @@
* Set the current minute (0-59).
*/
@Override
- public void setCurrentMinute(Integer currentMinute) {
+ public void setCurrentMinute(int currentMinute) {
if (mInitialMinute == currentMinute) {
return;
}
@@ -363,7 +427,7 @@
* @return The current minute.
*/
@Override
- public Integer getCurrentMinute() {
+ public int getCurrentMinute() {
return mRadialTimePickerView.getCurrentMinute();
}
@@ -373,7 +437,7 @@
* @param is24HourView True = 24 hour mode. False = AM/PM.
*/
@Override
- public void setIs24HourView(Boolean is24HourView) {
+ public void setIs24HourView(boolean is24HourView) {
if (is24HourView == mIs24HourView) {
return;
}
@@ -604,12 +668,12 @@
private void updateAmPmLabelStates(int amOrPm) {
final boolean isAm = amOrPm == AM;
+ mAmLabel.setActivated(isAm);
mAmLabel.setChecked(isAm);
- mAmLabel.setSelected(isAm);
final boolean isPm = amOrPm == PM;
+ mPmLabel.setActivated(isPm);
mPmLabel.setChecked(isPm);
- mPmLabel.setSelected(isPm);
}
/**
@@ -769,8 +833,8 @@
}
}
- mHourView.setSelected(index == HOUR_INDEX);
- mMinuteView.setSelected(index == MINUTE_INDEX);
+ mHourView.setActivated(index == HOUR_INDEX);
+ mMinuteView.setActivated(index == MINUTE_INDEX);
}
private void setAmOrPm(int amOrPm) {
@@ -960,9 +1024,9 @@
String minuteStr = (values[1] == -1) ? mDoublePlaceholderText :
String.format(minuteFormat, values[1]).replace(' ', mPlaceholderText);
mHourView.setText(hourStr);
- mHourView.setSelected(false);
+ mHourView.setActivated(false);
mMinuteView.setText(minuteStr);
- mMinuteView.setSelected(false);
+ mMinuteView.setActivated(false);
if (!mIs24HourView) {
updateAmPmLabelStates(values[2]);
}
diff --git a/core/java/android/widget/TimePickerSpinnerDelegate.java b/core/java/android/widget/TimePickerSpinnerDelegate.java
index 513c55b..df6b0a9 100644
--- a/core/java/android/widget/TimePickerSpinnerDelegate.java
+++ b/core/java/android/widget/TimePickerSpinnerDelegate.java
@@ -279,13 +279,13 @@
}
@Override
- public void setCurrentHour(Integer currentHour) {
+ public void setCurrentHour(int currentHour) {
setCurrentHour(currentHour, true);
}
- private void setCurrentHour(Integer currentHour, boolean notifyTimeChanged) {
+ private void setCurrentHour(int currentHour, boolean notifyTimeChanged) {
// why was Integer used in the first place?
- if (currentHour == null || currentHour == getCurrentHour()) {
+ if (currentHour == getCurrentHour()) {
return;
}
if (!is24HourView()) {
@@ -310,7 +310,7 @@
}
@Override
- public Integer getCurrentHour() {
+ public int getCurrentHour() {
int currentHour = mHourSpinner.getValue();
if (is24HourView()) {
return currentHour;
@@ -322,7 +322,7 @@
}
@Override
- public void setCurrentMinute(Integer currentMinute) {
+ public void setCurrentMinute(int currentMinute) {
if (currentMinute == getCurrentMinute()) {
return;
}
@@ -331,12 +331,12 @@
}
@Override
- public Integer getCurrentMinute() {
+ public int getCurrentMinute() {
return mMinuteSpinner.getValue();
}
@Override
- public void setIs24HourView(Boolean is24HourView) {
+ public void setIs24HourView(boolean is24HourView) {
if (mIs24HourView == is24HourView) {
return;
}
diff --git a/core/java/android/widget/YearPickerView.java b/core/java/android/widget/YearPickerView.java
index 7bd502e..7182414 100644
--- a/core/java/android/widget/YearPickerView.java
+++ b/core/java/android/widget/YearPickerView.java
@@ -111,16 +111,12 @@
mAdapter.setRange(min, max);
}
- public void setYearTextAppearance(int resId) {
- mAdapter.setItemTextAppearance(resId);
- }
-
- public void setYearActivatedTextAppearance(int resId) {
- mAdapter.setItemActivatedTextAppearance(resId);
- }
-
private static class YearAdapter extends BaseAdapter {
private static final int ITEM_LAYOUT = R.layout.year_label_text_view;
+ private static final int ITEM_TEXT_APPEARANCE =
+ R.style.TextAppearance_Material_DatePicker_List_YearLabel;
+ private static final int ITEM_TEXT_ACTIVATED_APPEARANCE =
+ R.style.TextAppearance_Material_DatePicker_List_YearLabel_Activated;
private final LayoutInflater mInflater;
@@ -128,9 +124,6 @@
private int mMinYear;
private int mCount;
- private int mItemTextAppearanceResId;
- private int mItemActivatedTextAppearanceResId;
-
public YearAdapter(Context context) {
mInflater = LayoutInflater.from(context);
}
@@ -155,16 +148,6 @@
return false;
}
- public void setItemTextAppearance(int resId) {
- mItemTextAppearanceResId = resId;
- notifyDataSetChanged();
- }
-
- public void setItemActivatedTextAppearance(int resId) {
- mItemActivatedTextAppearanceResId = resId;
- notifyDataSetChanged();
- }
-
@Override
public int getCount() {
return mCount;
@@ -203,10 +186,10 @@
final boolean activated = mActivatedYear == year;
final int textAppearanceResId;
- if (activated && mItemActivatedTextAppearanceResId != 0) {
- textAppearanceResId = mItemActivatedTextAppearanceResId;
+ if (activated && ITEM_TEXT_ACTIVATED_APPEARANCE != 0) {
+ textAppearanceResId = ITEM_TEXT_ACTIVATED_APPEARANCE;
} else {
- textAppearanceResId = mItemTextAppearanceResId;
+ textAppearanceResId = ITEM_TEXT_APPEARANCE;
}
final TextView v = (TextView) convertView;
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 3ceea9d..6b35f3f 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -604,9 +604,10 @@
if ((mAlwaysUseOption || mAdapter.hasFilteredItem()) && mAdapter.mOrigResolveList != null) {
// Build a reasonable intent filter, based on what matched.
IntentFilter filter = new IntentFilter();
+ String action = intent.getAction();
- if (intent.getAction() != null) {
- filter.addAction(intent.getAction());
+ if (action != null) {
+ filter.addAction(action);
}
Set<String> categories = intent.getCategories();
if (categories != null) {
@@ -688,8 +689,30 @@
if (r.match > bestMatch) bestMatch = r.match;
}
if (alwaysCheck) {
- getPackageManager().addPreferredActivity(filter, bestMatch, set,
- intent.getComponent());
+ PackageManager pm = getPackageManager();
+
+ // Set the preferred Activity
+ pm.addPreferredActivity(filter, bestMatch, set, intent.getComponent());
+
+ // Update Domain Verification status
+ int userId = getUserId();
+ ComponentName cn = intent.getComponent();
+ String packageName = cn.getPackageName();
+ String dataScheme = (data != null) ? data.getScheme() : null;
+
+ boolean isHttpOrHttps = (dataScheme != null) &&
+ (dataScheme.equals(IntentFilter.SCHEME_HTTP) ||
+ dataScheme.equals(IntentFilter.SCHEME_HTTPS));
+
+ boolean isViewAction = (action != null) && action.equals(Intent.ACTION_VIEW);
+ boolean hasCategoryBrowsable = (categories != null) &&
+ categories.contains(Intent.CATEGORY_BROWSABLE);
+
+ if (isHttpOrHttps && isViewAction && hasCategoryBrowsable) {
+ pm.updateIntentVerificationStatus(packageName,
+ PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS,
+ userId);
+ }
} else {
try {
AppGlobals.getPackageManager().setLastChosenActivity(intent,
diff --git a/core/java/com/android/internal/backup/IBackupTransport.aidl b/core/java/com/android/internal/backup/IBackupTransport.aidl
index 6158a7b..083d6c7 100644
--- a/core/java/com/android/internal/backup/IBackupTransport.aidl
+++ b/core/java/com/android/internal/backup/IBackupTransport.aidl
@@ -238,6 +238,7 @@
long requestFullBackupTime();
int performFullBackup(in PackageInfo targetPackage, in ParcelFileDescriptor socket);
+ int checkFullBackupSize(long size);
int sendBackupData(int numBytes);
void cancelFullBackup();
diff --git a/core/java/com/android/internal/logging/EventLogTags.logtags b/core/java/com/android/internal/logging/EventLogTags.logtags
new file mode 100644
index 0000000..870d20d
--- /dev/null
+++ b/core/java/com/android/internal/logging/EventLogTags.logtags
@@ -0,0 +1,7 @@
+# See system/core/logcat/event.logtags for a description of the format of this file.
+
+option java_package com.android.internal.logging;
+
+# interaction logs
+524287 sysui_view_visibility (category|1|5),(visible|1|6)
+524288 sysui_action (category|1|5)
diff --git a/core/java/com/android/internal/logging/MetricsConstants.java b/core/java/com/android/internal/logging/MetricsConstants.java
new file mode 100644
index 0000000..e5cba84
--- /dev/null
+++ b/core/java/com/android/internal/logging/MetricsConstants.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.logging;
+
+/**
+ * Constants for mestrics logs.
+ *
+ * @hide
+ */
+public interface MetricsConstants {
+ // These constants must match those in the analytic pipeline.
+ public static final int ACCESSIBILITY = 2;
+ public static final int ACCESSIBILITY_CAPTION_PROPERTIES = 3;
+ public static final int ACCESSIBILITY_SERVICE = 4;
+ public static final int ACCESSIBILITY_TOGGLE_DALTONIZER = 5;
+ public static final int ACCESSIBILITY_TOGGLE_GLOBAL_GESTURE = 6;
+ public static final int ACCESSIBILITY_TOGGLE_SCREEN_MAGNIFICATION = 7;
+ public static final int ACCOUNT = 8;
+ public static final int ACCOUNTS_ACCOUNT_SYNC = 9;
+ public static final int ACCOUNTS_CHOOSE_ACCOUNT_ACTIVITY = 10;
+ public static final int ACCOUNTS_MANAGE_ACCOUNTS = 11;
+ public static final int APN = 12;
+ public static final int APN_EDITOR = 13;
+ public static final int APPLICATION = 16;
+ public static final int APPLICATIONS_APP_LAUNCH = 17;
+ public static final int APPLICATIONS_APP_PERMISSION = 18;
+ public static final int APPLICATIONS_APP_STORAGE = 19;
+ public static final int APPLICATIONS_INSTALLED_APP_DETAILS = 20;
+ public static final int APPLICATIONS_PROCESS_STATS_DETAIL = 21;
+ public static final int APPLICATIONS_PROCESS_STATS_MEM_DETAIL = 22;
+ public static final int APPLICATIONS_PROCESS_STATS_UI = 23;
+ public static final int APP_OPS_DETAILS = 14;
+ public static final int APP_OPS_SUMMARY = 15;
+ public static final int BLUETOOTH = 24;
+ public static final int BLUETOOTH_DEVICE_PICKER = 25;
+ public static final int BLUETOOTH_DEVICE_PROFILES = 26;
+ public static final int CHOOSE_LOCK_GENERIC = 27;
+ public static final int CHOOSE_LOCK_PASSWORD = 28;
+ public static final int CHOOSE_LOCK_PATTERN = 29;
+ public static final int CONFIRM_LOCK_PASSWORD = 30;
+ public static final int CONFIRM_LOCK_PATTERN = 31;
+ public static final int CRYPT_KEEPER = 32;
+ public static final int CRYPT_KEEPER_CONFIRM = 33;
+ public static final int DASHBOARD_SEARCH_RESULTS = 34;
+ public static final int DASHBOARD_SUMMARY = 35;
+ public static final int DATA_USAGE = 36;
+ public static final int DATA_USAGE_SUMMARY = 37;
+ public static final int DATE_TIME = 38;
+ public static final int DEVELOPMENT = 39;
+ public static final int DEVICEINFO = 40;
+ public static final int DEVICEINFO_IMEI_INFORMATION = 41;
+ public static final int DEVICEINFO_MEMORY = 42;
+ public static final int DEVICEINFO_SIM_STATUS = 43;
+ public static final int DEVICEINFO_STATUS = 44;
+ public static final int DEVICEINFO_USB = 45;
+ public static final int DISPLAY = 46;
+ public static final int DREAM = 47;
+ public static final int ENCRYPTION = 48;
+ public static final int FINGERPRINT = 49;
+ public static final int FINGERPRINT_ENROLL = 50;
+ public static final int FUELGAUGE_BATTERY_HISTORY_DETAIL = 51;
+ public static final int FUELGAUGE_BATTERY_SAVER = 52;
+ public static final int FUELGAUGE_POWER_USAGE_DETAIL = 53;
+ public static final int FUELGAUGE_POWER_USAGE_SUMMARY = 54;
+ public static final int HOME = 55;
+ public static final int ICC_LOCK = 56;
+ public static final int INPUTMETHOD_KEYBOARD = 58;
+ public static final int INPUTMETHOD_LANGUAGE = 57;
+ public static final int INPUTMETHOD_SPELL_CHECKERS = 59;
+ public static final int INPUTMETHOD_SUBTYPE_ENABLER = 60;
+ public static final int INPUTMETHOD_USER_DICTIONARY = 61;
+ public static final int INPUTMETHOD_USER_DICTIONARY_ADD_WORD = 62;
+ public static final int LOCATION = 63;
+ public static final int LOCATION_MODE = 64;
+ public static final int MAIN_SETTINGS = 1;
+ public static final int MANAGE_APPLICATIONS = 65;
+ public static final int MASTER_CLEAR = 66;
+ public static final int MASTER_CLEAR_CONFIRM = 67;
+ public static final int NET_DATA_USAGE_METERED = 68;
+ public static final int NFC_BEAM = 69;
+ public static final int NFC_PAYMENT = 70;
+ public static final int NOTIFICATION = 71;
+ public static final int NOTIFICATION_APP_NOTIFICATION = 72;
+ public static final int NOTIFICATION_OTHER_SOUND = 73;
+ public static final int NOTIFICATION_REDACTION = 74;
+ public static final int NOTIFICATION_STATION = 75;
+ public static final int NOTIFICATION_ZEN_MODE = 76;
+ public static final int OWNER_INFO = 77;
+ public static final int PRINT_JOB_SETTINGS = 78;
+ public static final int PRINT_SERVICE_SETTINGS = 79;
+ public static final int PRINT_SETTINGS = 80;
+ public static final int PRIVACY = 81;
+ public static final int PROXY_SELECTOR = 82;
+ public static final int QS_AIRPLANEMODE = 112;
+ public static final int QS_BLUETOOTH = 113;
+ public static final int QS_CAST = 114;
+ public static final int QS_CELLULAR = 115;
+ public static final int QS_COLORINVERSION = 116;
+ public static final int QS_DATAUSAGEDETAIL = 117;
+ public static final int QS_DND = 118;
+ public static final int QS_FLASHLIGHT = 119;
+ public static final int QS_HOTSPOT = 120;
+ public static final int QS_INTENT = 121;
+ public static final int QS_LOCATION = 122;
+ public static final int QS_PANEL = 111;
+ public static final int QS_ROTATIONLOCK = 123;
+ public static final int QS_USERDETAIL = 125;
+ public static final int QS_USERDETAILITE = 124;
+ public static final int QS_WIFI = 126;
+ public static final int RESET_NETWORK = 83;
+ public static final int RESET_NETWORK_CONFIRM = 84;
+ public static final int RUNNING_SERVICE_DETAILS = 85;
+ public static final int SCREEN_PINNING = 86;
+ public static final int SECURITY = 87;
+ public static final int SIM = 88;
+ public static final int TESTING = 89;
+ public static final int TETHER = 90;
+ public static final int TRUSTED_CREDENTIALS = 92;
+ public static final int TRUST_AGENT = 91;
+ public static final int TTS_ENGINE_SETTINGS = 93;
+ public static final int TTS_TEXT_TO_SPEECH = 94;
+ public static final int TYPE_UNKNOWN = 0;
+ public static final int USAGE_ACCESS = 95;
+ public static final int USER = 96;
+ public static final int USERS_APP_RESTRICTIONS = 97;
+ public static final int USER_DETAILS = 98;
+ public static final int VIEW_UNKNOWN = 0;
+ public static final int VOICE_INPUT = 99;
+ public static final int VPN = 100;
+ public static final int WALLPAPER_TYPE = 101;
+ public static final int WFD_WIFI_DISPLAY = 102;
+ public static final int WIFI = 103;
+ public static final int WIFI_ADVANCED = 104;
+ public static final int WIFI_APITEST = 107;
+ public static final int WIFI_CALLING = 105;
+ public static final int WIFI_INFO = 108;
+ public static final int WIFI_P2P = 109;
+ public static final int WIFI_SAVED_ACCESS_POINTS = 106;
+ public static final int WIRELESS = 110;
+}
diff --git a/core/java/com/android/internal/logging/MetricsLogger.java b/core/java/com/android/internal/logging/MetricsLogger.java
new file mode 100644
index 0000000..9b45e34
--- /dev/null
+++ b/core/java/com/android/internal/logging/MetricsLogger.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.logging;
+
+
+import android.content.Context;
+import android.os.Build;
+
+/**
+ * Log all the things.
+ *
+ * @hide
+ */
+public class MetricsLogger implements MetricsConstants {
+ // These constants are temporary, they should migrate to MetricsConstants.
+ public static final int APPLICATIONS_ADVANCED = 132;
+ public static final int LOCATION_SCANNING = 133;
+ public static final int MANAGE_APPLICATIONS_ALL = 134;
+ public static final int MANAGE_APPLICATIONS_NOTIFICATIONS = 135;
+
+ public static final int ACTION_WIFI_ADD_NETWORK = 136;
+ public static final int ACTION_WIFI_CONNECT = 137;
+ public static final int ACTION_WIFI_FORCE_SCAN = 138;
+ public static final int ACTION_WIFI_FORGET = 139;
+ public static final int ACTION_WIFI_OFF = 140;
+ public static final int ACTION_WIFI_ON = 141;
+
+ public static final int MANAGE_PERMISSIONS = 142;
+
+ public static void visible(Context context, int category) throws IllegalArgumentException {
+ if (Build.IS_DEBUGGABLE && category == VIEW_UNKNOWN) {
+ throw new IllegalArgumentException("Must define metric category");
+ }
+ EventLogTags.writeSysuiViewVisibility(category, 100);
+ }
+
+ public static void hidden(Context context, int category) {
+ if (Build.IS_DEBUGGABLE && category == VIEW_UNKNOWN) {
+ throw new IllegalArgumentException("Must define metric category");
+ }
+ EventLogTags.writeSysuiViewVisibility(category, 0);
+ }
+
+ public static void action(Context context, int category) {
+ if (Build.IS_DEBUGGABLE && category == VIEW_UNKNOWN) {
+ throw new IllegalArgumentException("Must define metric category");
+ }
+ EventLogTags.writeSysuiAction(category);
+ }
+}
diff --git a/core/java/com/android/internal/midi/EventScheduler.java b/core/java/com/android/internal/midi/EventScheduler.java
new file mode 100644
index 0000000..7b9a48c
--- /dev/null
+++ b/core/java/com/android/internal/midi/EventScheduler.java
@@ -0,0 +1,244 @@
+/*
+ * 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.internal.midi;
+
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+/**
+ * Store arbitrary timestamped events using a Long timestamp.
+ * Only one Thread can write into the buffer.
+ * And only one Thread can read from the buffer.
+ */
+public class EventScheduler {
+ private static final long NANOS_PER_MILLI = 1000000;
+
+ private final Object mLock = new Object();
+ private SortedMap<Long, FastEventQueue> mEventBuffer;
+ private FastEventQueue mEventPool = null;
+ private int mMaxPoolSize = 200;
+ private boolean mClosed;
+
+ public EventScheduler() {
+ mEventBuffer = new TreeMap<Long, FastEventQueue>();
+ }
+
+ // If we keep at least one node in the list then it can be atomic
+ // and non-blocking.
+ private class FastEventQueue {
+ // One thread takes from the beginning of the list.
+ volatile SchedulableEvent mFirst;
+ // A second thread returns events to the end of the list.
+ volatile SchedulableEvent mLast;
+ volatile long mEventsAdded;
+ volatile long mEventsRemoved;
+
+ FastEventQueue(SchedulableEvent event) {
+ mFirst = event;
+ mLast = mFirst;
+ mEventsAdded = 1;
+ mEventsRemoved = 0;
+ }
+
+ int size() {
+ return (int)(mEventsAdded - mEventsRemoved);
+ }
+
+ /**
+ * Do not call this unless there is more than one event
+ * in the list.
+ * @return first event in the list
+ */
+ public SchedulableEvent remove() {
+ // Take first event.
+ mEventsRemoved++;
+ SchedulableEvent event = mFirst;
+ mFirst = event.mNext;
+ return event;
+ }
+
+ /**
+ * @param event
+ */
+ public void add(SchedulableEvent event) {
+ event.mNext = null;
+ mLast.mNext = event;
+ mLast = event;
+ mEventsAdded++;
+ }
+ }
+
+ /**
+ * Base class for events that can be stored in the EventScheduler.
+ */
+ public static class SchedulableEvent {
+ private long mTimestamp;
+ private SchedulableEvent mNext = null;
+
+ /**
+ * @param timestamp
+ */
+ public SchedulableEvent(long timestamp) {
+ mTimestamp = timestamp;
+ }
+
+ /**
+ * @return timestamp
+ */
+ public long getTimestamp() {
+ return mTimestamp;
+ }
+
+ /**
+ * The timestamp should not be modified when the event is in the
+ * scheduling buffer.
+ */
+ public void setTimestamp(long timestamp) {
+ mTimestamp = timestamp;
+ }
+ }
+
+ /**
+ * Get an event from the pool.
+ * Always leave at least one event in the pool.
+ * @return event or null
+ */
+ public SchedulableEvent removeEventfromPool() {
+ SchedulableEvent event = null;
+ if (mEventPool != null && (mEventPool.size() > 1)) {
+ event = mEventPool.remove();
+ }
+ return event;
+ }
+
+ /**
+ * Return events to a pool so they can be reused.
+ *
+ * @param event
+ */
+ public void addEventToPool(SchedulableEvent event) {
+ if (mEventPool == null) {
+ mEventPool = new FastEventQueue(event);
+ // If we already have enough items in the pool then just
+ // drop the event. This prevents unbounded memory leaks.
+ } else if (mEventPool.size() < mMaxPoolSize) {
+ mEventPool.add(event);
+ }
+ }
+
+ /**
+ * Add an event to the scheduler. Events with the same time will be
+ * processed in order.
+ *
+ * @param event
+ */
+ public void add(SchedulableEvent event) {
+ synchronized (mLock) {
+ FastEventQueue list = mEventBuffer.get(event.getTimestamp());
+ if (list == null) {
+ long lowestTime = mEventBuffer.isEmpty() ? Long.MAX_VALUE
+ : mEventBuffer.firstKey();
+ list = new FastEventQueue(event);
+ mEventBuffer.put(event.getTimestamp(), list);
+ // If the event we added is earlier than the previous earliest
+ // event then notify any threads waiting for the next event.
+ if (event.getTimestamp() < lowestTime) {
+ mLock.notify();
+ }
+ } else {
+ list.add(event);
+ }
+ }
+ }
+
+ private SchedulableEvent removeNextEventLocked(long lowestTime) {
+ SchedulableEvent event;
+ FastEventQueue list = mEventBuffer.get(lowestTime);
+ // Remove list from tree if this is the last node.
+ if ((list.size() == 1)) {
+ mEventBuffer.remove(lowestTime);
+ }
+ event = list.remove();
+ return event;
+ }
+
+ /**
+ * Check to see if any scheduled events are ready to be processed.
+ *
+ * @param timestamp
+ * @return next event or null if none ready
+ */
+ public SchedulableEvent getNextEvent(long time) {
+ SchedulableEvent event = null;
+ synchronized (mLock) {
+ if (!mEventBuffer.isEmpty()) {
+ long lowestTime = mEventBuffer.firstKey();
+ // Is it time for this list to be processed?
+ if (lowestTime <= time) {
+ event = removeNextEventLocked(lowestTime);
+ }
+ }
+ }
+ // Log.i(TAG, "getNextEvent: event = " + event);
+ return event;
+ }
+
+ /**
+ * Return the next available event or wait until there is an event ready to
+ * be processed. This method assumes that the timestamps are in nanoseconds
+ * and that the current time is System.nanoTime().
+ *
+ * @return event
+ * @throws InterruptedException
+ */
+ public SchedulableEvent waitNextEvent() throws InterruptedException {
+ SchedulableEvent event = null;
+ synchronized (mLock) {
+ while (!mClosed) {
+ long millisToWait = Integer.MAX_VALUE;
+ if (!mEventBuffer.isEmpty()) {
+ long now = System.nanoTime();
+ long lowestTime = mEventBuffer.firstKey();
+ // Is it time for the earliest list to be processed?
+ if (lowestTime <= now) {
+ event = removeNextEventLocked(lowestTime);
+ break;
+ } else {
+ // Figure out how long to sleep until next event.
+ long nanosToWait = lowestTime - now;
+ // Add 1 millisecond so we don't wake up before it is
+ // ready.
+ millisToWait = 1 + (nanosToWait / NANOS_PER_MILLI);
+ // Clip 64-bit value to 32-bit max.
+ if (millisToWait > Integer.MAX_VALUE) {
+ millisToWait = Integer.MAX_VALUE;
+ }
+ }
+ }
+ mLock.wait((int) millisToWait);
+ }
+ }
+ return event;
+ }
+
+ public void close() {
+ synchronized (mLock) {
+ mClosed = true;
+ mLock.notify();
+ }
+ }
+}
diff --git a/core/java/com/android/internal/midi/MidiConstants.java b/core/java/com/android/internal/midi/MidiConstants.java
new file mode 100644
index 0000000..87552e4
--- /dev/null
+++ b/core/java/com/android/internal/midi/MidiConstants.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.midi;
+
+/**
+ * MIDI related constants and static methods.
+ */
+public class MidiConstants {
+ public static final byte STATUS_COMMAND_MASK = (byte) 0xF0;
+ public static final byte STATUS_CHANNEL_MASK = (byte) 0x0F;
+
+ // Channel voice messages.
+ public static final byte STATUS_NOTE_OFF = (byte) 0x80;
+ public static final byte STATUS_NOTE_ON = (byte) 0x90;
+ public static final byte STATUS_POLYPHONIC_AFTERTOUCH = (byte) 0xA0;
+ public static final byte STATUS_CONTROL_CHANGE = (byte) 0xB0;
+ public static final byte STATUS_PROGRAM_CHANGE = (byte) 0xC0;
+ public static final byte STATUS_CHANNEL_PRESSURE = (byte) 0xD0;
+ public static final byte STATUS_PITCH_BEND = (byte) 0xE0;
+
+ // System Common Messages.
+ public static final byte STATUS_SYSTEM_EXCLUSIVE = (byte) 0xF0;
+ public static final byte STATUS_MIDI_TIME_CODE = (byte) 0xF1;
+ public static final byte STATUS_SONG_POSITION = (byte) 0xF2;
+ public static final byte STATUS_SONG_SELECT = (byte) 0xF3;
+ public static final byte STATUS_TUNE_REQUEST = (byte) 0xF6;
+ public static final byte STATUS_END_SYSEX = (byte) 0xF7;
+
+ // System Real-Time Messages
+ public static final byte STATUS_TIMING_CLOCK = (byte) 0xF8;
+ public static final byte STATUS_START = (byte) 0xFA;
+ public static final byte STATUS_CONTINUE = (byte) 0xFB;
+ public static final byte STATUS_STOP = (byte) 0xFC;
+ public static final byte STATUS_ACTIVE_SENSING = (byte) 0xFE;
+ public static final byte STATUS_RESET = (byte) 0xFF;
+
+ /** Number of bytes in a message nc from 8c to Ec */
+ public final static int CHANNEL_BYTE_LENGTHS[] = { 3, 3, 3, 3, 2, 2, 3 };
+
+ /** Number of bytes in a message Fn from F0 to FF */
+ public final static int SYSTEM_BYTE_LENGTHS[] = { 1, 2, 3, 2, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1 };
+
+ /********************************************************************/
+
+ public static int getBytesPerMessage(int command) {
+ if ((command < 0x80) || (command > 0xFF)) {
+ return 0;
+ } else if (command >= 0xF0) {
+ return SYSTEM_BYTE_LENGTHS[command & 0x0F];
+ } else {
+ return CHANNEL_BYTE_LENGTHS[(command >> 4) - 8];
+ }
+ }
+
+ /**
+ * @param msg
+ * @param offset
+ * @param count
+ * @return true if the entire message is ActiveSensing commands
+ */
+ public static boolean isAllActiveSensing(byte[] msg, int offset,
+ int count) {
+ // Count bytes that are not active sensing.
+ int goodBytes = 0;
+ for (int i = 0; i < count; i++) {
+ byte b = msg[offset + i];
+ if (b != MidiConstants.STATUS_ACTIVE_SENSING) {
+ goodBytes++;
+ }
+ }
+ return (goodBytes == 0);
+ }
+}
diff --git a/media/java/android/media/midi/MidiDispatcher.java b/core/java/com/android/internal/midi/MidiDispatcher.java
similarity index 73%
rename from media/java/android/media/midi/MidiDispatcher.java
rename to core/java/com/android/internal/midi/MidiDispatcher.java
index 0868346..377bc68 100644
--- a/media/java/android/media/midi/MidiDispatcher.java
+++ b/core/java/com/android/internal/midi/MidiDispatcher.java
@@ -14,19 +14,20 @@
* limitations under the License.
*/
-package android.media.midi;
+package com.android.internal.midi;
+
+import android.media.midi.MidiReceiver;
+import android.media.midi.MidiSender;
import java.io.IOException;
import java.util.concurrent.CopyOnWriteArrayList;
/**
- * Utility class for dispatching MIDI data to a list of {@link MidiReceiver}s.
- * This class subclasses {@link MidiReceiver} and dispatches any data it receives
+ * Utility class for dispatching MIDI data to a list of {@link android.media.midi.MidiReceiver}s.
+ * This class subclasses {@link android.media.midi.MidiReceiver} and dispatches any data it receives
* to its receiver list. Any receivers that throw an exception upon receiving data will
* be automatically removed from the receiver list, but no IOException will be returned
- * from the dispatcher's {@link #onReceive} in that case.
- *
- * @hide
+ * from the dispatcher's {@link android.media.midi.MidiReceiver#onReceive} in that case.
*/
public final class MidiDispatcher extends MidiReceiver {
@@ -35,7 +36,7 @@
private final MidiSender mSender = new MidiSender() {
/**
- * Called to connect a {@link MidiReceiver} to the sender
+ * Called to connect a {@link android.media.midi.MidiReceiver} to the sender
*
* @param receiver the receiver to connect
*/
@@ -44,7 +45,7 @@
}
/**
- * Called to disconnect a {@link MidiReceiver} from the sender
+ * Called to disconnect a {@link android.media.midi.MidiReceiver} from the sender
*
* @param receiver the receiver to disconnect
*/
@@ -54,7 +55,7 @@
};
/**
- * Returns the number of {@link MidiReceiver}s this dispatcher contains.
+ * Returns the number of {@link android.media.midi.MidiReceiver}s this dispatcher contains.
* @return the number of receivers
*/
public int getReceiverCount() {
@@ -62,7 +63,8 @@
}
/**
- * Returns a {@link MidiSender} which is used to add and remove {@link MidiReceiver}s
+ * Returns a {@link android.media.midi.MidiSender} which is used to add and remove
+ * {@link android.media.midi.MidiReceiver}s
* to the dispatcher's receiver list.
* @return the dispatcher's MidiSender
*/
diff --git a/core/java/com/android/internal/midi/MidiEventScheduler.java b/core/java/com/android/internal/midi/MidiEventScheduler.java
new file mode 100644
index 0000000..42d70f6
--- /dev/null
+++ b/core/java/com/android/internal/midi/MidiEventScheduler.java
@@ -0,0 +1,146 @@
+/*
+ * 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.internal.midi;
+
+import android.media.midi.MidiReceiver;
+
+import java.io.IOException;
+
+/**
+ * Add MIDI Events to an EventScheduler
+ */
+public class MidiEventScheduler extends EventScheduler {
+ private static final String TAG = "MidiEventScheduler";
+ // Maintain a pool of scheduled events to reduce memory allocation.
+ // This pool increases performance by about 14%.
+ private final static int POOL_EVENT_SIZE = 16;
+
+ private final MidiReceiver[] mReceivers;
+
+ private class SchedulingReceiver extends MidiReceiver {
+ private final int mPortNumber;
+
+ public SchedulingReceiver(int portNumber) {
+ mPortNumber = portNumber;
+ }
+
+ /**
+ * Store these bytes in the EventScheduler to be delivered at the specified
+ * time.
+ */
+ @Override
+ public void onReceive(byte[] msg, int offset, int count, long timestamp)
+ throws IOException {
+ MidiEvent event = createScheduledEvent(msg, offset, count, timestamp);
+ if (event != null) {
+ event.portNumber = mPortNumber;
+ add(event);
+ }
+ }
+ }
+
+ public static class MidiEvent extends SchedulableEvent {
+ public int portNumber;
+ public int count = 0;
+ public byte[] data;
+
+ private MidiEvent(int count) {
+ super(0);
+ data = new byte[count];
+ }
+
+ private MidiEvent(byte[] msg, int offset, int count, long timestamp) {
+ super(timestamp);
+ data = new byte[count];
+ System.arraycopy(msg, offset, data, 0, count);
+ this.count = count;
+ }
+
+ @Override
+ public String toString() {
+ String text = "Event: ";
+ for (int i = 0; i < count; i++) {
+ text += data[i] + ", ";
+ }
+ return text;
+ }
+ }
+
+ public MidiEventScheduler() {
+ this(0);
+ }
+
+ public MidiEventScheduler(int portCount) {
+ mReceivers = new MidiReceiver[portCount];
+ for (int i = 0; i < portCount; i++) {
+ mReceivers[i] = new SchedulingReceiver(i);
+ }
+ }
+
+ /**
+ * Create an event that contains the message.
+ */
+ private MidiEvent createScheduledEvent(byte[] msg, int offset, int count,
+ long timestamp) {
+ MidiEvent event;
+ if (count > POOL_EVENT_SIZE) {
+ event = new MidiEvent(msg, offset, count, timestamp);
+ } else {
+ event = (MidiEvent) removeEventfromPool();
+ if (event == null) {
+ event = new MidiEvent(POOL_EVENT_SIZE);
+ }
+ System.arraycopy(msg, offset, event.data, 0, count);
+ event.count = count;
+ event.setTimestamp(timestamp);
+ }
+ return event;
+ }
+
+ /**
+ * Return events to a pool so they can be reused.
+ *
+ * @param event
+ */
+ @Override
+ public void addEventToPool(SchedulableEvent event) {
+ // Make sure the event is suitable for the pool.
+ if (event instanceof MidiEvent) {
+ MidiEvent midiEvent = (MidiEvent) event;
+ if (midiEvent.data.length == POOL_EVENT_SIZE) {
+ super.addEventToPool(event);
+ }
+ }
+ }
+
+ /**
+ * This MidiReceiver will write date to the scheduling buffer.
+ * @return the MidiReceiver
+ */
+ public MidiReceiver getReceiver() {
+ return mReceivers[0];
+ }
+
+ /**
+ * This MidiReceiver will write date to the scheduling buffer.
+ * @return the MidiReceiver
+ */
+ public MidiReceiver getReceiver(int portNumber) {
+ return mReceivers[portNumber];
+ }
+
+}
diff --git a/core/java/com/android/internal/midi/MidiFramer.java b/core/java/com/android/internal/midi/MidiFramer.java
new file mode 100644
index 0000000..53d71bb
--- /dev/null
+++ b/core/java/com/android/internal/midi/MidiFramer.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.midi;
+
+import android.media.midi.MidiReceiver;
+
+import java.io.IOException;
+
+/**
+ * Convert stream of bytes to discrete messages.
+ *
+ * Parses the incoming bytes and then posts individual messages to the receiver
+ * specified in the constructor. Short messages of 1-3 bytes will be complete.
+ * System Exclusive messages may be posted in pieces.
+ *
+ * Resolves Running Status and
+ * interleaved System Real-Time messages.
+ */
+public class MidiFramer extends MidiReceiver {
+
+ public String TAG = "MidiFramer";
+ private MidiReceiver mReceiver;
+ private byte[] mBuffer = new byte[3];
+ private int mCount;
+ private int mRunningStatus;
+ private int mNeeded;
+
+ public MidiFramer(MidiReceiver receiver) {
+ mReceiver = receiver;
+ }
+
+ public static String formatMidiData(byte[] data, int offset, int count) {
+ String text = "MIDI+" + offset + " : ";
+ for (int i = 0; i < count; i++) {
+ text += String.format("0x%02X, ", data[offset + i]);
+ }
+ return text;
+ }
+
+ /*
+ * @see android.midi.MidiReceiver#onPost(byte[], int, int, long)
+ */
+ @Override
+ public void onReceive(byte[] data, int offset, int count, long timestamp)
+ throws IOException {
+ // Log.i(TAG, formatMidiData(data, offset, count));
+ for (int i = 0; i < count; i++) {
+ int b = data[offset] & 0xFF;
+ if (b >= 0x80) { // status byte?
+ if (b < 0xF0) { // channel message?
+ mRunningStatus = (byte) b;
+ mCount = 1;
+ mNeeded = MidiConstants.getBytesPerMessage(b) - 1;
+ } else if (b < 0xF8) { // system common?
+ mBuffer[0] = (byte) b;
+ mRunningStatus = 0;
+ mCount = 1;
+ mNeeded = MidiConstants.getBytesPerMessage(b) - 1;
+ } else { // real-time?
+ // Single byte message interleaved with other data.
+ mReceiver.sendWithTimestamp(data, offset, 1, timestamp);
+ }
+ } else { // data byte
+ mBuffer[mCount++] = (byte) b;
+ if (--mNeeded == 0) {
+ if (mRunningStatus != 0) {
+ mBuffer[0] = (byte) mRunningStatus;
+ }
+ mReceiver.sendWithTimestamp(mBuffer, 0, mCount, timestamp);
+ mNeeded = MidiConstants.getBytesPerMessage(mBuffer[0]) - 1;
+ mCount = 1;
+ }
+ }
+ ++offset;
+ }
+ }
+
+}
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 2c34ded..93dc995 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -16,18 +16,14 @@
package com.android.internal.os;
-import static android.net.NetworkStats.UID_ALL;
-import static com.android.server.NetworkManagementSocketTagger.PROP_QTAGUID_ENABLED;
-
+import android.annotation.Nullable;
import android.app.ActivityManager;
import android.bluetooth.BluetoothActivityEnergyInfo;
-import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHeadset;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkStats;
-import android.net.wifi.IWifiManager;
import android.net.wifi.WifiActivityEnergyInfo;
import android.net.wifi.WifiManager;
import android.os.BadParcelableException;
@@ -42,8 +38,6 @@
import android.os.ParcelFormatException;
import android.os.Parcelable;
import android.os.Process;
-import android.os.RemoteException;
-import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.WorkSource;
@@ -65,13 +59,14 @@
import android.util.Xml;
import android.view.Display;
-import com.android.internal.annotations.GuardedBy;
import com.android.internal.net.NetworkStatsFactory;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FastPrintWriter;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.JournaledFile;
import com.android.internal.util.XmlUtils;
+import com.android.server.NetworkManagementSocketTagger;
+import libcore.util.EmptyArray;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
@@ -132,6 +127,9 @@
static final int MSG_REPORT_POWER_CHANGE = 2;
static final long DELAY_UPDATE_WAKELOCKS = 5*1000;
+ private final KernelWakelockReader mKernelWakelockReader = new KernelWakelockReader();
+ private final KernelWakelockStats mTmpWakelockStats = new KernelWakelockStats();
+
public interface BatteryCallback {
public void batteryNeedsCpuUpdate();
public void batteryPowerChanged(boolean onBattery);
@@ -160,7 +158,12 @@
}
}
+ public interface ExternalStatsSync {
+ void scheduleSync();
+ }
+
public final MyHandler mHandler;
+ private final ExternalStatsSync mExternalSync;
private BatteryCallback mCallback;
@@ -330,7 +333,7 @@
int mPhoneSignalStrengthBin = -1;
int mPhoneSignalStrengthBinRaw = -1;
- final StopwatchTimer[] mPhoneSignalStrengthsTimer =
+ final StopwatchTimer[] mPhoneSignalStrengthsTimer =
new StopwatchTimer[SignalStrength.NUM_SIGNAL_STRENGTH_BINS];
StopwatchTimer mPhoneSignalScanningTimer;
@@ -445,18 +448,17 @@
private int mLoadedNumConnectivityChange;
private int mUnpluggedNumConnectivityChange;
+ private final NetworkStats.Entry mTmpNetworkStatsEntry = new NetworkStats.Entry();
+
/*
* Holds a SamplingTimer associated with each kernel wakelock name being tracked.
*/
- private final HashMap<String, SamplingTimer> mKernelWakelockStats =
- new HashMap<String, SamplingTimer>();
+ private final HashMap<String, SamplingTimer> mKernelWakelockStats = new HashMap<>();
public Map<String, ? extends Timer> getKernelWakelockStats() {
return mKernelWakelockStats;
}
- private static int sKernelWakelockUpdateVersion = 0;
-
String mLastWakeupReason = null;
long mLastWakeupUptimeMs = 0;
private final HashMap<String, SamplingTimer> mWakeupReasonStats = new HashMap<>();
@@ -465,55 +467,12 @@
return mWakeupReasonStats;
}
- private static final int[] PROC_WAKELOCKS_FORMAT = new int[] {
- Process.PROC_TAB_TERM|Process.PROC_OUT_STRING| // 0: name
- Process.PROC_QUOTES,
- Process.PROC_TAB_TERM|Process.PROC_OUT_LONG, // 1: count
- Process.PROC_TAB_TERM,
- Process.PROC_TAB_TERM,
- Process.PROC_TAB_TERM,
- Process.PROC_TAB_TERM|Process.PROC_OUT_LONG, // 5: totalTime
- };
-
- private static final int[] WAKEUP_SOURCES_FORMAT = new int[] {
- Process.PROC_TAB_TERM|Process.PROC_OUT_STRING, // 0: name
- Process.PROC_TAB_TERM|Process.PROC_COMBINE|
- Process.PROC_OUT_LONG, // 1: count
- Process.PROC_TAB_TERM|Process.PROC_COMBINE,
- Process.PROC_TAB_TERM|Process.PROC_COMBINE,
- Process.PROC_TAB_TERM|Process.PROC_COMBINE,
- Process.PROC_TAB_TERM|Process.PROC_COMBINE,
- Process.PROC_TAB_TERM|Process.PROC_COMBINE
- |Process.PROC_OUT_LONG, // 6: totalTime
- };
-
- private final String[] mProcWakelocksName = new String[3];
- private final long[] mProcWakelocksData = new long[3];
-
- /*
- * Used as a buffer for reading in data from /proc/wakelocks before it is processed and added
- * to mKernelWakelockStats.
- */
- private final Map<String, KernelWakelockStats> mProcWakelockFileStats = new HashMap<>();
-
- private final NetworkStatsFactory mNetworkStatsFactory = new NetworkStatsFactory();
- private NetworkStats mCurMobileSnapshot = new NetworkStats(SystemClock.elapsedRealtime(), 50);
- private NetworkStats mLastMobileSnapshot = new NetworkStats(SystemClock.elapsedRealtime(), 50);
- private NetworkStats mCurWifiSnapshot = new NetworkStats(SystemClock.elapsedRealtime(), 50);
- private NetworkStats mLastWifiSnapshot = new NetworkStats(SystemClock.elapsedRealtime(), 50);
- private NetworkStats mTmpNetworkStats;
- private final NetworkStats.Entry mTmpNetworkStatsEntry = new NetworkStats.Entry();
-
- @GuardedBy("this")
- private String[] mMobileIfaces = new String[0];
- @GuardedBy("this")
- private String[] mWifiIfaces = new String[0];
-
public BatteryStatsImpl() {
mFile = null;
mCheckinFile = null;
mDailyFile = null;
mHandler = null;
+ mExternalSync = null;
clearHistoryLocked();
}
@@ -523,7 +482,7 @@
}
static class TimeBase {
- private final ArrayList<TimeBaseObs> mObservers = new ArrayList<TimeBaseObs>();
+ private final ArrayList<TimeBaseObs> mObservers = new ArrayList<>();
private long mUptime;
private long mRealtime;
@@ -1778,147 +1737,6 @@
return timer;
}
- private final Map<String, KernelWakelockStats> readKernelWakelockStats() {
-
- FileInputStream is;
- byte[] buffer = new byte[32*1024];
- int len;
- boolean wakeup_sources;
-
- try {
- try {
- is = new FileInputStream("/d/wakeup_sources");
- wakeup_sources = true;
- } catch (java.io.FileNotFoundException e) {
- try {
- is = new FileInputStream("/proc/wakelocks");
- wakeup_sources = false;
- } catch (java.io.FileNotFoundException e2) {
- return null;
- }
- }
-
- len = is.read(buffer);
- is.close();
- } catch (java.io.IOException e) {
- return null;
- }
-
- if (len > 0) {
- if (len >= buffer.length) {
- Slog.wtf(TAG, "Kernel wake locks exceeded buffer size " + buffer.length);
- }
- int i;
- for (i=0; i<len; i++) {
- if (buffer[i] == '\0') {
- len = i;
- break;
- }
- }
- }
-
- return parseProcWakelocks(buffer, len, wakeup_sources);
- }
-
- private final Map<String, KernelWakelockStats> parseProcWakelocks(
- byte[] wlBuffer, int len, boolean wakeup_sources) {
- String name;
- int count;
- long totalTime;
- int startIndex;
- int endIndex;
- int numUpdatedWlNames = 0;
-
- // Advance past the first line.
- int i;
- for (i = 0; i < len && wlBuffer[i] != '\n' && wlBuffer[i] != '\0'; i++);
- startIndex = endIndex = i + 1;
-
- synchronized(this) {
- Map<String, KernelWakelockStats> m = mProcWakelockFileStats;
-
- sKernelWakelockUpdateVersion++;
- while (endIndex < len) {
- for (endIndex=startIndex;
- endIndex < len && wlBuffer[endIndex] != '\n' && wlBuffer[endIndex] != '\0';
- endIndex++);
- endIndex++; // endIndex is an exclusive upper bound.
- // Don't go over the end of the buffer, Process.parseProcLine might
- // write to wlBuffer[endIndex]
- if (endIndex >= (len - 1) ) {
- return m;
- }
-
- String[] nameStringArray = mProcWakelocksName;
- long[] wlData = mProcWakelocksData;
- // Stomp out any bad characters since this is from a circular buffer
- // A corruption is seen sometimes that results in the vm crashing
- // This should prevent crashes and the line will probably fail to parse
- for (int j = startIndex; j < endIndex; j++) {
- if ((wlBuffer[j] & 0x80) != 0) wlBuffer[j] = (byte) '?';
- }
- boolean parsed = Process.parseProcLine(wlBuffer, startIndex, endIndex,
- wakeup_sources ? WAKEUP_SOURCES_FORMAT :
- PROC_WAKELOCKS_FORMAT,
- nameStringArray, wlData, null);
-
- name = nameStringArray[0];
- count = (int) wlData[1];
-
- if (wakeup_sources) {
- // convert milliseconds to microseconds
- totalTime = wlData[2] * 1000;
- } else {
- // convert nanoseconds to microseconds with rounding.
- totalTime = (wlData[2] + 500) / 1000;
- }
-
- if (parsed && name.length() > 0) {
- if (!m.containsKey(name)) {
- m.put(name, new KernelWakelockStats(count, totalTime,
- sKernelWakelockUpdateVersion));
- numUpdatedWlNames++;
- } else {
- KernelWakelockStats kwlStats = m.get(name);
- if (kwlStats.mVersion == sKernelWakelockUpdateVersion) {
- kwlStats.mCount += count;
- kwlStats.mTotalTime += totalTime;
- } else {
- kwlStats.mCount = count;
- kwlStats.mTotalTime = totalTime;
- kwlStats.mVersion = sKernelWakelockUpdateVersion;
- numUpdatedWlNames++;
- }
- }
- }
- startIndex = endIndex;
- }
-
- if (m.size() != numUpdatedWlNames) {
- // Don't report old data.
- Iterator<KernelWakelockStats> itr = m.values().iterator();
- while (itr.hasNext()) {
- if (itr.next().mVersion != sKernelWakelockUpdateVersion) {
- itr.remove();
- }
- }
- }
- return m;
- }
- }
-
- private class KernelWakelockStats {
- public int mCount;
- public long mTotalTime;
- public int mVersion;
-
- KernelWakelockStats(int count, long totalTime, int version) {
- mCount = count;
- mTotalTime = totalTime;
- mVersion = version;
- }
- }
-
/*
* Get the KernelWakelockTimer associated with name, and create a new one if one
* doesn't already exist.
@@ -3390,7 +3208,7 @@
mMobileRadioActivePerAppTimer.startRunningLocked(elapsedRealtime);
} else {
mMobileRadioActiveTimer.stopRunningLocked(realElapsedRealtimeMs);
- updateNetworkActivityLocked(NET_UPDATE_MOBILE, realElapsedRealtimeMs);
+ updateMobileRadioStateLocked(realElapsedRealtimeMs);
mMobileRadioActivePerAppTimer.stopRunningLocked(realElapsedRealtimeMs);
}
}
@@ -3728,6 +3546,7 @@
addHistoryRecordLocked(elapsedRealtime, uptime);
mWifiOn = true;
mWifiOnTimer.startRunningLocked(elapsedRealtime);
+ scheduleSyncExternalStatsLocked();
}
}
@@ -3741,6 +3560,7 @@
addHistoryRecordLocked(elapsedRealtime, uptime);
mWifiOn = false;
mWifiOnTimer.stopRunningLocked(elapsedRealtime);
+ scheduleSyncExternalStatsLocked();
}
}
@@ -3903,6 +3723,7 @@
int uid = mapUid(ws.get(i));
getUidStatsLocked(uid).noteWifiRunningLocked(elapsedRealtime);
}
+ scheduleSyncExternalStatsLocked();
} else {
Log.w(TAG, "noteWifiRunningLocked -- called while WIFI running");
}
@@ -3941,6 +3762,7 @@
int uid = mapUid(ws.get(i));
getUidStatsLocked(uid).noteWifiStoppedLocked(elapsedRealtime);
}
+ scheduleSyncExternalStatsLocked();
} else {
Log.w(TAG, "noteWifiStoppedLocked -- called while WIFI not running");
}
@@ -3955,6 +3777,7 @@
}
mWifiState = wifiState;
mWifiStateTimer[wifiState].startRunningLocked(elapsedRealtime);
+ scheduleSyncExternalStatsLocked();
}
}
@@ -4026,6 +3849,7 @@
addHistoryRecordLocked(elapsedRealtime, uptime);
mBluetoothOn = true;
mBluetoothOnTimer.startRunningLocked(elapsedRealtime);
+ scheduleSyncExternalStatsLocked();
}
}
@@ -4039,6 +3863,7 @@
addHistoryRecordLocked(elapsedRealtime, uptime);
mBluetoothOn = false;
mBluetoothOnTimer.stopRunningLocked(elapsedRealtime);
+ scheduleSyncExternalStatsLocked();
}
}
@@ -4065,6 +3890,7 @@
if (DEBUG_HISTORY) Slog.v(TAG, "WIFI full lock on to: "
+ Integer.toHexString(mHistoryCur.states));
addHistoryRecordLocked(elapsedRealtime, uptime);
+ scheduleSyncExternalStatsLocked();
}
mWifiFullLockNesting++;
getUidStatsLocked(uid).noteFullWifiLockAcquiredLocked(elapsedRealtime);
@@ -4080,6 +3906,7 @@
if (DEBUG_HISTORY) Slog.v(TAG, "WIFI full lock off to: "
+ Integer.toHexString(mHistoryCur.states));
addHistoryRecordLocked(elapsedRealtime, uptime);
+ scheduleSyncExternalStatsLocked();
}
getUidStatsLocked(uid).noteFullWifiLockReleasedLocked(elapsedRealtime);
}
@@ -4137,6 +3964,7 @@
if (DEBUG_HISTORY) Slog.v(TAG, "WIFI multicast on to: "
+ Integer.toHexString(mHistoryCur.states));
addHistoryRecordLocked(elapsedRealtime, uptime);
+ scheduleSyncExternalStatsLocked();
}
mWifiMulticastNesting++;
getUidStatsLocked(uid).noteWifiMulticastEnabledLocked(elapsedRealtime);
@@ -4152,6 +3980,7 @@
if (DEBUG_HISTORY) Slog.v(TAG, "WIFI multicast off to: "
+ Integer.toHexString(mHistoryCur.states));
addHistoryRecordLocked(elapsedRealtime, uptime);
+ scheduleSyncExternalStatsLocked();
}
getUidStatsLocked(uid).noteWifiMulticastDisabledLocked(elapsedRealtime);
}
@@ -4259,7 +4088,8 @@
// During device boot, qtaguid isn't enabled until after the inital
// loading of battery stats. Now that they're enabled, take our initial
// snapshot for future delta calculation.
- updateNetworkActivityLocked(NET_UPDATE_ALL, SystemClock.elapsedRealtime());
+ updateMobileRadioStateLocked(SystemClock.elapsedRealtime());
+ updateWifiStateLocked(null);
}
@Override public long getScreenOnTime(long elapsedRealtimeUs, int which) {
@@ -5949,7 +5779,7 @@
Slog.w(TAG, "File corrupt: too many excessive power entries " + N);
return false;
}
-
+
mExcessivePower = new ArrayList<ExcessivePower>();
for (int i=0; i<N; i++) {
ExcessivePower ew = new ExcessivePower();
@@ -6727,7 +6557,7 @@
}
}
- public BatteryStatsImpl(File systemDir, Handler handler) {
+ public BatteryStatsImpl(File systemDir, Handler handler, ExternalStatsSync externalSync) {
if (systemDir != null) {
mFile = new JournaledFile(new File(systemDir, "batterystats.bin"),
new File(systemDir, "batterystats.bin.tmp"));
@@ -6736,6 +6566,7 @@
}
mCheckinFile = new AtomicFile(new File(systemDir, "batterystats-checkin.bin"));
mDailyFile = new AtomicFile(new File(systemDir, "batterystats-daily.xml"));
+ mExternalSync = externalSync;
mHandler = new MyHandler(handler.getLooper());
mStartCount++;
mScreenOnTimer = new StopwatchTimer(null, -1, null, mOnBatteryTimeBase);
@@ -6808,6 +6639,7 @@
mCheckinFile = null;
mDailyFile = null;
mHandler = null;
+ mExternalSync = null;
clearHistoryLocked();
readFromParcel(p);
}
@@ -7479,21 +7311,233 @@
mDischargeScreenOffUnplugLevel = mDischargeCurrentLevel;
}
}
-
+
public void pullPendingStateUpdatesLocked() {
- updateKernelWakelocksLocked();
- updateNetworkActivityLocked(NET_UPDATE_ALL, SystemClock.elapsedRealtime());
- // TODO(adamlesinski): enable when bluedroid stops deadlocking. b/19248786
- // updateBluetoothControllerActivityLocked();
- // TODO(adamlesinski): disabled to avoid deadlock. Need to change how external
- // data is pulled/accessed from BatteryStats. b/19729960
- // updateWifiControllerActivityLocked();
if (mOnBatteryInternal) {
final boolean screenOn = mScreenState == Display.STATE_ON;
updateDischargeScreenLevelsLocked(screenOn, screenOn);
}
}
+ private String[] mMobileIfaces = EmptyArray.STRING;
+ private String[] mWifiIfaces = EmptyArray.STRING;
+
+ private final NetworkStatsFactory mNetworkStatsFactory = new NetworkStatsFactory();
+
+ private static final int NETWORK_STATS_LAST = 0;
+ private static final int NETWORK_STATS_NEXT = 1;
+ private static final int NETWORK_STATS_DELTA = 2;
+
+ private final NetworkStats[] mMobileNetworkStats = new NetworkStats[] {
+ new NetworkStats(SystemClock.elapsedRealtime(), 50),
+ new NetworkStats(SystemClock.elapsedRealtime(), 50),
+ new NetworkStats(SystemClock.elapsedRealtime(), 50)
+ };
+
+ private final NetworkStats[] mWifiNetworkStats = new NetworkStats[] {
+ new NetworkStats(SystemClock.elapsedRealtime(), 50),
+ new NetworkStats(SystemClock.elapsedRealtime(), 50),
+ new NetworkStats(SystemClock.elapsedRealtime(), 50)
+ };
+
+ /**
+ * Retrieves the delta of network stats for the given network ifaces. Uses networkStatsBuffer
+ * as a buffer of NetworkStats objects to cycle through when computing deltas.
+ */
+ private NetworkStats getNetworkStatsDeltaLocked(String[] ifaces,
+ NetworkStats[] networkStatsBuffer)
+ throws IOException {
+ if (!SystemProperties.getBoolean(NetworkManagementSocketTagger.PROP_QTAGUID_ENABLED,
+ false)) {
+ return null;
+ }
+
+ final NetworkStats stats = mNetworkStatsFactory.readNetworkStatsDetail(NetworkStats.UID_ALL,
+ ifaces, NetworkStats.TAG_NONE, networkStatsBuffer[NETWORK_STATS_NEXT]);
+ networkStatsBuffer[NETWORK_STATS_DELTA] = NetworkStats.subtract(stats,
+ networkStatsBuffer[NETWORK_STATS_LAST], null, null,
+ networkStatsBuffer[NETWORK_STATS_DELTA]);
+ networkStatsBuffer[NETWORK_STATS_NEXT] = networkStatsBuffer[NETWORK_STATS_LAST];
+ networkStatsBuffer[NETWORK_STATS_LAST] = stats;
+ return networkStatsBuffer[NETWORK_STATS_DELTA];
+ }
+
+ /**
+ * Distribute WiFi energy info and network traffic to apps.
+ * @param info The energy information from the WiFi controller.
+ */
+ public void updateWifiStateLocked(@Nullable final WifiActivityEnergyInfo info) {
+ final NetworkStats delta;
+ try {
+ delta = getNetworkStatsDeltaLocked(mWifiIfaces, mWifiNetworkStats);
+ } catch (IOException e) {
+ Slog.wtf(TAG, "Failed to get wifi network stats", e);
+ return;
+ }
+
+ if (!mOnBatteryInternal) {
+ return;
+ }
+
+ if (delta != null) {
+ final int size = delta.size();
+ for (int i = 0; i < size; i++) {
+ final NetworkStats.Entry entry = delta.getValues(i, mTmpNetworkStatsEntry);
+
+ if (DEBUG) {
+ Slog.d(TAG, "Wifi uid " + entry.uid + ": delta rx=" + entry.rxBytes
+ + " tx=" + entry.txBytes);
+ }
+
+ if (entry.rxBytes == 0 || entry.txBytes == 0) {
+ continue;
+ }
+
+ final Uid u = getUidStatsLocked(mapUid(entry.uid));
+ u.noteNetworkActivityLocked(NETWORK_WIFI_RX_DATA, entry.rxBytes,
+ entry.rxPackets);
+ u.noteNetworkActivityLocked(NETWORK_WIFI_TX_DATA, entry.txBytes,
+ entry.txPackets);
+
+ mNetworkByteActivityCounters[NETWORK_WIFI_RX_DATA].addCountLocked(
+ entry.rxBytes);
+ mNetworkByteActivityCounters[NETWORK_WIFI_TX_DATA].addCountLocked(
+ entry.txBytes);
+ mNetworkPacketActivityCounters[NETWORK_WIFI_RX_DATA].addCountLocked(
+ entry.rxPackets);
+ mNetworkPacketActivityCounters[NETWORK_WIFI_TX_DATA].addCountLocked(
+ entry.txPackets);
+ }
+ }
+
+ if (info != null) {
+ // Update WiFi controller stats.
+ mWifiActivityCounters[CONTROLLER_RX_TIME].addCountLocked(
+ info.getControllerRxTimeMillis());
+ mWifiActivityCounters[CONTROLLER_TX_TIME].addCountLocked(
+ info.getControllerTxTimeMillis());
+ mWifiActivityCounters[CONTROLLER_IDLE_TIME].addCountLocked(
+ info.getControllerIdleTimeMillis());
+ mWifiActivityCounters[CONTROLLER_ENERGY].addCountLocked(
+ info.getControllerEnergyUsed());
+ }
+ }
+
+ /**
+ * Distribute Cell radio energy info and network traffic to apps.
+ */
+ public void updateMobileRadioStateLocked(long elapsedRealtimeMs) {
+ final NetworkStats delta;
+
+ try {
+ delta = getNetworkStatsDeltaLocked(mMobileIfaces, mMobileNetworkStats);
+ } catch (IOException e) {
+ Slog.wtf(TAG, "Failed to get mobile network stats", e);
+ return;
+ }
+
+ if (delta == null || !mOnBatteryInternal) {
+ return;
+ }
+
+ long radioTime = mMobileRadioActivePerAppTimer.checkpointRunningLocked(elapsedRealtimeMs);
+ long totalPackets = delta.getTotalPackets();
+
+ final int size = delta.size();
+ for (int i = 0; i < size; i++) {
+ final NetworkStats.Entry entry = delta.getValues(i, mTmpNetworkStatsEntry);
+
+ if (entry.rxBytes == 0 || entry.txBytes == 0) continue;
+
+ final Uid u = getUidStatsLocked(mapUid(entry.uid));
+ u.noteNetworkActivityLocked(NETWORK_MOBILE_RX_DATA, entry.rxBytes,
+ entry.rxPackets);
+ u.noteNetworkActivityLocked(NETWORK_MOBILE_TX_DATA, entry.txBytes,
+ entry.txPackets);
+
+ if (radioTime > 0) {
+ // Distribute total radio active time in to this app.
+ long appPackets = entry.rxPackets + entry.txPackets;
+ long appRadioTime = (radioTime*appPackets)/totalPackets;
+ u.noteMobileRadioActiveTimeLocked(appRadioTime);
+ // Remove this app from the totals, so that we don't lose any time
+ // due to rounding.
+ radioTime -= appRadioTime;
+ totalPackets -= appPackets;
+ }
+
+ mNetworkByteActivityCounters[NETWORK_MOBILE_RX_DATA].addCountLocked(
+ entry.rxBytes);
+ mNetworkByteActivityCounters[NETWORK_MOBILE_TX_DATA].addCountLocked(
+ entry.txBytes);
+ mNetworkPacketActivityCounters[NETWORK_MOBILE_RX_DATA].addCountLocked(
+ entry.rxPackets);
+ mNetworkPacketActivityCounters[NETWORK_MOBILE_TX_DATA].addCountLocked(
+ entry.txPackets);
+ }
+
+ if (radioTime > 0) {
+ // Whoops, there is some radio time we can't blame on an app!
+ mMobileRadioActiveUnknownTime.addCountLocked(radioTime);
+ mMobileRadioActiveUnknownCount.addCountLocked(1);
+ }
+ }
+
+ /**
+ * Distribute Bluetooth energy info and network traffic to apps.
+ * @param info The energy information from the bluetooth controller.
+ */
+ public void updateBluetoothStateLocked(@Nullable final BluetoothActivityEnergyInfo info) {
+ if (info != null && mOnBatteryInternal) {
+ mBluetoothActivityCounters[CONTROLLER_RX_TIME].addCountLocked(
+ info.getControllerRxTimeMillis());
+ mBluetoothActivityCounters[CONTROLLER_TX_TIME].addCountLocked(
+ info.getControllerTxTimeMillis());
+ mBluetoothActivityCounters[CONTROLLER_IDLE_TIME].addCountLocked(
+ info.getControllerIdleTimeMillis());
+ mBluetoothActivityCounters[CONTROLLER_ENERGY].addCountLocked(
+ info.getControllerEnergyUsed());
+ }
+ }
+
+ /**
+ * Read and distribute kernel wake lock use across apps.
+ */
+ public void updateKernelWakelocksLocked() {
+ final KernelWakelockStats wakelockStats = mKernelWakelockReader.readKernelWakelockStats(
+ mTmpWakelockStats);
+ if (wakelockStats == null) {
+ // Not crashing might make board bringup easier.
+ Slog.w(TAG, "Couldn't get kernel wake lock stats");
+ return;
+ }
+
+ for (Map.Entry<String, KernelWakelockStats.Entry> ent : wakelockStats.entrySet()) {
+ String name = ent.getKey();
+ KernelWakelockStats.Entry kws = ent.getValue();
+
+ SamplingTimer kwlt = mKernelWakelockStats.get(name);
+ if (kwlt == null) {
+ kwlt = new SamplingTimer(mOnBatteryScreenOffTimeBase,
+ true /* track reported val */);
+ mKernelWakelockStats.put(name, kwlt);
+ }
+ kwlt.updateCurrentReportedCount(kws.mCount);
+ kwlt.updateCurrentReportedTotalTime(kws.mTotalTime);
+ kwlt.setUpdateVersion(kws.mVersion);
+ }
+
+ if (wakelockStats.size() != mKernelWakelockStats.size()) {
+ // Set timers to stale if they didn't appear in /proc/wakelocks this time.
+ for (Map.Entry<String, SamplingTimer> ent : mKernelWakelockStats.entrySet()) {
+ SamplingTimer st = ent.getValue();
+ if (st.getUpdateVersion() != wakelockStats.kernelWakelockVersion) {
+ st.setStale();
+ }
+ }
+ }
+ }
+
void setOnBatteryLocked(final long mSecRealtime, final long mSecUptime, final boolean onBattery,
final int oldStatus, final int level) {
boolean doWrite = false;
@@ -7647,340 +7691,132 @@
}
}
+ private void scheduleSyncExternalStatsLocked() {
+ if (mExternalSync != null) {
+ mExternalSync.scheduleSync();
+ }
+ }
+
// This should probably be exposed in the API, though it's not critical
- private static final int BATTERY_PLUGGED_NONE = 0;
+ public static final int BATTERY_PLUGGED_NONE = 0;
- public void setBatteryState(int status, int health, int plugType, int level,
+ public void setBatteryStateLocked(int status, int health, int plugType, int level,
int temp, int volt) {
- synchronized(this) {
- final boolean onBattery = plugType == BATTERY_PLUGGED_NONE;
- final long uptime = SystemClock.uptimeMillis();
- final long elapsedRealtime = SystemClock.elapsedRealtime();
- if (!mHaveBatteryLevel) {
- mHaveBatteryLevel = true;
- // We start out assuming that the device is plugged in (not
- // on battery). If our first report is now that we are indeed
- // plugged in, then twiddle our state to correctly reflect that
- // since we won't be going through the full setOnBattery().
- if (onBattery == mOnBattery) {
- if (onBattery) {
- mHistoryCur.states &= ~HistoryItem.STATE_BATTERY_PLUGGED_FLAG;
- } else {
- mHistoryCur.states |= HistoryItem.STATE_BATTERY_PLUGGED_FLAG;
- }
- }
- mHistoryCur.batteryStatus = (byte)status;
- mHistoryCur.batteryLevel = (byte)level;
- mMaxChargeStepLevel = mMinDischargeStepLevel =
- mLastChargeStepLevel = mLastDischargeStepLevel = level;
- } else if (mCurrentBatteryLevel != level || mOnBattery != onBattery) {
- recordDailyStatsIfNeededLocked(level >= 100 && onBattery);
- }
- int oldStatus = mHistoryCur.batteryStatus;
- if (onBattery) {
- mDischargeCurrentLevel = level;
- if (!mRecordingHistory) {
- mRecordingHistory = true;
- startRecordingHistory(elapsedRealtime, uptime, true);
- }
- } else if (level < 96) {
- if (!mRecordingHistory) {
- mRecordingHistory = true;
- startRecordingHistory(elapsedRealtime, uptime, true);
- }
- }
- mCurrentBatteryLevel = level;
- if (mDischargePlugLevel < 0) {
- mDischargePlugLevel = level;
- }
- if (onBattery != mOnBattery) {
- mHistoryCur.batteryLevel = (byte)level;
- mHistoryCur.batteryStatus = (byte)status;
- mHistoryCur.batteryHealth = (byte)health;
- mHistoryCur.batteryPlugType = (byte)plugType;
- mHistoryCur.batteryTemperature = (short)temp;
- mHistoryCur.batteryVoltage = (char)volt;
- setOnBatteryLocked(elapsedRealtime, uptime, onBattery, oldStatus, level);
- } else {
- boolean changed = false;
- if (mHistoryCur.batteryLevel != level) {
- mHistoryCur.batteryLevel = (byte)level;
- changed = true;
- }
- if (mHistoryCur.batteryStatus != status) {
- mHistoryCur.batteryStatus = (byte)status;
- changed = true;
- }
- if (mHistoryCur.batteryHealth != health) {
- mHistoryCur.batteryHealth = (byte)health;
- changed = true;
- }
- if (mHistoryCur.batteryPlugType != plugType) {
- mHistoryCur.batteryPlugType = (byte)plugType;
- changed = true;
- }
- if (temp >= (mHistoryCur.batteryTemperature+10)
- || temp <= (mHistoryCur.batteryTemperature-10)) {
- mHistoryCur.batteryTemperature = (short)temp;
- changed = true;
- }
- if (volt > (mHistoryCur.batteryVoltage+20)
- || volt < (mHistoryCur.batteryVoltage-20)) {
- mHistoryCur.batteryVoltage = (char)volt;
- changed = true;
- }
- if (changed) {
- addHistoryRecordLocked(elapsedRealtime, uptime);
- }
- long modeBits = (((long)mInitStepMode) << STEP_LEVEL_INITIAL_MODE_SHIFT)
- | (((long)mModStepMode) << STEP_LEVEL_MODIFIED_MODE_SHIFT)
- | (((long)(level&0xff)) << STEP_LEVEL_LEVEL_SHIFT);
+ final boolean onBattery = plugType == BATTERY_PLUGGED_NONE;
+ final long uptime = SystemClock.uptimeMillis();
+ final long elapsedRealtime = SystemClock.elapsedRealtime();
+ if (!mHaveBatteryLevel) {
+ mHaveBatteryLevel = true;
+ // We start out assuming that the device is plugged in (not
+ // on battery). If our first report is now that we are indeed
+ // plugged in, then twiddle our state to correctly reflect that
+ // since we won't be going through the full setOnBattery().
+ if (onBattery == mOnBattery) {
if (onBattery) {
- if (mLastDischargeStepLevel != level && mMinDischargeStepLevel > level) {
- mDischargeStepTracker.addLevelSteps(mLastDischargeStepLevel - level,
- modeBits, elapsedRealtime);
- mDailyDischargeStepTracker.addLevelSteps(mLastDischargeStepLevel - level,
- modeBits, elapsedRealtime);
- mLastDischargeStepLevel = level;
- mMinDischargeStepLevel = level;
- mInitStepMode = mCurStepMode;
- mModStepMode = 0;
- }
+ mHistoryCur.states &= ~HistoryItem.STATE_BATTERY_PLUGGED_FLAG;
} else {
- if (mLastChargeStepLevel != level && mMaxChargeStepLevel < level) {
- mChargeStepTracker.addLevelSteps(level - mLastChargeStepLevel,
- modeBits, elapsedRealtime);
- mDailyChargeStepTracker.addLevelSteps(level - mLastChargeStepLevel,
- modeBits, elapsedRealtime);
- mLastChargeStepLevel = level;
- mMaxChargeStepLevel = level;
- mInitStepMode = mCurStepMode;
- mModStepMode = 0;
- }
+ mHistoryCur.states |= HistoryItem.STATE_BATTERY_PLUGGED_FLAG;
}
}
- if (!onBattery && status == BatteryManager.BATTERY_STATUS_FULL) {
- // We don't record history while we are plugged in and fully charged.
- // The next time we are unplugged, history will be cleared.
- mRecordingHistory = DEBUG;
+ mHistoryCur.batteryStatus = (byte)status;
+ mHistoryCur.batteryLevel = (byte)level;
+ mMaxChargeStepLevel = mMinDischargeStepLevel =
+ mLastChargeStepLevel = mLastDischargeStepLevel = level;
+ } else if (mCurrentBatteryLevel != level || mOnBattery != onBattery) {
+ recordDailyStatsIfNeededLocked(level >= 100 && onBattery);
+ }
+ int oldStatus = mHistoryCur.batteryStatus;
+ if (onBattery) {
+ mDischargeCurrentLevel = level;
+ if (!mRecordingHistory) {
+ mRecordingHistory = true;
+ startRecordingHistory(elapsedRealtime, uptime, true);
+ }
+ } else if (level < 96) {
+ if (!mRecordingHistory) {
+ mRecordingHistory = true;
+ startRecordingHistory(elapsedRealtime, uptime, true);
}
}
- }
-
- public void updateKernelWakelocksLocked() {
- Map<String, KernelWakelockStats> m = readKernelWakelockStats();
-
- if (m == null) {
- // Not crashing might make board bringup easier.
- Slog.w(TAG, "Couldn't get kernel wake lock stats");
- return;
+ mCurrentBatteryLevel = level;
+ if (mDischargePlugLevel < 0) {
+ mDischargePlugLevel = level;
}
+ if (onBattery != mOnBattery) {
+ mHistoryCur.batteryLevel = (byte)level;
+ mHistoryCur.batteryStatus = (byte)status;
+ mHistoryCur.batteryHealth = (byte)health;
+ mHistoryCur.batteryPlugType = (byte)plugType;
+ mHistoryCur.batteryTemperature = (short)temp;
+ mHistoryCur.batteryVoltage = (char)volt;
+ setOnBatteryLocked(elapsedRealtime, uptime, onBattery, oldStatus, level);
+ } else {
+ boolean changed = false;
+ if (mHistoryCur.batteryLevel != level) {
+ mHistoryCur.batteryLevel = (byte)level;
+ changed = true;
- for (Map.Entry<String, KernelWakelockStats> ent : m.entrySet()) {
- String name = ent.getKey();
- KernelWakelockStats kws = ent.getValue();
-
- SamplingTimer kwlt = mKernelWakelockStats.get(name);
- if (kwlt == null) {
- kwlt = new SamplingTimer(mOnBatteryScreenOffTimeBase,
- true /* track reported val */);
- mKernelWakelockStats.put(name, kwlt);
+ // TODO(adamlesinski): Schedule the creation of a HistoryStepDetails record
+ // which will pull external stats.
+ scheduleSyncExternalStatsLocked();
}
- kwlt.updateCurrentReportedCount(kws.mCount);
- kwlt.updateCurrentReportedTotalTime(kws.mTotalTime);
- kwlt.setUpdateVersion(sKernelWakelockUpdateVersion);
- }
-
- if (m.size() != mKernelWakelockStats.size()) {
- // Set timers to stale if they didn't appear in /proc/wakelocks this time.
- for (Map.Entry<String, SamplingTimer> ent : mKernelWakelockStats.entrySet()) {
- SamplingTimer st = ent.getValue();
- if (st.getUpdateVersion() != sKernelWakelockUpdateVersion) {
- st.setStale();
+ if (mHistoryCur.batteryStatus != status) {
+ mHistoryCur.batteryStatus = (byte)status;
+ changed = true;
+ }
+ if (mHistoryCur.batteryHealth != health) {
+ mHistoryCur.batteryHealth = (byte)health;
+ changed = true;
+ }
+ if (mHistoryCur.batteryPlugType != plugType) {
+ mHistoryCur.batteryPlugType = (byte)plugType;
+ changed = true;
+ }
+ if (temp >= (mHistoryCur.batteryTemperature+10)
+ || temp <= (mHistoryCur.batteryTemperature-10)) {
+ mHistoryCur.batteryTemperature = (short)temp;
+ changed = true;
+ }
+ if (volt > (mHistoryCur.batteryVoltage+20)
+ || volt < (mHistoryCur.batteryVoltage-20)) {
+ mHistoryCur.batteryVoltage = (char)volt;
+ changed = true;
+ }
+ if (changed) {
+ addHistoryRecordLocked(elapsedRealtime, uptime);
+ }
+ long modeBits = (((long)mInitStepMode) << STEP_LEVEL_INITIAL_MODE_SHIFT)
+ | (((long)mModStepMode) << STEP_LEVEL_MODIFIED_MODE_SHIFT)
+ | (((long)(level&0xff)) << STEP_LEVEL_LEVEL_SHIFT);
+ if (onBattery) {
+ if (mLastDischargeStepLevel != level && mMinDischargeStepLevel > level) {
+ mDischargeStepTracker.addLevelSteps(mLastDischargeStepLevel - level,
+ modeBits, elapsedRealtime);
+ mDailyDischargeStepTracker.addLevelSteps(mLastDischargeStepLevel - level,
+ modeBits, elapsedRealtime);
+ mLastDischargeStepLevel = level;
+ mMinDischargeStepLevel = level;
+ mInitStepMode = mCurStepMode;
+ mModStepMode = 0;
+ }
+ } else {
+ if (mLastChargeStepLevel != level && mMaxChargeStepLevel < level) {
+ mChargeStepTracker.addLevelSteps(level - mLastChargeStepLevel,
+ modeBits, elapsedRealtime);
+ mDailyChargeStepTracker.addLevelSteps(level - mLastChargeStepLevel,
+ modeBits, elapsedRealtime);
+ mLastChargeStepLevel = level;
+ mMaxChargeStepLevel = level;
+ mInitStepMode = mCurStepMode;
+ mModStepMode = 0;
}
}
}
- }
-
- static final int NET_UPDATE_MOBILE = 1<<0;
- static final int NET_UPDATE_WIFI = 1<<1;
- static final int NET_UPDATE_ALL = 0xffff;
-
- private void updateNetworkActivityLocked(int which, long elapsedRealtimeMs) {
- if (!SystemProperties.getBoolean(PROP_QTAGUID_ENABLED, false)) return;
-
- if ((which&NET_UPDATE_MOBILE) != 0 && mMobileIfaces.length > 0) {
- final NetworkStats snapshot;
- final NetworkStats last = mCurMobileSnapshot;
- try {
- snapshot = mNetworkStatsFactory.readNetworkStatsDetail(UID_ALL,
- mMobileIfaces, NetworkStats.TAG_NONE, mLastMobileSnapshot);
- } catch (IOException e) {
- Log.wtf(TAG, "Failed to read mobile network stats", e);
- return;
- }
-
- mCurMobileSnapshot = snapshot;
- mLastMobileSnapshot = last;
-
- if (mOnBatteryInternal) {
- final NetworkStats delta = NetworkStats.subtract(snapshot, last,
- null, null, mTmpNetworkStats);
- mTmpNetworkStats = delta;
-
- long radioTime = mMobileRadioActivePerAppTimer.checkpointRunningLocked(
- elapsedRealtimeMs);
- long totalPackets = delta.getTotalPackets();
-
- final int size = delta.size();
- for (int i = 0; i < size; i++) {
- final NetworkStats.Entry entry = delta.getValues(i, mTmpNetworkStatsEntry);
-
- if (entry.rxBytes == 0 || entry.txBytes == 0) continue;
-
- final Uid u = getUidStatsLocked(mapUid(entry.uid));
- u.noteNetworkActivityLocked(NETWORK_MOBILE_RX_DATA, entry.rxBytes,
- entry.rxPackets);
- u.noteNetworkActivityLocked(NETWORK_MOBILE_TX_DATA, entry.txBytes,
- entry.txPackets);
-
- if (radioTime > 0) {
- // Distribute total radio active time in to this app.
- long appPackets = entry.rxPackets + entry.txPackets;
- long appRadioTime = (radioTime*appPackets)/totalPackets;
- u.noteMobileRadioActiveTimeLocked(appRadioTime);
- // Remove this app from the totals, so that we don't lose any time
- // due to rounding.
- radioTime -= appRadioTime;
- totalPackets -= appPackets;
- }
-
- mNetworkByteActivityCounters[NETWORK_MOBILE_RX_DATA].addCountLocked(
- entry.rxBytes);
- mNetworkByteActivityCounters[NETWORK_MOBILE_TX_DATA].addCountLocked(
- entry.txBytes);
- mNetworkPacketActivityCounters[NETWORK_MOBILE_RX_DATA].addCountLocked(
- entry.rxPackets);
- mNetworkPacketActivityCounters[NETWORK_MOBILE_TX_DATA].addCountLocked(
- entry.txPackets);
- }
-
- if (radioTime > 0) {
- // Whoops, there is some radio time we can't blame on an app!
- mMobileRadioActiveUnknownTime.addCountLocked(radioTime);
- mMobileRadioActiveUnknownCount.addCountLocked(1);
- }
- }
+ if (!onBattery && status == BatteryManager.BATTERY_STATUS_FULL) {
+ // We don't record history while we are plugged in and fully charged.
+ // The next time we are unplugged, history will be cleared.
+ mRecordingHistory = DEBUG;
}
-
- if ((which&NET_UPDATE_WIFI) != 0 && mWifiIfaces.length > 0) {
- final NetworkStats snapshot;
- final NetworkStats last = mCurWifiSnapshot;
- try {
- snapshot = mNetworkStatsFactory.readNetworkStatsDetail(UID_ALL,
- mWifiIfaces, NetworkStats.TAG_NONE, mLastWifiSnapshot);
- } catch (IOException e) {
- Log.wtf(TAG, "Failed to read wifi network stats", e);
- return;
- }
-
- mCurWifiSnapshot = snapshot;
- mLastWifiSnapshot = last;
-
- if (mOnBatteryInternal) {
- final NetworkStats delta = NetworkStats.subtract(snapshot, last,
- null, null, mTmpNetworkStats);
- mTmpNetworkStats = delta;
-
- final int size = delta.size();
- for (int i = 0; i < size; i++) {
- final NetworkStats.Entry entry = delta.getValues(i, mTmpNetworkStatsEntry);
-
- if (DEBUG) {
- final NetworkStats.Entry cur = snapshot.getValues(i, null);
- Slog.d(TAG, "Wifi uid " + entry.uid + ": delta rx=" + entry.rxBytes
- + " tx=" + entry.txBytes + ", cur rx=" + cur.rxBytes
- + " tx=" + cur.txBytes);
- }
-
- if (entry.rxBytes == 0 || entry.txBytes == 0) continue;
-
- final Uid u = getUidStatsLocked(mapUid(entry.uid));
- u.noteNetworkActivityLocked(NETWORK_WIFI_RX_DATA, entry.rxBytes,
- entry.rxPackets);
- u.noteNetworkActivityLocked(NETWORK_WIFI_TX_DATA, entry.txBytes,
- entry.txPackets);
-
- mNetworkByteActivityCounters[NETWORK_WIFI_RX_DATA].addCountLocked(
- entry.rxBytes);
- mNetworkByteActivityCounters[NETWORK_WIFI_TX_DATA].addCountLocked(
- entry.txBytes);
- mNetworkPacketActivityCounters[NETWORK_WIFI_RX_DATA].addCountLocked(
- entry.rxPackets);
- mNetworkPacketActivityCounters[NETWORK_WIFI_TX_DATA].addCountLocked(
- entry.txPackets);
- }
- }
- }
- }
-
- private void updateBluetoothControllerActivityLocked() {
- BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
- if (adapter == null) {
- return;
- }
-
- // We read the data even if we are not on battery. Each read clears
- // the previous data, so we must always read to make sure the
- // data is for the current interval.
- BluetoothActivityEnergyInfo info = adapter.getControllerActivityEnergyInfo(
- BluetoothAdapter.ACTIVITY_ENERGY_INFO_REFRESHED);
- if (info == null || !info.isValid() || !mOnBatteryInternal) {
- // Bad info or we are not on battery.
- return;
- }
-
- mBluetoothActivityCounters[CONTROLLER_RX_TIME].addCountLocked(
- info.getControllerRxTimeMillis());
- mBluetoothActivityCounters[CONTROLLER_TX_TIME].addCountLocked(
- info.getControllerTxTimeMillis());
- mBluetoothActivityCounters[CONTROLLER_IDLE_TIME].addCountLocked(
- info.getControllerIdleTimeMillis());
- mBluetoothActivityCounters[CONTROLLER_ENERGY].addCountLocked(
- info.getControllerEnergyUsed());
- }
-
- private void updateWifiControllerActivityLocked() {
- IWifiManager wifiManager = IWifiManager.Stub.asInterface(
- ServiceManager.getService(Context.WIFI_SERVICE));
- if (wifiManager == null) {
- return;
- }
-
- WifiActivityEnergyInfo info;
- try {
- // We read the data even if we are not on battery. Each read clears
- // the previous data, so we must always read to make sure the
- // data is for the current interval.
- info = wifiManager.reportActivityInfo();
- } catch (RemoteException e) {
- // Nothing to report, WiFi is dead.
- return;
- }
-
- if (info == null || !info.isValid() || !mOnBatteryInternal) {
- // Bad info or we are not on battery.
- return;
- }
-
- mWifiActivityCounters[CONTROLLER_RX_TIME].addCountLocked(
- info.getControllerRxTimeMillis());
- mWifiActivityCounters[CONTROLLER_TX_TIME].addCountLocked(
- info.getControllerTxTimeMillis());
- mWifiActivityCounters[CONTROLLER_IDLE_TIME].addCountLocked(
- info.getControllerIdleTimeMillis());
- mWifiActivityCounters[CONTROLLER_ENERGY].addCountLocked(
- info.getControllerEnergyUsed());
}
public long getAwakeTimeBattery() {
diff --git a/core/java/com/android/internal/os/KernelWakelockReader.java b/core/java/com/android/internal/os/KernelWakelockReader.java
new file mode 100644
index 0000000..768d586
--- /dev/null
+++ b/core/java/com/android/internal/os/KernelWakelockReader.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.os;
+
+import android.os.Process;
+import android.util.Slog;
+
+import java.io.FileInputStream;
+import java.util.Iterator;
+
+/**
+ * Reads and parses wakelock stats from the kernel (/proc/wakelocks).
+ */
+public class KernelWakelockReader {
+ private static final String TAG = "KernelWakelockReader";
+ private static int sKernelWakelockUpdateVersion = 0;
+ private static final String sWakelockFile = "/proc/wakelocks";
+ private static final String sWakeupSourceFile = "/d/wakeup_sources";
+
+ private static final int[] PROC_WAKELOCKS_FORMAT = new int[] {
+ Process.PROC_TAB_TERM|Process.PROC_OUT_STRING| // 0: name
+ Process.PROC_QUOTES,
+ Process.PROC_TAB_TERM|Process.PROC_OUT_LONG, // 1: count
+ Process.PROC_TAB_TERM,
+ Process.PROC_TAB_TERM,
+ Process.PROC_TAB_TERM,
+ Process.PROC_TAB_TERM|Process.PROC_OUT_LONG, // 5: totalTime
+ };
+
+ private static final int[] WAKEUP_SOURCES_FORMAT = new int[] {
+ Process.PROC_TAB_TERM|Process.PROC_OUT_STRING, // 0: name
+ Process.PROC_TAB_TERM|Process.PROC_COMBINE|
+ Process.PROC_OUT_LONG, // 1: count
+ Process.PROC_TAB_TERM|Process.PROC_COMBINE,
+ Process.PROC_TAB_TERM|Process.PROC_COMBINE,
+ Process.PROC_TAB_TERM|Process.PROC_COMBINE,
+ Process.PROC_TAB_TERM|Process.PROC_COMBINE,
+ Process.PROC_TAB_TERM|Process.PROC_COMBINE
+ |Process.PROC_OUT_LONG, // 6: totalTime
+ };
+
+ private final String[] mProcWakelocksName = new String[3];
+ private final long[] mProcWakelocksData = new long[3];
+
+ /**
+ * Reads kernel wakelock stats and updates the staleStats with the new information.
+ * @param staleStats Existing object to update.
+ * @return the updated data.
+ */
+ public final KernelWakelockStats readKernelWakelockStats(KernelWakelockStats staleStats) {
+ byte[] buffer = new byte[32*1024];
+ int len;
+ boolean wakeup_sources;
+
+ try {
+ FileInputStream is;
+ try {
+ is = new FileInputStream(sWakeupSourceFile);
+ wakeup_sources = true;
+ } catch (java.io.FileNotFoundException e) {
+ try {
+ is = new FileInputStream(sWakelockFile);
+ wakeup_sources = false;
+ } catch (java.io.FileNotFoundException e2) {
+ return null;
+ }
+ }
+
+ len = is.read(buffer);
+ is.close();
+ } catch (java.io.IOException e) {
+ return null;
+ }
+
+ if (len > 0) {
+ if (len >= buffer.length) {
+ Slog.wtf(TAG, "Kernel wake locks exceeded buffer size " + buffer.length);
+ }
+ int i;
+ for (i=0; i<len; i++) {
+ if (buffer[i] == '\0') {
+ len = i;
+ break;
+ }
+ }
+ }
+ return parseProcWakelocks(buffer, len, wakeup_sources, staleStats);
+ }
+
+ /**
+ * Reads the wakelocks and updates the staleStats with the new information.
+ */
+ private KernelWakelockStats parseProcWakelocks(byte[] wlBuffer, int len, boolean wakeup_sources,
+ final KernelWakelockStats staleStats) {
+ String name;
+ int count;
+ long totalTime;
+ int startIndex;
+ int endIndex;
+ int numUpdatedWlNames = 0;
+
+ // Advance past the first line.
+ int i;
+ for (i = 0; i < len && wlBuffer[i] != '\n' && wlBuffer[i] != '\0'; i++);
+ startIndex = endIndex = i + 1;
+
+ synchronized(this) {
+ sKernelWakelockUpdateVersion++;
+ while (endIndex < len) {
+ for (endIndex=startIndex;
+ endIndex < len && wlBuffer[endIndex] != '\n' && wlBuffer[endIndex] != '\0';
+ endIndex++);
+ endIndex++; // endIndex is an exclusive upper bound.
+ // Don't go over the end of the buffer, Process.parseProcLine might
+ // write to wlBuffer[endIndex]
+ if (endIndex >= (len - 1) ) {
+ return staleStats;
+ }
+
+ String[] nameStringArray = mProcWakelocksName;
+ long[] wlData = mProcWakelocksData;
+ // Stomp out any bad characters since this is from a circular buffer
+ // A corruption is seen sometimes that results in the vm crashing
+ // This should prevent crashes and the line will probably fail to parse
+ for (int j = startIndex; j < endIndex; j++) {
+ if ((wlBuffer[j] & 0x80) != 0) wlBuffer[j] = (byte) '?';
+ }
+ boolean parsed = Process.parseProcLine(wlBuffer, startIndex, endIndex,
+ wakeup_sources ? WAKEUP_SOURCES_FORMAT :
+ PROC_WAKELOCKS_FORMAT,
+ nameStringArray, wlData, null);
+
+ name = nameStringArray[0];
+ count = (int) wlData[1];
+
+ if (wakeup_sources) {
+ // convert milliseconds to microseconds
+ totalTime = wlData[2] * 1000;
+ } else {
+ // convert nanoseconds to microseconds with rounding.
+ totalTime = (wlData[2] + 500) / 1000;
+ }
+
+ if (parsed && name.length() > 0) {
+ if (!staleStats.containsKey(name)) {
+ staleStats.put(name, new KernelWakelockStats.Entry(count, totalTime,
+ sKernelWakelockUpdateVersion));
+ numUpdatedWlNames++;
+ } else {
+ KernelWakelockStats.Entry kwlStats = staleStats.get(name);
+ if (kwlStats.mVersion == sKernelWakelockUpdateVersion) {
+ kwlStats.mCount += count;
+ kwlStats.mTotalTime += totalTime;
+ } else {
+ kwlStats.mCount = count;
+ kwlStats.mTotalTime = totalTime;
+ kwlStats.mVersion = sKernelWakelockUpdateVersion;
+ numUpdatedWlNames++;
+ }
+ }
+ }
+ startIndex = endIndex;
+ }
+
+ if (staleStats.size() != numUpdatedWlNames) {
+ // Don't report old data.
+ Iterator<KernelWakelockStats.Entry> itr = staleStats.values().iterator();
+ while (itr.hasNext()) {
+ if (itr.next().mVersion != sKernelWakelockUpdateVersion) {
+ itr.remove();
+ }
+ }
+ }
+
+ staleStats.kernelWakelockVersion = sKernelWakelockUpdateVersion;
+ return staleStats;
+ }
+ }
+}
diff --git a/core/java/com/android/internal/os/KernelWakelockStats.java b/core/java/com/android/internal/os/KernelWakelockStats.java
new file mode 100644
index 0000000..144ea00
--- /dev/null
+++ b/core/java/com/android/internal/os/KernelWakelockStats.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.os;
+
+import java.util.HashMap;
+
+/**
+ * Kernel wakelock stats object.
+ */
+public class KernelWakelockStats extends HashMap<String, KernelWakelockStats.Entry> {
+ public static class Entry {
+ public int mCount;
+ public long mTotalTime;
+ public int mVersion;
+
+ Entry(int count, long totalTime, int version) {
+ mCount = count;
+ mTotalTime = totalTime;
+ mVersion = version;
+ }
+ }
+
+ int kernelWakelockVersion;
+}
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index 8674a21..75b6446 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -41,15 +41,10 @@
/** enable the JIT compiler */
public static final int DEBUG_ENABLE_JIT = 1 << 5;
-
/** No external storage should be mounted. */
public static final int MOUNT_EXTERNAL_NONE = 0;
- /** Single-user external storage should be mounted. */
- public static final int MOUNT_EXTERNAL_SINGLEUSER = 1;
- /** Multi-user external storage should be mounted. */
- public static final int MOUNT_EXTERNAL_MULTIUSER = 2;
- /** All multi-user external storage should be mounted. */
- public static final int MOUNT_EXTERNAL_MULTIUSER_ALL = 3;
+ /** Default user-specific external storage should be mounted. */
+ public static final int MOUNT_EXTERNAL_DEFAULT = 1;
private static final ZygoteHooks VM_HOOKS = new ZygoteHooks();
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index 4d405b2..9106ccd 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -514,10 +514,8 @@
"Duplicate arg specified");
}
niceName = arg.substring(arg.indexOf('=') + 1);
- } else if (arg.equals("--mount-external-multiuser")) {
- mountExternal = Zygote.MOUNT_EXTERNAL_MULTIUSER;
- } else if (arg.equals("--mount-external-multiuser-all")) {
- mountExternal = Zygote.MOUNT_EXTERNAL_MULTIUSER_ALL;
+ } else if (arg.equals("--mount-external-default")) {
+ mountExternal = Zygote.MOUNT_EXTERNAL_DEFAULT;
} else if (arg.equals("--query-abi-list")) {
abiListQuery = true;
} else if (arg.startsWith("--instruction-set=")) {
diff --git a/core/java/com/android/internal/widget/FloatingToolbar.java b/core/java/com/android/internal/widget/FloatingToolbar.java
new file mode 100644
index 0000000..be9945d
--- /dev/null
+++ b/core/java/com/android/internal/widget/FloatingToolbar.java
@@ -0,0 +1,596 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.widget;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.content.Context;
+import android.graphics.Color;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.graphics.drawable.ColorDrawable;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.View.MeasureSpec;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.widget.Button;
+import android.widget.ImageButton;
+import android.widget.LinearLayout;
+import android.widget.PopupWindow;
+
+import com.android.internal.R;
+import com.android.internal.util.Preconditions;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * A floating toolbar for showing contextual menu items.
+ * This view shows as many menu item buttons as can fit in the horizontal toolbar and the
+ * the remaining menu items in a vertical overflow view when the overflow button is clicked.
+ * The horizontal toolbar morphs into the vertical overflow view.
+ */
+public final class FloatingToolbar {
+
+ private static final MenuItem.OnMenuItemClickListener NO_OP_MENUITEM_CLICK_LISTENER =
+ new MenuItem.OnMenuItemClickListener() {
+ @Override
+ public boolean onMenuItemClick(MenuItem item) {
+ return false;
+ }
+ };
+
+ private final Context mContext;
+ private final FloatingToolbarPopup mPopup;
+ private final ViewGroup mMenuItemButtonsContainer;
+ private final View.OnClickListener mMenuItemButtonOnClickListener =
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (v.getTag() instanceof MenuItem) {
+ mMenuItemClickListener.onMenuItemClick((MenuItem) v.getTag());
+ mPopup.dismiss();
+ }
+ }
+ };
+
+ private final Rect mContentRect = new Rect();
+ private final Point mCoordinates = new Point();
+
+ private Menu mMenu;
+ private List<CharSequence> mShowingTitles = new ArrayList<CharSequence>();
+ private MenuItem.OnMenuItemClickListener mMenuItemClickListener = NO_OP_MENUITEM_CLICK_LISTENER;
+ private View mOpenOverflowButton;
+
+ private int mSuggestedWidth;
+
+ /**
+ * Initializes a floating toolbar.
+ */
+ public FloatingToolbar(Context context, Window window) {
+ mContext = Preconditions.checkNotNull(context);
+ mPopup = new FloatingToolbarPopup(Preconditions.checkNotNull(window.getDecorView()));
+ mMenuItemButtonsContainer = createMenuButtonsContainer(context);
+ }
+
+ /**
+ * Sets the menu to be shown in this floating toolbar.
+ * NOTE: Call {@link #updateLayout()} or {@link #show()} to effect visual changes to the
+ * toolbar.
+ */
+ public FloatingToolbar setMenu(Menu menu) {
+ mMenu = Preconditions.checkNotNull(menu);
+ return this;
+ }
+
+ /**
+ * Sets the custom listener for invocation of menu items in this floating
+ * toolbar.
+ */
+ public FloatingToolbar setOnMenuItemClickListener(
+ MenuItem.OnMenuItemClickListener menuItemClickListener) {
+ if (menuItemClickListener != null) {
+ mMenuItemClickListener = menuItemClickListener;
+ } else {
+ mMenuItemClickListener = NO_OP_MENUITEM_CLICK_LISTENER;
+ }
+ return this;
+ }
+
+ /**
+ * Sets the content rectangle. This is the area of the interesting content that this toolbar
+ * should avoid obstructing.
+ * NOTE: Call {@link #updateLayout()} or {@link #show()} to effect visual changes to the
+ * toolbar.
+ */
+ public FloatingToolbar setContentRect(Rect rect) {
+ mContentRect.set(Preconditions.checkNotNull(rect));
+ return this;
+ }
+
+ /**
+ * Sets the suggested width of this floating toolbar.
+ * The actual width will be about this size but there are no guarantees that it will be exactly
+ * the suggested width.
+ * NOTE: Call {@link #updateLayout()} or {@link #show()} to effect visual changes to the
+ * toolbar.
+ */
+ public FloatingToolbar setSuggestedWidth(int suggestedWidth) {
+ mSuggestedWidth = suggestedWidth;
+ return this;
+ }
+
+ /**
+ * Shows this floating toolbar.
+ */
+ public FloatingToolbar show() {
+ List<MenuItem> menuItems = getVisibleAndEnabledMenuItems(mMenu);
+ if (hasContentChanged(menuItems) || hasWidthChanged()) {
+ mPopup.dismiss();
+ layoutMenuItemButtons(menuItems);
+ mShowingTitles = getMenuItemTitles(menuItems);
+ }
+ refreshCoordinates();
+ mPopup.updateCoordinates(mCoordinates.x, mCoordinates.y);
+ if (!mPopup.isShowing()) {
+ mPopup.show(mCoordinates.x, mCoordinates.y);
+ }
+ return this;
+ }
+
+ /**
+ * Updates this floating toolbar to reflect recent position and view updates.
+ * NOTE: This method is a no-op if the toolbar isn't showing.
+ */
+ public FloatingToolbar updateLayout() {
+ if (mPopup.isShowing()) {
+ // show() performs all the logic we need here.
+ show();
+ }
+ return this;
+ }
+
+ /**
+ * Dismisses this floating toolbar.
+ */
+ public void dismiss() {
+ mPopup.dismiss();
+ }
+
+ /**
+ * Returns {@code true} if this popup is currently showing. {@code false} otherwise.
+ */
+ public boolean isShowing() {
+ return mPopup.isShowing();
+ }
+
+ /**
+ * Refreshes {@link #mCoordinates} with values based on {@link #mContentRect}.
+ */
+ private void refreshCoordinates() {
+ int popupWidth = mPopup.getWidth();
+ int popupHeight = mPopup.getHeight();
+ if (!mPopup.isShowing()) {
+ // Popup isn't yet shown, get estimated size from the menu item buttons container.
+ mMenuItemButtonsContainer.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+ popupWidth = mMenuItemButtonsContainer.getMeasuredWidth();
+ popupHeight = mMenuItemButtonsContainer.getMeasuredHeight();
+ }
+ int x = mContentRect.centerX() - popupWidth / 2;
+ int y;
+ if (shouldDisplayAtTopOfContent()) {
+ y = mContentRect.top - popupHeight;
+ } else {
+ y = mContentRect.bottom;
+ }
+ mCoordinates.set(x, y);
+ }
+
+ /**
+ * Returns true if this floating toolbar's menu items have been reordered or changed.
+ */
+ private boolean hasContentChanged(List<MenuItem> menuItems) {
+ return !mShowingTitles.equals(getMenuItemTitles(menuItems));
+ }
+
+ /**
+ * Returns true if there is a significant change in width of the toolbar.
+ */
+ private boolean hasWidthChanged() {
+ int actualWidth = mPopup.getWidth();
+ int difference = Math.abs(actualWidth - mSuggestedWidth);
+ return difference > (actualWidth * 0.2);
+ }
+
+ /**
+ * Returns true if the preferred positioning of the toolbar is above the content rect.
+ */
+ private boolean shouldDisplayAtTopOfContent() {
+ return mContentRect.top - getMinimumOverflowHeight(mContext) > 0;
+ }
+
+ /**
+ * Returns the visible and enabled menu items in the specified menu.
+ * This method is recursive.
+ */
+ private List<MenuItem> getVisibleAndEnabledMenuItems(Menu menu) {
+ List<MenuItem> menuItems = new ArrayList<MenuItem>();
+ for (int i = 0; (menu != null) && (i < menu.size()); i++) {
+ MenuItem menuItem = menu.getItem(i);
+ if (menuItem.isVisible() && menuItem.isEnabled()) {
+ Menu subMenu = menuItem.getSubMenu();
+ if (subMenu != null) {
+ menuItems.addAll(getVisibleAndEnabledMenuItems(subMenu));
+ } else {
+ menuItems.add(menuItem);
+ }
+ }
+ }
+ return menuItems;
+ }
+
+ private List<CharSequence> getMenuItemTitles(List<MenuItem> menuItems) {
+ List<CharSequence> titles = new ArrayList<CharSequence>();
+ for (MenuItem menuItem : menuItems) {
+ titles.add(menuItem.getTitle());
+ }
+ return titles;
+ }
+
+ private void layoutMenuItemButtons(List<MenuItem> menuItems) {
+ final int toolbarWidth = getAdjustedToolbarWidth(mContext, mSuggestedWidth)
+ // Reserve space for the "open overflow" button.
+ - getEstimatedOpenOverflowButtonWidth(mContext);
+
+ int availableWidth = toolbarWidth;
+ LinkedList<MenuItem> remainingMenuItems = new LinkedList<MenuItem>(menuItems);
+
+ mMenuItemButtonsContainer.removeAllViews();
+
+ boolean isFirstItem = true;
+ while (!remainingMenuItems.isEmpty()) {
+ final MenuItem menuItem = remainingMenuItems.peek();
+ Button menuItemButton = createMenuItemButton(mContext, menuItem);
+
+ // Adding additional left padding for the first button to even out button spacing.
+ if (isFirstItem) {
+ menuItemButton.setPadding(
+ 2 * menuItemButton.getPaddingLeft(),
+ menuItemButton.getPaddingTop(),
+ menuItemButton.getPaddingRight(),
+ menuItemButton.getPaddingBottom());
+ isFirstItem = false;
+ }
+
+ // Adding additional right padding for the last button to even out button spacing.
+ if (remainingMenuItems.size() == 1) {
+ menuItemButton.setPadding(
+ menuItemButton.getPaddingLeft(),
+ menuItemButton.getPaddingTop(),
+ 2 * menuItemButton.getPaddingRight(),
+ menuItemButton.getPaddingBottom());
+ }
+
+ menuItemButton.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+ int menuItemButtonWidth = Math.min(menuItemButton.getMeasuredWidth(), toolbarWidth);
+ if (menuItemButtonWidth <= availableWidth) {
+ menuItemButton.setTag(menuItem);
+ menuItemButton.setOnClickListener(mMenuItemButtonOnClickListener);
+ mMenuItemButtonsContainer.addView(menuItemButton);
+ menuItemButton.getLayoutParams().width = menuItemButtonWidth;
+ availableWidth -= menuItemButtonWidth;
+ remainingMenuItems.pop();
+ } else {
+ // The "open overflow" button launches the vertical overflow from the
+ // floating toolbar.
+ createOpenOverflowButtonIfNotExists();
+ mMenuItemButtonsContainer.addView(mOpenOverflowButton);
+ break;
+ }
+ }
+ mPopup.setContentView(mMenuItemButtonsContainer);
+ }
+
+ /**
+ * Creates and returns the button that opens the vertical overflow.
+ */
+ private void createOpenOverflowButtonIfNotExists() {
+ mOpenOverflowButton = (ImageButton) LayoutInflater.from(mContext)
+ .inflate(R.layout.floating_popup_open_overflow_button, null);
+ mOpenOverflowButton.setOnClickListener(
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ // Open the overflow.
+ }
+ });
+ }
+
+ /**
+ * Creates and returns a floating toolbar menu buttons container.
+ */
+ private static ViewGroup createMenuButtonsContainer(Context context) {
+ return (ViewGroup) LayoutInflater.from(context)
+ .inflate(R.layout.floating_popup_container, null);
+ }
+
+ /**
+ * Creates and returns a menu button for the specified menu item.
+ */
+ private static Button createMenuItemButton(Context context, MenuItem menuItem) {
+ Button menuItemButton = (Button) LayoutInflater.from(context)
+ .inflate(R.layout.floating_popup_menu_button, null);
+ menuItemButton.setText(menuItem.getTitle());
+ menuItemButton.setContentDescription(menuItem.getTitle());
+ return menuItemButton;
+ }
+
+ private static int getMinimumOverflowHeight(Context context) {
+ return context.getResources().
+ getDimensionPixelSize(R.dimen.floating_toolbar_minimum_overflow_height);
+ }
+
+ private static int getEstimatedOpenOverflowButtonWidth(Context context) {
+ return context.getResources()
+ .getDimensionPixelSize(R.dimen.floating_toolbar_menu_button_minimum_width);
+ }
+
+ private static int getAdjustedToolbarWidth(Context context, int width) {
+ if (width <= 0 || width > getScreenWidth(context)) {
+ width = context.getResources()
+ .getDimensionPixelSize(R.dimen.floating_toolbar_default_width);
+ }
+ return width;
+ }
+
+ /**
+ * Returns the device's screen width.
+ */
+ public static int getScreenWidth(Context context) {
+ return context.getResources().getDisplayMetrics().widthPixels;
+ }
+
+ /**
+ * Returns the device's screen height.
+ */
+ public static int getScreenHeight(Context context) {
+ return context.getResources().getDisplayMetrics().heightPixels;
+ }
+
+
+ /**
+ * A popup window used by the floating toolbar.
+ */
+ private static final class FloatingToolbarPopup {
+
+ private final View mParent;
+ private final PopupWindow mPopupWindow;
+ private final ViewGroup mContentContainer;
+ private final Animator.AnimatorListener mOnDismissEnd =
+ new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mPopupWindow.dismiss();
+ mDismissAnimating = false;
+ }
+ };
+ private final AnimatorSet mGrowFadeInFromBottomAnimation;
+ private final AnimatorSet mShrinkFadeOutFromBottomAnimation;
+
+ private boolean mDismissAnimating;
+
+ /**
+ * Initializes a new floating bar popup.
+ *
+ * @param parent A parent view to get the {@link View#getWindowToken()} token from.
+ */
+ public FloatingToolbarPopup(View parent) {
+ mParent = Preconditions.checkNotNull(parent);
+ mContentContainer = createContentContainer(parent.getContext());
+ mPopupWindow = createPopupWindow(mContentContainer);
+ mGrowFadeInFromBottomAnimation = createGrowFadeInFromBottom(mContentContainer);
+ mShrinkFadeOutFromBottomAnimation =
+ createShrinkFadeOutFromBottomAnimation(mContentContainer, mOnDismissEnd);
+ }
+
+ /**
+ * Shows this popup at the specified coordinates.
+ * The specified coordinates may be adjusted to make sure the popup is entirely on-screen.
+ * If this popup is already showing, this will be a no-op.
+ */
+ public void show(int x, int y) {
+ if (isShowing()) {
+ updateCoordinates(x, y);
+ return;
+ }
+
+ mPopupWindow.showAtLocation(mParent, Gravity.NO_GRAVITY, 0, 0);
+ positionOnScreen(x, y);
+ growFadeInFromBottom();
+
+ mDismissAnimating = false;
+ }
+
+ /**
+ * Gets rid of this popup. If the popup isn't currently showing, this will be a no-op.
+ */
+ public void dismiss() {
+ if (!isShowing()) {
+ return;
+ }
+
+ if (mDismissAnimating) {
+ // This window is already dismissing. Don't restart the animation.
+ return;
+ }
+ mDismissAnimating = true;
+ shrinkFadeOutFromBottom();
+ }
+
+ /**
+ * Returns {@code true} if this popup is currently showing. {@code false} otherwise.
+ */
+ public boolean isShowing() {
+ return mPopupWindow.isShowing() && !mDismissAnimating;
+ }
+
+ /**
+ * Updates the coordinates of this popup.
+ * The specified coordinates may be adjusted to make sure the popup is entirely on-screen.
+ */
+ public void updateCoordinates(int x, int y) {
+ if (isShowing()) {
+ positionOnScreen(x, y);
+ }
+ }
+
+ /**
+ * Sets the content of this popup.
+ */
+ public void setContentView(View view) {
+ Preconditions.checkNotNull(view);
+ mContentContainer.removeAllViews();
+ mContentContainer.addView(view);
+ }
+
+ /**
+ * Returns the width of this popup.
+ */
+ public int getWidth() {
+ return mContentContainer.getWidth();
+ }
+
+ /**
+ * Returns the height of this popup.
+ */
+ public int getHeight() {
+ return mContentContainer.getHeight();
+ }
+
+ /**
+ * Returns the context this popup is running in.
+ */
+ public Context getContext() {
+ return mContentContainer.getContext();
+ }
+
+ private void positionOnScreen(int x, int y) {
+ if (getWidth() == 0) {
+ // content size is yet to be measured.
+ mContentContainer.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+ }
+ x = clamp(x, 0, getScreenWidth(getContext()) - getWidth());
+ y = clamp(y, 0, getScreenHeight(getContext()) - getHeight());
+
+ // Position the view w.r.t. the window.
+ mContentContainer.setX(x);
+ mContentContainer.setY(y);
+ }
+
+ /**
+ * Performs the "grow and fade in from the bottom" animation on the floating popup.
+ */
+ private void growFadeInFromBottom() {
+ setPivot();
+ mGrowFadeInFromBottomAnimation.start();
+ }
+
+ /**
+ * Performs the "shrink and fade out from bottom" animation on the floating popup.
+ */
+ private void shrinkFadeOutFromBottom() {
+ setPivot();
+ mShrinkFadeOutFromBottomAnimation.start();
+ }
+
+ /**
+ * Sets the popup content container's pivot.
+ */
+ private void setPivot() {
+ mContentContainer.setPivotX(mContentContainer.getMeasuredWidth() / 2);
+ mContentContainer.setPivotY(mContentContainer.getMeasuredHeight());
+ }
+
+ private static ViewGroup createContentContainer(Context context) {
+ return (ViewGroup) LayoutInflater.from(context)
+ .inflate(R.layout.floating_popup_container, null);
+ }
+
+ private static PopupWindow createPopupWindow(View content) {
+ ViewGroup popupContentHolder = new LinearLayout(content.getContext());
+ PopupWindow popupWindow = new PopupWindow(popupContentHolder);
+ popupWindow.setAnimationStyle(0);
+ popupWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
+ popupWindow.setWidth(getScreenWidth(content.getContext()));
+ popupWindow.setHeight(getScreenHeight(content.getContext()));
+ content.setLayoutParams(new ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
+ popupContentHolder.addView(content);
+ return popupWindow;
+ }
+
+ /**
+ * Creates a "grow and fade in from the bottom" animation for the specified view.
+ *
+ * @param view The view to animate
+ */
+ private static AnimatorSet createGrowFadeInFromBottom(View view) {
+ AnimatorSet growFadeInFromBottomAnimation = new AnimatorSet();
+ growFadeInFromBottomAnimation.playTogether(
+ ObjectAnimator.ofFloat(view, View.SCALE_X, 0.5f, 1).setDuration(125),
+ ObjectAnimator.ofFloat(view, View.SCALE_Y, 0.5f, 1).setDuration(125),
+ ObjectAnimator.ofFloat(view, View.ALPHA, 0, 1).setDuration(75));
+ return growFadeInFromBottomAnimation;
+ }
+
+ /**
+ * Creates a "shrink and fade out from bottom" animation for the specified view.
+ *
+ * @param view The view to animate
+ * @param listener The animation listener
+ */
+ private static AnimatorSet createShrinkFadeOutFromBottomAnimation(
+ View view, Animator.AnimatorListener listener) {
+ AnimatorSet shrinkFadeOutFromBottomAnimation = new AnimatorSet();
+ shrinkFadeOutFromBottomAnimation.playTogether(
+ ObjectAnimator.ofFloat(view, View.SCALE_Y, 1, 0.5f).setDuration(125),
+ ObjectAnimator.ofFloat(view, View.ALPHA, 1, 0).setDuration(75));
+ shrinkFadeOutFromBottomAnimation.setStartDelay(150);
+ shrinkFadeOutFromBottomAnimation.addListener(listener);
+ return shrinkFadeOutFromBottomAnimation;
+ }
+
+ /**
+ * Returns value, restricted to the range min->max (inclusive).
+ * If maximum is less than minimum, the result is undefined.
+ *
+ * @param value The value to clamp.
+ * @param minimum The minimum value in the range.
+ * @param maximum The maximum value in the range. Must not be less than minimum.
+ */
+ private static int clamp(int value, int minimum, int maximum) {
+ return Math.max(minimum, Math.min(value, maximum));
+ }
+ }
+}
diff --git a/core/java/com/android/internal/widget/ViewPager.java b/core/java/com/android/internal/widget/ViewPager.java
index 8018942..8d66191 100644
--- a/core/java/com/android/internal/widget/ViewPager.java
+++ b/core/java/com/android/internal/widget/ViewPager.java
@@ -42,6 +42,7 @@
import android.view.ViewParent;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
import android.view.accessibility.AccessibilityRecord;
import android.view.animation.Interpolator;
import android.widget.EdgeEffect;
@@ -371,8 +372,6 @@
mCloseEnough = (int) (CLOSE_ENOUGH * density);
mDefaultGutterSize = (int) (DEFAULT_GUTTER_SIZE * density);
- setAccessibilityDelegate(new MyAccessibilityDelegate());
-
if (getImportantForAccessibility() == IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
}
@@ -2695,29 +2694,6 @@
}
@Override
- public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
- // Dispatch scroll events from this ViewPager.
- if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_SCROLLED) {
- return super.dispatchPopulateAccessibilityEvent(event);
- }
-
- // Dispatch all other accessibility events from the current page.
- final int childCount = getChildCount();
- for (int i = 0; i < childCount; i++) {
- final View child = getChildAt(i);
- if (child.getVisibility() == VISIBLE) {
- final ItemInfo ii = infoForChild(child);
- if (ii != null && ii.position == mCurItem &&
- child.dispatchPopulateAccessibilityEvent(event)) {
- return true;
- }
- }
- }
-
- return false;
- }
-
- @Override
protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
return new LayoutParams();
}
@@ -2737,60 +2713,63 @@
return new LayoutParams(getContext(), attrs);
}
- class MyAccessibilityDelegate extends AccessibilityDelegate {
- @Override
- public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) {
- super.onInitializeAccessibilityEvent(host, event);
- event.setClassName(ViewPager.class.getName());
- final AccessibilityRecord record = AccessibilityRecord.obtain();
- record.setScrollable(canScroll());
- if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_SCROLLED
- && mAdapter != null) {
- record.setItemCount(mAdapter.getCount());
- record.setFromIndex(mCurItem);
- record.setToIndex(mCurItem);
- }
+ @Override
+ public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+ super.onInitializeAccessibilityEvent(event);
+
+ event.setClassName(ViewPager.class.getName());
+ event.setScrollable(canScroll());
+
+ if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_SCROLLED && mAdapter != null) {
+ event.setItemCount(mAdapter.getCount());
+ event.setFromIndex(mCurItem);
+ event.setToIndex(mCurItem);
+ }
+ }
+
+ @Override
+ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(info);
+
+ info.setClassName(ViewPager.class.getName());
+ info.setScrollable(canScroll());
+
+ if (canScrollHorizontally(1)) {
+ info.addAction(AccessibilityAction.ACTION_SCROLL_FORWARD);
}
- @Override
- public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
- super.onInitializeAccessibilityNodeInfo(host, info);
- info.setClassName(ViewPager.class.getName());
- info.setScrollable(canScroll());
- if (canScrollHorizontally(1)) {
- info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
- }
- if (canScrollHorizontally(-1)) {
- info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
- }
+ if (canScrollHorizontally(-1)) {
+ info.addAction(AccessibilityAction.ACTION_SCROLL_BACKWARD);
+ }
+ }
+
+ @Override
+ public boolean performAccessibilityAction(int action, Bundle args) {
+ if (super.performAccessibilityAction(action, args)) {
+ return true;
}
- @Override
- public boolean performAccessibilityAction(View host, int action, Bundle args) {
- if (super.performAccessibilityAction(host, action, args)) {
- return true;
- }
- switch (action) {
- case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD: {
- if (canScrollHorizontally(1)) {
- setCurrentItem(mCurItem + 1);
- return true;
- }
- } return false;
- case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD: {
- if (canScrollHorizontally(-1)) {
- setCurrentItem(mCurItem - 1);
- return true;
- }
- } return false;
- }
- return false;
+ switch (action) {
+ case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD:
+ if (canScrollHorizontally(1)) {
+ setCurrentItem(mCurItem + 1);
+ return true;
+ }
+ return false;
+ case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD:
+ if (canScrollHorizontally(-1)) {
+ setCurrentItem(mCurItem - 1);
+ return true;
+ }
+ return false;
}
- private boolean canScroll() {
- return (mAdapter != null) && (mAdapter.getCount() > 1);
- }
+ return false;
+ }
+
+ private boolean canScroll() {
+ return mAdapter != null && mAdapter.getCount() > 1;
}
private class PagerObserver extends DataSetObserver {
diff --git a/core/java/com/android/server/backup/SystemBackupAgent.java b/core/java/com/android/server/backup/SystemBackupAgent.java
index b5f2f37..037fd66 100644
--- a/core/java/com/android/server/backup/SystemBackupAgent.java
+++ b/core/java/com/android/server/backup/SystemBackupAgent.java
@@ -104,9 +104,9 @@
// steps during restore; the restore will happen properly when the individual
// files are restored piecemeal.
FullBackup.backupToTar(getPackageName(), FullBackup.ROOT_TREE_TOKEN, null,
- WALLPAPER_INFO_DIR, WALLPAPER_INFO, output.getData());
+ WALLPAPER_INFO_DIR, WALLPAPER_INFO, output);
FullBackup.backupToTar(getPackageName(), FullBackup.ROOT_TREE_TOKEN, null,
- WALLPAPER_IMAGE_DIR, WALLPAPER_IMAGE, output.getData());
+ WALLPAPER_IMAGE_DIR, WALLPAPER_IMAGE, output);
}
@Override
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 34de6c5..a4c91b3 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -558,6 +558,8 @@
char dex2oatXmxFlagsBuf[sizeof("-Xmx")-1 + PROPERTY_VALUE_MAX];
char dex2oatCompilerFilterBuf[sizeof("--compiler-filter=")-1 + PROPERTY_VALUE_MAX];
char dex2oatImageCompilerFilterBuf[sizeof("--compiler-filter=")-1 + PROPERTY_VALUE_MAX];
+ char dex2oatThreadsBuf[sizeof("-j")-1 + PROPERTY_VALUE_MAX];
+ char dex2oatThreadsImageBuf[sizeof("-j")-1 + PROPERTY_VALUE_MAX];
char dex2oatFlagsBuf[PROPERTY_VALUE_MAX];
char dex2oatImageFlagsBuf[PROPERTY_VALUE_MAX];
char extraOptsBuf[PROPERTY_VALUE_MAX];
@@ -733,6 +735,9 @@
parseCompilerOption("dalvik.vm.dex2oat-filter", dex2oatCompilerFilterBuf,
"--compiler-filter=", "-Xcompiler-option");
}
+ parseCompilerOption("dalvik.vm.dex2oat-threads", dex2oatThreadsBuf, "-j", "-Xcompiler-option");
+ parseCompilerOption("dalvik.vm.image-dex2oat-threads", dex2oatThreadsImageBuf, "-j",
+ "-Ximage-compiler-option");
property_get("dalvik.vm.dex2oat-flags", dex2oatFlagsBuf, "");
parseExtraOpts(dex2oatFlagsBuf, "-Xcompiler-option");
diff --git a/core/jni/android/graphics/MinikinUtils.cpp b/core/jni/android/graphics/MinikinUtils.cpp
index 8139c24..8bdbff4 100644
--- a/core/jni/android/graphics/MinikinUtils.cpp
+++ b/core/jni/android/graphics/MinikinUtils.cpp
@@ -26,10 +26,10 @@
namespace android {
-void MinikinUtils::doLayout(Layout* layout, const Paint* paint, int bidiFlags, TypefaceImpl* typeface,
- const uint16_t* buf, size_t start, size_t count, size_t bufSize) {
- TypefaceImpl* resolvedFace = TypefaceImpl_resolveDefault(typeface);
- layout->setFontCollection(resolvedFace->fFontCollection);
+FontStyle MinikinUtils::prepareMinikinPaint(MinikinPaint* minikinPaint, FontCollection** pFont,
+ const Paint* paint, TypefaceImpl* typeface) {
+ const TypefaceImpl* resolvedFace = TypefaceImpl_resolveDefault(typeface);
+ *pFont = resolvedFace->fFontCollection;
FontStyle resolved = resolvedFace->fStyle;
/* Prepare minikin FontStyle */
@@ -40,15 +40,26 @@
FontStyle minikinStyle(minikinLang, minikinVariant, resolved.getWeight(), resolved.getItalic());
/* Prepare minikin Paint */
- MinikinPaint minikinPaint;
- minikinPaint.size = (int)/*WHY?!*/paint->getTextSize();
- minikinPaint.scaleX = paint->getTextScaleX();
- minikinPaint.skewX = paint->getTextSkewX();
- minikinPaint.letterSpacing = paint->getLetterSpacing();
- minikinPaint.paintFlags = MinikinFontSkia::packPaintFlags(paint);
- minikinPaint.fontFeatureSettings = paint->getFontFeatureSettings();
- minikinPaint.hyphenEdit = HyphenEdit(paint->getHyphenEdit());
+ // Note: it would be nice to handle fractional size values (it would improve smooth zoom
+ // behavior), but historically size has been treated as an int.
+ // TODO: explore whether to enable fractional sizes, possibly when linear text flag is set.
+ minikinPaint->size = (int)paint->getTextSize();
+ minikinPaint->scaleX = paint->getTextScaleX();
+ minikinPaint->skewX = paint->getTextSkewX();
+ minikinPaint->letterSpacing = paint->getLetterSpacing();
+ minikinPaint->paintFlags = MinikinFontSkia::packPaintFlags(paint);
+ minikinPaint->fontFeatureSettings = paint->getFontFeatureSettings();
+ minikinPaint->hyphenEdit = HyphenEdit(paint->getHyphenEdit());
+ return minikinStyle;
+}
+void MinikinUtils::doLayout(Layout* layout, const Paint* paint, int bidiFlags,
+ TypefaceImpl* typeface, const uint16_t* buf, size_t start, size_t count,
+ size_t bufSize) {
+ FontCollection *font;
+ MinikinPaint minikinPaint;
+ FontStyle minikinStyle = prepareMinikinPaint(&minikinPaint, &font, paint, typeface);
+ layout->setFontCollection(font);
layout->doLayout(buf, start, count, bufSize, bidiFlags, minikinStyle, minikinPaint);
}
diff --git a/core/jni/android/graphics/MinikinUtils.h b/core/jni/android/graphics/MinikinUtils.h
index 236f1fd..1ee6245 100644
--- a/core/jni/android/graphics/MinikinUtils.h
+++ b/core/jni/android/graphics/MinikinUtils.h
@@ -31,22 +31,14 @@
namespace android {
-// TODO: these should be defined in Minikin's Layout.h
-enum {
- kBidi_LTR = 0,
- kBidi_RTL = 1,
- kBidi_Default_LTR = 2,
- kBidi_Default_RTL = 3,
- kBidi_Force_LTR = 4,
- kBidi_Force_RTL = 5,
-
- kBidi_Mask = 0x7
-};
-
class MinikinUtils {
public:
- static void doLayout(Layout* layout, const Paint* paint, int bidiFlags, TypefaceImpl* typeface,
- const uint16_t* buf, size_t start, size_t count, size_t bufSize);
+ static FontStyle prepareMinikinPaint(MinikinPaint* minikinPaint, FontCollection** pFont,
+ const Paint* paint, TypefaceImpl* typeface);
+
+ static void doLayout(Layout* layout, const Paint* paint, int bidiFlags,
+ TypefaceImpl* typeface, const uint16_t* buf, size_t start, size_t count,
+ size_t bufSize);
static float xOffsetForTextAlign(Paint* paint, const Layout& layout);
diff --git a/core/jni/android/graphics/TypefaceImpl.h b/core/jni/android/graphics/TypefaceImpl.h
index d36f83a..4b14917 100644
--- a/core/jni/android/graphics/TypefaceImpl.h
+++ b/core/jni/android/graphics/TypefaceImpl.h
@@ -62,4 +62,4 @@
}
-#endif // _ANDROID_GRAPHICS_TYPEFACE_IMPL_H_
\ No newline at end of file
+#endif // _ANDROID_GRAPHICS_TYPEFACE_IMPL_H_
diff --git a/core/jni/android_app_backup_FullBackup.cpp b/core/jni/android_app_backup_FullBackup.cpp
index 2c02b37..63b2e2a 100644
--- a/core/jni/android_app_backup_FullBackup.cpp
+++ b/core/jni/android_app_backup_FullBackup.cpp
@@ -15,6 +15,8 @@
*/
#define LOG_TAG "FullBackup_native"
+#include <sys/stat.h>
+
#include <utils/Log.h>
#include <utils/String8.h>
@@ -30,6 +32,12 @@
namespace android
{
+// android.app.backup.FullBackupDataOutput
+static struct {
+ jfieldID mData; // type android.app.backup.BackupDataOutput
+ jmethodID addSize;
+} sFullBackupDataOutput;
+
// android.app.backup.BackupDataOutput
static struct {
// This is actually a native pointer to the underlying BackupDataWriter instance
@@ -70,7 +78,7 @@
* linkdomain: where a symlink points for purposes of rewriting; current unused
* rootpath: prefix to be snipped from full path when encoding in tar
* path: absolute path to the file to be saved
- * dataOutput: the BackupDataOutput object that we're saving into
+ * dataOutput: the FullBackupDataOutput object that we're saving into
*/
static jint backupToTar(JNIEnv* env, jobject clazz, jstring packageNameObj,
jstring domainObj, jstring linkdomain,
@@ -91,15 +99,11 @@
if (rootchars) env->ReleaseStringUTFChars(rootpathObj, rootchars);
if (packagenamechars) env->ReleaseStringUTFChars(packageNameObj, packagenamechars);
- // Extract the data output fd
- BackupDataWriter* writer = (BackupDataWriter*) env->GetLongField(dataOutputObj,
- sBackupDataOutput.mBackupWriter);
-
- // Validate
- if (!writer) {
- ALOGE("No output stream provided [%s]", path.string());
- return (jint) -1;
- }
+ // Extract the data output fd. 'writer' ends up NULL in the measure-only case.
+ jobject bdo = env->GetObjectField(dataOutputObj, sFullBackupDataOutput.mData);
+ BackupDataWriter* writer = (bdo != NULL)
+ ? (BackupDataWriter*) env->GetLongField(bdo, sBackupDataOutput.mBackupWriter)
+ : NULL;
if (path.length() < rootpath.length()) {
ALOGE("file path [%s] shorter than root path [%s]",
@@ -107,20 +111,30 @@
return (jint) -1;
}
- return (jint) write_tarfile(packageName, domain, rootpath, path, writer);
+ off_t tarSize = 0;
+ jint err = write_tarfile(packageName, domain, rootpath, path, &tarSize, writer);
+ if (!err) {
+ //ALOGI("measured [%s] at %lld", path.string(), (long long) tarSize);
+ env->CallVoidMethod(dataOutputObj, sFullBackupDataOutput.addSize, (jlong) tarSize);
+ }
+
+ return err;
}
static const JNINativeMethod g_methods[] = {
{ "backupToTar",
- "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Landroid/app/backup/BackupDataOutput;)I",
+ "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Landroid/app/backup/FullBackupDataOutput;)I",
(void*)backupToTar },
};
int register_android_app_backup_FullBackup(JNIEnv* env)
{
- jclass clazz = FindClassOrDie(env, "android/app/backup/BackupDataOutput");
+ jclass fbdoClazz = FindClassOrDie(env, "android/app/backup/FullBackupDataOutput");
+ sFullBackupDataOutput.mData = GetFieldIDOrDie(env, fbdoClazz, "mData", "Landroid/app/backup/BackupDataOutput;");
+ sFullBackupDataOutput.addSize = GetMethodIDOrDie(env, fbdoClazz, "addSize", "(J)V");
- sBackupDataOutput.mBackupWriter = GetFieldIDOrDie(env, clazz, "mBackupWriter", "J");
+ jclass bdoClazz = FindClassOrDie(env, "android/app/backup/BackupDataOutput");
+ sBackupDataOutput.mBackupWriter = GetFieldIDOrDie(env, bdoClazz, "mBackupWriter", "J");
return RegisterMethodsOrDie(env, "android/app/backup/FullBackup", g_methods, NELEM(g_methods));
}
diff --git a/core/jni/android_graphics_Canvas.cpp b/core/jni/android_graphics_Canvas.cpp
index 49ee6c5..4c08b4b 100644
--- a/core/jni/android_graphics_Canvas.cpp
+++ b/core/jni/android_graphics_Canvas.cpp
@@ -89,7 +89,7 @@
static void restore(JNIEnv* env, jobject, jlong canvasHandle) {
Canvas* canvas = get_canvas(canvasHandle);
if (canvas->getSaveCount() <= 1) { // cannot restore anymore
- // fail silently on underflow, so as not to break existing apps that miscount
+ doThrowISE(env, "Underflow in restore - more restores than saves");
return;
}
canvas->restore();
@@ -98,7 +98,7 @@
static void restoreToCount(JNIEnv* env, jobject, jlong canvasHandle, jint restoreCount) {
Canvas* canvas = get_canvas(canvasHandle);
if (restoreCount < 1 || restoreCount > canvas->getSaveCount()) {
- // fail silently on underflow, so as not to break existing apps that miscount
+ doThrowIAE(env, "Underflow in restoreToCount - more restores than saves");
return;
}
canvas->restoreToCount(restoreCount);
diff --git a/core/jni/android_text_StaticLayout.cpp b/core/jni/android_text_StaticLayout.cpp
index 8800f0b..87c58d6 100644
--- a/core/jni/android_text_StaticLayout.cpp
+++ b/core/jni/android_text_StaticLayout.cpp
@@ -21,6 +21,7 @@
#include "unicode/brkiter.h"
#include "utils/misc.h"
#include "utils/Log.h"
+#include "ScopedStringChars.h"
#include "ScopedPrimitiveArray.h"
#include "JNIHelp.h"
#include "core_jni_helpers.h"
@@ -34,6 +35,7 @@
#include "MinikinSkia.h"
#include "MinikinUtils.h"
#include "Paint.h"
+#include "minikin/LineBreaker.h"
namespace android {
@@ -46,636 +48,119 @@
static jclass gLineBreaks_class;
static JLineBreaksID gLineBreaks_fieldID;
-class Builder {
- public:
- ~Builder() {
- utext_close(&mUText);
- delete mBreakIterator;
- }
+// set text and set a number of parameters for creating a layout (width, tabstops, strategy)
+static void nSetupParagraph(JNIEnv* env, jclass, jlong nativePtr, jcharArray text, jint length,
+ jfloat firstWidth, jint firstWidthLineLimit, jfloat restWidth,
+ jintArray variableTabStops, jint defaultTabStop, jint strategy) {
+ LineBreaker* b = reinterpret_cast<LineBreaker*>(nativePtr);
+ b->resize(length);
+ env->GetCharArrayRegion(text, 0, length, b->buffer());
+ b->setText();
+ b->setLineWidths(firstWidth, firstWidthLineLimit, restWidth);
+ if (variableTabStops == nullptr) {
+ b->setTabStops(nullptr, 0, defaultTabStop);
+ } else {
+ ScopedIntArrayRO stops(env, variableTabStops);
+ b->setTabStops(stops.get(), stops.size(), defaultTabStop);
+ }
+ b->setStrategy(static_cast<BreakStrategy>(strategy));
+}
- void setLocale(const icu::Locale& locale) {
- delete mBreakIterator;
- UErrorCode status = U_ZERO_ERROR;
- mBreakIterator = icu::BreakIterator::createLineInstance(locale, status);
- // TODO: check status
- }
-
- void resize(size_t size) {
- mTextBuf.resize(size);
- mWidthBuf.resize(size);
- }
-
- size_t size() const {
- return mTextBuf.size();
- }
-
- uint16_t* buffer() {
- return mTextBuf.data();
- }
-
- float* widths() {
- return mWidthBuf.data();
- }
-
- // set text to current contents of buffer
- void setText() {
- UErrorCode status = U_ZERO_ERROR;
- utext_openUChars(&mUText, mTextBuf.data(), mTextBuf.size(), &status);
- mBreakIterator->setText(&mUText, status);
- }
-
- void finish() {
- if (mTextBuf.size() > MAX_TEXT_BUF_RETAIN) {
- mTextBuf.clear();
- mTextBuf.shrink_to_fit();
- mWidthBuf.clear();
- mWidthBuf.shrink_to_fit();
- }
- }
-
- icu::BreakIterator* breakIterator() const {
- return mBreakIterator;
- }
-
- float measureStyleRun(Paint* paint, TypefaceImpl* typeface, size_t start, size_t end,
- bool isRtl);
-
- void addReplacement(size_t start, size_t end, float width);
-
- private:
- const size_t MAX_TEXT_BUF_RETAIN = 32678;
- icu::BreakIterator* mBreakIterator = nullptr;
- UText mUText = UTEXT_INITIALIZER;
- std::vector<uint16_t>mTextBuf;
- std::vector<float>mWidthBuf;
-};
-
-static const int CHAR_SPACE = 0x20;
-static const int CHAR_TAB = 0x09;
-static const int CHAR_NEWLINE = 0x0a;
-static const int CHAR_ZWSP = 0x200b;
-
-class TabStops {
- public:
- // specified stops must be a sorted array (allowed to be null)
- TabStops(JNIEnv* env, jintArray stops, jint defaultTabWidth) :
- mStops(env), mTabWidth(defaultTabWidth) {
- if (stops != nullptr) {
- mStops.reset(stops);
- mNumStops = mStops.size();
- } else {
- mNumStops = 0;
- }
- }
- float width(float widthSoFar) const {
- const jint* mStopsArray = mStops.get();
- for (int i = 0; i < mNumStops; i++) {
- if (mStopsArray[i] > widthSoFar) {
- return mStopsArray[i];
- }
- }
- // find the next tabstop after widthSoFar
- return static_cast<int>((widthSoFar + mTabWidth) / mTabWidth) * mTabWidth;
- }
- private:
- ScopedIntArrayRO mStops;
- const int mTabWidth;
- int mNumStops;
-
- // disable copying and assignment
- TabStops(const TabStops&);
- void operator=(const TabStops&);
-};
-
-enum PrimitiveType {
- kPrimitiveType_Box,
- kPrimitiveType_Glue,
- kPrimitiveType_Penalty,
- kPrimitiveType_Variable,
- kPrimitiveType_Wordbreak
-};
-
-static const float PENALTY_INFINITY = 1e7; // forced non-break, negative infinity is forced break
-
-struct Primitive {
- PrimitiveType type;
- int location;
- // 'Box' has width
- // 'Glue' has width
- // 'Penalty' has width and penalty
- // 'Variable' has tabStop
- // 'Wordbreak' has penalty
- union {
- struct {
- float width;
- float penalty;
- };
- const TabStops* tabStop;
- };
-};
-
-class LineWidth {
- public:
- LineWidth(float firstWidth, int firstWidthLineCount, float restWidth) :
- mFirstWidth(firstWidth), mFirstWidthLineCount(firstWidthLineCount),
- mRestWidth(restWidth) {}
- float getLineWidth(int line) const {
- return (line < mFirstWidthLineCount) ? mFirstWidth : mRestWidth;
- }
- private:
- const float mFirstWidth;
- const int mFirstWidthLineCount;
- const float mRestWidth;
-};
-
-class LineBreaker {
- public:
- LineBreaker(const std::vector<Primitive>& primitives,
- const LineWidth& lineWidth) :
- mPrimitives(primitives), mLineWidth(lineWidth) {}
- virtual ~LineBreaker() {}
- virtual void computeBreaks(std::vector<int>* breaks, std::vector<float>* widths,
- std::vector<unsigned char>* flags) const = 0;
- protected:
- const std::vector<Primitive>& mPrimitives;
- const LineWidth& mLineWidth;
-};
-
-class OptimizingLineBreaker : public LineBreaker {
- public:
- OptimizingLineBreaker(const std::vector<Primitive>& primitives, const LineWidth& lineWidth) :
- LineBreaker(primitives, lineWidth) {}
- void computeBreaks(std::vector<int>* breaks, std::vector<float>* widths,
- std::vector<unsigned char>* flags) const {
- int numBreaks = mPrimitives.size();
- Node* opt = new Node[numBreaks];
- opt[0].prev = -1;
- opt[0].prevCount = 0;
- opt[0].width = 0;
- opt[0].demerits = 0;
- opt[0].flags = false;
- opt[numBreaks - 1].prev = -1;
- opt[numBreaks - 1].prevCount = 0;
-
- std::list<int> active;
- active.push_back(0);
- int lastBreak = 0;
- for (int i = 0; i < numBreaks; i++) {
- const Primitive& p = mPrimitives[i];
- if (p.type == kPrimitiveType_Penalty) {
- const bool finalBreak = (i + 1 == numBreaks);
- bool breakFound = false;
- Node bestBreak;
- for (std::list<int>::iterator it = active.begin(); it != active.end(); /* incrementing done in loop */) {
- const int pos = *it;
- bool flags;
- float width, printedWidth;
- const int lines = opt[pos].prevCount;
- const float maxWidth = mLineWidth.getLineWidth(lines);
- // we have to compute metrics every time --
- // we can't really precompute this stuff and just deal with breaks
- // because of the way tab characters work, this makes it computationally
- // harder, but this way, we can still optimize while treating tab characters
- // correctly
- computeMetrics(pos, i, &width, &printedWidth, &flags);
- if (printedWidth <= maxWidth) {
- float demerits = computeDemerits(maxWidth, printedWidth,
- finalBreak, p.penalty) + opt[pos].demerits;
- if (!breakFound || demerits < bestBreak.demerits) {
- bestBreak.prev = pos;
- bestBreak.prevCount = opt[pos].prevCount + 1;
- bestBreak.demerits = demerits;
- bestBreak.width = printedWidth;
- bestBreak.flags = flags;
- breakFound = true;
- }
- ++it;
- } else {
- active.erase(it++); // safe to delete like this
- }
- }
- if (p.penalty == -PENALTY_INFINITY) {
- active.clear();
- }
- if (breakFound) {
- opt[i] = bestBreak;
- active.push_back(i);
- lastBreak = i;
- }
- if (active.empty()) {
- // we can't give up!
- float width, printedWidth;
- bool flags;
- const int lines = opt[lastBreak].prevCount;
- const float maxWidth = mLineWidth.getLineWidth(lines);
- const int breakIndex = desperateBreak(lastBreak, numBreaks, maxWidth, &width, &printedWidth, &flags);
-
- opt[breakIndex].prev = lastBreak;
- opt[breakIndex].prevCount = lines + 1;
- opt[breakIndex].demerits = 0; // doesn't matter, it's the only one
- opt[breakIndex].width = width;
- opt[breakIndex].flags = flags;
-
- active.push_back(breakIndex);
- lastBreak = breakIndex;
- i = breakIndex; // incremented by i++
- }
- }
- }
-
- int idx = numBreaks - 1;
- int count = opt[idx].prevCount;
- breaks->resize(count);
- widths->resize(count);
- flags->resize(count);
- while (opt[idx].prev != -1) {
- --count;
-
- (*breaks)[count] = mPrimitives[idx].location;
- (*widths)[count] = opt[idx].width;
- (*flags)[count] = opt[idx].flags;
-
- idx = opt[idx].prev;
- }
- delete[] opt;
- }
- private:
- inline void computeMetrics(int start, int end, float* width, float* printedWidth, bool* flags) const {
- bool f = false;
- float w = 0, pw = 0;
- for (int i = start; i < end; i++) {
- const Primitive& p = mPrimitives[i];
- if (p.type == kPrimitiveType_Box || p.type == kPrimitiveType_Glue) {
- w += p.width;
- if (p.type == kPrimitiveType_Box) {
- pw = w;
- }
- } else if (p.type == kPrimitiveType_Variable) {
- w = p.tabStop->width(w);
- f = true;
- }
- }
- *width = w;
- *printedWidth = pw;
- *flags = f;
- }
-
- inline float computeDemerits(float maxWidth, float width, bool finalBreak, float penalty) const {
- float deviation = finalBreak ? 0 : maxWidth - width;
- return (deviation * deviation) + penalty;
- }
-
- // returns end pos (chosen break), -1 if fail
- inline int desperateBreak(int start, int limit, float maxWidth, float* width, float* printedWidth, bool* flags) const {
- float w = 0, pw = 0;
- bool breakFound = false;
- int breakIndex = 0, firstTabIndex = INT_MAX;
- for (int i = start; i < limit; i++) {
- const Primitive& p = mPrimitives[i];
-
- if (p.type == kPrimitiveType_Box || p.type == kPrimitiveType_Glue) {
- w += p.width;
- if (p.type == kPrimitiveType_Box) {
- pw = w;
- }
- } else if (p.type == kPrimitiveType_Variable) {
- w = p.tabStop->width(w);
- firstTabIndex = std::min(firstTabIndex, i);
- }
-
- if (pw > maxWidth) {
- if (breakFound) {
- break;
- } else {
- // no choice, keep going
- }
- }
-
- // must make progress
- if (i > start && (p.type == kPrimitiveType_Penalty || p.type == kPrimitiveType_Wordbreak)) {
- breakFound = true;
- breakIndex = i;
- }
- }
-
- if (breakFound) {
- *width = w;
- *printedWidth = pw;
- *flags = (start <= firstTabIndex && firstTabIndex < breakIndex);
- return breakIndex;
- } else {
- return -1;
- }
- }
-
- struct Node {
- int prev; // set to sentinel value (-1) for initial node
- int prevCount; // number of breaks so far
- float demerits;
- float width;
- bool flags;
- };
-};
-
-class GreedyLineBreaker : public LineBreaker {
- public:
- GreedyLineBreaker(const std::vector<Primitive>& primitives, const LineWidth& lineWidth) :
- LineBreaker(primitives, lineWidth) {}
- void computeBreaks(std::vector<int>* breaks, std::vector<float>* widths,
- std::vector<unsigned char>* flags) const {
- int lineNum = 0;
- float width = 0, printedWidth = 0;
- bool breakFound = false, goodBreakFound = false;
- int breakIndex = 0, goodBreakIndex = 0;
- float breakWidth = 0, goodBreakWidth = 0;
- int firstTabIndex = INT_MAX;
-
- float maxWidth = mLineWidth.getLineWidth(lineNum);
-
- const int numPrimitives = mPrimitives.size();
- // greedily fit as many characters as possible on each line
- // loop over all primitives, and choose the best break point
- // (if possible, a break point without splitting a word)
- // after going over the maximum length
- for (int i = 0; i < numPrimitives; i++) {
- const Primitive& p = mPrimitives[i];
-
- // update the current line width
- if (p.type == kPrimitiveType_Box || p.type == kPrimitiveType_Glue) {
- width += p.width;
- if (p.type == kPrimitiveType_Box) {
- printedWidth = width;
- }
- } else if (p.type == kPrimitiveType_Variable) {
- width = p.tabStop->width(width);
- // keep track of first tab character in the region we are examining
- // so we can determine whether or not a line contains a tab
- firstTabIndex = std::min(firstTabIndex, i);
- }
-
- // find the best break point for the characters examined so far
- if (printedWidth > maxWidth) {
- if (breakFound || goodBreakFound) {
- if (goodBreakFound) {
- // a true line break opportunity existed in the characters examined so far,
- // so there is no need to split a word
- i = goodBreakIndex; // no +1 because of i++
- lineNum++;
- maxWidth = mLineWidth.getLineWidth(lineNum);
- breaks->push_back(mPrimitives[goodBreakIndex].location);
- widths->push_back(goodBreakWidth);
- flags->push_back(firstTabIndex < goodBreakIndex);
- firstTabIndex = INT_MAX;
- } else {
- // must split a word because there is no other option
- i = breakIndex; // no +1 because of i++
- lineNum++;
- maxWidth = mLineWidth.getLineWidth(lineNum);
- breaks->push_back(mPrimitives[breakIndex].location);
- widths->push_back(breakWidth);
- flags->push_back(firstTabIndex < breakIndex);
- firstTabIndex = INT_MAX;
- }
- printedWidth = width = 0;
- goodBreakFound = breakFound = false;
- goodBreakWidth = breakWidth = 0;
- continue;
- } else {
- // no choice, keep going... must make progress by putting at least one
- // character on a line, even if part of that character is cut off --
- // there is no other option
- }
- }
-
- // update possible break points
- if (p.type == kPrimitiveType_Penalty && p.penalty < PENALTY_INFINITY) {
- // this does not handle penalties with width
-
- // handle forced line break
- if (p.penalty == -PENALTY_INFINITY) {
- lineNum++;
- maxWidth = mLineWidth.getLineWidth(lineNum);
- breaks->push_back(p.location);
- widths->push_back(printedWidth);
- flags->push_back(firstTabIndex < i);
- firstTabIndex = INT_MAX;
- printedWidth = width = 0;
- goodBreakFound = breakFound = false;
- goodBreakWidth = breakWidth = 0;
- continue;
- }
- if (i > breakIndex && (printedWidth <= maxWidth || breakFound == false)) {
- breakFound = true;
- breakIndex = i;
- breakWidth = printedWidth;
- }
- if (i > goodBreakIndex && printedWidth <= maxWidth) {
- goodBreakFound = true;
- goodBreakIndex = i;
- goodBreakWidth = printedWidth;
- }
- } else if (p.type == kPrimitiveType_Wordbreak) {
- // only do this if necessary -- we don't want to break words
- // when possible, but sometimes it is unavoidable
- if (i > breakIndex && (printedWidth <= maxWidth || breakFound == false)) {
- breakFound = true;
- breakIndex = i;
- breakWidth = printedWidth;
- }
- }
- }
-
- if (breakFound || goodBreakFound) {
- // output last break if there are more characters to output
- if (goodBreakFound) {
- breaks->push_back(mPrimitives[goodBreakIndex].location);
- widths->push_back(goodBreakWidth);
- flags->push_back(firstTabIndex < goodBreakIndex);
- } else {
- breaks->push_back(mPrimitives[breakIndex].location);
- widths->push_back(breakWidth);
- flags->push_back(firstTabIndex < breakIndex);
- }
- }
- }
-};
-
-static jint recycleCopy(JNIEnv* env, jobject recycle, jintArray recycleBreaks,
- jfloatArray recycleWidths, jbooleanArray recycleFlags,
- jint recycleLength, const std::vector<jint>& breaks,
- const std::vector<jfloat>& widths, const std::vector<jboolean>& flags) {
- int bufferLength = breaks.size();
- if (recycleLength < bufferLength) {
+static void recycleCopy(JNIEnv* env, jobject recycle, jintArray recycleBreaks,
+ jfloatArray recycleWidths, jintArray recycleFlags,
+ jint recycleLength, size_t nBreaks, const jint* breaks,
+ const jfloat* widths, const jint* flags) {
+ if ((size_t)recycleLength < nBreaks) {
// have to reallocate buffers
- recycleBreaks = env->NewIntArray(bufferLength);
- recycleWidths = env->NewFloatArray(bufferLength);
- recycleFlags = env->NewBooleanArray(bufferLength);
+ recycleBreaks = env->NewIntArray(nBreaks);
+ recycleWidths = 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.flags, recycleFlags);
}
// copy data
- env->SetIntArrayRegion(recycleBreaks, 0, breaks.size(), &breaks.front());
- env->SetFloatArrayRegion(recycleWidths, 0, widths.size(), &widths.front());
- env->SetBooleanArrayRegion(recycleFlags, 0, flags.size(), &flags.front());
-
- return bufferLength;
-}
-
-void computePrimitives(const jchar* textArr, const jfloat* widthsArr, jint length, const std::vector<int>& breaks,
- const TabStops& tabStopCalculator, std::vector<Primitive>* primitives) {
- int breaksSize = breaks.size();
- int breakIndex = 0;
- Primitive p;
- for (int i = 0; i < length; i++) {
- p.location = i;
- jchar c = textArr[i];
- if (c == CHAR_SPACE || c == CHAR_ZWSP) {
- p.type = kPrimitiveType_Glue;
- p.width = widthsArr[i];
- primitives->push_back(p);
- } else if (c == CHAR_TAB) {
- p.type = kPrimitiveType_Variable;
- p.tabStop = &tabStopCalculator; // shared between all variable primitives
- primitives->push_back(p);
- } else if (c != CHAR_NEWLINE) {
- while (breakIndex < breaksSize && breaks[breakIndex] < i) breakIndex++;
- p.width = 0;
- if (breakIndex < breaksSize && breaks[breakIndex] == i) {
- p.type = kPrimitiveType_Penalty;
- p.penalty = 0;
- } else {
- p.type = kPrimitiveType_Wordbreak;
- }
- if (widthsArr[i] != 0) {
- primitives->push_back(p);
- }
-
- p.type = kPrimitiveType_Box;
- p.width = widthsArr[i];
- primitives->push_back(p);
- }
- }
- // final break at end of everything
- p.location = length;
- p.type = kPrimitiveType_Penalty;
- p.width = 0;
- p.penalty = -PENALTY_INFINITY;
- primitives->push_back(p);
-}
-
-// sets the text on the builder
-static void nSetText(JNIEnv* env, jclass, jlong nativePtr, jcharArray text, int length) {
- Builder* b = reinterpret_cast<Builder*>(nativePtr);
- b->resize(length);
- env->GetCharArrayRegion(text, 0, length, b->buffer());
- b->setText();
+ env->SetIntArrayRegion(recycleBreaks, 0, nBreaks, breaks);
+ env->SetFloatArrayRegion(recycleWidths, 0, nBreaks, widths);
+ env->SetIntArrayRegion(recycleFlags, 0, nBreaks, flags);
}
static jint nComputeLineBreaks(JNIEnv* env, jclass, jlong nativePtr,
- jint length,
- jfloat firstWidth, jint firstWidthLineLimit, jfloat restWidth,
- jintArray variableTabStops, jint defaultTabStop, jboolean optimize,
jobject recycle, jintArray recycleBreaks,
- jfloatArray recycleWidths, jbooleanArray recycleFlags,
+ jfloatArray recycleWidths, jintArray recycleFlags,
jint recycleLength) {
- Builder* b = reinterpret_cast<Builder*>(nativePtr);
+ LineBreaker* b = reinterpret_cast<LineBreaker*>(nativePtr);
- std::vector<int> breaks;
+ size_t nBreaks = b->computeBreaks();
- icu::BreakIterator* breakIterator = b->breakIterator();
- int loc = breakIterator->first();
- while ((loc = breakIterator->next()) != icu::BreakIterator::DONE) {
- breaks.push_back(loc);
- }
+ recycleCopy(env, recycle, recycleBreaks, recycleWidths, recycleFlags, recycleLength,
+ nBreaks, b->getBreaks(), b->getWidths(), b->getFlags());
- // TODO: all these allocations can be moved into the builder
- std::vector<Primitive> primitives;
- TabStops tabStops(env, variableTabStops, defaultTabStop);
- computePrimitives(b->buffer(), b->widths(), length, breaks, tabStops, &primitives);
-
- LineWidth lineWidth(firstWidth, firstWidthLineLimit, restWidth);
- std::vector<int> computedBreaks;
- std::vector<float> computedWidths;
- std::vector<unsigned char> computedFlags;
-
- if (optimize) {
- OptimizingLineBreaker breaker(primitives, lineWidth);
- breaker.computeBreaks(&computedBreaks, &computedWidths, &computedFlags);
- } else {
- GreedyLineBreaker breaker(primitives, lineWidth);
- breaker.computeBreaks(&computedBreaks, &computedWidths, &computedFlags);
- }
b->finish();
- return recycleCopy(env, recycle, recycleBreaks, recycleWidths, recycleFlags, recycleLength,
- computedBreaks, computedWidths, computedFlags);
+ return static_cast<jint>(nBreaks);
}
static jlong nNewBuilder(JNIEnv*, jclass) {
- return reinterpret_cast<jlong>(new Builder);
+ return reinterpret_cast<jlong>(new LineBreaker);
}
static void nFreeBuilder(JNIEnv*, jclass, jlong nativePtr) {
- delete reinterpret_cast<Builder*>(nativePtr);
+ delete reinterpret_cast<LineBreaker*>(nativePtr);
}
static void nFinishBuilder(JNIEnv*, jclass, jlong nativePtr) {
- Builder* b = reinterpret_cast<Builder*>(nativePtr);
+ LineBreaker* b = reinterpret_cast<LineBreaker*>(nativePtr);
b->finish();
}
-static void nSetLocale(JNIEnv* env, jclass, jlong nativePtr, jstring javaLocaleName) {
+static jlong nLoadHyphenator(JNIEnv* env, jclass, jstring patternData) {
+ ScopedStringChars str(env, patternData);
+ Hyphenator* hyphenator = Hyphenator::load(str.get(), str.size());
+ return reinterpret_cast<jlong>(hyphenator);
+}
+
+static void nSetLocale(JNIEnv* env, jclass, jlong nativePtr, jstring javaLocaleName,
+ jlong nativeHyphenator) {
ScopedIcuLocale icuLocale(env, javaLocaleName);
- Builder* b = reinterpret_cast<Builder*>(nativePtr);
+ LineBreaker* b = reinterpret_cast<LineBreaker*>(nativePtr);
+ Hyphenator* hyphenator = reinterpret_cast<Hyphenator*>(nativeHyphenator);
if (icuLocale.valid()) {
- b->setLocale(icuLocale.locale());
+ b->setLocale(icuLocale.locale(), hyphenator);
}
}
-float Builder::measureStyleRun(Paint* paint, TypefaceImpl* typeface, size_t start, size_t end,
- bool isRtl) {
- Layout layout;
- int bidiFlags = isRtl ? kBidi_Force_RTL : kBidi_Force_LTR;
- // TODO: should we provide more context?
- MinikinUtils::doLayout(&layout, paint, bidiFlags, typeface, mTextBuf.data() + start, 0,
- end - start, end - start);
- layout.getAdvances(mWidthBuf.data() + start);
- return layout.getAdvance();
-}
-
-void Builder::addReplacement(size_t start, size_t end, float width) {
- mWidthBuf[start] = width;
- std::fill(&mWidthBuf[start + 1], &mWidthBuf[end], 0.0f);
-}
-
// Basically similar to Paint.getTextRunAdvances but with C++ interface
static jfloat nAddStyleRun(JNIEnv* env, jclass, jlong nativePtr,
jlong nativePaint, jlong nativeTypeface, jint start, jint end, jboolean isRtl) {
- Builder* b = reinterpret_cast<Builder*>(nativePtr);
+ LineBreaker* b = reinterpret_cast<LineBreaker*>(nativePtr);
Paint* paint = reinterpret_cast<Paint*>(nativePaint);
TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(nativeTypeface);
- return b->measureStyleRun(paint, typeface, start, end, isRtl);
+ FontCollection *font;
+ MinikinPaint minikinPaint;
+ FontStyle style = MinikinUtils::prepareMinikinPaint(&minikinPaint, &font, paint, typeface);
+ return b->addStyleRun(&minikinPaint, font, style, start, end, isRtl);
}
// Accept width measurements for the run, passed in from Java
static void nAddMeasuredRun(JNIEnv* env, jclass, jlong nativePtr,
jint start, jint end, jfloatArray widths) {
- Builder* b = reinterpret_cast<Builder*>(nativePtr);
- env->GetFloatArrayRegion(widths, start, end - start, b->widths() + start);
+ LineBreaker* b = reinterpret_cast<LineBreaker*>(nativePtr);
+ env->GetFloatArrayRegion(widths, start, end - start, b->charWidths() + start);
+ b->addStyleRun(nullptr, nullptr, FontStyle{}, start, end, false);
}
static void nAddReplacementRun(JNIEnv* env, jclass, jlong nativePtr,
jint start, jint end, jfloat width) {
- Builder* b = reinterpret_cast<Builder*>(nativePtr);
+ LineBreaker* b = reinterpret_cast<LineBreaker*>(nativePtr);
b->addReplacement(start, end, width);
}
static void nGetWidths(JNIEnv* env, jclass, jlong nativePtr, jfloatArray widths) {
- Builder* b = reinterpret_cast<Builder*>(nativePtr);
- env->SetFloatArrayRegion(widths, 0, b->size(), b->widths());
+ LineBreaker* b = reinterpret_cast<LineBreaker*>(nativePtr);
+ env->SetFloatArrayRegion(widths, 0, b->size(), b->charWidths());
}
static JNINativeMethod gMethods[] = {
@@ -683,13 +168,14 @@
{"nNewBuilder", "()J", (void*) nNewBuilder},
{"nFreeBuilder", "(J)V", (void*) nFreeBuilder},
{"nFinishBuilder", "(J)V", (void*) nFinishBuilder},
- {"nSetLocale", "(JLjava/lang/String;)V", (void*) nSetLocale},
- {"nSetText", "(J[CI)V", (void*) nSetText},
+ {"nLoadHyphenator", "(Ljava/lang/String;)J", (void*) nLoadHyphenator},
+ {"nSetLocale", "(JLjava/lang/String;J)V", (void*) nSetLocale},
+ {"nSetupParagraph", "(J[CIFIF[III)V", (void*) nSetupParagraph},
{"nAddStyleRun", "(JJJIIZ)F", (void*) nAddStyleRun},
{"nAddMeasuredRun", "(JII[F)V", (void*) nAddMeasuredRun},
{"nAddReplacementRun", "(JIIF)V", (void*) nAddReplacementRun},
{"nGetWidths", "(J[F)V", (void*) nGetWidths},
- {"nComputeLineBreaks", "(JIFIF[IIZLandroid/text/StaticLayout$LineBreaks;[I[F[ZI)I",
+ {"nComputeLineBreaks", "(JLandroid/text/StaticLayout$LineBreaks;[I[F[II)I",
(void*) nComputeLineBreaks}
};
@@ -700,7 +186,7 @@
gLineBreaks_fieldID.breaks = GetFieldIDOrDie(env, gLineBreaks_class, "breaks", "[I");
gLineBreaks_fieldID.widths = GetFieldIDOrDie(env, gLineBreaks_class, "widths", "[F");
- gLineBreaks_fieldID.flags = GetFieldIDOrDie(env, gLineBreaks_class, "flags", "[Z");
+ gLineBreaks_fieldID.flags = GetFieldIDOrDie(env, gLineBreaks_class, "flags", "[I");
return RegisterMethodsOrDie(env, "android/text/StaticLayout", gMethods, NELEM(gMethods));
}
diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp
index fff8604..f1c90ea 100644
--- a/core/jni/android_view_Surface.cpp
+++ b/core/jni/android_view_Surface.cpp
@@ -192,6 +192,9 @@
case HAL_PIXEL_FORMAT_YCbCr_422_I:
// Name differs, though the value is the same
return PublicFormat::YUY2;
+ case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED:
+ // Name differs, though the value is the same
+ return PublicFormat::PRIVATE;
case HAL_PIXEL_FORMAT_Y16:
// Dataspace-dependent
switch (dataSpace) {
@@ -216,7 +219,6 @@
break;
case HAL_PIXEL_FORMAT_BGRA_8888:
case HAL_PIXEL_FORMAT_RAW_OPAQUE:
- case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED:
// Not defined in public API
return PublicFormat::UNKNOWN;
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index 3d9a9ed..11b3805 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -21,6 +21,7 @@
#include "jni.h"
#include <nativehelper/JNIHelp.h>
#include "core_jni_helpers.h"
+#include <ScopedPrimitiveArray.h>
#include <EGL/egl.h>
#include <EGL/eglext.h>
@@ -35,6 +36,7 @@
#include <Animator.h>
#include <AnimationContext.h>
#include <IContextFactory.h>
+#include <JankTracker.h>
#include <RenderNode.h>
#include <renderthread/CanvasContext.h>
#include <renderthread/RenderProxy.h>
@@ -219,6 +221,12 @@
proxy->setTextureAtlas(buffer, map, len);
}
+static void android_view_ThreadedRenderer_setProcessStatsBuffer(JNIEnv* env, jobject clazz,
+ jlong proxyPtr, jint fd) {
+ RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
+ proxy->setProcessStatsBuffer(fd);
+}
+
static jlong android_view_ThreadedRenderer_createRootRenderNode(JNIEnv* env, jobject clazz) {
RootRenderNode* node = new RootRenderNode(env);
node->incStrong(0);
@@ -403,6 +411,16 @@
proxy->dumpProfileInfo(fd, dumpFlags);
}
+static void android_view_ThreadedRenderer_dumpProfileData(JNIEnv* env, jobject clazz,
+ jbyteArray jdata, jobject javaFileDescriptor) {
+ int fd = jniGetFDFromFileDescriptor(env, javaFileDescriptor);
+ ScopedByteArrayRO buffer(env, jdata);
+ if (buffer.get()) {
+ JankTracker::dumpBuffer(buffer.get(), buffer.size(), fd);
+ }
+}
+
+
// ----------------------------------------------------------------------------
// Shaders
// ----------------------------------------------------------------------------
@@ -423,6 +441,7 @@
static JNINativeMethod gMethods[] = {
{ "nSetAtlas", "(JLandroid/view/GraphicBuffer;[J)V", (void*) android_view_ThreadedRenderer_setAtlas },
+ { "nSetProcessStatsBuffer", "(JI)V", (void*) android_view_ThreadedRenderer_setProcessStatsBuffer },
{ "nCreateRootRenderNode", "()J", (void*) android_view_ThreadedRenderer_createRootRenderNode },
{ "nCreateProxy", "(ZJ)J", (void*) android_view_ThreadedRenderer_createProxy },
{ "nDeleteProxy", "(J)V", (void*) android_view_ThreadedRenderer_deleteProxy },
@@ -449,6 +468,7 @@
{ "nStopDrawing", "(J)V", (void*) android_view_ThreadedRenderer_stopDrawing },
{ "nNotifyFramePending", "(J)V", (void*) android_view_ThreadedRenderer_notifyFramePending },
{ "nDumpProfileInfo", "(JLjava/io/FileDescriptor;I)V", (void*) android_view_ThreadedRenderer_dumpProfileInfo },
+ { "nDumpProfileData", "([BLjava/io/FileDescriptor;)V", (void*) android_view_ThreadedRenderer_dumpProfileData },
{ "setupShadersDiskCache", "(Ljava/lang/String;)V",
(void*) android_view_ThreadedRenderer_setupShadersDiskCache },
};
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 2bfeadb..76db5d3 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -65,9 +65,7 @@
// Must match values in com.android.internal.os.Zygote.
enum MountExternalKind {
MOUNT_EXTERNAL_NONE = 0,
- MOUNT_EXTERNAL_SINGLEUSER = 1,
- MOUNT_EXTERNAL_MULTIUSER = 2,
- MOUNT_EXTERNAL_MULTIUSER_ALL = 3,
+ MOUNT_EXTERNAL_DEFAULT = 1,
};
static void RuntimeAbort(JNIEnv* env) {
@@ -269,57 +267,16 @@
// See storage config details at http://source.android.com/tech/storage/
userid_t user_id = multiuser_get_user_id(uid);
- // Create bind mounts to expose external storage
- if (mount_mode == MOUNT_EXTERNAL_MULTIUSER || mount_mode == MOUNT_EXTERNAL_MULTIUSER_ALL) {
- // These paths must already be created by init.rc
- const char* source = getenv("EMULATED_STORAGE_SOURCE");
- const char* target = getenv("EMULATED_STORAGE_TARGET");
- const char* legacy = getenv("EXTERNAL_STORAGE");
- if (source == NULL || target == NULL || legacy == NULL) {
- ALOGW("Storage environment undefined; unable to provide external storage");
- return false;
- }
+ // Bind mount user-specific storage into place
+ const String8 source(String8::format("/mnt/user/%d", user_id));
+ const String8 target(String8::format("/storage/self"));
- // Prepare source paths
+ if (fs_prepare_dir(source.string(), 0755, 0, 0) == -1) {
+ return false;
+ }
- // /mnt/shell/emulated/0
- const String8 source_user(String8::format("%s/%d", source, user_id));
- // /storage/emulated/0
- const String8 target_user(String8::format("%s/%d", target, user_id));
-
- if (fs_prepare_dir(source_user.string(), 0000, 0, 0) == -1
- || fs_prepare_dir(target_user.string(), 0000, 0, 0) == -1) {
- return false;
- }
-
- if (mount_mode == MOUNT_EXTERNAL_MULTIUSER_ALL) {
- // Mount entire external storage tree for all users
- if (TEMP_FAILURE_RETRY(mount(source, target, NULL, MS_BIND, NULL)) == -1) {
- ALOGW("Failed to mount %s to %s: %s", source, target, strerror(errno));
- return false;
- }
- } else {
- // Only mount user-specific external storage
- if (TEMP_FAILURE_RETRY(mount(source_user.string(), target_user.string(), NULL,
- MS_BIND, NULL)) == -1) {
- ALOGW("Failed to mount %s to %s: %s", source_user.string(), target_user.string(),
- strerror(errno));
- return false;
- }
- }
-
- if (fs_prepare_dir(legacy, 0000, 0, 0) == -1) {
- return false;
- }
-
- // Finally, mount user-specific path into place for legacy users
- if (TEMP_FAILURE_RETRY(
- mount(target_user.string(), legacy, NULL, MS_BIND | MS_REC, NULL)) == -1) {
- ALOGW("Failed to mount %s to %s: %s", target_user.string(), legacy, strerror(errno));
- return false;
- }
- } else {
- ALOGW("Mount mode %d unsupported", mount_mode);
+ if (TEMP_FAILURE_RETRY(mount(source.string(), target.string(), NULL, MS_BIND, NULL)) == -1) {
+ ALOGW("Failed to mount %s to %s: %s", source.string(), target.string(), strerror(errno));
return false;
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 851c4bf..3796c9c 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -73,6 +73,7 @@
<protected-broadcast android:name="android.intent.action.USER_BACKGROUND" />
<protected-broadcast android:name="android.intent.action.USER_FOREGROUND" />
<protected-broadcast android:name="android.intent.action.USER_SWITCHED" />
+ <protected-broadcast android:name="android.intent.action.INTENT_FILTER_NEEDS_VERIFICATION" />
<protected-broadcast android:name="android.os.action.POWER_SAVE_MODE_CHANGED" />
<protected-broadcast android:name="android.os.action.POWER_SAVE_MODE_CHANGING" />
@@ -2799,6 +2800,23 @@
android:description="@string/permdesc_bindPackageVerifier"
android:protectionLevel="signature" />
+ <!-- @SystemApi @hide Intent filter verifier needs to have this permission before the
+ PackageManager will trust it to verify intent filters.
+ -->
+ <permission android:name="android.permission.INTENT_FILTER_VERIFICATION_AGENT"
+ android:label="@string/permlab_intentFilterVerificationAgent"
+ android:description="@string/permdesc_intentFilterVerificationAgent"
+ android:protectionLevel="signature|system" />
+
+ <!-- Must be required by intent filter verifier receiver, to ensure that only the
+ system can interact with it.
+ @hide
+ -->
+ <permission android:name="android.permission.BIND_INTENT_FILTER_VERIFIER"
+ android:label="@string/permlab_bindIntentFilterVerifier"
+ android:description="@string/permdesc_bindIntentFilterVerifier"
+ android:protectionLevel="signature" />
+
<!-- @SystemApi Allows applications to access serial ports via the SerialManager.
@hide -->
<permission android:name="android.permission.SERIAL_PORT"
@@ -3161,9 +3179,9 @@
</intent-filter>
</receiver>
- <receiver android:name="com.android.server.updates.TZInfoInstallReceiver" >
+ <receiver android:name="com.android.server.updates.TzDataInstallReceiver" >
<intent-filter>
- <action android:name="android.intent.action.UPDATE_TZINFO" />
+ <action android:name="android.intent.action.UPDATE_TZDATA" />
<data android:scheme="content" android:host="*" android:mimeType="*/*" />
</intent-filter>
</receiver>
diff --git a/core/res/res/color/primary_text_secondary_when_activated_material.xml b/core/res/res/color/primary_text_secondary_when_activated_material.xml
new file mode 100644
index 0000000..7ab4a2e
--- /dev/null
+++ b/core/res/res/color/primary_text_secondary_when_activated_material.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item
+ android:state_activated="true"
+ android:color="?attr/textColorPrimary" />
+ <item
+ android:color="?attr/textColorSecondary" />
+</selector>
\ No newline at end of file
diff --git a/core/res/res/color/date_picker_header_text_material.xml b/core/res/res/color/primary_text_secondary_when_activated_material_inverse.xml
similarity index 100%
rename from core/res/res/color/date_picker_header_text_material.xml
rename to core/res/res/color/primary_text_secondary_when_activated_material_inverse.xml
diff --git a/core/res/res/color/time_picker_header_text_material.xml b/core/res/res/color/time_picker_header_text_material.xml
deleted file mode 100644
index cda894b..0000000
--- a/core/res/res/color/time_picker_header_text_material.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item
- android:state_selected="true"
- android:color="?attr/textColorPrimaryInverse" />
- <item
- android:color="?attr/textColorSecondaryInverse" />
-</selector>
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_chevron_left.xml b/core/res/res/drawable/ic_chevron_left.xml
new file mode 100644
index 0000000..dc24706
--- /dev/null
+++ b/core/res/res/drawable/ic_chevron_left.xml
@@ -0,0 +1,25 @@
+<!--
+ Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="?attr/colorControlNormal">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M15.41 7.41L14 6l-6 6 6 6 1.41,-1.41L10.83 12z"/>
+</vector>
diff --git a/core/res/res/drawable/ic_chevron_right.xml b/core/res/res/drawable/ic_chevron_right.xml
new file mode 100644
index 0000000..4e6d8e3
--- /dev/null
+++ b/core/res/res/drawable/ic_chevron_right.xml
@@ -0,0 +1,25 @@
+<!--
+ Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="?attr/colorControlNormal">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6,-6z"/>
+</vector>
diff --git a/core/res/res/layout-land/time_picker_material.xml b/core/res/res/layout-land/time_picker_material.xml
index 1b85e8f..89c3749 100644
--- a/core/res/res/layout-land/time_picker_material.xml
+++ b/core/res/res/layout-land/time_picker_material.xml
@@ -53,6 +53,7 @@
android:id="@+id/hours"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:textAppearance="@style/TextAppearance.Material.TimePicker.TimeLabel"
android:singleLine="true"
android:ellipsize="none"
android:gravity="right"
@@ -64,6 +65,7 @@
android:id="@+id/separator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:textAppearance="@style/TextAppearance.Material.TimePicker.TimeLabel"
android:importantForAccessibility="no"
tools:text=":"
tools:textSize="@dimen/timepicker_time_label_size"
@@ -75,6 +77,7 @@
android:id="@+id/minutes"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:textAppearance="@style/TextAppearance.Material.TimePicker.TimeLabel"
android:singleLine="true"
android:ellipsize="none"
android:gravity="left"
@@ -97,6 +100,7 @@
android:id="@+id/am_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:textAppearance="@style/TextAppearance.Material.TimePicker.AmPmLabel"
android:paddingStart="@dimen/timepicker_ampm_horizontal_padding"
android:paddingEnd="@dimen/timepicker_ampm_horizontal_padding"
android:paddingTop="@dimen/timepicker_am_top_padding"
@@ -111,6 +115,7 @@
android:id="@+id/pm_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:textAppearance="@style/TextAppearance.Material.TimePicker.AmPmLabel"
android:paddingStart="@dimen/timepicker_ampm_horizontal_padding"
android:paddingEnd="@dimen/timepicker_ampm_horizontal_padding"
android:paddingTop="@dimen/timepicker_pm_top_padding"
diff --git a/core/res/res/layout/date_picker_month_item_material.xml b/core/res/res/layout/date_picker_month_item_material.xml
new file mode 100644
index 0000000..cb79cee
--- /dev/null
+++ b/core/res/res/layout/date_picker_month_item_material.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<android.widget.SimpleMonthView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/month_view"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingStart="@dimen/day_picker_padding_horizontal"
+ android:paddingEnd="@dimen/day_picker_padding_horizontal"
+ android:paddingTop="@dimen/day_picker_padding_top" />
diff --git a/core/res/res/layout/date_picker_view_animator_material.xml b/core/res/res/layout/date_picker_view_animator_material.xml
index 98ef1dd..620ddfa 100644
--- a/core/res/res/layout/date_picker_view_animator_material.xml
+++ b/core/res/res/layout/date_picker_view_animator_material.xml
@@ -26,9 +26,8 @@
android:id="@+id/date_picker_day_picker"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:paddingStart="@dimen/day_picker_padding_horizontal"
- android:paddingEnd="@dimen/day_picker_padding_horizontal"
- android:paddingTop="@dimen/day_picker_padding_top" />
+ android:inAnimation="@anim/fade_in"
+ android:outAnimation="@anim/fade_out" />
<android.widget.YearPickerView
android:id="@+id/date_picker_year_picker"
diff --git a/core/res/res/layout/floating_popup_container.xml b/core/res/res/layout/floating_popup_container.xml
new file mode 100644
index 0000000..f247919
--- /dev/null
+++ b/core/res/res/layout/floating_popup_container.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* Copyright 2015, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="horizontal"
+ android:layout_width="wrap_content"
+ android:layout_height="@dimen/floating_toolbar_height"
+ android:elevation="2dp"
+ android:focusable="true"
+ android:focusableInTouchMode="true"
+ android:background="@android:color/background_light" />
diff --git a/core/res/res/layout/floating_popup_menu_button.xml b/core/res/res/layout/floating_popup_menu_button.xml
new file mode 100644
index 0000000..9fa13bd
--- /dev/null
+++ b/core/res/res/layout/floating_popup_menu_button.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* Copyright 2015, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<Button xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:minWidth="@dimen/floating_toolbar_menu_button_side_padding"
+ android:paddingLeft="@dimen/floating_toolbar_menu_button_side_padding"
+ android:paddingRight="@dimen/floating_toolbar_menu_button_side_padding"
+ android:paddingTop="0dp"
+ android:paddingBottom="0dp"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:fontFamily="sans-serif"
+ android:textSize="@dimen/floating_toolbar_text_size"
+ android:textAllCaps="true"
+ android:background="?attr/selectableItemBackground" />
\ No newline at end of file
diff --git a/core/res/res/layout/floating_popup_open_overflow_button.xml b/core/res/res/layout/floating_popup_open_overflow_button.xml
new file mode 100644
index 0000000..4c1176c
--- /dev/null
+++ b/core/res/res/layout/floating_popup_open_overflow_button.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* Copyright 2015, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<ImageButton xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="@dimen/floating_toolbar_menu_button_minimum_width"
+ android:layout_height="match_parent"
+ android:minWidth="@dimen/floating_toolbar_menu_button_minimum_width"
+ android:minHeight="@dimen/floating_toolbar_height"
+ android:src="@drawable/ic_menu_moreoverflow_material"
+ android:contentDescription="@string/action_menu_overflow_description"
+ android:background="?attr/selectableItemBackgroundBorderless" />
diff --git a/core/res/res/layout/time_picker_header_material.xml b/core/res/res/layout/time_picker_header_material.xml
index 0ef404d..be9e443 100644
--- a/core/res/res/layout/time_picker_header_material.xml
+++ b/core/res/res/layout/time_picker_header_material.xml
@@ -15,6 +15,8 @@
~ limitations under the License
-->
+<!-- This layout is duplicated in land/time_picker_material.xml, so any
+ changes made here need to be manually copied over. -->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/time_header"
@@ -32,6 +34,7 @@
android:layout_height="wrap_content"
android:layout_toLeftOf="@+id/separator"
android:layout_alignBaseline="@+id/separator"
+ android:textAppearance="@style/TextAppearance.Material.TimePicker.TimeLabel"
android:singleLine="true"
android:ellipsize="none"
android:gravity="right"
@@ -46,6 +49,7 @@
android:layout_marginLeft="@dimen/timepicker_separator_padding"
android:layout_marginRight="@dimen/timepicker_separator_padding"
android:layout_centerInParent="true"
+ android:textAppearance="@style/TextAppearance.Material.TimePicker.TimeLabel"
android:importantForAccessibility="no"
tools:text=":"
tools:textSize="@dimen/timepicker_time_label_size"
@@ -59,6 +63,7 @@
android:layout_height="wrap_content"
android:layout_toRightOf="@+id/separator"
android:layout_alignBaseline="@+id/separator"
+ android:textAppearance="@style/TextAppearance.Material.TimePicker.TimeLabel"
android:singleLine="true"
android:ellipsize="none"
android:gravity="left"
@@ -83,6 +88,7 @@
android:paddingStart="@dimen/timepicker_ampm_horizontal_padding"
android:paddingEnd="@dimen/timepicker_ampm_horizontal_padding"
android:paddingTop="@dimen/timepicker_am_top_padding"
+ android:textAppearance="@style/TextAppearance.Material.TimePicker.AmPmLabel"
android:lines="1"
android:ellipsize="none"
tools:text="AM"
@@ -95,6 +101,7 @@
android:paddingStart="@dimen/timepicker_ampm_horizontal_padding"
android:paddingEnd="@dimen/timepicker_ampm_horizontal_padding"
android:paddingTop="@dimen/timepicker_pm_top_padding"
+ android:textAppearance="@style/TextAppearance.Material.TimePicker.AmPmLabel"
android:lines="1"
android:ellipsize="none"
tools:text="PM"
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index aed69ba..44db8cc 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -126,6 +126,7 @@
<string name="wfcRegErrorTitle" msgid="2301376280632110664">"Wi-Fi-oproepe"</string>
<string-array name="wfcOperatorErrorMessages">
</string-array>
+ <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
<string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Nie aangestuur nie"</string>
<string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> na <xliff:g id="TIME_DELAY">{2}</xliff:g> sekondes"</string>
@@ -746,6 +747,22 @@
<string name="permdesc_manageFingerprint" msgid="178208705828055464">"Laat die program toe om metodes te benut om vingerafdruksjablone vir gebruik by te voeg en uit te vee."</string>
<string name="permlab_useFingerprint" msgid="3150478619915124905">"gebruik vingerafdrukhardeware"</string>
<string name="permdesc_useFingerprint" msgid="9165097460730684114">"Laat die program toe om vingerafdrukhardeware vir stawing te gebruik"</string>
+ <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Gedeeltelike vingerafdruk is bespeur. Probeer asseblief weer."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Kon nie vingerafdruk verwerk nie. Probeer asseblief weer."</string>
+ <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Vingerafdruksensor is vuil. Maak dit skoon en probeer weer."</string>
+ <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"Vinger is te vinnig beweeg. Probeer asseblief weer."</string>
+ <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"Vinger is te stadig beweeg. Probeer asseblief weer."</string>
+ <string-array name="fingerprint_acquired_vendor">
+ <item msgid="2892952818207766996">"Handelaarspesifieke aankoopfoutboodskap 0"</item>
+ </string-array>
+ <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"Kon nie verwerk nie. Probeer weer."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"Hardeware is nie beskikbaar nie."</string>
+ <string name="fingerprint_error_no_space" msgid="1055819001126053318">"Vingerafdruk kan nie geberg word nie. Verwyder asseblief \'n bestaande vingerafdruk."</string>
+ <string name="fingerprint_error_timeout" msgid="3927186043737732875">"Vingerafdrukuittelling is bereik. Probeer weer."</string>
+ <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Vingerafdrukuittelling is bereik. Probeer weer."</string>
+ <string-array name="fingerprint_error_vendor">
+ <item msgid="5804600450373644614">"Handelaarspesifieke foutboodskap."</item>
+ </string-array>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"lees sinkroniseer-instellings"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Laat die program toe om die sinkroniseringinstellings van \'n rekening te lees. Byvoorbeeld, dit kan bepaal of die People-program met \'n rekening gesinkroniseer is."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"wissel tussen sinkronisasie aan en af"</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index f6b38ad..be3a864 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -126,6 +126,7 @@
<string name="wfcRegErrorTitle" msgid="2301376280632110664">"የWi-Fi ጥሪ ማድረጊያ"</string>
<string-array name="wfcOperatorErrorMessages">
</string-array>
+ <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
<string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>፡አልተላለፈም"</string>
<string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>፡<xliff:g id="DIALING_NUMBER">{1}</xliff:g> ከ<xliff:g id="TIME_DELAY">{2}</xliff:g> ሰከንዶች በኋላ"</string>
@@ -746,6 +747,22 @@
<string name="permdesc_manageFingerprint" msgid="178208705828055464">"መተግበሪያው ጥቅም ላይ እንዲውሉ የጣት አሻራ ቅንብር ደንቦችን ለማከል እና ለመሰረዝ የሚያስችሉ ስልቶችን እንዲያስጀምር ያስችለዋል።"</string>
<string name="permlab_useFingerprint" msgid="3150478619915124905">"የጣት አሻራ ሃርድዌርን ተጠቀም"</string>
<string name="permdesc_useFingerprint" msgid="9165097460730684114">"መተግበሪያው የጣት አሻራ ሃርድዌር ለማረጋገጥ ስራ እንዲጠቀም ያስችለዋል"</string>
+ <string name="fingerprint_acquired_partial" msgid="735082772341716043">"ከፊል የጣት አሻራ ተገኝቷል። እባክዎ እንደገና ይሞክሩ።"</string>
+ <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"ጣት አሻራን መስራት አልተቻለም። እባክዎ እንደገና ይሞክሩ።"</string>
+ <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"የጣት አሻራ ዳሳሽ ቆሽሿል። እባክዎ ያጽዱት እና እንደገና ይሞክሩ።"</string>
+ <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"ጣት በጣም በፍጥነት ተንቀሳቅሷል። እባክዎ እንደገና ይሞክሩ።"</string>
+ <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"ጣት በጣም በዝግታ ተንቀሳቅሷል። እባክዎ እንደገና ይሞክሩ።"</string>
+ <string-array name="fingerprint_acquired_vendor">
+ <item msgid="2892952818207766996">"አቅራቢ-ተኮር ግዢ የስህተት መልዕክት 0"</item>
+ </string-array>
+ <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"ሂደትን ማከናወን አልተቻለም። እንደገና ይሞክሩ።"</string>
+ <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"ሃርድዌር አይገኝም።"</string>
+ <string name="fingerprint_error_no_space" msgid="1055819001126053318">"የጣት አሻራ ሊከማች አይችልም። እባክዎ አሁን ያለውን የጣት አሻራ ያስወግዱ።"</string>
+ <string name="fingerprint_error_timeout" msgid="3927186043737732875">"የጣት አሻራ ማብቂያ ጊዜ ደርሷል። እንደገና ይሞክሩ።"</string>
+ <string name="fingerprint_error_vendor" msgid="3175724710791609491">"የጣት አሻራ ማብቂያ ጊዜ ደርሷል። እንደገና ይሞክሩ።"</string>
+ <string-array name="fingerprint_error_vendor">
+ <item msgid="5804600450373644614">"አቅራቢ-ተኮር የስህተት መልዕክት"</item>
+ </string-array>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"የሥምሪያ ቅንብሮች አንብብ"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"መተግበሪያው የአንድ መለያ የማመሳሰል ቅንብሮችን እንዲያነብ ይፈቅድለታል። ለምሳሌ ይህ የሰዎች መተግበሪያ ከመለያ ጋር መመሳሰሉን አለመመሳሰሉን ሊወስን ይችላል።"</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"ማመሳሰያ በማብራትና በማጥፋት መካከል ቀያይር"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index ddb0e4d..a451930 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -130,6 +130,7 @@
<string name="wfcRegErrorTitle" msgid="2301376280632110664">"الاتصال عبر Wi-Fi"</string>
<string-array name="wfcOperatorErrorMessages">
</string-array>
+ <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
<string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: لم تتم إعادة التوجيه"</string>
<string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> بعد <xliff:g id="TIME_DELAY">{2}</xliff:g> ثانية"</string>
@@ -750,6 +751,22 @@
<string name="permdesc_manageFingerprint" msgid="178208705828055464">"للسماح للتطبيق باستدعاء طرق لإضافة نماذج من بصمات الأصابع وحذفها."</string>
<string name="permlab_useFingerprint" msgid="3150478619915124905">"لاستخدام أجهزة بصمة الإصبع"</string>
<string name="permdesc_useFingerprint" msgid="9165097460730684114">"للسماح للتطبيق باستخدام أجهزة بصمة الإصبع للمصادقة"</string>
+ <string name="fingerprint_acquired_partial" msgid="735082772341716043">"تم اكتشاف بصمة الإصبع بشكل جزئي؛ يرجى إعادة المحاولة."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"تعذرت معالجة بصمة الإصبع. يُرجى إعادة المحاولة."</string>
+ <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"جهاز استشعار بصمات الأصابع متسخ، يرجى تنظيفه وإعادة المحاولة."</string>
+ <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"تحرك الإصبع بسرعة كبيرة جدًا؛ يرجى إعادة المحاولة."</string>
+ <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"تحرك الإصبع ببطء شديد جدًا؛ يرجى إعادة المحاولة."</string>
+ <string-array name="fingerprint_acquired_vendor">
+ <item msgid="2892952818207766996">"رسالة الخطأ 0 التي حددها المورّد بشأن الاكتساب"</item>
+ </string-array>
+ <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"تعذرت المعالجة؛ أعد المحاولة."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"الجهاز غير متاح."</string>
+ <string name="fingerprint_error_no_space" msgid="1055819001126053318">"يتعذر تخزين بصمة الإصبع؛ يرجى إزالة إحدى البصمات المخزنة."</string>
+ <string name="fingerprint_error_timeout" msgid="3927186043737732875">"تم بلوغ مهلة إدخال بصمة الإصبع. أعد المحاولة."</string>
+ <string name="fingerprint_error_vendor" msgid="3175724710791609491">"تم بلوغ مهلة إدخال بصمة الإصبع. أعد المحاولة."</string>
+ <string-array name="fingerprint_error_vendor">
+ <item msgid="5804600450373644614">"رسالة الخطأ التي حددها المورّد."</item>
+ </string-array>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"قراءة إعدادات المزامنة"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"للسماح للتطبيق بقراءة الإعدادات المتزامنة لحساب ما. على سبيل المثال، يمكن أن يؤدي هذا إلى تحديد ما إذا تمت مزامنة تطبيق \"الأشخاص\" مع حساب ما."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"التبديل بين تشغيل المزامنة وإيقافها"</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index efae80e..fa5b464 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -126,6 +126,7 @@
<string name="wfcRegErrorTitle" msgid="2301376280632110664">"Обаждания през Wi-Fi"</string>
<string-array name="wfcOperatorErrorMessages">
</string-array>
+ <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
<string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Не е пренасочено"</string>
<string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> след <xliff:g id="TIME_DELAY">{2}</xliff:g> секунди"</string>
@@ -746,6 +747,22 @@
<string name="permdesc_manageFingerprint" msgid="178208705828055464">"Разрешава на приложението да извиква начини за добавяне и изтриване на шаблони за отпечатъци, които да се използват."</string>
<string name="permlab_useFingerprint" msgid="3150478619915124905">"използване на хардуера за отпечатъци"</string>
<string name="permdesc_useFingerprint" msgid="9165097460730684114">"Разрешава на приложението да използва хардуера за отпечатъци с цел удостоверяване"</string>
+ <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Открит е частичен отпечатък. Моля, опитайте отново."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Отпечатъкът не можа да се обработи. Моля, опитайте отново."</string>
+ <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Сензорът за отпечатъци е мръсен. Моля, почистете го и опитайте отново."</string>
+ <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"Преместихте пръста си твърде бързо. Моля, опитайте отново."</string>
+ <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"Преместихте пръста си твърде бавно. Моля, опитайте отново."</string>
+ <string-array name="fingerprint_acquired_vendor">
+ <item msgid="2892952818207766996">"Съобщение за грешка 0 при придобиване от конкретен доставчик"</item>
+ </string-array>
+ <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"Не може да се обработи. Опитайте отново."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"Няма достъп до хардуера."</string>
+ <string name="fingerprint_error_no_space" msgid="1055819001126053318">"Отпечатъкът не може да бъде съхранен. Моля, премахнете съществуващ."</string>
+ <string name="fingerprint_error_timeout" msgid="3927186043737732875">"Времето за изчакване за отпечатък изтече. Опитайте отново."</string>
+ <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Времето за изчакване за отпечатък изтече. Опитайте отново."</string>
+ <string-array name="fingerprint_error_vendor">
+ <item msgid="5804600450373644614">"Съобщение за грешка от конкретен доставчик."</item>
+ </string-array>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"четене на настройките за синхронизиране"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Разрешава на приложението да чете настройките за синхронизиране на профил. Например това може да определи дали приложението Хора е синхронизирано с даден профил."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"включване и изключване на синхронизирането"</string>
diff --git a/core/res/res/values-bn-rBD/strings.xml b/core/res/res/values-bn-rBD/strings.xml
index c47a502..b29768d 100644
--- a/core/res/res/values-bn-rBD/strings.xml
+++ b/core/res/res/values-bn-rBD/strings.xml
@@ -126,6 +126,7 @@
<string name="wfcRegErrorTitle" msgid="2301376280632110664">"Wi-Fi কলিং"</string>
<string-array name="wfcOperatorErrorMessages">
</string-array>
+ <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
<string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: ফরওয়ার্ড করা হয়নি"</string>
<string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> <xliff:g id="TIME_DELAY">{2}</xliff:g> সেকেন্ড পরে"</string>
@@ -746,6 +747,22 @@
<string name="permdesc_manageFingerprint" msgid="178208705828055464">"ব্যবহার করার জন্য আঙ্গুলের ছাপের টেম্প্লেটগুলি যোগ করা এবং মোছার পদ্ধতিগুলি গ্রহন করতে অ্যাপ্লিকেশানটিতে অমুমতি দেয়৷"</string>
<string name="permlab_useFingerprint" msgid="3150478619915124905">"আঙ্গুলের ছাপ নেওয়ার হার্ডওয়্যার ব্যবহার করুন"</string>
<string name="permdesc_useFingerprint" msgid="9165097460730684114">"অনুমোদনের জন্য আঙ্গুলের ছাপ নেওয়ার হার্ডওয়্যার ব্যবহার করতে অ্যাপ্লিকেশানটিতে অনুমতি দেয়"</string>
+ <string name="fingerprint_acquired_partial" msgid="735082772341716043">"আঙ্গুলের ছাপ আংশিক সনাক্ত করা হয়েছে৷ অনুগ্রহ করে আবার চেষ্টা করুন৷"</string>
+ <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"আঙ্গুলের ছাপ প্রক্রিয়া করা যায়নি৷ অনুগ্রহ করে আবার চেষ্টা করুন৷"</string>
+ <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"আঙ্গুলের ছাপ নেওয়ার সেন্সরটি অপরিস্কার৷ অনুগ্রহ করে পরিষ্কার করে আবার চেষ্টা করুন৷"</string>
+ <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"আঙ্গুল অতি দ্রুত সরানো হয়েছে৷ অনুগ্রহ করে আবার চেষ্টা করুন৷"</string>
+ <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"আঙ্গুল ধীরে সরানো হয়েছে৷ অনুগ্রহ করে আবার চেষ্টা করুন৷"</string>
+ <string-array name="fingerprint_acquired_vendor">
+ <item msgid="2892952818207766996">"বিক্রেতা-নির্দিষ্ট অর্জনে ত্রুটি বার্তা ০"</item>
+ </string-array>
+ <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"প্রক্রিয়া করতে অক্ষম হয়েছে৷ আবার চেষ্টা করুন৷"</string>
+ <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"হার্ডওয়্যার অনুপলব্ধ৷"</string>
+ <string name="fingerprint_error_no_space" msgid="1055819001126053318">"আঙ্গুলের ছাপ সংরক্ষণ করা যাবে না৷ অনুগ্রহ করে একটি বিদ্যমান আঙ্গুলের ছাপ সরান৷"</string>
+ <string name="fingerprint_error_timeout" msgid="3927186043737732875">"আঙ্গুলের ছাপ নেওয়ার সময়সীমা শেষ হযেছে৷ আবার চেষ্টা করুন৷"</string>
+ <string name="fingerprint_error_vendor" msgid="3175724710791609491">"আঙ্গুলের ছাপ নেওয়ার সময়সীমা শেষ হযেছে৷ আবার চেষ্টা করুন৷"</string>
+ <string-array name="fingerprint_error_vendor">
+ <item msgid="5804600450373644614">"বিক্রেতা-নির্দিষ্ট ত্রুটি বার্তা৷"</item>
+ </string-array>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"সিঙ্ক সেটিংস পড়ে"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"অ্যাপ্লিকেশানটিকে একটি অ্যাকাউন্টের জন্য সিঙ্ক সেটিংস পড়ার অনুমতি দেয়৷ উদাহরণস্বরূপ, \'পিপল\' অ্যাপ্লিকেশানটি কোনো অ্যাকাউন্টের সাথে সিঙ্ক করা আছে কিনা তা নির্ধারণ করতে পারে৷"</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"সমন্বয় চালু এবং বন্ধ করা টগল করুন"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index ed964e9..6a0066a 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -126,6 +126,7 @@
<string name="wfcRegErrorTitle" msgid="2301376280632110664">"Trucades per Wi-Fi"</string>
<string-array name="wfcOperatorErrorMessages">
</string-array>
+ <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
<string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: no s\'ha desviat"</string>
<string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> després de <xliff:g id="TIME_DELAY">{2}</xliff:g> segons"</string>
@@ -746,6 +747,22 @@
<string name="permdesc_manageFingerprint" msgid="178208705828055464">"Permet que l\'aplicació invoqui mètodes per afegir i suprimir plantilles d\'empremtes digitals que es puguin fer servir."</string>
<string name="permlab_useFingerprint" msgid="3150478619915124905">"Utilitzar el maquinari d\'empremtes digitals"</string>
<string name="permdesc_useFingerprint" msgid="9165097460730684114">"Permet que l\'aplicació faci servir maquinari d\'empremtes digitals per a l\'autenticació"</string>
+ <string name="fingerprint_acquired_partial" msgid="735082772341716043">"S\'ha detectat una empremta digital parcial. Torna-ho a provar."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"No s\'ha pogut processar l\'empremta digital. Torna-ho a provar."</string>
+ <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"El sensor d\'empremtes digitals està brut. Neteja\'l i torna-ho a provar."</string>
+ <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"El dit s\'ha mogut massa ràpid. Torna-ho a provar."</string>
+ <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"El dit s\'ha mogut massa lentament. Torna-ho a provar."</string>
+ <string-array name="fingerprint_acquired_vendor">
+ <item msgid="2892952818207766996">"Missatge d\'error d\'adquisició 0 específic del proveïdor"</item>
+ </string-array>
+ <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"No es pot processar. Torna-ho a provar."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"No hi ha maquinari 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>
+ <string name="fingerprint_error_vendor" msgid="3175724710791609491">"S\'ha esgotat el temps d\'espera per a l\'empremta digital. Torna-ho a provar."</string>
+ <string-array name="fingerprint_error_vendor">
+ <item msgid="5804600450373644614">"Missatge d\'error específic del proveïdor."</item>
+ </string-array>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"llegir la configuració de sincronització"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Permet que l\'aplicació llegeixi la configuració de sincronització d\'un compte. Per exemple, això pot determinar que l\'aplicació Persones estigui sincronitzada amb un compte."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"activar o desactivar la sincronització"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index d7e86a5..7429bfc 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -128,6 +128,7 @@
<string name="wfcRegErrorTitle" msgid="2301376280632110664">"Volání přes Wi-Fi"</string>
<string-array name="wfcOperatorErrorMessages">
</string-array>
+ <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
<string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Nepřesměrováno"</string>
<string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> po <xliff:g id="TIME_DELAY">{2}</xliff:g> sek."</string>
@@ -748,6 +749,22 @@
<string name="permdesc_manageFingerprint" msgid="178208705828055464">"Umožňuje aplikaci volat metody k přidání a smazání šablon otisků prstů, které budou použity."</string>
<string name="permlab_useFingerprint" msgid="3150478619915124905">"použití hardwaru na čtení otisků prstů"</string>
<string name="permdesc_useFingerprint" msgid="9165097460730684114">"Umožňuje aplikaci použít k ověření hardware na čtení otisků prstů"</string>
+ <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Byla zjištěna jen část otisku prstu. Zkuste to znovu."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Zpracování otisku prstu se nezdařilo. Zkuste to znovu."</string>
+ <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Senzor otisků prstů je znečištěn. Vyčistěte jej a zkuste to znovu."</string>
+ <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"Pohyb prstem byl příliš rychlý. Zkuste to znovu."</string>
+ <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"Pohyb prstem byl příliš pomalý. Zkuste to znovu."</string>
+ <string-array name="fingerprint_acquired_vendor">
+ <item msgid="2892952818207766996">"Chybová zpráva 0 dodavatele ohledně načtení otisků"</item>
+ </string-array>
+ <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"Otisk prstu nelze zpracovat. Zkuste to znovu."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"Hardware není dostupný."</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>
+ <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Časový limit sejmutí otisku prstu vypršel. Zkuste to znovu."</string>
+ <string-array name="fingerprint_error_vendor">
+ <item msgid="5804600450373644614">"Chybová zpráva dodavatele"</item>
+ </string-array>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"čtení nastavení synchronizace"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Umožňuje aplikaci číst nastavení synchronizace v účtu. Může například určit, zda je s účtem synchronizována aplikace Lidé."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"vypnutí nebo zapnutí synchronizace"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index b97720d..b6014ef 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -126,6 +126,7 @@
<string name="wfcRegErrorTitle" msgid="2301376280632110664">"Opkald via Wi-Fi"</string>
<string-array name="wfcOperatorErrorMessages">
</string-array>
+ <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
<string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Ikke viderestillet"</string>
<string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> efter <xliff:g id="TIME_DELAY">{2}</xliff:g> sekunder"</string>
@@ -746,6 +747,22 @@
<string name="permdesc_manageFingerprint" msgid="178208705828055464">"Tillader, at appen kan køre metoder til at tilføje og slette fingeraftryksskabeloner"</string>
<string name="permlab_useFingerprint" msgid="3150478619915124905">"brug fingeraftrykhardware"</string>
<string name="permdesc_useFingerprint" msgid="9165097460730684114">"Tillader, at appen kan bruge fingeraftrykhardware til godkendelse"</string>
+ <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Der blev registreret et delvist fingeraftryk. Prøv igen."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Fingeraftrykket kunne ikke behandles. Prøv igen."</string>
+ <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Sensoren til registrering af fingeraftryk er beskidt. Tør den af, og prøv igen."</string>
+ <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"Du bevægede fingeren for hurtigt. Prøv igen."</string>
+ <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"Du bevægede fingeren for langsomt. Prøv igen."</string>
+ <string-array name="fingerprint_acquired_vendor">
+ <item msgid="2892952818207766996">"Leverandørspecifik fejlmeddelelse 0 i forbindelse med hentning"</item>
+ </string-array>
+ <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"Registreringen kan ikke gennemføres. Prøv igen."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"Hardwaren 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>
+ <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Registrering af fingeraftryk fik timeout. Prøv igen."</string>
+ <string-array name="fingerprint_error_vendor">
+ <item msgid="5804600450373644614">"Leverandørspecifik fejlmeddelelse."</item>
+ </string-array>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"læse indstillinger for synkronisering"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Tillader, at appen kan læse synkroniseringsindstillingerne for en konto. Denne tilladelse kan f.eks. fastslå, om appen Personer er synkroniseret med en konto."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"slå synkronisering til og fra"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 227e20c..0db7fea 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -126,6 +126,7 @@
<string name="wfcRegErrorTitle" msgid="2301376280632110664">"WLAN-Telefonie"</string>
<string-array name="wfcOperatorErrorMessages">
</string-array>
+ <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
<string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Nicht weitergeleitet"</string>
<string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g><xliff:g id="DIALING_NUMBER">{1}</xliff:g> nach <xliff:g id="TIME_DELAY">{2}</xliff:g> Sekunden."</string>
@@ -746,6 +747,22 @@
<string name="permdesc_manageFingerprint" msgid="178208705828055464">"Erlaubt der App, Methoden zum Hinzufügen und Löschen zu verwendender Fingerabdruckvorlagen aufzurufen"</string>
<string name="permlab_useFingerprint" msgid="3150478619915124905">"Fingerabdruckhardware verwenden"</string>
<string name="permdesc_useFingerprint" msgid="9165097460730684114">"Erlaubt der App, Fingerabdruckhardware zur Authentifizierung zu verwenden"</string>
+ <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Fingerabdruck teilweise erkannt. Versuchen Sie es erneut."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Fingerabdruck konnte nicht verarbeitet werden. Versuchen Sie es erneut."</string>
+ <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Fingerabdrucksensor ist verschmutzt. Reinigen Sie ihn und versuchen Sie es erneut."</string>
+ <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"Finger zu schnell bewegt. Versuchen Sie es erneut."</string>
+ <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"Finger zu langsam bewegt. Versuchen Sie es erneut."</string>
+ <string-array name="fingerprint_acquired_vendor">
+ <item msgid="2892952818207766996">"Anbieterspezifische Erfassungsfehlermeldung 0"</item>
+ </string-array>
+ <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"Unbrauchbar. Versuchen Sie es erneut."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"Hardware nicht verfügbar"</string>
+ <string name="fingerprint_error_no_space" msgid="1055819001126053318">"Fingerabdruck kann nicht gespeichert werden. Entfernen Sie einen vorhandenen Fingerabdruck."</string>
+ <string name="fingerprint_error_timeout" msgid="3927186043737732875">"Zeitüberschreitung für Fingerabdruck. Versuchen Sie es erneut."</string>
+ <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Zeitüberschreitung für Fingerabdruck. Versuchen Sie es erneut."</string>
+ <string-array name="fingerprint_error_vendor">
+ <item msgid="5804600450373644614">"Anbieterspezifische Fehlermeldung"</item>
+ </string-array>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"Synchronisierungseinstellungen lesen"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Ermöglicht der App, die Synchronisierungseinstellungen eines Kontos zu lesen. Beispielsweise kann damit festgestellt werden, ob Kontakte mit einem Konto synchronisiert werden."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"Synchronisierung aktivieren oder deaktivieren"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index c3e1bb74..8a9e9d3 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -126,6 +126,7 @@
<string name="wfcRegErrorTitle" msgid="2301376280632110664">"Κλήση Wi-Fi"</string>
<string-array name="wfcOperatorErrorMessages">
</string-array>
+ <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
<string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Δεν προωθήθηκε"</string>
<string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> μετά από <xliff:g id="TIME_DELAY">{2}</xliff:g> δευτερόλεπτα"</string>
@@ -746,6 +747,22 @@
<string name="permdesc_manageFingerprint" msgid="178208705828055464">"Επιτρέπει στην εφαρμογή να επικαλείται μεθόδους για την προσθήκη και τη διαγραφή προτύπων μοναδικού χαρακτηριστικού για χρήση."</string>
<string name="permlab_useFingerprint" msgid="3150478619915124905">"χρήση εξοπλισμού μοναδικού χαρακτηριστικού"</string>
<string name="permdesc_useFingerprint" msgid="9165097460730684114">"Επιτρέπει στην εφαρμογή να χρησιμοποιεί εξοπλισμό μοναδικού χαρακτηριστικού για έλεγχο ταυτότητας"</string>
+ <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Εντοπίστηκε μερικό μοναδικό χαρακτηριστικό. Δοκιμάστε ξανά."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Δεν ήταν δυνατή η επεξεργασία του μοναδικού χαρακτηριστικού. Δοκιμάστε ξανά."</string>
+ <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Ο αισθητήρας μοναδικού χαρακτηριστικού δεν είναι καθαρός. Καθαρίστε τον και δοκιμάστε ξανά."</string>
+ <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"Πολύ γρήγορη κίνηση δαχτύλου. Δοκιμάστε ξανά."</string>
+ <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"Πολύ αργή κίνηση δαχτύλου. Δοκιμάστε ξανά."</string>
+ <string-array name="fingerprint_acquired_vendor">
+ <item msgid="2892952818207766996">"Μήνυμα σφάλματος εξαγοράς για συγκεκριμένο προμηθευτή 0"</item>
+ </string-array>
+ <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"Δεν είναι δυνατή η επεξεργασία. Δοκιμάστε ξανά."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"Ο εξοπλισμός δεν είναι διαθέσιμος."</string>
+ <string name="fingerprint_error_no_space" msgid="1055819001126053318">"Δεν είναι δυνατή η αποθήκευση μοναδικού χαρακτηριστικού. Καταργήστε το υπάρχον μοναδικό χαρακτηριστικό."</string>
+ <string name="fingerprint_error_timeout" msgid="3927186043737732875">"Λήξη χρονικού ορίου μοναδικού χαρακτηριστικού. Δοκιμάστε ξανά."</string>
+ <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Λήξη χρονικού ορίου μοναδικού χαρακτηριστικού. Δοκιμάστε ξανά."</string>
+ <string-array name="fingerprint_error_vendor">
+ <item msgid="5804600450373644614">"Μήνυμα σφάλματος για συγκεκριμένο προμηθευτή."</item>
+ </string-array>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"ανάγνωση ρυθμίσεων συγχρονισμού"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Επιτρέπει στην εφαρμογή την ανάγνωση των ρυθμίσεων συγχρονισμού για έναν λογαριασμό. Για παράδειγμα, αυτό μπορεί να καθορίσει εάν η εφαρμογή \"Άτομα\" είναι συγχρονισμένη με έναν λογαριασμό."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"εναλλαγή ενεργοποίησης και απενεργοποίησης συγχρονισμού"</string>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index bfcb90c..389b584 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -126,6 +126,7 @@
<string name="wfcRegErrorTitle" msgid="2301376280632110664">"Wi-Fi Calling"</string>
<string-array name="wfcOperatorErrorMessages">
</string-array>
+ <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
<string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Not forwarded"</string>
<string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> after <xliff:g id="TIME_DELAY">{2}</xliff:g> seconds"</string>
@@ -746,6 +747,22 @@
<string name="permdesc_manageFingerprint" msgid="178208705828055464">"Allows the app to invoke methods to add and delete fingerprint templates for use."</string>
<string name="permlab_useFingerprint" msgid="3150478619915124905">"use fingerprint hardware"</string>
<string name="permdesc_useFingerprint" msgid="9165097460730684114">"Allows the app to use fingerprint hardware for authentication"</string>
+ <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Partial fingerprint detected. Please try again."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Couldn\'t process fingerprint. Please try again."</string>
+ <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Fingerprint sensor is dirty. Please clean and try again."</string>
+ <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"Finger moved to fast. Please try again."</string>
+ <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"Finger moved to slow. Please try again."</string>
+ <string-array name="fingerprint_acquired_vendor">
+ <item msgid="2892952818207766996">"Vendor-specific acquisition error message 0"</item>
+ </string-array>
+ <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"Unable to process. Try again."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"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>
+ <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Fingerprint timeout reached. Try again."</string>
+ <string-array name="fingerprint_error_vendor">
+ <item msgid="5804600450373644614">"Vendor-specific error message."</item>
+ </string-array>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"read sync settings"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Allows the app to read the sync settings for an account. For example, this can determine whether the People app is synced with an account."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"toggle sync on and off"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index bfcb90c..389b584 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -126,6 +126,7 @@
<string name="wfcRegErrorTitle" msgid="2301376280632110664">"Wi-Fi Calling"</string>
<string-array name="wfcOperatorErrorMessages">
</string-array>
+ <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
<string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Not forwarded"</string>
<string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> after <xliff:g id="TIME_DELAY">{2}</xliff:g> seconds"</string>
@@ -746,6 +747,22 @@
<string name="permdesc_manageFingerprint" msgid="178208705828055464">"Allows the app to invoke methods to add and delete fingerprint templates for use."</string>
<string name="permlab_useFingerprint" msgid="3150478619915124905">"use fingerprint hardware"</string>
<string name="permdesc_useFingerprint" msgid="9165097460730684114">"Allows the app to use fingerprint hardware for authentication"</string>
+ <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Partial fingerprint detected. Please try again."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Couldn\'t process fingerprint. Please try again."</string>
+ <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Fingerprint sensor is dirty. Please clean and try again."</string>
+ <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"Finger moved to fast. Please try again."</string>
+ <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"Finger moved to slow. Please try again."</string>
+ <string-array name="fingerprint_acquired_vendor">
+ <item msgid="2892952818207766996">"Vendor-specific acquisition error message 0"</item>
+ </string-array>
+ <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"Unable to process. Try again."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"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>
+ <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Fingerprint timeout reached. Try again."</string>
+ <string-array name="fingerprint_error_vendor">
+ <item msgid="5804600450373644614">"Vendor-specific error message."</item>
+ </string-array>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"read sync settings"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Allows the app to read the sync settings for an account. For example, this can determine whether the People app is synced with an account."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"toggle sync on and off"</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index bfcb90c..389b584 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -126,6 +126,7 @@
<string name="wfcRegErrorTitle" msgid="2301376280632110664">"Wi-Fi Calling"</string>
<string-array name="wfcOperatorErrorMessages">
</string-array>
+ <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
<string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Not forwarded"</string>
<string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> after <xliff:g id="TIME_DELAY">{2}</xliff:g> seconds"</string>
@@ -746,6 +747,22 @@
<string name="permdesc_manageFingerprint" msgid="178208705828055464">"Allows the app to invoke methods to add and delete fingerprint templates for use."</string>
<string name="permlab_useFingerprint" msgid="3150478619915124905">"use fingerprint hardware"</string>
<string name="permdesc_useFingerprint" msgid="9165097460730684114">"Allows the app to use fingerprint hardware for authentication"</string>
+ <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Partial fingerprint detected. Please try again."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Couldn\'t process fingerprint. Please try again."</string>
+ <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Fingerprint sensor is dirty. Please clean and try again."</string>
+ <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"Finger moved to fast. Please try again."</string>
+ <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"Finger moved to slow. Please try again."</string>
+ <string-array name="fingerprint_acquired_vendor">
+ <item msgid="2892952818207766996">"Vendor-specific acquisition error message 0"</item>
+ </string-array>
+ <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"Unable to process. Try again."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"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>
+ <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Fingerprint timeout reached. Try again."</string>
+ <string-array name="fingerprint_error_vendor">
+ <item msgid="5804600450373644614">"Vendor-specific error message."</item>
+ </string-array>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"read sync settings"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Allows the app to read the sync settings for an account. For example, this can determine whether the People app is synced with an account."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"toggle sync on and off"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 78cc227..642ab92 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -126,6 +126,7 @@
<string name="wfcRegErrorTitle" msgid="2301376280632110664">"Llamada por Wi-Fi"</string>
<string-array name="wfcOperatorErrorMessages">
</string-array>
+ <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
<string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: no se ha remitido"</string>
<string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> después de <xliff:g id="TIME_DELAY">{2}</xliff:g> segundos"</string>
@@ -746,6 +747,22 @@
<string name="permdesc_manageFingerprint" msgid="178208705828055464">"Permite que la aplicación emplee métodos para agregar y eliminar plantillas de huellas digitales para su uso."</string>
<string name="permlab_useFingerprint" msgid="3150478619915124905">"Utilizar hardware de huellas digitales"</string>
<string name="permdesc_useFingerprint" msgid="9165097460730684114">"Permite que la aplicación utilice el hardware de huellas digitales para realizar la autenticación."</string>
+ <string name="fingerprint_acquired_partial" msgid="735082772341716043">"La huella digital se detectó parcialmente. Vuelve a intentarlo."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"No se pudo procesar la huella digital. Vuelve a intentarlo."</string>
+ <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"El sensor de huellas digitales está sucio. Limpia el sensor y vuelve a intentarlo."</string>
+ <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"Moviste el dedo muy rápido. Vuelve a intentarlo."</string>
+ <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"Moviste el dedo muy despacio. Vuelve a intentarlo."</string>
+ <string-array name="fingerprint_acquired_vendor">
+ <item msgid="2892952818207766996">"Mensaje de error de adquisición específico del proveedor 0"</item>
+ </string-array>
+ <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"No se puede procesar. Vuelve a intentarlo."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"El hardware 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>
+ <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Finalizó el tiempo de espera para la huella digital. Vuelve a intentarlo."</string>
+ <string-array name="fingerprint_error_vendor">
+ <item msgid="5804600450373644614">"Mensaje de error específico del proveedor"</item>
+ </string-array>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"leer la configuración de sincronización"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Este permiso permite que la aplicación consulte la configuración de sincronización de una cuenta. Esto permite, por ejemplo, determinar si la aplicación Personas está sincronizada con una cuenta."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"activar y desactivar la sincronización"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 969881c..8e1c37b 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -126,6 +126,7 @@
<string name="wfcRegErrorTitle" msgid="2301376280632110664">"Llamadas Wi-Fi"</string>
<string-array name="wfcOperatorErrorMessages">
</string-array>
+ <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
<string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: No desviada"</string>
<string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> transcurridos <xliff:g id="TIME_DELAY">{2}</xliff:g> segundos"</string>
@@ -746,6 +747,22 @@
<string name="permdesc_manageFingerprint" msgid="178208705828055464">"Permite que la aplicación invoque métodos para añadir y eliminar plantillas de huellas digitales y utilizarlas."</string>
<string name="permlab_useFingerprint" msgid="3150478619915124905">"utilizar hardware de huellas digitales"</string>
<string name="permdesc_useFingerprint" msgid="9165097460730684114">"Permite que la aplicación utilice el hardware de huellas digitales para realizar la autenticación"</string>
+ <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Se ha detectado una huella digital parcial. Vuelve a intentarlo."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"No se ha podido procesar la huella digital. Vuelve a intentarlo."</string>
+ <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"El sensor de huellas digitales está sucio. Límpialo y vuelve a intentarlo."</string>
+ <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"Has movido el dedo muy rápido. Vuelve a intentarlo."</string>
+ <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"Has movido el dedo muy despacio. Vuelve a intentarlo."</string>
+ <string-array name="fingerprint_acquired_vendor">
+ <item msgid="2892952818207766996">"Mensaje de error de adquisición específico del proveedor: 0"</item>
+ </string-array>
+ <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"No se puede procesar la huella digital. Vuelve a intentarlo."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"Hardware no 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>
+ <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Se ha alcanzado el tiempo de espera de la huella digital. Vuelve a intentarlo."</string>
+ <string-array name="fingerprint_error_vendor">
+ <item msgid="5804600450373644614">"Mensaje de error específico del proveedor."</item>
+ </string-array>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"leer la configuración de sincronización"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Permite que la aplicación consulte la configuración de sincronización de una cuenta. La aplicación puede utilizar este permiso, por ejemplo, para determinar si la aplicación Contactos está sincronizada con una cuenta."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"activar y desactivar la sincronización"</string>
diff --git a/core/res/res/values-et-rEE/strings.xml b/core/res/res/values-et-rEE/strings.xml
index 72b13da..10f1a45 100644
--- a/core/res/res/values-et-rEE/strings.xml
+++ b/core/res/res/values-et-rEE/strings.xml
@@ -126,6 +126,7 @@
<string name="wfcRegErrorTitle" msgid="2301376280632110664">"WiFi-kõned"</string>
<string-array name="wfcOperatorErrorMessages">
</string-array>
+ <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
<string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: pole suunatud"</string>
<string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> <xliff:g id="TIME_DELAY">{2}</xliff:g> sekundi pärast"</string>
@@ -746,6 +747,22 @@
<string name="permdesc_manageFingerprint" msgid="178208705828055464">"Võimaldab rakendusel tühistada meetodid kasutatavate sõrmejäljemallide lisamiseks ja kustutamiseks."</string>
<string name="permlab_useFingerprint" msgid="3150478619915124905">"sõrmejälje riistvara kasutamine"</string>
<string name="permdesc_useFingerprint" msgid="9165097460730684114">"Võimaldab rakendusel autentimiseks kasutada sõrmejälje riistvara"</string>
+ <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Tuvastati osaline sõrmejälg. Proovige uuesti."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Sõrmejälge ei õnnestunud töödelda. Proovige uuesti."</string>
+ <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Sõrmejäljeandur on must. Puhastage see ja proovige uuesti."</string>
+ <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"Sõrm liikus liiga kiiresti. Proovige uuesti."</string>
+ <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"Sõrm liikus liiga aeglaselt. Proovige uuesti."</string>
+ <string-array name="fingerprint_acquired_vendor">
+ <item msgid="2892952818207766996">"Teenusepakkujapõhise värbamise veateade 0"</item>
+ </string-array>
+ <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"Töötlemine ei õnnestu. Proovige uuesti."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"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>
+ <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Sõrmejälje riistvara taimeri ajalõpp. Proovige uuesti."</string>
+ <string-array name="fingerprint_error_vendor">
+ <item msgid="5804600450373644614">"Teenusepakkujapõhine veateade."</item>
+ </string-array>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"loe sünkroonimisseadeid"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Võimaldab rakendusel lugeda konto sünkroonimisseadeid. Näiteks võib see määrata, kas rakendus Inimesed on kontoga sünkroonitud."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"lülitage sünkroonimine sisse ja välja"</string>
diff --git a/core/res/res/values-eu-rES/strings.xml b/core/res/res/values-eu-rES/strings.xml
index da96ec3..e6ade17 100644
--- a/core/res/res/values-eu-rES/strings.xml
+++ b/core/res/res/values-eu-rES/strings.xml
@@ -126,6 +126,7 @@
<string name="wfcRegErrorTitle" msgid="2301376280632110664">"Wi-Fi bidezko deiak"</string>
<string-array name="wfcOperatorErrorMessages">
</string-array>
+ <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
<string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: ez da desbideratu"</string>
<string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> zenbakira <xliff:g id="TIME_DELAY">{2}</xliff:g> segundotan"</string>
@@ -746,6 +747,22 @@
<string name="permdesc_manageFingerprint" msgid="178208705828055464">"Hatz-marka digitalen txantiloiak gehitzeko eta ezabatzeko metodoei dei egitea baimentzen die aplikazioei."</string>
<string name="permlab_useFingerprint" msgid="3150478619915124905">"Erabili hatz-marka digitalen hardwarea"</string>
<string name="permdesc_useFingerprint" msgid="9165097460730684114">"Autentifikatzeko hatz-marka digitalen hardwarea erabiltzea baimentzen die aplikazioei."</string>
+ <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Hatz-marka digitala ez da osorik hauteman. Saiatu berriro."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Ezin izan da hatza-marka prozesatu. Saiatu berriro."</string>
+ <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Hatz-marka digitalen sentsorea zikina dago. Garbi ezazu, eta saiatu berriro."</string>
+ <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"Hatza bizkorregi mugitu duzu. Saiatu berriro."</string>
+ <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"Hatza mantsoegi mugitu duzu. Saiatu berriro."</string>
+ <string-array name="fingerprint_acquired_vendor">
+ <item msgid="2892952818207766996">"Saltzailearen berariazko errore-mezua, erosketarekin erlazionatuta"</item>
+ </string-array>
+ <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"Ezin da prozesatu. Saiatu berriro."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"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>
+ <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Hatz-marka digitalak prozesatzeko denbora-muga gainditu da. Saiatu berriro."</string>
+ <string-array name="fingerprint_error_vendor">
+ <item msgid="5804600450373644614">"Saltzailearen berariazko errore-mezua."</item>
+ </string-array>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"Irakurri sinkronizazio-ezarpenak"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Kontu baten sinkronizazio-ezarpenak irakurtzeko baimena ematen die aplikazioei. Adibidez, Jendea aplikazioa konturen batekin sinkronizatuta dagoen zehatz dezake."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"sinkronizazioa aktibatzea eta desaktibatzea"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 1aa1cc8..babe1f0 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -126,6 +126,7 @@
<string name="wfcRegErrorTitle" msgid="2301376280632110664">"تماس از طریق Wi-Fi"</string>
<string-array name="wfcOperatorErrorMessages">
</string-array>
+ <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
<string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: هدایت نشده"</string>
<string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> پس از <xliff:g id="TIME_DELAY">{2}</xliff:g> ثانیه"</string>
@@ -746,6 +747,22 @@
<string name="permdesc_manageFingerprint" msgid="178208705828055464">"به برنامه امکان میدهد روشهایی را برای افزودن و حذف الگوهای اثر انگشت جهت استفاده، فعال کند."</string>
<string name="permlab_useFingerprint" msgid="3150478619915124905">"استفاده از سختافزار اثر انگشت"</string>
<string name="permdesc_useFingerprint" msgid="9165097460730684114">"به برنامه امکان میدهد از سختافزار اثر انگشت برای احراز هویت استفاده کند"</string>
+ <string name="fingerprint_acquired_partial" msgid="735082772341716043">"بخشی از اثر انگشت شناسایی شد. لطفاً دوباره امتحان کنید."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"اثرانگشت پردازش نشد. لطفاً دوباره امتحان کنید."</string>
+ <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"حسگر اثر انگشت کثیف است. لطفاً آن را تمیز کنید و دوباره امتحان نمایید."</string>
+ <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"انگشت خیلی سریع حرکت کرد. لطفاً دوباره امتحان کنید."</string>
+ <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"انگشت خیلی آهسته حرکت کرد. لطفاً دوباره امتحان کنید."</string>
+ <string-array name="fingerprint_acquired_vendor">
+ <item msgid="2892952818207766996">"پیام خطای خرید خاص فروشنده ۰"</item>
+ </string-array>
+ <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"پردازش ممکن نیست. دوباره امتحان کنید."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"سختافزار در دسترس نیست."</string>
+ <string name="fingerprint_error_no_space" msgid="1055819001126053318">"ذخیره اثر انگشت ممکن نیست. لطفاً یک اثر انگشت موجود را حذف کنید."</string>
+ <string name="fingerprint_error_timeout" msgid="3927186043737732875">"مهلت زمانی ثبت اثر انگشت به پایان رسید. دوباره امتحان کنید."</string>
+ <string name="fingerprint_error_vendor" msgid="3175724710791609491">"مهلت زمانی ثبت اثر انگشت به پایان رسید. دوباره امتحان کنید."</string>
+ <string-array name="fingerprint_error_vendor">
+ <item msgid="5804600450373644614">"پیام خطای خاص فروشنده."</item>
+ </string-array>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"خواندن تنظیمات همگامسازی"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"به برنامه اجازه میدهد تنظیمات را برای یک حساب بخواند. بهعنوان مثال، این ویژگی میتواند تعیین کند آیا حساب «افراد» شما با یک حساب همگامسازی شده است."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"تغییر وضعیت همگامسازی بین فعال و غیرفعال"</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 39860e3..76d3ba4 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -126,6 +126,7 @@
<string name="wfcRegErrorTitle" msgid="2301376280632110664">"Wi-Fi-puhelut"</string>
<string-array name="wfcOperatorErrorMessages">
</string-array>
+ <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
<string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: ei siirretty"</string>
<string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> <xliff:g id="TIME_DELAY">{2}</xliff:g> sekunnin päästä"</string>
@@ -746,6 +747,22 @@
<string name="permdesc_manageFingerprint" msgid="178208705828055464">"Sallii sovelluksen käyttää menetelmiä, joilla voidaan lisätä tai poistaa sormenjälkimalleja."</string>
<string name="permlab_useFingerprint" msgid="3150478619915124905">"sormenjälkilaitteiston käyttö"</string>
<string name="permdesc_useFingerprint" msgid="9165097460730684114">"Sallii sovelluksen käyttää sormenjälkilaitteistoa todennukseen."</string>
+ <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Sormenjälki havaittiin vain osittain. Yritä uudelleen."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Sormenjäljen käsittely epäonnistui. Yritä uudelleen."</string>
+ <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Sormenjälkitunnistin on likainen. Puhdista tunnistin ja yritä uudelleen."</string>
+ <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"Liikutit sormea liian nopeasti. Yritä uudelleen."</string>
+ <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"Liikutit sormea liian hitaasti. Yritä uudelleen."</string>
+ <string-array name="fingerprint_acquired_vendor">
+ <item msgid="2892952818207766996">"Toimittajakohtainen hankintavirheilmoitus 0"</item>
+ </string-array>
+ <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"Käsittely ei onnistu. Yritä uudelleen."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"Laitteisto 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>
+ <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Sormenjälkitunnistimen toiminta aikakatkaistiin. Yritä uudelleen."</string>
+ <string-array name="fingerprint_error_vendor">
+ <item msgid="5804600450373644614">"Toimittajakohtainen virheilmoitus."</item>
+ </string-array>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"lue synkronointiasetuksia"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Antaa sovelluksen lukea tilien synkronointiasetuksia. Sovellus voi esimerkiksi määrittää, onko Henkilöt-sovellus synkronoitu tilin kanssa."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"ota synkronointi käyttöön tai poista se käytöstä"</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index 4a3e4a1..81bd0653 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -126,6 +126,7 @@
<string name="wfcRegErrorTitle" msgid="2301376280632110664">"Appels Wi-Fi"</string>
<string-array name="wfcOperatorErrorMessages">
</string-array>
+ <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
<string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g> : non transféré"</string>
<string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g> : <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g> : <xliff:g id="DIALING_NUMBER">{1}</xliff:g> au bout de <xliff:g id="TIME_DELAY">{2}</xliff:g> secondes"</string>
@@ -746,6 +747,22 @@
<string name="permdesc_manageFingerprint" msgid="178208705828055464">"Permet à l\'application de faire appel à des méthodes d\'ajout et de suppression de modèles d\'empreinte digitale que vous pouvez utiliser."</string>
<string name="permlab_useFingerprint" msgid="3150478619915124905">"utiliser le matériel d\'empreinte digitale"</string>
<string name="permdesc_useFingerprint" msgid="9165097460730684114">"Permet à l\'application d\'utiliser du matériel d\'empreinte digitale pour l\'authentification"</string>
+ <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Empreinte digitale partielle détectée. Veuillez essayer de nouveau."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Impossible de traiter les empreintes digitales. Veuillez essayer de nouveau."</string>
+ <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Le capteur d\'empreintes digitales est sale. Veuillez le nettoyer et essayer de nouveau."</string>
+ <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"Le doigt a bougé trop vite. Veuillez essayer de nouveau."</string>
+ <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"Le doigt a bougé trop lentement. Veuillez essayer de nouveau."</string>
+ <string-array name="fingerprint_acquired_vendor">
+ <item msgid="2892952818207766996">"Message d\'erreur d\'acquisition propre au fournisseur 0"</item>
+ </string-array>
+ <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"Traitement impossible. Essayer de nouveau."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"Matériel non disponible."</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>
+ <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Le temps attribué pour lire l\'empreinte est écoulé. Veuillez essayer de nouveau."</string>
+ <string-array name="fingerprint_error_vendor">
+ <item msgid="5804600450373644614">"Message d\'erreur propre au fournisseur."</item>
+ </string-array>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"lire les paramètres de synchronisation"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Permet à l\'application d\'accéder aux paramètres de synchronisation d\'un compte. Par exemple, cette autorisation peut permettre de déterminer si l\'application Contacts est synchronisée avec un compte ou non."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"activer ou désactiver la synchronisation"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 1713dd8..115c567 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -126,6 +126,7 @@
<string name="wfcRegErrorTitle" msgid="2301376280632110664">"Appels Wi-Fi"</string>
<string-array name="wfcOperatorErrorMessages">
</string-array>
+ <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
<string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g> : non transféré"</string>
<string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g> : <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g> : <xliff:g id="DIALING_NUMBER">{1}</xliff:g> au bout de <xliff:g id="TIME_DELAY">{2}</xliff:g> secondes"</string>
@@ -746,6 +747,22 @@
<string name="permdesc_manageFingerprint" msgid="178208705828055464">"Autoriser l\'application à invoquer des méthodes pour ajouter et supprimer des modèles d\'empreintes digitales"</string>
<string name="permlab_useFingerprint" msgid="3150478619915124905">"Utiliser le matériel d\'empreintes digitales"</string>
<string name="permdesc_useFingerprint" msgid="9165097460730684114">"Autoriser l\'application à utiliser le matériel d\'empreintes digitales pour l\'authentification"</string>
+ <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Empreinte numérique partiellement détectée. Veuillez réessayer."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Impossible de reconnaître l\'empreinte numérique. Veuillez réessayer."</string>
+ <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Le capteur d\'empreintes numériques est sale. Veuillez le nettoyer, puis réessayer."</string>
+ <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"Vous avez retiré votre doigt trop rapidement. Veuillez réessayer."</string>
+ <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"Vous avez déplacé votre doigt trop lentement. Veuillez réessayer."</string>
+ <string-array name="fingerprint_acquired_vendor">
+ <item msgid="2892952818207766996">"Message d\'erreur d\'acquisition spécifique au fournisseur 0"</item>
+ </string-array>
+ <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"Impossible de reconnaître l\'empreinte numérique. Veuillez réessayer."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"Matériel non disponible."</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>
+ <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Délai de détection de l\'empreinte numérique expiré. Veuillez réessayer."</string>
+ <string-array name="fingerprint_error_vendor">
+ <item msgid="5804600450373644614">"Message d\'erreur spécifique au fournisseur."</item>
+ </string-array>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"lire les paramètres de synchronisation"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Permet à l\'application d\'accéder aux paramètres de synchronisation d\'un compte. Par exemple, cette autorisation peut permettre de déterminer si l\'application Contacts est synchronisée avec un compte ou non."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"activer/désactiver la synchronisation"</string>
diff --git a/core/res/res/values-gl-rES/strings.xml b/core/res/res/values-gl-rES/strings.xml
index e35d654..a282039 100644
--- a/core/res/res/values-gl-rES/strings.xml
+++ b/core/res/res/values-gl-rES/strings.xml
@@ -126,6 +126,7 @@
<string name="wfcRegErrorTitle" msgid="2301376280632110664">"Chamadas por wifi"</string>
<string-array name="wfcOperatorErrorMessages">
</string-array>
+ <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
<string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: non desviada"</string>
<string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> tras <xliff:g id="TIME_DELAY">{2}</xliff:g> segundos"</string>
@@ -746,6 +747,22 @@
<string name="permdesc_manageFingerprint" msgid="178208705828055464">"Permite que a aplicación invoque métodos para engadir e eliminar modelos de uso de identificación dixital."</string>
<string name="permlab_useFingerprint" msgid="3150478619915124905">"usar hardware de identificación dixital"</string>
<string name="permdesc_useFingerprint" msgid="9165097460730684114">"Permite que a aplicación utilice hardware de identificación dixital para a autenticación"</string>
+ <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Detectouse unha identificación dixital parcial. Téntao de novo."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Non se puido procesar a impresión dixital. Téntao de novo."</string>
+ <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"O sensor de identificación dixital está sucio. Límpao e téntao de novo."</string>
+ <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"O dedo moveuse demasiado rápido. Téntao de novo."</string>
+ <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"O dedo moveuse demasiado lento. Téntao de novo."</string>
+ <string-array name="fingerprint_acquired_vendor">
+ <item msgid="2892952818207766996">"Mensaxe de erro de adquisición específico do vendedor 0"</item>
+ </string-array>
+ <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"Non se pode procesar. Téntao de novo."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"Hardware non dispoñible."</string>
+ <string name="fingerprint_error_no_space" msgid="1055819001126053318">"Non se pode almacenar a identificación dixital. Elimina unha identificación dixital existente."</string>
+ <string name="fingerprint_error_timeout" msgid="3927186043737732875">"Esgotouse o tempo de espera da identificación dixital. Téntao de novo."</string>
+ <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Esgotouse o tempo de espera da identificación dixital. Téntao de novo."</string>
+ <string-array name="fingerprint_error_vendor">
+ <item msgid="5804600450373644614">"Mensaxe de erro específico do vendedor"</item>
+ </string-array>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"ler a configuración de sincronización"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Permite á aplicación ler a configuración de sincronización dunha conta. Por exemplo, esta acción pode determinar se a aplicación Contactos se sincroniza cunha conta."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"activar e desactivar a sincronización"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 53c395d..943ed28 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -126,6 +126,7 @@
<string name="wfcRegErrorTitle" msgid="2301376280632110664">"वाई-फ़ाई कॉलिंग"</string>
<string-array name="wfcOperatorErrorMessages">
</string-array>
+ <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
<string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: अग्रेषित नहीं किया गया"</string>
<string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> <xliff:g id="TIME_DELAY">{2}</xliff:g> सेकंड के बाद"</string>
@@ -746,6 +747,22 @@
<string name="permdesc_manageFingerprint" msgid="178208705828055464">"अंगुली की छाप वाले टेम्पलेट का उपयोग करने के लिए जोड़ने और हटाने हेतु ऐप को विधियां प्रारंभ करने देती है."</string>
<string name="permlab_useFingerprint" msgid="3150478619915124905">"अंगुली की छाप के लिए हार्डवेयर का उपयोग करें"</string>
<string name="permdesc_useFingerprint" msgid="9165097460730684114">"ऐप के प्रमाणीकरण के लिए अंगुली की छाप हार्डवेयर का उपयोग करने देती है"</string>
+ <string name="fingerprint_acquired_partial" msgid="735082772341716043">"आंशिक फ़िंगरप्रिंट की पहचान की गई. कृपया पुनः प्रयास करें."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"फ़िंगरप्रिंट संसाधित नहीं हो सका. कृपया पुन: प्रयास करें."</string>
+ <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"फ़िंगरप्रिंट संवेदक गंदा है. कृपया साफ़ करें और पुनः प्रयास करें."</string>
+ <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"अंगुली को तेज़ी से चलाया गया. कृपया पुनः प्रयास करें."</string>
+ <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"अंगुली को धीरे चलाया गया. कृपया पुनः प्रयास करें."</string>
+ <string-array name="fingerprint_acquired_vendor">
+ <item msgid="2892952818207766996">"विक्रेता-विशिष्ट प्राप्ति त्रुटि संदेश 0"</item>
+ </string-array>
+ <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"संसाधित करने में असमर्थ. पुनः प्रयास करें."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"हार्डवेयर उपलब्ध नहीं है."</string>
+ <string name="fingerprint_error_no_space" msgid="1055819001126053318">"फ़िंगरप्रिंट को संग्रहीत नहीं किया जा सका. कृपया कोई मौजूदा फ़िंगरप्रिंट निकालें."</string>
+ <string name="fingerprint_error_timeout" msgid="3927186043737732875">"फ़िंगरप्रिंट का समय समाप्त हो गया. पुनः प्रयास करें."</string>
+ <string name="fingerprint_error_vendor" msgid="3175724710791609491">"फ़िंगरप्रिंट का समय समाप्त हो गया. पुनः प्रयास करें."</string>
+ <string-array name="fingerprint_error_vendor">
+ <item msgid="5804600450373644614">"विक्रेता-विशिष्ट त्रुटि संदेश."</item>
+ </string-array>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"समन्वयन सेटिंग पढ़ें"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"ऐप्स को किसी खाते की समन्वयन सेटिंग पढ़ने देता है. उदाहरण के लिए, इससे यह निर्धारित किया जा सकता है कि लोग ऐप्स किसी खाते के साथ समन्वयित है या नहीं."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"समन्वयन बंद या चालू टॉगल करें"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 7d39eb8..bc3f839 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -127,6 +127,7 @@
<string name="wfcRegErrorTitle" msgid="2301376280632110664">"Wi-Fi pozivi"</string>
<string-array name="wfcOperatorErrorMessages">
</string-array>
+ <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
<string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Nije proslijeđeno"</string>
<string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> nakon <xliff:g id="TIME_DELAY">{2}</xliff:g> s"</string>
@@ -747,6 +748,22 @@
<string name="permdesc_manageFingerprint" msgid="178208705828055464">"Aplikaciji omogućuje pozivanje načina za dodavanje i brisanje predložaka otisaka prstiju koji će se upotrijebiti."</string>
<string name="permlab_useFingerprint" msgid="3150478619915124905">"upotreba hardvera za čitanje otisaka prstiju"</string>
<string name="permdesc_useFingerprint" msgid="9165097460730684114">"Aplikaciji omogućuje upotrebu hardvera za čitanje otisaka prstiju radi autentifikacije."</string>
+ <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Otkriven je djelomični otisak prsta. Pokušajte ponovo."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Obrada otiska prsta nije uspjela. Pokušajte ponovo."</string>
+ <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Senzor otiska prsta nije čist. Očistite ga i pokušajte ponovo."</string>
+ <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"Prebrzo pomicanje prsta. Pokušajte ponovo."</string>
+ <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"Presporo pomicanje prsta. Pokušajte ponovo."</string>
+ <string-array name="fingerprint_acquired_vendor">
+ <item msgid="2892952818207766996">"Poruka pogreške prilikom dohvaćanja vezana uz dobavljača 0"</item>
+ </string-array>
+ <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"Nije obrađeno. Pokušajte ponovo."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"Hardver 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>
+ <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Isteklo je vrijeme čekanja za otisak prsta. Pokušajte ponovo."</string>
+ <string-array name="fingerprint_error_vendor">
+ <item msgid="5804600450373644614">"Poruka pogreške vezana uz dobavljača."</item>
+ </string-array>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"čitanje postavki sinkronizacije"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Aplikaciji omogućuje čitanje postavki sinkronizacije za račun. Time se, primjerice, može utvrditi je li aplikacija Osobe sinkronizirana s računom."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"uključivanje/isključivanje sinkronizacije"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 9a9bfb3..df2d876 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -126,6 +126,7 @@
<string name="wfcRegErrorTitle" msgid="2301376280632110664">"Wi-Fi-hívás"</string>
<string-array name="wfcOperatorErrorMessages">
</string-array>
+ <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
<string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: nincs átirányítva"</string>
<string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> <xliff:g id="TIME_DELAY">{2}</xliff:g> másodperc után"</string>
@@ -746,6 +747,22 @@
<string name="permdesc_manageFingerprint" msgid="178208705828055464">"Lehetővé teszi az alkalmazás számára a használni kívánt ujjlenyomatsablonok hozzáadására és törlésére szolgáló metódusok indítását."</string>
<string name="permlab_useFingerprint" msgid="3150478619915124905">"ujjlenyomat-olvasó hardver használata"</string>
<string name="permdesc_useFingerprint" msgid="9165097460730684114">"Lehetővé teszi az alkalmazás számára az ujjlenyomat-olvasó hardver hitelesítésre való használatát"</string>
+ <string name="fingerprint_acquired_partial" msgid="735082772341716043">"A rendszer az ujjlenyomatnak csak egy részletét érzékelte. Próbálkozzon újra."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Nem sikerült feldolgozni az ujjlenyomatot. Próbálkozzon újra."</string>
+ <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Az ujjlenyomat-olvasó koszos. Tisztítsa meg, majd próbálkozzon újra."</string>
+ <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"Túl hamar vette el az ujját. Próbálkozzon újra."</string>
+ <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"Túl lassan vette el az ujját. Próbálkozzon újra."</string>
+ <string-array name="fingerprint_acquired_vendor">
+ <item msgid="2892952818207766996">"Szolgáltatóspecifikus akvizíciós hibakód 0"</item>
+ </string-array>
+ <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"A feldolgozás sikertelen. Próbálkozzon újra."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"A 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>
+ <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Az ujjlenyomat-beolvasási műveletkor időtúllépés történt. Próbálkozzon újra."</string>
+ <string-array name="fingerprint_error_vendor">
+ <item msgid="5804600450373644614">"Szolgáltatóspecifikus hibaüzenet"</item>
+ </string-array>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"szinkronizálási beállítások olvasása"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Lehetővé teszi az alkalmazás számára egy fiók szinkronizálási beállításainak beolvasását. Például ellenőrizheti, hogy a Személyek alkalmazás szinkronizálva van-e egy fiókkal."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"szinkronizálás be-és kikapcsolása"</string>
diff --git a/core/res/res/values-hy-rAM/strings.xml b/core/res/res/values-hy-rAM/strings.xml
index 1b5dfaa..9a59303 100644
--- a/core/res/res/values-hy-rAM/strings.xml
+++ b/core/res/res/values-hy-rAM/strings.xml
@@ -126,6 +126,7 @@
<string name="wfcRegErrorTitle" msgid="2301376280632110664">"Զանգեր Wi-Fi-ի միջոցով"</string>
<string-array name="wfcOperatorErrorMessages">
</string-array>
+ <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
<string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>. Չի վերահասցեավորվել"</string>
<string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>. <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>. <xliff:g id="DIALING_NUMBER">{1}</xliff:g> <xliff:g id="TIME_DELAY">{2}</xliff:g> վայրկյանից"</string>
@@ -236,7 +237,7 @@
<string name="permgrouplab_writeDictionary" msgid="8090237702432576788">"Գրել օգտվողի բառարանում"</string>
<string name="permgroupdesc_writeDictionary" msgid="2711561994497361646">"Ավելացնել բառեր օգտվողի բառարանում:"</string>
<string name="permgrouplab_bookmarks" msgid="1949519673103968229">"Էջանիշեր և պատմություն"</string>
- <string name="permgroupdesc_bookmarks" msgid="4169771606257963028">"Ուղղակի մուտք դեպի էջանիշեր և դիտարկչի պատմություն:"</string>
+ <string name="permgroupdesc_bookmarks" msgid="4169771606257963028">"Ուղղակի մուտք դեպի էջանիշեր և դիտարկիչի պատմություն:"</string>
<string name="permgrouplab_deviceAlarms" msgid="6117704629728824101">"Ազդանշան"</string>
<string name="permgroupdesc_deviceAlarms" msgid="4769356362251641175">"Կարգավորել զարթուցիչի ժամացույցը:"</string>
<string name="permgrouplab_voicemail" msgid="4162237145027592133">"Ձայնային փոստ"</string>
@@ -746,6 +747,22 @@
<string name="permdesc_manageFingerprint" msgid="178208705828055464">"Հավելվածին թույլ է տալիս կատարել այնպիսի գործառույթներ, որոնց միջոցով կարելի է օգտագործման համար ավելացնել և հեռացնել մատնահետքերի նմուշներ:"</string>
<string name="permlab_useFingerprint" msgid="3150478619915124905">"օգտագործել մատնահետքերի գրանցման սարքը"</string>
<string name="permdesc_useFingerprint" msgid="9165097460730684114">"Հավելվածին թույլ է տալիս նույնականացման համար օգտագործել մատնահետքերի գրանցման սարքը"</string>
+ <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Մատնահետքը հայտնաբերվել է մասամբ: Փորձեք նորից:"</string>
+ <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Չհաջողվեց մշակել մատնահետքը: Նորից փորձեք:"</string>
+ <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Մատնահետքերի սենսորն աղտոտված է: Մաքրեք այն և փորձեք նորից:"</string>
+ <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"Մատը շարժեցիք շատ արագ: Փորձեք նորից:"</string>
+ <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"Մատը շարժեցիք շատ դանդաղ: Փորձեք նորից:"</string>
+ <string-array name="fingerprint_acquired_vendor">
+ <item msgid="2892952818207766996">"Վաճառողի կողմից սահմանվող ձեռքբերման սխալի հաղորդագրություն 0"</item>
+ </string-array>
+ <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"Հնարավոր չէ շարունակել: Փորձեք նորից:"</string>
+ <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"Սարքն անհասանելի է:"</string>
+ <string name="fingerprint_error_no_space" msgid="1055819001126053318">"Հնարավոր չէ պահել մատնահետքը: Հեռացրեք առկա մատնահետքը:"</string>
+ <string name="fingerprint_error_timeout" msgid="3927186043737732875">"Մատնահետքի գրանցման ժամանակը սպառվել է: Փորձեք նորից:"</string>
+ <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Մատնահետքի գրանցման ժամանակը սպառվել է: Փորձեք նորից:"</string>
+ <string-array name="fingerprint_error_vendor">
+ <item msgid="5804600450373644614">"Վաճառողի կողմից սահմանվող սխալի հաղորդագրություն:"</item>
+ </string-array>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"կարդալ համաժամեցման կարգավորումները"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Թույլ է տալիս հավելվածին կարդալ համաժամեցման կարգավորումները հաշվի համար: Օրինակ` այն կարող է որոշել, արդյոք Մարդիկ հավելվածը համաժամեցված է հաշվի հետ:"</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"համաժամեցումը փոխարկել միացվածի և անջատվածի"</string>
@@ -1100,11 +1117,11 @@
<string name="autofill_area" msgid="3547409050889952423">"Տարածք"</string>
<string name="autofill_emirate" msgid="2893880978835698818">"Էմիրություն"</string>
<string name="permlab_readHistoryBookmarks" msgid="3775265775405106983">"կարդալ ձեր վեբ էջանիշերը և պատմությունը"</string>
- <string name="permdesc_readHistoryBookmarks" msgid="8462378226600439658">"Թույլ է տալիս հավելվածին կարդալ դիտարկչի այցելած բոլոր URL-ների պատմությունը և դիտարկչի բոլոր էջանիշերը: Նշում. այս թույլտվությունը չի կարող գործածվել կողմնակի դիտարկիչների կամ վեբ զննարկման հնարավորություններով այլ հավելվածների կողմից:"</string>
+ <string name="permdesc_readHistoryBookmarks" msgid="8462378226600439658">"Թույլ է տալիս հավելվածին կարդալ դիտարկիչի այցելած բոլոր URL-ների պատմությունը և դիտարկիչի բոլոր էջանիշերը: Նշում. այս թույլտվությունը չի կարող գործածվել կողմնակի դիտարկիչների կամ վեբ զննարկման հնարավորություններով այլ հավելվածների կողմից:"</string>
<string name="permlab_writeHistoryBookmarks" msgid="3714785165273314490">"գրել վեբ էջանիշերը և պատմությունը"</string>
- <string name="permdesc_writeHistoryBookmarks" product="tablet" msgid="6825527469145760922">"Թույլ է տալիս հավելվածին փոփոխել դիտարկչի պատմությունը կամ ձեր գրասալիկում պահված էջանիշերը: Այն կարող է թույլ տալ հավելվածին ջնջել կամ փոփոխել դիտարկչի տվյալները: Նշում. այս թույլտվությունը չի կարող գործածվել կողմնակի դիտարկիչների կամ վեբ զննարկման հնարավորություններով այլ հավելվածների կողմից:"</string>
+ <string name="permdesc_writeHistoryBookmarks" product="tablet" msgid="6825527469145760922">"Թույլ է տալիս հավելվածին փոփոխել դիտարկիչի պատմությունը կամ ձեր գրասալիկում պահված էջանիշերը: Այն կարող է թույլ տալ հավելվածին ջնջել կամ փոփոխել դիտարկիչի տվյալները: Նշում. այս թույլտվությունը չի կարող գործածվել կողմնակի դիտարկիչների կամ վեբ զննարկման հնարավորություններով այլ հավելվածների կողմից:"</string>
<string name="permdesc_writeHistoryBookmarks" product="tv" msgid="7007393823197766548">"Թույլ է տալիս հավելվածին փոփոխել դիտարկիչի պատմությունը կամ հեռուստացույցում պահված էջանիշները: Սա կարող է թույլ տալ հավելվածին ջնջել կամ փոփոխել դիտարկիչի տվյալները: Ուշադրություն. այս թույլտվությունը չի կարող հարկադրվել երրորդ կողմի դիտարկիչների կամ այլ հավելվածների կողմից, որոնք նույնպես կարողանում են վեբ էջեր բացել:"</string>
- <string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"Թույլ է տալիս հավելվածին փոփոխել դիտարկչի պատմությունը կամ ձեր հեռախոսում պահված էջանիշերը: Այն կարող է թույլ տալ հավելվածին ջնջել կամ փոփոխել դիտարկչի տվյալները: Նշում. այս թույլտվությունը չի կարող գործածվել կողմնակի դիտարկիչների կամ վեբ զննարկման հնարավորություններով այլ հավելվածների կողմից:"</string>
+ <string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"Թույլ է տալիս հավելվածին փոփոխել դիտարկիչի պատմությունը կամ ձեր հեռախոսում պահված էջանիշերը: Այն կարող է թույլ տալ հավելվածին ջնջել կամ փոփոխել դիտարկիչի տվյալները: Նշում. այս թույլտվությունը չի կարող գործածվել կողմնակի դիտարկիչների կամ վեբ զննարկման հնարավորություններով այլ հավելվածների կողմից:"</string>
<string name="permlab_setAlarm" msgid="1379294556362091814">"դնել ազդանշան"</string>
<string name="permdesc_setAlarm" msgid="316392039157473848">"Թույլ է տալիս հավելվածին սահմանել զարթուցիչի ծրագրում տեղադրված ազդանշանը: Զարթուցիչի որոշ հավելվածներ չեն կարող կիրառել այս հատկությունը:"</string>
<string name="permlab_writeVoicemail" msgid="7309899891683938100">"գրել ձայնային փոստ"</string>
@@ -1113,8 +1130,8 @@
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"Թույլ է տալիս հավելվածին ավելացնել հաղորդագրություններ ձեր ձայնային փոստի արկղում:"</string>
<string name="permlab_readVoicemail" msgid="8415201752589140137">"կարդալ ձայնային փոստը"</string>
<string name="permdesc_readVoicemail" msgid="8926534735321616550">"Ծրագրին թույլ է տալիս կարդալ ձեր ձայնային փոստը"</string>
- <string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"փոփոխել դիտարկչի աշխարհագրական տեղանքի թույլտվությունները"</string>
- <string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Թույլ է տալիս հավելվածին փոփոխել դիտարկչի աշխարհագրական դիրքի թույլտվությունները: Վնասարար հավելվածները կարող են օգտագործել սա` թույլատրելու ուղարկել տեղադրության վերաբերյալ տեղեկությունները կամայական վեբ կայքերին:"</string>
+ <string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"փոփոխել դիտարկիչի աշխարհագրական տեղանքի թույլտվությունները"</string>
+ <string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Թույլ է տալիս հավելվածին փոփոխել դիտարկիչի աշխարհագրական դիրքի թույլտվությունները: Վնասարար հավելվածները կարող են օգտագործել սա` թույլատրելու ուղարկել տեղադրության վերաբերյալ տեղեկությունները կամայական վեբ կայքերին:"</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"հաստատել փաթեթները"</string>
<string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"Թույլ է տալիս հավելվածին հաստատել, որ փաթեթը տեղադրելի է:"</string>
<string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"միանալ փաթեթի ստուգիչին"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index b0deed2..f1775d0 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -126,6 +126,7 @@
<string name="wfcRegErrorTitle" msgid="2301376280632110664">"Panggilan Wi-Fi"</string>
<string-array name="wfcOperatorErrorMessages">
</string-array>
+ <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
<string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Tidak diteruskan"</string>
<string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> setelah <xliff:g id="TIME_DELAY">{2}</xliff:g> detik"</string>
@@ -746,6 +747,22 @@
<string name="permdesc_manageFingerprint" msgid="178208705828055464">"Mengizinkan aplikasi memanggil metode untuk menambahkan dan menghapus template sidik jari untuk digunakan."</string>
<string name="permlab_useFingerprint" msgid="3150478619915124905">"gunakan perangkat keras sidik jari"</string>
<string name="permdesc_useFingerprint" msgid="9165097460730684114">"Mengizinkan aplikasi untuk menggunakan perangkat keras sidik jari untuk otentikasi"</string>
+ <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Sebagian sidik jari terdeteksi. Coba lagi."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Tidak dapat memproses sidik jari. Coba lagi."</string>
+ <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Sensor sidik jari kotor. Bersihkan dan coba lagi."</string>
+ <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"Jari digerakkan terlalu cepat. Coba lagi."</string>
+ <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"Jari digerakkan terlalu lambat. Coba lagi."</string>
+ <string-array name="fingerprint_acquired_vendor">
+ <item msgid="2892952818207766996">"Pesan kesalahan akuisisi khusus vendor 0"</item>
+ </string-array>
+ <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"Tidak dapat memproses. Coba lagi."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"Perangkat keras 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>
+ <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Waktu sidik jari habis. Coba lagi."</string>
+ <string-array name="fingerprint_error_vendor">
+ <item msgid="5804600450373644614">"Pesan kesalahan khusus vendor"</item>
+ </string-array>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"baca setelan sinkron"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Memungkinkan aplikasi membaca setelan sinkronisasi untuk sebuah akun. Misalnya, izin ini dapat menentukan apakah aplikasi Orang disinkronkan dengan sebuah akun."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"nyalakan dan matikan sinkronisasi"</string>
diff --git a/core/res/res/values-is-rIS/strings.xml b/core/res/res/values-is-rIS/strings.xml
index 5538c00..0797db1 100644
--- a/core/res/res/values-is-rIS/strings.xml
+++ b/core/res/res/values-is-rIS/strings.xml
@@ -126,6 +126,7 @@
<string name="wfcRegErrorTitle" msgid="2301376280632110664">"Wi-Fi símtöl"</string>
<string-array name="wfcOperatorErrorMessages">
</string-array>
+ <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
<string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Ekki áframsent"</string>
<string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> eftir <xliff:g id="TIME_DELAY">{2}</xliff:g> sekúndur"</string>
@@ -746,6 +747,22 @@
<string name="permdesc_manageFingerprint" msgid="178208705828055464">"Gerir forritinu kleift að beita aðferðum til að bæta við og eyða fingrafarasniðmátum til notkunar."</string>
<string name="permlab_useFingerprint" msgid="3150478619915124905">"nota fingrafarabúnað"</string>
<string name="permdesc_useFingerprint" msgid="9165097460730684114">"Leyfir forritinu að nota fingrafarabúnað til auðkenningar"</string>
+ <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Hluti fingrafars greindist. Reyndu aftur."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Ekki var hægt að vinna úr fingrafarinu. Reyndu aftur."</string>
+ <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Fingrafaraskynjarinn er óhreinn. Hreinsaðu hann og reyndu aftur."</string>
+ <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"Fingurinn hreyfðist of hratt. Reyndu aftur."</string>
+ <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"Fingurinn hreyfðist of hægt. Reyndu aftur."</string>
+ <string-array name="fingerprint_acquired_vendor">
+ <item msgid="2892952818207766996">"Skráningarvilluboð 0 frá framleiðanda"</item>
+ </string-array>
+ <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"Ekki var hægt að vinna úr þessu. Reyndu aftur."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"Vélbúnaður er ekki tiltækur."</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>
+ <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Tímamörk runnu út fyrir fingrafar. Reyndu aftur."</string>
+ <string-array name="fingerprint_error_vendor">
+ <item msgid="5804600450373644614">"Villuboð frá framleiðanda."</item>
+ </string-array>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"lesa samstillingar"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Leyfir forriti að lesa kosti samstillingar fyrir reikning. Þetta er til dæmis hægt að nota til að komast að því hvort forritið Fólk er samstillt við reikning."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"kveikja og slökkva á samstillingu"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index d8dd2ea..0269cc7 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -126,6 +126,7 @@
<string name="wfcRegErrorTitle" msgid="2301376280632110664">"Chiamate Wi-Fi"</string>
<string-array name="wfcOperatorErrorMessages">
</string-array>
+ <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
<string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: inoltro non effettuato"</string>
<string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g><xliff:g id="DIALING_NUMBER">{1}</xliff:g> dopo <xliff:g id="TIME_DELAY">{2}</xliff:g> secondi"</string>
@@ -746,6 +747,22 @@
<string name="permdesc_manageFingerprint" msgid="178208705828055464">"Consente all\'app di richiamare metodi per aggiungere e rimuovere modelli di impronte digitali da utilizzare."</string>
<string name="permlab_useFingerprint" msgid="3150478619915124905">"utilizza hardware per il riconoscimento delle impronte digitali"</string>
<string name="permdesc_useFingerprint" msgid="9165097460730684114">"Consente all\'app di utilizzare l\'hardware per il riconoscimento delle impronte digitali per eseguire l\'autenticazione"</string>
+ <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Rilevata impronta digitale parziale. Riprova."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Impossibile elaborare l\'impronta digitale. Riprova."</string>
+ <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Il sensore di impronte digitali è sporco. Puliscilo e riprova."</string>
+ <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"Movimento del dito troppo rapido. Riprova."</string>
+ <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"Movimento del dito troppo lento. Riprova."</string>
+ <string-array name="fingerprint_acquired_vendor">
+ <item msgid="2892952818207766996">"Messaggio di errore di acquisizione specifico del fornitore: 0"</item>
+ </string-array>
+ <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"Impossibile elaborare l\'impronta. Riprova."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"Hardware 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>
+ <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Timeout impronta digitale. Riprova."</string>
+ <string-array name="fingerprint_error_vendor">
+ <item msgid="5804600450373644614">"Messaggio di errore specifico del fornitore."</item>
+ </string-array>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"lettura impostazioni di sincronizz."</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Consente all\'applicazione di leggere le impostazioni di sincronizzazione per un account. Ad esempio, questa autorizzazione può determinare se l\'applicazione Persone è sincronizzata con un account."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"attivazione e disattivazione della sincronizzazione"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 1839b45..54367fe 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -128,6 +128,7 @@
<string name="wfcRegErrorTitle" msgid="2301376280632110664">"שיחות ב-Wi-Fi"</string>
<string-array name="wfcOperatorErrorMessages">
</string-array>
+ <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
<string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: ללא העברה"</string>
<string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> כעבור <xliff:g id="TIME_DELAY">{2}</xliff:g> שניות"</string>
@@ -748,6 +749,22 @@
<string name="permdesc_manageFingerprint" msgid="178208705828055464">"מאפשר לאפליקציה להפעיל שיטות להוספה ומחיקה של תבניות טביעות אצבעות שבהן ייעשה שימוש."</string>
<string name="permlab_useFingerprint" msgid="3150478619915124905">"שימוש בחומרה של טביעות אצבעות"</string>
<string name="permdesc_useFingerprint" msgid="9165097460730684114">"מאפשר לאפליקציה להשתמש בחומרה של טביעות אצבעות לצורך אימות"</string>
+ <string name="fingerprint_acquired_partial" msgid="735082772341716043">"זוהתה טביעת אצבע חלקית. נסה שוב."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"לא ניתן היה לעבד את טביעת האצבע. נסה שוב."</string>
+ <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"החיישן של טביעות האצבעות מלוכלך. נקה אותו ונסה שוב."</string>
+ <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"האצבע זזה מהר מדי, נסה שוב."</string>
+ <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"האצבע זזה לאט מדי, נסה שוב."</string>
+ <string-array name="fingerprint_acquired_vendor">
+ <item msgid="2892952818207766996">"הודעת שגיאה 0 של רכישה ספציפית לספק"</item>
+ </string-array>
+ <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"אין אפשרות לעבד. נסה שוב."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"החומרה לא זמינה."</string>
+ <string name="fingerprint_error_no_space" msgid="1055819001126053318">"לא ניתן לאחסן טביעת אצבע. הסר טביעת אצבע קיימת."</string>
+ <string name="fingerprint_error_timeout" msgid="3927186043737732875">"חלף זמן קצוב לתפוגה של טביעת אצבע. נסה שוב."</string>
+ <string name="fingerprint_error_vendor" msgid="3175724710791609491">"חלף זמן קצוב לתפוגה של טביעת אצבע. נסה שוב."</string>
+ <string-array name="fingerprint_error_vendor">
+ <item msgid="5804600450373644614">"הודעת שגיאה ספציפית לספק."</item>
+ </string-array>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"קרא את הגדרות הסינכרון"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"מאפשר לאפליקציה לקרוא את הגדרות הסנכרון של חשבון. לדוגמה, ניתן לגלות כך האם האפליקציה \'אנשים\' מסונכרן עם חשבון כלשהו."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"הפעלת וכיבוי סנכרון"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 613a2f1..e4ef4be 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -126,6 +126,7 @@
<string name="wfcRegErrorTitle" msgid="2301376280632110664">"Wi-Fi通話"</string>
<string-array name="wfcOperatorErrorMessages">
</string-array>
+ <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
<string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>:転送できません"</string>
<string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>:<xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>:<xliff:g id="DIALING_NUMBER">{1}</xliff:g> (<xliff:g id="TIME_DELAY">{2}</xliff:g>秒後)"</string>
@@ -746,6 +747,22 @@
<string name="permdesc_manageFingerprint" msgid="178208705828055464">"使用する指紋テンプレートの追加や削除を行う方法の呼び出しをアプリに許可します。"</string>
<string name="permlab_useFingerprint" msgid="3150478619915124905">"指紋ハードウェアの使用"</string>
<string name="permdesc_useFingerprint" msgid="9165097460730684114">"指紋ハードウェアを認証に使用することをアプリに許可します"</string>
+ <string name="fingerprint_acquired_partial" msgid="735082772341716043">"指紋を一部しか検出できませんでした。もう一度お試しください。"</string>
+ <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"指紋を処理できませんでした。もう一度お試しください。"</string>
+ <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"指紋センサーに汚れがあります。汚れを落としてもう一度お試しください。"</string>
+ <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"指の動きが速すぎました。もう一度お試しください。"</string>
+ <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"指の動きが遅すぎました。もう一度お試しください。"</string>
+ <string-array name="fingerprint_acquired_vendor">
+ <item msgid="2892952818207766996">"ベンダー固有の取得エラーメッセージ0"</item>
+ </string-array>
+ <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"処理できませんでした。もう一度お試しください。"</string>
+ <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"ハードウェアを利用できません。"</string>
+ <string name="fingerprint_error_no_space" msgid="1055819001126053318">"指紋を保存できません。既存の指紋を削除してください。"</string>
+ <string name="fingerprint_error_timeout" msgid="3927186043737732875">"指紋の読み取りがタイムアウトになりました。もう一度お試しください。"</string>
+ <string name="fingerprint_error_vendor" msgid="3175724710791609491">"指紋の読み取りがタイムアウトになりました。もう一度お試しください。"</string>
+ <string-array name="fingerprint_error_vendor">
+ <item msgid="5804600450373644614">"ベンダー固有のエラーメッセージです。"</item>
+ </string-array>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"同期設定の読み取り"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"アカウントの同期設定の読み取りをアプリに許可します。たとえば、連絡帳アプリがアカウントと同期しているかどうかをアプリから特定できるようになります。"</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"同期のON/OFFの切り替え"</string>
diff --git a/core/res/res/values-ka-rGE/strings.xml b/core/res/res/values-ka-rGE/strings.xml
index befe9e7..8f9e8cf 100644
--- a/core/res/res/values-ka-rGE/strings.xml
+++ b/core/res/res/values-ka-rGE/strings.xml
@@ -126,6 +126,7 @@
<string name="wfcRegErrorTitle" msgid="2301376280632110664">"დარეკვა Wi-Fi-ს მეშვეობით"</string>
<string-array name="wfcOperatorErrorMessages">
</string-array>
+ <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
<string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: არ არის გადამისამართებული"</string>
<string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> <xliff:g id="TIME_DELAY">{2}</xliff:g> წამის შემდეგ"</string>
@@ -746,6 +747,22 @@
<string name="permdesc_manageFingerprint" msgid="178208705828055464">"საშუალებას აძლევს აპლიკაციას დაამატოს ან ამოშალოს გამოსაყენებელი თითის ანაბეჭდის ნიმუშები,"</string>
<string name="permlab_useFingerprint" msgid="3150478619915124905">"თითის ანაბეჭდის აპარატის გამოყენება"</string>
<string name="permdesc_useFingerprint" msgid="9165097460730684114">"საშუალებას აძლევს აპლიკაციას გამოიყენოს ავტენთიფიკაციისათვის თითის ანაბეჭდის აპარატი"</string>
+ <string name="fingerprint_acquired_partial" msgid="735082772341716043">"აღმოჩენილია თითის ნაწილობრივი ანაბეჭდი. გთხოვთ, სცადოთ ხელახლა."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"თითის ანაბეჭდი ვერ მუშავდება. გთხოვთ, სცადოთ ხელახლა."</string>
+ <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"თითის ანაბეჭდის სენსორი დაბინძურებულია. გთხოვთ, გაასუფთაოთ და სცადოთ ხელახლა."</string>
+ <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"თითის აღება მეტისმეტად სწრაფად მოხდა. გთხოვთ, სცადოთ ხელახლა."</string>
+ <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"თითის აღება მეტისმეტად ნელა მოხდა. გთხოვთ, სცადოთ ხელახლა."</string>
+ <string-array name="fingerprint_acquired_vendor">
+ <item msgid="2892952818207766996">"მომწოდებლის მიხედვით სპეციფიკური მოპოვების შეცდომის შეტყობინება 0"</item>
+ </string-array>
+ <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"დამუშავება შეუძლებელია. სცადეთ ხელახლა."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"აპარატურა არ არის ხელმისაწვდომი."</string>
+ <string name="fingerprint_error_no_space" msgid="1055819001126053318">"თითის ანაბეჭდის შენახვა ვერ ხერხდება. გთხოვთ, ამოშალოთ არსებული თითის ანაბეჭდი."</string>
+ <string name="fingerprint_error_timeout" msgid="3927186043737732875">"თითის ანაბეჭდის ლოდინის დრო ამოიწურა. სცადეთ ხელახლა."</string>
+ <string name="fingerprint_error_vendor" msgid="3175724710791609491">"თითის ანაბეჭდის ლოდინის დრო ამოიწურა. სცადეთ ხელახლა."</string>
+ <string-array name="fingerprint_error_vendor">
+ <item msgid="5804600450373644614">"მომწოდებლის მიხედვით სპეციფიკური შეცდომის შეტყობინება."</item>
+ </string-array>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"სინქრონიზაციის პარამეტრების წაკითხვა"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"აპს შეეძლება, წაიკითხოს ანგარიშის სინქრონიზაციის პარამეტრები. მაგალითად, მას შეეძლება განსაზღვროს, არის თუ არა People აპი სინქრონიზებული ანგარიშთან."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"სინქრონიზაციის ჩართვა და გამორთვა"</string>
diff --git a/core/res/res/values-kk-rKZ/strings.xml b/core/res/res/values-kk-rKZ/strings.xml
index 8b19127..45f91b5 100644
--- a/core/res/res/values-kk-rKZ/strings.xml
+++ b/core/res/res/values-kk-rKZ/strings.xml
@@ -126,6 +126,7 @@
<string name="wfcRegErrorTitle" msgid="2301376280632110664">"Wi-Fi қоңыраулары"</string>
<string-array name="wfcOperatorErrorMessages">
</string-array>
+ <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
<string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Басқа нөмірге бағытталмады"</string>
<string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> <xliff:g id="TIME_DELAY">{2}</xliff:g> секундтан кейін"</string>
@@ -746,6 +747,22 @@
<string name="permdesc_manageFingerprint" msgid="178208705828055464">"Қолданбаға пайдаланатын саусақ ізі үлгілерін қосу және жою әдістерін шақыруға мүмкіндік береді."</string>
<string name="permlab_useFingerprint" msgid="3150478619915124905">"саусақ ізі жабдығын пайдалану"</string>
<string name="permdesc_useFingerprint" msgid="9165097460730684114">"Қолданбаға аутентификацияалу үшін саусақ ізі жабдығын пайдалануға мүмкіндік береді"</string>
+ <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Саусақ ізі ішінара анықталды. Әрекетті қайталаңыз."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Саусақ ізін өңдеу мүмкін емес. Әрекетті қайталаңыз."</string>
+ <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Саусақ ізі сенсоры лас. Тазалап, әрекетті қайталаңыз."</string>
+ <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"Саусақ тым тез қозғалды. Әрекетті қайталаңыз."</string>
+ <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"Саусақ тым баяу қозғалды. Әрекетті қайталаңыз."</string>
+ <string-array name="fingerprint_acquired_vendor">
+ <item msgid="2892952818207766996">"Жеткізушіге тән алу қатесі туралы хабар 0"</item>
+ </string-array>
+ <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"Өңдеу мүмкін емес. Әрекетті қайталаңыз."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"Жабдық қол жетімді емес."</string>
+ <string name="fingerprint_error_no_space" msgid="1055819001126053318">"Саусақ ізін сақтау мүмкін емес. Бар саусақ ізін жойыңыз."</string>
+ <string name="fingerprint_error_timeout" msgid="3927186043737732875">"Саусақ ізін күту уақыты бітті. Әрекетті қайталаңыз."</string>
+ <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Саусақ ізін күту уақыты бітті. Әрекетті қайталаңыз."</string>
+ <string-array name="fingerprint_error_vendor">
+ <item msgid="5804600450373644614">"Жеткізушіге тән қате туралы хабар."</item>
+ </string-array>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"синх параметрлерін оқу"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Қолданбаға есептік жазба синхрондау параметрлерін оқу мүмкіндігін береді. Мысалы, бұл арқылы People қолданбасының есептік жазбамен сихрондалғаны анықталуы мүмкін."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"синх қосу және өшіру арасында ауысу"</string>
diff --git a/core/res/res/values-km-rKH/strings.xml b/core/res/res/values-km-rKH/strings.xml
index 12c9661..9ccc1ab 100644
--- a/core/res/res/values-km-rKH/strings.xml
+++ b/core/res/res/values-km-rKH/strings.xml
@@ -126,6 +126,7 @@
<string name="wfcRegErrorTitle" msgid="2301376280632110664">"ការហៅតាម Wi-Fi"</string>
<string-array name="wfcOperatorErrorMessages">
</string-array>
+ <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
<string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g> ៖ មិនបានបញ្ជូនបន្ត"</string>
<string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> បន្ទាប់ពី <xliff:g id="TIME_DELAY">{2}</xliff:g> វិនាទី"</string>
@@ -746,6 +747,22 @@
<string name="permdesc_manageFingerprint" msgid="178208705828055464">"អនុញ្ញាតឲ្យកម្មវិធីប្រើវិធីសាស្ត្របន្ថែម និងលុបពុម្ពម្រាមដៃសម្រាប់ប្រើប្រាស់។"</string>
<string name="permlab_useFingerprint" msgid="3150478619915124905">"ប្រើផ្នែករឹងស្នាមម្រាមដៃ"</string>
<string name="permdesc_useFingerprint" msgid="9165097460730684114">"អនុញ្ញាតឲ្យកម្មវិធីប្រើផ្នែករឹងស្នាមម្រាមដៃសម្រាប់ការផ្ទៀងផ្ទាត់"</string>
+ <string name="fingerprint_acquired_partial" msgid="735082772341716043">"បានផ្តិតយកស្នាមម្រាមដៃមិនពេញលក្ខណៈ។ សូមព្យាយាមម្តងទៀត។"</string>
+ <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"មិនអាចដំណើរការស្នាមម្រាមដៃបានទេ។ សូមព្យាយាមម្តងទៀត។"</string>
+ <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"ឧបករណ៍ផ្តិតម្រាមដៃប្រលាក់ហើយ។ សូមសម្អាត ហើយព្យាយាមម្តងទៀត។"</string>
+ <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"ម្រាមដៃមានចលនារហ័សពេក។ សូមព្យាយាមម្តងទៀត។"</string>
+ <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"ម្រាមដៃមានចលនាយឺតពេក។ សូមព្យាយាមម្តងទៀត។"</string>
+ <string-array name="fingerprint_acquired_vendor">
+ <item msgid="2892952818207766996">"សារកំហុសនៃការទិញពីអ្នកលក់ជាក់លាក់ 0"</item>
+ </string-array>
+ <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"មិនអាចដំណើរការបានទេ។ សូមព្យាយាមម្តងទៀត។"</string>
+ <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"មិនមានផ្នែករឹងទេ។"</string>
+ <string name="fingerprint_error_no_space" msgid="1055819001126053318">"មិនអាចផ្ទុកស្នាមម្រាមដៃទេ។ សូមយកស្នាមម្រាមដៃដែលមានស្រាប់ចេញ។"</string>
+ <string name="fingerprint_error_timeout" msgid="3927186043737732875">"ការផ្តិតម្រាមដៃបានអស់ពេល។ សូមព្យាយាមម្តងទៀត។"</string>
+ <string name="fingerprint_error_vendor" msgid="3175724710791609491">"ការផ្តិតម្រាមដៃបានអស់ពេល។ សូមព្យាយាមម្តងទៀត។"</string>
+ <string-array name="fingerprint_error_vendor">
+ <item msgid="5804600450373644614">"សារកំហុសពីអ្នកលក់ជាក់លាក់។"</item>
+ </string-array>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"អានការកំណត់ធ្វើសមកាលកម្ម"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"ឲ្យកម្មវិធីអានការកំណត់ធ្វើសមកាលកម្មសម្រាប់គណនី។ ឧទាហរណ៍ វាអាចកំណត់ថាតើកម្មវិធីត្រូវបានបើកជាមួយគណនីដែរឬទេ។"</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"បិទ/បើកការធ្វើសមកាលកម្ម"</string>
diff --git a/core/res/res/values-kn-rIN/strings.xml b/core/res/res/values-kn-rIN/strings.xml
index 2f967ea..9c10119 100644
--- a/core/res/res/values-kn-rIN/strings.xml
+++ b/core/res/res/values-kn-rIN/strings.xml
@@ -126,6 +126,7 @@
<string name="wfcRegErrorTitle" msgid="2301376280632110664">"ವೈ-ಫೈ ಕರೆ ಮಾಡುವಿಕೆ"</string>
<string-array name="wfcOperatorErrorMessages">
</string-array>
+ <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
<string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: ಫಾರ್ವರ್ಡ್ ಮಾಡಲಾಗಿಲ್ಲ"</string>
<string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="TIME_DELAY">{2}</xliff:g> ಸೆಕೆಂಡುಗಳ ನಂತರ <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
@@ -746,6 +747,22 @@
<string name="permdesc_manageFingerprint" msgid="178208705828055464">"ಬಳಕೆಗೆ ಫಿಂಗರ್ಪ್ರಿಂಟ್ ಟೆಂಪ್ಲೇಟ್ಗಳನ್ನು ಸೇರಿಸಲು ಮತ್ತು ಅಳಿಸಲು ವಿಧಾನಗಳನ್ನು ಮನವಿ ಮಾಡಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string>
<string name="permlab_useFingerprint" msgid="3150478619915124905">"ಫಿಂಗರ್ಪ್ರಿಂಟ್ ಹಾರ್ಡ್ವೇರ್ ಬಳಸಿ"</string>
<string name="permdesc_useFingerprint" msgid="9165097460730684114">"ಪ್ರಮಾಣೀಕರಣಕ್ಕಾಗಿ ಫಿಂಗರ್ಪ್ರಿಂಟ್ ಹಾರ್ಡ್ವೇರ್ ಬಳಸಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅನುಮತಿಸುತ್ತದೆ"</string>
+ <string name="fingerprint_acquired_partial" msgid="735082772341716043">"ಭಾಗಶಃ ಫಿಂಗರ್ಪ್ರಿಂಟ್ ಪತ್ತೆಯಾಗಿದೆ. ದಯವಿಟ್ಟು ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"ಫಿಂಗರ್ಪ್ರಿಂಟ್ ಪ್ರಕ್ರಿಯೆಗೊಳಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ. ದಯವಿಟ್ಟು ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string>
+ <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"ಫಿಂಗರ್ಪ್ರಿಂಟ್ ಸೆನ್ಸಾರ್ ಕೊಳೆಯಾಗಿದೆ. ದಯವಿಟ್ಟು ಅದನ್ನು ಸ್ವಚ್ಛಗೊಳಿಸಿ ಹಾಗೂ ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string>
+ <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"ಬೆರಳನ್ನು ವೇಗವಾಗಿ ಸರಿಸಲಾಗಿದೆ. ದಯವಿಟ್ಟು ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string>
+ <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"ಬೆರಳನ್ನು ನಿಧಾನವಾಗಿ ಸರಿಸಲಾಗಿದೆ. ದಯವಿಟ್ಟು ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string>
+ <string-array name="fingerprint_acquired_vendor">
+ <item msgid="2892952818207766996">"ಮಾರಾಟಗಾರ-ನಿರ್ದಿಷ್ಟ ಸ್ವಾಧೀನ ದೋಷ ಸಂದೇಶ 0"</item>
+ </string-array>
+ <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"ಪ್ರಕ್ರಿಯೆಗೊಳಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ. ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"ಹಾರ್ಡ್ವೇರ್ ಲಭ್ಯವಿಲ್ಲ."</string>
+ <string name="fingerprint_error_no_space" msgid="1055819001126053318">"ಫಿಂಗರ್ಪ್ರಿಂಟ್ ಸಂಗ್ರಹಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ. ಅಸ್ತಿತ್ವದಲ್ಲಿರುವ ಫಿಂಗರ್ಪ್ರಿಂಟ್ ತೆಗೆದುಹಾಕಿ."</string>
+ <string name="fingerprint_error_timeout" msgid="3927186043737732875">"ಫಿಂಗರ್ಪ್ರಿಂಟ್ ಅವಧಿ ಮೀರಿದೆ. ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string>
+ <string name="fingerprint_error_vendor" msgid="3175724710791609491">"ಫಿಂಗರ್ಪ್ರಿಂಟ್ ಅವಧಿ ಮೀರಿದೆ. ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string>
+ <string-array name="fingerprint_error_vendor">
+ <item msgid="5804600450373644614">"ಮಾರಾಟಗಾರ-ನಿರ್ದಿಷ್ಟ ದೋಷ ಸಂದೇಶ."</item>
+ </string-array>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"ಸಿಂಕ್ ಸೆಟ್ಟಿಂಗ್ಗಳನ್ನು ರೀಡ್ ಮಾಡು"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"ಒಂದು ಖಾತೆಯ ಸಿಂಕ್ ಸೆಟ್ಟಿಂಗ್ಗಳನ್ನು ಓದಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅವಕಾಶ ನೀಡುತ್ತದೆ. ಉದಾಹರಣೆಗೆ, ಖಾತೆಯೊಂದಿಗೆ ಜನರ ಅಪ್ಲಿಕೇಶನ್ ಸಿಂಕ್ ಮಾಡಲಾಗಿದೆಯೇ ಎಂಬುದನ್ನು ಇದು ನಿರ್ಧರಿಸಬಹುದು."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"ಸಿಂಕ್ ಆನ್ ಮತ್ತು ಸಿಂಕ್ ಆಫ್ ಟಾಗಲ್ ಮಾಡಿ"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 654fffb..6da084e 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -126,6 +126,7 @@
<string name="wfcRegErrorTitle" msgid="2301376280632110664">"Wi-Fi 통화"</string>
<string-array name="wfcOperatorErrorMessages">
</string-array>
+ <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
<string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: 착신전환 안됨"</string>
<string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g><xliff:g id="TIME_DELAY">{2}</xliff:g>초 후"</string>
@@ -746,6 +747,22 @@
<string name="permdesc_manageFingerprint" msgid="178208705828055464">"사용할 지문 템플릿의 추가 및 삭제 메소드를 앱에서 실행하도록 허용합니다."</string>
<string name="permlab_useFingerprint" msgid="3150478619915124905">"지문 하드웨어 사용"</string>
<string name="permdesc_useFingerprint" msgid="9165097460730684114">"앱에서 지문 하드웨어를 인증에 사용하도록 허용합니다."</string>
+ <string name="fingerprint_acquired_partial" msgid="735082772341716043">"지문이 일부만 감지되었습니다. 다시 시도해 주세요."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"지문을 처리할 수 없습니다. 다시 시도해 주세요."</string>
+ <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"지문 센서가 더럽습니다. 깨끗이 닦고 다시 시도하세요."</string>
+ <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"손가락을 너무 빨리 움직였습니다. 다시 시도해 주세요."</string>
+ <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"손가락을 너무 느리게 움직였습니다. 다시 시도해 주세요."</string>
+ <string-array name="fingerprint_acquired_vendor">
+ <item msgid="2892952818207766996">"공급업체별 획득 오류 메시지 0"</item>
+ </string-array>
+ <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"처리할 수 없습니다. 다시 시도하세요."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"하드웨어를 사용할 수 없습니다."</string>
+ <string name="fingerprint_error_no_space" msgid="1055819001126053318">"지문을 저장할 수 없습니다. 기존 지문을 삭제하세요."</string>
+ <string name="fingerprint_error_timeout" msgid="3927186043737732875">"지문 인식 시간이 초과되었습니다. 다시 시도하세요."</string>
+ <string name="fingerprint_error_vendor" msgid="3175724710791609491">"지문 인식 시간이 초과되었습니다. 다시 시도하세요."</string>
+ <string-array name="fingerprint_error_vendor">
+ <item msgid="5804600450373644614">"공급업체별 오류 메시지"</item>
+ </string-array>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"동기화 설정 읽기"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"앱이 계정의 동기화 설정을 읽을 수 있도록 허용합니다. 예를 들어, 계정에서 주소록 앱을 동기화할지 여부를 확인할 수 있습니다."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"동기화 사용 및 사용 중지 전환"</string>
diff --git a/core/res/res/values-ky-rKG/strings.xml b/core/res/res/values-ky-rKG/strings.xml
index 5905240..7696fbb 100644
--- a/core/res/res/values-ky-rKG/strings.xml
+++ b/core/res/res/values-ky-rKG/strings.xml
@@ -194,6 +194,7 @@
<string name="wfcRegErrorTitle" msgid="2301376280632110664">"Wi-Fi Чалуу"</string>
<string-array name="wfcOperatorErrorMessages">
</string-array>
+ <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
<!-- no translation found for cfTemplateNotForwarded (1683685883841272560) -->
<skip />
<!-- no translation found for cfTemplateForwarded (1302922117498590521) -->
@@ -948,6 +949,22 @@
<string name="permdesc_manageFingerprint" msgid="178208705828055464">"Колдонмого пайдалануу үчүн манжа изинин үлгүлөрүн кошуу жана жок кылуу мүмкүндүгүн берет."</string>
<string name="permlab_useFingerprint" msgid="3150478619915124905">"манжа изинин аппараттык камсыздоосун колдонуу"</string>
<string name="permdesc_useFingerprint" msgid="9165097460730684114">"Колдонмого аныктыгын текшерүү үчүн манжа изинин аппараттык камсыздоосун пайдалануу мүмкүндүгүн берет"</string>
+ <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Манжа изи жарым-жартылай аныкталды. Кайра аракет кылыңыз."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Манжа изи иштелбей койду. Кайра аракет кылыңыз."</string>
+ <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Манжа изинин сенсору кирдеп калган. Тазалап, кайра аракет кылыңыз."</string>
+ <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"Манжа өтө тез жылдырылды. Кайра аракет кылыңыз."</string>
+ <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"Манжа өтө жай жылдырылды. Кайра аракет кылыңыз."</string>
+ <string-array name="fingerprint_acquired_vendor">
+ <item msgid="2892952818207766996">"Жеткирүүчүгө тиешелүү ээ болуу катасы жөнүндө билдирүү 0"</item>
+ </string-array>
+ <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"Иштетилбей жатат. Кайра аракет кылыңыз."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"Аппараттык камсыздоо жеткиликтүү эмес."</string>
+ <string name="fingerprint_error_no_space" msgid="1055819001126053318">"Манжа изин сактоо мүмкүн эмес. Учурдагы манжа изин алып салыңыз."</string>
+ <string name="fingerprint_error_timeout" msgid="3927186043737732875">"Манжа изин күтүү мөөнөтү бүттү. Кайра аракет кылыңыз."</string>
+ <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Манжа изин күтүү мөөнөтү бүттү. Кайра аракет кылыңыз."</string>
+ <string-array name="fingerprint_error_vendor">
+ <item msgid="5804600450373644614">"Жеткирүүчүгө тиешелүү ката жөнүндө билдирүү."</item>
+ </string-array>
<!-- no translation found for permlab_readSyncSettings (6201810008230503052) -->
<skip />
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Колдонмого эсеп менен синхрондошуу тууралоолорун окуганга уруксат берет. Мисалы, Кишилер колдонмосу эсеп менен синхрондошкондугун аныктай алат."</string>
diff --git a/core/res/res/values-lo-rLA/strings.xml b/core/res/res/values-lo-rLA/strings.xml
index c932d2d..b8e2cff 100644
--- a/core/res/res/values-lo-rLA/strings.xml
+++ b/core/res/res/values-lo-rLA/strings.xml
@@ -126,6 +126,7 @@
<string name="wfcRegErrorTitle" msgid="2301376280632110664">"ການໂທ Wi-Fi"</string>
<string-array name="wfcOperatorErrorMessages">
</string-array>
+ <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
<string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: ບໍ່ຖືກສົ່ງຕໍ່"</string>
<string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> ຫຼັງຈາກ <xliff:g id="TIME_DELAY">{2}</xliff:g> ວິນາທີ"</string>
@@ -746,6 +747,22 @@
<string name="permdesc_manageFingerprint" msgid="178208705828055464">"ອະນຸຍາດໃຫ້ແອັບເຮັດໃຫ້ວິທີການຕ່າງໆເພີ່ມ ແລະລຶບແມ່ແບບລາຍນີ້ວມືສຳລັບການໃຊ້."</string>
<string name="permlab_useFingerprint" msgid="3150478619915124905">"ໃຊ້ຮາດແວລາຍນີ້ວມື"</string>
<string name="permdesc_useFingerprint" msgid="9165097460730684114">"ອະນຸຍາດໃຫ້ແອັບນຳໃຊ້ຮາດແວລາຍນີ້ວມືສຳລັບການຮັບຮອງ"</string>
+ <string name="fingerprint_acquired_partial" msgid="735082772341716043">"ກວດພົບລາຍນີ້ວມືບາງສ່ວນແລ້ວ. ກະລຸນາລອງໃໝ່ອີກ."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"ບໍ່ສາມາດດຳເນີນການລາຍນີ້ວມືໄດ້. ກະລຸນາລອງໃໝ່ອີກ."</string>
+ <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"ເຊັນເຊີລາຍນີ້ວມືເປື້ອນ. ກະລຸນາທຳຄວາມສະອາດ ແລະລອງໃໝ່ອີກ."</string>
+ <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"ຍ້າຍນີ້ວມືໄປໄວເກີນໄປ. ກະລຸນາລອງໃໝ່ອີກ."</string>
+ <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"ຍ້າຍນີ້ວມືໄປຊ້ເກີນໄປ. ກະລຸນາລອງໃໝ່ອີກ."</string>
+ <string-array name="fingerprint_acquired_vendor">
+ <item msgid="2892952818207766996">"ຂໍ້ຄວາມການຜິດພາດການໄດ້ມາສະເພາະຜູ້ຂາຍ 0"</item>
+ </string-array>
+ <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"ບໍ່ສາມາດປະມວນຜົນໄດ້. ລອງໃໝ່ອີກ."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"ບໍ່ມີຮາດແວໃຫ້ຢູ່."</string>
+ <string name="fingerprint_error_no_space" msgid="1055819001126053318">"ບໍ່ສາມາດເກັບຮັກສາລາຍນີ້ວມືໄວ້ໄດ້. ກະລຸນາເອົາລາຍນີ້ວມືທີ່ມີຢູ່ອອກໄປ."</string>
+ <string name="fingerprint_error_timeout" msgid="3927186043737732875">"ເວລາລາຍນີ້ວມືບໍ່ເຂົ້າເຖິງໄດ້. ລອງໃໝ່ອີກ."</string>
+ <string name="fingerprint_error_vendor" msgid="3175724710791609491">"ເວລາລາຍນີ້ວມືບໍ່ເຂົ້າເຖິງໄດ້. ລອງໃໝ່ອີກ."</string>
+ <string-array name="fingerprint_error_vendor">
+ <item msgid="5804600450373644614">"ຂໍ້ຄວາມການຂັດຂ້ອງສະເພາະຜູ້ຂາຍ"</item>
+ </string-array>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"ອ່ານການຕັ້ງຄ່າຊິ້ງຂໍ້ມູນ"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"ອະນຸຍາດໃຫ້ແອັບຯ ອ່ານການຕັ້ງຄ່າການຊິ້ງຂໍ້ມູນຂອງບັນຊີໄດ້. ຕົວຢ່າງເຊັ່ນ: ມັນຈະສາມາດກວດສອບໄດ້ແອັບຯ People ຖືກຊິ້ງຂໍ້ມູນກັບບັນຊີໃດນຶ່ງແລ້ວຫຼືຍັງ."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"ສະລັບການເປີດ ແລະປິດການຊິ້ງຂໍ້ມູນ"</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index 42b7770..f1f81bd 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -128,6 +128,7 @@
<string name="wfcRegErrorTitle" msgid="2301376280632110664">"„Wi-Fi“ skambinimas"</string>
<string-array name="wfcOperatorErrorMessages">
</string-array>
+ <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
<string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: neperadresuota"</string>
<string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> po <xliff:g id="TIME_DELAY">{2}</xliff:g> sek."</string>
@@ -748,6 +749,22 @@
<string name="permdesc_manageFingerprint" msgid="178208705828055464">"Leidžiama programai aktyvinti metodus, norint pridėti ir ištrinti naudojamus kontrolinių kodų šablonus."</string>
<string name="permlab_useFingerprint" msgid="3150478619915124905">"naudoti kontrolinio kodo aparatinę įrangą"</string>
<string name="permdesc_useFingerprint" msgid="9165097460730684114">"Leidžiama programai naudoti kontrolinio kodo aparatinę įrangą tapatybei nustatyti"</string>
+ <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Aptiktas dalinis kontrolinis kodas. Bandykite dar kartą."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Nepavyko apdoroti kontrolinio kodo. Bandykite dar kartą."</string>
+ <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Kontrolinio kodo jutiklis purvinas. Nuvalykite ir bandykite dar kartą."</string>
+ <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"Per greitai judinate pirštą. Bandykite dar kartą."</string>
+ <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"Per lėtai judinate pirštą. Bandykite dar kartą."</string>
+ <string-array name="fingerprint_acquired_vendor">
+ <item msgid="2892952818207766996">"Konkretaus paslaugų teikėjo įgijimo klaidos pranešimas (0)"</item>
+ </string-array>
+ <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"Nepavyko apdoroti. Bandykite dar kartą."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"Aparatinė įranga negalima."</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>
+ <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Baigėsi kontrolinio kodo nustatymo skirtasis laikas. Bandykite dar kartą."</string>
+ <string-array name="fingerprint_error_vendor">
+ <item msgid="5804600450373644614">"Konkretaus paslaugų teikėjo klaidos pranešimas"</item>
+ </string-array>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"skaityti sinchronizavimo nustatymus"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Leidžiama programai skaityti ir sinchronizuoti paskyros nustatymus. Pvz., taip gali būti nustatoma, ar su paskyra sinchronizuota Žmonių programa."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"įjungti arba išjungti sinchronizavimą"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 1be0eeb..a55ea95 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -127,6 +127,7 @@
<string name="wfcRegErrorTitle" msgid="2301376280632110664">"Wi-Fi zvani"</string>
<string-array name="wfcOperatorErrorMessages">
</string-array>
+ <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
<string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: nav pāradresēts"</string>
<string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> pēc <xliff:g id="TIME_DELAY">{2}</xliff:g> sekundes(-ēm)"</string>
@@ -747,6 +748,22 @@
<string name="permdesc_manageFingerprint" msgid="178208705828055464">"Atļauj lietotnei izsaukt metodes izmantojamo pirkstu nospiedumu veidņu pievienošanai un dzēšanai."</string>
<string name="permlab_useFingerprint" msgid="3150478619915124905">"lietot pirkstu nospiedumu aparatūru"</string>
<string name="permdesc_useFingerprint" msgid="9165097460730684114">"Atļauj lietotnei izmantot pirkstu nospiedumu aparatūru autentificēšanai."</string>
+ <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Noteikts daļējs pirksta nospiedums. Lūdzu, mēģiniet vēlreiz."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Nevarēja apstrādāt pirksta nospiedumu. Lūdzu, mēģiniet vēlreiz."</string>
+ <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Pirkstu nospiedumu sensors ir netīrs. Lūdzu, notīriet to un mēģiniet vēlreiz."</string>
+ <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"Pārāk ātra pirksta kustība. Lūdzu, mēģiniet vēlreiz."</string>
+ <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"Pārāk lēna pirksta kustība. Lūdzu, mēģiniet vēlreiz."</string>
+ <string-array name="fingerprint_acquired_vendor">
+ <item msgid="2892952818207766996">"Piegādātāja noteikts iegūšanas kļūdas ziņojums 0"</item>
+ </string-array>
+ <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"Nevar apstrādāt pirksta nospiedumu. Mēģiniet vēlreiz."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"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>
+ <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Pirkstu nospiedumu nolasīšanas aparatūras noildze. Mēģiniet vēlreiz."</string>
+ <string-array name="fingerprint_error_vendor">
+ <item msgid="5804600450373644614">"Piegādātāja noteikts kļūdas ziņojums"</item>
+ </string-array>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"lasīt sinhronizācijas iestatījumus"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Ļauj lietotnei lasīt konta sinhronizācijas iestatījumus. Piemēram, šādi var noteikt, vai lietotne Personas ir sinhronizēta ar kontu."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"ieslēgt un izslēgt sinhronizāciju"</string>
diff --git a/core/res/res/values-mcc310-mnc260-af/strings.xml b/core/res/res/values-mcc310-mnc260-af/strings.xml
index e33c27b..2d4b749 100644
--- a/core/res/res/values-mcc310-mnc260-af/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-af/strings.xml
@@ -25,4 +25,5 @@
<string-array name="wfcOperatorErrorMessages">
<item msgid="931634632269046788">"Wi-Fi-oproepe is nie beskikbaar nie. Kontak jou diensverskaffer om Wi-Fi-oproepe te aktiveer."</item>
</string-array>
+ <string name="wfcSpnFormat" msgid="4982938551498609442">"%s Wi-Fi-oproep"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc260-am/strings.xml b/core/res/res/values-mcc310-mnc260-am/strings.xml
index 24a88097..126631a 100644
--- a/core/res/res/values-mcc310-mnc260-am/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-am/strings.xml
@@ -25,4 +25,5 @@
<string-array name="wfcOperatorErrorMessages">
<item msgid="931634632269046788">"Wi-Fi ጥሪ ማድረጊያ አይገኝም። የ Wi-Fi ጥሪ ማድረጊያን ለማንቃት የእርስዎን አገልግሎት አቅራቢ ያነጋግሩ።"</item>
</string-array>
+ <string name="wfcSpnFormat" msgid="4982938551498609442">"የ%s Wi-Fi ጥሪ"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc260-ar/strings.xml b/core/res/res/values-mcc310-mnc260-ar/strings.xml
index 8c2e6f6..1175eec 100644
--- a/core/res/res/values-mcc310-mnc260-ar/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-ar/strings.xml
@@ -25,4 +25,5 @@
<string-array name="wfcOperatorErrorMessages">
<item msgid="931634632269046788">"الاتصال عبر Wi-Fi ليس متوفرًا. اتصل بمشغل شبكة الجوّال لتمكين الاتصال عبر Wi-Fi."</item>
</string-array>
+ <string name="wfcSpnFormat" msgid="4982938551498609442">"%s جارٍ الاتصال عبر Wi-Fi"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc260-bg/strings.xml b/core/res/res/values-mcc310-mnc260-bg/strings.xml
index 86ad9ab..be1d0d3 100644
--- a/core/res/res/values-mcc310-mnc260-bg/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-bg/strings.xml
@@ -25,4 +25,5 @@
<string-array name="wfcOperatorErrorMessages">
<item msgid="931634632269046788">"Обажданията през Wi-Fi не са налице. Свържете се с оператора си, за да ги активирате."</item>
</string-array>
+ <string name="wfcSpnFormat" msgid="4982938551498609442">"%s – обаждания през Wi-Fi"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc260-bn-rBD/strings.xml b/core/res/res/values-mcc310-mnc260-bn-rBD/strings.xml
index 97df2db..0135108 100644
--- a/core/res/res/values-mcc310-mnc260-bn-rBD/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-bn-rBD/strings.xml
@@ -25,4 +25,5 @@
<string-array name="wfcOperatorErrorMessages">
<item msgid="931634632269046788">"Wi-Fi কলিং উপলব্ধ নেই৷ Wi-Fi কলিং সক্ষম করতে আপনার পরিষেবা প্রদানকারীর সাথে যোগাযোগ করুন৷"</item>
</string-array>
+ <string name="wfcSpnFormat" msgid="4982938551498609442">"%s Wi-Fi কলিং"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc260-ca/strings.xml b/core/res/res/values-mcc310-mnc260-ca/strings.xml
index e7d2158..5a10585 100644
--- a/core/res/res/values-mcc310-mnc260-ca/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-ca/strings.xml
@@ -25,4 +25,5 @@
<string-array name="wfcOperatorErrorMessages">
<item msgid="931634632269046788">"Les trucades per Wi-Fi no estan disponibles. Contacta amb l\'operador de telefonia mòbil per activar-les."</item>
</string-array>
+ <string name="wfcSpnFormat" msgid="4982938551498609442">"Trucada de Wi-Fi de: %s"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc260-cs/strings.xml b/core/res/res/values-mcc310-mnc260-cs/strings.xml
index 987284d..8f1d5dc 100644
--- a/core/res/res/values-mcc310-mnc260-cs/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-cs/strings.xml
@@ -25,4 +25,5 @@
<string-array name="wfcOperatorErrorMessages">
<item msgid="931634632269046788">"Volání přes Wi-Fi není k dispozici. Kontaktujte operátora, aby volání přes Wi-Fi aktivoval."</item>
</string-array>
+ <string name="wfcSpnFormat" msgid="4982938551498609442">"Volání přes Wi-Fi: %s"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc260-da/strings.xml b/core/res/res/values-mcc310-mnc260-da/strings.xml
index 20532b2..4e6c2d0 100644
--- a/core/res/res/values-mcc310-mnc260-da/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-da/strings.xml
@@ -25,4 +25,5 @@
<string-array name="wfcOperatorErrorMessages">
<item msgid="931634632269046788">"Opkald via Wi-Fi er ikke muligt. Kontakt dit mobilselskab for at aktivere Opkald via Wi-Fi."</item>
</string-array>
+ <string name="wfcSpnFormat" msgid="4982938551498609442">"%s Wi-Fi-opkald"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc260-de/strings.xml b/core/res/res/values-mcc310-mnc260-de/strings.xml
index e69d062..f4838eb 100644
--- a/core/res/res/values-mcc310-mnc260-de/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-de/strings.xml
@@ -25,4 +25,5 @@
<string-array name="wfcOperatorErrorMessages">
<item msgid="931634632269046788">"WLAN-Anrufe sind nicht möglich. Bitte kontaktieren Sie Ihren Mobilfunkanbieter bezüglich der Aktivierung der WLAN-Telefonie."</item>
</string-array>
+ <string name="wfcSpnFormat" msgid="4982938551498609442">"%s WLAN-Anrufe"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc260-el/strings.xml b/core/res/res/values-mcc310-mnc260-el/strings.xml
index cd5d615..b3d3aed 100644
--- a/core/res/res/values-mcc310-mnc260-el/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-el/strings.xml
@@ -25,4 +25,5 @@
<string-array name="wfcOperatorErrorMessages">
<item msgid="931634632269046788">"Η κλήση Wi-Fi δεν είναι διαθέσιμη. Επικοινωνήστε με την εταιρεία κινητής τηλεφωνίας σας για να ενεργοποιήσετε την κλήση Wi-Fi."</item>
</string-array>
+ <string name="wfcSpnFormat" msgid="4982938551498609442">"%s Κλήση Wi-Fi"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc260-en-rAU/strings.xml b/core/res/res/values-mcc310-mnc260-en-rAU/strings.xml
index 3f764c0..8a9da8f 100644
--- a/core/res/res/values-mcc310-mnc260-en-rAU/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-en-rAU/strings.xml
@@ -25,4 +25,5 @@
<string-array name="wfcOperatorErrorMessages">
<item msgid="931634632269046788">"Wi-Fi Calling isn\'t available. Contact your operator to enable Wi-Fi Calling."</item>
</string-array>
+ <string name="wfcSpnFormat" msgid="4982938551498609442">"%s Wi-Fi Calling"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc260-en-rGB/strings.xml b/core/res/res/values-mcc310-mnc260-en-rGB/strings.xml
index 3f764c0..8a9da8f 100644
--- a/core/res/res/values-mcc310-mnc260-en-rGB/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-en-rGB/strings.xml
@@ -25,4 +25,5 @@
<string-array name="wfcOperatorErrorMessages">
<item msgid="931634632269046788">"Wi-Fi Calling isn\'t available. Contact your operator to enable Wi-Fi Calling."</item>
</string-array>
+ <string name="wfcSpnFormat" msgid="4982938551498609442">"%s Wi-Fi Calling"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc260-en-rIN/strings.xml b/core/res/res/values-mcc310-mnc260-en-rIN/strings.xml
index 3f764c0..8a9da8f 100644
--- a/core/res/res/values-mcc310-mnc260-en-rIN/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-en-rIN/strings.xml
@@ -25,4 +25,5 @@
<string-array name="wfcOperatorErrorMessages">
<item msgid="931634632269046788">"Wi-Fi Calling isn\'t available. Contact your operator to enable Wi-Fi Calling."</item>
</string-array>
+ <string name="wfcSpnFormat" msgid="4982938551498609442">"%s Wi-Fi Calling"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc260-es-rUS/strings.xml b/core/res/res/values-mcc310-mnc260-es-rUS/strings.xml
index 3e13ebb..3f37a6d 100644
--- a/core/res/res/values-mcc310-mnc260-es-rUS/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-es-rUS/strings.xml
@@ -25,4 +25,5 @@
<string-array name="wfcOperatorErrorMessages">
<item msgid="931634632269046788">"Las llamadas con Wi-Fi no están disponibles. Comunícate con el proveedor para habilitar la función."</item>
</string-array>
+ <string name="wfcSpnFormat" msgid="4982938551498609442">"Llamada por Wi-Fi de %s"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc260-es/strings.xml b/core/res/res/values-mcc310-mnc260-es/strings.xml
index 9dea2f1..24017c2 100644
--- a/core/res/res/values-mcc310-mnc260-es/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-es/strings.xml
@@ -25,4 +25,5 @@
<string-array name="wfcOperatorErrorMessages">
<item msgid="931634632269046788">"Las llamadas Wi-Fi no están disponibles. Ponte en contacto con tu operador para habilitarlas."</item>
</string-array>
+ <string name="wfcSpnFormat" msgid="4982938551498609442">"Llamada Wi-Fi de %s"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc260-et-rEE/strings.xml b/core/res/res/values-mcc310-mnc260-et-rEE/strings.xml
index 465507a..5d3d277 100644
--- a/core/res/res/values-mcc310-mnc260-et-rEE/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-et-rEE/strings.xml
@@ -25,4 +25,5 @@
<string-array name="wfcOperatorErrorMessages">
<item msgid="931634632269046788">"WiFi-kõned pole saadaval. WiFi-kõnede lubamiseks võtke ühendust operaatoriga."</item>
</string-array>
+ <string name="wfcSpnFormat" msgid="4982938551498609442">"%s WiFi kaudu helistamine"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc260-eu-rES/strings.xml b/core/res/res/values-mcc310-mnc260-eu-rES/strings.xml
index 23194ff..3bcedf7 100644
--- a/core/res/res/values-mcc310-mnc260-eu-rES/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-eu-rES/strings.xml
@@ -25,4 +25,5 @@
<string-array name="wfcOperatorErrorMessages">
<item msgid="931634632269046788">"Wi-Fi bidezko deiak ez daude erabilgarri. Gaitzeko, jarri operadorearekin harremanetan."</item>
</string-array>
+ <string name="wfcSpnFormat" msgid="4982938551498609442">"%s Wi-Fi bidezko deiak"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc260-fa/strings.xml b/core/res/res/values-mcc310-mnc260-fa/strings.xml
index 083a613..198d9e2 100644
--- a/core/res/res/values-mcc310-mnc260-fa/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-fa/strings.xml
@@ -25,4 +25,5 @@
<string-array name="wfcOperatorErrorMessages">
<item msgid="931634632269046788">"تماس از طریق Wi-Fi امکانپذیر نیست. برای فعال کردن تماس Wi-Fi، با شرکت مخابراتیتان تماس بگیرید."</item>
</string-array>
+ <string name="wfcSpnFormat" msgid="4982938551498609442">"تماس %s Wi-Fi"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc260-fi/strings.xml b/core/res/res/values-mcc310-mnc260-fi/strings.xml
index 43efe46..a6c0986f 100644
--- a/core/res/res/values-mcc310-mnc260-fi/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-fi/strings.xml
@@ -25,4 +25,5 @@
<string-array name="wfcOperatorErrorMessages">
<item msgid="931634632269046788">"Wi-Fi-puhelut eivät ole käytettävissä. Pyydä operaattoriasi ottamaan Wi-Fi-puhelut käyttöön."</item>
</string-array>
+ <string name="wfcSpnFormat" msgid="4982938551498609442">"Wi-Fi-puhelut: %s"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc260-fr-rCA/strings.xml b/core/res/res/values-mcc310-mnc260-fr-rCA/strings.xml
index 2d63c89..92f5e79 100644
--- a/core/res/res/values-mcc310-mnc260-fr-rCA/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-fr-rCA/strings.xml
@@ -25,4 +25,5 @@
<string-array name="wfcOperatorErrorMessages">
<item msgid="931634632269046788">"La fonction d\'appel par Wi-Fi n\'est pas disponible. Communiquez avec votre fournisseur de services pour activer les appels par Wi-Fi."</item>
</string-array>
+ <string name="wfcSpnFormat" msgid="4982938551498609442">"Appels Wi-Fi %s"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc260-fr/strings.xml b/core/res/res/values-mcc310-mnc260-fr/strings.xml
index 358bec8..b4fe869 100644
--- a/core/res/res/values-mcc310-mnc260-fr/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-fr/strings.xml
@@ -25,4 +25,5 @@
<string-array name="wfcOperatorErrorMessages">
<item msgid="931634632269046788">"Les appels Wi-Fi ne sont pas disponibles. Contactez votre opérateur pour les activer."</item>
</string-array>
+ <string name="wfcSpnFormat" msgid="4982938551498609442">"Appels Wi-Fi %s"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc260-gl-rES/strings.xml b/core/res/res/values-mcc310-mnc260-gl-rES/strings.xml
index 32ef3d3..11080f3 100644
--- a/core/res/res/values-mcc310-mnc260-gl-rES/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-gl-rES/strings.xml
@@ -25,4 +25,5 @@
<string-array name="wfcOperatorErrorMessages">
<item msgid="931634632269046788">"As chamadas por wifi non están dispoñible. Contacta co teu operador para activar as chamadas por wifi."</item>
</string-array>
+ <string name="wfcSpnFormat" msgid="4982938551498609442">"Chamadas wifi de %s"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc260-hi/strings.xml b/core/res/res/values-mcc310-mnc260-hi/strings.xml
index 93193fd..fb65db3 100644
--- a/core/res/res/values-mcc310-mnc260-hi/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-hi/strings.xml
@@ -25,4 +25,5 @@
<string-array name="wfcOperatorErrorMessages">
<item msgid="931634632269046788">"Wi-Fi कॉलिंग उपलब्ध नहीं है. वाई-फ़ाई कॉलिंग सक्षम करने के लिए अपने वाहक से संपर्क करें."</item>
</string-array>
+ <string name="wfcSpnFormat" msgid="4982938551498609442">"%s वाई-फ़ाई कॉलिंग"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc260-hr/strings.xml b/core/res/res/values-mcc310-mnc260-hr/strings.xml
index 30e22f1..7442481 100644
--- a/core/res/res/values-mcc310-mnc260-hr/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-hr/strings.xml
@@ -25,4 +25,5 @@
<string-array name="wfcOperatorErrorMessages">
<item msgid="931634632269046788">"Wi-Fi pozivi nisu dostupni. Obratite se mobilnom operateru radi omogućivanja Wi-Fi poziva."</item>
</string-array>
+ <string name="wfcSpnFormat" msgid="4982938551498609442">"%s Wi-Fi pozivanje"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc260-hu/strings.xml b/core/res/res/values-mcc310-mnc260-hu/strings.xml
index 44b0a9e..c8241e9 100644
--- a/core/res/res/values-mcc310-mnc260-hu/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-hu/strings.xml
@@ -25,4 +25,5 @@
<string-array name="wfcOperatorErrorMessages">
<item msgid="931634632269046788">"A Wi-Fi-hívás nem érhető el. Engedélyezéséhez forduljon szolgáltatójához."</item>
</string-array>
+ <string name="wfcSpnFormat" msgid="4982938551498609442">"%s Wi-Fi-hívás"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc260-hy-rAM/strings.xml b/core/res/res/values-mcc310-mnc260-hy-rAM/strings.xml
index aa63f70..9296ab4 100644
--- a/core/res/res/values-mcc310-mnc260-hy-rAM/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-hy-rAM/strings.xml
@@ -25,4 +25,5 @@
<string-array name="wfcOperatorErrorMessages">
<item msgid="931634632269046788">"Wi-Fi-ի միջոցով զանգերն անհասանելի են: Դրանք միացնելու համար դիմեք ձեր օպերատորին:"</item>
</string-array>
+ <string name="wfcSpnFormat" msgid="4982938551498609442">"%s Wi-Fi զանգեր"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc260-in/strings.xml b/core/res/res/values-mcc310-mnc260-in/strings.xml
index 669d384..d697236 100644
--- a/core/res/res/values-mcc310-mnc260-in/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-in/strings.xml
@@ -25,4 +25,5 @@
<string-array name="wfcOperatorErrorMessages">
<item msgid="931634632269046788">"Panggilan Wi-Fi tidak tersedia. Hubungi operator untuk mengaktifkan Panggilan Wi-Fi."</item>
</string-array>
+ <string name="wfcSpnFormat" msgid="4982938551498609442">"%s Panggilan Wi-Fi"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc260-is-rIS/strings.xml b/core/res/res/values-mcc310-mnc260-is-rIS/strings.xml
index 2549756..1ba95b3 100644
--- a/core/res/res/values-mcc310-mnc260-is-rIS/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-is-rIS/strings.xml
@@ -25,4 +25,5 @@
<string-array name="wfcOperatorErrorMessages">
<item msgid="931634632269046788">"Wi-Fi símtöl eru ekki í boði. Hafðu samband við símafyrirtækið þitt til að virkja Wi-Fi símtöl."</item>
</string-array>
+ <string name="wfcSpnFormat" msgid="4982938551498609442">"%s Wi-Fi símtöl"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc260-it/strings.xml b/core/res/res/values-mcc310-mnc260-it/strings.xml
index 7f58424..f84e8f7 100644
--- a/core/res/res/values-mcc310-mnc260-it/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-it/strings.xml
@@ -25,4 +25,5 @@
<string-array name="wfcOperatorErrorMessages">
<item msgid="931634632269046788">"Le chiamate Wi-Fi non sono disponibili. Contatta il tuo operatore per attivarle."</item>
</string-array>
+ <string name="wfcSpnFormat" msgid="4982938551498609442">"Chiamata Wi-Fi %s"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc260-iw/strings.xml b/core/res/res/values-mcc310-mnc260-iw/strings.xml
index 161c868..b882387 100644
--- a/core/res/res/values-mcc310-mnc260-iw/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-iw/strings.xml
@@ -25,4 +25,5 @@
<string-array name="wfcOperatorErrorMessages">
<item msgid="931634632269046788">"שיחות ב-Wi-Fi אינן זמינות. צור קשר עם הספק שלך כדי להפעיל שיחות ב-Wi-Fi."</item>
</string-array>
+ <string name="wfcSpnFormat" msgid="4982938551498609442">"שיחות Wi-Fi של %s"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc260-ja/strings.xml b/core/res/res/values-mcc310-mnc260-ja/strings.xml
index 237b606..9933a50 100644
--- a/core/res/res/values-mcc310-mnc260-ja/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-ja/strings.xml
@@ -25,4 +25,5 @@
<string-array name="wfcOperatorErrorMessages">
<item msgid="931634632269046788">"Wi-Fi通話が利用できません。携帯通信会社に連絡してWi-Fi通話を有効にしてください。"</item>
</string-array>
+ <string name="wfcSpnFormat" msgid="4982938551498609442">"Wi-Fi通話(%s)"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc260-ka-rGE/strings.xml b/core/res/res/values-mcc310-mnc260-ka-rGE/strings.xml
index 53488dd..d1eadac 100644
--- a/core/res/res/values-mcc310-mnc260-ka-rGE/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-ka-rGE/strings.xml
@@ -25,4 +25,5 @@
<string-array name="wfcOperatorErrorMessages">
<item msgid="931634632269046788">"დარეკვა Wi-Fi-ს მეშვეობით მიუწვდომელია. დაუკავშირდით თქვენს ოპერატორს Wi-Fi-ს მეშვეობით დარეკვის ჩასართავად."</item>
</string-array>
+ <string name="wfcSpnFormat" msgid="4982938551498609442">"%s დარეკვა Wi-Fi-ს მეშვეობით"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc260-kk-rKZ/strings.xml b/core/res/res/values-mcc310-mnc260-kk-rKZ/strings.xml
index 10ce253..b135e1d 100644
--- a/core/res/res/values-mcc310-mnc260-kk-rKZ/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-kk-rKZ/strings.xml
@@ -25,4 +25,5 @@
<string-array name="wfcOperatorErrorMessages">
<item msgid="931634632269046788">"Wi-Fi қоңырауы қол жетімді емес. Wi-Fi қоңырауы қосу үшін жабдықтаушыға хабарласыңыз."</item>
</string-array>
+ <string name="wfcSpnFormat" msgid="4982938551498609442">"%s Wi-Fi арқылы қоңырау шалу"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc260-km-rKH/strings.xml b/core/res/res/values-mcc310-mnc260-km-rKH/strings.xml
index 7f62ff6..b3e86df8 100644
--- a/core/res/res/values-mcc310-mnc260-km-rKH/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-km-rKH/strings.xml
@@ -25,4 +25,5 @@
<string-array name="wfcOperatorErrorMessages">
<item msgid="931634632269046788">"ការហៅតាម Wi-Fi មិនមាននោះទេ។ សូមទាក់ទងទៅអ្នកផ្តល់សេវាកម្មទូរស័ព្ទរបស់អ្នកដើម្បីបើកដំណើរការហៅតាម Wi-Fi។"</item>
</string-array>
+ <string name="wfcSpnFormat" msgid="4982938551498609442">"ការហៅតាមរយៈ Wi-Fi %s"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc260-kn-rIN/strings.xml b/core/res/res/values-mcc310-mnc260-kn-rIN/strings.xml
index 4977708..6a6378c 100644
--- a/core/res/res/values-mcc310-mnc260-kn-rIN/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-kn-rIN/strings.xml
@@ -25,4 +25,5 @@
<string-array name="wfcOperatorErrorMessages">
<item msgid="931634632269046788">"ವೈ-ಫೈ ಕರೆ ಮಾಡುವಿಕೆ ಲಭ್ಯವಿಲ್ಲ. ವೈ-ಫೈ ಕರೆ ಮಾಡುವಿಕೆಯನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಲು ನಿಮ್ಮ ವಾಹಕವನ್ನು ಸಂಪರ್ಕಿಸಿ."</item>
</string-array>
+ <string name="wfcSpnFormat" msgid="4982938551498609442">"%s ವೈ-ಫೈ ಕರೆ ಮಾಡುವಿಕೆ"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc260-ko/strings.xml b/core/res/res/values-mcc310-mnc260-ko/strings.xml
index 0dcc45a..9a4d89c 100644
--- a/core/res/res/values-mcc310-mnc260-ko/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-ko/strings.xml
@@ -25,4 +25,5 @@
<string-array name="wfcOperatorErrorMessages">
<item msgid="931634632269046788">"Wi-Fi 통화를 사용할 수 없습니다. Wi-Fi 통화를 사용하려면 이동통신사에 문의하세요."</item>
</string-array>
+ <string name="wfcSpnFormat" msgid="4982938551498609442">"%s Wi-Fi 통화"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc260-ky-rKG/strings.xml b/core/res/res/values-mcc310-mnc260-ky-rKG/strings.xml
index b8f1e5a..290ce32 100644
--- a/core/res/res/values-mcc310-mnc260-ky-rKG/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-ky-rKG/strings.xml
@@ -25,4 +25,5 @@
<string-array name="wfcOperatorErrorMessages">
<item msgid="931634632269046788">"Wi-Fi Чалуу жеткиликтүү эмес. Wi-Fi Чалууну иштетүү үчүн операторуңузга кайрылыңыз."</item>
</string-array>
+ <string name="wfcSpnFormat" msgid="4982938551498609442">"%s Wi-Fi Чалуу"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc260-lo-rLA/strings.xml b/core/res/res/values-mcc310-mnc260-lo-rLA/strings.xml
index 8f826e4..3b96835 100644
--- a/core/res/res/values-mcc310-mnc260-lo-rLA/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-lo-rLA/strings.xml
@@ -25,4 +25,5 @@
<string-array name="wfcOperatorErrorMessages">
<item msgid="931634632269046788">"ການໂທ Wi-Fi ບໍ່ພ້ອມໃຊ້ງານ. ໃຫ້ຕິດຕໍ່ຫາຜູ້ໃຫ້ບໍລິການຂອງທ່ານເພື່ອເປີດໃຊ້ການໂທ Wi-Fi."</item>
</string-array>
+ <string name="wfcSpnFormat" msgid="4982938551498609442">"ການໂທ %s Wi-Fi"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc260-lt/strings.xml b/core/res/res/values-mcc310-mnc260-lt/strings.xml
index b4a0dbb..09d151b 100644
--- a/core/res/res/values-mcc310-mnc260-lt/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-lt/strings.xml
@@ -25,4 +25,5 @@
<string-array name="wfcOperatorErrorMessages">
<item msgid="931634632269046788">"„Wi-Fi“ skambinimo funkcija nepasiekiama. Susisiekite su operatoriumi, kad įgalintumėte „Wi-Fi“ skambinimo funkciją."</item>
</string-array>
+ <string name="wfcSpnFormat" msgid="4982938551498609442">"„%s“ „Wi-Fi“ skambinimas"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc260-lv/strings.xml b/core/res/res/values-mcc310-mnc260-lv/strings.xml
index 19fafeb..2da9a9d 100644
--- a/core/res/res/values-mcc310-mnc260-lv/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-lv/strings.xml
@@ -25,4 +25,5 @@
<string-array name="wfcOperatorErrorMessages">
<item msgid="931634632269046788">"Wi-Fi zvani nav pieejami. Lai iespējotu Wi-Fi zvanus, sazinieties ar savu mobilo sakaru operatoru."</item>
</string-array>
+ <string name="wfcSpnFormat" msgid="4982938551498609442">"%s Wi-Fi zvani"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc260-mk-rMK/strings.xml b/core/res/res/values-mcc310-mnc260-mk-rMK/strings.xml
index a4125b4..ddf0af6 100644
--- a/core/res/res/values-mcc310-mnc260-mk-rMK/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-mk-rMK/strings.xml
@@ -25,4 +25,5 @@
<string-array name="wfcOperatorErrorMessages">
<item msgid="931634632269046788">"Повикувањето преку Wi-Fi не е достапно. Контактирајте го операторот за да овозможите Повикување преку Wi-Fi."</item>
</string-array>
+ <string name="wfcSpnFormat" msgid="4982938551498609442">"%s Повикување преку Wi-Fi"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc260-ml-rIN/strings.xml b/core/res/res/values-mcc310-mnc260-ml-rIN/strings.xml
index 732e18b..2565306 100644
--- a/core/res/res/values-mcc310-mnc260-ml-rIN/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-ml-rIN/strings.xml
@@ -25,4 +25,5 @@
<string-array name="wfcOperatorErrorMessages">
<item msgid="931634632269046788">"Wi-Fi കോളിംഗ് ലഭ്യമല്ല. Wi-Fi കോളിംഗ് പ്രവർത്തനക്ഷമമാക്കാൻ നിങ്ങളുടെ കാരിയറെ ബന്ധപ്പെടുക."</item>
</string-array>
+ <string name="wfcSpnFormat" msgid="4982938551498609442">"%s Wi-Fi കോളിംഗ്"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc260-mn-rMN/strings.xml b/core/res/res/values-mcc310-mnc260-mn-rMN/strings.xml
index 8b311ee..c52dd31 100644
--- a/core/res/res/values-mcc310-mnc260-mn-rMN/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-mn-rMN/strings.xml
@@ -25,4 +25,5 @@
<string-array name="wfcOperatorErrorMessages">
<item msgid="931634632269046788">"Wi-Fi Calling одоогоор боломжгүй байна. Wi-Fi Calling идэвхжүүлэхийн тулд оператортойгоо холбогдоно уу."</item>
</string-array>
+ <string name="wfcSpnFormat" msgid="4982938551498609442">"%s Wi-Fi Дуудлага"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc260-mr-rIN/strings.xml b/core/res/res/values-mcc310-mnc260-mr-rIN/strings.xml
index e191a68..86aa1b0 100644
--- a/core/res/res/values-mcc310-mnc260-mr-rIN/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-mr-rIN/strings.xml
@@ -25,4 +25,5 @@
<string-array name="wfcOperatorErrorMessages">
<item msgid="931634632269046788">"वाय-फाय कॉलिंग उपलब्ध नाही. वाय-फाय कॉलिंग सक्षम करण्यासाठी आपल्या वाहकाशी संपर्क साधा."</item>
</string-array>
+ <string name="wfcSpnFormat" msgid="4982938551498609442">"%s वाय-फाय कॉलिंग"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc260-ms-rMY/strings.xml b/core/res/res/values-mcc310-mnc260-ms-rMY/strings.xml
index aa13114..94e9705 100644
--- a/core/res/res/values-mcc310-mnc260-ms-rMY/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-ms-rMY/strings.xml
@@ -25,4 +25,5 @@
<string-array name="wfcOperatorErrorMessages">
<item msgid="931634632269046788">"Panggilan Wi-Fi tidak tersedia. Hubungi pembawa anda untuk mendayakan Panggilan Wi-Fi."</item>
</string-array>
+ <string name="wfcSpnFormat" msgid="4982938551498609442">"%s Panggilan Wi-Fi"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc260-my-rMM/strings.xml b/core/res/res/values-mcc310-mnc260-my-rMM/strings.xml
index 90e99af..bf624ce 100644
--- a/core/res/res/values-mcc310-mnc260-my-rMM/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-my-rMM/strings.xml
@@ -25,4 +25,5 @@
<string-array name="wfcOperatorErrorMessages">
<item msgid="931634632269046788">"ဝိုင်ဖိုင်ခေါ်ဆိုမှု မရပါ။ ဝိုင်ဖိုင် ခေါ်နိုင်ရန် သင်၏ ဖုန်းဝန်ဆောင်မှုပေးသူအား ဆက်သွယ်ပါ။"</item>
</string-array>
+ <string name="wfcSpnFormat" msgid="4982938551498609442">"%s ဝိုင်ဖိုင် ခေါ်ဆိုမှု"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc260-nb/strings.xml b/core/res/res/values-mcc310-mnc260-nb/strings.xml
index 7ece702..c4e9f6e 100644
--- a/core/res/res/values-mcc310-mnc260-nb/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-nb/strings.xml
@@ -25,4 +25,5 @@
<string-array name="wfcOperatorErrorMessages">
<item msgid="931634632269046788">"Wi-Fi-anrop er ikke tilgjengelig. Ta kontakt med operatøren din for å slå på Wi-Fi-anrop."</item>
</string-array>
+ <string name="wfcSpnFormat" msgid="4982938551498609442">"%s Wi-Fi-anrop"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc260-ne-rNP/strings.xml b/core/res/res/values-mcc310-mnc260-ne-rNP/strings.xml
index 72f94f3..2ba19e5 100644
--- a/core/res/res/values-mcc310-mnc260-ne-rNP/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-ne-rNP/strings.xml
@@ -25,4 +25,5 @@
<string-array name="wfcOperatorErrorMessages">
<item msgid="931634632269046788">"Wi-Fi कलिङ उपलब्ध छैन। Wi-Fi कलिङ सक्षम पार्न तपाईँको वाहकलाई सम्पर्क गर्नुहोस्।"</item>
</string-array>
+ <string name="wfcSpnFormat" msgid="4982938551498609442">"%s Wi-Fi कलिङ"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc260-nl/strings.xml b/core/res/res/values-mcc310-mnc260-nl/strings.xml
index 1fbe404..20b7aa4 100644
--- a/core/res/res/values-mcc310-mnc260-nl/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-nl/strings.xml
@@ -25,4 +25,5 @@
<string-array name="wfcOperatorErrorMessages">
<item msgid="931634632269046788">"Bellen via wifi is niet beschikbaar. Neem contact op met uw provider om bellen via wifi in te schakelen."</item>
</string-array>
+ <string name="wfcSpnFormat" msgid="4982938551498609442">"Bellen via wifi van %s"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc260-pl/strings.xml b/core/res/res/values-mcc310-mnc260-pl/strings.xml
index 41bb1e6..ef3e2e3 100644
--- a/core/res/res/values-mcc310-mnc260-pl/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-pl/strings.xml
@@ -25,4 +25,5 @@
<string-array name="wfcOperatorErrorMessages">
<item msgid="931634632269046788">"Połączenia przez Wi-Fi są niedostępne. Skontaktuj się z operatorem sieci, by je włączyć."</item>
</string-array>
+ <string name="wfcSpnFormat" msgid="4982938551498609442">"Połączenia przez Wi-Fi (%s)"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc260-pt-rPT/strings.xml b/core/res/res/values-mcc310-mnc260-pt-rPT/strings.xml
index bb22d9e..8ea4cb4c 100644
--- a/core/res/res/values-mcc310-mnc260-pt-rPT/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-pt-rPT/strings.xml
@@ -25,4 +25,5 @@
<string-array name="wfcOperatorErrorMessages">
<item msgid="931634632269046788">"As chamadas Wi-Fi não estão disponíveis. Contacte o seu operador para as ativar."</item>
</string-array>
+ <string name="wfcSpnFormat" msgid="4982938551498609442">"Chamadas por Wi-Fi da %s"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc260-pt/strings.xml b/core/res/res/values-mcc310-mnc260-pt/strings.xml
index d422e83..e01fc4a 100644
--- a/core/res/res/values-mcc310-mnc260-pt/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-pt/strings.xml
@@ -25,4 +25,5 @@
<string-array name="wfcOperatorErrorMessages">
<item msgid="931634632269046788">"A chamada por Wi-Fi não está disponível. Entre em contato com sua operadora para ativá-la."</item>
</string-array>
+ <string name="wfcSpnFormat" msgid="4982938551498609442">"%s chamada Wi-Fi"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc260-ro/strings.xml b/core/res/res/values-mcc310-mnc260-ro/strings.xml
index 422280f..0a78caa 100644
--- a/core/res/res/values-mcc310-mnc260-ro/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-ro/strings.xml
@@ -25,4 +25,5 @@
<string-array name="wfcOperatorErrorMessages">
<item msgid="931634632269046788">"Apelarea prin Wi-Fi nu este disponibilă. Contactați-vă operatorul pentru a activa Apelarea prin Wi-Fi."</item>
</string-array>
+ <string name="wfcSpnFormat" msgid="4982938551498609442">"Apelare prin Wi-Fi %s"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc260-ru/strings.xml b/core/res/res/values-mcc310-mnc260-ru/strings.xml
index eeceb7c..3266bdb 100644
--- a/core/res/res/values-mcc310-mnc260-ru/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-ru/strings.xml
@@ -25,4 +25,5 @@
<string-array name="wfcOperatorErrorMessages">
<item msgid="931634632269046788">"Звонки по Wi-Fi недоступны. Чтобы включить эту функцию, свяжитесь со своим оператором."</item>
</string-array>
+ <string name="wfcSpnFormat" msgid="4982938551498609442">"Звонки по Wi-Fi (%s)"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc260-si-rLK/strings.xml b/core/res/res/values-mcc310-mnc260-si-rLK/strings.xml
index dd4da30..d1e5c27 100644
--- a/core/res/res/values-mcc310-mnc260-si-rLK/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-si-rLK/strings.xml
@@ -25,4 +25,5 @@
<string-array name="wfcOperatorErrorMessages">
<item msgid="931634632269046788">"Wi-Fi ඇමතීම ලබාගත නොහැක. Wi-Fi ඇමතීම ක්රියාත්මක කිරීමට ඔබගේ වාහකයා සම්බන්ධ කරගන්න."</item>
</string-array>
+ <string name="wfcSpnFormat" msgid="4982938551498609442">"%s Wi-Fi අමතමින්"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc260-sk/strings.xml b/core/res/res/values-mcc310-mnc260-sk/strings.xml
index 527388f..2adccc8 100644
--- a/core/res/res/values-mcc310-mnc260-sk/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-sk/strings.xml
@@ -25,4 +25,5 @@
<string-array name="wfcOperatorErrorMessages">
<item msgid="931634632269046788">"Volanie cez Wi-Fi nie je k dispozícii. Kontaktujte svojho operátora a požiadajte ho o povolenie volania cez Wi-Fi."</item>
</string-array>
+ <string name="wfcSpnFormat" msgid="4982938551498609442">"Volanie siete Wi-Fi %s"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc260-sl/strings.xml b/core/res/res/values-mcc310-mnc260-sl/strings.xml
index 2dc873b..e4bedcf 100644
--- a/core/res/res/values-mcc310-mnc260-sl/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-sl/strings.xml
@@ -25,4 +25,5 @@
<string-array name="wfcOperatorErrorMessages">
<item msgid="931634632269046788">"Klicanje prek Wi-Fi-ja ni na voljo. Obrnite se na operaterja, da vam omogoči klicanje prek Wi-Fi-ja."</item>
</string-array>
+ <string name="wfcSpnFormat" msgid="4982938551498609442">"Klicanje prek Wi-Fi-ja (%s)"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc260-sr/strings.xml b/core/res/res/values-mcc310-mnc260-sr/strings.xml
index 76f8aef..6b00845 100644
--- a/core/res/res/values-mcc310-mnc260-sr/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-sr/strings.xml
@@ -25,4 +25,5 @@
<string-array name="wfcOperatorErrorMessages">
<item msgid="931634632269046788">"Позивање преко Wi-Fi-ја није доступно. Контактирајте мобилног оператера да бисте омогућили позивање преко Wi-Fi-ја."</item>
</string-array>
+ <string name="wfcSpnFormat" msgid="4982938551498609442">"Wi-Fi позивање преко оператера %s"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc260-sv/strings.xml b/core/res/res/values-mcc310-mnc260-sv/strings.xml
index 8e0d159..4984914 100644
--- a/core/res/res/values-mcc310-mnc260-sv/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-sv/strings.xml
@@ -25,4 +25,5 @@
<string-array name="wfcOperatorErrorMessages">
<item msgid="931634632269046788">"Det går inte att ringa Wi-Fi-samtal. Kontakta operatören om du vill aktivera Wi-Fi-samtal."</item>
</string-array>
+ <string name="wfcSpnFormat" msgid="4982938551498609442">"%s Wi-Fi-samtal"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc260-sw/strings.xml b/core/res/res/values-mcc310-mnc260-sw/strings.xml
index ff9bf3f..eab7344 100644
--- a/core/res/res/values-mcc310-mnc260-sw/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-sw/strings.xml
@@ -25,4 +25,5 @@
<string-array name="wfcOperatorErrorMessages">
<item msgid="931634632269046788">"Huduma ya Upigaji simu kwa Wi-Fi haipatikani. Wasiliana na mtoa huduma wako ili awashe Upigaji simu kwa Wi-Fi."</item>
</string-array>
+ <string name="wfcSpnFormat" msgid="4982938551498609442">"%s Upigaji Simu kwa Wi-Fi"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc260-ta-rIN/strings.xml b/core/res/res/values-mcc310-mnc260-ta-rIN/strings.xml
index d8b3130..e7da4b6 100644
--- a/core/res/res/values-mcc310-mnc260-ta-rIN/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-ta-rIN/strings.xml
@@ -25,4 +25,5 @@
<string-array name="wfcOperatorErrorMessages">
<item msgid="931634632269046788">"வைஃபை அழைப்பு கிடைக்கவில்லை. வைஃபை அழைப்பை இயக்க, மொபைல் நிறுவனத்தைத் தொடர்புகொள்ளவும்."</item>
</string-array>
+ <string name="wfcSpnFormat" msgid="4982938551498609442">"%s வைஃபை அழைப்பு"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc260-te-rIN/strings.xml b/core/res/res/values-mcc310-mnc260-te-rIN/strings.xml
index ffbab18..98de7b7 100644
--- a/core/res/res/values-mcc310-mnc260-te-rIN/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-te-rIN/strings.xml
@@ -25,4 +25,5 @@
<string-array name="wfcOperatorErrorMessages">
<item msgid="931634632269046788">"Wi-Fi కాలింగ్ అందుబాటులో లేదు. Wi-Fi కాలింగ్ను ప్రారంభించడానికి మీ క్యారియర్ను సంప్రదించండి."</item>
</string-array>
+ <string name="wfcSpnFormat" msgid="4982938551498609442">"%s Wi-Fi కాలింగ్"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc260-th/strings.xml b/core/res/res/values-mcc310-mnc260-th/strings.xml
index 52dc04f..eb64b8f 100644
--- a/core/res/res/values-mcc310-mnc260-th/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-th/strings.xml
@@ -25,4 +25,5 @@
<string-array name="wfcOperatorErrorMessages">
<item msgid="931634632269046788">"การโทรผ่าน Wi-Fi ไม่พร้อมใช้งาน โปรดติดต่อผู้ให้บริการของคุณเพื่อเปิดใช้การโทรผ่าน Wi-Fi"</item>
</string-array>
+ <string name="wfcSpnFormat" msgid="4982938551498609442">"กำลังเรียก Wi-Fi ของ %s"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc260-tl/strings.xml b/core/res/res/values-mcc310-mnc260-tl/strings.xml
index 52d97a8..1ac32d0 100644
--- a/core/res/res/values-mcc310-mnc260-tl/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-tl/strings.xml
@@ -25,4 +25,5 @@
<string-array name="wfcOperatorErrorMessages">
<item msgid="931634632269046788">"Hindi available ang Pagtawag sa pamamagitan ng Wi-Fi. Makipag-ugnayan sa iyong carrier upang i-enable ang Pagtawag sa pamamagitan ng Wi-Fi."</item>
</string-array>
+ <string name="wfcSpnFormat" msgid="4982938551498609442">"Pagtawag sa Pamamagitan ng Wi-Fi ng %s"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc260-tr/strings.xml b/core/res/res/values-mcc310-mnc260-tr/strings.xml
index b28702b82..5a479f7 100644
--- a/core/res/res/values-mcc310-mnc260-tr/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-tr/strings.xml
@@ -25,4 +25,5 @@
<string-array name="wfcOperatorErrorMessages">
<item msgid="931634632269046788">"Kablosuz Çağrı kullanılamıyor. Kablosuz Çağrı özelliğini etkinleştirmek için operatörünüzle iletişim kurun."</item>
</string-array>
+ <string name="wfcSpnFormat" msgid="4982938551498609442">"%s Kablosuz Çağrı"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc260-uk/strings.xml b/core/res/res/values-mcc310-mnc260-uk/strings.xml
index 243c7b0..f878528 100644
--- a/core/res/res/values-mcc310-mnc260-uk/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-uk/strings.xml
@@ -25,4 +25,5 @@
<string-array name="wfcOperatorErrorMessages">
<item msgid="931634632269046788">"Ви не можете телефонувати через Wi-Fi. Щоб увімкнути цю функцію, зв’яжіться з оператором."</item>
</string-array>
+ <string name="wfcSpnFormat" msgid="4982938551498609442">"Дзвінок через Wi-Fi від оператора %s"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc260-ur-rPK/strings.xml b/core/res/res/values-mcc310-mnc260-ur-rPK/strings.xml
index 77ee52b..a9e56d2 100644
--- a/core/res/res/values-mcc310-mnc260-ur-rPK/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-ur-rPK/strings.xml
@@ -25,4 +25,5 @@
<string-array name="wfcOperatorErrorMessages">
<item msgid="931634632269046788">"Wi-Fi کالنگ دستیاب نہیں ہے۔ Wi-Fi کالنگ فعال کرنے کیلئے اپنے کیریئر سے رابطہ کریں۔"</item>
</string-array>
+ <string name="wfcSpnFormat" msgid="4982938551498609442">"%s Wi-Fi کالنگ"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc260-uz-rUZ/strings.xml b/core/res/res/values-mcc310-mnc260-uz-rUZ/strings.xml
index 1cd4795..26c6cd8 100644
--- a/core/res/res/values-mcc310-mnc260-uz-rUZ/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-uz-rUZ/strings.xml
@@ -25,4 +25,5 @@
<string-array name="wfcOperatorErrorMessages">
<item msgid="931634632269046788">"Wi-Fi qo‘ng‘iroq mavjud emas. Wi-Fi qo‘ng‘iroqni yoqish uchun tarmoq operatori bilan bog‘laning."</item>
</string-array>
+ <string name="wfcSpnFormat" msgid="4982938551498609442">"%s Wi-Fi qo‘ng‘iroqlar"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc260-vi/strings.xml b/core/res/res/values-mcc310-mnc260-vi/strings.xml
index d47da6d..242ec73 100644
--- a/core/res/res/values-mcc310-mnc260-vi/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-vi/strings.xml
@@ -25,4 +25,5 @@
<string-array name="wfcOperatorErrorMessages">
<item msgid="931634632269046788">"Tính năng Gọi qua Wi-Fi không khả dụng. Hãy liên hệ với nhà cung cấp dịch vụ để bật tính năng Gọi qua Wi-Fi."</item>
</string-array>
+ <string name="wfcSpnFormat" msgid="4982938551498609442">"Gọi điện qua Wi-Fi %s"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc260-zh-rCN/strings.xml b/core/res/res/values-mcc310-mnc260-zh-rCN/strings.xml
index 561484e..a359441 100644
--- a/core/res/res/values-mcc310-mnc260-zh-rCN/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-zh-rCN/strings.xml
@@ -25,4 +25,5 @@
<string-array name="wfcOperatorErrorMessages">
<item msgid="931634632269046788">"WLAN 通话功能不可用。请与您的运营商联系,以便启用 WLAN 通话功能。"</item>
</string-array>
+ <string name="wfcSpnFormat" msgid="4982938551498609442">"%s WLAN 通话功能"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc260-zh-rHK/strings.xml b/core/res/res/values-mcc310-mnc260-zh-rHK/strings.xml
index d93be6d..984e55f 100644
--- a/core/res/res/values-mcc310-mnc260-zh-rHK/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-zh-rHK/strings.xml
@@ -25,4 +25,5 @@
<string-array name="wfcOperatorErrorMessages">
<item msgid="931634632269046788">"無法使用 Wi-Fi 通話。請聯絡您的流動網絡供應商,以啟用 Wi-Fi 通話。"</item>
</string-array>
+ <string name="wfcSpnFormat" msgid="4982938551498609442">"%s Wi-Fi 通話"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc260-zh-rTW/strings.xml b/core/res/res/values-mcc310-mnc260-zh-rTW/strings.xml
index 3acfb84..6a82063 100644
--- a/core/res/res/values-mcc310-mnc260-zh-rTW/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-zh-rTW/strings.xml
@@ -25,4 +25,5 @@
<string-array name="wfcOperatorErrorMessages">
<item msgid="931634632269046788">"無法使用 Wi-Fi 通話功能。請與您的行動通訊業者聯絡,為您啟用 Wi-Fi 通話功能。"</item>
</string-array>
+ <string name="wfcSpnFormat" msgid="4982938551498609442">"%s Wi-Fi 通話"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc260-zu/strings.xml b/core/res/res/values-mcc310-mnc260-zu/strings.xml
index a037bb8..2c06e9a 100644
--- a/core/res/res/values-mcc310-mnc260-zu/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-zu/strings.xml
@@ -25,4 +25,5 @@
<string-array name="wfcOperatorErrorMessages">
<item msgid="931634632269046788">"Ukushaya kwe-Wi-Fi akutholakali. Xhumana nenkampani yakho yenethiwekhi ukuze unike amandla ukushaya kwe-Wi-Fi."</item>
</string-array>
+ <string name="wfcSpnFormat" msgid="4982938551498609442">"%s ukushaya kwe-Wi-Fi"</string>
</resources>
diff --git a/core/res/res/values-mk-rMK/strings.xml b/core/res/res/values-mk-rMK/strings.xml
index 387238e..76e05c8 100644
--- a/core/res/res/values-mk-rMK/strings.xml
+++ b/core/res/res/values-mk-rMK/strings.xml
@@ -126,6 +126,7 @@
<string name="wfcRegErrorTitle" msgid="2301376280632110664">"Повикување преку Wi-Fi"</string>
<string-array name="wfcOperatorErrorMessages">
</string-array>
+ <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
<string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: не е препратено"</string>
<string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> по <xliff:g id="TIME_DELAY">{2}</xliff:g> секунди"</string>
@@ -746,6 +747,22 @@
<string name="permdesc_manageFingerprint" msgid="178208705828055464">"Дозволува апликацијата да повика начини за додавање и бришење шаблони на отпечатоци за користење."</string>
<string name="permlab_useFingerprint" msgid="3150478619915124905">"користи хардвер за отпечатоци"</string>
<string name="permdesc_useFingerprint" msgid="9165097460730684114">"Дозволува апликацијата да користи хардвер за отпечатоци за проверка"</string>
+ <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Откриен е делумен отпечаток. Обидете се повторно."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Отпечатокот не можеше да се обработи. Обидете се повторно."</string>
+ <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Сензорот за отпечатоци е валкан. Исчистете го и обидете се повторно."</string>
+ <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"Прстот се движеше премногу брзо. Обидете се повторно."</string>
+ <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"Прстот се движеше премногу бавно. Обидете се повторно."</string>
+ <string-array name="fingerprint_acquired_vendor">
+ <item msgid="2892952818207766996">"Добивање порака за грешка карактеристична за снабдувачот 0"</item>
+ </string-array>
+ <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"Не може да се обработи. Обидете се повторно."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"Хардвер не е достапен."</string>
+ <string name="fingerprint_error_no_space" msgid="1055819001126053318">"Отпечатокот не може да се складира. Отстранете го постоечкиот отпечаток."</string>
+ <string name="fingerprint_error_timeout" msgid="3927186043737732875">"Се достигна времето на истекување на отпечатокот. Обидете се повторно."</string>
+ <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Се достигна времето на истекување на отпечатокот. Обидете се повторно."</string>
+ <string-array name="fingerprint_error_vendor">
+ <item msgid="5804600450373644614">"Порака за грешка карактеристична за снабдувачот."</item>
+ </string-array>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"прочитај синхронизирани подесувања"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Овозможува апликацијата да ги чита подесувањата за синхронизирање на сметка. На пример, така може да се утврди дали апликацијата „Луѓе“ е синхронизирана со сметка."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"вклучи и исклучи синхронизација"</string>
diff --git a/core/res/res/values-ml-rIN/strings.xml b/core/res/res/values-ml-rIN/strings.xml
index 68fd869..e18cc6c 100644
--- a/core/res/res/values-ml-rIN/strings.xml
+++ b/core/res/res/values-ml-rIN/strings.xml
@@ -126,6 +126,7 @@
<string name="wfcRegErrorTitle" msgid="2301376280632110664">"Wi-Fi കോളിംഗ്"</string>
<string-array name="wfcOperatorErrorMessages">
</string-array>
+ <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
<string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: കൈമാറിയില്ല"</string>
<string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="TIME_DELAY">{2}</xliff:g> നിമിഷത്തിനുശേഷം <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
@@ -746,6 +747,22 @@
<string name="permdesc_manageFingerprint" msgid="178208705828055464">"ഉപയോഗിക്കാനായി വിരലടയാള ടെംപ്ലേറ്റുകൾ ചേർക്കാനും ഇല്ലാതാക്കാനുമുള്ള രീതികൾ അഭ്യർത്ഥിക്കാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു."</string>
<string name="permlab_useFingerprint" msgid="3150478619915124905">"ഫിംഗർപ്രിന്റ് ഹാർഡ്വെയർ ഉപയോഗിക്കുക"</string>
<string name="permdesc_useFingerprint" msgid="9165097460730684114">"പ്രാമാണീകരണത്തിനായി വിരലടയാളം ഉപയോഗിക്കാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു"</string>
+ <string name="fingerprint_acquired_partial" msgid="735082772341716043">"വിരലടയാളം ഭാഗികമായി തിരിച്ചറിഞ്ഞു. വീണ്ടും ശ്രമിക്കുക."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"വിരലടയാളം പ്രോസസ്സ് ചെയ്യാനായില്ല. വീണ്ടും ശ്രമിക്കുക."</string>
+ <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"വിരലടയാള സെൻസറിന് വൃത്തിയില്ല. അത് ശുചിയാക്കി വീണ്ടും ശ്രമിക്കുക."</string>
+ <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"വിരൽ വേഗത്തിൽ നീക്കി. വീണ്ടും ശ്രമിക്കുക."</string>
+ <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"വിരൽ പതുക്കെ നീക്കി. വീണ്ടും ശ്രമിക്കുക."</string>
+ <string-array name="fingerprint_acquired_vendor">
+ <item msgid="2892952818207766996">"വെൻഡർ നിർദ്ദിഷ്ട നേടൽ പിശക് സന്ദേശം 0"</item>
+ </string-array>
+ <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"പ്രോസസ്സ് ചെയ്യാനാവുന്നില്ല. വീണ്ടും ശ്രമിക്കുക."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"ഹാർഡ്വെയർ ലഭ്യമല്ല."</string>
+ <string name="fingerprint_error_no_space" msgid="1055819001126053318">"വിരലടയാളം സംഭരിക്കാനാവില്ല. നിലവിലുള്ള വിരലടയാളം നീക്കംചെയ്യുക."</string>
+ <string name="fingerprint_error_timeout" msgid="3927186043737732875">"വിരലടയാളം നൽകേണ്ട സമയം കഴിഞ്ഞു. വീണ്ടും ശ്രമിക്കുക."</string>
+ <string name="fingerprint_error_vendor" msgid="3175724710791609491">"വിരലടയാളം നൽകേണ്ട സമയം കഴിഞ്ഞു. വീണ്ടും ശ്രമിക്കുക."</string>
+ <string-array name="fingerprint_error_vendor">
+ <item msgid="5804600450373644614">"വെൻഡർ നിർദ്ദിഷ്ട പിശക് സന്ദേശം."</item>
+ </string-array>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"സമന്വയ ക്രമീകരണങ്ങൾ റീഡുചെയ്യുക"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"ഒരു അക്കൗണ്ടിനായി സമന്വയ ക്രമീകരണങ്ങൾ റീഡുചെയ്യാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു. ഉദാഹരണത്തിന്, ആളുകൾ അപ്ലിക്കേഷൻ ഒരു അക്കൗണ്ടിൽ സമന്വയിപ്പിച്ചിട്ടുണ്ടോയെന്നത് നിർണ്ണയിക്കാൻ ഇതിനാകും."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"സമന്വയം ഓണാക്കുക, ഓഫാക്കുക ടോഗിൾചെയ്യുക"</string>
diff --git a/core/res/res/values-mn-rMN/strings.xml b/core/res/res/values-mn-rMN/strings.xml
index 07fb9a9..da75a4a 100644
--- a/core/res/res/values-mn-rMN/strings.xml
+++ b/core/res/res/values-mn-rMN/strings.xml
@@ -126,6 +126,7 @@
<string name="wfcRegErrorTitle" msgid="2301376280632110664">"Wi-Fi Calling"</string>
<string-array name="wfcOperatorErrorMessages">
</string-array>
+ <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
<string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: дамжуулагдаагүй"</string>
<string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> <xliff:g id="TIME_DELAY">{2}</xliff:g> секундын дараа"</string>
@@ -746,6 +747,22 @@
<string name="permdesc_manageFingerprint" msgid="178208705828055464">"Хурууны хээний загварыг нэмэх эсвэл усгтах үйлдлийг хийх зөвшөөрлийг програмд олгодог."</string>
<string name="permlab_useFingerprint" msgid="3150478619915124905">"хурууны хээний програм хангамжийг ашиглах"</string>
<string name="permdesc_useFingerprint" msgid="9165097460730684114">"Баталгаажуулалт хийх зорилгоор хурууны хээний програм хамгамжийг ашиглах зөвшөөрлийг програмд олгодог"</string>
+ <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Хурууны хээг дутуу уншуулсан байна. Дахин оролдоно уу."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Хурууны хээ боловсруулж чадахгүй байна. Дахин оролдоно уу."</string>
+ <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Хурууны хээ мэдрэгч бохирдсон байна. Та цэвэрлэсний дараагаар дахин оролдоно уу."</string>
+ <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"Хурууг уншиж дуусаагүй байхад авсан байна. Та дахин уншуулна уу."</string>
+ <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"Хурууг хэт удаан уншуулсан байна. Та дахин уншуулна уу."</string>
+ <string-array name="fingerprint_acquired_vendor">
+ <item msgid="2892952818207766996">"Борлуулагч-хурууны хээ авахад гардаг алдаа 0"</item>
+ </string-array>
+ <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"Мэдээллийг оруулах боломжгүй байна. Дахин оролдоно уу."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"Техник хангамж бэлэн бус байна"</string>
+ <string name="fingerprint_error_no_space" msgid="1055819001126053318">"Хурууны хээг хадгалах боломжгүй байна. Одоо байгаа хурууны хээг арилгана уу."</string>
+ <string name="fingerprint_error_timeout" msgid="3927186043737732875">"Хурууны хээ оруулах хугацаа өнгөрсөн байна. Дахин оруулна уу."</string>
+ <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Хурууны хээ оруулах хугацаа өнгөрсөн байна. Дахин оруулна уу."</string>
+ <string-array name="fingerprint_error_vendor">
+ <item msgid="5804600450373644614">"Борлуулаг-тодорхой алдааны зурвас"</item>
+ </string-array>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"синк тохиргоог унших"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Апп нь акаунтын синк тохиргоог унших боломжтой. Жишээ нь энэ нь Хүмүүс апп акаунттай синк хийгдсэн эсэхийг тодорхойлох боломжтой."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"синкийг унтрааж асаах тохиргоо"</string>
diff --git a/core/res/res/values-mr-rIN/strings.xml b/core/res/res/values-mr-rIN/strings.xml
index 737154e0..2fe3f37 100644
--- a/core/res/res/values-mr-rIN/strings.xml
+++ b/core/res/res/values-mr-rIN/strings.xml
@@ -126,6 +126,7 @@
<string name="wfcRegErrorTitle" msgid="2301376280632110664">"वाय-फाय कॉलिंग"</string>
<string-array name="wfcOperatorErrorMessages">
</string-array>
+ <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
<string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: अग्रेषित केला नाही"</string>
<string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="TIME_DELAY">{2}</xliff:g> सेकंदांनंतर <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
@@ -746,6 +747,22 @@
<string name="permdesc_manageFingerprint" msgid="178208705828055464">"वापर करण्याकरिता फिंगरप्रिंट टेम्पलेट जोडण्यासाठी आणि हटविण्यासाठी पद्धती रद्द करण्यास अॅपला अनुमती देते."</string>
<string name="permlab_useFingerprint" msgid="3150478619915124905">"फिंगरप्रिंट हार्डवेअर वापरा"</string>
<string name="permdesc_useFingerprint" msgid="9165097460730684114">"प्रमाणीकरणाकरिता फिंगरप्रिंट हार्डवेअरचा वापर करण्यासाठी अॅपला अनुमती देते"</string>
+ <string name="fingerprint_acquired_partial" msgid="735082772341716043">"आंशिक फिंगरप्रिंट आढळली. कृपया पुन्हा प्रयत्न करा."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"फिंगरप्रिंटवर प्रक्रिया करणे शक्य झाले नाही. कृपया पुन्हा प्रयत्न करा."</string>
+ <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"फिंगरप्रिंट सेन्सर खराब आहे. कृपया साफ करा आणि पुन्हा प्रयत्न करा."</string>
+ <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"बोट लवकर हलविले. कृपया पुन्हा प्रयत्न करा."</string>
+ <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"बोट हळू हलविले. कृपया पुन्हा प्रयत्न करा."</string>
+ <string-array name="fingerprint_acquired_vendor">
+ <item msgid="2892952818207766996">"व्यापारी-विशिष्ट प्राप्ती त्रुटी संदेश 0"</item>
+ </string-array>
+ <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"प्रक्रिया करण्यात अयशस्वी. पुन्हा प्रयत्न करा."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"हार्डवेअर उपलब्ध नाही."</string>
+ <string name="fingerprint_error_no_space" msgid="1055819001126053318">"फिंगरप्रिंट संचयित केले जाऊ शकत नाही. कृपया विद्यमान फिंगरप्रिंट काढा."</string>
+ <string name="fingerprint_error_timeout" msgid="3927186043737732875">"फिंगरप्रिंट कालबाह्य झाले. पुन्हा प्रयत्न करा."</string>
+ <string name="fingerprint_error_vendor" msgid="3175724710791609491">"फिंगरप्रिंट कालबाह्य झाले. पुन्हा प्रयत्न करा."</string>
+ <string-array name="fingerprint_error_vendor">
+ <item msgid="5804600450373644614">"व्यापारी-विशिष्ट त्रुटी संदेश."</item>
+ </string-array>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"संकालन सेटिंग्ज वाचा"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"खात्याच्या संकालन सेटिंग्ज वाचण्यासाठी अॅप ला अनुमती देते. उदाहरणार्थ, हे खात्यासह लोकांचा अॅप संकालित केला आहे किंवा नाही हे निर्धारित करू शकते."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"संकालन चालू आणि बंद करा टॉगल करा"</string>
diff --git a/core/res/res/values-ms-rMY/strings.xml b/core/res/res/values-ms-rMY/strings.xml
index 10d5d5f..132a7d4 100644
--- a/core/res/res/values-ms-rMY/strings.xml
+++ b/core/res/res/values-ms-rMY/strings.xml
@@ -126,6 +126,7 @@
<string name="wfcRegErrorTitle" msgid="2301376280632110664">"Panggilan Wi-Fi"</string>
<string-array name="wfcOperatorErrorMessages">
</string-array>
+ <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
<string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Tidak dimajukan"</string>
<string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> selepas <xliff:g id="TIME_DELAY">{2}</xliff:g> saat"</string>
@@ -746,6 +747,22 @@
<string name="permdesc_manageFingerprint" msgid="178208705828055464">"Membenarkan apl menggunakan kaedah untuk menambahkan dan memadamkan templat cap jari untuk digunakan."</string>
<string name="permlab_useFingerprint" msgid="3150478619915124905">"gunakan perkakasan cap jari"</string>
<string name="permdesc_useFingerprint" msgid="9165097460730684114">"Membenarkan apl menggunakan perkakasan cap jari untuk pengesahan"</string>
+ <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Cap jari separa dikesan. Sila cuba lagi."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Tidak dapat memproses cap jari. Sila cuba lagi."</string>
+ <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Penderia cap jari kotor. Sila bersihkan dan cuba lagi."</string>
+ <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"Jari digerakkan terlalu cepat. Sila cuba lagi."</string>
+ <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"Jari digerakkan terlalu perlahan. Sila cuba lagi."</string>
+ <string-array name="fingerprint_acquired_vendor">
+ <item msgid="2892952818207766996">"Mesej ralat pemerolehan khusus vendor 0"</item>
+ </string-array>
+ <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"Tidak dapat memproses. Cuba lagi."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"Perkakasan 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>
+ <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Tamat masa cap jari dicapai. Cuba lagi."</string>
+ <string-array name="fingerprint_error_vendor">
+ <item msgid="5804600450373644614">"Mesej ralat khusus vendor"</item>
+ </string-array>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"membaca tetapan penyegerakan"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Membenarkan apl membaca tetapan segerak untuk akaun. Sebagai contoh, ini boleh menentukan sama ada apl Orang disegerakkan dengan akaun."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"togol segerak hidup dan mati"</string>
diff --git a/core/res/res/values-my-rMM/strings.xml b/core/res/res/values-my-rMM/strings.xml
index f97c576..2e3f182 100644
--- a/core/res/res/values-my-rMM/strings.xml
+++ b/core/res/res/values-my-rMM/strings.xml
@@ -126,6 +126,7 @@
<string name="wfcRegErrorTitle" msgid="2301376280632110664">"ဝိုင်ဖိုင် ခေါ်ဆိုမှု"</string>
<string-array name="wfcOperatorErrorMessages">
</string-array>
+ <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
<string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: ထပ်ဆင့်မပို့နိုင်ပါ"</string>
<string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> နောက် <xliff:g id="TIME_DELAY">{2}</xliff:g> စက္ကန့်"</string>
@@ -746,6 +747,22 @@
<string name="permdesc_manageFingerprint" msgid="178208705828055464">"အသုံးပြုရန်အတွက် လက်ဗွေရာပုံစံများကို ပေါင်းထည့်ရန် သို့မဟုတ် ဖျက်ရန်နည်းလမ်းများကို အပ်ဖ်အား အသုံးပြုခွင့်ပြုသည်။"</string>
<string name="permlab_useFingerprint" msgid="3150478619915124905">"လက်ဗွေရာပစ္စည်းကို အသုံးပြုမည်"</string>
<string name="permdesc_useFingerprint" msgid="9165097460730684114">"စစ်မှန်ကြောင်းအထောက်အထားပြသခြင်းအတွက် လက်ဗွေရာပစ္စည်းကို အသုံးပြုရန် အပ်ဖ်အားခွင့်ပြုသည်။"</string>
+ <string name="fingerprint_acquired_partial" msgid="735082772341716043">"လက်ဗွေရဦ တစ်ပိုင်းတစ်စ တွေ့ရှိသည်။ ကျေးဇူးပြု၍ ထပ်မံကြိုးစားပါ။"</string>
+ <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"လက်ဗွေရာယူခြင်း မဆောင်ရွက်နိုင်ပါ။ ထပ်မံကြိုးစားပါ။"</string>
+ <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"လက်ဗွေရာဖတ်ကိရိယာ ညစ်ပေနေသည်။ ကျေးဇူးပြု၍ ရှင်းလင်းကာ ထပ်မံကြိုးစားပါ။"</string>
+ <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"လက်ရွှေ့လျားခြင်း အလွန်မြန်သည်။ ကျေးဇူးပြု၍ ထပ်မံကြိုးစားပါ။"</string>
+ <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"လက်ရွှေ့လျားခြင်း အလွန်နှေးသည်။ ကျေးဇူးပြု၍ ထပ်မံကြိုးစားပါ။"</string>
+ <string-array name="fingerprint_acquired_vendor">
+ <item msgid="2892952818207766996">"ရောင်းသူ-တိကျသတ်မှတ်သော ရယူခြင်းဆိုင်ရာ မှားယွင်းချက် စာ ၀"</item>
+ </string-array>
+ <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"လုပ်ဆောင်၍မရပါ။ ထပ်မံကြိုးစားပါ။"</string>
+ <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"ပစ္စည်းမရနိုင်ပါ။"</string>
+ <string name="fingerprint_error_no_space" msgid="1055819001126053318">"လက်ဗွေရာ သိုလှောင်၍မရပါ။ ကျေးဇူးပြု၍ ရှိပြီးလက်ဗွေရာအား ဖယ်ရှားပါ။"</string>
+ <string name="fingerprint_error_timeout" msgid="3927186043737732875">"လက်ဗွေရာအချိန်ကုန် သွားပါသည်။ ထပ်မံကြိုးစားပါ။"</string>
+ <string name="fingerprint_error_vendor" msgid="3175724710791609491">"လက်ဗွေရာအချိန်ကုန် သွားပါသည်။ ထပ်မံကြိုးစားပါ။"</string>
+ <string-array name="fingerprint_error_vendor">
+ <item msgid="5804600450373644614">"ရောင်းသူ-တိကျသတ်မှတ်သော မှားယွင်းချက် စာ။"</item>
+ </string-array>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"ထပ်တူပြုအဆင်အပြင်အားဖတ်ခြင်း"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"အပလီကေးရှင်းအား အကောင့်တစ်ခုအတွက် ထပ်တူညီအောင် လုပ်ဆောင်မှု ဆက်တင်အား ကြည့်ခွင့် ပြုပါ။ ဥပမာ People app က အကောင့်တစ်ခုနဲ့ ထပ်တူညီအောင် လုပ်ရန် ဆက်သွယ်ထားမှု ရှိမရှိ သိရှိနိုင်ခြင်း"</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"ထပ်တူညီအောင် လုပ်ခြင်းအား ပြုနိုင်၊ မပြုနိုင် အပြောင်းအလဲလုပ်ခြင်း"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 3b45940..de3cae4 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -126,6 +126,7 @@
<string name="wfcRegErrorTitle" msgid="2301376280632110664">"Wi-Fi-anrop"</string>
<string-array name="wfcOperatorErrorMessages">
</string-array>
+ <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
<string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Ikke viderekoblet"</string>
<string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> etter <xliff:g id="TIME_DELAY">{2}</xliff:g> sekunder"</string>
@@ -746,6 +747,22 @@
<string name="permdesc_manageFingerprint" msgid="178208705828055464">"Lar appen fremkalle metoder for å legge til og slette fingeravtrykkmaler for bruk."</string>
<string name="permlab_useFingerprint" msgid="3150478619915124905">"bruke fingeravtrykkmaskinvare"</string>
<string name="permdesc_useFingerprint" msgid="9165097460730684114">"Lar appen bruke fingeravtrykkmaskinvare til godkjenning"</string>
+ <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Deler av fingeravtrykket er registrert. Prøv på nytt."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Kunne ikke registrere fingeravtrykket. Prøv på nytt."</string>
+ <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Fingeravtrykksensoren er skitten. Rengjør den og prøv på nytt."</string>
+ <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"Fingeren ble fjernet for raskt. Prøv på nytt."</string>
+ <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"Fingeren ble fjernet for sakte. Prøv på nytt."</string>
+ <string-array name="fingerprint_acquired_vendor">
+ <item msgid="2892952818207766996">"Feilmelding 0 for leverandørspesifikk brukeranskaffelse"</item>
+ </string-array>
+ <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"Kunne ikke behandle fingeravtrykket. Prøv på nytt."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"Maskinvaren 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>
+ <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Tidsavbrudd for fingeravtrykk er nådd. Prøv på nytt."</string>
+ <string-array name="fingerprint_error_vendor">
+ <item msgid="5804600450373644614">"Leverandørspesifikk feilmelding."</item>
+ </string-array>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"lese synkroniseringsinnstillinger"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Lar appen lese synkroniseringsinnstillingene for en konto. For eksempel kan den finne ut om Personer-appen er synkronisert med en konto."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"slå synkronisering av og på"</string>
diff --git a/core/res/res/values-ne-rNP/strings.xml b/core/res/res/values-ne-rNP/strings.xml
index 907047f..1b8180a 100644
--- a/core/res/res/values-ne-rNP/strings.xml
+++ b/core/res/res/values-ne-rNP/strings.xml
@@ -126,6 +126,7 @@
<string name="wfcRegErrorTitle" msgid="2301376280632110664">"Wi-Fi कलिङ"</string>
<string-array name="wfcOperatorErrorMessages">
</string-array>
+ <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
<string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: अगाडि पठाइएको छैन"</string>
<string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> पछि <xliff:g id="TIME_DELAY">{2}</xliff:g> सेकेन्ड"</string>
@@ -746,6 +747,22 @@
<string name="permdesc_manageFingerprint" msgid="178208705828055464">"अनुप्रयोगलाई प्रयोगको लागि औठाछाप टेम्प्लेट थप्न र मेटाउने तरिका आह्वान गर्न अनुमति दिन्छ।"</string>
<string name="permlab_useFingerprint" msgid="3150478619915124905">"औठाछाप हार्डवेयर प्रयोग गर्नुहोस्"</string>
<string name="permdesc_useFingerprint" msgid="9165097460730684114">"अनुप्रयोगलाई प्रमाणीकरणको लागि औठाछाप हार्डवेयर प्रयोग गर्न अनुमति दिन्छ"</string>
+ <string name="fingerprint_acquired_partial" msgid="735082772341716043">"आंशिक औठाछाप पत्ता लाग्यो। कृपया फेरि प्रयास गर्नुहोस्।"</string>
+ <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"औठाछाप प्रशोधन गर्न सकिएन। कृपया फेरि प्रयास गर्नुहोस्।"</string>
+ <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"औँठाछाप सेन्सर फोहोर छ। कृपया सफा गरेर फेरि प्रयास गर्नुहोस्।"</string>
+ <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"औंला निकै छिटो सारियो। कृपया फेरि प्रयास गर्नुहोस्।"</string>
+ <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"औंला निकै ढिला सारियो। कृपया फेरि प्रयास गर्नुहोस्।"</string>
+ <string-array name="fingerprint_acquired_vendor">
+ <item msgid="2892952818207766996">"उत्पादक-निर्दिष्ट अधिग्रहण त्रुटि सन्देश 0"</item>
+ </string-array>
+ <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"प्रसोधन गर्न असमर्थ। फेरि प्रयास गर्नुहोस्।"</string>
+ <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"हार्डवेयर उपलब्ध छैन।"</string>
+ <string name="fingerprint_error_no_space" msgid="1055819001126053318">"औँठाछाप भण्डारण गर्न सकिँदैन। कृपया अवस्थित औठाछाप हटाउनुहोस्।"</string>
+ <string name="fingerprint_error_timeout" msgid="3927186043737732875">"औँठाछापको समय सकिएको छ। फेरि प्रयास गर्नुहोस्।"</string>
+ <string name="fingerprint_error_vendor" msgid="3175724710791609491">"औँठाछापको समय सकिएको छ। फेरि प्रयास गर्नुहोस्।"</string>
+ <string-array name="fingerprint_error_vendor">
+ <item msgid="5804600450373644614">"उत्पादक-निर्दिष्ट त्रुटि सन्देश।"</item>
+ </string-array>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"समीकरण सेटिङहरू पढ्नुहोस्"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"अनुप्रयोगलाई खाताको लागि सिङ्क सेटिङहरू पढ्न अनुमति दिन्छ। उदाहरणको लागि यसले व्यक्तिहरको अनुप्रयोग खातासँग सिङ्क भएको नभएको निर्धारण गर्न सक्दछ।"</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"टगल सिङ्क खुला र बन्द"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index d6c02f5..425cb3a 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -126,6 +126,7 @@
<string name="wfcRegErrorTitle" msgid="2301376280632110664">"Bellen via wifi"</string>
<string-array name="wfcOperatorErrorMessages">
</string-array>
+ <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
<string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: niet doorgeschakeld"</string>
<string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> na <xliff:g id="TIME_DELAY">{2}</xliff:g> seconden"</string>
@@ -746,6 +747,22 @@
<string name="permdesc_manageFingerprint" msgid="178208705828055464">"Hiermee kan de app methoden aanroepen om vingerafdruksjablonen toe te voegen en te verwijderen voor gebruik."</string>
<string name="permlab_useFingerprint" msgid="3150478619915124905">"vingerafdrukhardware gebruiken"</string>
<string name="permdesc_useFingerprint" msgid="9165097460730684114">"Hiermee kan de app vingerafdrukhardware gebruiken voor verificatie"</string>
+ <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Gedeeltelijke vingerafdruk gedetecteerd. Probeer het opnieuw."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Kan vingerafdruk niet verwerken. Probeer het opnieuw."</string>
+ <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"De vingerafdruksensor moet worden schoongemaakt. Probeer het daarna opnieuw."</string>
+ <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"Vinger te snel bewogen. Probeer het opnieuw."</string>
+ <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"Vinger te langzaam bewogen. Probeer het opnieuw."</string>
+ <string-array name="fingerprint_acquired_vendor">
+ <item msgid="2892952818207766996">"Leveranciersspecifieke foutmelding voor acquisitie 0"</item>
+ </string-array>
+ <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"Kan niet verwerken. Probeer het opnieuw."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"Hardware 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>
+ <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Time-out bereikt voor vingerafdruk. Probeer het opnieuw."</string>
+ <string-array name="fingerprint_error_vendor">
+ <item msgid="5804600450373644614">"Leveranciersspecifieke foutmelding"</item>
+ </string-array>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"synchronisatie-instellingen lezen"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Hiermee kan de app de synchronisatie-instellingen voor een account lezen. Dit kan bijvoorbeeld bepalen of de app Personen wordt gesynchroniseerd met een account."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"synchronisatie in- en uitschakelen"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 8e31053..b10ea37 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -128,6 +128,7 @@
<string name="wfcRegErrorTitle" msgid="2301376280632110664">"Połączenia przez Wi-Fi"</string>
<string-array name="wfcOperatorErrorMessages">
</string-array>
+ <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
<string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: nieprzekierowane"</string>
<string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> po <xliff:g id="TIME_DELAY">{2}</xliff:g> sekundach"</string>
@@ -748,6 +749,22 @@
<string name="permdesc_manageFingerprint" msgid="178208705828055464">"Zezwala aplikacji aktywować metody dodawania i usuwania szablonów odcisków palców."</string>
<string name="permlab_useFingerprint" msgid="3150478619915124905">"używanie czytnika linii papilarnych"</string>
<string name="permdesc_useFingerprint" msgid="9165097460730684114">"Zezwala aplikacji na używanie czytnika linii papilarnych na potrzeby autoryzacji"</string>
+ <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Odcisk palca został odczytany tylko częściowo. Spróbuj ponownie."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Nie udało się przetworzyć linii papilarnych. Spróbuj ponownie."</string>
+ <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Czytnik linii papilarnych jest zabrudzony. Wyczyść go i spróbuj ponownie."</string>
+ <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"Palec został uniesiony zbyt szybko. Spróbuj ponownie."</string>
+ <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"Palec został przesunięty zbyt wolno. Spróbuj ponownie."</string>
+ <string-array name="fingerprint_acquired_vendor">
+ <item msgid="2892952818207766996">"Komunikat o błędzie pozyskania specyficzny dla dostawcy 0"</item>
+ </string-array>
+ <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"Nie można przetworzyć. Spróbuj ponownie."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"Czytnik jest niedostę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>
+ <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Osiągnięto limit czasu odczytu linii papilarnych. Spróbuj ponownie."</string>
+ <string-array name="fingerprint_error_vendor">
+ <item msgid="5804600450373644614">"Komunikat o błędzie specyficzny dla dostawcy."</item>
+ </string-array>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"czytanie ustawień synchronizacji"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Zezwala aplikacji na odczyt ustawień synchronizacji konta. Pozwala to na przykład określić, czy aplikacja Ludzie jest zsynchronizowana z kontem."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"włączanie i wyłączanie synchronizacji"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 7a0985c..4f1187c 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -126,6 +126,7 @@
<string name="wfcRegErrorTitle" msgid="2301376280632110664">"Chamadas Wi-Fi"</string>
<string-array name="wfcOperatorErrorMessages">
</string-array>
+ <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
<string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Não reencaminhado"</string>
<string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> após <xliff:g id="TIME_DELAY">{2}</xliff:g> segundos"</string>
@@ -746,6 +747,22 @@
<string name="permdesc_manageFingerprint" msgid="178208705828055464">"Permite que a aplicação invoque métodos para adicionar e eliminar modelos de impressão digital para utilização."</string>
<string name="permlab_useFingerprint" msgid="3150478619915124905">"utilizar o hardware de impressão digital"</string>
<string name="permdesc_useFingerprint" msgid="9165097460730684114">"Permite que a aplicação utilize o hardware de impressão digital para autenticação"</string>
+ <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Impressão digital detetada. Tente novamente."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Não foi possível processar a impressão digital. Tente novamente."</string>
+ <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"O sensor de impressões digitais está sujo. Limpe-o e tente novamente."</string>
+ <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"O dedo moveu-se demasiado rápido. Tente novamente."</string>
+ <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"O dedo moveu-se demasiado devagar. Tente novamente."</string>
+ <string-array name="fingerprint_acquired_vendor">
+ <item msgid="2892952818207766996">"Mensagem de erro de aquisição específica do fornecedor 0"</item>
+ </string-array>
+ <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"Não é possível processar. Tente novamente."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"Hardware 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>
+ <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Foi atingido o limite de tempo da impressão digital. Tente novamente."</string>
+ <string-array name="fingerprint_error_vendor">
+ <item msgid="5804600450373644614">"Mensagem de erro específica do fornecedor."</item>
+ </string-array>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"ler definições de sincronização"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Permite que a aplicação leia as definições de sincronização de uma conta. Por exemplo, pode determinar se a aplicação Pessoas está sincronizada com uma conta."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"ativar e desativar a sincronização"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 8f86b40..6f68f06 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -126,6 +126,7 @@
<string name="wfcRegErrorTitle" msgid="2301376280632110664">"Chamadas por Wi-Fi"</string>
<string-array name="wfcOperatorErrorMessages">
</string-array>
+ <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
<string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Não encaminhado"</string>
<string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> após <xliff:g id="TIME_DELAY">{2}</xliff:g> segundos"</string>
@@ -746,6 +747,22 @@
<string name="permdesc_manageFingerprint" msgid="178208705828055464">"Permite que o app execute métodos para adicionar e excluir modelos de impressão digital para uso."</string>
<string name="permlab_useFingerprint" msgid="3150478619915124905">"usar hardware de impressão digital"</string>
<string name="permdesc_useFingerprint" msgid="9165097460730684114">"Permite que o app use hardware de impressão digital para autenticação."</string>
+ <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Impressão digital parcial detectada. Tente novamente."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Não foi possível processar a impressão digital. Tente novamente."</string>
+ <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"O sensor de impressão digital está sujo. Limpe-o e tente novamente."</string>
+ <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"O dedo foi retirado rápido demais. Tente novamente."</string>
+ <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"O movimento do dedo está muito lento. Tente novamente."</string>
+ <string-array name="fingerprint_acquired_vendor">
+ <item msgid="2892952818207766996">"Mensagem de erro de aquisição específica de fornecedor 0"</item>
+ </string-array>
+ <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"Não foi possível concluir o processo. Tente novamente."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"Hardware indisponí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>
+ <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Tempo máximo para captura da impressão digital atingido. Tente novamente."</string>
+ <string-array name="fingerprint_error_vendor">
+ <item msgid="5804600450373644614">"Mensagem de erro específica de fornecedor."</item>
+ </string-array>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"ler as configurações de sincronização"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Permite que o app leia as configurações de sincronização de uma conta. Por exemplo, pode determinar se o app People está sincronizado com uma conta."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"ativar e desativar sincronização"</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 6246941..dc29c19 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -127,6 +127,7 @@
<string name="wfcRegErrorTitle" msgid="2301376280632110664">"Apelare prin Wi-Fi"</string>
<string-array name="wfcOperatorErrorMessages">
</string-array>
+ <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
<string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: neredirecţionată"</string>
<string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> după <xliff:g id="TIME_DELAY">{2}</xliff:g> (de) secunde"</string>
@@ -747,6 +748,22 @@
<string name="permdesc_manageFingerprint" msgid="178208705828055464">"Permite aplicației să invoce metode pentru a adăuga și pentru a șterge șabloane de amprentă pentru utilizare."</string>
<string name="permlab_useFingerprint" msgid="3150478619915124905">"folosește hardware-ul pentru amprentă"</string>
<string name="permdesc_useFingerprint" msgid="9165097460730684114">"Permite aplicației să folosească hardware pentru amprentă pentru autentificare"</string>
+ <string name="fingerprint_acquired_partial" msgid="735082772341716043">"S-a detectat parțial amprenta. Încercați din nou."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Amprenta nu a putut fi procesată. Încercați din nou."</string>
+ <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Senzorul pentru amprente este murdar. Curățați-l și încercați din nou."</string>
+ <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"Ați mișcat degetul prea repede. Încercați din nou."</string>
+ <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"Ați mișcat degetul prea încet. Încercați din nou."</string>
+ <string-array name="fingerprint_acquired_vendor">
+ <item msgid="2892952818207766996">"Mesaj de eroare 0 specific furnizorului pentru achiziție"</item>
+ </string-array>
+ <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"Nu se poate procesa. Încercați din nou."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"Hardware-ul 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>
+ <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Timpul pentru amprentare a expirat. Încercați din nou."</string>
+ <string-array name="fingerprint_error_vendor">
+ <item msgid="5804600450373644614">"Mesaj de eroare specific furnizorului."</item>
+ </string-array>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"citire setări sincronizare"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Permite aplicației să citească setările de sincronizare ale unui cont. De exemplu, cu această permisiune aplicația poate determina dacă aplicația Persoane este sincronizată cu un anumit cont."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"activează/dezactivează sincronizarea"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 21cc5b7..eb3b74a 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -128,6 +128,7 @@
<string name="wfcRegErrorTitle" msgid="2301376280632110664">"Звонки по Wi-Fi"</string>
<string-array name="wfcOperatorErrorMessages">
</string-array>
+ <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
<string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: не переадресовано"</string>
<string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> через <xliff:g id="TIME_DELAY">{2}</xliff:g> с."</string>
@@ -748,6 +749,22 @@
<string name="permdesc_manageFingerprint" msgid="178208705828055464">"Приложение сможет добавлять и удалять шаблоны отпечатков пальцев."</string>
<string name="permlab_useFingerprint" msgid="3150478619915124905">"использование сканера отпечатков"</string>
<string name="permdesc_useFingerprint" msgid="9165097460730684114">"Приложение сможет использовать сканер отпечатков пальцев для аутентификации."</string>
+ <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Отсканирована только часть пальца. Повторите попытку."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Не удалось распознать отпечаток. Повторите попытку."</string>
+ <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Очистите сканер и повторите попытку."</string>
+ <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"Вы слишком быстро убрали палец. Повторите попытку."</string>
+ <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"Вы слишком долго удерживали палец. Повторите попытку."</string>
+ <string-array name="fingerprint_acquired_vendor">
+ <item msgid="2892952818207766996">"Сообщение продавца об ошибке при сканировании отпечатка (0)"</item>
+ </string-array>
+ <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"Не удалось распознать отпечаток. Повторите попытку."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"Сканер недоступен."</string>
+ <string name="fingerprint_error_no_space" msgid="1055819001126053318">"Чтобы сохранить новый отпечаток, удалите существующий."</string>
+ <string name="fingerprint_error_timeout" msgid="3927186043737732875">"Превышено время ожидания. Повторите попытку."</string>
+ <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Превышено время ожидания. Повторите попытку."</string>
+ <string-array name="fingerprint_error_vendor">
+ <item msgid="5804600450373644614">"Сообщение продавца об ошибке."</item>
+ </string-array>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"Просмотр настроек синхронизации"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Приложение сможет просматривать настройки синхронизации аккаунта, например определять, включена ли синхронизация для приложения \"Контакты\"."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"Включение/выключение синхронизации"</string>
diff --git a/core/res/res/values-si-rLK/strings.xml b/core/res/res/values-si-rLK/strings.xml
index 1b9b317..6d8b6df 100644
--- a/core/res/res/values-si-rLK/strings.xml
+++ b/core/res/res/values-si-rLK/strings.xml
@@ -126,6 +126,7 @@
<string name="wfcRegErrorTitle" msgid="2301376280632110664">"Wi-Fi ඇමතීම"</string>
<string-array name="wfcOperatorErrorMessages">
</string-array>
+ <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
<string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: ඉදිරියට නොයවන ලදි"</string>
<string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: තත්පර <xliff:g id="TIME_DELAY">{2}</xliff:g> ට පසුව <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
@@ -746,6 +747,22 @@
<string name="permdesc_manageFingerprint" msgid="178208705828055464">"ඇඟිලි සලකුණු සැකිලි එකතු කිරීමට සහ ඉවත් කිරීමට අදාළ විධික්රම භාවිතය සඳහා මෙම යෙදුමට අවසර දෙයි."</string>
<string name="permlab_useFingerprint" msgid="3150478619915124905">"ඇඟිලි සලකුණු දෘඩාංග භාවිතා කරන්න."</string>
<string name="permdesc_useFingerprint" msgid="9165097460730684114">"අනන්යතාවය තහවුරු කරගැනීමට ඇඟිලි සලකුණු දෘඩාංග භාවිතා කිරීමට මෙම යෙදුමට ඉඩ දෙන්න."</string>
+ <string name="fingerprint_acquired_partial" msgid="735082772341716043">"ඇඟිලි සලකුණ අඩ වශයෙන් අනාවරණය කර ගැනිණි. කරුණාකර නැවත උත්සාහ කරන්න."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"ඇඟිලි සලකුණ පිරිසැකසීමට නොහැකි විය. කරුණාකර නැවත උත්සාහ කරන්න."</string>
+ <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"ඇඟිලි සලකුණු සංවේදකය අපිරිසිදුයි. කරුණාකර පිරිසිදු කර නැවත උත්සාහ කරන්න."</string>
+ <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"ඇඟිල්ල වේගයෙන් ගෙනයන ලදි. කරුණාකර නැවත උත්සාහ කරන්න."</string>
+ <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"ඇඟිල්ල සෙමින් ගෙන යන ලදී. කරුණාකර නැවත උත්සාහ කරන්න."</string>
+ <string-array name="fingerprint_acquired_vendor">
+ <item msgid="2892952818207766996">"විකුණුම්කරු-විශේෂිත අත්පත්පත් කර ගැනීමේ දෝෂ පණිවිඩය 0"</item>
+ </string-array>
+ <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"ක්රියාවලිය සම්පූර්ණ කිරීමට නොහැක. නැවත උත්සාහ කරන්න."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"දෘඪාංගය ලද නොහැකිය."</string>
+ <string name="fingerprint_error_no_space" msgid="1055819001126053318">"ඇඟිලි සලකුණ ගබඩා කළ නොහැක. දැනට පවතින ඇඟිලි සලකුණක් ඉවත් කරන්න."</string>
+ <string name="fingerprint_error_timeout" msgid="3927186043737732875">"ඇඟිලි සලකුණු කාල නිමාව ළඟා විය. නැවත උත්සාහ කරන්න."</string>
+ <string name="fingerprint_error_vendor" msgid="3175724710791609491">"ඇඟිලි සලකුණු කාල නිමාව ළඟා විය. නැවත උත්සාහ කරන්න."</string>
+ <string-array name="fingerprint_error_vendor">
+ <item msgid="5804600450373644614">"විකුණුම්කරු-විශේෂිත දෝෂ පණිවිඩය."</item>
+ </string-array>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"සමමුහුර්ත සැකසීම් කියවන්න"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"ගිණුම සඳහා සමමුහුර්ත සැකසීම් කියවීමට යෙදුමට අවසර දෙන්න. උදාහරණයක් ලෙස, ගිණුමක් සමඟ පුද්ගල යෙදුම සමමුහුර්ත දැයි මෙයට හඳුනා ගත හැක."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"සමමුහුර්ත කිරීම සක්රිය කරන්න සහ අක්රිය කරන්න"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index c46fc93..9059038 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -128,6 +128,7 @@
<string name="wfcRegErrorTitle" msgid="2301376280632110664">"Volanie cez Wi-Fi"</string>
<string-array name="wfcOperatorErrorMessages">
</string-array>
+ <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
<string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Nepresmerované"</string>
<string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> po <xliff:g id="TIME_DELAY">{2}</xliff:g> s"</string>
@@ -748,6 +749,22 @@
<string name="permdesc_manageFingerprint" msgid="178208705828055464">"Umožňuje aplikácii zavolať metódy, ktoré pridávajú a odstraňujú vzory odtlačkov prstov."</string>
<string name="permlab_useFingerprint" msgid="3150478619915124905">"použiť hardvér na snímanie odtlačkov prstov"</string>
<string name="permdesc_useFingerprint" msgid="9165097460730684114">"Umožňuje aplikácii používať na overenie totožnosti hardvér na snímanie odtlačkov prstov."</string>
+ <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Podarilo sa rozpoznať iba časť odtlačku prsta. Skúste to znova."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Odtlačok prsta sa nepodarilo spracovať. Skúste to znova."</string>
+ <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Snímač odtlačkov je špinavý. Vyčistite ho a skúste to znova."</string>
+ <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"Pohli ste prstom príliš rýchlo. Skúste to znova."</string>
+ <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"Pohli ste prstom príliš pomaly. Skúste to znova."</string>
+ <string-array name="fingerprint_acquired_vendor">
+ <item msgid="2892952818207766996">"Chybové hlásenie o akvizícii týkajúcej sa konkrétneho dodávateľa 0"</item>
+ </string-array>
+ <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"Došlo k chybe spracovania. Skúste to znova."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"Hardvér 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>
+ <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Časový limit rozpoznania odtlačku vypršal. Skúste to znova."</string>
+ <string-array name="fingerprint_error_vendor">
+ <item msgid="5804600450373644614">"Chybové hlásenie týkajúce sa konkrétneho dodávateľa"</item>
+ </string-array>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"čítať nastavenia synchronizácie"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Umožňuje aplikácii čítať nastavenia synchronizácie v účte. Môže napríklad určiť, či je s účtom synchronizovaná aplikácia Ľudia."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"zapnúť alebo vypnúť synchronizáciu"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index a42b022..c5f78b1 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -128,6 +128,7 @@
<string name="wfcRegErrorTitle" msgid="2301376280632110664">"Klicanje prek Wi-Fi-ja"</string>
<string-array name="wfcOperatorErrorMessages">
</string-array>
+ <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
<string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: ni posredovano"</string>
<string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> po toliko sekundah: <xliff:g id="TIME_DELAY">{2}</xliff:g>"</string>
@@ -748,6 +749,22 @@
<string name="permdesc_manageFingerprint" msgid="178208705828055464">"Aplikaciji omogoča sprožanje načinov za dodajanje in brisanje predlog s prstnimi odtisi za uporabo."</string>
<string name="permlab_useFingerprint" msgid="3150478619915124905">"uporaba strojne opreme za prstne odtise"</string>
<string name="permdesc_useFingerprint" msgid="9165097460730684114">"Aplikaciji omogoča uporabo strojne opreme za prstne odtise za preverjanje pristnosti"</string>
+ <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Zaznan delni prstni odtis. Poskusite znova."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Prstnega odtisa ni bilo mogoče obdelati. Poskusite znova."</string>
+ <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Tipalo prstnih odtisov je umazano. Očistite ga in poskusite znova."</string>
+ <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"Prehiter premik prsta. Poskusite znova."</string>
+ <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"Prepočasen premik prsta. Poskusite znova."</string>
+ <string-array name="fingerprint_acquired_vendor">
+ <item msgid="2892952818207766996">"Vendor-specific acquisition error message 0"</item>
+ </string-array>
+ <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"Obdelava ni mogoča. Poskusite znova."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"Strojna oprema 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>
+ <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Dosežena časovna omejitev za prstni odtis. Poskusite znova."</string>
+ <string-array name="fingerprint_error_vendor">
+ <item msgid="5804600450373644614">"Vendor-specifc error message."</item>
+ </string-array>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"branje nastavitev sinhronizacije"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Aplikaciji omogoča branje nastavitev sinhronizacije za račun. S tem lahko aplikacija na primer ugotovi, ali je aplikacija Ljudje sinhronizirana z računom."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"vklop in izklop sinhronizacije"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 7e2f18e..189113a 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -127,6 +127,7 @@
<string name="wfcRegErrorTitle" msgid="2301376280632110664">"Позивање преко Wi-Fi-ја"</string>
<string-array name="wfcOperatorErrorMessages">
</string-array>
+ <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
<string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Није прослеђено"</string>
<string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> након <xliff:g id="TIME_DELAY">{2}</xliff:g> секунде(и)"</string>
@@ -747,6 +748,22 @@
<string name="permdesc_manageFingerprint" msgid="178208705828055464">"Дозвољава апликацији да активира методе за додавање и брисање шаблона отисака прстију који ће се користити."</string>
<string name="permlab_useFingerprint" msgid="3150478619915124905">"користи хардвер за отиске прстију"</string>
<string name="permdesc_useFingerprint" msgid="9165097460730684114">"Дозвољава апликацији да користи хардвер за отиске прстију ради потврде аутентичности"</string>
+ <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Откривен је делимични отисак прста. Покушајте поново."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Није успела обрада отиска прста. Покушајте поново."</string>
+ <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Сензор за отиске прстију је прљав. Очистите га и покушајте поново."</string>
+ <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"Превише брзо сте померили прст. Покушајте поново."</string>
+ <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"Превише споро сте померили прст. Покушајте поново."</string>
+ <string-array name="fingerprint_acquired_vendor">
+ <item msgid="2892952818207766996">"Порука о грешци за аквизицију специфична за произвођача 0"</item>
+ </string-array>
+ <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"Обрада није могућа. Покушајте поново."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"Хардвер није доступан."</string>
+ <string name="fingerprint_error_no_space" msgid="1055819001126053318">"Није могуће сачувати отисак прста. Уклоните неки од постојећих отисака прстију."</string>
+ <string name="fingerprint_error_timeout" msgid="3927186043737732875">"Временско ограничење за отисак прста је истекло. Покушајте поново."</string>
+ <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Временско ограничење за отисак прста је истекло. Покушајте поново."</string>
+ <string-array name="fingerprint_error_vendor">
+ <item msgid="5804600450373644614">"Порука о грешци специфична за произвођача."</item>
+ </string-array>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"читање подешавања синхронизације"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Дозвољава апликацији да чита подешавања синхронизације за налог. На пример, овако може да се утврди да ли је апликација Људи синхронизована са налогом."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"укључивање и искључивање синхронизације"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 326842c..3973636 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -126,6 +126,7 @@
<string name="wfcRegErrorTitle" msgid="2301376280632110664">"Wi-Fi-samtal"</string>
<string-array name="wfcOperatorErrorMessages">
</string-array>
+ <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
<string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Vidarebefordras inte"</string>
<string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g><xliff:g id="DIALING_NUMBER">{1}</xliff:g> efter <xliff:g id="TIME_DELAY">{2}</xliff:g> sekunder"</string>
@@ -746,6 +747,22 @@
<string name="permdesc_manageFingerprint" msgid="178208705828055464">"Tillåter att appen anropar metoder för att lägga till och radera fingeravtrycksmallar."</string>
<string name="permlab_useFingerprint" msgid="3150478619915124905">"använda maskinvara för fingeravtryck"</string>
<string name="permdesc_useFingerprint" msgid="9165097460730684114">"Tillåter att appen använder maskinvara för fingeravtryck vid autentisering"</string>
+ <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Ofullständigt fingeravtryck. Försök igen."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Det gick inte att bearbeta fingeravtrycket. Försök igen."</string>
+ <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Fingeravtryckssensorn är smutsig. Rengör den och försök igen."</string>
+ <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"Du flyttade fingret för snabbt. Försök igen."</string>
+ <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"Du flyttade fingret för långsamt. Försök igen."</string>
+ <string-array name="fingerprint_acquired_vendor">
+ <item msgid="2892952818207766996">"Leverantörsspecifikt felmeddelande om förvärv 0"</item>
+ </string-array>
+ <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"Det går inte att bearbeta fingeravtrycket. Försök igen."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"Maskinvaran är inte tillgänglig."</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>
+ <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Tidsgränsen för fingeravtrycket har uppnåtts. Försök igen."</string>
+ <string-array name="fingerprint_error_vendor">
+ <item msgid="5804600450373644614">"Leverantörsspecifikt felmeddelande"</item>
+ </string-array>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"läsa synkroniseringsinställningar"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Tillåter att appen läser synkroniseringsinställningarna för ett konto. Detta kan användas till exempel för att avgöra om appen Personer är synkroniserad med ett konto."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"aktivera/inaktivera synkronisering"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index d681855..0f1d7ae 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -126,6 +126,7 @@
<string name="wfcRegErrorTitle" msgid="2301376280632110664">"Upigaji Simu kwa Wi-Fi"</string>
<string-array name="wfcOperatorErrorMessages">
</string-array>
+ <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
<string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Haijatumiwa mwingine"</string>
<string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> baada ya sekunde <xliff:g id="TIME_DELAY">{2}</xliff:g>"</string>
@@ -746,6 +747,22 @@
<string name="permdesc_manageFingerprint" msgid="178208705828055464">"Huruhusu programu kuomba njia za kuongeza na kufuta violezo vya kitambulisho kwa matumizi."</string>
<string name="permlab_useFingerprint" msgid="3150478619915124905">"tumia maunzi ya kitambulisho"</string>
<string name="permdesc_useFingerprint" msgid="9165097460730684114">"Huruhusu programu kutumia maunzi ya kitambulisho kwa uthibitisho"</string>
+ <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Kihisi kimegundua sehemu ya kitambulisho. Tafadhali jaribu tena."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Haikuweza kuchakata kitambulisho. Tafadhali jaribu tena."</string>
+ <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Kihisi kitambulisho ni kichafu. Tafadhali kisafishe na ujaribu tena."</string>
+ <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"Ulisogeza kidole kwa kasi mno. Tafadhali jaribu tena."</string>
+ <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"Ulisogeza kidole pole pole mno. Tafadhali jaribu tena."</string>
+ <string-array name="fingerprint_acquired_vendor">
+ <item msgid="2892952818207766996">"Ujumbe maalum kwa muuzaji kuhusu hitilafu ya uletaji wa kitambulisho 0"</item>
+ </string-array>
+ <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"Haikuweza kuchakata. Jaribu tena."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"Maunzi 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>
+ <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Muda wa kitambulisho umekwisha. Jaribu tena."</string>
+ <string-array name="fingerprint_error_vendor">
+ <item msgid="5804600450373644614">"Ujumbe maalum kwa muuzaji kuhusu hitilafu."</item>
+ </string-array>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"kusoma mipangilio ya usawazishaji"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Inaruhusu programu kusoma mipangilio ya upatanishi wa akaunti. Kwa mfano, huku kunaweza kuamua kama programu ya Watu imepatanishwa na akaunti."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"kuwasha na kuzima usawazishaji"</string>
diff --git a/core/res/res/values-ta-rIN/strings.xml b/core/res/res/values-ta-rIN/strings.xml
index 086fe6a..c473bc1 100644
--- a/core/res/res/values-ta-rIN/strings.xml
+++ b/core/res/res/values-ta-rIN/strings.xml
@@ -126,6 +126,7 @@
<string name="wfcRegErrorTitle" msgid="2301376280632110664">"வைஃபை அழைப்பு"</string>
<string-array name="wfcOperatorErrorMessages">
</string-array>
+ <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
<string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: பகிரப்படவில்லை"</string>
<string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="TIME_DELAY">{2}</xliff:g> வினாடிகளுக்குப் பிறகு <xliff:g id="DIALING_NUMBER">{1}</xliff:g> ஐப் பகிர்"</string>
@@ -746,6 +747,22 @@
<string name="permdesc_manageFingerprint" msgid="178208705828055464">"பயன்படுத்துவதற்காக, கைரேகை டெம்ப்ளேட்களைச் சேர்க்க மற்றும் நீக்குவதற்கான செயல்முறைகளை இயக்குவதற்குப் பயன்பாட்டை அனுமதிக்கும்."</string>
<string name="permlab_useFingerprint" msgid="3150478619915124905">"கைரேகை வன்பொருளைப் பயன்படுத்து"</string>
<string name="permdesc_useFingerprint" msgid="9165097460730684114">"அங்கீகரிப்பதற்கு, கைரேகை வன்பொருளைப் பயன்படுத்த, பயன்பாட்டை அனுமதிக்கும்"</string>
+ <string name="fingerprint_acquired_partial" msgid="735082772341716043">"கைரேகையை ஓரளவுதான் கண்டறிய முடிந்தது. மீண்டும் முயலவும்."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"கைரேகையைச் செயலாக்க முடியவில்லை. மீண்டும் முயலவும்."</string>
+ <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"கைரேகை உணர்வியில் தூசி உள்ளது. சுத்தம் செய்து, முயலவும்."</string>
+ <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"விரலை வேகமாக எடுத்துவிட்டீர்கள். மீண்டும் முயலவும்."</string>
+ <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"விரலை மெதுவாக எடுத்துவிட்டீர்கள். மீண்டும் முயலவும்."</string>
+ <string-array name="fingerprint_acquired_vendor">
+ <item msgid="2892952818207766996">"விற்பனையாளர் சார்ந்த பெறுதல் தொடர்பான பிழைச் செய்தி 0"</item>
+ </string-array>
+ <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"செயல்படுத்த முடியவில்லை. மீண்டும் முயலவும்."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"வன்பொருள் இல்லை."</string>
+ <string name="fingerprint_error_no_space" msgid="1055819001126053318">"கைரேகையைச் சேமிக்க முடியவில்லை. ஏற்கனவே உள்ள கைரேகையை அகற்றவும்."</string>
+ <string name="fingerprint_error_timeout" msgid="3927186043737732875">"கைரேகைக்கான நேரம் முடிந்தது. மீண்டும் முயலவும்."</string>
+ <string name="fingerprint_error_vendor" msgid="3175724710791609491">"கைரேகைக்கான நேரம் முடிந்தது. மீண்டும் முயலவும்."</string>
+ <string-array name="fingerprint_error_vendor">
+ <item msgid="5804600450373644614">"விற்பனையாளர் சார்ந்த பிழைச் செய்தி."</item>
+ </string-array>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"ஒத்திசைவு அமைப்புகளைப் படித்தல்"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"கணக்கிற்கான ஒத்திசைவு அமைப்புகளைப் படிக்க பயன்பாட்டை அனுமதிக்கிறது. எடுத்துக்காட்டாக, பீப்பிள் பயன்பாடு கணக்குடன் ஒத்திசைக்கப்பட்டுள்ளதா என்பதை இது தீர்மானிக்கலாம்."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"ஒத்திசைவை இயக்குவதையும், முடக்குவதையும் மாற்றுதல்"</string>
diff --git a/core/res/res/values-te-rIN/strings.xml b/core/res/res/values-te-rIN/strings.xml
index 5f59597..5bc59c3 100644
--- a/core/res/res/values-te-rIN/strings.xml
+++ b/core/res/res/values-te-rIN/strings.xml
@@ -126,6 +126,7 @@
<string name="wfcRegErrorTitle" msgid="2301376280632110664">"Wi-Fi కాలింగ్"</string>
<string-array name="wfcOperatorErrorMessages">
</string-array>
+ <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
<string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: ఫార్వార్డ్ చేయబడలేదు"</string>
<string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="TIME_DELAY">{2}</xliff:g> సెకన్ల తర్వాత <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
@@ -746,6 +747,22 @@
<string name="permdesc_manageFingerprint" msgid="178208705828055464">"వినియోగం కోసం వేలిముద్ర టెంప్లేట్లను జోడించే మరియు తొలగించే పద్ధతులను అమలు చేయడానికి అనువర్తనాన్ని అనుమతిస్తుంది."</string>
<string name="permlab_useFingerprint" msgid="3150478619915124905">"వేలిముద్ర హార్డ్వేర్ని ఉపయోగించడానికి అనుమతి"</string>
<string name="permdesc_useFingerprint" msgid="9165097460730684114">"ప్రామాణీకరణ కోసం వేలిముద్ర హార్డ్వేర్ను ఉపయోగించడానికి అనువర్తనాన్ని అనుమతిస్తుంది"</string>
+ <string name="fingerprint_acquired_partial" msgid="735082772341716043">"పాక్షిక వేలిముద్ర గుర్తించబడింది. దయచేసి మళ్లీ ప్రయత్నించండి."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"వేలిముద్రను ప్రాసెస్ చేయడం సాధ్యపడలేదు. దయచేసి మళ్లీ ప్రయత్నించండి."</string>
+ <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"వేలిముద్ర సెన్సార్ మురికిగా ఉంది. దయచేసి శుభ్రపరిచి, మళ్లీ ప్రయత్నించండి."</string>
+ <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"వేలిని చాలా తొందరగా కదిలించారు. దయచేసి మళ్లీ ప్రయత్నించండి."</string>
+ <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"వేలిని చాలా నిదానంగా కదిలించారు. దయచేసి మళ్లీ ప్రయత్నించండి."</string>
+ <string-array name="fingerprint_acquired_vendor">
+ <item msgid="2892952818207766996">"విక్రేత-నిర్దిష్ట సేకరణ లోప సందేశం 0"</item>
+ </string-array>
+ <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"ప్రాసెస్ చేయడం సాధ్యపడలేదు. మళ్లీ ప్రయత్నించండి."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"హార్డ్వేర్ అందుబాటులో లేదు."</string>
+ <string name="fingerprint_error_no_space" msgid="1055819001126053318">"వేలిముద్రను నిల్వ చేయడం సాధ్యపడదు. దయచేసి ఇప్పటికే ఉన్న వేలిముద్రను తీసివేయండి."</string>
+ <string name="fingerprint_error_timeout" msgid="3927186043737732875">"వేలిముద్ర గడువు సమయం చేరుకుంది. మళ్లీ ప్రయత్నించండి."</string>
+ <string name="fingerprint_error_vendor" msgid="3175724710791609491">"వేలిముద్ర గడువు సమయం చేరుకుంది. మళ్లీ ప్రయత్నించండి."</string>
+ <string-array name="fingerprint_error_vendor">
+ <item msgid="5804600450373644614">"విక్రేత-నిర్దిష్ట లోప సందేశం."</item>
+ </string-array>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"సమకాలీకరణ సెట్టింగ్లను చదవడం"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"ఖాతా యొక్క సమకాలీకరణ సెట్టింగ్లను చదవడానికి అనువర్తనాన్ని అనుమతిస్తుంది. ఉదాహరణకు, వ్యక్తుల అనువర్తనం ఖాతాతో సమకాలీకరించబడాలా లేదా అనే విషయాన్ని ఇది నిశ్చయించవచ్చు."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"సమకాలీకరణను ఆన్ మరియు ఆఫ్కు టోగుల్ చేయడం"</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index df4ff52..adbe8c9 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -126,6 +126,7 @@
<string name="wfcRegErrorTitle" msgid="2301376280632110664">"การโทรผ่าน Wi-Fi"</string>
<string-array name="wfcOperatorErrorMessages">
</string-array>
+ <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
<string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: ไม่ได้โอนสาย"</string>
<string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> หลังผ่านไป <xliff:g id="TIME_DELAY">{2}</xliff:g> วินาที"</string>
@@ -746,6 +747,22 @@
<string name="permdesc_manageFingerprint" msgid="178208705828055464">"อนุญาตให้แอปเรียกใช้วิธีการเพื่อเพิ่มและลบเทมเพลตลายนิ้วมือสำหรับการใช้งาน"</string>
<string name="permlab_useFingerprint" msgid="3150478619915124905">"ใช้ฮาร์ดแวร์ลายนิ้วมือ"</string>
<string name="permdesc_useFingerprint" msgid="9165097460730684114">"อนุญาตให้แอปใช้ฮาร์ดแวร์ลายนิ้วมือเพื่อตรวจสอบสิทธิ์"</string>
+ <string name="fingerprint_acquired_partial" msgid="735082772341716043">"ตรวจพบลายนิ้วมือเพียงบางส่วน โปรดลองอีกครั้ง"</string>
+ <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"ไม่สามารถประมวลผลลายนิ้วมือได้ โปรดลองอีกครั้ง"</string>
+ <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"เซ็นเซอร์ลายนิ้วมือไม่สะอาด โปรดทำความสะอาดและลองอีกครั้ง"</string>
+ <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"เคลื่อนนิ้วเร็วเกินไป โปรดลองอีกครั้ง"</string>
+ <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"เคลื่อนนิ้วช้าเกินไป โปรดลองอีกครั้ง"</string>
+ <string-array name="fingerprint_acquired_vendor">
+ <item msgid="2892952818207766996">"ข้อความแสดงข้อผิดพลาดการกระทำเฉพาะผู้ขาย 0"</item>
+ </string-array>
+ <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"ไม่สามารถดำเนินการได้ โปรดลองอีกครั้ง"</string>
+ <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"ฮาร์ดแวร์ไม่พร้อมใช้งาน"</string>
+ <string name="fingerprint_error_no_space" msgid="1055819001126053318">"ไม่สามารถเก็บลายนิ้วมือได้ โปรดนำลายนิ้วมือที่มีอยู่ออก"</string>
+ <string name="fingerprint_error_timeout" msgid="3927186043737732875">"หมดเวลาใช้ลายนิ้วมือแล้ว โปรดลองอีกครั้ง"</string>
+ <string name="fingerprint_error_vendor" msgid="3175724710791609491">"หมดเวลาใช้ลายนิ้วมือแล้ว โปรดลองอีกครั้ง"</string>
+ <string-array name="fingerprint_error_vendor">
+ <item msgid="5804600450373644614">"ข้อความแสดงข้อผิดพลาดเฉพาะผู้ขาย"</item>
+ </string-array>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"อ่านการตั้งค่าการซิงค์แล้ว"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"อนุญาตให้แอปพลิเคชันอ่านการตั้งค่าการซิงค์ของบัญชี ตัวอย่างเช่น การอนุญาตนี้สามารถระบุได้ว่าแอปพลิเคชัน People ซิงค์กับบัญชีหรือไม่"</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"สลับระหว่างเปิดและปิดการซิงค์"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 41480ed..978939b 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -126,6 +126,7 @@
<string name="wfcRegErrorTitle" msgid="2301376280632110664">"Pagtawag sa pamamagitan ng Wi-Fi"</string>
<string-array name="wfcOperatorErrorMessages">
</string-array>
+ <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
<string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Hindi naipasa"</string>
<string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> pagkatapos ng <xliff:g id="TIME_DELAY">{2}</xliff:g> (na) segundo"</string>
@@ -746,6 +747,22 @@
<string name="permdesc_manageFingerprint" msgid="178208705828055464">"Pinapayagan ang app na mag-invoke ng mga paraan upang magdagdag at mag-delete ng mga template ng fingerprint na magagamit."</string>
<string name="permlab_useFingerprint" msgid="3150478619915124905">"gamitina ng hardware ng fingerprint"</string>
<string name="permdesc_useFingerprint" msgid="9165097460730684114">"Pinapayagan ang app na gumamit ng hardware ng fingerprint para sa pagpapatotoo"</string>
+ <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Hindi buo ang natukoy na fingerprint. Pakisubukang muli."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Hindi maproseso ang fingerprint. Pakisubukang muli."</string>
+ <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Marumi ang sensor ng fingerprint. Pakilinis at subukang muli."</string>
+ <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"Masyadong mabilis ang paggalaw ng daliri. Pakisubukang muli."</string>
+ <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"Masyadong mabagal ang paggalaw ng daliri. Pakisubukang muli."</string>
+ <string-array name="fingerprint_acquired_vendor">
+ <item msgid="2892952818207766996">"Mensahe ng error sa pagkuha na partikular sa vendor 0"</item>
+ </string-array>
+ <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"Hindi maproseso. Subukang muli."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"Hindi available ang hardware."</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>
+ <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Nag-time out ang fingerprint. Subukang muli."</string>
+ <string-array name="fingerprint_error_vendor">
+ <item msgid="5804600450373644614">"Mensahe ng error na partikular sa vendor."</item>
+ </string-array>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"basahin ang mga setting ng sync"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Pinapayagan ang app na basahin ang mga setting ng pag-sync para sa isang account. Halimbawa, matutukoy nito kung naka-sync ang app na Mga Tao sa isang account."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"I-toggle on at off ang pag-sync"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index f254b5c..6c80b92 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -126,6 +126,7 @@
<string name="wfcRegErrorTitle" msgid="2301376280632110664">"Kablosuz Çağrı"</string>
<string-array name="wfcOperatorErrorMessages">
</string-array>
+ <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
<string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Yönlendirilmedi"</string>
<string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="TIME_DELAY">{2}</xliff:g> saniye sonra <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
@@ -746,6 +747,22 @@
<string name="permdesc_manageFingerprint" msgid="178208705828055464">"Uygulamanın, kullanılacak parmak izi şablonlarını ekleme ve silme yöntemlerini başlatmasına izin verir."</string>
<string name="permlab_useFingerprint" msgid="3150478619915124905">"parmak izi donanımını kullanma"</string>
<string name="permdesc_useFingerprint" msgid="9165097460730684114">"Uygulamanın kimlik doğrulama için parmak izi donanımını kullanmasına izin verir."</string>
+ <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Parmak izinin tümü algılanamadı. Lütfen tekrar deneyin."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Parmak izi işlenemedi. Lütfen tekrar deneyin."</string>
+ <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Parmak izi sensörü kirli. Lütfen temizleyin ve tekrar deneyin."</string>
+ <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"Parmak hareketi çok hızlıydı. Lütfen tekrar deneyin."</string>
+ <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"Parmak hareketi çok yavaştı. Lütfen tekrar deneyin."</string>
+ <string-array name="fingerprint_acquired_vendor">
+ <item msgid="2892952818207766996">"Tedarikçi firmaya özel edinme hata iletisi 0"</item>
+ </string-array>
+ <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"İşlenemedi. Tekrar deneyin."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"Donanım yok."</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>
+ <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Parmak izi için zaman aşımı oluştu. Tekrar deneyin."</string>
+ <string-array name="fingerprint_error_vendor">
+ <item msgid="5804600450373644614">"Tedarikçi firmaya özel hata iletisi."</item>
+ </string-array>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"senk. ayarlarını okuma"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Uygulamaya bir hesaba ait senkronizasyon ayarlarını okuma izni verir. Örneğin, bu izne sahip bir uygulama Kişiler uygulamasının bir hesapla senkronize olup olmadığını belirleyebilir."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"senkronizasyonu açma/kapatma"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 78beaf5..07eb0e8 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -128,6 +128,7 @@
<string name="wfcRegErrorTitle" msgid="2301376280632110664">"Дзвінок через Wi-Fi"</string>
<string-array name="wfcOperatorErrorMessages">
</string-array>
+ <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
<string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: не переслано"</string>
<string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> після <xliff:g id="TIME_DELAY">{2}</xliff:g> сек."</string>
@@ -748,6 +749,22 @@
<string name="permdesc_manageFingerprint" msgid="178208705828055464">"Увімкнути в додатку функції для додавання й видалення шаблонів цифрових відбитків."</string>
<string name="permlab_useFingerprint" msgid="3150478619915124905">"користуватися апаратним забезпеченням для цифрових відбитків"</string>
<string name="permdesc_useFingerprint" msgid="9165097460730684114">"Дозволити додатку використовувати апаратне забезпечення для автентифікації"</string>
+ <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Відбиток розпізнано частково. Повторіть спробу."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Не вдалось обробити відбиток. Повторіть спробу."</string>
+ <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Датчик відбитків забруднився. Очистьте його та повторіть спробу."</string>
+ <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"Ви забрали палець надто швидко. Повторіть спробу."</string>
+ <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"Ви забрали палець надто повільно. Повторіть спробу."</string>
+ <string-array name="fingerprint_acquired_vendor">
+ <item msgid="2892952818207766996">"Повідомленя про помилку: 0. Не отримано відбиток постачальника"</item>
+ </string-array>
+ <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"Не вдалось обробити. Повторіть спробу."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"Апаратне забезпечення недоступне."</string>
+ <string name="fingerprint_error_no_space" msgid="1055819001126053318">"Не вдалося зберегти відбиток. Видаліть наявний відбиток."</string>
+ <string name="fingerprint_error_timeout" msgid="3927186043737732875">"Час очікування відбитка минув. Повторіть спробу."</string>
+ <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Час очікування відбитка минув. Повторіть спробу."</string>
+ <string-array name="fingerprint_error_vendor">
+ <item msgid="5804600450373644614">"Повідомлення про помилку щодо відбитка постачальника."</item>
+ </string-array>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"читати налаштування синхронізації"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Дозволяє програмі читати налаштування синхронізації для облікового запису, наприклад, визначати, чи програма Люди синхронізується з обліковим записом."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"вмикати й вимикати синхронізацію"</string>
diff --git a/core/res/res/values-ur-rPK/strings.xml b/core/res/res/values-ur-rPK/strings.xml
index 4f604aa..a5e139d 100644
--- a/core/res/res/values-ur-rPK/strings.xml
+++ b/core/res/res/values-ur-rPK/strings.xml
@@ -126,6 +126,7 @@
<string name="wfcRegErrorTitle" msgid="2301376280632110664">"Wi-Fi کالنگ"</string>
<string-array name="wfcOperatorErrorMessages">
</string-array>
+ <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
<string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g> : فارورڈ نہیں کی گئی"</string>
<string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> بعد از <xliff:g id="TIME_DELAY">{2}</xliff:g> سیکنڈ"</string>
@@ -746,6 +747,22 @@
<string name="permdesc_manageFingerprint" msgid="178208705828055464">"ایپ کو استعمال کیلئے فنگر پرنٹ کی تمثیلات شامل کرنے اور حذف کرنے کیلئے طریقوں کو کالعدم قرار دینے کی اجازت دیتا ہے۔"</string>
<string name="permlab_useFingerprint" msgid="3150478619915124905">"فنگر پرنٹ ہارڈ ویئر استعمال کریں"</string>
<string name="permdesc_useFingerprint" msgid="9165097460730684114">"ایپ کو توثیق کیلئے فنگر پرنٹ ہارڈ ویئر استعمال کرنے کی اجازت دیتا ہے"</string>
+ <string name="fingerprint_acquired_partial" msgid="735082772341716043">"جزوی فنگر پرنٹ کی شناخت ہوئی۔ براہ کرم دوبارہ کوشش کریں۔"</string>
+ <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"فنگر پرنٹ پر کارروائی نہیں کی جا سکی۔ براہ کرم دوبارہ کوشش کریں۔"</string>
+ <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"فنگر پرنٹ سینسر گندا ہے۔ براہ کرم صاف کریں اور دوبارہ کوشش کریں۔"</string>
+ <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"انگلی کو کافی تیزی سے ہٹا لیا گیا۔ براہ کرم دوبارہ کوشش کریں۔"</string>
+ <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"انگلی کو بہت آہستہ ہٹا لیا گیا۔ براہ کرم دوبارہ کوشش کریں۔"</string>
+ <string-array name="fingerprint_acquired_vendor">
+ <item msgid="2892952818207766996">"وینڈر کیلئے مخصوص حصول کی خرابی کا پیغام 0"</item>
+ </string-array>
+ <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"کارروائی کرنے سے قاصر ہے۔ دوبارہ کوشش کریں۔"</string>
+ <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"ہارڈ ویئر دستیاب نہیں ہے۔"</string>
+ <string name="fingerprint_error_no_space" msgid="1055819001126053318">"فنگر پرنٹ اسٹور نہیں کیا جا سکتا ہے۔ براہ کرم ایک موجودہ فنگر پرنٹ ہٹائیں۔"</string>
+ <string name="fingerprint_error_timeout" msgid="3927186043737732875">"فنگر پرنٹ کی میعاد ختم ہوگئی۔ دوبارہ کوشش کریں۔"</string>
+ <string name="fingerprint_error_vendor" msgid="3175724710791609491">"فنگر پرنٹ کی میعاد ختم ہوگئی۔ دوبارہ کوشش کریں۔"</string>
+ <string-array name="fingerprint_error_vendor">
+ <item msgid="5804600450373644614">"وینڈر کیلئے مخصوص خرابی کا پیغام۔"</item>
+ </string-array>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"مطابقت پذیری کی ترتیبات پڑھیں"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"ایپ کو کسی اکاؤنٹ کیلئے مطابقت پذیری کی ترتیبات پڑھنے کی اجازت دیتا ہے۔ مثلا، یہ تعین کرسکتا ہے کہ آیا People ایپ کسی اکاؤنٹ کے ساتھ مطابقت پذیر ہے۔"</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"مطابقت پذیری آن اور آف ٹوگل کریں"</string>
diff --git a/core/res/res/values-uz-rUZ/strings.xml b/core/res/res/values-uz-rUZ/strings.xml
index dc90c25..f0a6abb 100644
--- a/core/res/res/values-uz-rUZ/strings.xml
+++ b/core/res/res/values-uz-rUZ/strings.xml
@@ -126,6 +126,7 @@
<string name="wfcRegErrorTitle" msgid="2301376280632110664">"Wi-Fi qo‘ng‘iroq"</string>
<string-array name="wfcOperatorErrorMessages">
</string-array>
+ <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
<string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Yo‘naltirilmadi"</string>
<string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> <xliff:g id="TIME_DELAY">{2}</xliff:g> soniyadan so‘ng"</string>
@@ -746,6 +747,22 @@
<string name="permdesc_manageFingerprint" msgid="178208705828055464">"Ilova foydalanish uchun barmoq izi namunalarini qo‘shish va o‘chirish usullarini qo‘llashi mumkin."</string>
<string name="permlab_useFingerprint" msgid="3150478619915124905">"barmoq izi sensoridan foydalanish"</string>
<string name="permdesc_useFingerprint" msgid="9165097460730684114">"Ilova haqiqiylikni tekshirish uchun barmoq izi sensoridan foydalanishi mumkin"</string>
+ <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Barmoq izi qisman aniqlandi. Qayta urinib ko‘ring."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Barmoq izi aniqlanmadi. Qayta urinib ko‘ring."</string>
+ <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Barmoq izi sensori kirlangan. Uni tozalab, keyin qayta urinib ko‘ring."</string>
+ <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"Barmoq juda tez harakatlandi. Qayta urinib ko‘ring."</string>
+ <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"Barmoq juda sekin harakatlandi. Qayta urinib ko‘ring."</string>
+ <string-array name="fingerprint_acquired_vendor">
+ <item msgid="2892952818207766996">"Sotuvchidan barmoq izini olishda sodir bo‘ladigan xatolik xabari 0"</item>
+ </string-array>
+ <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"Barmoq izini aniqlab bo‘lmadi. Qayta urinib ko‘ring."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"Barmoq izi sensoridan foydalanib bo‘lmaydi."</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>
+ <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Barmoq izini aniqlash vaqti tugab qoldi. Qayta urinib ko‘ring."</string>
+ <string-array name="fingerprint_error_vendor">
+ <item msgid="5804600450373644614">"Xizmat bilan bog‘liq xatolik xabari."</item>
+ </string-array>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"sinx-sh sozlamalarini o‘qish"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Ilovaga hisobning sinxronlash sozlamalarini o‘qish uchun ruxsat beradi. Masalan, bu \"Odamlar\" ilovasi hisob bilan sinxronlangan yoki aksini aniqlay oladi."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"sinx.ni yoqish/o‘chirish"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 5031e85..73c2b67 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -126,6 +126,7 @@
<string name="wfcRegErrorTitle" msgid="2301376280632110664">"Gọi qua Wi-Fi"</string>
<string-array name="wfcOperatorErrorMessages">
</string-array>
+ <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
<string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Không được chuyển tiếp"</string>
<string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> sau <xliff:g id="TIME_DELAY">{2}</xliff:g> giây"</string>
@@ -746,6 +747,22 @@
<string name="permdesc_manageFingerprint" msgid="178208705828055464">"Cho phép ứng dụng gọi các phương pháp để thêm và xóa các mẫu vân tay để sử dụng."</string>
<string name="permlab_useFingerprint" msgid="3150478619915124905">"sử dụng phần cứng vân tay"</string>
<string name="permdesc_useFingerprint" msgid="9165097460730684114">"Cho phép ứng dụng sử dụng phần cứng vân tay để xác thực"</string>
+ <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Đã phát hiện được một phần vân tay. Vui lòng thử lại."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Không thể xử lý vân tay. Vui lòng thử lại."</string>
+ <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Cảm biến vân tay bị bẩn. Hãy làm sạch và thử lại."</string>
+ <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"Ngón tay đã di chuyển quá nhanh. Vui lòng thử lại."</string>
+ <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"Ngón tay đã di chuyển quá chậm. Vui lòng thử lại."</string>
+ <string-array name="fingerprint_acquired_vendor">
+ <item msgid="2892952818207766996">"Thông báo lỗi lấy vân tay cho người bán cụ thể 0"</item>
+ </string-array>
+ <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"Không thể xử lý. Hãy thử lại."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"Phần cứng không có sẵn."</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>
+ <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Đã hết thời gian chờ vân tay. Hãy thử lại."</string>
+ <string-array name="fingerprint_error_vendor">
+ <item msgid="5804600450373644614">"Thông báo lỗi cho người bán cụ thể."</item>
+ </string-array>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"đọc cài đặt đồng bộ hóa"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Cho phép ứng dụng đọc cài đặt đồng bộ hóa cho tài khoản. Ví dụ: việc này có thể xác định liệu ứng dụng Mọi người đã được đồng bộ hóa với tài khoản chưa."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"chuyển đổi bật và tắt đồng bộ hóa"</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index d8631a6a..63dd5c7 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -126,6 +126,7 @@
<string name="wfcRegErrorTitle" msgid="2301376280632110664">"WLAN 通话"</string>
<string-array name="wfcOperatorErrorMessages">
</string-array>
+ <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
<string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>:无法转接"</string>
<string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>:<xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>:<xliff:g id="TIME_DELAY">{2}</xliff:g>秒后<xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
@@ -746,6 +747,22 @@
<string name="permdesc_manageFingerprint" msgid="178208705828055464">"允许该应用调用方法来添加和删除可用的指纹模板。"</string>
<string name="permlab_useFingerprint" msgid="3150478619915124905">"使用指纹硬件"</string>
<string name="permdesc_useFingerprint" msgid="9165097460730684114">"允许该应用使用指纹硬件进行身份验证"</string>
+ <string name="fingerprint_acquired_partial" msgid="735082772341716043">"仅检测到部分指纹,请重试。"</string>
+ <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"无法处理指纹,请重试。"</string>
+ <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"指纹传感器有脏污。请擦拭干净,然后重试。"</string>
+ <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"手指移动太快,请重试。"</string>
+ <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"手指移动太慢,请重试。"</string>
+ <string-array name="fingerprint_acquired_vendor">
+ <item msgid="2892952818207766996">"针对供应商的指纹获取错误消息 0"</item>
+ </string-array>
+ <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"无法处理指纹,请重试。"</string>
+ <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"硬件无法使用。"</string>
+ <string name="fingerprint_error_no_space" msgid="1055819001126053318">"无法存储指纹。请移除一个现有的指纹。"</string>
+ <string name="fingerprint_error_timeout" msgid="3927186043737732875">"指纹录入操作超时,请重试。"</string>
+ <string name="fingerprint_error_vendor" msgid="3175724710791609491">"指纹录入操作超时,请重试。"</string>
+ <string-array name="fingerprint_error_vendor">
+ <item msgid="5804600450373644614">"针对供应商的错误消息"</item>
+ </string-array>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"读取同步设置"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"允许该应用读取某个帐户的同步设置。例如,此权限可确定“联系人”应用是否与某个帐户同步。"</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"启用和停用同步"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index 9b9e945..4956a88 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -126,6 +126,7 @@
<string name="wfcRegErrorTitle" msgid="2301376280632110664">"Wi-Fi 通話"</string>
<string-array name="wfcOperatorErrorMessages">
</string-array>
+ <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
<string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>:尚未轉接"</string>
<string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>:<xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> 於 <xliff:g id="TIME_DELAY">{2}</xliff:g> 秒後轉接"</string>
@@ -746,6 +747,22 @@
<string name="permdesc_manageFingerprint" msgid="178208705828055464">"允許應用程式調用加入和刪除指紋模板的方法以供使用。"</string>
<string name="permlab_useFingerprint" msgid="3150478619915124905">"使用指紋硬件"</string>
<string name="permdesc_useFingerprint" msgid="9165097460730684114">"允許應用程式使用指紋硬件驗證"</string>
+ <string name="fingerprint_acquired_partial" msgid="735082772341716043">"只偵測到部分指紋。請再試一次。"</string>
+ <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"無法處理指紋。請再試一次。"</string>
+ <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"指紋感應器不乾淨。請清潔後再試一次。"</string>
+ <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"手指移動太快。請再試一次。"</string>
+ <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"手指移動太慢。請再試一次。"</string>
+ <string-array name="fingerprint_acquired_vendor">
+ <item msgid="2892952818207766996">"供應商專用採集錯誤訊息 0"</item>
+ </string-array>
+ <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"無法處理。請再試一次。"</string>
+ <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"硬件無法使用。"</string>
+ <string name="fingerprint_error_no_space" msgid="1055819001126053318">"指紋無法儲存。請移除現有指紋。"</string>
+ <string name="fingerprint_error_timeout" msgid="3927186043737732875">"指紋已逾時。請再試一次。"</string>
+ <string name="fingerprint_error_vendor" msgid="3175724710791609491">"指紋已逾時。請再試一次。"</string>
+ <string-array name="fingerprint_error_vendor">
+ <item msgid="5804600450373644614">"供應商專用錯誤訊息。"</item>
+ </string-array>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"讀取同步處理設定"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"允許應用程式讀取帳戶的同步設定,例如確定「通訊錄」應用程式是否和某個帳戶保持同步。"</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"開啟和關閉同步功能"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 323ccc39..98c1cd2 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -126,6 +126,7 @@
<string name="wfcRegErrorTitle" msgid="2301376280632110664">"Wi-Fi 通話"</string>
<string-array name="wfcOperatorErrorMessages">
</string-array>
+ <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
<string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>:未轉接"</string>
<string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>:<xliff:g id="TIME_DELAY">{2}</xliff:g> 秒後 <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
@@ -746,6 +747,22 @@
<string name="permdesc_manageFingerprint" msgid="178208705828055464">"允許應用程式呼叫方法來新增及移除可用的指紋範本"</string>
<string name="permlab_useFingerprint" msgid="3150478619915124905">"使用指紋硬體"</string>
<string name="permdesc_useFingerprint" msgid="9165097460730684114">"允許應用程式使用指紋硬體進行驗證"</string>
+ <string name="fingerprint_acquired_partial" msgid="735082772341716043">"僅偵測到部分指紋,請再試一次。"</string>
+ <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"無法處理指紋,請再試一次。"</string>
+ <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"指紋感應器有髒汙。請清潔感應器,然後再試一次。"</string>
+ <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"手指移動速度過快,請再試一次。"</string>
+ <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"手指移動速度過慢,請再試一次。"</string>
+ <string-array name="fingerprint_acquired_vendor">
+ <item msgid="2892952818207766996">"供應商專用的指紋擷取錯誤訊息 0"</item>
+ </string-array>
+ <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"無法辨識指紋,請再試一次。"</string>
+ <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"硬體無法使用。"</string>
+ <string name="fingerprint_error_no_space" msgid="1055819001126053318">"無法儲存指紋,請先移除現有指紋。"</string>
+ <string name="fingerprint_error_timeout" msgid="3927186043737732875">"指紋處理作業逾時,請再試一次。"</string>
+ <string name="fingerprint_error_vendor" msgid="3175724710791609491">"指紋處理作業逾時,請再試一次。"</string>
+ <string-array name="fingerprint_error_vendor">
+ <item msgid="5804600450373644614">"供應商專用的錯誤訊息。"</item>
+ </string-array>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"讀取同步處理設定"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"允許應用程式讀取帳戶的同步處理設定,例如判斷「使用者」應用程式是否和某個帳戶進行同步處理。"</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"開啟及關閉同步功能"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index ec68b17..1833ece 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -126,6 +126,7 @@
<string name="wfcRegErrorTitle" msgid="2301376280632110664">"Ukushaya kwe-Wi-Fi"</string>
<string-array name="wfcOperatorErrorMessages">
</string-array>
+ <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
<string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Akudlulisiwe"</string>
<string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> emuva kwamasekhondi angu-<xliff:g id="TIME_DELAY">{2}</xliff:g>"</string>
@@ -746,6 +747,22 @@
<string name="permdesc_manageFingerprint" msgid="178208705828055464">"Ivumela uhlelo lokusebenza ukuthi libuyisele izindlela zokungeza nokususa izifanekiso zezigxivizo zeminwe ngokusetshenziswa."</string>
<string name="permlab_useFingerprint" msgid="3150478619915124905">"sebenzisa izingxenyekazi zekhompyutha zezigxivizo zeminwe"</string>
<string name="permdesc_useFingerprint" msgid="9165097460730684114">"Ivumela uhlelo lokusebenza ukuthi lusebenzise izingxenyekazi zekhompyutha zezigxivizo zeminwe ukuze kuqinisekiswe"</string>
+ <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Izigxivizo zeminwe ezincane zitholiwe. Sicela uzame futhi."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Ayikwazanga ukucubungula izigxivizo zeminwe. Sicela uzame futhi."</string>
+ <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Inzwa yezigxivizo zeminwe ingcolile. Sicela uyihlanze uphinde uzame futhi."</string>
+ <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"Umunwe uhanjiswe ngokushesha kakhulu. Sicela uzame futhi."</string>
+ <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"Umunwe uhanjiswe kancane kakhulu. Sicela uzame futhi."</string>
+ <string-array name="fingerprint_acquired_vendor">
+ <item msgid="2892952818207766996">"Umlayezo wephutha wokutholwa okucacisiwe komthengisi ongu-0"</item>
+ </string-array>
+ <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"Ayikwazi ukucubungula. Zama futhi."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"Izingxenyekazi zekhompuyutha 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>
+ <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Kufinyelelwe isikhathi sokuvala sezigxivizo zeminwe. Zama futhi"</string>
+ <string-array name="fingerprint_error_vendor">
+ <item msgid="5804600450373644614">"Umlayezo wephutha ocacile womthengisi."</item>
+ </string-array>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"funda izilungiselelo zokuvumelanisa"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Ivumela uhlelo lokusebenza ukufunda izilungiselelo zokuvumelanisa ze-akhawunti. Isibonelo, lokhu kungacacisa ukuthi noma ngabe uhlelo lokusebenza le-People livumelanisiwe ne-akhawunti."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"shintsha phakathi kokuvula kanye nokucisha ukuvumelanisa"</string>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 79a6bde..dcb4b9e 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -4444,11 +4444,6 @@
</declare-styleable>
<declare-styleable name="DatePicker">
- <!-- The first year (inclusive), for example "1940". {@deprecated Use minDate instead.} -->
- <attr name="startYear" format="integer" />
- <!-- The last year (inclusive), for example "2010". {@deprecated Use maxDate instead.} -->
- <attr name="endYear" format="integer" />
-
<!-- The first day of week according to {@link java.util.Calendar}. -->
<attr name="firstDayOfWeek" />
<!-- The minimal date shown by this calendar view in mm/dd/yyyy format. -->
@@ -4474,12 +4469,14 @@
<!-- The background for the selected date header. -->
<attr name="headerBackground" />
- <!-- The list year's text appearance in the list. -->
+ <!-- The list year's text appearance in the list.
+ {@deprecated Use yearListTextColor. }-->
<attr name="yearListItemTextAppearance" format="reference" />
<!-- @hide The list year's text appearance in the list when activated. -->
<attr name="yearListItemActivatedTextAppearance" format="reference" />
<!-- The text color list of the calendar. -->
<attr name="calendarTextColor" format="color" />
+
<!-- Defines the look of the widget. Prior to the L release, the only choice was
spinner. As of L, with the Material theme selected, the default layout is calendar,
but this attribute can be used to force spinner to be used instead. -->
@@ -4490,17 +4487,29 @@
<enum name="calendar" value="2" />
</attr>
- <!-- @deprecated The text appearance for the month (ex. May) in the selected date header. -->
+ <!-- The first year (inclusive), for example "1940".
+ {@deprecated Use minDate instead.} -->
+ <attr name="startYear" format="integer" />
+ <!-- The last year (inclusive), for example "2010".
+ {@deprecated Use maxDate instead.} -->
+ <attr name="endYear" format="integer" />
+ <!-- The text appearance for the month (ex. May) in the selected date header.
+ {@deprecated Use headerTextColor instead.} -->
<attr name="headerMonthTextAppearance" format="reference" />
- <!-- @deprecated The text appearance for the day of month (ex. 28) in the selected date header. -->
+ <!-- The text appearance for the day of month (ex. 28) in the selected date header.
+ {@deprecated Use headerTextColor instead.} -->
<attr name="headerDayOfMonthTextAppearance" format="reference" />
- <!-- The text appearance for the year (ex. 2014) in the selected date header. -->
+ <!-- The text appearance for the year (ex. 2014) in the selected date header.
+ {@deprecated Use headerTextColor instead.} -->
<attr name="headerYearTextAppearance" format="reference" />
- <!-- @deprecated The background color for the header's day of week. -->
+ <!-- The background color for the header's day of week.
+ {@deprecated No longer displayed.} -->
<attr name="dayOfWeekBackground" format="color" />
- <!-- @deprecated The text color for the header's day of week. -->
+ <!-- The text color for the header's day of week.
+ {@deprecated No longer displayed.} -->
<attr name="dayOfWeekTextAppearance" format="reference" />
- <!-- @deprecated The list year's selected circle color in the list. -->
+ <!-- The list year's selected circle color in the list.
+ {@deprecated No longer displayed.} -->
<attr name="yearListSelectorColor" format="color" />
</declare-styleable>
@@ -4787,24 +4796,27 @@
<attr name="legacyLayout" format="reference" />
<!-- @hide The layout of the time picker. -->
<attr name="internalLayout" />
- <!-- The text appearance for the AM/PM header. -->
- <attr name="headerAmPmTextAppearance" format="reference" />
- <!-- The text appearance for the time header. -->
- <attr name="headerTimeTextAppearance" format="reference" />
+
+ <!-- The text color for the selected time header text, ex. "12" or
+ "PM". This should be a color state list where the activated state
+ will be used when the minute picker or hour picker is active.-->
+ <attr name="headerTextColor" />
<!-- The background for the header containing the currently selected time. -->
<attr name="headerBackground" />
- <!-- The color for the hours/minutes numbers. -->
+
+ <!-- The color for the hours/minutes numbers. This should be a color
+ state list where the activated state will be used when the number
+ is active.-->
<attr name="numbersTextColor" format="color" />
- <!-- The color for the inner hours numbers used in 24-hour mode. -->
+ <!-- The color for the inner hours numbers used in 24-hour mode. This
+ should be a color state list where the activated state will be
+ used when the number is active.-->
<attr name="numbersInnerTextColor" format="color" />
<!-- The background color for the hours/minutes numbers. -->
<attr name="numbersBackgroundColor" format="color" />
- <!-- The color for the AM/PM selectors. -->
- <attr name="amPmTextColor" format="color" />
- <!-- The background color state list for the AM/PM selectors. -->
- <attr name="amPmBackgroundColor" format="color" />
<!-- The color for the hours/minutes selector. -->
<attr name="numbersSelectorColor" format="color" />
+
<!-- Defines the look of the widget. Prior to the L release, the only choice was
spinner. As of L, with the Material theme selected, the default layout is clock,
but this attribute can be used to force spinner to be used instead. -->
@@ -4814,6 +4826,19 @@
<!-- Time picker with clock face to select the time. -->
<enum name="clock" value="2" />
</attr>
+
+ <!-- The text appearance for the AM/PM header.
+ @deprecated Use headerTextColor instead. -->
+ <attr name="headerAmPmTextAppearance" format="reference" />
+ <!-- The text appearance for the time header.
+ @deprecated Use headerTextColor instead. -->
+ <attr name="headerTimeTextAppearance" format="reference" />
+ <!-- The color for the AM/PM selectors.
+ {@deprecated Use headerTextColor instead.}-->
+ <attr name="amPmTextColor" format="color" />
+ <!-- The background color state list for the AM/PM selectors.
+ {@deprecated Use headerBackground instead.}-->
+ <attr name="amPmBackgroundColor" format="color" />
</declare-styleable>
<!-- ========================= -->
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index aefb67c..b0b4e3a 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -389,13 +389,12 @@
with the same {@link android.R.attr#taskAffinity} as it has. -->
<attr name="allowTaskReparenting" format="boolean" />
- <!-- Declare that this application may use cleartext traffic (e.g., HTTP rather than HTTPS;
- WebSockets rather than WebSockets Secure; XMPP, IMAP, STMP without STARTTLS or TLS).
- Defaults to true. If set to false {@code false}, the app declares that it does not
- intend to use cleartext network traffic, in which case platform components (e.g.,
- HTTP stacks, {@code WebView}, {@code MediaPlayer}) will refuse app's requests to use
- cleartext traffic. Third-party libraries are encouraged to honor this flag as well.
- @hide -->
+ <!-- Declare that this application may use cleartext traffic, such as HTTP rather than HTTPS;
+ WebSockets rather than WebSockets Secure; XMPP, IMAP, STMP without STARTTLS or TLS.
+ Defaults to true. If set to false {@code false}, the application declares that it does not
+ intend to use cleartext network traffic, in which case platform components (e.g. HTTP
+ stacks, {@code WebView}, {@code MediaPlayer}) will refuse applications's requests to use
+ cleartext traffic. Third-party libraries are encouraged to honor this flag as well. -->
<attr name="usesCleartextTraffic" format="boolean" />
<!-- Declare that code from this application will need to be loaded into other
@@ -1043,6 +1042,18 @@
libraries in the apk must be stored and page-aligned. -->
<attr name="extractNativeLibs" format="boolean"/>
+ <!-- Specify whether an activity intent filter will need to be verified thru its set
+ of data URIs. This will only be used when the Intent's action is set to
+ {@link android.content.Intent#ACTION_VIEW Intent.ACTION_VIEW} and the Intent's category is
+ set to {@link android.content.Intent#CATEGORY_BROWSABLE Intent.CATEGORY_BROWSABLE} and the
+ intern filter data scheme is set to "http" or "https". When set to true, the intent filter
+ will need to use its data tag for getting the URIs to verify with.
+
+ For each URI, an HTTPS network request will be done to <code>/.well-known/associations.json</code>
+ host to verify that the web site is okay with the app intercepting the URI.
+ -->
+ <attr name="autoVerify" format="boolean" />
+
<!-- The <code>manifest</code> tag is the root of an
<code>AndroidManifest.xml</code> file,
describing the contents of an Android package (.apk) file. One
@@ -1164,13 +1175,13 @@
"com.google". -->
<attr name="requiredAccountType" format="string"/>
<attr name="isGame" />
- <!-- Declare that this application may use cleartext traffic (e.g., HTTP rather than HTTPS;
- WebSockets rather than WebSockets Secure; XMPP, IMAP, STMP without STARTTLS or TLS).
- Defaults to true. If set to false {@code false}, the app declares that it does not
- intend to use cleartext network traffic, in which case platform components (e.g.,
- HTTP stacks, {@code WebView}, {@code MediaPlayer}) will refuse app's requests to use
- cleartext traffic. Third-party libraries are encouraged to honor this flag as well.
- @hide -->
+ <!-- Declare that this application may use cleartext traffic, such as HTTP rather than
+ HTTPS; WebSockets rather than WebSockets Secure; XMPP, IMAP, STMP without STARTTLS or
+ TLS). Defaults to true. If set to false {@code false}, the application declares that it
+ does not intend to use cleartext network traffic, in which case platform components
+ (e.g. HTTP stacks, {@code WebView}, {@code MediaPlayer}) will refuse applications's
+ requests to use cleartext traffic. Third-party libraries are encouraged to honor this
+ flag as well. -->
<attr name="usesCleartextTraffic" />
<attr name="multiArch" />
<attr name="extractNativeLibs" />
@@ -1841,6 +1852,7 @@
<attr name="banner" />
<attr name="logo" />
<attr name="priority" />
+ <attr name="autoVerify" />
</declare-styleable>
<!-- Attributes that can be supplied in an AndroidManifest.xml
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 6c6d2cc..7d08e7f 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -364,26 +364,32 @@
<dimen name="resolver_max_width">480dp</dimen>
- <!-- Size of the offset applied to the position of the circular mask. This is only
- used on circular displays. In the case where there is no "chin", this will default
- to 0 -->
- <dimen name="circular_display_mask_offset">0px</dimen>
+ <!-- Size of the offset applied to the position of the circular mask. This
+ is only used on circular displays. In the case where there is no
+ "chin", this will default to 0 -->
+ <dimen name="circular_display_mask_offset">0px</dimen>
+ <!-- Amount to reduce the size of the circular mask by (to compensate for
+ aliasing effects). This is only used on circular displays. -->
+ <dimen name="circular_display_mask_thickness">1px</dimen>
- <!-- Amount to reduce the size of the circular mask by (to compensate for aliasing
- effects). This is only used on circular displays. -->
- <dimen name="circular_display_mask_thickness">1px</dimen>
+ <dimen name="lock_pattern_dot_line_width">3dp</dimen>
+ <dimen name="lock_pattern_dot_size">12dp</dimen>
+ <dimen name="lock_pattern_dot_size_activated">28dp</dimen>
- <dimen name="lock_pattern_dot_line_width">3dp</dimen>
- <dimen name="lock_pattern_dot_size">12dp</dimen>
- <dimen name="lock_pattern_dot_size_activated">28dp</dimen>
+ <dimen name="text_handle_min_size">40dp</dimen>
- <dimen name="text_handle_min_size">40dp</dimen>
+ <!-- Lighting and shadow properties -->
+ <dimen name="light_y">-200dp</dimen>
+ <dimen name="light_z">800dp</dimen>
+ <dimen name="light_radius">600dp</dimen>
+ <item type="dimen" format="float" name="ambient_shadow_alpha">0.075</item>
+ <item type="dimen" format="float" name="spot_shadow_alpha">0.15</item>
- <!-- Lighting and shadow properties -->
- <dimen name="light_y">-200dp</dimen>
- <dimen name="light_z">800dp</dimen>
- <dimen name="light_radius">600dp</dimen>
- <item type="dimen" format="float" name="ambient_shadow_alpha">0.075</item>
- <item type="dimen" format="float" name="spot_shadow_alpha">0.15</item>
-
+ <!-- Floating toolbar dimensions -->
+ <dimen name="floating_toolbar_height">48dp</dimen>
+ <dimen name="floating_toolbar_menu_button_side_padding">8dp</dimen>
+ <dimen name="floating_toolbar_text_size">14sp</dimen>
+ <dimen name="floating_toolbar_menu_button_minimum_width">48dp</dimen>
+ <dimen name="floating_toolbar_default_width">250dp</dimen>
+ <dimen name="floating_toolbar_minimum_overflow_height">192dp</dimen>
</resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 5e4b039..5c7daf2 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2652,4 +2652,9 @@
<public type="attr" name="colorBackgroundFloating" />
<public type="attr" name="extractNativeLibs" />
+ <public type="attr" name="usesCleartextTraffic" />
+
+ <!--IntentFilter auto verification -->
+ <public type="attr" name="autoVerify" />
+
</resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 88225bd..702510a 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -3367,6 +3367,22 @@
<string name="permdesc_bindPackageVerifier">Allows the holder to make requests of
package verifiers. Should never be needed for normal apps.</string>
+ <!-- Title of an application permission which allows the application to verify whether
+ a different intent filter is able to be verified by some internal logic. [CHAR LIMIT=40] -->
+ <string name="permlab_intentFilterVerificationAgent">verify intent filter</string>
+ <!-- Description of an application permission which allows the application to verify whether
+ a different intent filter is able to be verified by some internal heuristic. [CHAR LIMIT=NONE] -->
+ <string name="permdesc_intentFilterVerificationAgent">Allows the app to check if an intent filter
+ is verified or not.</string>
+
+ <!-- Title of an application permission which allows the application to verify whether
+ a different intent filter is able to be verified by some internal logic. [CHAR LIMIT=40] -->
+ <string name="permlab_bindIntentFilterVerifier">bind to an intent filter verifier</string>
+ <!-- Description of an application permission which allows the application to verify whether
+ a different intent filter is able to be verified by some internal logic. [CHAR LIMIT=NONE] -->
+ <string name="permdesc_bindIntentFilterVerifier">Allows the holder to make requests of
+ intent filter verifiers. Should never be needed for normal apps.</string>
+
<!-- Title of an application permission which allows the application to access serial ports via the SerialManager. [CHAR LIMIT=40] -->
<string name="permlab_serialPort">access serial ports</string>
<!-- Description of an application permission which allows the application access serial ports via the SerialManager. [CHAR LIMIT=NONE] -->
@@ -4415,6 +4431,10 @@
<string name="date_picker_increment_year_button">Increase year</string>
<!-- Description of the button to decrease the DatePicker's year value. [CHAR LIMIT=NONE] -->
<string name="date_picker_decrement_year_button">Decrease year</string>
+ <!-- Description of the button to move to the previous month. [CHAR LIMIT=NONE] -->
+ <string name="date_picker_prev_month_button">Previous month</string>
+ <!-- Description of the button to move to the next month. [CHAR LIMIT=NONE] -->
+ <string name="date_picker_next_month_button">Next month</string>
<!-- KeyboardView - accessibility support -->
<!-- Description of the Alt button in a KeyboardView. [CHAR LIMIT=NONE] -->
@@ -5078,8 +5098,6 @@
<string name="select_day">Select month and day</string>
<!-- Accessibility announcement for the year picker [CHAR LIMIT=NONE] -->
<string name="select_year">Select year</string>
- <!-- Accessibility description for the item that is currently selected. -->
- <string name="item_is_selected"><xliff:g id="item" example="2013">%1$s</xliff:g> selected</string>
<!-- Accessibility announcement when a number that had been typed in is deleted [CHAR_LIMIT=NONE] -->
<string name="deleted_key"><xliff:g id="key" example="4">%1$s</xliff:g> deleted</string>
diff --git a/core/res/res/values/styles_device_defaults.xml b/core/res/res/values/styles_device_defaults.xml
index 223578d..23ac221 100644
--- a/core/res/res/values/styles_device_defaults.xml
+++ b/core/res/res/values/styles_device_defaults.xml
@@ -240,10 +240,6 @@
<!-- @deprecated Action bars are now themed using the inheritable android:theme attribute. -->
<style name="TextAppearance.DeviceDefault.Widget.ActionMode.Subtitle.Inverse" parent="TextAppearance.Material.Widget.ActionMode.Subtitle.Inverse"/>
<style name="TextAppearance.DeviceDefault.Widget.ActionBar.Menu" parent="TextAppearance.Material.Widget.ActionBar.Menu"/>
- <style name="TextAppearance.DeviceDefault.DatePicker.DayOfWeekLabel" parent="TextAppearance.Material.DatePicker.DayOfWeekLabel"/>
- <style name="TextAppearance.DeviceDefault.DatePicker.MonthLabel" parent="TextAppearance.Material.DatePicker.MonthLabel"/>
- <style name="TextAppearance.DeviceDefault.DatePicker.DayOfMonthLabel" parent="TextAppearance.Material.DatePicker.DayOfMonthLabel"/>
- <style name="TextAppearance.DeviceDefault.DatePicker.YearLabel" parent="TextAppearance.Material.DatePicker.YearLabel"/>
<!-- Preference Styles -->
<style name="Preference.DeviceDefault" parent="Preference.Material"/>
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index f1f7462..9cf7884 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -381,40 +381,22 @@
<style name="TextAppearance.Material.TimePicker.TimeLabel" parent="TextAppearance.Material">
<item name="textSize">@dimen/timepicker_time_label_size</item>
- <item name="textColor">@color/time_picker_header_text_material</item>
+ <item name="textColor">@color/primary_text_secondary_when_activated_material_inverse</item>
</style>
<style name="TextAppearance.Material.TimePicker.AmPmLabel" parent="TextAppearance.Material.Button">
<item name="textSize">@dimen/timepicker_ampm_label_size</item>
- <item name="textColor">@color/time_picker_header_text_material</item>
- </style>
-
- <style name="TextAppearance.Material.DatePicker.DayOfWeekLabel" parent="TextAppearance.Material">
- <item name="includeFontPadding">false</item>
- <item name="textColor">?attr/textColorPrimaryInverse</item>
- <item name="textSize">@dimen/datepicker_header_text_size</item>
- </style>
-
- <style name="TextAppearance.Material.DatePicker.MonthLabel" parent="TextAppearance.Material">
- <item name="includeFontPadding">false</item>
- <item name="textColor">@color/date_picker_header_text_material</item>
- <item name="textSize">@dimen/datepicker_selected_date_month_size</item>
- </style>
-
- <style name="TextAppearance.Material.DatePicker.DayOfMonthLabel" parent="TextAppearance.Material">
- <item name="includeFontPadding">false</item>
- <item name="textColor">@color/date_picker_header_text_material</item>
- <item name="textSize">@dimen/datepicker_selected_date_day_size</item>
+ <item name="textColor">@color/primary_text_secondary_when_activated_material_inverse</item>
</style>
<style name="TextAppearance.Material.DatePicker.YearLabel" parent="TextAppearance.Material">
- <item name="textColor">@color/date_picker_header_text_material</item>
+ <item name="textColor">@color/primary_text_secondary_when_activated_material_inverse</item>
<item name="textSize">@dimen/date_picker_year_label_size</item>
<item name="fontFamily">sans-serif-medium</item>
</style>
<style name="TextAppearance.Material.DatePicker.DateLabel" parent="TextAppearance.Material">
- <item name="textColor">@color/date_picker_header_text_material</item>
+ <item name="textColor">@color/primary_text_secondary_when_activated_material_inverse</item>
<item name="textSize">@dimen/date_picker_date_label_size</item>
<item name="fontFamily">sans-serif-medium</item>
</style>
@@ -660,8 +642,7 @@
<item name="legacyLayout">@layout/time_picker_legacy_material</item>
<!-- Attributes for new-style TimePicker. -->
<item name="internalLayout">@layout/time_picker_material</item>
- <item name="headerTimeTextAppearance">@style/TextAppearance.Material.TimePicker.TimeLabel</item>
- <item name="headerAmPmTextAppearance">@style/TextAppearance.Material.TimePicker.AmPmLabel</item>
+ <item name="headerTextColor">@color/primary_text_secondary_when_activated_material</item>
<item name="headerBackground">#ff555555</item>
<item name="numbersTextColor">?attr/textColorPrimaryActivated</item>
<item name="numbersInnerTextColor">?attr/textColorSecondaryActivated</item>
@@ -676,8 +657,7 @@
<item name="calendarViewShown">true</item>
<!-- Attributes for new-style DatePicker. -->
<item name="internalLayout">@layout/date_picker_material</item>
- <item name="yearListItemTextAppearance">@style/TextAppearance.Material.DatePicker.List.YearLabel</item>
- <item name="yearListItemActivatedTextAppearance">@style/TextAppearance.Material.DatePicker.List.YearLabel.Activated</item>
+ <item name="headerTextColor">@color/primary_text_secondary_when_activated_material</item>
<item name="headerBackground">#ff555555</item>
</style>
@@ -1033,11 +1013,13 @@
<style name="Widget.Material.Light.NumberPicker" parent="Widget.Material.NumberPicker"/>
<style name="Widget.Material.Light.TimePicker" parent="Widget.Material.TimePicker">
+ <item name="headerTextColor">@color/primary_text_secondary_when_activated_material_inverse</item>
<item name="headerBackground">?attr/colorAccent</item>
<item name="numbersBackgroundColor">#ffeeeeee</item>
</style>
<style name="Widget.Material.Light.DatePicker" parent="Widget.Material.DatePicker">
+ <item name="headerTextColor">@color/primary_text_secondary_when_activated_material_inverse</item>
<item name="headerBackground">?attr/colorAccent</item>
</style>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index fcf9ff4..19352c9 100755
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2040,7 +2040,6 @@
<java-symbol type="integer" name="config_downtime_condition_lookahead_threshold_hrs" />
<java-symbol type="string" name="muted_by" />
- <java-symbol type="string" name="item_is_selected" />
<java-symbol type="string" name="select_day" />
<java-symbol type="string" name="select_year" />
@@ -2190,8 +2189,32 @@
<java-symbol type="style" name="TextAppearance.Material.Widget.Calendar.Month" />
<java-symbol type="style" name="TextAppearance.Material.Widget.Calendar.DayOfWeek" />
<java-symbol type="style" name="TextAppearance.Material.Widget.Calendar.Day" />
+ <java-symbol type="style" name="TextAppearance.Material.DatePicker.List.YearLabel" />
+ <java-symbol type="style" name="TextAppearance.Material.DatePicker.List.YearLabel.Activated" />
<java-symbol type="dimen" name="day_picker_padding_top"/>
<java-symbol type="dimen" name="date_picker_day_of_week_height"/>
+ <java-symbol type="string" name="storage_internal" />
+ <java-symbol type="string" name="storage_sd_card" />
+ <java-symbol type="string" name="storage_usb" />
+
<java-symbol type="id" name="accessibility_action_show_on_screen" />
+
+ <!-- Floating toolbar -->
+ <java-symbol type="layout" name="floating_popup_container" />
+ <java-symbol type="layout" name="floating_popup_menu_button" />
+ <java-symbol type="layout" name="floating_popup_open_overflow_button" />
+ <java-symbol type="dimen" name="floating_toolbar_height" />
+ <java-symbol type="dimen" name="floating_toolbar_menu_button_side_padding" />
+ <java-symbol type="dimen" name="floating_toolbar_text_size" />
+ <java-symbol type="dimen" name="floating_toolbar_menu_button_minimum_width" />
+ <java-symbol type="dimen" name="floating_toolbar_default_width" />
+ <java-symbol type="dimen" name="floating_toolbar_minimum_overflow_height" />
+
+ <java-symbol type="drawable" name="ic_chevron_left" />
+ <java-symbol type="drawable" name="ic_chevron_right" />
+ <java-symbol type="string" name="date_picker_prev_month_button" />
+ <java-symbol type="string" name="date_picker_next_month_button" />
+ <java-symbol type="layout" name="date_picker_month_item_material" />
+ <java-symbol type="id" name="month_view" />
</resources>
diff --git a/core/tests/benchmarks/src/android/net/NetworkStatsBenchmark.java b/core/tests/benchmarks/src/android/net/NetworkStatsBenchmark.java
new file mode 100644
index 0000000..1a50432
--- /dev/null
+++ b/core/tests/benchmarks/src/android/net/NetworkStatsBenchmark.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.net;
+
+import com.google.caliper.Param;
+import com.google.caliper.SimpleBenchmark;
+
+public class NetworkStatsBenchmark extends SimpleBenchmark {
+ private static final String UNDERLYING_IFACE = "wlan0";
+ private static final String TUN_IFACE = "tun0";
+ private static final int TUN_UID = 999999999;
+
+ @Param({"100", "1000"})
+ private int mSize;
+ private NetworkStats mNetworkStats;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ mNetworkStats = new NetworkStats(0, mSize + 2);
+ int uid = 0;
+ NetworkStats.Entry recycle = new NetworkStats.Entry();
+ for (int i = 0; i < mSize; i++) {
+ recycle.iface = (i < mSize / 2) ? TUN_IFACE : UNDERLYING_IFACE;
+ recycle.uid = uid;
+ recycle.set = i % 2;
+ recycle.tag = NetworkStats.TAG_NONE;
+ recycle.rxBytes = 60000;
+ recycle.rxPackets = 60;
+ recycle.txBytes = 150000;
+ recycle.txPackets = 1500;
+ recycle.operations = 0;
+ mNetworkStats.addValues(recycle);
+ if (recycle.set == 1) {
+ uid++;
+ }
+ }
+ recycle.iface = UNDERLYING_IFACE;
+ recycle.uid = TUN_UID;
+ recycle.set = NetworkStats.SET_FOREGROUND;
+ recycle.tag = NetworkStats.TAG_NONE;
+ recycle.rxBytes = 90000 * mSize;
+ recycle.rxPackets = 40 * mSize;
+ recycle.txBytes = 180000 * mSize;
+ recycle.txPackets = 1200 * mSize;
+ recycle.operations = 0;
+ mNetworkStats.addValues(recycle);
+ }
+
+ public void timeMigrateTun(int reps) {
+ for (int i = 0; i < reps; i++) {
+ NetworkStats stats = mNetworkStats.clone();
+ stats.migrateTun(TUN_UID, TUN_IFACE, UNDERLYING_IFACE);
+ }
+ }
+
+ /**
+ * Since timeMigrateTun() includes a clone() call on the NetworkStats object,
+ * we need to measure the cost of the clone() call itself in order to get more
+ * accurate measurement on the migrateTun() method.
+ */
+ public void timeClone(int reps) {
+ for (int i = 0; i < reps; i++) {
+ NetworkStats stats = mNetworkStats.clone();
+ }
+ }
+}
diff --git a/core/tests/coretests/src/android/net/NetworkStatsTest.java b/core/tests/coretests/src/android/net/NetworkStatsTest.java
index fd922a2..a470de1 100644
--- a/core/tests/coretests/src/android/net/NetworkStatsTest.java
+++ b/core/tests/coretests/src/android/net/NetworkStatsTest.java
@@ -18,6 +18,8 @@
import static android.net.NetworkStats.SET_DEFAULT;
import static android.net.NetworkStats.SET_FOREGROUND;
+import static android.net.NetworkStats.SET_DBG_VPN_IN;
+import static android.net.NetworkStats.SET_DBG_VPN_OUT;
import static android.net.NetworkStats.SET_ALL;
import static android.net.NetworkStats.IFACE_ALL;
import static android.net.NetworkStats.TAG_NONE;
@@ -346,7 +348,7 @@
.addValues(underlyingIface, tunUid, SET_FOREGROUND, TAG_NONE, 0L, 0L, 0L, 0L, 0L);
assertTrue(delta.migrateTun(tunUid, tunIface, underlyingIface));
- assertEquals(17, delta.size());
+ assertEquals(21, delta.size());
// tunIface and TEST_IFACE entries are not changed.
assertValues(delta, 0, tunIface, 10100, SET_DEFAULT, TAG_NONE,
@@ -377,14 +379,33 @@
0L, 0L, 0L, 0L, 0L);
// New entries are added for new application's underlying Iface traffic
- assertValues(delta, 13, underlyingIface, 10120, SET_DEFAULT, TAG_NONE,
+ assertContains(delta, underlyingIface, 10120, SET_DEFAULT, TAG_NONE,
72667L, 197L, 41872l, 219L, 0L);
- assertValues(delta, 14, underlyingIface, 10120, SET_FOREGROUND, TAG_NONE,
+ assertContains(delta, underlyingIface, 10120, SET_FOREGROUND, TAG_NONE,
9297L, 17L, 3936, 19L, 0L);
- assertValues(delta, 15, underlyingIface, 10120, SET_DEFAULT, testTag1,
+ assertContains(delta, underlyingIface, 10120, SET_DEFAULT, testTag1,
21691L, 41L, 13179L, 46L, 0L);
- assertValues(delta, 16, underlyingIface, 10120, SET_FOREGROUND, testTag1,
+ assertContains(delta, underlyingIface, 10120, SET_FOREGROUND, testTag1,
1281L, 2L, 634L, 1L, 0L);
+
+ // New entries are added for debug purpose
+ assertContains(delta, underlyingIface, 10100, SET_DBG_VPN_IN, TAG_NONE,
+ 39605L, 46L, 11690, 49, 0);
+ assertContains(delta, underlyingIface, 10120, SET_DBG_VPN_IN, TAG_NONE,
+ 81964, 214, 45808, 238, 0);
+ assertContains(delta, underlyingIface, tunUid, SET_DBG_VPN_IN, TAG_NONE,
+ 4983, 10, 1717, 10, 0);
+ assertContains(delta, underlyingIface, tunUid, SET_DBG_VPN_OUT, TAG_NONE,
+ 126552, 270, 59215, 297, 0);
+
+ }
+
+ private static void assertContains(NetworkStats stats, String iface, int uid, int set,
+ int tag, long rxBytes, long rxPackets, long txBytes, long txPackets, long operations) {
+ int index = stats.findIndex(iface, uid, set, tag);
+ assertTrue(index != -1);
+ assertValues(stats, index, iface, uid, set, tag,
+ rxBytes, rxPackets, txBytes, txPackets, operations);
}
private static void assertValues(NetworkStats stats, int index, String iface, int uid, int set,
diff --git a/data/fonts/Android.mk b/data/fonts/Android.mk
index 38321a3..a0b26f3 100644
--- a/data/fonts/Android.mk
+++ b/data/fonts/Android.mk
@@ -49,6 +49,8 @@
################################
# Do not include Motoya on space-constrained devices
ifneq ($(SMALLER_FONT_FOOTPRINT),true)
+# Do not include Motoya if we are including full NotoSans
+ifneq ($(FONT_NOTOSANS_FULL),true)
include $(CLEAR_VARS)
LOCAL_MODULE := MTLmr3m.ttf
@@ -59,6 +61,7 @@
include $(BUILD_PREBUILT)
extra_font_files += MTLmr3m.ttf
+endif # !FONT_NOTOSANS_FULL
endif # !SMALLER_FONT_FOOTPRINT
################################
diff --git a/docs/html/tools/revisions/build-tools.jd b/docs/html/tools/revisions/build-tools.jd
index b08fbcf..e8706c1 100644
--- a/docs/html/tools/revisions/build-tools.jd
+++ b/docs/html/tools/revisions/build-tools.jd
@@ -78,6 +78,19 @@
<div class="toggle-content opened">
<p><a href="#" onclick="return toggleContent(this)">
<img src="{@docRoot}assets/images/triangle-opened.png" class="toggle-content-img"
+ alt=""/>Build Tools, Revision 22.0.1</a> <em>(March 2015)</em>
+ </p>
+ <div class="toggle-content-toggleme">
+ <p>Fixed compatibility issues with
+ <a href="{@docRoot}guide/topics/renderscript/compute.html">RenderScript</a> kernels on
+ Android 4.4 (API level 19) to Android 4.1 (API level 16) devices.</p>
+ </div>
+</div>
+
+
+<div class="toggle-content closed">
+ <p><a href="#" onclick="return toggleContent(this)">
+ <img src="{@docRoot}assets/images/triangle-closed.png" class="toggle-content-img"
alt=""/>Build Tools, Revision 22.0.0</a> <em>(March 2015)</em>
</p>
<div class="toggle-content-toggleme">
diff --git a/graphics/java/android/graphics/ImageFormat.java b/graphics/java/android/graphics/ImageFormat.java
index 49c4247..c63c8ba 100644
--- a/graphics/java/android/graphics/ImageFormat.java
+++ b/graphics/java/android/graphics/ImageFormat.java
@@ -356,6 +356,108 @@
public static final int RAW10 = 0x25;
/**
+ * <p>
+ * Android 12-bit raw format
+ * </p>
+ * <p>
+ * This is a single-plane, 12-bit per pixel, densely packed (in each row),
+ * unprocessed format, usually representing raw Bayer-pattern images coming
+ * from an image sensor.
+ * </p>
+ * <p>
+ * In an image buffer with this format, starting from the first pixel of each
+ * row, each two consecutive pixels are packed into 3 bytes (24 bits). The first
+ * and second byte contains the top 8 bits of first and second pixel. The third
+ * byte contains the 4 least significant bits of the two pixels, the exact layout
+ * data for each two consecutive pixels is illustrated below (Pi[j] stands for
+ * the jth bit of the ith pixel):
+ * </p>
+ * <table>
+ * <thead>
+ * <tr>
+ * <th align="center"></th>
+ * <th align="center">bit 7</th>
+ * <th align="center">bit 6</th>
+ * <th align="center">bit 5</th>
+ * <th align="center">bit 4</th>
+ * <th align="center">bit 3</th>
+ * <th align="center">bit 2</th>
+ * <th align="center">bit 1</th>
+ * <th align="center">bit 0</th>
+ * </tr>
+ * </thead> <tbody>
+ * <tr>
+ * <td align="center">Byte 0:</td>
+ * <td align="center">P0[11]</td>
+ * <td align="center">P0[10]</td>
+ * <td align="center">P0[ 9]</td>
+ * <td align="center">P0[ 8]</td>
+ * <td align="center">P0[ 7]</td>
+ * <td align="center">P0[ 6]</td>
+ * <td align="center">P0[ 5]</td>
+ * <td align="center">P0[ 4]</td>
+ * </tr>
+ * <tr>
+ * <td align="center">Byte 1:</td>
+ * <td align="center">P1[11]</td>
+ * <td align="center">P1[10]</td>
+ * <td align="center">P1[ 9]</td>
+ * <td align="center">P1[ 8]</td>
+ * <td align="center">P1[ 7]</td>
+ * <td align="center">P1[ 6]</td>
+ * <td align="center">P1[ 5]</td>
+ * <td align="center">P1[ 4]</td>
+ * </tr>
+ * <tr>
+ * <td align="center">Byte 2:</td>
+ * <td align="center">P1[ 3]</td>
+ * <td align="center">P1[ 2]</td>
+ * <td align="center">P1[ 1]</td>
+ * <td align="center">P1[ 0]</td>
+ * <td align="center">P0[ 3]</td>
+ * <td align="center">P0[ 2]</td>
+ * <td align="center">P0[ 1]</td>
+ * <td align="center">P0[ 0]</td>
+ * </tr>
+ * </tbody>
+ * </table>
+ * <p>
+ * This format assumes
+ * <ul>
+ * <li>a width multiple of 4 pixels</li>
+ * <li>an even height</li>
+ * </ul>
+ * </p>
+ *
+ * <pre>size = row stride * height</pre> where the row stride is in <em>bytes</em>,
+ * not pixels.
+ *
+ * <p>
+ * Since this is a densely packed format, the pixel stride is always 0. The
+ * application must use the pixel data layout defined in above table to
+ * access each row data. When row stride is equal to {@code width * (12 / 8)}, there
+ * will be no padding bytes at the end of each row, the entire image data is
+ * densely packed. When stride is larger than {@code width * (12 / 8)}, padding
+ * bytes will be present at the end of each row.
+ * </p>
+ * <p>
+ * For example, the {@link android.media.Image} object can provide data in
+ * this format from a {@link android.hardware.camera2.CameraDevice} (if
+ * supported) through a {@link android.media.ImageReader} object. The
+ * {@link android.media.Image#getPlanes() Image#getPlanes()} will return a
+ * single plane containing the pixel data. The pixel stride is always 0 in
+ * {@link android.media.Image.Plane#getPixelStride()}, and the
+ * {@link android.media.Image.Plane#getRowStride()} describes the vertical
+ * neighboring pixel distance (in bytes) between adjacent rows.
+ * </p>
+ *
+ * @see android.media.Image
+ * @see android.media.ImageReader
+ * @see android.hardware.camera2.CameraDevice
+ */
+ public static final int RAW12 = 0x26;
+
+ /**
* Android dense depth image format.
*
* Each pixel is 16 bits, representing a depth ranging measurement from
@@ -388,6 +490,33 @@
public static final int DEPTH_POINT_CLOUD = 0x101;
/**
+ * Android private opaque image format.
+ * <p>
+ * The choices of the actual format and pixel data layout are entirely up to
+ * the device-specific and framework internal implementations, and may vary
+ * depending on use cases even for the same device. The buffers of this
+ * format can be produced by components like
+ * {@link android.media.ImageWriter ImageWriter} , and interpreted correctly
+ * by consumers like {@link android.hardware.camera2.CameraDevice
+ * CameraDevice} based on the device/framework private information. However,
+ * these buffers are not directly accessible to the application.
+ * </p>
+ * <p>
+ * When an {@link android.media.Image Image} of this format is obtained from
+ * an {@link android.media.ImageReader ImageReader} or
+ * {@link android.media.ImageWriter ImageWriter}, the
+ * {@link android.media.Image#getPlanes() getPlanes()} method will return an
+ * empty {@link android.media.Image.Plane Plane} array.
+ * </p>
+ * <p>
+ * If a buffer of this format is to be used as an OpenGL ES texture, the
+ * framework will assume that sampling the texture will always return an
+ * alpha value of 1.0 (i.e. the buffer contains only opaque pixel values).
+ * </p>
+ */
+ public static final int PRIVATE = 0x22;
+
+ /**
* Use this function to retrieve the number of bits per pixel of an
* ImageFormat.
*
@@ -418,6 +547,8 @@
return 16;
case RAW10:
return 10;
+ case RAW12:
+ return 12;
}
return -1;
}
@@ -445,8 +576,10 @@
case YUV_420_888:
case RAW_SENSOR:
case RAW10:
+ case RAW12:
case DEPTH16:
case DEPTH_POINT_CLOUD:
+ case PRIVATE:
return true;
}
diff --git a/graphics/java/android/graphics/drawable/BitmapDrawable.java b/graphics/java/android/graphics/drawable/BitmapDrawable.java
index 68ffed6..dc10a81 100644
--- a/graphics/java/android/graphics/drawable/BitmapDrawable.java
+++ b/graphics/java/android/graphics/drawable/BitmapDrawable.java
@@ -721,7 +721,7 @@
final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.BitmapDrawable);
updateStateFromTypedArray(a);
- verifyState(a);
+ verifyRequiredAttributes(a);
a.recycle();
// Update local properties.
@@ -733,11 +733,13 @@
*
* @throws XmlPullParserException if any required attributes are missing
*/
- private void verifyState(TypedArray a) throws XmlPullParserException {
+ private void verifyRequiredAttributes(TypedArray a) throws XmlPullParserException {
+ // If we're not waiting on a theme, verify required attributes.
final BitmapState state = mBitmapState;
- if (state.mBitmap == null) {
+ if (state.mBitmap == null && (state.mThemeAttrs == null
+ || state.mThemeAttrs[R.styleable.BitmapDrawable_src] == 0)) {
throw new XmlPullParserException(a.getPositionDescription() +
- ": <bitmap> requires a valid src attribute");
+ ": <bitmap> requires a valid 'src' attribute");
}
}
@@ -759,7 +761,7 @@
final Bitmap bitmap = BitmapFactory.decodeResource(r, srcResId);
if (bitmap == null) {
throw new XmlPullParserException(a.getPositionDescription() +
- ": <bitmap> requires a valid src attribute");
+ ": <bitmap> requires a valid 'src' attribute");
}
state.mBitmap = bitmap;
diff --git a/include/android_runtime/android_view_Surface.h b/include/android_runtime/android_view_Surface.h
index a6836a8..ed83314 100644
--- a/include/android_runtime/android_view_Surface.h
+++ b/include/android_runtime/android_view_Surface.h
@@ -43,6 +43,7 @@
NV21 = 0x11,
YUY2 = 0x14,
RAW_SENSOR = 0x20,
+ PRIVATE = 0x22,
YUV_420_888 = 0x23,
RAW10 = 0x25,
JPEG = 0x100,
diff --git a/include/androidfw/BackupHelpers.h b/include/androidfw/BackupHelpers.h
index 0841af6..fc1ad47 100644
--- a/include/androidfw/BackupHelpers.h
+++ b/include/androidfw/BackupHelpers.h
@@ -17,6 +17,8 @@
#ifndef _UTILS_BACKUP_HELPERS_H
#define _UTILS_BACKUP_HELPERS_H
+#include <sys/stat.h>
+
#include <utils/Errors.h>
#include <utils/String8.h>
#include <utils/KeyedVector.h>
@@ -135,7 +137,8 @@
char const* const* files, char const* const *keys, int fileCount);
int write_tarfile(const String8& packageName, const String8& domain,
- const String8& rootPath, const String8& filePath, BackupDataWriter* outputStream);
+ const String8& rootPath, const String8& filePath, off_t* outSize,
+ BackupDataWriter* outputStream);
class RestoreHelperBase
{
diff --git a/keystore/java/android/security/AndroidKeyPairGenerator.java b/keystore/java/android/security/AndroidKeyPairGenerator.java
index 9d9a173..5fae831 100644
--- a/keystore/java/android/security/AndroidKeyPairGenerator.java
+++ b/keystore/java/android/security/AndroidKeyPairGenerator.java
@@ -136,6 +136,8 @@
throw new IllegalStateException("could not generate key in keystore");
}
+ Credentials.deleteSecretKeyTypeForAlias(mKeyStore, alias);
+
final PrivateKey privKey;
final OpenSSLEngine engine = OpenSSLEngine.getInstance("keystore");
try {
diff --git a/keystore/java/android/security/AndroidKeyStore.java b/keystore/java/android/security/AndroidKeyStore.java
index acbae8f..1d16ca1 100644
--- a/keystore/java/android/security/AndroidKeyStore.java
+++ b/keystore/java/android/security/AndroidKeyStore.java
@@ -19,6 +19,9 @@
import com.android.org.conscrypt.OpenSSLEngine;
import com.android.org.conscrypt.OpenSSLKeyHolder;
+import android.security.keymaster.KeyCharacteristics;
+import android.security.keymaster.KeymasterArguments;
+import android.security.keymaster.KeymasterDefs;
import android.util.Log;
import java.io.ByteArrayInputStream;
@@ -31,6 +34,7 @@
import java.security.KeyStore.PrivateKeyEntry;
import java.security.KeyStore.ProtectionParameter;
import java.security.KeyStore;
+import java.security.KeyStore.SecretKeyEntry;
import java.security.KeyStoreException;
import java.security.KeyStoreSpi;
import java.security.NoSuchAlgorithmException;
@@ -50,6 +54,8 @@
import java.util.Iterator;
import java.util.Set;
+import javax.crypto.SecretKey;
+
/**
* A java.security.KeyStore interface for the Android KeyStore. An instance of
* it can be created via the {@link java.security.KeyStore#getInstance(String)
@@ -77,18 +83,72 @@
@Override
public Key engineGetKey(String alias, char[] password) throws NoSuchAlgorithmException,
UnrecoverableKeyException {
- if (!isKeyEntry(alias)) {
- return null;
+ if (isPrivateKeyEntry(alias)) {
+ final OpenSSLEngine engine = OpenSSLEngine.getInstance("keystore");
+ try {
+ return engine.getPrivateKeyById(Credentials.USER_PRIVATE_KEY + alias);
+ } catch (InvalidKeyException e) {
+ UnrecoverableKeyException t = new UnrecoverableKeyException("Can't get key");
+ t.initCause(e);
+ throw t;
+ }
+ } else if (isSecretKeyEntry(alias)) {
+ KeyCharacteristics keyCharacteristics = new KeyCharacteristics();
+ String keyAliasInKeystore = Credentials.USER_SECRET_KEY + alias;
+ int errorCode = mKeyStore.getKeyCharacteristics(
+ keyAliasInKeystore, null, null, keyCharacteristics);
+ if ((errorCode != KeymasterDefs.KM_ERROR_OK)
+ && (errorCode != android.security.KeyStore.NO_ERROR)) {
+ throw new UnrecoverableKeyException("Failed to load information about key."
+ + " Error code: " + errorCode);
+ }
+
+ int keymasterAlgorithm =
+ keyCharacteristics.hwEnforced.getInt(KeymasterDefs.KM_TAG_ALGORITHM, -1);
+ if (keymasterAlgorithm == -1) {
+ keymasterAlgorithm =
+ keyCharacteristics.swEnforced.getInt(KeymasterDefs.KM_TAG_ALGORITHM, -1);
+ }
+ if (keymasterAlgorithm == -1) {
+ throw new UnrecoverableKeyException("Key algorithm unknown");
+ }
+ @KeyStoreKeyConstraints.AlgorithmEnum int keyAlgorithm;
+ try {
+ keyAlgorithm = KeyStoreKeyConstraints.Algorithm.fromKeymaster(keymasterAlgorithm);
+ } catch (IllegalArgumentException e) {
+ throw (UnrecoverableKeyException)
+ new UnrecoverableKeyException("Unsupported key algorithm").initCause(e);
+ }
+
+ int keymasterDigest =
+ keyCharacteristics.hwEnforced.getInt(KeymasterDefs.KM_TAG_DIGEST, -1);
+ if (keymasterDigest == -1) {
+ keymasterDigest =
+ keyCharacteristics.swEnforced.getInt(KeymasterDefs.KM_TAG_DIGEST, -1);
+ }
+ @KeyStoreKeyConstraints.DigestEnum Integer digest = null;
+ if (keymasterDigest != -1) {
+ try {
+ digest = KeyStoreKeyConstraints.Digest.fromKeymaster(keymasterDigest);
+ } catch (IllegalArgumentException e) {
+ throw (UnrecoverableKeyException)
+ new UnrecoverableKeyException("Unsupported digest").initCause(e);
+ }
+ }
+
+ String keyAlgorithmString;
+ try {
+ keyAlgorithmString = KeyStoreKeyConstraints.Algorithm.toJCASecretKeyAlgorithm(
+ keyAlgorithm, digest);
+ } catch (IllegalArgumentException e) {
+ throw (UnrecoverableKeyException)
+ new UnrecoverableKeyException("Unsupported secret key type").initCause(e);
+ }
+
+ return new KeyStoreSecretKey(keyAliasInKeystore, keyAlgorithmString);
}
- final OpenSSLEngine engine = OpenSSLEngine.getInstance("keystore");
- try {
- return engine.getPrivateKeyById(Credentials.USER_PRIVATE_KEY + alias);
- } catch (InvalidKeyException e) {
- UnrecoverableKeyException t = new UnrecoverableKeyException("Can't get key");
- t.initCause(e);
- throw t;
- }
+ return null;
}
@Override
@@ -186,6 +246,11 @@
return d;
}
+ d = getModificationDate(Credentials.USER_SECRET_KEY + alias);
+ if (d != null) {
+ return d;
+ }
+
d = getModificationDate(Credentials.USER_CERTIFICATE + alias);
if (d != null) {
return d;
@@ -203,8 +268,10 @@
if (key instanceof PrivateKey) {
setPrivateKeyEntry(alias, (PrivateKey) key, chain, null);
+ } else if (key instanceof SecretKey) {
+ setSecretKeyEntry(alias, (SecretKey) key, null);
} else {
- throw new KeyStoreException("Only PrivateKeys are supported");
+ throw new KeyStoreException("Only PrivateKey and SecretKey are supported");
}
}
@@ -319,6 +386,7 @@
Credentials.deleteAllTypesForAlias(mKeyStore, alias);
} else {
Credentials.deleteCertificateTypesForAlias(mKeyStore, alias);
+ Credentials.deleteSecretKeyTypeForAlias(mKeyStore, alias);
}
final int flags = (params == null) ? 0 : params.getFlags();
@@ -340,6 +408,173 @@
}
}
+ private void setSecretKeyEntry(String entryAlias, SecretKey key, KeyStoreParameter params)
+ throws KeyStoreException {
+ if (key instanceof KeyStoreSecretKey) {
+ // KeyStore-backed secret key. It cannot be duplicated into another entry and cannot
+ // overwrite its own entry.
+ String keyAliasInKeystore = ((KeyStoreSecretKey) key).getAlias();
+ if (keyAliasInKeystore == null) {
+ throw new KeyStoreException("KeyStore-backed secret key does not have an alias");
+ }
+ if (!keyAliasInKeystore.startsWith(Credentials.USER_SECRET_KEY)) {
+ throw new KeyStoreException("KeyStore-backed secret key has invalid alias: "
+ + keyAliasInKeystore);
+ }
+ String keyEntryAlias =
+ keyAliasInKeystore.substring(Credentials.USER_SECRET_KEY.length());
+ if (!entryAlias.equals(keyEntryAlias)) {
+ throw new KeyStoreException("Can only replace KeyStore-backed keys with same"
+ + " alias: " + entryAlias + " != " + keyEntryAlias);
+ }
+ // This is the entry where this key is already stored. No need to do anything.
+ if (params != null) {
+ throw new KeyStoreException("Modifying KeyStore-backed key using protection"
+ + " parameters not supported");
+ }
+ return;
+ }
+
+ if (params == null) {
+ throw new KeyStoreException(
+ "Protection parameters must be specified when importing a symmetric key");
+ }
+
+ // Not a KeyStore-backed secret key -- import its key material into keystore.
+ String keyExportFormat = key.getFormat();
+ if (keyExportFormat == null) {
+ throw new KeyStoreException(
+ "Only secret keys that export their key material are supported");
+ } else if (!"RAW".equals(keyExportFormat)) {
+ throw new KeyStoreException(
+ "Unsupported secret key material export format: " + keyExportFormat);
+ }
+ byte[] keyMaterial = key.getEncoded();
+ if (keyMaterial == null) {
+ throw new KeyStoreException("Key did not export its key material despite supporting"
+ + " RAW format export");
+ }
+
+ String keyAlgorithmString = key.getAlgorithm();
+ @KeyStoreKeyConstraints.AlgorithmEnum int keyAlgorithm;
+ @KeyStoreKeyConstraints.AlgorithmEnum Integer digest;
+ try {
+ keyAlgorithm =
+ KeyStoreKeyConstraints.Algorithm.fromJCASecretKeyAlgorithm(keyAlgorithmString);
+ digest = KeyStoreKeyConstraints.Digest.fromJCASecretKeyAlgorithm(keyAlgorithmString);
+ } catch (IllegalArgumentException e) {
+ throw new KeyStoreException("Unsupported secret key algorithm: " + keyAlgorithmString);
+ }
+
+ if ((params.getAlgorithm() != null) && (params.getAlgorithm() != keyAlgorithm)) {
+ throw new KeyStoreException("Key algorithm mismatch. Key: " + keyAlgorithmString
+ + ", parameter spec: "
+ + KeyStoreKeyConstraints.Algorithm.toString(params.getAlgorithm()));
+ }
+
+ KeymasterArguments args = new KeymasterArguments();
+ args.addInt(KeymasterDefs.KM_TAG_ALGORITHM,
+ KeyStoreKeyConstraints.Algorithm.toKeymaster(keyAlgorithm));
+
+ if (digest != null) {
+ // Digest available from JCA key algorithm
+ if (params.getDigest() != null) {
+ // Digest also specified in parameters -- check that these two match
+ if (digest != params.getDigest()) {
+ throw new KeyStoreException("Key digest mismatch. Key: " + keyAlgorithmString
+ + ", parameter spec: "
+ + KeyStoreKeyConstraints.Digest.toString(params.getDigest()));
+ }
+ }
+ } else {
+ // Digest not available from JCA key algorithm
+ digest = params.getDigest();
+ }
+ if (digest != null) {
+ args.addInt(KeymasterDefs.KM_TAG_DIGEST,
+ KeyStoreKeyConstraints.Digest.toKeymaster(digest));
+ }
+ if (keyAlgorithm == KeyStoreKeyConstraints.Algorithm.HMAC) {
+ if (digest == null) {
+ throw new IllegalStateException("Digest algorithm must be specified for key"
+ + " algorithm " + keyAlgorithmString);
+ }
+ Integer digestOutputSizeBytes =
+ KeyStoreKeyConstraints.Digest.getOutputSizeBytes(digest);
+ if (digestOutputSizeBytes != null) {
+ // TODO: Remove MAC length constraint once Keymaster API no longer requires it.
+ // TODO: Switch to bits instead of bytes, once this is fixed in Keymaster
+ args.addInt(KeymasterDefs.KM_TAG_MAC_LENGTH, digestOutputSizeBytes);
+ }
+ }
+
+ @KeyStoreKeyConstraints.PurposeEnum int purposes = (params.getPurposes() != null)
+ ? params.getPurposes()
+ : (KeyStoreKeyConstraints.Purpose.ENCRYPT
+ | KeyStoreKeyConstraints.Purpose.DECRYPT
+ | KeyStoreKeyConstraints.Purpose.SIGN
+ | KeyStoreKeyConstraints.Purpose.VERIFY);
+ for (int keymasterPurpose :
+ KeyStoreKeyConstraints.Purpose.allToKeymaster(purposes)) {
+ args.addInt(KeymasterDefs.KM_TAG_PURPOSE, keymasterPurpose);
+ }
+ if (params.getBlockMode() != null) {
+ args.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE,
+ KeyStoreKeyConstraints.BlockMode.toKeymaster(params.getBlockMode()));
+ }
+ if (params.getPadding() != null) {
+ args.addInt(KeymasterDefs.KM_TAG_PADDING,
+ KeyStoreKeyConstraints.Padding.toKeymaster(params.getPadding()));
+ }
+ if (params.getMaxUsesPerBoot() != null) {
+ args.addInt(KeymasterDefs.KM_TAG_MAX_USES_PER_BOOT, params.getMaxUsesPerBoot());
+ }
+ if (params.getMinSecondsBetweenOperations() != null) {
+ args.addInt(KeymasterDefs.KM_TAG_MIN_SECONDS_BETWEEN_OPS,
+ params.getMinSecondsBetweenOperations());
+ }
+ if (params.getUserAuthenticators().isEmpty()) {
+ args.addBoolean(KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED);
+ } else {
+ // TODO: Pass-in user authenticator IDs once the Keymaster API has stabilized
+// for (int userAuthenticatorId : params.getUserAuthenticators()) {
+// args.addInt(KeymasterDefs.KM_TAG_USER_AUTH_ID, userAuthenticatorId);
+// }
+ }
+ if (params.getUserAuthenticationValidityDurationSeconds() != null) {
+ args.addInt(KeymasterDefs.KM_TAG_AUTH_TIMEOUT,
+ params.getUserAuthenticationValidityDurationSeconds());
+ }
+ if (params.getKeyValidityStart() != null) {
+ args.addDate(KeymasterDefs.KM_TAG_ACTIVE_DATETIME, params.getKeyValidityStart());
+ }
+ if (params.getKeyValidityForOriginationEnd() != null) {
+ args.addDate(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME,
+ params.getKeyValidityForOriginationEnd());
+ }
+ if (params.getKeyValidityForConsumptionEnd() != null) {
+ args.addDate(KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME,
+ params.getKeyValidityForConsumptionEnd());
+ }
+
+ // TODO: Remove this once keymaster does not require us to specify the size of imported key.
+ args.addInt(KeymasterDefs.KM_TAG_KEY_SIZE, keyMaterial.length * 8);
+
+ Credentials.deleteAllTypesForAlias(mKeyStore, entryAlias);
+ String keyAliasInKeystore = Credentials.USER_SECRET_KEY + entryAlias;
+ int errorCode = mKeyStore.importKey(
+ keyAliasInKeystore,
+ args,
+ KeymasterDefs.KM_KEY_FORMAT_RAW,
+ keyMaterial,
+ params.getFlags(),
+ new KeyCharacteristics());
+ if (errorCode != android.security.KeyStore.NO_ERROR) {
+ throw new KeyStoreException("Failed to import secret key. Keystore error code: "
+ + errorCode);
+ }
+ }
+
@Override
public void engineSetKeyEntry(String alias, byte[] userKey, Certificate[] chain)
throws KeyStoreException {
@@ -413,6 +648,7 @@
}
return mKeyStore.contains(Credentials.USER_PRIVATE_KEY + alias)
+ || mKeyStore.contains(Credentials.USER_SECRET_KEY + alias)
|| mKeyStore.contains(Credentials.USER_CERTIFICATE + alias)
|| mKeyStore.contains(Credentials.CA_CERTIFICATE + alias);
}
@@ -428,6 +664,10 @@
}
private boolean isKeyEntry(String alias) {
+ return isPrivateKeyEntry(alias) || isSecretKeyEntry(alias);
+ }
+
+ private boolean isPrivateKeyEntry(String alias) {
if (alias == null) {
throw new NullPointerException("alias == null");
}
@@ -435,6 +675,14 @@
return mKeyStore.contains(Credentials.USER_PRIVATE_KEY + alias);
}
+ private boolean isSecretKeyEntry(String alias) {
+ if (alias == null) {
+ throw new NullPointerException("alias == null");
+ }
+
+ return mKeyStore.contains(Credentials.USER_SECRET_KEY + alias);
+ }
+
private boolean isCertificateEntry(String alias) {
if (alias == null) {
throw new NullPointerException("alias == null");
@@ -554,11 +802,14 @@
PrivateKeyEntry prE = (PrivateKeyEntry) entry;
setPrivateKeyEntry(alias, prE.getPrivateKey(), prE.getCertificateChain(),
(KeyStoreParameter) param);
- return;
+ } else if (entry instanceof SecretKeyEntry) {
+ SecretKeyEntry secE = (SecretKeyEntry) entry;
+ setSecretKeyEntry(alias, secE.getSecretKey(), (KeyStoreParameter) param);
+ } else {
+ throw new KeyStoreException(
+ "Entry must be a PrivateKeyEntry, SecretKeyEntry or TrustedCertificateEntry"
+ + "; was " + entry);
}
-
- throw new KeyStoreException(
- "Entry must be a PrivateKeyEntry or TrustedCertificateEntry; was " + entry);
}
}
diff --git a/keystore/java/android/security/AndroidKeyStoreProvider.java b/keystore/java/android/security/AndroidKeyStoreProvider.java
index 9081e92..7313c48 100644
--- a/keystore/java/android/security/AndroidKeyStoreProvider.java
+++ b/keystore/java/android/security/AndroidKeyStoreProvider.java
@@ -35,5 +35,13 @@
// java.security.KeyPairGenerator
put("KeyPairGenerator.EC", AndroidKeyPairGenerator.EC.class.getName());
put("KeyPairGenerator.RSA", AndroidKeyPairGenerator.RSA.class.getName());
+
+ // javax.crypto.KeyGenerator
+ put("KeyGenerator.AES", KeyStoreKeyGeneratorSpi.AES.class.getName());
+ put("KeyGenerator.HmacSHA256", KeyStoreKeyGeneratorSpi.HmacSHA256.class.getName());
+
+ // javax.crypto.Mac
+ put("Mac.HmacSHA256", KeyStoreHmacSpi.HmacSHA256.class.getName());
+ put("Mac.HmacSHA256 SupportedKeyClasses", KeyStoreSecretKey.class.getName());
}
}
diff --git a/keystore/java/android/security/Credentials.java b/keystore/java/android/security/Credentials.java
index af76d9d..6283e02 100644
--- a/keystore/java/android/security/Credentials.java
+++ b/keystore/java/android/security/Credentials.java
@@ -61,6 +61,9 @@
/** Key prefix for user private keys. */
public static final String USER_PRIVATE_KEY = "USRPKEY_";
+ /** Key prefix for user secret keys. */
+ public static final String USER_SECRET_KEY = "USRSKEY_";
+
/** Key prefix for VPN. */
public static final String VPN = "VPN_";
@@ -218,7 +221,8 @@
* Make sure every type is deleted. There can be all three types, so
* don't use a conditional here.
*/
- return keystore.delKey(Credentials.USER_PRIVATE_KEY + alias)
+ return keystore.delete(Credentials.USER_PRIVATE_KEY + alias)
+ | keystore.delete(Credentials.USER_SECRET_KEY + alias)
| deleteCertificateTypesForAlias(keystore, alias);
}
@@ -235,4 +239,20 @@
return keystore.delete(Credentials.USER_CERTIFICATE + alias)
| keystore.delete(Credentials.CA_CERTIFICATE + alias);
}
+
+ /**
+ * Delete private key for a particular {@code alias}.
+ * Returns {@code true} if an entry was was deleted.
+ */
+ static boolean deletePrivateKeyTypeForAlias(KeyStore keystore, String alias) {
+ return keystore.delete(Credentials.USER_PRIVATE_KEY + alias);
+ }
+
+ /**
+ * Delete secret key for a particular {@code alias}.
+ * Returns {@code true} if an entry was was deleted.
+ */
+ static boolean deleteSecretKeyTypeForAlias(KeyStore keystore, String alias) {
+ return keystore.delete(Credentials.USER_SECRET_KEY + alias);
+ }
}
diff --git a/keystore/java/android/security/CryptoOperationException.java b/keystore/java/android/security/CryptoOperationException.java
new file mode 100644
index 0000000..ce64455
--- /dev/null
+++ b/keystore/java/android/security/CryptoOperationException.java
@@ -0,0 +1,45 @@
+package android.security;
+
+/**
+ * Base class for exceptions during cryptographic operations which cannot throw a suitable checked
+ * exception.
+ *
+ * <p>The contract of the majority of crypto primitives/operations (e.g. {@code Cipher} or
+ * {@code Signature}) is that they can throw a checked exception during initialization, but are not
+ * permitted to throw a checked exception during operation. Because crypto operations can fail
+ * for a variety of reasons after initialization, this base class provides type-safety for unchecked
+ * exceptions that may be thrown in those cases.
+ *
+ * @hide
+ */
+public class CryptoOperationException extends RuntimeException {
+
+ /**
+ * Constructs a new {@code CryptoOperationException} without detail message and cause.
+ */
+ public CryptoOperationException() {
+ super();
+ }
+
+ /**
+ * Constructs a new {@code CryptoOperationException} with the provided detail message and no
+ * cause.
+ */
+ public CryptoOperationException(String message) {
+ super(message);
+ }
+
+ /**
+ * Constructs a new {@code CryptoOperationException} with the provided detail message and cause.
+ */
+ public CryptoOperationException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ /**
+ * Constructs a new {@code CryptoOperationException} with the provided cause.
+ */
+ public CryptoOperationException(Throwable cause) {
+ super(cause);
+ }
+}
diff --git a/keystore/java/android/security/KeyGeneratorSpec.java b/keystore/java/android/security/KeyGeneratorSpec.java
new file mode 100644
index 0000000..6274b70
--- /dev/null
+++ b/keystore/java/android/security/KeyGeneratorSpec.java
@@ -0,0 +1,471 @@
+package android.security;
+
+import android.content.Context;
+import android.text.TextUtils;
+
+import java.security.cert.Certificate;
+import java.security.spec.AlgorithmParameterSpec;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.crypto.KeyGenerator;
+import javax.crypto.SecretKey;
+
+/**
+ * {@link AlgorithmParameterSpec} for initializing a {@code KeyGenerator} that works with
+ * <a href="{@docRoot}training/articles/keystore.html">Android KeyStore facility</a>.
+ *
+ * <p>The Android KeyStore facility is accessed through a {@link KeyGenerator} API
+ * using the {@code AndroidKeyStore} provider. The {@code context} passed in may be used to pop up
+ * some UI to ask the user to unlock or initialize the Android KeyStore facility.
+ *
+ * <p>After generation, the {@code keyStoreAlias} is used with the
+ * {@link java.security.KeyStore#getEntry(String, java.security.KeyStore.ProtectionParameter)}
+ * interface to retrieve the {@link SecretKey} and its associated {@link Certificate} chain.
+ *
+ * @hide
+ */
+public class KeyGeneratorSpec implements AlgorithmParameterSpec {
+
+ private final Context mContext;
+ private final String mKeystoreAlias;
+ private final int mFlags;
+ private final Integer mKeySize;
+ private final Date mKeyValidityStart;
+ private final Date mKeyValidityForOriginationEnd;
+ private final Date mKeyValidityForConsumptionEnd;
+ private final @KeyStoreKeyConstraints.PurposeEnum Integer mPurposes;
+ private final @KeyStoreKeyConstraints.PaddingEnum Integer mPadding;
+ private final @KeyStoreKeyConstraints.BlockModeEnum Integer mBlockMode;
+ private final Integer mMinSecondsBetweenOperations;
+ private final Integer mMaxUsesPerBoot;
+ private final Set<Integer> mUserAuthenticators;
+ private final Integer mUserAuthenticationValidityDurationSeconds;
+
+ private KeyGeneratorSpec(
+ Context context,
+ String keyStoreAlias,
+ int flags,
+ Integer keySize,
+ Date keyValidityStart,
+ Date keyValidityForOriginationEnd,
+ Date keyValidityForConsumptionEnd,
+ @KeyStoreKeyConstraints.PurposeEnum Integer purposes,
+ @KeyStoreKeyConstraints.PaddingEnum Integer padding,
+ @KeyStoreKeyConstraints.BlockModeEnum Integer blockMode,
+ Integer minSecondsBetweenOperations,
+ Integer maxUsesPerBoot,
+ Set<Integer> userAuthenticators,
+ Integer userAuthenticationValidityDurationSeconds) {
+ if (context == null) {
+ throw new IllegalArgumentException("context == null");
+ } else if (TextUtils.isEmpty(keyStoreAlias)) {
+ throw new IllegalArgumentException("keyStoreAlias must not be empty");
+ } else if ((userAuthenticationValidityDurationSeconds != null)
+ && (userAuthenticationValidityDurationSeconds < 0)) {
+ throw new IllegalArgumentException(
+ "userAuthenticationValidityDurationSeconds must not be negative");
+ }
+
+ mContext = context;
+ mKeystoreAlias = keyStoreAlias;
+ mFlags = flags;
+ mKeySize = keySize;
+ mKeyValidityStart = keyValidityStart;
+ mKeyValidityForOriginationEnd = keyValidityForOriginationEnd;
+ mKeyValidityForConsumptionEnd = keyValidityForConsumptionEnd;
+ mPurposes = purposes;
+ mPadding = padding;
+ mBlockMode = blockMode;
+ mMinSecondsBetweenOperations = minSecondsBetweenOperations;
+ mMaxUsesPerBoot = maxUsesPerBoot;
+ mUserAuthenticators = (userAuthenticators != null)
+ ? new HashSet<Integer>(userAuthenticators)
+ : Collections.<Integer>emptySet();
+ mUserAuthenticationValidityDurationSeconds = userAuthenticationValidityDurationSeconds;
+ }
+
+ /**
+ * Gets the Android context used for operations with this instance.
+ */
+ public Context getContext() {
+ return mContext;
+ }
+
+ /**
+ * Returns the alias that will be used in the {@code java.security.KeyStore} in conjunction with
+ * the {@code AndroidKeyStore}.
+ */
+ public String getKeystoreAlias() {
+ return mKeystoreAlias;
+ }
+
+ /**
+ * @hide
+ */
+ public int getFlags() {
+ return mFlags;
+ }
+
+ /**
+ * Gets the requested key size or {@code null} if the default size should be used.
+ */
+ public Integer getKeySize() {
+ return mKeySize;
+ }
+
+ /**
+ * Gets the time instant before which the key is not yet valid.
+ *
+ * @return instant or {@code null} if not restricted.
+ */
+ public Date getKeyValidityStart() {
+ return mKeyValidityStart;
+ }
+
+ /**
+ * Gets the time instant after which the key is no long valid for decryption and verification.
+ *
+ * @return instant or {@code null} if not restricted.
+ *
+ * @hide
+ */
+ public Date getKeyValidityForConsumptionEnd() {
+ return mKeyValidityForConsumptionEnd;
+ }
+
+ /**
+ * Gets the time instant after which the key is no long valid for encryption and signing.
+ *
+ * @return instant or {@code null} if not restricted.
+ */
+ public Date getKeyValidityForOriginationEnd() {
+ return mKeyValidityForOriginationEnd;
+ }
+
+ /**
+ * Gets the set of purposes for which the key can be used to the provided set of purposes.
+ *
+ * @return set of purposes or {@code null} if the key can be used for any purpose.
+ */
+ public @KeyStoreKeyConstraints.PurposeEnum Integer getPurposes() {
+ return mPurposes;
+ }
+
+ /**
+ * Gets the padding scheme to which the key is restricted.
+ *
+ * @return padding scheme or {@code null} if the padding scheme is not restricted.
+ */
+ public @KeyStoreKeyConstraints.PaddingEnum Integer getPadding() {
+ return mPadding;
+ }
+
+ /**
+ * Gets the block mode to which the key is restricted when used for encryption or decryption.
+ *
+ * @return block more or {@code null} if block mode is not restricted.
+ *
+ * @hide
+ */
+ public @KeyStoreKeyConstraints.BlockModeEnum Integer getBlockMode() {
+ return mBlockMode;
+ }
+
+ /**
+ * Gets the minimum number of seconds that must expire since the most recent use of the key
+ * before it can be used again.
+ *
+ * @return number of seconds or {@code null} if there is no restriction on how frequently a key
+ * can be used.
+ *
+ * @hide
+ */
+ public Integer getMinSecondsBetweenOperations() {
+ return mMinSecondsBetweenOperations;
+ }
+
+ /**
+ * Gets the number of times the key can be used without rebooting the device.
+ *
+ * @return maximum number of times or {@code null} if there is no restriction.
+ * @hide
+ */
+ public Integer getMaxUsesPerBoot() {
+ return mMaxUsesPerBoot;
+ }
+
+ /**
+ * Gets the user authenticators which protect access to this key. The key can only be used iff
+ * the user has authenticated to at least one of these user authenticators.
+ *
+ * @return user authenticators or empty set if the key can be used without user authentication.
+ *
+ * @hide
+ */
+ public Set<Integer> getUserAuthenticators() {
+ return new HashSet<Integer>(mUserAuthenticators);
+ }
+
+ /**
+ * Gets the duration of time (seconds) for which this key can be used after the user
+ * successfully authenticates to one of the associated user authenticators.
+ *
+ * @return duration in seconds or {@code null} if not restricted. {@code 0} means authentication
+ * is required for every use of the key.
+ *
+ * @hide
+ */
+ public Integer getUserAuthenticationValidityDurationSeconds() {
+ return mUserAuthenticationValidityDurationSeconds;
+ }
+
+ /**
+ * Returns {@code true} if the key must be encrypted in the {@link java.security.KeyStore}.
+ */
+ public boolean isEncryptionRequired() {
+ return (mFlags & KeyStore.FLAG_ENCRYPTED) != 0;
+ }
+
+ public static class Builder {
+ private final Context mContext;
+ private String mKeystoreAlias;
+ private int mFlags;
+ private Integer mKeySize;
+ private Date mKeyValidityStart;
+ private Date mKeyValidityForOriginationEnd;
+ private Date mKeyValidityForConsumptionEnd;
+ private @KeyStoreKeyConstraints.PurposeEnum Integer mPurposes;
+ private @KeyStoreKeyConstraints.PaddingEnum Integer mPadding;
+ private @KeyStoreKeyConstraints.BlockModeEnum Integer mBlockMode;
+ private Integer mMinSecondsBetweenOperations;
+ private Integer mMaxUsesPerBoot;
+ private Set<Integer> mUserAuthenticators;
+ private Integer mUserAuthenticationValidityDurationSeconds;
+
+ /**
+ * Creates a new instance of the {@code Builder} with the given {@code context}. The
+ * {@code context} passed in may be used to pop up some UI to ask the user to unlock or
+ * initialize the Android KeyStore facility.
+ */
+ public Builder(Context context) {
+ if (context == null) {
+ throw new NullPointerException("context == null");
+ }
+ mContext = context;
+ }
+
+ /**
+ * Sets the alias to be used to retrieve the key later from a {@link java.security.KeyStore}
+ * instance using the {@code AndroidKeyStore} provider.
+ *
+ * <p>The alias must be provided. There is no default.
+ */
+ public Builder setAlias(String alias) {
+ if (alias == null) {
+ throw new NullPointerException("alias == null");
+ }
+ mKeystoreAlias = alias;
+ return this;
+ }
+
+ /**
+ * Sets the size (in bits) of the key to be generated.
+ *
+ * <p>By default, the key size will be determines based on the key algorithm. For example,
+ * for {@code HmacSHA256}, the key size will default to {@code 256}.
+ */
+ public Builder setKeySize(int keySize) {
+ mKeySize = keySize;
+ return this;
+ }
+
+ /**
+ * Indicates that this key must be encrypted at rest on storage. Note that enabling this
+ * will require that the user enable a strong lock screen (e.g., PIN, password) before
+ * creating or using the generated key is successful.
+ */
+ public Builder setEncryptionRequired(boolean required) {
+ if (required) {
+ mFlags |= KeyStore.FLAG_ENCRYPTED;
+ } else {
+ mFlags &= ~KeyStore.FLAG_ENCRYPTED;
+ }
+ return this;
+ }
+
+ /**
+ * Sets the time instant before which the key is not yet valid.
+ *
+ * <b>By default, the key is valid at any instant.
+ *
+ * @see #setKeyValidityEnd(Date)
+ *
+ * @hide
+ */
+ public Builder setKeyValidityStart(Date startDate) {
+ mKeyValidityStart = startDate;
+ return this;
+ }
+
+ /**
+ * Sets the time instant after which the key is no longer valid.
+ *
+ * <b>By default, the key is valid at any instant.
+ *
+ * @see #setKeyValidityStart(Date)
+ * @see #setKeyValidityForConsumptionEnd(Date)
+ * @see #setKeyValidityForOriginationEnd(Date)
+ *
+ * @hide
+ */
+ public Builder setKeyValidityEnd(Date endDate) {
+ setKeyValidityForOriginationEnd(endDate);
+ setKeyValidityForConsumptionEnd(endDate);
+ return this;
+ }
+
+ /**
+ * Sets the time instant after which the key is no longer valid for encryption and signing.
+ *
+ * <b>By default, the key is valid at any instant.
+ *
+ * @see #setKeyValidityForConsumptionEnd(Date)
+ *
+ * @hide
+ */
+ public Builder setKeyValidityForOriginationEnd(Date endDate) {
+ mKeyValidityForOriginationEnd = endDate;
+ return this;
+ }
+
+ /**
+ * Sets the time instant after which the key is no longer valid for decryption and
+ * verification.
+ *
+ * <b>By default, the key is valid at any instant.
+ *
+ * @see #setKeyValidityForOriginationEnd(Date)
+ *
+ * @hide
+ */
+ public Builder setKeyValidityForConsumptionEnd(Date endDate) {
+ mKeyValidityForConsumptionEnd = endDate;
+ return this;
+ }
+
+ /**
+ * Restricts the purposes for which the key can be used to the provided set of purposes.
+ *
+ * <p>By default, the key can be used for encryption, decryption, signing, and verification.
+ *
+ * @hide
+ */
+ public Builder setPurposes(@KeyStoreKeyConstraints.PurposeEnum int purposes) {
+ mPurposes = purposes;
+ return this;
+ }
+
+ /**
+ * Restricts the key to being used only with the provided padding scheme. Attempts to use
+ * the key with any other padding will be rejected.
+ *
+ * <p>This restriction must be specified for keys which are used for encryption/decryption.
+ *
+ * @hide
+ */
+ public Builder setPadding(@KeyStoreKeyConstraints.PaddingEnum int padding) {
+ mPadding = padding;
+ return this;
+ }
+
+ /**
+ * Restricts the key to being used only with the provided block mode when encrypting or
+ * decrypting. Attempts to use the key with any other block modes will be rejected.
+ *
+ * <p>This restriction must be specified for keys which are used for encryption/decryption.
+ *
+ * @hide
+ */
+ public Builder setBlockMode(@KeyStoreKeyConstraints.BlockModeEnum int blockMode) {
+ mBlockMode = blockMode;
+ return this;
+ }
+
+ /**
+ * Sets the minimum number of seconds that must expire since the most recent use of the key
+ * before it can be used again.
+ *
+ * <p>By default, there is no restriction on how frequently a key can be used.
+ *
+ * @hide
+ */
+ public Builder setMinSecondsBetweenOperations(int seconds) {
+ mMinSecondsBetweenOperations = seconds;
+ return this;
+ }
+
+ /**
+ * Sets the maximum number of times a key can be used without rebooting the device.
+ *
+ * <p>By default, the key can be used for an unlimited number of times.
+ *
+ * @hide
+ */
+ public Builder setMaxUsesPerBoot(int count) {
+ mMaxUsesPerBoot = count;
+ return this;
+ }
+
+ /**
+ * Sets the user authenticators which protect access to this key. The key can only be used
+ * iff the user has authenticated to at least one of these user authenticators.
+ *
+ * <p>By default, the key can be used without user authentication.
+ *
+ * @param userAuthenticators user authenticators or empty list if this key can be accessed
+ * without user authentication.
+ *
+ * @see #setUserAuthenticationValidityDurationSeconds(int)
+ *
+ * @hide
+ */
+ public Builder setUserAuthenticators(Set<Integer> userAuthenticators) {
+ mUserAuthenticators =
+ (userAuthenticators != null) ? new HashSet<Integer>(userAuthenticators) : null;
+ return this;
+ }
+
+ /**
+ * Sets the duration of time (seconds) for which this key can be used after the user
+ * successfully authenticates to one of the associated user authenticators.
+ *
+ * <p>By default, the user needs to authenticate for every use of the key.
+ *
+ * @param seconds duration in seconds or {@code 0} if the user needs to authenticate for
+ * every use of the key.
+ *
+ * @see #setUserAuthenticators(Set)
+ *
+ * @hide
+ */
+ public Builder setUserAuthenticationValidityDurationSeconds(int seconds) {
+ mUserAuthenticationValidityDurationSeconds = seconds;
+ return this;
+ }
+
+ /**
+ * Builds a new instance instance of {@code KeyGeneratorSpec}.
+ *
+ * @throws IllegalArgumentException if a required field is missing or violates a constraint.
+ */
+ public KeyGeneratorSpec build() {
+ return new KeyGeneratorSpec(mContext, mKeystoreAlias, mFlags, mKeySize,
+ mKeyValidityStart, mKeyValidityForOriginationEnd, mKeyValidityForConsumptionEnd,
+ mPurposes, mPadding, mBlockMode, mMinSecondsBetweenOperations, mMaxUsesPerBoot,
+ mUserAuthenticators, mUserAuthenticationValidityDurationSeconds);
+ }
+ }
+}
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index 957e3c1..94a479b 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -389,19 +389,19 @@
}
}
- public int generateKey(String alias, KeymasterArguments args, int uid, int flags,
- KeyCharacteristics outCharacteristics) {
+ public int generateKey(String alias, KeymasterArguments args, byte[] entropy, int uid,
+ int flags, KeyCharacteristics outCharacteristics) {
try {
- return mBinder.generateKey(alias, args, uid, flags, outCharacteristics);
+ return mBinder.generateKey(alias, args, entropy, uid, flags, outCharacteristics);
} catch (RemoteException e) {
Log.w(TAG, "Cannot connect to keystore", e);
return SYSTEM_ERROR;
}
}
- public int generateKey(String alias, KeymasterArguments args, int flags,
+ public int generateKey(String alias, KeymasterArguments args, byte[] entropy, int flags,
KeyCharacteristics outCharacteristics) {
- return generateKey(alias, args, UID_SELF, flags, outCharacteristics);
+ return generateKey(alias, args, entropy, UID_SELF, flags, outCharacteristics);
}
public int getKeyCharacteristics(String alias, KeymasterBlob clientId, KeymasterBlob appId,
@@ -441,9 +441,9 @@
}
public OperationResult begin(String alias, int purpose, boolean pruneable,
- KeymasterArguments args, KeymasterArguments outArgs) {
+ KeymasterArguments args, byte[] entropy, KeymasterArguments outArgs) {
try {
- return mBinder.begin(getToken(), alias, purpose, pruneable, args, outArgs);
+ return mBinder.begin(getToken(), alias, purpose, pruneable, args, entropy, outArgs);
} catch (RemoteException e) {
Log.w(TAG, "Cannot connect to keystore", e);
return null;
@@ -476,4 +476,34 @@
return SYSTEM_ERROR;
}
}
+
+ /**
+ * Check if the operation referenced by {@code token} is currently authorized.
+ *
+ * @param token An operation token returned by a call to {@link KeyStore.begin}.
+ */
+ public boolean isOperationAuthorized(IBinder token) {
+ try {
+ return mBinder.isOperationAuthorized(token);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Cannot connect to keystore", e);
+ return false;
+ }
+ }
+
+ /**
+ * Add an authentication record to the keystore authorization table.
+ *
+ * @param authToken The packed bytes of a hw_auth_token_t to be provided to keymaster.
+ * @return {@code KeyStore.NO_ERROR} on success, otherwise an error value corresponding to
+ * a {@code KeymasterDefs.KM_ERROR_} value or {@code KeyStore} ResponseCode.
+ */
+ public int addAuthToken(byte[] authToken) {
+ try {
+ return mBinder.addAuthToken(authToken);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Cannot connect to keystore", e);
+ return SYSTEM_ERROR;
+ }
+ }
}
diff --git a/keystore/java/android/security/KeyStoreConnectException.java b/keystore/java/android/security/KeyStoreConnectException.java
new file mode 100644
index 0000000..4c465a4
--- /dev/null
+++ b/keystore/java/android/security/KeyStoreConnectException.java
@@ -0,0 +1,12 @@
+package android.security;
+
+/**
+ * Indicates a communications error with keystore service.
+ *
+ * @hide
+ */
+public class KeyStoreConnectException extends CryptoOperationException {
+ public KeyStoreConnectException() {
+ super("Failed to communicate with keystore service");
+ }
+}
diff --git a/keystore/java/android/security/KeyStoreCryptoOperationChunkedStreamer.java b/keystore/java/android/security/KeyStoreCryptoOperationChunkedStreamer.java
new file mode 100644
index 0000000..a37ddce
--- /dev/null
+++ b/keystore/java/android/security/KeyStoreCryptoOperationChunkedStreamer.java
@@ -0,0 +1,228 @@
+package android.security;
+
+import android.security.keymaster.OperationResult;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+/**
+ * Helper for streaming a crypto operation's input and output via {@link KeyStore} service's
+ * {@code update} and {@code finish} operations.
+ *
+ * <p>The helper abstracts away to issues that need to be solved in most code that uses KeyStore's
+ * update and finish operations. Firstly, KeyStore's update and finish operations can consume only a
+ * limited amount of data in one go because the operations are marshalled via Binder. Secondly, the
+ * update operation may consume less data than provided, in which case the caller has to buffer
+ * the remainder for next time. The helper exposes {@link #update(byte[], int, int) update} and
+ * {@link #doFinal(byte[], int, int) doFinal} operations which can be used to conveniently implement
+ * various JCA crypto primitives.
+ *
+ * <p>KeyStore operation through which data is streamed is abstracted away as
+ * {@link KeyStoreOperation} to avoid having this class deal with operation tokens and occasional
+ * additional parameters to update and final operations.
+ *
+ * @hide
+ */
+public class KeyStoreCryptoOperationChunkedStreamer {
+ public interface KeyStoreOperation {
+ /**
+ * Returns the result of the KeyStore update operation or null if keystore couldn't be
+ * reached.
+ */
+ OperationResult update(byte[] input);
+
+ /**
+ * Returns the result of the KeyStore finish operation or null if keystore couldn't be
+ * reached.
+ */
+ OperationResult finish(byte[] input);
+ }
+
+ // Binder buffer is about 1MB, but it's shared between all active transactions of the process.
+ // Thus, it's safer to use a much smaller upper bound.
+ private static final int DEFAULT_MAX_CHUNK_SIZE = 64 * 1024;
+ private static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
+
+ private final KeyStoreOperation mKeyStoreOperation;
+ private final int mMaxChunkSize;
+
+ private byte[] mBuffered = EMPTY_BYTE_ARRAY;
+ private int mBufferedOffset;
+ private int mBufferedLength;
+
+ public KeyStoreCryptoOperationChunkedStreamer(KeyStoreOperation operation) {
+ this(operation, DEFAULT_MAX_CHUNK_SIZE);
+ }
+
+ public KeyStoreCryptoOperationChunkedStreamer(KeyStoreOperation operation, int maxChunkSize) {
+ mKeyStoreOperation = operation;
+ mMaxChunkSize = maxChunkSize;
+ }
+
+ public byte[] update(byte[] input, int inputOffset, int inputLength) throws KeymasterException {
+ if (inputLength == 0) {
+ // No input provided
+ return EMPTY_BYTE_ARRAY;
+ }
+
+ ByteArrayOutputStream bufferedOutput = null;
+
+ while (inputLength > 0) {
+ byte[] chunk;
+ int inputBytesInChunk;
+ if ((mBufferedLength + inputLength) > mMaxChunkSize) {
+ // Too much input for one chunk -- extract one max-sized chunk and feed it into the
+ // update operation.
+ chunk = new byte[mMaxChunkSize];
+ System.arraycopy(mBuffered, mBufferedOffset, chunk, 0, mBufferedLength);
+ inputBytesInChunk = chunk.length - mBufferedLength;
+ System.arraycopy(input, inputOffset, chunk, mBufferedLength, inputBytesInChunk);
+ } else {
+ // All of available input fits into one chunk.
+ if ((mBufferedLength == 0) && (inputOffset == 0)
+ && (inputLength == input.length)) {
+ // Nothing buffered and all of input array needs to be fed into the update
+ // operation.
+ chunk = input;
+ inputBytesInChunk = input.length;
+ } else {
+ // Need to combine buffered data with input data into one array.
+ chunk = new byte[mBufferedLength + inputLength];
+ inputBytesInChunk = inputLength;
+ System.arraycopy(mBuffered, mBufferedOffset, chunk, 0, mBufferedLength);
+ System.arraycopy(input, inputOffset, chunk, mBufferedLength, inputLength);
+ }
+ }
+ // Update input array references to reflect that some of its bytes are now in mBuffered.
+ inputOffset += inputBytesInChunk;
+ inputLength -= inputBytesInChunk;
+
+ OperationResult opResult = mKeyStoreOperation.update(chunk);
+ if (opResult == null) {
+ throw new KeyStoreConnectException();
+ } else if (opResult.resultCode != KeyStore.NO_ERROR) {
+ throw KeymasterUtils.getExceptionForKeymasterError(opResult.resultCode);
+ }
+
+ if (opResult.inputConsumed == chunk.length) {
+ // The whole chunk was consumed
+ mBuffered = EMPTY_BYTE_ARRAY;
+ mBufferedOffset = 0;
+ mBufferedLength = 0;
+ } else if (opResult.inputConsumed == 0) {
+ // Nothing was consumed. More input needed.
+ if (inputLength > 0) {
+ // More input is available, but it wasn't included into the previous chunk
+ // because the chunk reached its maximum permitted size.
+ // Shouldn't have happened.
+ throw new CryptoOperationException("Nothing consumed from max-sized chunk: "
+ + chunk.length + " bytes");
+ }
+ mBuffered = chunk;
+ mBufferedOffset = 0;
+ mBufferedLength = chunk.length;
+ } else if (opResult.inputConsumed < chunk.length) {
+ // The chunk was consumed only partially -- buffer the rest of the chunk
+ mBuffered = chunk;
+ mBufferedOffset = opResult.inputConsumed;
+ mBufferedLength = chunk.length - opResult.inputConsumed;
+ } else {
+ throw new CryptoOperationException("Consumed more than provided: "
+ + opResult.inputConsumed + ", provided: " + chunk.length);
+ }
+
+ if ((opResult.output != null) && (opResult.output.length > 0)) {
+ if (inputLength > 0) {
+ // More output might be produced in this loop -- buffer the current output
+ if (bufferedOutput == null) {
+ bufferedOutput = new ByteArrayOutputStream();
+ try {
+ bufferedOutput.write(opResult.output);
+ } catch (IOException e) {
+ throw new CryptoOperationException("Failed to buffer output", e);
+ }
+ }
+ } else {
+ // No more output will be produced in this loop
+ if (bufferedOutput == null) {
+ // No previously buffered output
+ return opResult.output;
+ } else {
+ // There was some previously buffered output
+ try {
+ bufferedOutput.write(opResult.output);
+ } catch (IOException e) {
+ throw new CryptoOperationException("Failed to buffer output", e);
+ }
+ return bufferedOutput.toByteArray();
+ }
+ }
+ }
+ }
+
+ if (bufferedOutput == null) {
+ // No output produced
+ return EMPTY_BYTE_ARRAY;
+ } else {
+ return bufferedOutput.toByteArray();
+ }
+ }
+
+ public byte[] doFinal(byte[] input, int inputOffset, int inputLength)
+ throws KeymasterException {
+ if (inputLength == 0) {
+ // No input provided -- simplify the rest of the code
+ input = EMPTY_BYTE_ARRAY;
+ inputOffset = 0;
+ }
+
+ byte[] updateOutput = null;
+ if ((mBufferedLength + inputLength) > mMaxChunkSize) {
+ updateOutput = update(input, inputOffset, inputLength);
+ inputOffset += inputLength;
+ inputLength = 0;
+ }
+ // All of available input fits into one chunk.
+
+ byte[] finalChunk;
+ if ((mBufferedLength == 0) && (inputOffset == 0)
+ && (inputLength == input.length)) {
+ // Nothing buffered and all of input array needs to be fed into the finish operation.
+ finalChunk = input;
+ } else {
+ // Need to combine buffered data with input data into one array.
+ finalChunk = new byte[mBufferedLength + inputLength];
+ System.arraycopy(mBuffered, mBufferedOffset, finalChunk, 0, mBufferedLength);
+ System.arraycopy(input, inputOffset, finalChunk, mBufferedLength, inputLength);
+ }
+ mBuffered = EMPTY_BYTE_ARRAY;
+ mBufferedLength = 0;
+ mBufferedOffset = 0;
+
+ OperationResult opResult = mKeyStoreOperation.finish(finalChunk);
+ if (opResult == null) {
+ throw new KeyStoreConnectException();
+ } else if (opResult.resultCode != KeyStore.NO_ERROR) {
+ throw KeymasterUtils.getExceptionForKeymasterError(opResult.resultCode);
+ }
+
+ if (opResult.inputConsumed != finalChunk.length) {
+ throw new CryptoOperationException("Unexpected number of bytes consumed by finish: "
+ + opResult.inputConsumed + " instead of " + finalChunk.length);
+ }
+
+ // Return the concatenation of the output of update and finish.
+ byte[] result;
+ byte[] finishOutput = opResult.output;
+ if ((updateOutput == null) || (updateOutput.length == 0)) {
+ result = finishOutput;
+ } else if ((finishOutput == null) || (finishOutput.length == 0)) {
+ result = updateOutput;
+ } else {
+ result = new byte[updateOutput.length + finishOutput.length];
+ System.arraycopy(updateOutput, 0, result, 0, updateOutput.length);
+ System.arraycopy(finishOutput, 0, result, updateOutput.length, finishOutput.length);
+ }
+ return (result != null) ? result : EMPTY_BYTE_ARRAY;
+ }
+}
diff --git a/keystore/java/android/security/KeyStoreHmacSpi.java b/keystore/java/android/security/KeyStoreHmacSpi.java
new file mode 100644
index 0000000..3080d7b
--- /dev/null
+++ b/keystore/java/android/security/KeyStoreHmacSpi.java
@@ -0,0 +1,174 @@
+package android.security;
+
+import android.os.IBinder;
+import android.security.keymaster.KeymasterArguments;
+import android.security.keymaster.KeymasterDefs;
+import android.security.keymaster.OperationResult;
+
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.spec.AlgorithmParameterSpec;
+
+import javax.crypto.MacSpi;
+
+/**
+ * {@link MacSpi} which provides HMAC implementations backed by Android KeyStore.
+ *
+ * @hide
+ */
+public abstract class KeyStoreHmacSpi extends MacSpi {
+
+ public static class HmacSHA256 extends KeyStoreHmacSpi {
+ public HmacSHA256() {
+ super(KeyStoreKeyConstraints.Digest.SHA256, 256 / 8);
+ }
+ }
+
+ private final KeyStore mKeyStore = KeyStore.getInstance();
+ private final @KeyStoreKeyConstraints.DigestEnum int mDigest;
+ private final int mMacSizeBytes;
+
+ private String mKeyAliasInKeyStore;
+
+ // The fields below are reset by the engineReset operation.
+ private KeyStoreCryptoOperationChunkedStreamer mChunkedStreamer;
+ private IBinder mOperationToken;
+
+ protected KeyStoreHmacSpi(@KeyStoreKeyConstraints.DigestEnum int digest, int macSizeBytes) {
+ mDigest = digest;
+ mMacSizeBytes = macSizeBytes;
+ }
+
+ @Override
+ protected int engineGetMacLength() {
+ return mMacSizeBytes;
+ }
+
+ @Override
+ protected void engineInit(Key key, AlgorithmParameterSpec params) throws InvalidKeyException,
+ InvalidAlgorithmParameterException {
+ if (key == null) {
+ throw new InvalidKeyException("key == null");
+ } else if (!(key instanceof KeyStoreSecretKey)) {
+ throw new InvalidKeyException(
+ "Only Android KeyStore secret keys supported. Key: " + key);
+ }
+
+ if (params != null) {
+ throw new InvalidAlgorithmParameterException(
+ "Unsupported algorithm parameters: " + params);
+ }
+
+ mKeyAliasInKeyStore = ((KeyStoreSecretKey) key).getAlias();
+ engineReset();
+ }
+
+ @Override
+ protected void engineReset() {
+ IBinder operationToken = mOperationToken;
+ if (operationToken != null) {
+ mOperationToken = null;
+ mKeyStore.abort(operationToken);
+ }
+ mChunkedStreamer = null;
+
+ KeymasterArguments keymasterArgs = new KeymasterArguments();
+ keymasterArgs.addInt(KeymasterDefs.KM_TAG_DIGEST, mDigest);
+
+ OperationResult opResult = mKeyStore.begin(mKeyAliasInKeyStore,
+ KeymasterDefs.KM_PURPOSE_SIGN,
+ true,
+ keymasterArgs,
+ null,
+ new KeymasterArguments());
+ if (opResult == null) {
+ throw new KeyStoreConnectException();
+ } else if (opResult.resultCode != KeyStore.NO_ERROR) {
+ throw new CryptoOperationException("Failed to start keystore operation",
+ KeymasterUtils.getExceptionForKeymasterError(opResult.resultCode));
+ }
+ mOperationToken = opResult.token;
+ if (mOperationToken == null) {
+ throw new CryptoOperationException("Keystore returned null operation token");
+ }
+ mChunkedStreamer = new KeyStoreCryptoOperationChunkedStreamer(
+ new KeyStoreStreamingConsumer(mKeyStore, mOperationToken));
+ }
+
+ @Override
+ protected void engineUpdate(byte input) {
+ engineUpdate(new byte[] {input}, 0, 1);
+ }
+
+ @Override
+ protected void engineUpdate(byte[] input, int offset, int len) {
+ if (mChunkedStreamer == null) {
+ throw new IllegalStateException("Not initialized");
+ }
+
+ byte[] output;
+ try {
+ output = mChunkedStreamer.update(input, offset, len);
+ } catch (KeymasterException e) {
+ throw new CryptoOperationException("Keystore operation failed", e);
+ }
+ if ((output != null) && (output.length != 0)) {
+ throw new CryptoOperationException("Update operation unexpectedly produced output");
+ }
+ }
+
+ @Override
+ protected byte[] engineDoFinal() {
+ if (mChunkedStreamer == null) {
+ throw new IllegalStateException("Not initialized");
+ }
+
+ byte[] result;
+ try {
+ result = mChunkedStreamer.doFinal(null, 0, 0);
+ } catch (KeymasterException e) {
+ throw new CryptoOperationException("Keystore operation failed", e);
+ }
+
+ engineReset();
+ return result;
+ }
+
+ @Override
+ public void finalize() throws Throwable {
+ try {
+ IBinder operationToken = mOperationToken;
+ if (operationToken != null) {
+ mOperationToken = null;
+ mKeyStore.abort(operationToken);
+ }
+ } finally {
+ super.finalize();
+ }
+ }
+
+ /**
+ * KeyStore-backed consumer of {@code MacSpi}'s chunked stream.
+ */
+ private static class KeyStoreStreamingConsumer
+ implements KeyStoreCryptoOperationChunkedStreamer.KeyStoreOperation {
+ private final KeyStore mKeyStore;
+ private final IBinder mOperationToken;
+
+ private KeyStoreStreamingConsumer(KeyStore keyStore, IBinder operationToken) {
+ mKeyStore = keyStore;
+ mOperationToken = operationToken;
+ }
+
+ @Override
+ public OperationResult update(byte[] input) {
+ return mKeyStore.update(mOperationToken, null, input);
+ }
+
+ @Override
+ public OperationResult finish(byte[] input) {
+ return mKeyStore.finish(mOperationToken, null, input);
+ }
+ }
+}
diff --git a/keystore/java/android/security/KeyStoreKeyConstraints.java b/keystore/java/android/security/KeyStoreKeyConstraints.java
new file mode 100644
index 0000000..b5e2436
--- /dev/null
+++ b/keystore/java/android/security/KeyStoreKeyConstraints.java
@@ -0,0 +1,471 @@
+package android.security;
+
+import android.annotation.IntDef;
+import android.security.keymaster.KeymasterDefs;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Locale;
+
+/**
+ * Constraints for {@code AndroidKeyStore} keys.
+ *
+ * @hide
+ */
+public abstract class KeyStoreKeyConstraints {
+ private KeyStoreKeyConstraints() {}
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(flag=true, value={Purpose.ENCRYPT, Purpose.DECRYPT, Purpose.SIGN, Purpose.VERIFY})
+ public @interface PurposeEnum {}
+
+ /**
+ * Purpose of key.
+ */
+ public static abstract class Purpose {
+ private Purpose() {}
+
+ /**
+ * Purpose: encryption.
+ */
+ public static final int ENCRYPT = 1 << 0;
+
+ /**
+ * Purpose: decryption.
+ */
+ public static final int DECRYPT = 1 << 1;
+
+ /**
+ * Purpose: signing.
+ */
+ public static final int SIGN = 1 << 2;
+
+ /**
+ * Purpose: signature verification.
+ */
+ public static final int VERIFY = 1 << 3;
+
+ /**
+ * Number of flags defined above. Needs to be kept in sync with the flags above.
+ */
+ private static final int VALUE_COUNT = 4;
+
+ /**
+ * @hide
+ */
+ public static int toKeymaster(@PurposeEnum int purpose) {
+ switch (purpose) {
+ case ENCRYPT:
+ return KeymasterDefs.KM_PURPOSE_ENCRYPT;
+ case DECRYPT:
+ return KeymasterDefs.KM_PURPOSE_DECRYPT;
+ case SIGN:
+ return KeymasterDefs.KM_PURPOSE_SIGN;
+ case VERIFY:
+ return KeymasterDefs.KM_PURPOSE_VERIFY;
+ default:
+ throw new IllegalArgumentException("Unknown purpose: " + purpose);
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public static @PurposeEnum int fromKeymaster(int purpose) {
+ switch (purpose) {
+ case KeymasterDefs.KM_PURPOSE_ENCRYPT:
+ return ENCRYPT;
+ case KeymasterDefs.KM_PURPOSE_DECRYPT:
+ return DECRYPT;
+ case KeymasterDefs.KM_PURPOSE_SIGN:
+ return SIGN;
+ case KeymasterDefs.KM_PURPOSE_VERIFY:
+ return VERIFY;
+ default:
+ throw new IllegalArgumentException("Unknown purpose: " + purpose);
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public static int[] allToKeymaster(int purposes) {
+ int[] result = new int[VALUE_COUNT];
+ int resultCount = 0;
+ int purpose = 1;
+ for (int i = 0; i < 32; i++) {
+ if ((purposes & 1) != 0) {
+ result[resultCount] = toKeymaster(purpose);
+ resultCount++;
+ }
+ purposes >>>= 1;
+ purpose <<= 1;
+ if (purposes == 0) {
+ break;
+ }
+ }
+ return Arrays.copyOf(result, resultCount);
+ }
+
+ /**
+ * @hide
+ */
+ public static @PurposeEnum int allFromKeymaster(Collection<Integer> purposes) {
+ @PurposeEnum int result = 0;
+ for (int keymasterPurpose : purposes) {
+ result |= fromKeymaster(keymasterPurpose);
+ }
+ return result;
+ }
+ }
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({Algorithm.AES, Algorithm.HMAC})
+ public @interface AlgorithmEnum {}
+
+ /**
+ * Key algorithm.
+ */
+ public static abstract class Algorithm {
+ private Algorithm() {}
+
+ /**
+ * Key algorithm: AES.
+ */
+ public static final int AES = 0;
+
+ /**
+ * Key algorithm: HMAC.
+ */
+ public static final int HMAC = 1;
+
+ /**
+ * @hide
+ */
+ public static int toKeymaster(@AlgorithmEnum int algorithm) {
+ switch (algorithm) {
+ case AES:
+ return KeymasterDefs.KM_ALGORITHM_AES;
+ case HMAC:
+ return KeymasterDefs.KM_ALGORITHM_HMAC;
+ default:
+ throw new IllegalArgumentException("Unknown algorithm: " + algorithm);
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public static @AlgorithmEnum int fromKeymaster(int algorithm) {
+ switch (algorithm) {
+ case KeymasterDefs.KM_ALGORITHM_AES:
+ return AES;
+ case KeymasterDefs.KM_ALGORITHM_HMAC:
+ return HMAC;
+ default:
+ throw new IllegalArgumentException("Unknown algorithm: " + algorithm);
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public static String toString(@AlgorithmEnum int algorithm) {
+ switch (algorithm) {
+ case AES:
+ return "AES";
+ case HMAC:
+ return "HMAC";
+ default:
+ throw new IllegalArgumentException("Unknown algorithm: " + algorithm);
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public static @AlgorithmEnum int fromJCASecretKeyAlgorithm(String algorithm) {
+ if (algorithm == null) {
+ throw new NullPointerException("algorithm == null");
+ } else if ("AES".equalsIgnoreCase(algorithm)) {
+ return AES;
+ } else if (algorithm.toLowerCase(Locale.US).startsWith("hmac")) {
+ return HMAC;
+ } else {
+ throw new IllegalArgumentException(
+ "Unsupported secret key algorithm: " + algorithm);
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public static String toJCASecretKeyAlgorithm(@AlgorithmEnum int algorithm,
+ @DigestEnum Integer digest) {
+ switch (algorithm) {
+ case AES:
+ return "AES";
+ case HMAC:
+ if (digest == null) {
+ throw new IllegalArgumentException("HMAC digest not specified");
+ }
+ switch (digest) {
+ case Digest.SHA256:
+ return "HmacSHA256";
+ default:
+ throw new IllegalArgumentException(
+ "Unsupported HMAC digest: " + digest);
+ }
+ default:
+ throw new IllegalArgumentException("Unsupported key algorithm: " + algorithm);
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public static String toJCAKeyPairAlgorithm(@AlgorithmEnum int algorithm) {
+ switch (algorithm) {
+ default:
+ throw new IllegalArgumentException("Unsupported key alorithm: " + algorithm);
+ }
+ }
+ }
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({Padding.NONE, Padding.ZERO, Padding.PKCS7})
+ public @interface PaddingEnum {}
+
+ /**
+ * Padding for signing and encryption.
+ */
+ public static abstract class Padding {
+ private Padding() {}
+
+ /**
+ * No padding.
+ */
+ public static final int NONE = 0;
+
+ /**
+ * Pad with zeros.
+ */
+ public static final int ZERO = 1;
+
+ /**
+ * PKCS#7 padding.
+ */
+ public static final int PKCS7 = 2;
+
+ /**
+ * @hide
+ */
+ public static int toKeymaster(int padding) {
+ switch (padding) {
+ case NONE:
+ return KeymasterDefs.KM_PAD_NONE;
+ case ZERO:
+ return KeymasterDefs.KM_PAD_ZERO;
+ case PKCS7:
+ return KeymasterDefs.KM_PAD_PKCS7;
+ default:
+ throw new IllegalArgumentException("Unknown padding: " + padding);
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public static @PaddingEnum int fromKeymaster(int padding) {
+ switch (padding) {
+ case KeymasterDefs.KM_PAD_NONE:
+ return NONE;
+ case KeymasterDefs.KM_PAD_ZERO:
+ return ZERO;
+ case KeymasterDefs.KM_PAD_PKCS7:
+ return PKCS7;
+ default:
+ throw new IllegalArgumentException("Unknown padding: " + padding);
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public static String toString(@PaddingEnum int padding) {
+ switch (padding) {
+ case NONE:
+ return "NONE";
+ case ZERO:
+ return "ZERO";
+ case PKCS7:
+ return "PKCS#7";
+ default:
+ throw new IllegalArgumentException("Unknown padding: " + padding);
+ }
+ }
+ }
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({Digest.NONE, Digest.SHA256})
+ public @interface DigestEnum {}
+
+ /**
+ * Digests that can be used with a key when signing or generating Message Authentication
+ * Codes (MACs).
+ */
+ public static abstract class Digest {
+ private Digest() {}
+
+ /**
+ * No digest: sign/authenticate the raw message.
+ */
+ public static final int NONE = 0;
+
+ /**
+ * SHA-256 digest.
+ */
+ public static final int SHA256 = 1;
+
+ /**
+ * @hide
+ */
+ public static String toString(@DigestEnum int digest) {
+ switch (digest) {
+ case NONE:
+ return "NONE";
+ case SHA256:
+ return "SHA256";
+ default:
+ throw new IllegalArgumentException("Unknown digest: " + digest);
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public static int toKeymaster(@DigestEnum int digest) {
+ switch (digest) {
+ case NONE:
+ return KeymasterDefs.KM_DIGEST_NONE;
+ case SHA256:
+ return KeymasterDefs.KM_DIGEST_SHA_2_256;
+ default:
+ throw new IllegalArgumentException("Unknown digest: " + digest);
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public static @DigestEnum int fromKeymaster(int digest) {
+ switch (digest) {
+ case KeymasterDefs.KM_DIGEST_NONE:
+ return NONE;
+ case KeymasterDefs.KM_DIGEST_SHA_2_256:
+ return SHA256;
+ default:
+ throw new IllegalArgumentException("Unknown digest: " + digest);
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public static @DigestEnum Integer fromJCASecretKeyAlgorithm(String algorithm) {
+ String algorithmLower = algorithm.toLowerCase(Locale.US);
+ if (algorithmLower.startsWith("hmac")) {
+ if ("hmacsha256".equals(algorithmLower)) {
+ return SHA256;
+ } else {
+ throw new IllegalArgumentException("Unsupported digest: "
+ + algorithmLower.substring("hmac".length()));
+ }
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public static String toJCASignatureAlgorithmDigest(@DigestEnum int digest) {
+ switch (digest) {
+ case NONE:
+ return "NONE";
+ case SHA256:
+ return "SHA256";
+ default:
+ throw new IllegalArgumentException("Unknown digest: " + digest);
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public static Integer getOutputSizeBytes(@DigestEnum int digest) {
+ switch (digest) {
+ case NONE:
+ return null;
+ case SHA256:
+ return 256 / 8;
+ default:
+ throw new IllegalArgumentException("Unknown digest: " + digest);
+ }
+ }
+ }
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({BlockMode.ECB})
+ public @interface BlockModeEnum {}
+
+ /**
+ * Block modes that can be used when encrypting/decrypting using a key.
+ */
+ public static abstract class BlockMode {
+ private BlockMode() {}
+
+ /**
+ * Electronic Codebook (ECB) block mode.
+ */
+ public static final int ECB = 0;
+
+ /**
+ * @hide
+ */
+ public static int toKeymaster(@BlockModeEnum int mode) {
+ switch (mode) {
+ case ECB:
+ return KeymasterDefs.KM_MODE_ECB;
+ default:
+ throw new IllegalArgumentException("Unknown block mode: " + mode);
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public static @BlockModeEnum int fromKeymaster(int mode) {
+ switch (mode) {
+ case KeymasterDefs.KM_MODE_ECB:
+ return ECB;
+ default:
+ throw new IllegalArgumentException("Unknown block mode: " + mode);
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public static String toString(@BlockModeEnum int mode) {
+ switch (mode) {
+ case ECB:
+ return "ECB";
+ default:
+ throw new IllegalArgumentException("Unknown block mode: " + mode);
+ }
+ }
+ }
+}
diff --git a/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java b/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java
new file mode 100644
index 0000000..f1f9436
--- /dev/null
+++ b/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java
@@ -0,0 +1,197 @@
+package android.security;
+
+import android.security.keymaster.KeyCharacteristics;
+import android.security.keymaster.KeymasterArguments;
+import android.security.keymaster.KeymasterDefs;
+
+import java.security.InvalidAlgorithmParameterException;
+import java.security.SecureRandom;
+import java.security.spec.AlgorithmParameterSpec;
+
+import javax.crypto.KeyGeneratorSpi;
+import javax.crypto.SecretKey;
+
+/**
+ * {@link KeyGeneratorSpi} backed by Android KeyStore.
+ *
+ * @hide
+ */
+public abstract class KeyStoreKeyGeneratorSpi extends KeyGeneratorSpi {
+
+ public static class AES extends KeyStoreKeyGeneratorSpi {
+ public AES() {
+ super(KeyStoreKeyConstraints.Algorithm.AES, 128);
+ }
+ }
+
+ public static class HmacSHA256 extends KeyStoreKeyGeneratorSpi {
+ public HmacSHA256() {
+ super(KeyStoreKeyConstraints.Algorithm.HMAC,
+ KeyStoreKeyConstraints.Digest.SHA256,
+ KeyStoreKeyConstraints.Digest.getOutputSizeBytes(
+ KeyStoreKeyConstraints.Digest.SHA256) * 8);
+ }
+ }
+
+ private final KeyStore mKeyStore = KeyStore.getInstance();
+ private final @KeyStoreKeyConstraints.AlgorithmEnum int mAlgorithm;
+ private final @KeyStoreKeyConstraints.AlgorithmEnum Integer mDigest;
+ private final int mDefaultKeySizeBits;
+
+ private KeyGeneratorSpec mSpec;
+ private SecureRandom mRng;
+
+ protected KeyStoreKeyGeneratorSpi(
+ @KeyStoreKeyConstraints.AlgorithmEnum int algorithm,
+ int defaultKeySizeBits) {
+ this(algorithm, null, defaultKeySizeBits);
+ }
+
+ protected KeyStoreKeyGeneratorSpi(
+ @KeyStoreKeyConstraints.AlgorithmEnum int algorithm,
+ @KeyStoreKeyConstraints.DigestEnum Integer digest,
+ int defaultKeySizeBits) {
+ mAlgorithm = algorithm;
+ mDigest = digest;
+ mDefaultKeySizeBits = defaultKeySizeBits;
+ }
+
+ @Override
+ protected SecretKey engineGenerateKey() {
+ KeyGeneratorSpec spec = mSpec;
+ if (spec == null) {
+ throw new IllegalStateException("Not initialized");
+ }
+
+ if ((spec.isEncryptionRequired())
+ && (mKeyStore.state() != KeyStore.State.UNLOCKED)) {
+ throw new IllegalStateException(
+ "Android KeyStore must be in initialized and unlocked state if encryption is"
+ + " required");
+ }
+
+ KeymasterArguments args = new KeymasterArguments();
+ args.addInt(KeymasterDefs.KM_TAG_ALGORITHM,
+ KeyStoreKeyConstraints.Algorithm.toKeymaster(mAlgorithm));
+ if (mDigest != null) {
+ args.addInt(KeymasterDefs.KM_TAG_DIGEST,
+ KeyStoreKeyConstraints.Digest.toKeymaster(mDigest));
+ }
+ if (mAlgorithm == KeyStoreKeyConstraints.Algorithm.HMAC) {
+ if (mDigest == null) {
+ throw new IllegalStateException("Digest algorithm must be specified for key"
+ + " algorithm " + KeyStoreKeyConstraints.Algorithm.toString(mAlgorithm));
+ }
+ Integer digestOutputSizeBytes =
+ KeyStoreKeyConstraints.Digest.getOutputSizeBytes(mDigest);
+ if (digestOutputSizeBytes != null) {
+ // TODO: Remove MAC length constraint once Keymaster API no longer requires it.
+ // TODO: Switch to bits instead of bytes, once this is fixed in Keymaster
+ args.addInt(KeymasterDefs.KM_TAG_MAC_LENGTH, digestOutputSizeBytes);
+ }
+ }
+ int keySizeBits = (spec.getKeySize() != null) ? spec.getKeySize() : mDefaultKeySizeBits;
+ args.addInt(KeymasterDefs.KM_TAG_KEY_SIZE, keySizeBits);
+ @KeyStoreKeyConstraints.PurposeEnum int purposes = (spec.getPurposes() != null)
+ ? spec.getPurposes()
+ : (KeyStoreKeyConstraints.Purpose.ENCRYPT
+ | KeyStoreKeyConstraints.Purpose.DECRYPT
+ | KeyStoreKeyConstraints.Purpose.SIGN
+ | KeyStoreKeyConstraints.Purpose.VERIFY);
+ for (int keymasterPurpose :
+ KeyStoreKeyConstraints.Purpose.allToKeymaster(purposes)) {
+ args.addInt(KeymasterDefs.KM_TAG_PURPOSE, keymasterPurpose);
+ }
+ if (spec.getBlockMode() != null) {
+ args.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE,
+ KeyStoreKeyConstraints.BlockMode.toKeymaster(spec.getBlockMode()));
+ }
+ if (spec.getPadding() != null) {
+ args.addInt(KeymasterDefs.KM_TAG_PADDING,
+ KeyStoreKeyConstraints.Padding.toKeymaster(spec.getPadding()));
+ }
+ if (spec.getMaxUsesPerBoot() != null) {
+ args.addInt(KeymasterDefs.KM_TAG_MAX_USES_PER_BOOT, spec.getMaxUsesPerBoot());
+ }
+ if (spec.getMinSecondsBetweenOperations() != null) {
+ args.addInt(KeymasterDefs.KM_TAG_MIN_SECONDS_BETWEEN_OPS,
+ spec.getMinSecondsBetweenOperations());
+ }
+ if (spec.getUserAuthenticators().isEmpty()) {
+ args.addBoolean(KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED);
+ } else {
+ // TODO: Pass-in user authenticator IDs once the Keymaster API has stabilized
+// for (int userAuthenticatorId : spec.getUserAuthenticators()) {
+// args.addInt(KeymasterDefs.KM_TAG_USER_AUTH_ID, userAuthenticatorId);
+// }
+ }
+ if (spec.getUserAuthenticationValidityDurationSeconds() != null) {
+ args.addInt(KeymasterDefs.KM_TAG_AUTH_TIMEOUT,
+ spec.getUserAuthenticationValidityDurationSeconds());
+ }
+ if (spec.getKeyValidityStart() != null) {
+ args.addDate(KeymasterDefs.KM_TAG_ACTIVE_DATETIME, spec.getKeyValidityStart());
+ }
+ if (spec.getKeyValidityForOriginationEnd() != null) {
+ args.addDate(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME,
+ spec.getKeyValidityForOriginationEnd());
+ }
+ if (spec.getKeyValidityForConsumptionEnd() != null) {
+ args.addDate(KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME,
+ spec.getKeyValidityForConsumptionEnd());
+ }
+
+ if (((purposes & KeyStoreKeyConstraints.Purpose.ENCRYPT) != 0)
+ || ((purposes & KeyStoreKeyConstraints.Purpose.DECRYPT) != 0)) {
+ // Permit caller-specified IV. This is needed due to the Cipher abstraction.
+ args.addBoolean(KeymasterDefs.KM_TAG_CALLER_NONCE);
+ }
+
+ byte[] additionalEntropy = null;
+ SecureRandom rng = mRng;
+ if (rng != null) {
+ additionalEntropy = new byte[(keySizeBits + 7) / 8];
+ rng.nextBytes(additionalEntropy);
+ }
+
+ int flags = spec.getFlags();
+ String keyAliasInKeystore = Credentials.USER_SECRET_KEY + spec.getKeystoreAlias();
+ int errorCode = mKeyStore.generateKey(
+ keyAliasInKeystore, args, additionalEntropy, flags, new KeyCharacteristics());
+ if (errorCode != KeyStore.NO_ERROR) {
+ throw new CryptoOperationException("Failed to generate key",
+ KeymasterUtils.getExceptionForKeymasterError(errorCode));
+ }
+ String keyAlgorithmJCA =
+ KeyStoreKeyConstraints.Algorithm.toJCASecretKeyAlgorithm(mAlgorithm, mDigest);
+ return new KeyStoreSecretKey(keyAliasInKeystore, keyAlgorithmJCA);
+ }
+
+ @Override
+ protected void engineInit(SecureRandom random) {
+ throw new UnsupportedOperationException("Cannot initialize without an "
+ + KeyGeneratorSpec.class.getName() + " parameter");
+ }
+
+ @Override
+ protected void engineInit(AlgorithmParameterSpec params, SecureRandom random)
+ throws InvalidAlgorithmParameterException {
+ if ((params == null) || (!(params instanceof KeyGeneratorSpec))) {
+ throw new InvalidAlgorithmParameterException("Cannot initialize without an "
+ + KeyGeneratorSpec.class.getName() + " parameter");
+ }
+ KeyGeneratorSpec spec = (KeyGeneratorSpec) params;
+ if (spec.getKeystoreAlias() == null) {
+ throw new InvalidAlgorithmParameterException("KeyStore entry alias not provided");
+ }
+
+ mSpec = spec;
+ mRng = random;
+ }
+
+ @Override
+ protected void engineInit(int keySize, SecureRandom random) {
+ throw new UnsupportedOperationException("Cannot initialize without a "
+ + KeyGeneratorSpec.class.getName() + " parameter");
+ }
+}
diff --git a/keystore/java/android/security/KeyStoreParameter.java b/keystore/java/android/security/KeyStoreParameter.java
index 2eeb6ad..2428c2a 100644
--- a/keystore/java/android/security/KeyStoreParameter.java
+++ b/keystore/java/android/security/KeyStoreParameter.java
@@ -20,6 +20,10 @@
import java.security.KeyPairGenerator;
import java.security.KeyStore.ProtectionParameter;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Set;
/**
* This provides the optional parameters that can be specified for
@@ -43,9 +47,51 @@
*/
public final class KeyStoreParameter implements ProtectionParameter {
private int mFlags;
+ private final Date mKeyValidityStart;
+ private final Date mKeyValidityForOriginationEnd;
+ private final Date mKeyValidityForConsumptionEnd;
+ private final @KeyStoreKeyConstraints.PurposeEnum Integer mPurposes;
+ private final @KeyStoreKeyConstraints.AlgorithmEnum Integer mAlgorithm;
+ private final @KeyStoreKeyConstraints.PaddingEnum Integer mPadding;
+ private final @KeyStoreKeyConstraints.DigestEnum Integer mDigest;
+ private final @KeyStoreKeyConstraints.BlockModeEnum Integer mBlockMode;
+ private final Integer mMinSecondsBetweenOperations;
+ private final Integer mMaxUsesPerBoot;
+ private final Set<Integer> mUserAuthenticators;
+ private final Integer mUserAuthenticationValidityDurationSeconds;
- private KeyStoreParameter(int flags) {
+ private KeyStoreParameter(int flags, Date keyValidityStart,
+ Date keyValidityForOriginationEnd, Date keyValidityForConsumptionEnd,
+ @KeyStoreKeyConstraints.PurposeEnum Integer purposes,
+ @KeyStoreKeyConstraints.AlgorithmEnum Integer algorithm,
+ @KeyStoreKeyConstraints.PaddingEnum Integer padding,
+ @KeyStoreKeyConstraints.DigestEnum Integer digest,
+ @KeyStoreKeyConstraints.BlockModeEnum Integer blockMode,
+ Integer minSecondsBetweenOperations,
+ Integer maxUsesPerBoot,
+ Set<Integer> userAuthenticators,
+ Integer userAuthenticationValidityDurationSeconds) {
+ if ((userAuthenticationValidityDurationSeconds != null)
+ && (userAuthenticationValidityDurationSeconds < 0)) {
+ throw new IllegalArgumentException(
+ "userAuthenticationValidityDurationSeconds must not be negative");
+ }
+
mFlags = flags;
+ mKeyValidityStart = keyValidityStart;
+ mKeyValidityForOriginationEnd = keyValidityForOriginationEnd;
+ mKeyValidityForConsumptionEnd = keyValidityForConsumptionEnd;
+ mPurposes = purposes;
+ mAlgorithm = algorithm;
+ mPadding = padding;
+ mDigest = digest;
+ mBlockMode = blockMode;
+ mMinSecondsBetweenOperations = minSecondsBetweenOperations;
+ mMaxUsesPerBoot = maxUsesPerBoot;
+ mUserAuthenticators = (userAuthenticators != null)
+ ? new HashSet<Integer>(userAuthenticators)
+ : Collections.<Integer>emptySet();
+ mUserAuthenticationValidityDurationSeconds = userAuthenticationValidityDurationSeconds;
}
/**
@@ -64,6 +110,141 @@
}
/**
+ * Gets the time instant before which the key is not yet valid.
+ *
+ * @return instant or {@code null} if not restricted.
+ * @hide
+ */
+ public Date getKeyValidityStart() {
+ return mKeyValidityStart;
+ }
+
+ /**
+ * Gets the time instant after which the key is no long valid for decryption and verification.
+ *
+ * @return instant or {@code null} if not restricted.
+ *
+ * @hide
+ */
+ public Date getKeyValidityForConsumptionEnd() {
+ return mKeyValidityForConsumptionEnd;
+ }
+
+ /**
+ * Gets the time instant after which the key is no long valid for encryption and signing.
+ *
+ * @return instant or {@code null} if not restricted.
+ *
+ * @hide
+ */
+ public Date getKeyValidityForOriginationEnd() {
+ return mKeyValidityForOriginationEnd;
+ }
+
+ /**
+ * Gets the set of purposes for which the key can be used to the provided set of purposes.
+ *
+ * @return set of purposes or {@code null} if the key can be used for any purpose.
+ *
+ * @hide
+ */
+ public @KeyStoreKeyConstraints.PurposeEnum Integer getPurposes() {
+ return mPurposes;
+ }
+
+ /**
+ * Gets the algorithm to which the key is restricted.
+ *
+ * @return algorithm or {@code null} if it's not restricted.
+ * @hide
+ */
+ public @KeyStoreKeyConstraints.AlgorithmEnum Integer getAlgorithm() {
+ return mAlgorithm;
+ }
+
+ /**
+ * Gets the padding scheme to which the key is restricted.
+ *
+ * @return padding scheme or {@code null} if the padding scheme is not restricted.
+ *
+ * @hide
+ */
+ public @KeyStoreKeyConstraints.PaddingEnum Integer getPadding() {
+ return mPadding;
+ }
+
+ /**
+ * Gets the digest to which the key is restricted when generating Message Authentication Codes
+ * (MACs).
+ *
+ * @return digest or {@code null} if the digest is not restricted.
+ *
+ * @hide
+ */
+ public @KeyStoreKeyConstraints.DigestEnum Integer getDigest() {
+ return mDigest;
+ }
+
+ /**
+ * Gets the block mode to which the key is restricted when used for encryption or decryption.
+ *
+ * @return block more or {@code null} if block mode is not restricted.
+ *
+ * @hide
+ */
+ public @KeyStoreKeyConstraints.BlockModeEnum Integer getBlockMode() {
+ return mBlockMode;
+ }
+
+ /**
+ * Gets the minimum number of seconds that must expire since the most recent use of the key
+ * before it can be used again.
+ *
+ * @return number of seconds or {@code null} if there is no restriction on how frequently a key
+ * can be used.
+ *
+ * @hide
+ */
+ public Integer getMinSecondsBetweenOperations() {
+ return mMinSecondsBetweenOperations;
+ }
+
+ /**
+ * Gets the number of times the key can be used without rebooting the device.
+ *
+ * @return maximum number of times or {@code null} if there is no restriction.
+ * @hide
+ */
+ public Integer getMaxUsesPerBoot() {
+ return mMaxUsesPerBoot;
+ }
+
+ /**
+ * Gets the user authenticators which protect access to this key. The key can only be used iff
+ * the user has authenticated to at least one of these user authenticators.
+ *
+ * @return user authenticators or empty set if the key can be used without user authentication.
+ *
+ * @hide
+ */
+ public Set<Integer> getUserAuthenticators() {
+ return new HashSet<Integer>(mUserAuthenticators);
+ }
+
+ /**
+ * Gets the duration of time (seconds) for which this key can be used after the user
+ * successfully authenticates to one of the associated user authenticators.
+ *
+ * @return duration in seconds or {@code null} if not restricted. {@code 0} means authentication
+ * is required for every use of the key.
+ *
+ * @hide
+ */
+ public Integer getUserAuthenticationValidityDurationSeconds() {
+ return mUserAuthenticationValidityDurationSeconds;
+ }
+
+ /**
* Builder class for {@link KeyStoreParameter} objects.
* <p>
* This will build protection parameters for use with the
@@ -82,6 +263,18 @@
*/
public final static class Builder {
private int mFlags;
+ private Date mKeyValidityStart;
+ private Date mKeyValidityForOriginationEnd;
+ private Date mKeyValidityForConsumptionEnd;
+ private @KeyStoreKeyConstraints.PurposeEnum Integer mPurposes;
+ private @KeyStoreKeyConstraints.AlgorithmEnum Integer mAlgorithm;
+ private @KeyStoreKeyConstraints.PaddingEnum Integer mPadding;
+ private @KeyStoreKeyConstraints.DigestEnum Integer mDigest;
+ private @KeyStoreKeyConstraints.BlockModeEnum Integer mBlockMode;
+ private Integer mMinSecondsBetweenOperations;
+ private Integer mMaxUsesPerBoot;
+ private Set<Integer> mUserAuthenticators;
+ private Integer mUserAuthenticationValidityDurationSeconds;
/**
* Creates a new instance of the {@code Builder} with the given
@@ -113,13 +306,207 @@
}
/**
- * Builds the instance of the {@code KeyPairGeneratorSpec}.
+ * Sets the time instant before which the key is not yet valid.
+ *
+ * <b>By default, the key is valid at any instant.
+ *
+ * @see #setKeyValidityEnd(Date)
+ *
+ * @hide
+ */
+ public Builder setKeyValidityStart(Date startDate) {
+ mKeyValidityStart = startDate;
+ return this;
+ }
+
+ /**
+ * Sets the time instant after which the key is no longer valid.
+ *
+ * <b>By default, the key is valid at any instant.
+ *
+ * @see #setKeyValidityStart(Date)
+ * @see #setKeyValidityForConsumptionEnd(Date)
+ * @see #setKeyValidityForOriginationEnd(Date)
+ *
+ * @hide
+ */
+ public Builder setKeyValidityEnd(Date endDate) {
+ setKeyValidityForOriginationEnd(endDate);
+ setKeyValidityForConsumptionEnd(endDate);
+ return this;
+ }
+
+ /**
+ * Sets the time instant after which the key is no longer valid for encryption and signing.
+ *
+ * <b>By default, the key is valid at any instant.
+ *
+ * @see #setKeyValidityForConsumptionEnd(Date)
+ *
+ * @hide
+ */
+ public Builder setKeyValidityForOriginationEnd(Date endDate) {
+ mKeyValidityForOriginationEnd = endDate;
+ return this;
+ }
+
+ /**
+ * Sets the time instant after which the key is no longer valid for decryption and
+ * verification.
+ *
+ * <b>By default, the key is valid at any instant.
+ *
+ * @see #setKeyValidityForOriginationEnd(Date)
+ *
+ * @hide
+ */
+ public Builder setKeyValidityForConsumptionEnd(Date endDate) {
+ mKeyValidityForConsumptionEnd = endDate;
+ return this;
+ }
+
+ /**
+ * Restricts the purposes for which the key can be used to the provided set of purposes.
+ *
+ * <p>By default, the key can be used for encryption, decryption, signing, and verification.
+ *
+ * @hide
+ */
+ public Builder setPurposes(@KeyStoreKeyConstraints.PurposeEnum int purposes) {
+ mPurposes = purposes;
+ return this;
+ }
+
+ /**
+ * Sets the algorithm of the key.
+ *
+ * <p>The algorithm of symmetric keys can be deduced from the key itself. Thus, explicitly
+ * specifying the algorithm of symmetric keys using this method is not necessary.
+ *
+ * @hide
+ */
+ public Builder setAlgorithm(@KeyStoreKeyConstraints.AlgorithmEnum int algorithm) {
+ mAlgorithm = algorithm;
+ return this;
+ }
+
+ /**
+ * Restricts the key to being used only with the provided padding scheme. Attempts to use
+ * the key with any other padding will be rejected.
+ *
+ * <p>This restriction must be specified for keys which are used for encryption/decryption.
+ *
+ * @hide
+ */
+ public Builder setPadding(@KeyStoreKeyConstraints.PaddingEnum int padding) {
+ mPadding = padding;
+ return this;
+ }
+
+ /**
+ * Restricts the key to being used only with the provided digest when generating Message
+ * Authentication Codes (MACs). Attempts to use the key with any other digest will be
+ * rejected.
+ *
+ * <p>For MAC keys, the default is to restrict to the digest specified in the key algorithm
+ * name.
+ *
+ * @see java.security.Key#getAlgorithm()
+ *
+ * @hide
+ */
+ public Builder setDigest(@KeyStoreKeyConstraints.DigestEnum int digest) {
+ mDigest = digest;
+ return this;
+ }
+
+ /**
+ * Restricts the key to being used only with the provided block mode when encrypting or
+ * decrypting. Attempts to use the key with any other block modes will be rejected.
+ *
+ * <p>This restriction must be specified for keys which are used for encryption/decryption.
+ *
+ * @hide
+ */
+ public Builder setBlockMode(@KeyStoreKeyConstraints.BlockModeEnum int blockMode) {
+ mBlockMode = blockMode;
+ return this;
+ }
+
+ /**
+ * Sets the minimum number of seconds that must expire since the most recent use of the key
+ * before it can be used again.
+ *
+ * <p>By default, there is no restriction on how frequently a key can be used.
+ *
+ * @hide
+ */
+ public Builder setMinSecondsBetweenOperations(int seconds) {
+ mMinSecondsBetweenOperations = seconds;
+ return this;
+ }
+
+ /**
+ * Sets the maximum number of times a key can be used without rebooting the device.
+ *
+ * <p>By default, the key can be used for an unlimited number of times.
+ *
+ * @hide
+ */
+ public Builder setMaxUsesPerBoot(int count) {
+ mMaxUsesPerBoot = count;
+ return this;
+ }
+
+ /**
+ * Sets the user authenticators which protect access to this key. The key can only be used
+ * iff the user has authenticated to at least one of these user authenticators.
+ *
+ * <p>By default, the key can be used without user authentication.
+ *
+ * @param userAuthenticators user authenticators or empty list if this key can be accessed
+ * without user authentication.
+ *
+ * @see #setUserAuthenticationValidityDurationSeconds(int)
+ *
+ * @hide
+ */
+ public Builder setUserAuthenticators(Set<Integer> userAuthenticators) {
+ mUserAuthenticators =
+ (userAuthenticators != null) ? new HashSet<Integer>(userAuthenticators) : null;
+ return this;
+ }
+
+ /**
+ * Sets the duration of time (seconds) for which this key can be used after the user
+ * successfully authenticates to one of the associated user authenticators.
+ *
+ * <p>By default, the user needs to authenticate for every use of the key.
+ *
+ * @param seconds duration in seconds or {@code 0} if the user needs to authenticate for
+ * every use of the key.
+ *
+ * @see #setUserAuthenticators(Set)
+ *
+ * @hide
+ */
+ public Builder setUserAuthenticationValidityDurationSeconds(int seconds) {
+ mUserAuthenticationValidityDurationSeconds = seconds;
+ return this;
+ }
+
+ /**
+ * Builds the instance of the {@code KeyStoreParameter}.
*
* @throws IllegalArgumentException if a required field is missing
- * @return built instance of {@code KeyPairGeneratorSpec}
+ * @return built instance of {@code KeyStoreParameter}
*/
public KeyStoreParameter build() {
- return new KeyStoreParameter(mFlags);
+ return new KeyStoreParameter(mFlags, mKeyValidityStart,
+ mKeyValidityForOriginationEnd, mKeyValidityForConsumptionEnd, mPurposes,
+ mAlgorithm, mPadding, mDigest, mBlockMode, mMinSecondsBetweenOperations,
+ mMaxUsesPerBoot, mUserAuthenticators,
+ mUserAuthenticationValidityDurationSeconds);
}
}
}
diff --git a/keystore/java/android/security/KeyStoreSecretKey.java b/keystore/java/android/security/KeyStoreSecretKey.java
new file mode 100644
index 0000000..9410127
--- /dev/null
+++ b/keystore/java/android/security/KeyStoreSecretKey.java
@@ -0,0 +1,39 @@
+package android.security;
+
+import javax.crypto.SecretKey;
+
+/**
+ * {@link SecretKey} backed by keystore.
+ *
+ * @hide
+ */
+public class KeyStoreSecretKey implements SecretKey {
+ private final String mAlias;
+ private final String mAlgorithm;
+
+ public KeyStoreSecretKey(String alias, String algorithm) {
+ mAlias = alias;
+ mAlgorithm = algorithm;
+ }
+
+ String getAlias() {
+ return mAlias;
+ }
+
+ @Override
+ public String getAlgorithm() {
+ return mAlgorithm;
+ }
+
+ @Override
+ public String getFormat() {
+ // This key does not export its key material
+ return null;
+ }
+
+ @Override
+ public byte[] getEncoded() {
+ // This key does not export its key material
+ return null;
+ }
+}
diff --git a/keystore/java/android/security/KeymasterException.java b/keystore/java/android/security/KeymasterException.java
new file mode 100644
index 0000000..bc8198f
--- /dev/null
+++ b/keystore/java/android/security/KeymasterException.java
@@ -0,0 +1,20 @@
+package android.security;
+
+/**
+ * Keymaster exception.
+ *
+ * @hide
+ */
+public class KeymasterException extends Exception {
+
+ private final int mErrorCode;
+
+ public KeymasterException(int errorCode, String message) {
+ super(message);
+ mErrorCode = errorCode;
+ }
+
+ public int getErrorCode() {
+ return mErrorCode;
+ }
+}
diff --git a/keystore/java/android/security/KeymasterUtils.java b/keystore/java/android/security/KeymasterUtils.java
new file mode 100644
index 0000000..4f17586
--- /dev/null
+++ b/keystore/java/android/security/KeymasterUtils.java
@@ -0,0 +1,23 @@
+package android.security;
+
+import android.security.keymaster.KeymasterDefs;
+
+/**
+ * @hide
+ */
+public abstract class KeymasterUtils {
+ private KeymasterUtils() {}
+
+ public static KeymasterException getExceptionForKeymasterError(int keymasterErrorCode) {
+ switch (keymasterErrorCode) {
+ case KeymasterDefs.KM_ERROR_INVALID_AUTHORIZATION_TIMEOUT:
+ // The name of this parameter significantly differs between Keymaster and framework
+ // APIs. Use the framework wording to make life easier for developers.
+ return new KeymasterException(keymasterErrorCode,
+ "Invalid user authentication validity duration");
+ default:
+ return new KeymasterException(keymasterErrorCode,
+ KeymasterDefs.getErrorMessage(keymasterErrorCode));
+ }
+ }
+}
diff --git a/keystore/tests/src/android/security/AndroidKeyStoreTest.java b/keystore/tests/src/android/security/AndroidKeyStoreTest.java
index 9775e64..7a88dee 100644
--- a/keystore/tests/src/android/security/AndroidKeyStoreTest.java
+++ b/keystore/tests/src/android/security/AndroidKeyStoreTest.java
@@ -2127,7 +2127,7 @@
assertEquals("The keystore size should match expected", 2, mKeyStore.size());
assertAliases(new String[] { TEST_ALIAS_2, TEST_ALIAS_3 });
- assertTrue(mAndroidKeyStore.delKey(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_3));
+ assertTrue(mAndroidKeyStore.delete(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_3));
assertEquals("The keystore size should match expected", 1, mKeyStore.size());
assertAliases(new String[] { TEST_ALIAS_2 });
diff --git a/keystore/tests/src/android/security/KeyStoreTest.java b/keystore/tests/src/android/security/KeyStoreTest.java
index f755bb0..7468fb5e 100644
--- a/keystore/tests/src/android/security/KeyStoreTest.java
+++ b/keystore/tests/src/android/security/KeyStoreTest.java
@@ -717,7 +717,7 @@
RSAKeyGenParameterSpec.F4.longValue());
KeyCharacteristics outCharacteristics = new KeyCharacteristics();
- int result = mKeyStore.generateKey(name, args, 0, outCharacteristics);
+ int result = mKeyStore.generateKey(name, args, null, 0, outCharacteristics);
assertEquals("generateRsaKey should succeed", KeyStore.NO_ERROR, result);
return outCharacteristics;
}
@@ -726,6 +726,24 @@
generateRsaKey("test");
mKeyStore.delete("test");
}
+
+ public void testGenerateRsaWithEntropy() throws Exception {
+ byte[] entropy = new byte[] {1,2,3,4,5};
+ String name = "test";
+ KeymasterArguments args = new KeymasterArguments();
+ args.addInt(KeymasterDefs.KM_TAG_PURPOSE, KeymasterDefs.KM_PURPOSE_ENCRYPT);
+ args.addInt(KeymasterDefs.KM_TAG_PURPOSE, KeymasterDefs.KM_PURPOSE_DECRYPT);
+ args.addInt(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_RSA);
+ args.addInt(KeymasterDefs.KM_TAG_PADDING, KeymasterDefs.KM_PAD_NONE);
+ args.addInt(KeymasterDefs.KM_TAG_KEY_SIZE, 2048);
+ args.addLong(KeymasterDefs.KM_TAG_RSA_PUBLIC_EXPONENT,
+ RSAKeyGenParameterSpec.F4.longValue());
+
+ KeyCharacteristics outCharacteristics = new KeyCharacteristics();
+ int result = mKeyStore.generateKey(name, args, entropy, 0, outCharacteristics);
+ assertEquals("generateKey should succeed", KeyStore.NO_ERROR, result);
+ }
+
public void testGenerateAndDelete() throws Exception {
generateRsaKey("test");
assertTrue("delete should succeed", mKeyStore.delete("test"));
@@ -756,7 +774,7 @@
RSAKeyGenParameterSpec.F4.longValue());
KeyCharacteristics outCharacteristics = new KeyCharacteristics();
- int result = mKeyStore.generateKey(name, args, 0, outCharacteristics);
+ int result = mKeyStore.generateKey(name, args, null, 0, outCharacteristics);
assertEquals("generateRsaKey should succeed", KeyStore.NO_ERROR, result);
assertEquals("getKeyCharacteristics should fail without application ID",
KeymasterDefs.KM_ERROR_INVALID_KEY_BLOB,
@@ -790,13 +808,13 @@
args.addInt(KeymasterDefs.KM_TAG_MAC_LENGTH, 16);
KeyCharacteristics outCharacteristics = new KeyCharacteristics();
- int rc = mKeyStore.generateKey(name, args, 0, outCharacteristics);
+ int rc = mKeyStore.generateKey(name, args, null, 0, outCharacteristics);
assertEquals("Generate should succeed", KeyStore.NO_ERROR, rc);
KeymasterArguments out = new KeymasterArguments();
args = new KeymasterArguments();
OperationResult result = mKeyStore.begin(name, KeymasterDefs.KM_PURPOSE_ENCRYPT,
- true, args, out);
+ true, args, null, out);
IBinder token = result.token;
assertEquals("Begin should succeed", KeyStore.NO_ERROR, result.resultCode);
result = mKeyStore.update(token, null, new byte[] {0x01, 0x02, 0x03, 0x04});
@@ -826,7 +844,7 @@
private byte[] doOperation(String name, int purpose, byte[] in, KeymasterArguments beginArgs) {
KeymasterArguments out = new KeymasterArguments();
OperationResult result = mKeyStore.begin(name, purpose,
- true, beginArgs, out);
+ true, beginArgs, null, out);
assertEquals("Begin should succeed", KeyStore.NO_ERROR, result.resultCode);
IBinder token = result.token;
result = mKeyStore.update(token, null, in);
@@ -885,18 +903,19 @@
args.addInt(KeymasterDefs.KM_TAG_MAC_LENGTH, 16);
KeyCharacteristics outCharacteristics = new KeyCharacteristics();
- int rc = mKeyStore.generateKey(name, args, 0, outCharacteristics);
+ int rc = mKeyStore.generateKey(name, args, null, 0, outCharacteristics);
assertEquals("Generate should succeed", KeyStore.NO_ERROR, rc);
KeymasterArguments out = new KeymasterArguments();
args = new KeymasterArguments();
OperationResult result = mKeyStore.begin(name, KeymasterDefs.KM_PURPOSE_ENCRYPT,
- true, args, out);
+ true, args, null, out);
assertEquals("Begin should succeed", KeyStore.NO_ERROR, result.resultCode);
IBinder first = result.token;
// Implementation detail: softkeymaster supports 16 concurrent operations
for (int i = 0; i < 16; i++) {
- result = mKeyStore.begin(name, KeymasterDefs.KM_PURPOSE_ENCRYPT, true, args, out);
+ result = mKeyStore.begin(name, KeymasterDefs.KM_PURPOSE_ENCRYPT, true, args, null,
+ out);
assertEquals("Begin should succeed", KeyStore.NO_ERROR, result.resultCode);
}
// At this point the first operation should be pruned.
diff --git a/libs/androidfw/BackupHelpers.cpp b/libs/androidfw/BackupHelpers.cpp
index 227de3b..9300794 100644
--- a/libs/androidfw/BackupHelpers.cpp
+++ b/libs/androidfw/BackupHelpers.cpp
@@ -478,7 +478,8 @@
}
int write_tarfile(const String8& packageName, const String8& domain,
- const String8& rootpath, const String8& filepath, BackupDataWriter* writer)
+ const String8& rootpath, const String8& filepath, off_t* outSize,
+ BackupDataWriter* writer)
{
// In the output stream everything is stored relative to the root
const char* relstart = filepath.string() + rootpath.length();
@@ -488,6 +489,7 @@
// If relpath is empty, it means this is the top of one of the standard named
// domain directories, so we should just skip it
if (relpath.length() == 0) {
+ *outSize = 0;
return 0;
}
@@ -517,12 +519,25 @@
return err;
}
+ // very large files need a pax extended size header
+ if (s.st_size > 077777777777LL) {
+ needExtended = true;
+ }
+
String8 fullname; // for pax later on
String8 prefix;
const int isdir = S_ISDIR(s.st_mode);
if (isdir) s.st_size = 0; // directories get no actual data in the tar stream
+ // Report the size, including a rough tar overhead estimation: 512 bytes for the
+ // overall tar file-block header, plus 2 blocks if using the pax extended format,
+ // plus the raw content size rounded up to a multiple of 512.
+ *outSize = 512 + (needExtended ? 1024 : 0) + 512*((s.st_size + 511)/512);
+
+ // Measure case: we've returned the size; now return without moving data
+ if (!writer) return 0;
+
// !!! TODO: use mmap when possible to avoid churning the buffer cache
// !!! TODO: this will break with symlinks; need to use readlink(2)
int fd = open(filepath.string(), O_RDONLY);
@@ -560,10 +575,6 @@
snprintf(buf + 116, 8, "0%lo", (unsigned long)s.st_gid);
// [ 124 : 12 ] file size in bytes
- if (s.st_size > 077777777777LL) {
- // very large files need a pax extended size header
- needExtended = true;
- }
snprintf(buf + 124, 12, "%011llo", (isdir) ? 0LL : s.st_size);
// [ 136 : 12 ] last mod time as a UTC time_t
diff --git a/libs/hwui/ClipArea.cpp b/libs/hwui/ClipArea.cpp
index 852204a..eb520b4 100644
--- a/libs/hwui/ClipArea.cpp
+++ b/libs/hwui/ClipArea.cpp
@@ -234,7 +234,7 @@
bool ClipArea::clipRegion(const SkRegion& region, SkRegion::Op op) {
enterRegionMode();
mClipRegion.op(region, op);
- setClipRectToRegionBounds();
+ onClipRegionUpdated();
return true;
}
@@ -263,6 +263,9 @@
bool ClipArea::rectangleModeClipRectWithTransform(const Rect& r,
const mat4* transform, SkRegion::Op op) {
+ // TODO: we should be able to handle kReplace_Op efficiently without
+ // going through RegionMode and later falling back into RectangleMode.
+
if (op != SkRegion::kIntersect_Op) {
enterRegionMode();
return regionModeClipRectWithTransform(r, transform, op);
@@ -324,15 +327,16 @@
*/
void ClipArea::enterRegionMode() {
- if (mMode != kModeRegion) {
- if (mMode == kModeRectangle) {
+ Mode oldMode = mMode;
+ mMode = kModeRegion;
+ if (oldMode != kModeRegion) {
+ if (oldMode == kModeRectangle) {
mClipRegion.setRect(mClipRect.left, mClipRect.top,
mClipRect.right, mClipRect.bottom);
} else {
mClipRegion = mRectangleList.convertToRegion(createViewportRegion());
- setClipRectToRegionBounds();
+ onClipRegionUpdated();
}
- mMode = kModeRegion;
}
}
@@ -342,7 +346,7 @@
SkRegion transformedRectRegion;
regionFromPath(transformedRect, transformedRectRegion);
mClipRegion.op(transformedRectRegion, op);
- setClipRectToRegionBounds();
+ onClipRegionUpdated();
return true;
}
@@ -352,12 +356,13 @@
transform, op);
}
-void ClipArea::setClipRectToRegionBounds() {
+void ClipArea::onClipRegionUpdated() {
if (!mClipRegion.isEmpty()) {
mClipRect.set(mClipRegion.getBounds());
if (mClipRegion.isRect()) {
mClipRegion.setEmpty();
+ enterRectangleMode();
}
} else {
mClipRect.setEmpty();
diff --git a/libs/hwui/ClipArea.h b/libs/hwui/ClipArea.h
index 16e6df9..e284af0 100644
--- a/libs/hwui/ClipArea.h
+++ b/libs/hwui/ClipArea.h
@@ -144,7 +144,7 @@
float bottom, const mat4* transform, SkRegion::Op op);
void ensureClipRegion();
- void setClipRectToRegionBounds();
+ void onClipRegionUpdated();
bool clipRegionOp(float left, float top, float right, float bottom,
SkRegion::Op op);
diff --git a/libs/hwui/JankTracker.cpp b/libs/hwui/JankTracker.cpp
index 7df61f27..65be9e1 100644
--- a/libs/hwui/JankTracker.cpp
+++ b/libs/hwui/JankTracker.cpp
@@ -16,8 +16,12 @@
#include "JankTracker.h"
#include <algorithm>
+#include <cutils/ashmem.h>
+#include <cutils/log.h>
#include <cstdio>
+#include <errno.h>
#include <inttypes.h>
+#include <sys/mman.h>
namespace android {
namespace uirenderer {
@@ -63,11 +67,114 @@
= FrameInfoFlags::kWindowLayoutChanged
| FrameInfoFlags::kSurfaceCanvas;
+// The bucketing algorithm controls so to speak
+// If a frame is <= to this it goes in bucket 0
+static const uint32_t kBucketMinThreshold = 7;
+// If a frame is > this, start counting in increments of 2ms
+static const uint32_t kBucket2msIntervals = 32;
+// If a frame is > this, start counting in increments of 4ms
+static const uint32_t kBucket4msIntervals = 48;
+
+// This will be called every frame, performance sensitive
+// Uses bit twiddling to avoid branching while achieving the packing desired
+static uint32_t frameCountIndexForFrameTime(nsecs_t frameTime, uint32_t max) {
+ uint32_t index = static_cast<uint32_t>(ns2ms(frameTime));
+ // If index > kBucketMinThreshold mask will be 0xFFFFFFFF as a result
+ // of negating 1 (twos compliment, yaay) else mask will be 0
+ uint32_t mask = -(index > kBucketMinThreshold);
+ // If index > threshold, this will essentially perform:
+ // amountAboveThreshold = index - threshold;
+ // index = threshold + (amountAboveThreshold / 2)
+ // However if index is <= this will do nothing. It will underflow, do
+ // a right shift by 0 (no-op), then overflow back to the original value
+ index = ((index - kBucket4msIntervals) >> (index > kBucket4msIntervals))
+ + kBucket4msIntervals;
+ index = ((index - kBucket2msIntervals) >> (index > kBucket2msIntervals))
+ + kBucket2msIntervals;
+ // If index was < minThreshold at the start of all this it's going to
+ // be a pretty garbage value right now. However, mask is 0 so we'll end
+ // up with the desired result of 0.
+ index = (index - kBucketMinThreshold) & mask;
+ return index < max ? index : max;
+}
+
+// Only called when dumping stats, less performance sensitive
+static uint32_t frameTimeForFrameCountIndex(uint32_t index) {
+ index = index + kBucketMinThreshold;
+ if (index > kBucket2msIntervals) {
+ index += (index - kBucket2msIntervals);
+ }
+ if (index > kBucket4msIntervals) {
+ // This works because it was already doubled by the above if
+ // 1 is added to shift slightly more towards the middle of the bucket
+ index += (index - kBucket4msIntervals) + 1;
+ }
+ return index;
+}
+
JankTracker::JankTracker(nsecs_t frameIntervalNanos) {
+ // By default this will use malloc memory. It may be moved later to ashmem
+ // if there is shared space for it and a request comes in to do that.
+ mData = new ProfileData;
reset();
setFrameInterval(frameIntervalNanos);
}
+JankTracker::~JankTracker() {
+ freeData();
+}
+
+void JankTracker::freeData() {
+ if (mIsMapped) {
+ munmap(mData, sizeof(ProfileData));
+ } else {
+ delete mData;
+ }
+ mIsMapped = false;
+ mData = nullptr;
+}
+
+void JankTracker::switchStorageToAshmem(int ashmemfd) {
+ int regionSize = ashmem_get_size_region(ashmemfd);
+ if (regionSize < static_cast<int>(sizeof(ProfileData))) {
+ ALOGW("Ashmem region is too small! Received %d, required %u",
+ regionSize, static_cast<unsigned int>(sizeof(ProfileData)));
+ return;
+ }
+ ProfileData* newData = reinterpret_cast<ProfileData*>(
+ mmap(NULL, sizeof(ProfileData), PROT_READ | PROT_WRITE,
+ MAP_SHARED, ashmemfd, 0));
+ if (newData == MAP_FAILED) {
+ int err = errno;
+ ALOGW("Failed to move profile data to ashmem fd %d, error = %d",
+ ashmemfd, err);
+ return;
+ }
+
+ // The new buffer may have historical data that we want to build on top of
+ // But let's make sure we don't overflow Just In Case
+ uint32_t divider = 0;
+ if (newData->totalFrameCount > (1 << 24)) {
+ divider = 4;
+ }
+ for (size_t i = 0; i < mData->jankTypeCounts.size(); i++) {
+ newData->jankTypeCounts[i] >>= divider;
+ newData->jankTypeCounts[i] += mData->jankTypeCounts[i];
+ }
+ for (size_t i = 0; i < mData->frameCounts.size(); i++) {
+ newData->frameCounts[i] >>= divider;
+ newData->frameCounts[i] += mData->frameCounts[i];
+ }
+ newData->jankFrameCount >>= divider;
+ newData->jankFrameCount += mData->jankFrameCount;
+ newData->totalFrameCount >>= divider;
+ newData->totalFrameCount += mData->totalFrameCount;
+
+ freeData();
+ mData = newData;
+ mIsMapped = true;
+}
+
void JankTracker::setFrameInterval(nsecs_t frameInterval) {
mFrameInterval = frameInterval;
mThresholds[kMissedVsync] = 1;
@@ -92,16 +199,15 @@
}
void JankTracker::addFrame(const FrameInfo& frame) {
- mTotalFrameCount++;
+ mData->totalFrameCount++;
// Fast-path for jank-free frames
int64_t totalDuration =
frame[FrameInfoIndex::kFrameCompleted] - frame[FrameInfoIndex::kIntendedVsync];
- uint32_t framebucket = std::min(
- static_cast<typeof mFrameCounts.size()>(ns2ms(totalDuration)),
- mFrameCounts.size());
+ uint32_t framebucket = frameCountIndexForFrameTime(
+ totalDuration, mData->frameCounts.size());
// Keep the fast path as fast as possible.
if (CC_LIKELY(totalDuration < mFrameInterval)) {
- mFrameCounts[framebucket]++;
+ mData->frameCounts[framebucket]++;
return;
}
@@ -109,47 +215,52 @@
return;
}
- mFrameCounts[framebucket]++;
- mJankFrameCount++;
+ mData->frameCounts[framebucket]++;
+ mData->jankFrameCount++;
for (int i = 0; i < NUM_BUCKETS; i++) {
int64_t delta = frame[COMPARISONS[i].end] - frame[COMPARISONS[i].start];
if (delta >= mThresholds[i] && delta < IGNORE_EXCEEDING) {
- mBuckets[i].count++;
+ mData->jankTypeCounts[i]++;
}
}
}
-void JankTracker::dump(int fd) {
- FILE* file = fdopen(fd, "a");
- fprintf(file, "\nFrame stats:");
- fprintf(file, "\n Total frames rendered: %u", mTotalFrameCount);
- fprintf(file, "\n Janky frames: %u (%.2f%%)", mJankFrameCount,
- (float) mJankFrameCount / (float) mTotalFrameCount * 100.0f);
- fprintf(file, "\n 90th percentile: %ums", findPercentile(90));
- fprintf(file, "\n 95th percentile: %ums", findPercentile(95));
- fprintf(file, "\n 99th percentile: %ums", findPercentile(99));
- for (int i = 0; i < NUM_BUCKETS; i++) {
- fprintf(file, "\n Number %s: %u", JANK_TYPE_NAMES[i], mBuckets[i].count);
+void JankTracker::dumpBuffer(const void* buffer, size_t bufsize, int fd) {
+ if (bufsize < sizeof(ProfileData)) {
+ return;
}
- fprintf(file, "\n");
- fflush(file);
+ const ProfileData* data = reinterpret_cast<const ProfileData*>(buffer);
+ dumpData(data, fd);
+}
+
+void JankTracker::dumpData(const ProfileData* data, int fd) {
+ dprintf(fd, "\nTotal frames rendered: %u", data->totalFrameCount);
+ dprintf(fd, "\nJanky frames: %u (%.2f%%)", data->jankFrameCount,
+ (float) data->jankFrameCount / (float) data->totalFrameCount * 100.0f);
+ dprintf(fd, "\n90th percentile: %ums", findPercentile(data, 90));
+ dprintf(fd, "\n95th percentile: %ums", findPercentile(data, 95));
+ dprintf(fd, "\n99th percentile: %ums", findPercentile(data, 99));
+ for (int i = 0; i < NUM_BUCKETS; i++) {
+ dprintf(fd, "\nNumber %s: %u", JANK_TYPE_NAMES[i], data->jankTypeCounts[i]);
+ }
+ dprintf(fd, "\n");
}
void JankTracker::reset() {
- mBuckets.fill({0});
- mFrameCounts.fill(0);
- mTotalFrameCount = 0;
- mJankFrameCount = 0;
+ mData->jankTypeCounts.fill(0);
+ mData->frameCounts.fill(0);
+ mData->totalFrameCount = 0;
+ mData->jankFrameCount = 0;
}
-uint32_t JankTracker::findPercentile(int percentile) {
- int pos = percentile * mTotalFrameCount / 100;
- int remaining = mTotalFrameCount - pos;
- for (int i = mFrameCounts.size() - 1; i >= 0; i--) {
- remaining -= mFrameCounts[i];
+uint32_t JankTracker::findPercentile(const ProfileData* data, int percentile) {
+ int pos = percentile * data->totalFrameCount / 100;
+ int remaining = data->totalFrameCount - pos;
+ for (int i = data->frameCounts.size() - 1; i >= 0; i--) {
+ remaining -= data->frameCounts[i];
if (remaining <= 0) {
- return i;
+ return frameTimeForFrameCountIndex(i);
}
}
return 0;
diff --git a/libs/hwui/JankTracker.h b/libs/hwui/JankTracker.h
index ae339ec..4783001 100644
--- a/libs/hwui/JankTracker.h
+++ b/libs/hwui/JankTracker.h
@@ -20,6 +20,8 @@
#include "renderthread/TimeLord.h"
#include "utils/RingBuffer.h"
+#include <cutils/compiler.h>
+
#include <array>
#include <memory>
@@ -37,33 +39,45 @@
NUM_BUCKETS,
};
-struct JankBucket {
- // Number of frames that hit this bucket
- uint32_t count;
+// Try to keep as small as possible, should match ASHMEM_SIZE in
+// GraphicsStatsService.java
+struct ProfileData {
+ std::array<uint32_t, NUM_BUCKETS> jankTypeCounts;
+ // See comments on kBucket* constants for what this holds
+ std::array<uint32_t, 57> frameCounts;
+
+ uint32_t totalFrameCount;
+ uint32_t jankFrameCount;
};
// TODO: Replace DrawProfiler with this
class JankTracker {
public:
JankTracker(nsecs_t frameIntervalNanos);
-
- void setFrameInterval(nsecs_t frameIntervalNanos);
+ ~JankTracker();
void addFrame(const FrameInfo& frame);
- void dump(int fd);
+ void dump(int fd) { dumpData(mData, fd); }
void reset();
+ void switchStorageToAshmem(int ashmemfd);
+
+ uint32_t findPercentile(int p) { return findPercentile(mData, p); }
+
+ ANDROID_API static void dumpBuffer(const void* buffer, size_t bufsize, int fd);
+
private:
- uint32_t findPercentile(int p);
+ void freeData();
+ void setFrameInterval(nsecs_t frameIntervalNanos);
- std::array<JankBucket, NUM_BUCKETS> mBuckets;
+ static uint32_t findPercentile(const ProfileData* data, int p);
+ static void dumpData(const ProfileData* data, int fd);
+
std::array<int64_t, NUM_BUCKETS> mThresholds;
- std::array<uint32_t, 128> mFrameCounts;
-
int64_t mFrameInterval;
- uint32_t mTotalFrameCount;
- uint32_t mJankFrameCount;
+ ProfileData* mData;
+ bool mIsMapped = false;
};
} /* namespace uirenderer */
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 3781969..02fbd89 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -858,6 +858,8 @@
.setModelViewMapUnitToRectOptionalSnap(snap, rect)
.setRoundRectClipState(currentSnapshot()->roundRectClipState)
.build();
+ renderGlop(glop);
+ return;
}
float alpha = getLayerAlpha(layer);
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 9456073..9237151 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -41,15 +41,10 @@
RenderNode* rootRenderNode, IContextFactory* contextFactory)
: mRenderThread(thread)
, mEglManager(thread.eglManager())
- , mEglSurface(EGL_NO_SURFACE)
- , mBufferPreserved(false)
- , mSwapBehavior(kSwap_default)
, mOpaque(!translucent)
- , mCanvas(nullptr)
- , mHaveNewSurface(false)
, mAnimationContext(contextFactory->createAnimationContext(mRenderThread.timeLord()))
, mRootRenderNode(rootRenderNode)
- , mCurrentFrameInfo(nullptr) {
+ , mJankTracker(thread.timeLord().frameIntervalNanos()) {
mRenderThread.renderState().registerCanvasContext(this);
mProfiler.setDensity(mRenderThread.mainDisplayInfo().density);
}
@@ -258,6 +253,7 @@
// TODO: Use a fence for real completion?
mCurrentFrameInfo->markFrameCompleted();
+ mJankTracker.addFrame(*mCurrentFrameInfo);
mRenderThread.jankTracker().addFrame(*mCurrentFrameInfo);
profiler().finishFrame();
}
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index c3904c2..f5f1f54 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -127,23 +127,24 @@
RenderThread& mRenderThread;
EglManager& mEglManager;
sp<ANativeWindow> mNativeWindow;
- EGLSurface mEglSurface;
- bool mBufferPreserved;
- SwapBehavior mSwapBehavior;
+ EGLSurface mEglSurface = EGL_NO_SURFACE;
+ bool mBufferPreserved = false;
+ SwapBehavior mSwapBehavior = kSwap_default;
bool mOpaque;
- OpenGLRenderer* mCanvas;
- bool mHaveNewSurface;
+ OpenGLRenderer* mCanvas = nullptr;
+ bool mHaveNewSurface = false;
DamageAccumulator mDamageAccumulator;
std::unique_ptr<AnimationContext> mAnimationContext;
const sp<RenderNode> mRootRenderNode;
DrawProfiler mProfiler;
- FrameInfo* mCurrentFrameInfo;
+ FrameInfo* mCurrentFrameInfo = nullptr;
// Ring buffer large enough for 1 second worth of frames
RingBuffer<FrameInfo, 60> mFrames;
std::string mName;
+ JankTracker mJankTracker;
std::set<RenderNode*> mPrefetechedLayers;
};
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index 0091790..cc87241 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -441,6 +441,19 @@
post(task);
}
+CREATE_BRIDGE2(setProcessStatsBuffer, RenderThread* thread, int fd) {
+ args->thread->jankTracker().switchStorageToAshmem(args->fd);
+ close(args->fd);
+ return nullptr;
+}
+
+void RenderProxy::setProcessStatsBuffer(int fd) {
+ SETUP_TASK(setProcessStatsBuffer);
+ args->thread = &mRenderThread;
+ args->fd = dup(fd);
+ post(task);
+}
+
void RenderProxy::post(RenderTask* task) {
mRenderThread.queue(task);
}
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index 43cbe07..29c6f08 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -99,6 +99,7 @@
ANDROID_API static void dumpGraphicsMemory(int fd);
ANDROID_API void setTextureAtlas(const sp<GraphicBuffer>& buffer, int64_t* map, size_t size);
+ ANDROID_API void setProcessStatsBuffer(int fd);
private:
RenderThread& mRenderThread;
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 28941b9..cb70e8b 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -134,6 +134,22 @@
public static final String VOLUME_CHANGED_ACTION = "android.media.VOLUME_CHANGED_ACTION";
/**
+ * @hide Broadcast intent when the devices for a particular stream type changes.
+ * Includes the stream, the new devices and previous devices.
+ * Notes:
+ * - for internal platform use only, do not make public,
+ * - never used for "remote" volume changes
+ *
+ * @see #EXTRA_VOLUME_STREAM_TYPE
+ * @see #EXTRA_VOLUME_STREAM_DEVICES
+ * @see #EXTRA_PREV_VOLUME_STREAM_DEVICES
+ * @see #getDevicesForStream
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String STREAM_DEVICES_CHANGED_ACTION =
+ "android.media.STREAM_DEVICES_CHANGED_ACTION";
+
+ /**
* @hide Broadcast intent when a stream mute state changes.
* Includes the stream that changed and the new mute state
*
@@ -196,6 +212,18 @@
"android.media.EXTRA_PREV_VOLUME_STREAM_VALUE";
/**
+ * @hide The devices associated with the stream for the stream devices changed intent.
+ */
+ public static final String EXTRA_VOLUME_STREAM_DEVICES =
+ "android.media.EXTRA_VOLUME_STREAM_DEVICES";
+
+ /**
+ * @hide The previous devices associated with the stream for the stream devices changed intent.
+ */
+ public static final String EXTRA_PREV_VOLUME_STREAM_DEVICES =
+ "android.media.EXTRA_PREV_VOLUME_STREAM_DEVICES";
+
+ /**
* @hide The new master volume mute state for the master mute changed intent.
* Value is boolean
*/
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index d77fcd8..83954ae 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -2602,15 +2602,21 @@
return;
case MEDIA_STOPPED:
- if (mTimeProvider != null) {
- mTimeProvider.onStopped();
+ {
+ TimeProvider timeProvider = mTimeProvider;
+ if (timeProvider != null) {
+ timeProvider.onStopped();
+ }
}
break;
case MEDIA_STARTED:
case MEDIA_PAUSED:
- if (mTimeProvider != null) {
- mTimeProvider.onPaused(msg.what == MEDIA_PAUSED);
+ {
+ TimeProvider timeProvider = mTimeProvider;
+ if (timeProvider != null) {
+ timeProvider.onPaused(msg.what == MEDIA_PAUSED);
+ }
}
break;
@@ -2620,21 +2626,26 @@
return;
case MEDIA_SEEK_COMPLETE:
- if (mOnSeekCompleteListener != null) {
- mOnSeekCompleteListener.onSeekComplete(mMediaPlayer);
- }
- // fall through
+ if (mOnSeekCompleteListener != null) {
+ mOnSeekCompleteListener.onSeekComplete(mMediaPlayer);
+ }
+ // fall through
case MEDIA_SKIPPED:
- if (mTimeProvider != null) {
- mTimeProvider.onSeekComplete(mMediaPlayer);
- }
- return;
+ {
+ TimeProvider timeProvider = mTimeProvider;
+ if (timeProvider != null) {
+ timeProvider.onSeekComplete(mMediaPlayer);
+ }
+ }
+ return;
case MEDIA_SET_VIDEO_SIZE:
- if (mOnVideoSizeChangedListener != null)
- mOnVideoSizeChangedListener.onVideoSizeChanged(mMediaPlayer, msg.arg1, msg.arg2);
- return;
+ if (mOnVideoSizeChangedListener != null) {
+ mOnVideoSizeChangedListener.onVideoSizeChanged(
+ mMediaPlayer, msg.arg1, msg.arg2);
+ }
+ return;
case MEDIA_ERROR:
Log.e(TAG, "Error (" + msg.arg1 + "," + msg.arg2 + ")");
diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java
index b4c612a..c227eb7 100644
--- a/media/java/android/media/MediaRouter.java
+++ b/media/java/android/media/MediaRouter.java
@@ -1500,18 +1500,18 @@
/**
* The default playback type, "local", indicating the presentation of the media is happening
- * on the same device (e.g. a phone, a tablet) as where it is controlled from.
+ * on the same device (e.g. a phone, a tablet) as where it is controlled from.
* @see #getPlaybackType()
*/
public final static int PLAYBACK_TYPE_LOCAL = 0;
/**
* A playback type indicating the presentation of the media is happening on
- * a different device (i.e. the remote device) than where it is controlled from.
+ * a different device (i.e. the remote device) than where it is controlled from.
* @see #getPlaybackType()
*/
public final static int PLAYBACK_TYPE_REMOTE = 1;
/**
- * Playback information indicating the playback volume is fixed, i.e. it cannot be
+ * Playback information indicating the playback volume is fixed, i.e. it cannot be
* controlled from this object. An example of fixed playback volume is a remote player,
* playing over HDMI where the user prefers to control the volume on the HDMI sink, rather
* than attenuate at the source.
diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java
index 1255276..9ea6722 100644
--- a/media/java/android/media/MediaScanner.java
+++ b/media/java/android/media/MediaScanner.java
@@ -553,15 +553,8 @@
boolean isimage = MediaFile.isImageFileType(mFileType);
if (isaudio || isvideo || isimage) {
- if (mExternalIsEmulated && path.startsWith(mExternalStoragePath)) {
- // try to rewrite the path to bypass the sd card fuse layer
- String directPath = Environment.getMediaStorageDirectory() +
- path.substring(mExternalStoragePath.length());
- File f = new File(directPath);
- if (f.exists()) {
- path = directPath;
- }
- }
+ path = Environment.maybeTranslateEmulatedPathToInternal(new File(path))
+ .getAbsolutePath();
}
// we only extract metadata for audio and video files
diff --git a/media/java/android/media/midi/MidiDeviceServer.java b/media/java/android/media/midi/MidiDeviceServer.java
index d27351f..bc85f92 100644
--- a/media/java/android/media/midi/MidiDeviceServer.java
+++ b/media/java/android/media/midi/MidiDeviceServer.java
@@ -24,6 +24,8 @@
import android.system.OsConstants;
import android.util.Log;
+import com.android.internal.midi.MidiDispatcher;
+
import dalvik.system.CloseGuard;
import libcore.io.IoUtils;
diff --git a/media/java/android/media/midi/MidiOutputPort.java b/media/java/android/media/midi/MidiOutputPort.java
index b8ed36f..0290a76 100644
--- a/media/java/android/media/midi/MidiOutputPort.java
+++ b/media/java/android/media/midi/MidiOutputPort.java
@@ -21,6 +21,8 @@
import android.os.RemoteException;
import android.util.Log;
+import com.android.internal.midi.MidiDispatcher;
+
import dalvik.system.CloseGuard;
import libcore.io.IoUtils;
diff --git a/media/java/android/media/session/MediaController.java b/media/java/android/media/session/MediaController.java
index 8def486..dd81a22 100644
--- a/media/java/android/media/session/MediaController.java
+++ b/media/java/android/media/session/MediaController.java
@@ -516,8 +516,8 @@
}
/**
- * Callback for receiving updates on from the session. A Callback can be
- * registered using {@link #registerCallback}
+ * Callback for receiving updates from the session. A Callback can be
+ * registered using {@link #registerCallback}.
*/
public static abstract class Callback {
/**
diff --git a/media/java/android/mtp/MtpStorage.java b/media/java/android/mtp/MtpStorage.java
index e20eabc..7b8102b 100644
--- a/media/java/android/mtp/MtpStorage.java
+++ b/media/java/android/mtp/MtpStorage.java
@@ -59,7 +59,7 @@
*
* @return the storage ID
*/
- public static int getStorageId(int index) {
+ public static int getStorageIdForIndex(int index) {
// storage ID is 0x00010001 for primary storage,
// then 0x00020001, 0x00030001, etc. for secondary storages
return ((index + 1) << 16) + 1;
diff --git a/media/jni/android_media_ImageReader.cpp b/media/jni/android_media_ImageReader.cpp
index 9fc7e8e..708c083 100644
--- a/media/jni/android_media_ImageReader.cpp
+++ b/media/jni/android_media_ImageReader.cpp
@@ -431,6 +431,19 @@
pData = buffer->data;
dataSize = buffer->stride * buffer->height;
break;
+ case HAL_PIXEL_FORMAT_RAW12:
+ // Single plane 10bpp bayer data.
+ ALOG_ASSERT(idx == 0, "Wrong index: %d", idx);
+ LOG_ALWAYS_FATAL_IF(buffer->width % 4,
+ "Width is not multiple of 4 %d", buffer->width);
+ LOG_ALWAYS_FATAL_IF(buffer->height % 2,
+ "Height is not even %d", buffer->height);
+ LOG_ALWAYS_FATAL_IF(buffer->stride < (buffer->width * 12 / 8),
+ "stride (%d) should be at least %d",
+ buffer->stride, buffer->width * 12 / 8);
+ pData = buffer->data;
+ dataSize = buffer->stride * buffer->height;
+ break;
case HAL_PIXEL_FORMAT_RGBA_8888:
case HAL_PIXEL_FORMAT_RGBX_8888:
// Single plane, 32bpp.
@@ -492,8 +505,10 @@
break;
case HAL_PIXEL_FORMAT_BLOB:
case HAL_PIXEL_FORMAT_RAW10:
- // Blob is used for JPEG data, RAW10 is used for 10-bit raw data, they are
- // single plane, row and pixel strides are 0.
+ case HAL_PIXEL_FORMAT_RAW12:
+ // Blob is used for JPEG data, RAW10 and RAW12 is used for 10-bit and 12-bit raw data,
+ // those are single plane data with pixel stride 0 since they don't really have a
+ // well defined pixel stride
ALOG_ASSERT(idx == 0, "Wrong index: %d", idx);
pixelStride = 0;
break;
@@ -549,12 +564,14 @@
rowStride = (idx == 0) ? buffer->stride : ALIGN(buffer->stride / 2, 16);
break;
case HAL_PIXEL_FORMAT_BLOB:
- // Blob is used for JPEG data, RAW10 is used for 10-bit raw data, they are
- // single plane, row and pixel strides are 0.
+ // Blob is used for JPEG data. It is single plane and has 0 row stride and
+ // 0 pixel stride
ALOG_ASSERT(idx == 0, "Wrong index: %d", idx);
rowStride = 0;
break;
case HAL_PIXEL_FORMAT_RAW10:
+ case HAL_PIXEL_FORMAT_RAW12:
+ // RAW10 and RAW12 are used for 10-bit and 12-bit raw data, they are single plane
ALOG_ASSERT(idx == 0, "Wrong index: %d", idx);
rowStride = buffer->stride;
break;
diff --git a/packages/DocumentsUI/res/menu/mode_directory.xml b/packages/DocumentsUI/res/menu/mode_directory.xml
index 0a3645f..695060d 100644
--- a/packages/DocumentsUI/res/menu/mode_directory.xml
+++ b/packages/DocumentsUI/res/menu/mode_directory.xml
@@ -29,4 +29,8 @@
android:icon="@drawable/ic_menu_delete"
android:title="@string/menu_delete"
android:showAsAction="always" />
+ <item
+ android:id="@+id/menu_select_all"
+ android:title="@string/menu_select_all"
+ android:showAsAction="never" />
</menu>
diff --git a/packages/DocumentsUI/res/values/strings.xml b/packages/DocumentsUI/res/values/strings.xml
index 268ce18..4ad337de 100644
--- a/packages/DocumentsUI/res/values/strings.xml
+++ b/packages/DocumentsUI/res/values/strings.xml
@@ -46,6 +46,8 @@
<string name="menu_delete">Delete</string>
<!-- Menu item title that selects the current directory [CHAR LIMIT=48] -->
<string name="menu_select">Select \"<xliff:g id="directory" example="My Directory">^1</xliff:g>\"</string>
+ <!-- Menu item title that selects all documents in the current directory [CHAR LIMIT=24] -->
+ <string name="menu_select_all">Select All</string>
<!-- Menu item that reveals internal storage built into the device [CHAR LIMIT=24] -->
<string name="menu_advanced_show" product="nosdcard">Show internal storage</string>
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
index f55912c..a75dc42 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
@@ -501,6 +501,14 @@
mode.finish();
return true;
+ } else if (id == R.id.menu_select_all) {
+ int count = mCurrentView.getCount();
+ for (int i = 0; i < count; i++) {
+ mCurrentView.setItemChecked(i, true);
+ }
+ updateDisplayState();
+ return true;
+
} else {
return false;
}
diff --git a/packages/IntentFilterVerifier/Android.mk b/packages/IntentFilterVerifier/Android.mk
new file mode 100644
index 0000000..99feda5
--- /dev/null
+++ b/packages/IntentFilterVerifier/Android.mk
@@ -0,0 +1,22 @@
+LOCAL_PATH:= $(call my-dir)
+
+# Build the IntentFilterVerifier.
+include $(CLEAR_VARS)
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ volley \
+
+LOCAL_JAVA_LIBRARIES += org.apache.http.legacy
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := IntentFilterVerifier
+
+LOCAL_PRIVILEGED_MODULE := true
+
+LOCAL_PROGUARD_FLAGS := $(proguard.flags)
+
+include $(BUILD_PACKAGE)
+
+# Build the test package.
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/packages/IntentFilterVerifier/AndroidManifest.xml b/packages/IntentFilterVerifier/AndroidManifest.xml
new file mode 100644
index 0000000..3829cc5
--- /dev/null
+++ b/packages/IntentFilterVerifier/AndroidManifest.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.verifier.intentfilter"
+ coreApp="true">
+ <uses-permission android:name="android.permission.INTENT_FILTER_VERIFICATION_AGENT"/>
+ <uses-permission android:name="android.permission.INTERNET"/>
+ <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
+
+ <application
+ android:label="@string/service_name"
+ android:allowBackup="false">
+
+ <receiver
+ android:name="com.android.verifier.intentfilter.IntentVerificationReceiver"
+ android:permission="android.permission.BIND_INTENT_FILTER_VERIFIER" >
+ <intent-filter
+ android:priority="-1" >
+ <action android:name="android.intent.action.INTENT_FILTER_NEEDS_VERIFICATION" />
+ <data android:mimeType="application/vnd.android.package-archive" />
+ </intent-filter>
+ </receiver>
+
+ <service android:name=".IntentVerificationService"
+ android:label="@string/service_name"
+ android:exported="false"/>
+
+ </application>
+
+</manifest>
diff --git a/packages/IntentFilterVerifier/CleanSpec.mk b/packages/IntentFilterVerifier/CleanSpec.mk
new file mode 100644
index 0000000..e4575ae
--- /dev/null
+++ b/packages/IntentFilterVerifier/CleanSpec.mk
@@ -0,0 +1,49 @@
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# If you don't need to do a full clean build but would like to touch
+# a file or delete some intermediate files, add a clean step to the end
+# of the list. These steps will only be run once, if they haven't been
+# run before.
+#
+# E.g.:
+# $(call add-clean-step, touch -c external/sqlite/sqlite3.h)
+# $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libz_intermediates)
+#
+# Always use "touch -c" and "rm -f" or "rm -rf" to gracefully deal with
+# files that are missing or have been moved.
+#
+# Use $(PRODUCT_OUT) to get to the "out/target/product/blah/" directory.
+# Use $(OUT_DIR) to refer to the "out" directory.
+#
+# If you need to re-do something that's already mentioned, just copy
+# the command and add it to the bottom of the list. E.g., if a change
+# that you made last week required touching a file and a change you
+# made today requires touching the same file, just copy the old
+# touch step and add it to the end of the list.
+#
+# *****************************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST ABOVE THE BANNER
+# *****************************************************************
+
+# For example:
+#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/AndroidTests_intermediates)
+#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/core_intermediates)
+#$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f)
+#$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*)
+
+# ******************************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST ABOVE THIS BANNER
+# ******************************************************************
diff --git a/packages/IntentFilterVerifier/proguard.flags b/packages/IntentFilterVerifier/proguard.flags
new file mode 100644
index 0000000..6e4bec3
--- /dev/null
+++ b/packages/IntentFilterVerifier/proguard.flags
@@ -0,0 +1 @@
+-verbose
\ No newline at end of file
diff --git a/packages/IntentFilterVerifier/res/values/strings.xml b/packages/IntentFilterVerifier/res/values/strings.xml
new file mode 100644
index 0000000..22f3cd5
--- /dev/null
+++ b/packages/IntentFilterVerifier/res/values/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Package name shown to users when they look at installed applications
+ and running processes. This service verifies packages that are
+ requested to be installed. [CHAR LIMIT=50] -->
+ <string name="service_name">Basic Intent Filter Verification Service</string>
+
+</resources>
diff --git a/packages/IntentFilterVerifier/src/com/android/verifier/intentfilter/IntentVerificationReceiver.java b/packages/IntentFilterVerifier/src/com/android/verifier/intentfilter/IntentVerificationReceiver.java
new file mode 100644
index 0000000..de25f8c
--- /dev/null
+++ b/packages/IntentFilterVerifier/src/com/android/verifier/intentfilter/IntentVerificationReceiver.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.verifier.intentfilter;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.os.Bundle;
+import android.util.Log;
+import android.util.Slog;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class IntentVerificationReceiver extends BroadcastReceiver {
+ static final String TAG = IntentVerificationReceiver.class.getName();
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final String action = intent.getAction();
+ if (Intent.ACTION_INTENT_FILTER_NEEDS_VERIFICATION.equals(action)) {
+ Bundle extras = intent.getExtras();
+ if (extras != null) {
+ int verificationId = extras.getInt(
+ PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_ID);
+ String hosts = extras.getString(
+ PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_HOSTS);
+
+ Log.d(TAG, "Received IntentFilter verification broadcast with verificationId: "
+ + verificationId);
+
+ if (canDoVerification(context)) {
+ Intent serviceIntent = new Intent(context, IntentVerificationService.class);
+ serviceIntent.fillIn(intent, 0);
+ serviceIntent.putExtras(intent.getExtras());
+
+ Slog.d(TAG, "Starting Intent Verification Service.");
+
+ context.startService(serviceIntent);
+ } else {
+ sendVerificationFailure(context, verificationId, hosts);
+ }
+ }
+
+ } else {
+ Log.w(TAG, "Unexpected action: " + action);
+ }
+ }
+
+ private void sendVerificationFailure(Context context, int verificationId, String hosts) {
+ List<String> list = Arrays.asList(hosts.split(" "));
+ context.getPackageManager().verifyIntentFilter(
+ verificationId, PackageManager.INTENT_FILTER_VERIFICATION_FAILURE, list);
+
+ Log.d(TAG, "No network! Failing IntentFilter verification with verificationId: " +
+ verificationId + " and hosts: " + hosts);
+ }
+
+ private boolean canDoVerification(Context context) {
+ return hasNetwork(context);
+ }
+
+ public boolean hasNetwork(Context context) {
+ ConnectivityManager cm =
+ (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
+ if (cm != null) {
+ NetworkInfo info = cm.getActiveNetworkInfo();
+ return (info != null) && info.isConnected();
+ } else {
+ return false;
+ }
+ }
+}
diff --git a/packages/IntentFilterVerifier/src/com/android/verifier/intentfilter/IntentVerificationRequest.java b/packages/IntentFilterVerifier/src/com/android/verifier/intentfilter/IntentVerificationRequest.java
new file mode 100644
index 0000000..8f9c86f
--- /dev/null
+++ b/packages/IntentFilterVerifier/src/com/android/verifier/intentfilter/IntentVerificationRequest.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.verifier.intentfilter;
+
+import com.android.volley.Response;
+import com.android.volley.toolbox.JsonArrayRequest;
+import org.json.JSONArray;
+
+public class IntentVerificationRequest extends JsonArrayRequest {
+
+ public IntentVerificationRequest(String url, Response.Listener<JSONArray> listener,
+ Response.ErrorListener errorListener) {
+ super(url, listener, errorListener);
+ }
+}
diff --git a/packages/IntentFilterVerifier/src/com/android/verifier/intentfilter/IntentVerificationService.java b/packages/IntentFilterVerifier/src/com/android/verifier/intentfilter/IntentVerificationService.java
new file mode 100644
index 0000000..3e4db6c
--- /dev/null
+++ b/packages/IntentFilterVerifier/src/com/android/verifier/intentfilter/IntentVerificationService.java
@@ -0,0 +1,468 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.verifier.intentfilter;
+
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.Signature;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.Slog;
+import com.android.volley.RequestQueue;
+import com.android.volley.Response;
+import com.android.volley.VolleyError;
+import com.android.volley.toolbox.Volley;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Set;
+
+public class IntentVerificationService extends Service {
+ private static final String TAG = "IntentVerificationService";
+
+ private static final String WELL_KNOWN_ASSOCIATIONS_JSON = "/.well-known/associations.json";
+ private static final String DEFAULT_SCHEME = "https";
+
+ private static final String JSON_KEY_TARGET = "target";
+ private static final String JSON_KEY_NAMESPACE = "namespace";
+ private static final String JSON_KEY_PACKAGE_NAME = "package_name";
+ private static final String JSON_KEY_CERT_FINGERPRINTS = "sha256_cert_fingerprints";
+
+ private static final String JSON_VAL_ANDROID_APP = "android_app";
+
+ private static final char[] HEX_DIGITS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ 'A', 'B', 'C', 'D', 'E', 'F' };
+
+ private ConnectivityManager mConnectivityManager;
+ private Looper mHandlerLooper;
+ private VerificationHandler mHandler;
+ private RequestQueue mRequestQueue;
+
+ private static class VerificationState {
+ public final int verificationId;
+ public final String hosts;
+ public final String packageName;
+ public final Set<String> fingerprints;
+ public int responseCode = PackageManager.INTENT_FILTER_VERIFICATION_SUCCESS;
+ public int counter;
+ public int numberOfHosts;
+ public ArrayList<String> failedHosts = new ArrayList<>();
+
+ private final Object lock = new Object();
+
+ public VerificationState(int id, String h, String p, Set<String> fps) {
+ verificationId = id;
+ hosts = h;
+ packageName = p;
+ fingerprints = fps;
+ numberOfHosts = hosts.split(" ").length;
+ }
+ public boolean setResponseCodeAndCheckMax(int code) {
+ synchronized (lock) {
+ if (code == PackageManager.INTENT_FILTER_VERIFICATION_FAILURE) {
+ responseCode = code;
+ counter++;
+ } else if (code == PackageManager.INTENT_FILTER_VERIFICATION_SUCCESS) {
+ counter++;
+ }
+ return (counter == numberOfHosts);
+ }
+ }
+
+ public void addFailedHost(String host) {
+ synchronized (failedHosts) {
+ failedHosts.add(host);
+ }
+ }
+
+ public ArrayList<String> getFailedHosts() {
+ return failedHosts;
+ }
+ }
+
+ private HashMap<Integer, VerificationState> mVerificationMap =
+ new HashMap<Integer, VerificationState>();
+
+ private class VerificationHandler extends Handler {
+ private static final int MSG_STOP_SERVICE = 0;
+ private static final int MSG_VERIFY_INTENT_START = 1;
+ private static final int MSG_VERIFY_INTENT_DONE = 2;
+
+ private static final long SHUTDOWN_DELAY_MILLIS = 8 * 1000;
+
+ private final Context mContext;
+
+ public VerificationHandler(Context context, Looper looper) {
+ super(looper);
+
+ mContext = context;
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_VERIFY_INTENT_START:
+ final Intent intent = (Intent) msg.obj;
+ Bundle extras = intent.getExtras();
+ boolean immediate = false;
+
+ if (extras != null) {
+ immediate = doVerification(extras);
+ }
+
+ // There was no network, so we can stop soon
+ if (immediate) {
+ stopDelayed();
+ }
+ break;
+
+ case MSG_VERIFY_INTENT_DONE:
+ VerificationState vs = (VerificationState) msg.obj;
+ processVerificationDone(mContext, vs);
+ clearVerificationState(vs);
+ break;
+
+ case MSG_STOP_SERVICE:
+ stopSelf();
+ break;
+
+ default:
+ Slog.i(TAG, "Unknown message posted " + msg.toString());
+ break;
+
+ }
+ }
+
+ private void stopDelayed() {
+ removeMessages(MSG_STOP_SERVICE);
+ sendEmptyMessageDelayed(MSG_STOP_SERVICE, SHUTDOWN_DELAY_MILLIS);
+ }
+ }
+
+ private VerificationState getVerificationState(int id, String hosts, String packageName,
+ Set<String> fingerprints) {
+ synchronized (mVerificationMap) {
+ VerificationState vs = mVerificationMap.get(id);
+ if (vs == null) {
+ vs = new VerificationState(id, hosts, packageName, fingerprints);
+ }
+ return vs;
+ }
+ }
+
+ private void clearVerificationState(VerificationState vs) {
+ mVerificationMap.remove(vs);
+ }
+
+ private boolean doVerification(Bundle extras) {
+ String scheme = extras.getString(PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_URI_SCHEME);
+ if (TextUtils.isEmpty(scheme)) {
+ scheme = DEFAULT_SCHEME;
+ }
+
+ int verificationId = extras.getInt(PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_ID);
+ String hosts = extras.getString(PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_HOSTS);
+ String packageName = extras.getString(
+ PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_PACKAGE_NAME);
+
+ Set<String> fingerprints = getFingerprints(packageName);
+
+ Log.d(TAG, "Received IntentFilter verification broadcast with verificationId:" +
+ verificationId + " hosts:'" + hosts + "' scheme:" + scheme);
+
+ VerificationState vs = getVerificationState(verificationId, hosts, packageName,
+ fingerprints);
+
+ if (hasNetwork()) {
+ sendNetworkVerifications(scheme, vs);
+ return false;
+ }
+
+ // No network, so fail immediately
+ sendFailureResponseIfNeeded(vs);
+
+ return true;
+ }
+
+ private Set<String> getFingerprints(String packageName) {
+ Context context = getApplicationContext();
+ try {
+ Signature[] signatures = context.getPackageManager().getPackageInfo(packageName,
+ PackageManager.GET_SIGNATURES).signatures;
+ if (signatures.length > 0) {
+ HashSet<String> result = new HashSet<String>();
+ for (Signature sig : signatures) {
+ String fingerprint = computeNormalizedSha256Fingerprint(sig.toByteArray());
+ result.add(fingerprint);
+ }
+ return result;
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(TAG, "Cannot get signatures for package name: " + packageName);
+ }
+ return Collections.EMPTY_SET;
+ }
+
+ private static String computeNormalizedSha256Fingerprint(byte[] signature) {
+ MessageDigest digester;
+ try {
+ digester = MessageDigest.getInstance("SHA-256");
+ } catch (NoSuchAlgorithmException e) {
+ throw new AssertionError("No SHA-256 implementation found.");
+ }
+ digester.update(signature);
+ return byteArrayToHexString(digester.digest());
+ }
+
+ private static String byteArrayToHexString(byte[] array) {
+ if (array.length == 0) {
+ return "";
+ }
+ char[] buf = new char[array.length * 3 - 1];
+
+ int bufIndex = 0;
+ for (int i = 0; i < array.length; i++) {
+ byte b = array[i];
+ if (i > 0) {
+ buf[bufIndex++] = ':';
+ }
+ buf[bufIndex++] = HEX_DIGITS[(b >>> 4) & 0x0F];
+ buf[bufIndex++] = HEX_DIGITS[b & 0x0F];
+ }
+ return new String(buf);
+ }
+
+ private static String getAssociationPath() {
+ return WELL_KNOWN_ASSOCIATIONS_JSON;
+ }
+
+ private void sendNetworkVerifications(String scheme, final VerificationState vs) {
+ final int verificationId = vs.verificationId;
+ final String hosts = vs.hosts;
+
+ String[] array = hosts.split(" ");
+ for (final String host : array) {
+ try {
+ final URL url = new URL(scheme, host, getAssociationPath());
+ final String urlStr = url.toString();
+ Log.d(TAG, "Using verification URL: " + urlStr);
+ IntentVerificationRequest req = new IntentVerificationRequest(urlStr,
+ new Response.Listener<JSONArray>() {
+ @Override
+ public void onResponse(JSONArray response) {
+ Log.d(TAG, "From: " + urlStr + " received response: "
+ + response.toString());
+ handleResponse(vs, host, response);
+ }
+ }, new Response.ErrorListener() {
+ @Override
+ public void onErrorResponse(VolleyError error) {
+ Slog.d(TAG, "From: " + urlStr + " got error: " + error.getMessage()
+ + (error.networkResponse != null ? " with status code: "
+ + error.networkResponse.statusCode : ""));
+ handleError(vs, host);
+ }
+ }
+ );
+ mRequestQueue.add(req);
+ } catch (MalformedURLException e) {
+ Log.w(TAG, "Cannot send verificationId: " + verificationId + " to host: " + host);
+ }
+ }
+ }
+
+ private void handleError(VerificationState vs, String host) {
+ vs.addFailedHost(host);
+ sendFailureResponseIfNeeded(vs);
+ }
+
+ private void handleResponse(VerificationState vs, String host, JSONArray response) {
+ try {
+ if (response.length() == 0) {
+ Log.d(TAG, "Domain response is empty!");
+ handleError(vs, host);
+ return;
+ }
+
+ JSONObject firstRelation = (JSONObject) response.get(0);
+ if (firstRelation == null) {
+ Log.d(TAG, "Domain response is should have a relation!");
+ handleError(vs, host);
+ return;
+ }
+
+ JSONObject target = (JSONObject) firstRelation.get(JSON_KEY_TARGET);
+ if (target == null) {
+ Log.d(TAG, "Domain response target is empty!");
+ handleError(vs, host);
+ return;
+ }
+
+ String nameSpace = target.getString(JSON_KEY_NAMESPACE);
+ if (TextUtils.isEmpty(nameSpace) || !nameSpace.equals(JSON_VAL_ANDROID_APP)) {
+ Log.d(TAG, "Domain response target name space is not valid: " + nameSpace);
+ handleError(vs, host);
+ return;
+ }
+
+ String packageName = target.getString(JSON_KEY_PACKAGE_NAME);
+ JSONArray certFingerprints = target.getJSONArray(JSON_KEY_CERT_FINGERPRINTS);
+
+ // Early exits is the JSON response is not correct for the package name or signature
+ if (TextUtils.isEmpty(packageName)) {
+ Log.d(TAG, "Domain response has empty package name!");
+ handleError(vs, host);
+ return;
+ }
+ if (certFingerprints.length() == 0) {
+ Log.d(TAG, "Domain response has empty cert signature!");
+ handleError(vs, host);
+ return;
+ }
+ // Now do the real test on package name and signature
+ if (!packageName.equalsIgnoreCase(vs.packageName)) {
+ Log.d(TAG, "Domain response has package name mismatch!" + packageName +
+ " vs " + vs.packageName);
+ handleError(vs, host);
+ return;
+ }
+ final int count = certFingerprints.length();
+ for (int i = 0; i < count; i++) {
+ String fingerprint = certFingerprints.getString(i);
+ if (!vs.fingerprints.contains(fingerprint)) {
+ Log.d(TAG, "Domain response has cert fingerprint mismatch! " +
+ "The domain fingerprint '" + fingerprint + "' is not from the App");
+ handleError(vs, host);
+ return;
+ }
+ }
+ sendSuccessResponseIfNeeded(vs);
+ } catch (JSONException e) {
+ Log.d(TAG, "Domain response is not well formed", e);
+ handleError(vs, host);
+ }
+ }
+
+ private void sendSuccessResponseIfNeeded(VerificationState vs) {
+ if (vs.setResponseCodeAndCheckMax(PackageManager.INTENT_FILTER_VERIFICATION_SUCCESS)) {
+ sendMessage(vs);
+ }
+ }
+
+ private void sendFailureResponseIfNeeded(VerificationState vs) {
+ if (vs.setResponseCodeAndCheckMax(PackageManager.INTENT_FILTER_VERIFICATION_FAILURE)) {
+ sendMessage(vs);
+ }
+ }
+
+ private void sendMessage(VerificationState vs) {
+ Message msg = mHandler.obtainMessage(VerificationHandler.MSG_VERIFY_INTENT_DONE);
+ msg.obj = vs;
+ mHandler.sendMessage(msg);
+ }
+
+ private void processVerificationDone(Context context, VerificationState state) {
+ int verificationId = state.verificationId;
+ String hosts = state.hosts;
+ int responseCode = state.responseCode;
+
+ final PackageManager pm = context.getPackageManager();
+
+ // Callback the PackageManager
+ pm.verifyIntentFilter(verificationId, responseCode, state.getFailedHosts());
+ Log.d(TAG, "IntentFilter with verificationId: " + verificationId + " and hosts: " +
+ hosts + " got verification code: " + responseCode);
+ }
+
+ /**
+ * We only connect to this service from the same process.
+ */
+ public class LocalBinder extends Binder {
+ IntentVerificationService getService() { return IntentVerificationService.this; }
+ }
+
+ private final IBinder mBinder = new LocalBinder();
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return mBinder;
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ Slog.i(TAG, "Received start id " + startId + ": " + intent);
+
+ final Message msg = mHandler.obtainMessage(VerificationHandler.MSG_VERIFY_INTENT_START);
+ msg.obj = intent;
+ mHandler.sendMessage(msg);
+
+ return START_STICKY;
+ }
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+
+ Slog.d(TAG, "Starting up...");
+
+ final HandlerThread handlerThread = new HandlerThread("IntentVerificationService");
+ handlerThread.start();
+ mHandlerLooper = handlerThread.getLooper();
+
+ mHandler = new VerificationHandler(getApplicationContext(), mHandlerLooper);
+
+ mRequestQueue = Volley.newRequestQueue(this);
+ mRequestQueue.start();
+
+ mConnectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+
+ Slog.d(TAG, "Shutting down...");
+
+ mHandlerLooper.quit();
+ mRequestQueue.stop();
+ }
+
+ private boolean hasNetwork() {
+ NetworkInfo info = mConnectivityManager.getActiveNetworkInfo();
+ return (info != null) && info.isConnected();
+ }
+}
diff --git a/packages/Keyguard/src/com/android/keyguard/EmergencyButton.java b/packages/Keyguard/src/com/android/keyguard/EmergencyButton.java
index 3627e3e..7d5bf6b 100644
--- a/packages/Keyguard/src/com/android/keyguard/EmergencyButton.java
+++ b/packages/Keyguard/src/com/android/keyguard/EmergencyButton.java
@@ -36,7 +36,10 @@
* allows the user to return to the call.
*/
public class EmergencyButton extends Button {
- private static final String ACTION_EMERGENCY_DIAL = "com.android.phone.EmergencyDialer.DIAL";
+ private static final Intent INTENT_EMERGENCY_DIAL = new Intent()
+ .setAction("com.android.phone.EmergencyDialer.DIAL")
+ .setPackage("com.android.phone")
+ .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() {
@@ -112,12 +115,9 @@
mEmergencyButtonCallback.onEmergencyButtonClickedWhenInCall();
}
} else {
- final boolean bypassHandler = true;
- KeyguardUpdateMonitor.getInstance(mContext).reportEmergencyCallAction(bypassHandler);
- Intent intent = new Intent(ACTION_EMERGENCY_DIAL);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
- | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
- getContext().startActivityAsUser(intent,
+ KeyguardUpdateMonitor.getInstance(mContext).reportEmergencyCallAction(
+ true /* bypassHandler */);
+ getContext().startActivityAsUser(INTENT_EMERGENCY_DIAL,
new UserHandle(mLockPatternUtils.getCurrentUser()));
}
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 2c63647..8328d112 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -1806,7 +1806,7 @@
final int oldVersion = secureSettings.getVersionLocked();
final int newVersion = SETTINGS_VERSION;
- // If up do data - done.
+ // If up do date - done.
if (oldVersion == newVersion) {
return;
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index 3bf6828..c7092b3 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -416,78 +416,48 @@
private void parseStateLocked(XmlPullParser parser)
throws IOException, XmlPullParserException {
- parser.next();
- skipEmptyTextTags(parser);
- expect(parser, XmlPullParser.START_TAG, TAG_SETTINGS);
+ final int outerDepth = parser.getDepth();
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+ if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+ continue;
+ }
+
+ String tagName = parser.getName();
+ if (tagName.equals(TAG_SETTINGS)) {
+ parseSettingsLocked(parser);
+ }
+ }
+ }
+
+ private void parseSettingsLocked(XmlPullParser parser)
+ throws IOException, XmlPullParserException {
mVersion = Integer.parseInt(parser.getAttributeValue(null, ATTR_VERSION));
- parser.next();
-
- while (parseSettingLocked(parser)) {
- parser.next();
- }
-
- skipEmptyTextTags(parser);
- expect(parser, XmlPullParser.END_TAG, TAG_SETTINGS);
- }
-
- private boolean parseSettingLocked(XmlPullParser parser)
- throws IOException, XmlPullParserException {
- skipEmptyTextTags(parser);
- if (!accept(parser, XmlPullParser.START_TAG, TAG_SETTING)) {
- return false;
- }
-
- String id = parser.getAttributeValue(null, ATTR_ID);
- String name = parser.getAttributeValue(null, ATTR_NAME);
- String value = parser.getAttributeValue(null, ATTR_VALUE);
- String packageName = parser.getAttributeValue(null, ATTR_PACKAGE);
- mSettings.put(name, new Setting(name, unpackValue(value),
- unpackValue(packageName), id));
-
- if (DEBUG_PERSISTENCE) {
- Slog.i(LOG_TAG, "[RESTORED] " + name + "=" + value);
- }
-
- parser.next();
-
- skipEmptyTextTags(parser);
- expect(parser, XmlPullParser.END_TAG, TAG_SETTING);
-
- return true;
- }
-
- private void expect(XmlPullParser parser, int type, String tag)
- throws IOException, XmlPullParserException {
- if (!accept(parser, type, tag)) {
- throw new XmlPullParserException("Expected event: " + type
- + " and tag: " + tag + " but got event: " + parser.getEventType()
- + " and tag:" + parser.getName());
- }
- }
-
- private void skipEmptyTextTags(XmlPullParser parser)
- throws IOException, XmlPullParserException {
- while (accept(parser, XmlPullParser.TEXT, null)
- && parser.isWhitespace()) {
- parser.next();
- }
- }
-
- private boolean accept(XmlPullParser parser, int type, String tag)
- throws IOException, XmlPullParserException {
- if (parser.getEventType() != type) {
- return false;
- }
- if (tag != null) {
- if (!tag.equals(parser.getName())) {
- return false;
+ final int outerDepth = parser.getDepth();
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+ if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+ continue;
}
- } else if (parser.getName() != null) {
- return false;
+
+ String tagName = parser.getName();
+ if (tagName.equals(TAG_SETTING)) {
+ String id = parser.getAttributeValue(null, ATTR_ID);
+ String name = parser.getAttributeValue(null, ATTR_NAME);
+ String value = parser.getAttributeValue(null, ATTR_VALUE);
+ String packageName = parser.getAttributeValue(null, ATTR_PACKAGE);
+ mSettings.put(name, new Setting(name, unpackValue(value),
+ unpackValue(packageName), id));
+
+ if (DEBUG_PERSISTENCE) {
+ Slog.i(LOG_TAG, "[RESTORED] " + name + "=" + value);
+ }
+ }
}
- return true;
}
private final class MyHandler extends Handler {
diff --git a/packages/SharedStorageBackup/src/com/android/sharedstoragebackup/ObbBackupService.java b/packages/SharedStorageBackup/src/com/android/sharedstoragebackup/ObbBackupService.java
index 0485334..0f8ccd7 100644
--- a/packages/SharedStorageBackup/src/com/android/sharedstoragebackup/ObbBackupService.java
+++ b/packages/SharedStorageBackup/src/com/android/sharedstoragebackup/ObbBackupService.java
@@ -17,8 +17,8 @@
package com.android.sharedstoragebackup;
import android.app.Service;
-import android.app.backup.BackupDataOutput;
import android.app.backup.FullBackup;
+import android.app.backup.FullBackupDataOutput;
import android.app.backup.IBackupManager;
import android.content.Intent;
import android.os.Environment;
@@ -67,7 +67,7 @@
Log.i(TAG, obbList.size() + " files to back up");
}
final String rootPath = obbDir.getCanonicalPath();
- final BackupDataOutput out = new BackupDataOutput(outFd);
+ final FullBackupDataOutput out = new FullBackupDataOutput(data);
for (File f : obbList) {
final String filePath = f.getCanonicalPath();
if (DEBUG) {
@@ -92,7 +92,7 @@
}
try {
- callbackBinder.opComplete(token);
+ callbackBinder.opComplete(token, 0);
} catch (RemoteException e) {
}
}
@@ -119,7 +119,7 @@
Log.i(TAG, "Exception restoring OBB " + path, e);
} finally {
try {
- callbackBinder.opComplete(token);
+ callbackBinder.opComplete(token, 0);
} catch (RemoteException e) {
}
}
diff --git a/packages/Shell/res/values-af/strings.xml b/packages/Shell/res/values-af/strings.xml
index 56d7cc2..43beb57 100644
--- a/packages/Shell/res/values-af/strings.xml
+++ b/packages/Shell/res/values-af/strings.xml
@@ -22,6 +22,5 @@
<string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Raak om jou foutverslag te deel"</string>
<string name="bugreport_confirm" msgid="5130698467795669780">"Foutverslae bevat data van die stelsel se verskillende loglêers af, insluitend persoonlike en private inligting. Deel foutverslae net met programme en mense wat jy vertrou."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Wys hierdie boodskap volgende keer"</string>
- <!-- no translation found for bugreport_storage_title (5332488144740527109) -->
- <skip />
+ <string name="bugreport_storage_title" msgid="5332488144740527109">"Foutverslae"</string>
</resources>
diff --git a/packages/Shell/res/values-am/strings.xml b/packages/Shell/res/values-am/strings.xml
index 233c851..e86ecf8 100644
--- a/packages/Shell/res/values-am/strings.xml
+++ b/packages/Shell/res/values-am/strings.xml
@@ -22,6 +22,5 @@
<string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"የሳንካ ሪፖርትዎን ለማጋራት ይንክኩ"</string>
<string name="bugreport_confirm" msgid="5130698467795669780">"የሳንካ ሪፖርቶች የግል መረጃን ጨምሮ ከበርካታ የስርዓቱ ምዝግብ ማስታወሻዎች የመጣ ውሂብን ይዟል። የሳንካ ሪፖርቶች ለሚያምኗቸው መተግበሪያዎችን እና ሰዎችን ብቻ ያጋሩ።"</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"ይህን መልዕክት በሚቀጥለው ጊዜ አሳይ"</string>
- <!-- no translation found for bugreport_storage_title (5332488144740527109) -->
- <skip />
+ <string name="bugreport_storage_title" msgid="5332488144740527109">"የሳንካ ሪፖርቶች"</string>
</resources>
diff --git a/packages/Shell/res/values-ar/strings.xml b/packages/Shell/res/values-ar/strings.xml
index 385c51c..0fcf019 100644
--- a/packages/Shell/res/values-ar/strings.xml
+++ b/packages/Shell/res/values-ar/strings.xml
@@ -22,6 +22,5 @@
<string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"المس لمشاركة تقرير الأخطاء"</string>
<string name="bugreport_confirm" msgid="5130698467795669780">"تحتوي تقارير الأخطاء على بيانات من ملفات سجلات النظام المتنوعة، بما في ذلك معلومات شخصية وخاصة. لا تشارك تقارير الأخطاء إلا مع التطبيقات والأشخاص الموثوق بهم."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"إظهار هذه الرسالة في المرة القادمة"</string>
- <!-- no translation found for bugreport_storage_title (5332488144740527109) -->
- <skip />
+ <string name="bugreport_storage_title" msgid="5332488144740527109">"تقارير الأخطاء"</string>
</resources>
diff --git a/packages/Shell/res/values-bg/strings.xml b/packages/Shell/res/values-bg/strings.xml
index 97e82bf..2d779b6 100644
--- a/packages/Shell/res/values-bg/strings.xml
+++ b/packages/Shell/res/values-bg/strings.xml
@@ -22,6 +22,5 @@
<string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Докоснете, за да споделите отчета си за програмни грешки"</string>
<string name="bugreport_confirm" msgid="5130698467795669780">"Отчетите за програмни грешки съдържат данни от различни регистрационни файлове на системата, включително лична и поверителна информация. Споделяйте ги само с приложения и хора, на които имате доверие."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Това съобщение да се показва следващия път"</string>
- <!-- no translation found for bugreport_storage_title (5332488144740527109) -->
- <skip />
+ <string name="bugreport_storage_title" msgid="5332488144740527109">"Отчети за програмни грешки"</string>
</resources>
diff --git a/packages/Shell/res/values-bn-rBD/strings.xml b/packages/Shell/res/values-bn-rBD/strings.xml
index 13f9952..76da84b 100644
--- a/packages/Shell/res/values-bn-rBD/strings.xml
+++ b/packages/Shell/res/values-bn-rBD/strings.xml
@@ -22,6 +22,5 @@
<string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"আপনার ত্রুটির প্রতিবেদন ভাগ করতে স্পর্শ করুন"</string>
<string name="bugreport_confirm" msgid="5130698467795669780">"ত্রুটির প্রতিবেদনগুলিতে থাকা ডেটা, সিস্টেমের বিভিন্ন লগ ফাইলগুলি থেকে আসে, যাতে ব্যক্তিগত এবং গোপনীয় তথ্য অন্তর্ভুক্ত থাকে৷ আপনি বিশ্বাস করেন শুধুমাত্র এমন অ্যাপ্লিকেশান এবং ব্যক্তিদের সাথে ত্রুটির প্রতিবেদনগুলি ভাগ করুন৷"</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"এই বার্তাটি পরের বার দেখান"</string>
- <!-- no translation found for bugreport_storage_title (5332488144740527109) -->
- <skip />
+ <string name="bugreport_storage_title" msgid="5332488144740527109">"ত্রুটির প্রতিবেদনগুলি"</string>
</resources>
diff --git a/packages/Shell/res/values-ca/strings.xml b/packages/Shell/res/values-ca/strings.xml
index 7b5076a..327fdc2 100644
--- a/packages/Shell/res/values-ca/strings.xml
+++ b/packages/Shell/res/values-ca/strings.xml
@@ -22,6 +22,5 @@
<string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Toca aquí per compartir el teu informe d\'error."</string>
<string name="bugreport_confirm" msgid="5130698467795669780">"Els informes d\'error contenen dades dels diferents fitxers de registre del sistema, inclosa informació privada i personal. Comparteix els informes d\'error només amb les aplicacions i amb les persones en qui confies."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Mostra aquest missatge la propera vegada"</string>
- <!-- no translation found for bugreport_storage_title (5332488144740527109) -->
- <skip />
+ <string name="bugreport_storage_title" msgid="5332488144740527109">"Informes d\'error"</string>
</resources>
diff --git a/packages/Shell/res/values-cs/strings.xml b/packages/Shell/res/values-cs/strings.xml
index 1faf341..3e36c6f 100644
--- a/packages/Shell/res/values-cs/strings.xml
+++ b/packages/Shell/res/values-cs/strings.xml
@@ -22,6 +22,5 @@
<string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Chybové hlášení můžete sdílet klepnutím."</string>
<string name="bugreport_confirm" msgid="5130698467795669780">"Chybová hlášení obsahují data z různých souborů protokolů systému včetně osobních a soukromých informací. Chybová hlášení sdílejte pouze s aplikacemi a uživateli, kterým důvěřujete."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Zobrazit tuto zprávu příště"</string>
- <!-- no translation found for bugreport_storage_title (5332488144740527109) -->
- <skip />
+ <string name="bugreport_storage_title" msgid="5332488144740527109">"Zprávy o chybách"</string>
</resources>
diff --git a/packages/Shell/res/values-da/strings.xml b/packages/Shell/res/values-da/strings.xml
index 28507b3..8925b85 100644
--- a/packages/Shell/res/values-da/strings.xml
+++ b/packages/Shell/res/values-da/strings.xml
@@ -22,6 +22,5 @@
<string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Tryk for at dele din fejlrapport"</string>
<string name="bugreport_confirm" msgid="5130698467795669780">"Fejlrapporter indeholder data fra systemets forskellige logfiler, f.eks. personlige og private oplysninger. Del kun fejlrapporter med apps og personer, du har tillid til."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Vis denne underretning næste gang"</string>
- <!-- no translation found for bugreport_storage_title (5332488144740527109) -->
- <skip />
+ <string name="bugreport_storage_title" msgid="5332488144740527109">"Fejlrapporter"</string>
</resources>
diff --git a/packages/Shell/res/values-de/strings.xml b/packages/Shell/res/values-de/strings.xml
index a1738b8..19d58fe 100644
--- a/packages/Shell/res/values-de/strings.xml
+++ b/packages/Shell/res/values-de/strings.xml
@@ -22,6 +22,5 @@
<string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Tippen, um Fehlerbericht zu teilen"</string>
<string name="bugreport_confirm" msgid="5130698467795669780">"Fehlerberichte enthalten Daten aus verschiedenen Protokolldateien des Systems, darunter auch personenbezogene und private Daten. Teilen Sie Fehlerberichte nur mit Apps und Personen, denen Sie vertrauen."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Diese Nachricht nächstes Mal zeigen"</string>
- <!-- no translation found for bugreport_storage_title (5332488144740527109) -->
- <skip />
+ <string name="bugreport_storage_title" msgid="5332488144740527109">"Fehlerberichte"</string>
</resources>
diff --git a/packages/Shell/res/values-el/strings.xml b/packages/Shell/res/values-el/strings.xml
index 26f024e..5fadaa4 100644
--- a/packages/Shell/res/values-el/strings.xml
+++ b/packages/Shell/res/values-el/strings.xml
@@ -22,6 +22,5 @@
<string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Αγγίξτε για να μοιραστείτε τη αναφορά σφαλμάτων"</string>
<string name="bugreport_confirm" msgid="5130698467795669780">"Οι αναφορές σφαλμάτων περιέχουν δεδομένα από τα διάφορα αρχεία καταγραφής του συστήματος, συμπεριλαμβανομένων προσωπικών και ιδιωτικών πληροφοριών. Να μοιράζεστε αναφορές σφαλμάτων μόνο με εφαρμογές και άτομα που εμπιστεύεστε."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Εμφάνιση αυτού του μηνύματος την επόμενη φορά"</string>
- <!-- no translation found for bugreport_storage_title (5332488144740527109) -->
- <skip />
+ <string name="bugreport_storage_title" msgid="5332488144740527109">"Αναφορές σφαλμάτων"</string>
</resources>
diff --git a/packages/Shell/res/values-en-rAU/strings.xml b/packages/Shell/res/values-en-rAU/strings.xml
index 2242473..b50fc2a 100644
--- a/packages/Shell/res/values-en-rAU/strings.xml
+++ b/packages/Shell/res/values-en-rAU/strings.xml
@@ -22,6 +22,5 @@
<string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Touch to share your bug report"</string>
<string name="bugreport_confirm" msgid="5130698467795669780">"Bug reports contain data from the system\'s various log files, including personal and private information. Only share bug reports with apps and people that you trust."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Show this message next time"</string>
- <!-- no translation found for bugreport_storage_title (5332488144740527109) -->
- <skip />
+ <string name="bugreport_storage_title" msgid="5332488144740527109">"Bug reports"</string>
</resources>
diff --git a/packages/Shell/res/values-en-rGB/strings.xml b/packages/Shell/res/values-en-rGB/strings.xml
index 2242473..b50fc2a 100644
--- a/packages/Shell/res/values-en-rGB/strings.xml
+++ b/packages/Shell/res/values-en-rGB/strings.xml
@@ -22,6 +22,5 @@
<string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Touch to share your bug report"</string>
<string name="bugreport_confirm" msgid="5130698467795669780">"Bug reports contain data from the system\'s various log files, including personal and private information. Only share bug reports with apps and people that you trust."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Show this message next time"</string>
- <!-- no translation found for bugreport_storage_title (5332488144740527109) -->
- <skip />
+ <string name="bugreport_storage_title" msgid="5332488144740527109">"Bug reports"</string>
</resources>
diff --git a/packages/Shell/res/values-en-rIN/strings.xml b/packages/Shell/res/values-en-rIN/strings.xml
index 2242473..b50fc2a 100644
--- a/packages/Shell/res/values-en-rIN/strings.xml
+++ b/packages/Shell/res/values-en-rIN/strings.xml
@@ -22,6 +22,5 @@
<string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Touch to share your bug report"</string>
<string name="bugreport_confirm" msgid="5130698467795669780">"Bug reports contain data from the system\'s various log files, including personal and private information. Only share bug reports with apps and people that you trust."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Show this message next time"</string>
- <!-- no translation found for bugreport_storage_title (5332488144740527109) -->
- <skip />
+ <string name="bugreport_storage_title" msgid="5332488144740527109">"Bug reports"</string>
</resources>
diff --git a/packages/Shell/res/values-es-rUS/strings.xml b/packages/Shell/res/values-es-rUS/strings.xml
index 240332a..06edfdc 100644
--- a/packages/Shell/res/values-es-rUS/strings.xml
+++ b/packages/Shell/res/values-es-rUS/strings.xml
@@ -22,6 +22,5 @@
<string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Toca para compartir tu informe de errores."</string>
<string name="bugreport_confirm" msgid="5130698467795669780">"Los informes de errores contienen datos de los distintos archivos de registro del sistema, incluida la información personal y privada. Comparte los informes de errores únicamente con aplicaciones y personas en las que confíes."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Mostrar este mensaje la próxima vez"</string>
- <!-- no translation found for bugreport_storage_title (5332488144740527109) -->
- <skip />
+ <string name="bugreport_storage_title" msgid="5332488144740527109">"Informes de errores"</string>
</resources>
diff --git a/packages/Shell/res/values-es/strings.xml b/packages/Shell/res/values-es/strings.xml
index ea75003..3398ca3 100644
--- a/packages/Shell/res/values-es/strings.xml
+++ b/packages/Shell/res/values-es/strings.xml
@@ -22,6 +22,5 @@
<string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Toca para compartir tu informe de error"</string>
<string name="bugreport_confirm" msgid="5130698467795669780">"Los informes de errores contienen datos de los distintos archivos de registro del sistema, incluida información personal y privada. Comparte los informes de errores únicamente con aplicaciones y usuarios en los que confíes."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Mostrar este mensaje la próxima vez"</string>
- <!-- no translation found for bugreport_storage_title (5332488144740527109) -->
- <skip />
+ <string name="bugreport_storage_title" msgid="5332488144740527109">"Informes de error"</string>
</resources>
diff --git a/packages/Shell/res/values-et-rEE/strings.xml b/packages/Shell/res/values-et-rEE/strings.xml
index 4fec483..549ee26 100644
--- a/packages/Shell/res/values-et-rEE/strings.xml
+++ b/packages/Shell/res/values-et-rEE/strings.xml
@@ -22,6 +22,5 @@
<string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Veaaruande jagamiseks puudutage"</string>
<string name="bugreport_confirm" msgid="5130698467795669780">"Veaaruanded sisaldavad andmeid erinevatest süsteemi logifailidest, sh isiklikku ja privaatset teavet. Jagage veaaruandeid ainult usaldusväärsete rakenduste ja inimestega."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Kuva see sõnum järgmisel korral"</string>
- <!-- no translation found for bugreport_storage_title (5332488144740527109) -->
- <skip />
+ <string name="bugreport_storage_title" msgid="5332488144740527109">"Veaaruanded"</string>
</resources>
diff --git a/packages/Shell/res/values-eu-rES/strings.xml b/packages/Shell/res/values-eu-rES/strings.xml
index d689026..048ab8e 100644
--- a/packages/Shell/res/values-eu-rES/strings.xml
+++ b/packages/Shell/res/values-eu-rES/strings.xml
@@ -22,6 +22,5 @@
<string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Akatsen txostena partekatzeko, ukitu"</string>
<string name="bugreport_confirm" msgid="5130698467795669780">"Akatsen txostenek sistemaren erregistro-fitxategietako datuak dauzkate, informazio pertsonala eta pribatua barne. Akatsen txostenak partekatzen badituzu, partekatu soilik aplikazio eta pertsona fidagarriekin."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Erakutsi mezu hau hurrengoan"</string>
- <!-- no translation found for bugreport_storage_title (5332488144740527109) -->
- <skip />
+ <string name="bugreport_storage_title" msgid="5332488144740527109">"Akatsen txostenak"</string>
</resources>
diff --git a/packages/Shell/res/values-fa/strings.xml b/packages/Shell/res/values-fa/strings.xml
index e0f0cf1..f42ba81 100644
--- a/packages/Shell/res/values-fa/strings.xml
+++ b/packages/Shell/res/values-fa/strings.xml
@@ -22,6 +22,5 @@
<string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"جهت اشتراکگذاری گزارش اشکال خود لمس کنید"</string>
<string name="bugreport_confirm" msgid="5130698467795669780">"گزارشهای اشکال حاوی دادههایی از فایلهای گزارش مختلف در سیستم هستند، شامل اطلاعات شخصی و خصوصی. گزارشهای اشکال را فقط با افراد و برنامههای مورد اعتماد خود به اشتراک بگذارید."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"دفعه بعد این پیام نشان داده شود"</string>
- <!-- no translation found for bugreport_storage_title (5332488144740527109) -->
- <skip />
+ <string name="bugreport_storage_title" msgid="5332488144740527109">"گزارش اشکال"</string>
</resources>
diff --git a/packages/Shell/res/values-fi/strings.xml b/packages/Shell/res/values-fi/strings.xml
index 5a89fd9..26003b3 100644
--- a/packages/Shell/res/values-fi/strings.xml
+++ b/packages/Shell/res/values-fi/strings.xml
@@ -22,6 +22,5 @@
<string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Jaa virheraportti koskettamalla tätä"</string>
<string name="bugreport_confirm" msgid="5130698467795669780">"Virheraportit sisältävät järjestelmän lokitietoja, ja niihin voi sisältyä henkilökohtaisia ja yksityisiä tietoja. Jaa virheraportteja vain luotettaville sovelluksille ja käyttäjille."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Näytä tämä viesti seuraavalla kerralla"</string>
- <!-- no translation found for bugreport_storage_title (5332488144740527109) -->
- <skip />
+ <string name="bugreport_storage_title" msgid="5332488144740527109">"Virheraportit"</string>
</resources>
diff --git a/packages/Shell/res/values-fr-rCA/strings.xml b/packages/Shell/res/values-fr-rCA/strings.xml
index 56469b0..b4b81ec 100644
--- a/packages/Shell/res/values-fr-rCA/strings.xml
+++ b/packages/Shell/res/values-fr-rCA/strings.xml
@@ -22,6 +22,5 @@
<string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Appuyer ici pour partager votre rapport de bogue"</string>
<string name="bugreport_confirm" msgid="5130698467795669780">"Les rapports de bogue contiennent des données des fichiers journaux du système, y compris des informations personnelles et privées. Ne partagez les rapports de bogue qu\'avec les applications et les personnes que vous estimez fiables."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Afficher ce message la prochaine fois"</string>
- <!-- no translation found for bugreport_storage_title (5332488144740527109) -->
- <skip />
+ <string name="bugreport_storage_title" msgid="5332488144740527109">"Rapports de bogues"</string>
</resources>
diff --git a/packages/Shell/res/values-fr/strings.xml b/packages/Shell/res/values-fr/strings.xml
index 7a27d8c..e801054 100644
--- a/packages/Shell/res/values-fr/strings.xml
+++ b/packages/Shell/res/values-fr/strings.xml
@@ -22,6 +22,5 @@
<string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Appuyez ici pour partager le rapport de bug"</string>
<string name="bugreport_confirm" msgid="5130698467795669780">"Les rapports de bug contiennent des données des fichiers journaux du système, y compris des informations personnelles et privées. Ne partagez les rapports de bug qu\'avec les applications et les personnes que vous estimez fiables."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Afficher ce message la prochaine fois"</string>
- <!-- no translation found for bugreport_storage_title (5332488144740527109) -->
- <skip />
+ <string name="bugreport_storage_title" msgid="5332488144740527109">"Rapports d\'erreur"</string>
</resources>
diff --git a/packages/Shell/res/values-gl-rES/strings.xml b/packages/Shell/res/values-gl-rES/strings.xml
index 8509096..5690c9e 100644
--- a/packages/Shell/res/values-gl-rES/strings.xml
+++ b/packages/Shell/res/values-gl-rES/strings.xml
@@ -22,6 +22,5 @@
<string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Toca aquí para compartir o teu informe de erros"</string>
<string name="bugreport_confirm" msgid="5130698467795669780">"Os informes de erros conteñen datos dos distintos ficheiros de rexistro do sistema, incluída información persoal e privada. Comparte os informes de erros unicamente con aplicacións e persoas de confianza."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Mostrar esta mensaxe a próxima vez"</string>
- <!-- no translation found for bugreport_storage_title (5332488144740527109) -->
- <skip />
+ <string name="bugreport_storage_title" msgid="5332488144740527109">"Informes de erros"</string>
</resources>
diff --git a/packages/Shell/res/values-hi/strings.xml b/packages/Shell/res/values-hi/strings.xml
index 3402c38..aee1121 100644
--- a/packages/Shell/res/values-hi/strings.xml
+++ b/packages/Shell/res/values-hi/strings.xml
@@ -22,6 +22,5 @@
<string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"अपनी बग रिपोर्ट साझा करने के लिए स्पर्श करें"</string>
<string name="bugreport_confirm" msgid="5130698467795669780">"बग रिपोर्ट में व्यक्तिगत और निजी जानकारी सहित, सिस्टम की विभिन्न लॉग फ़ाइलों का डेटा होता है. बग रिपोर्ट केवल विश्वसनीय ऐप्स और व्यक्तियों से ही साझा करें."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"यह संदेश अगली बार दिखाएं"</string>
- <!-- no translation found for bugreport_storage_title (5332488144740527109) -->
- <skip />
+ <string name="bugreport_storage_title" msgid="5332488144740527109">"बग रिपोर्ट"</string>
</resources>
diff --git a/packages/Shell/res/values-hr/strings.xml b/packages/Shell/res/values-hr/strings.xml
index 37f04470..cf122ab 100644
--- a/packages/Shell/res/values-hr/strings.xml
+++ b/packages/Shell/res/values-hr/strings.xml
@@ -22,6 +22,5 @@
<string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Dodirnite za dijeljenje prijave programske pogreške"</string>
<string name="bugreport_confirm" msgid="5130698467795669780">"Prijave programskih pogrešaka sadržavaju podatke iz različitih datoteka zapisnika sustava, uključujući osobne i privatne informacije. Prijave programskih pogrešaka dijelite samo s aplikacijama i osobama koje smatrate pouzdanima."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Prikaži tu poruku sljedeći put"</string>
- <!-- no translation found for bugreport_storage_title (5332488144740527109) -->
- <skip />
+ <string name="bugreport_storage_title" msgid="5332488144740527109">"Izvješća o programskim pogreškama"</string>
</resources>
diff --git a/packages/Shell/res/values-hu/strings.xml b/packages/Shell/res/values-hu/strings.xml
index a9b15ae..f0bc227 100644
--- a/packages/Shell/res/values-hu/strings.xml
+++ b/packages/Shell/res/values-hu/strings.xml
@@ -22,6 +22,5 @@
<string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Érintse meg a programhiba-jelentés megosztásához"</string>
<string name="bugreport_confirm" msgid="5130698467795669780">"A programhiba-jelentések a rendszer különféle naplófájljaiból származó adatokat tartalmaznak, köztük személyes és magánjellegű információkat is. Csak olyan alkalmazásokkal és személyekkel osszon meg programhiba-jelentéseket, amelyekben vagy akikben megbízik."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Üzenet mutatása legközelebb"</string>
- <!-- no translation found for bugreport_storage_title (5332488144740527109) -->
- <skip />
+ <string name="bugreport_storage_title" msgid="5332488144740527109">"Hibajelentések"</string>
</resources>
diff --git a/packages/Shell/res/values-hy-rAM/strings.xml b/packages/Shell/res/values-hy-rAM/strings.xml
index c89f687..a254192 100644
--- a/packages/Shell/res/values-hy-rAM/strings.xml
+++ b/packages/Shell/res/values-hy-rAM/strings.xml
@@ -22,6 +22,5 @@
<string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Հպեք` ձեր վրիպակի մասին զեկույցը տարածելու համար"</string>
<string name="bugreport_confirm" msgid="5130698467795669780">"Վրիպակի զեկույցները պարունակում են տվյալներ համակարգի տարբեր մուտքի ֆայլերից, այդ թվում նաև անհատական և գաղտնի տեղեկություններ: Վրիպակի զեկույցները կիսեք միայն այն հավելվածների և մարդկանց հետ, որոնց վստահում եք:"</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Այս հաղորդագրությունը ցույց տալ հաջորդ անգամ"</string>
- <!-- no translation found for bugreport_storage_title (5332488144740527109) -->
- <skip />
+ <string name="bugreport_storage_title" msgid="5332488144740527109">"Վրիպակների հաշվետվություններ"</string>
</resources>
diff --git a/packages/Shell/res/values-in/strings.xml b/packages/Shell/res/values-in/strings.xml
index 5dd388a..627fc5e 100644
--- a/packages/Shell/res/values-in/strings.xml
+++ b/packages/Shell/res/values-in/strings.xml
@@ -22,6 +22,5 @@
<string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Sentuh untuk membagikan laporan bug Anda"</string>
<string name="bugreport_confirm" msgid="5130698467795669780">"Laporan bug berisi data dari berbagai file log sistem, termasuk informasi pribadi dan rahasia. Hanya bagikan laporan bug dengan aplikasi dan orang yang Anda percaya."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Tampilkan pesan ini lain kali"</string>
- <!-- no translation found for bugreport_storage_title (5332488144740527109) -->
- <skip />
+ <string name="bugreport_storage_title" msgid="5332488144740527109">"Laporan bug"</string>
</resources>
diff --git a/packages/Shell/res/values-is-rIS/strings.xml b/packages/Shell/res/values-is-rIS/strings.xml
index 22802d0..dbd39c4 100644
--- a/packages/Shell/res/values-is-rIS/strings.xml
+++ b/packages/Shell/res/values-is-rIS/strings.xml
@@ -22,6 +22,5 @@
<string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Snertu til að deila villutilkynningunni"</string>
<string name="bugreport_confirm" msgid="5130698467795669780">"Villutilkynningar innihalda gögn úr hinum ýmsu annálsskrám kerfisins, þ. á m. persónuleg gögn og trúnaðarupplýsingar. Deildu villutilkynningum eingöngu með forritum og fólki sem þú treystir."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Sýna þessi skilaboð næst"</string>
- <!-- no translation found for bugreport_storage_title (5332488144740527109) -->
- <skip />
+ <string name="bugreport_storage_title" msgid="5332488144740527109">"Villutilkynningar"</string>
</resources>
diff --git a/packages/Shell/res/values-it/strings.xml b/packages/Shell/res/values-it/strings.xml
index c2606a8..cd63891 100644
--- a/packages/Shell/res/values-it/strings.xml
+++ b/packages/Shell/res/values-it/strings.xml
@@ -22,6 +22,5 @@
<string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Tocca per condividere la segnalazione di bug"</string>
<string name="bugreport_confirm" msgid="5130698467795669780">"Le segnalazioni di bug contengono dati da vari file di log del sistema, incluse informazioni personali e private. Condividi le segnalazioni di bug solo con app e persone attendibili."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Mostra questo messaggio la prossima volta"</string>
- <!-- no translation found for bugreport_storage_title (5332488144740527109) -->
- <skip />
+ <string name="bugreport_storage_title" msgid="5332488144740527109">"Rapporti sui bug"</string>
</resources>
diff --git a/packages/Shell/res/values-iw/strings.xml b/packages/Shell/res/values-iw/strings.xml
index f77f259..39c784f 100644
--- a/packages/Shell/res/values-iw/strings.xml
+++ b/packages/Shell/res/values-iw/strings.xml
@@ -22,6 +22,5 @@
<string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"גע כדי לשתף את דוח הבאגים שלך"</string>
<string name="bugreport_confirm" msgid="5130698467795669780">"דוחות על באגים כוללים נתונים מקובצי היומן השונים במערכת, כולל מידע אישי ופרטי. שתף דוחות באגים רק עם אפליקציות ואנשים שאתה סומך עליהם."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"הצג את ההודעה הזו בפעם הבאה"</string>
- <!-- no translation found for bugreport_storage_title (5332488144740527109) -->
- <skip />
+ <string name="bugreport_storage_title" msgid="5332488144740527109">"דוחות באגים"</string>
</resources>
diff --git a/packages/Shell/res/values-ja/strings.xml b/packages/Shell/res/values-ja/strings.xml
index 3891e54..48be802 100644
--- a/packages/Shell/res/values-ja/strings.xml
+++ b/packages/Shell/res/values-ja/strings.xml
@@ -22,6 +22,5 @@
<string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"バグレポートを共有するにはタップします"</string>
<string name="bugreport_confirm" msgid="5130698467795669780">"バグレポートには、個人の非公開情報など、システムのさまざまなログファイルのデータが含まれます。共有する場合は信頼するアプリとユーザーのみを選択してください。"</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"このメッセージを次回も表示する"</string>
- <!-- no translation found for bugreport_storage_title (5332488144740527109) -->
- <skip />
+ <string name="bugreport_storage_title" msgid="5332488144740527109">"バグレポート"</string>
</resources>
diff --git a/packages/Shell/res/values-ka-rGE/strings.xml b/packages/Shell/res/values-ka-rGE/strings.xml
index 23bb215..bb539d0 100644
--- a/packages/Shell/res/values-ka-rGE/strings.xml
+++ b/packages/Shell/res/values-ka-rGE/strings.xml
@@ -22,6 +22,5 @@
<string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"შეეხეთ თქვენი ხარვეზების ანგარიშის გასაზიარებლად"</string>
<string name="bugreport_confirm" msgid="5130698467795669780">"ხარვეზის ანგარიშები მოიცავს მონაცემებს სხვადასხვა სისტემური ჟურნალის ფაილებიდან, მათ შორის პირად და კონფიდენციალურ ინფორმაციას."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"შემდგომში აჩვენე ეს შეტყობინება"</string>
- <!-- no translation found for bugreport_storage_title (5332488144740527109) -->
- <skip />
+ <string name="bugreport_storage_title" msgid="5332488144740527109">"შეცდომების ანგარიშები"</string>
</resources>
diff --git a/packages/Shell/res/values-kk-rKZ/strings.xml b/packages/Shell/res/values-kk-rKZ/strings.xml
index 48f8643..b22ca45 100644
--- a/packages/Shell/res/values-kk-rKZ/strings.xml
+++ b/packages/Shell/res/values-kk-rKZ/strings.xml
@@ -22,6 +22,5 @@
<string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Бөліс үшін, вирус туралы баянатты түртіңіз."</string>
<string name="bugreport_confirm" msgid="5130698467795669780">"Вирус туралы баянатта жүйеде тіркелген әртүрлі файлдар туралы деректер болады, оған жеке және құпия ақпарат та кіреді. Вирус баянаттарын сенімді қолданбалар және сенімді адамдармен ғана бөлісіңіз."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Бұл хабарды келесі жолы көрсетіңіз"</string>
- <!-- no translation found for bugreport_storage_title (5332488144740527109) -->
- <skip />
+ <string name="bugreport_storage_title" msgid="5332488144740527109">"Қате туралы баяндамалар"</string>
</resources>
diff --git a/packages/Shell/res/values-km-rKH/strings.xml b/packages/Shell/res/values-km-rKH/strings.xml
index 88b9f57..50c893b 100644
--- a/packages/Shell/res/values-km-rKH/strings.xml
+++ b/packages/Shell/res/values-km-rKH/strings.xml
@@ -22,6 +22,5 @@
<string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"ប៉ះ ដើម្បីចែករំលែករបាយការណ៍កំហុសរបស់អ្នក"</string>
<string name="bugreport_confirm" msgid="5130698467795669780">"របាយការណ៍កំហុសរួមមានឯកសារកំណត់ហេតុផ្សេងៗរបស់ប្រព័ន្ធ រួមមានព័ត៌មានផ្ទាល់ខ្លួន និងឯកជន។ ចែករំលែករបាយការណ៍កំហុសជាមួយកម្មវិធី និងមនុស្សដែលអ្នកទុកចិត្ត។"</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"បង្ហាញសារនេះពេលក្រោយ"</string>
- <!-- no translation found for bugreport_storage_title (5332488144740527109) -->
- <skip />
+ <string name="bugreport_storage_title" msgid="5332488144740527109">"រាយការណ៍ពីកំហុស"</string>
</resources>
diff --git a/packages/Shell/res/values-kn-rIN/strings.xml b/packages/Shell/res/values-kn-rIN/strings.xml
index 29d8988..7ab6abf 100644
--- a/packages/Shell/res/values-kn-rIN/strings.xml
+++ b/packages/Shell/res/values-kn-rIN/strings.xml
@@ -22,6 +22,5 @@
<string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"ನಿಮ್ಮ ದೋಷದ ವರದಿಯನ್ನು ಹಂಚಿಕೊಳ್ಳಲು ಸ್ಪರ್ಶಿಸಿ"</string>
<string name="bugreport_confirm" msgid="5130698467795669780">"ವೈಯಕ್ತಿಕ ಮತ್ತು ಖಾಸಗಿ ಮಾಹಿತಿಯು ಸೇರಿದಂತೆ, ಸಿಸ್ಟಂನ ಹಲವಾರು ಲಾಗ್ ಫೈಲ್ಗಳಿಂದ ಡೇಟಾವನ್ನು ದೋಷದ ವರದಿಗಳು ಒಳಗೊಂಡಿವೆ. ನೀವು ನಂಬುವಂತಹ ಅಪ್ಲಿಕೇಶನ್ಗಳು ಮತ್ತು ಜನರೊಂದಿಗೆ ಮಾತ್ರ ದೋಷದ ವರದಿಗಳನ್ನು ಹಂಚಿಕೊಳ್ಳಿ."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"ಈ ಸಂದೇಶವನ್ನು ಮುಂದಿನ ಬಾರಿ ತೋರಿಸಿ"</string>
- <!-- no translation found for bugreport_storage_title (5332488144740527109) -->
- <skip />
+ <string name="bugreport_storage_title" msgid="5332488144740527109">"ದೋಷ ವರದಿಗಳು"</string>
</resources>
diff --git a/packages/Shell/res/values-ko/strings.xml b/packages/Shell/res/values-ko/strings.xml
index ca4402f..da0b31f 100644
--- a/packages/Shell/res/values-ko/strings.xml
+++ b/packages/Shell/res/values-ko/strings.xml
@@ -22,6 +22,5 @@
<string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"버그 신고서를 공유하려면 터치하세요."</string>
<string name="bugreport_confirm" msgid="5130698467795669780">"버그 신고서는 시스템의 다양한 로그 파일 데이터(예: 개인 및 비공개 정보)를 포함합니다. 신뢰할 수 있는 앱과 사용자에게만 버그 신고서를 공유하세요."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"다음에 이 메시지 표시"</string>
- <!-- no translation found for bugreport_storage_title (5332488144740527109) -->
- <skip />
+ <string name="bugreport_storage_title" msgid="5332488144740527109">"버그 신고"</string>
</resources>
diff --git a/packages/Shell/res/values-ky-rKG/strings.xml b/packages/Shell/res/values-ky-rKG/strings.xml
index 5243dee..7183f77 100644
--- a/packages/Shell/res/values-ky-rKG/strings.xml
+++ b/packages/Shell/res/values-ky-rKG/strings.xml
@@ -22,6 +22,5 @@
<string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Ката тууралуу билдирүүңүздү жөнөтүш үчүн, тийиңиз"</string>
<string name="bugreport_confirm" msgid="5130698467795669780">"Ката тууралуу билдирүүлөр системанын ар кандай лог файлдарынын берилиштерин камтыйт, аларга өздүк жана купуя маалыматтар дагы кирет. Ката тууралуу билдирүүлөрдү сиз ишенген колдонмолор жана адамдар менен гана бөлүшүңүз."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Бул билдирүү кийин көрсөтүлсүн"</string>
- <!-- no translation found for bugreport_storage_title (5332488144740527109) -->
- <skip />
+ <string name="bugreport_storage_title" msgid="5332488144740527109">"Мүчүлүштүктөрдү кабарлоолор"</string>
</resources>
diff --git a/packages/Shell/res/values-lo-rLA/strings.xml b/packages/Shell/res/values-lo-rLA/strings.xml
index 0411290..fcc58e9 100644
--- a/packages/Shell/res/values-lo-rLA/strings.xml
+++ b/packages/Shell/res/values-lo-rLA/strings.xml
@@ -22,6 +22,5 @@
<string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"ແຕະເພື່ອສົ່ງການລາຍງານປັນຫາຂອງທ່ານ"</string>
<string name="bugreport_confirm" msgid="5130698467795669780">"ການລາຍງານຂໍ້ຜິດພາດປະກອບມີ ຂໍ້ມູນຈາກໄຟລ໌ບັນທຶກຂອງລະບົບຫຼາຍໄຟລ໌, ຮວມທັງຂໍ້ມູນສ່ວນໂຕນຳ. ທ່ານຕ້ອງແບ່ງປັນລາຍງານຂໍ້ຜິດພາດໃຫ້ແອັບຯ ແລະຄົນທີ່ທ່ານເຊື່ອຖືໄດ້ເທົ່ານັ້ນ."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"ສະແດງຂໍ້ຄວາມນີ້ອີກໃນເທື່ອຕໍ່ໄປ"</string>
- <!-- no translation found for bugreport_storage_title (5332488144740527109) -->
- <skip />
+ <string name="bugreport_storage_title" msgid="5332488144740527109">"ລາຍງານບັນຫາ"</string>
</resources>
diff --git a/packages/Shell/res/values-lt/strings.xml b/packages/Shell/res/values-lt/strings.xml
index 9d04d37..51655a4 100644
--- a/packages/Shell/res/values-lt/strings.xml
+++ b/packages/Shell/res/values-lt/strings.xml
@@ -22,6 +22,5 @@
<string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Palieskite, kad bendrintumėte riktų ataskaitą"</string>
<string name="bugreport_confirm" msgid="5130698467795669780">"Riktų ataskaitose pateikiami duomenys iš įvairių sistemos žurnalo failų, įskaitant asmeninę ir privačią informaciją. Riktų ataskaitas bendrinkite tik su patikimomis programomis ir žmonėmis."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Rodyti šį pranešimą kitą kartą"</string>
- <!-- no translation found for bugreport_storage_title (5332488144740527109) -->
- <skip />
+ <string name="bugreport_storage_title" msgid="5332488144740527109">"Riktų ataskaitos"</string>
</resources>
diff --git a/packages/Shell/res/values-lv/strings.xml b/packages/Shell/res/values-lv/strings.xml
index 62f5692..cf1a75a 100644
--- a/packages/Shell/res/values-lv/strings.xml
+++ b/packages/Shell/res/values-lv/strings.xml
@@ -22,6 +22,5 @@
<string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Pieskarieties, lai kopīgotu kļūdu pārskatu."</string>
<string name="bugreport_confirm" msgid="5130698467795669780">"Kļūdu pārskatā ir iekļauti dati no dažādiem sistēmas žurnālfailiem, tostarp personas dati un privāta informācija. Kļūdu pārskatus ieteicams kopīgot tikai ar uzticamām lietotnēm un lietotājiem."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Rādīt šo ziņojumu nākamajā reizē"</string>
- <!-- no translation found for bugreport_storage_title (5332488144740527109) -->
- <skip />
+ <string name="bugreport_storage_title" msgid="5332488144740527109">"Kļūdu ziņojumi"</string>
</resources>
diff --git a/packages/Shell/res/values-mk-rMK/strings.xml b/packages/Shell/res/values-mk-rMK/strings.xml
index a0184df..785a841 100644
--- a/packages/Shell/res/values-mk-rMK/strings.xml
+++ b/packages/Shell/res/values-mk-rMK/strings.xml
@@ -22,6 +22,5 @@
<string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Допри да се сподели твојот извештај за грешка"</string>
<string name="bugreport_confirm" msgid="5130698467795669780">"Извештаите за грешка содржат податоци од разни датотеки за евиденција на системот, вклучувајќи лични и приватни информации. Извештаите за грешка споделувајте ги само со апликации и луѓе на коишто им верувате."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Прикажи ја поракава следниот пат"</string>
- <!-- no translation found for bugreport_storage_title (5332488144740527109) -->
- <skip />
+ <string name="bugreport_storage_title" msgid="5332488144740527109">"Извештаи за грешки"</string>
</resources>
diff --git a/packages/Shell/res/values-ml-rIN/strings.xml b/packages/Shell/res/values-ml-rIN/strings.xml
index d3eae5a..8fa6d67c 100644
--- a/packages/Shell/res/values-ml-rIN/strings.xml
+++ b/packages/Shell/res/values-ml-rIN/strings.xml
@@ -22,6 +22,5 @@
<string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"നിങ്ങളുടെ ബഗ് റിപ്പോർട്ട് പങ്കിടാൻ സ്പർശിക്കുക"</string>
<string name="bugreport_confirm" msgid="5130698467795669780">"വ്യക്തിഗതവും സ്വകാര്യവുമായ വിവരങ്ങൾ ഉൾപ്പെടെ, സിസ്റ്റത്തിന്റെ നിരവധി ലോഗ് ഫയലുകളിൽ നിന്നുള്ള ഡാറ്റ, ബഗ് റിപ്പോർട്ടുകളിൽ അടങ്ങിയിരിക്കുന്നു. നിങ്ങൾ വിശ്വസിക്കുന്ന അപ്ലിക്കേഷനുകൾക്കും ആളുകൾക്കും മാത്രം ബഗ് റിപ്പോർട്ടുകൾ പങ്കിടുക."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"ഈ സന്ദേശം അടുത്ത തവണ ദൃശ്യമാക്കുക"</string>
- <!-- no translation found for bugreport_storage_title (5332488144740527109) -->
- <skip />
+ <string name="bugreport_storage_title" msgid="5332488144740527109">"ബഗ് റിപ്പോർട്ടുകൾ"</string>
</resources>
diff --git a/packages/Shell/res/values-mn-rMN/strings.xml b/packages/Shell/res/values-mn-rMN/strings.xml
index 63e78ae..4010072 100644
--- a/packages/Shell/res/values-mn-rMN/strings.xml
+++ b/packages/Shell/res/values-mn-rMN/strings.xml
@@ -22,6 +22,5 @@
<string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Та алдааны мэдэгдлийг хуваалцах бол хүрнэ үү"</string>
<string name="bugreport_confirm" msgid="5130698467795669780">"Алдааны репорт нь хувийн болон нууц мэдээлэл зэргийг агуулсан системийн төрөл бүрийн лог файлын датаг агуулна. Алдааны репортыг зөвхөн итгэлтэй апп болон хүмүүст хуваалцана уу."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Энэ мессежийг дараагийн удаа харуулах"</string>
- <!-- no translation found for bugreport_storage_title (5332488144740527109) -->
- <skip />
+ <string name="bugreport_storage_title" msgid="5332488144740527109">"Гэмтлийн тухай тайлан"</string>
</resources>
diff --git a/packages/Shell/res/values-mr-rIN/strings.xml b/packages/Shell/res/values-mr-rIN/strings.xml
index 7e97395..c4993fc 100644
--- a/packages/Shell/res/values-mr-rIN/strings.xml
+++ b/packages/Shell/res/values-mr-rIN/strings.xml
@@ -22,6 +22,5 @@
<string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"आपला दोष अहवाल सामायिक करण्यासाठी स्पर्श करा"</string>
<string name="bugreport_confirm" msgid="5130698467795669780">"दोष अहवालांमध्ये वैयक्तिक आणि खाजगी माहितीसह, सिस्टमच्या अनेक लॉग फायलींमधील डेटा असतो. केवळ आपला विश्वास असलेल्या अॅप्स आणि लोकांसह दोष अहवाल सामायिक करा."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"पुढील वेळी हा संदेश दर्शवा"</string>
- <!-- no translation found for bugreport_storage_title (5332488144740527109) -->
- <skip />
+ <string name="bugreport_storage_title" msgid="5332488144740527109">"दोष अहवाल"</string>
</resources>
diff --git a/packages/Shell/res/values-ms-rMY/strings.xml b/packages/Shell/res/values-ms-rMY/strings.xml
index 60efb6b..f3e4f7e 100644
--- a/packages/Shell/res/values-ms-rMY/strings.xml
+++ b/packages/Shell/res/values-ms-rMY/strings.xml
@@ -22,6 +22,5 @@
<string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Sentuh untuk berkongsi laporan pepijat anda"</string>
<string name="bugreport_confirm" msgid="5130698467795669780">"Laporan pepijat mengandungi data dari pelbagai fail log sistem, termasuk maklumat peribadi dan sulit. Kongsikan laporan pepijat hanya dengan apl dan orang yang anda percayai."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Tunjukkan mesej ini pada masa akan datang"</string>
- <!-- no translation found for bugreport_storage_title (5332488144740527109) -->
- <skip />
+ <string name="bugreport_storage_title" msgid="5332488144740527109">"Laporan pepijat"</string>
</resources>
diff --git a/packages/Shell/res/values-my-rMM/strings.xml b/packages/Shell/res/values-my-rMM/strings.xml
index 093b6c5..d223bd9 100644
--- a/packages/Shell/res/values-my-rMM/strings.xml
+++ b/packages/Shell/res/values-my-rMM/strings.xml
@@ -22,6 +22,5 @@
<string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"အမှားအယွင်း မှတ်တမ်းကို မျှဝေရန် ထိလိုက်ပါ"</string>
<string name="bugreport_confirm" msgid="5130698467795669780">"အမှားအယွင်း မှတ်တမ်းမှာ ပါရှိသော အချက်အလက်များမှာ ကိုယ်ရေးကိုယ်တာ နဲ့ လုံခြုံရေး အချက်အလက်များပါဝင်သော စနစ်မှ ပြုလုပ်မှု မှတ်တမ်းများ ဖြစ်ပါသည်၊ အမှားအယွင်း မှတ်တမ်းများကို ယုံကြည်ရသော အပလီကေးရှင်းများနဲ့ လူများကိုသာ ပေးဝေပြသမှု လုပ်ပါရန်။"</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"ဤစာတန်းကို နောက်တစ်ခါတွင် ပြရန်"</string>
- <!-- no translation found for bugreport_storage_title (5332488144740527109) -->
- <skip />
+ <string name="bugreport_storage_title" msgid="5332488144740527109">"ချို့ယွင်းမှု အစီရင်ခံစာများ"</string>
</resources>
diff --git a/packages/Shell/res/values-nb/strings.xml b/packages/Shell/res/values-nb/strings.xml
index 891fddb..c00e2d0 100644
--- a/packages/Shell/res/values-nb/strings.xml
+++ b/packages/Shell/res/values-nb/strings.xml
@@ -22,6 +22,5 @@
<string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Trykk for å dele feilrapporten din"</string>
<string name="bugreport_confirm" msgid="5130698467795669780">"Feilrapporter inkluderer data fra systemets forskjellige loggfiler. Dette omfatter personlig og privat informasjon. Du bør bare dele feilrapporter med apper og folk du stoler på."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Vis denne meldingen neste gang"</string>
- <!-- no translation found for bugreport_storage_title (5332488144740527109) -->
- <skip />
+ <string name="bugreport_storage_title" msgid="5332488144740527109">"Feilrapporter"</string>
</resources>
diff --git a/packages/Shell/res/values-ne-rNP/strings.xml b/packages/Shell/res/values-ne-rNP/strings.xml
index 71c393f..7344889 100644
--- a/packages/Shell/res/values-ne-rNP/strings.xml
+++ b/packages/Shell/res/values-ne-rNP/strings.xml
@@ -22,6 +22,5 @@
<string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"तपाईंको बग रिपोर्ट साझेदारी गर्न छुनुहोस्"</string>
<string name="bugreport_confirm" msgid="5130698467795669780">"बग रिपोर्टहरूमा प्रणालीका विभिन्न लग फाइलहरूबाट व्यक्तिगत तथा नीजि सूचनासहितको डेटा रहन्छ। बग रिपोर्टहरू अनुप्रयोगहरू र तपाईँले विश्वास गरेका व्यक्तिहरूसँग मात्र साझेदारी गर्नुहोस्।"</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"यो सन्देश अर्को पटक देखाउनुहोस्"</string>
- <!-- no translation found for bugreport_storage_title (5332488144740527109) -->
- <skip />
+ <string name="bugreport_storage_title" msgid="5332488144740527109">"बग रिपोर्टहरू"</string>
</resources>
diff --git a/packages/Shell/res/values-nl/strings.xml b/packages/Shell/res/values-nl/strings.xml
index 2f0951b..2936387 100644
--- a/packages/Shell/res/values-nl/strings.xml
+++ b/packages/Shell/res/values-nl/strings.xml
@@ -22,6 +22,5 @@
<string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Raak aan om uw foutenrapport te delen"</string>
<string name="bugreport_confirm" msgid="5130698467795669780">"Foutenrapporten bevatten gegevens uit de verschillende logbestanden van het systeem, waaronder persoonlijke en privégegevens. Deel foutenrapporten alleen met apps en mensen die u vertrouwt."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Dit bericht de volgende keer weergeven"</string>
- <!-- no translation found for bugreport_storage_title (5332488144740527109) -->
- <skip />
+ <string name="bugreport_storage_title" msgid="5332488144740527109">"Foutenrapporten"</string>
</resources>
diff --git a/packages/Shell/res/values-pl/strings.xml b/packages/Shell/res/values-pl/strings.xml
index b97070d..266f070 100644
--- a/packages/Shell/res/values-pl/strings.xml
+++ b/packages/Shell/res/values-pl/strings.xml
@@ -22,6 +22,5 @@
<string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Kliknij, by udostępnić raport o błędach"</string>
<string name="bugreport_confirm" msgid="5130698467795669780">"Raporty o błędach zawierają dane z różnych plików dzienników systemu, w tym dane osobowe i prywatne. Udostępniaj je tylko aplikacjom i osobom, którym ufasz."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Pokaż ten komunikat następnym razem"</string>
- <!-- no translation found for bugreport_storage_title (5332488144740527109) -->
- <skip />
+ <string name="bugreport_storage_title" msgid="5332488144740527109">"Zgłoszenia błędów"</string>
</resources>
diff --git a/packages/Shell/res/values-pt-rPT/strings.xml b/packages/Shell/res/values-pt-rPT/strings.xml
index 24ebc64..648ef94 100644
--- a/packages/Shell/res/values-pt-rPT/strings.xml
+++ b/packages/Shell/res/values-pt-rPT/strings.xml
@@ -22,6 +22,5 @@
<string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Toque para partilhar o relatório de erros"</string>
<string name="bugreport_confirm" msgid="5130698467795669780">"Os relatórios de erros incluem dados de vários ficheiros de registo do sistema, nomeadamente informações pessoais e privadas. Partilhe relatórios de erros apenas com aplicações e pessoas fidedignas."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Mostrar esta mensagem da próxima vez"</string>
- <!-- no translation found for bugreport_storage_title (5332488144740527109) -->
- <skip />
+ <string name="bugreport_storage_title" msgid="5332488144740527109">"Relatórios de erros"</string>
</resources>
diff --git a/packages/Shell/res/values-pt/strings.xml b/packages/Shell/res/values-pt/strings.xml
index c7c00b9..e04d600 100644
--- a/packages/Shell/res/values-pt/strings.xml
+++ b/packages/Shell/res/values-pt/strings.xml
@@ -22,6 +22,5 @@
<string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Toque para compartilhar seu relatório de bugs"</string>
<string name="bugreport_confirm" msgid="5130698467795669780">"Os relatórios de bugs contêm dados de diversos arquivos de registro do sistema, inclusive informações pessoais e particulares. Compartilhe relatórios de bugs somente com apps e pessoas nos quais você confia."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Mostrar esta mensagem da próxima vez"</string>
- <!-- no translation found for bugreport_storage_title (5332488144740527109) -->
- <skip />
+ <string name="bugreport_storage_title" msgid="5332488144740527109">"Relatórios de bugs"</string>
</resources>
diff --git a/packages/Shell/res/values-ro/strings.xml b/packages/Shell/res/values-ro/strings.xml
index 4b78a42..aab29b7 100644
--- a/packages/Shell/res/values-ro/strings.xml
+++ b/packages/Shell/res/values-ro/strings.xml
@@ -22,6 +22,5 @@
<string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Atingeți pentru a permite accesul la raportul despre erori"</string>
<string name="bugreport_confirm" msgid="5130698467795669780">"Rapoartele despre erori conțin date din diferite fișiere de jurnal ale sistemului, inclusiv informații private și personale. Permiteți accesul la rapoartele despre erori numai aplicațiilor și persoanelor în care aveți încredere."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Afișați acest mesaj data viitoare"</string>
- <!-- no translation found for bugreport_storage_title (5332488144740527109) -->
- <skip />
+ <string name="bugreport_storage_title" msgid="5332488144740527109">"Rapoarte de erori"</string>
</resources>
diff --git a/packages/Shell/res/values-ru/strings.xml b/packages/Shell/res/values-ru/strings.xml
index c353654..1f1444d 100644
--- a/packages/Shell/res/values-ru/strings.xml
+++ b/packages/Shell/res/values-ru/strings.xml
@@ -22,6 +22,5 @@
<string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Нажмите, чтобы отправить отчет об ошибках"</string>
<string name="bugreport_confirm" msgid="5130698467795669780">"Отчеты об ошибках содержат данные различных системных журналов и могут включать личную информацию. Рекомендуем открывать к ним доступ только лицам и приложениям, заслуживающим доверие."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Показать это сообщение в следующий раз"</string>
- <!-- no translation found for bugreport_storage_title (5332488144740527109) -->
- <skip />
+ <string name="bugreport_storage_title" msgid="5332488144740527109">"Отчеты об ошибках"</string>
</resources>
diff --git a/packages/Shell/res/values-si-rLK/strings.xml b/packages/Shell/res/values-si-rLK/strings.xml
index f3fabed..4244f2b 100644
--- a/packages/Shell/res/values-si-rLK/strings.xml
+++ b/packages/Shell/res/values-si-rLK/strings.xml
@@ -22,6 +22,5 @@
<string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"ඔබගේ දෝෂ වාර්තාව බෙදා ගැනීමට ස්පර්ශ කරන්න"</string>
<string name="bugreport_confirm" msgid="5130698467795669780">"පුද්ගලික සහ පෞද්ගලික තොරතුරු ඇතුළත්ව පද්ධතියේ විවිධ ලොග් ගොනු වල දත්ත දෝෂ වාර්තාවේ අඩංගු වේ. ඔබට විශ්වාසවන්ත යෙදුම් සහ පුද්ගලයින් සමඟ පමණක් දෝෂ වාර්තා බෙදා ගන්න."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"ඊළඟ වෙලාවේ මෙම පණිවිඩය පෙන්වන්න"</string>
- <!-- no translation found for bugreport_storage_title (5332488144740527109) -->
- <skip />
+ <string name="bugreport_storage_title" msgid="5332488144740527109">"දෝෂ වාර්තා"</string>
</resources>
diff --git a/packages/Shell/res/values-sk/strings.xml b/packages/Shell/res/values-sk/strings.xml
index 9a42dce..a79059d 100644
--- a/packages/Shell/res/values-sk/strings.xml
+++ b/packages/Shell/res/values-sk/strings.xml
@@ -22,6 +22,5 @@
<string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Hlásenie o chybách môžete zdielať klepnutím"</string>
<string name="bugreport_confirm" msgid="5130698467795669780">"Správy o chybách obsahujú údaje z rôznych súborov denníkov systému vrátane osobných a súkromných informácií. Zdieľajte ich iba s dôveryhodnými aplikáciami a ľuďmi."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Zobraziť túto správu nabudúce"</string>
- <!-- no translation found for bugreport_storage_title (5332488144740527109) -->
- <skip />
+ <string name="bugreport_storage_title" msgid="5332488144740527109">"Hlásenia o chybe"</string>
</resources>
diff --git a/packages/Shell/res/values-sl/strings.xml b/packages/Shell/res/values-sl/strings.xml
index d736c91..8c3bedc 100644
--- a/packages/Shell/res/values-sl/strings.xml
+++ b/packages/Shell/res/values-sl/strings.xml
@@ -22,6 +22,5 @@
<string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Dotaknite se, če želite deliti sporočilo o napaki z drugimi"</string>
<string name="bugreport_confirm" msgid="5130698467795669780">"Poročila o napakah vsebujejo podatke iz različnih dnevniških datotek sistema, vključno z osebnimi in zasebnimi podatki. Poročila o napakah delite samo z aplikacijami in ljudmi, ki jim zaupate."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Pokaži to sporočilo naslednjič"</string>
- <!-- no translation found for bugreport_storage_title (5332488144740527109) -->
- <skip />
+ <string name="bugreport_storage_title" msgid="5332488144740527109">"Poročila o napakah"</string>
</resources>
diff --git a/packages/Shell/res/values-sr/strings.xml b/packages/Shell/res/values-sr/strings.xml
index bfd83e7..763bd2b 100644
--- a/packages/Shell/res/values-sr/strings.xml
+++ b/packages/Shell/res/values-sr/strings.xml
@@ -22,6 +22,5 @@
<string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Додирните да бисте делили извештај о грешци"</string>
<string name="bugreport_confirm" msgid="5130698467795669780">"Извештаји о грешкама садрже податке из различитих системских датотека евиденције, укључујући личне и приватне податке. Делите извештаје о грешкама само са апликацијама и људима у које имате поверења."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Прикажи ову поруку следећи пут"</string>
- <!-- no translation found for bugreport_storage_title (5332488144740527109) -->
- <skip />
+ <string name="bugreport_storage_title" msgid="5332488144740527109">"Извештаји о грешкама"</string>
</resources>
diff --git a/packages/Shell/res/values-sv/strings.xml b/packages/Shell/res/values-sv/strings.xml
index 93e4845..47ee2f7 100644
--- a/packages/Shell/res/values-sv/strings.xml
+++ b/packages/Shell/res/values-sv/strings.xml
@@ -22,6 +22,5 @@
<string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Tryck om du vill dela felrapporten"</string>
<string name="bugreport_confirm" msgid="5130698467795669780">"Felrapporter innehåller data från systemets olika loggfiler, inklusive personliga och privata uppgifter. Dela bara felrapporter med personer du litar på."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Visa det här meddelandet nästa gång"</string>
- <!-- no translation found for bugreport_storage_title (5332488144740527109) -->
- <skip />
+ <string name="bugreport_storage_title" msgid="5332488144740527109">"Felrapporter"</string>
</resources>
diff --git a/packages/Shell/res/values-sw/strings.xml b/packages/Shell/res/values-sw/strings.xml
index 620b090..0d12cc9 100644
--- a/packages/Shell/res/values-sw/strings.xml
+++ b/packages/Shell/res/values-sw/strings.xml
@@ -22,6 +22,5 @@
<string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Gusa ili ushiriki ripoti yako ya hitilafu"</string>
<string name="bugreport_confirm" msgid="5130698467795669780">"Ripoti ya hitilafu ina data kutoka kwenye faili za kumbukumbu mbalimbali za mfumo, pamoja na maelezo ya kibinafsi na faragha. Shiriki ripoti ya hitilafu na programu na watu unaowaamini pekee."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Onyesha ujumbe huu wakati mwingine"</string>
- <!-- no translation found for bugreport_storage_title (5332488144740527109) -->
- <skip />
+ <string name="bugreport_storage_title" msgid="5332488144740527109">"Ripoti za hitilafu"</string>
</resources>
diff --git a/packages/Shell/res/values-ta-rIN/strings.xml b/packages/Shell/res/values-ta-rIN/strings.xml
index c0e3b28..cd0462b 100644
--- a/packages/Shell/res/values-ta-rIN/strings.xml
+++ b/packages/Shell/res/values-ta-rIN/strings.xml
@@ -22,6 +22,5 @@
<string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"உங்கள் பிழை அறிக்கையைப் பகிர, தொடவும்"</string>
<string name="bugreport_confirm" msgid="5130698467795669780">"பிழை அறிக்கைகளில், சொந்த வாழ்க்கை மற்றும் தனிப்பட்ட தகவல் உள்பட கணினியின் பல்வேறு பதிவுகளில் உள்ள தரவு இருக்கும். நீங்கள் நம்பும் பயன்பாடுகள் மற்றும் நபர்களுடன் மட்டும் பிழை அறிக்கைகளைப் பகிரவும்."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"இந்தச் செய்தியை அடுத்த முறைக் காட்டு"</string>
- <!-- no translation found for bugreport_storage_title (5332488144740527109) -->
- <skip />
+ <string name="bugreport_storage_title" msgid="5332488144740527109">"பிழை அறிக்கைகள்"</string>
</resources>
diff --git a/packages/Shell/res/values-te-rIN/strings.xml b/packages/Shell/res/values-te-rIN/strings.xml
index 78ed6ee..127f602 100644
--- a/packages/Shell/res/values-te-rIN/strings.xml
+++ b/packages/Shell/res/values-te-rIN/strings.xml
@@ -22,6 +22,5 @@
<string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"మీ బగ్ నివేదికను భాగస్వామ్యం చేయడానికి తాకండి"</string>
<string name="bugreport_confirm" msgid="5130698467795669780">"బగ్ నివేదికలు వ్యక్తిగతమైన మరియు రహస్యమైన సమాచారంతో సహా సిస్టమ్ యొక్క విభిన్న లాగ్ ఫైల్ల్లోని డేటాను కలిగి ఉంటాయి. కనుక బగ్ నివేదికలను మీరు విశ్వసించే అనువర్తనాలు మరియు వ్యక్తులతో మాత్రమే భాగస్వామ్యం చేయండి."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"తదుపరిసారి ఈ సందేశాన్ని చూపు"</string>
- <!-- no translation found for bugreport_storage_title (5332488144740527109) -->
- <skip />
+ <string name="bugreport_storage_title" msgid="5332488144740527109">"బగ్ నివేదికలు"</string>
</resources>
diff --git a/packages/Shell/res/values-th/strings.xml b/packages/Shell/res/values-th/strings.xml
index 0c14f81..2bc8f0d 100644
--- a/packages/Shell/res/values-th/strings.xml
+++ b/packages/Shell/res/values-th/strings.xml
@@ -22,6 +22,5 @@
<string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"แตะเพื่อแชร์รายงานข้อบกพร่องของคุณ"</string>
<string name="bugreport_confirm" msgid="5130698467795669780">"รายงานข้อบกพร่องมีข้อมูลจากไฟล์บันทึกต่างๆ ของระบบ รวมถึงข้อมูลส่วนตัว แชร์รายงานข้อบกพร่องกับแอปและบุคคลที่คุณไว้ใจเท่านั้น"</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"แสดงข้อความนี้ในครั้งต่อไป"</string>
- <!-- no translation found for bugreport_storage_title (5332488144740527109) -->
- <skip />
+ <string name="bugreport_storage_title" msgid="5332488144740527109">"รายงานข้อบกพร่อง"</string>
</resources>
diff --git a/packages/Shell/res/values-tl/strings.xml b/packages/Shell/res/values-tl/strings.xml
index 63a3c4f..a5c0e8a 100644
--- a/packages/Shell/res/values-tl/strings.xml
+++ b/packages/Shell/res/values-tl/strings.xml
@@ -22,6 +22,5 @@
<string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Pindutin upang ibahagi ang iyong ulat ng bug"</string>
<string name="bugreport_confirm" msgid="5130698467795669780">"Naglalaman ang mga ulat ng bug ng data mula sa iba\'t ibang file ng log ng system, kabilang ang personal at pribadong impormasyon. Magbahagi lang ng mga ulat ng bug sa apps at mga tao na pinagkakatiwalaan mo."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Ipakita ang mensaheng ito sa susunod"</string>
- <!-- no translation found for bugreport_storage_title (5332488144740527109) -->
- <skip />
+ <string name="bugreport_storage_title" msgid="5332488144740527109">"Mga ulat sa bug"</string>
</resources>
diff --git a/packages/Shell/res/values-tr/strings.xml b/packages/Shell/res/values-tr/strings.xml
index 8dddb9f..67390b7 100644
--- a/packages/Shell/res/values-tr/strings.xml
+++ b/packages/Shell/res/values-tr/strings.xml
@@ -22,6 +22,5 @@
<string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Hata raporunuzu paylaşmak için dokunun"</string>
<string name="bugreport_confirm" msgid="5130698467795669780">"Hata raporları, kişisel ve özel bilgiler dahil olmak üzere sistemin çeşitli günlük dosyalarından veriler içerir. Hata raporlarını sadece güvendiğiniz uygulamalar ve kişilerle paylaşın."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Bir dahaki sefere bu iletiyi göster"</string>
- <!-- no translation found for bugreport_storage_title (5332488144740527109) -->
- <skip />
+ <string name="bugreport_storage_title" msgid="5332488144740527109">"Hata raporları"</string>
</resources>
diff --git a/packages/Shell/res/values-uk/strings.xml b/packages/Shell/res/values-uk/strings.xml
index ca10a05..f8f1798 100644
--- a/packages/Shell/res/values-uk/strings.xml
+++ b/packages/Shell/res/values-uk/strings.xml
@@ -22,6 +22,5 @@
<string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Торкніться, щоб надіслати звіт про помилки"</string>
<string name="bugreport_confirm" msgid="5130698467795669780">"Звіти про помилки містять дані з різних файлів журналу системи, зокрема особисті та конфіденційні. Надсилайте звіт про помилки лише тим, кому довіряєте."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Показати це повідомлення наступного разу"</string>
- <!-- no translation found for bugreport_storage_title (5332488144740527109) -->
- <skip />
+ <string name="bugreport_storage_title" msgid="5332488144740527109">"Звіти про помилки"</string>
</resources>
diff --git a/packages/Shell/res/values-ur-rPK/strings.xml b/packages/Shell/res/values-ur-rPK/strings.xml
index 948966f..73d6877 100644
--- a/packages/Shell/res/values-ur-rPK/strings.xml
+++ b/packages/Shell/res/values-ur-rPK/strings.xml
@@ -22,6 +22,5 @@
<string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"اپنی بَگ رپورٹ کا اشتراک کرنے کیلئے ٹچ کریں"</string>
<string name="bugreport_confirm" msgid="5130698467795669780">"بَگ رپورٹس میں سسٹم کی مختلف لاگ فائلوں سے ڈیٹا شامل ہوتا ہے، بشمول ذاتی اور نجی معلومات۔ بَگ رپورٹس کا اشتراک صرف اپنے بھروسے مند ایپس اور لوگوں کے ساتھ کریں۔"</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"یہ پیغام اگلی بار دکھائیں"</string>
- <!-- no translation found for bugreport_storage_title (5332488144740527109) -->
- <skip />
+ <string name="bugreport_storage_title" msgid="5332488144740527109">"بگ رپورٹس"</string>
</resources>
diff --git a/packages/Shell/res/values-uz-rUZ/strings.xml b/packages/Shell/res/values-uz-rUZ/strings.xml
index ff76706..b221171 100644
--- a/packages/Shell/res/values-uz-rUZ/strings.xml
+++ b/packages/Shell/res/values-uz-rUZ/strings.xml
@@ -22,6 +22,5 @@
<string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Xatolik hisobotini bo‘lishish uchun barmog‘ingizni tegizing."</string>
<string name="bugreport_confirm" msgid="5130698467795669780">"Xatolik hisobotlari tizimdagi har xil jurnal fayllardagi ma’lumotlarni, shuningdek, shaxsiy hamda maxfiy ma’lumotlarni o‘z ichiga oladi. Xatolik hisobotlarini faqat ishonchli dasturlar va odamlar bilan bo‘lishing."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Ushbu xabar keyingi safar ko‘rsatilsin"</string>
- <!-- no translation found for bugreport_storage_title (5332488144740527109) -->
- <skip />
+ <string name="bugreport_storage_title" msgid="5332488144740527109">"Xatoliklar hisoboti"</string>
</resources>
diff --git a/packages/Shell/res/values-vi/strings.xml b/packages/Shell/res/values-vi/strings.xml
index 61db64e..16a7df9 100644
--- a/packages/Shell/res/values-vi/strings.xml
+++ b/packages/Shell/res/values-vi/strings.xml
@@ -22,6 +22,5 @@
<string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Chạm để chia sẻ báo cáo lỗi của bạn"</string>
<string name="bugreport_confirm" msgid="5130698467795669780">"Các báo cáo lỗi chứa dữ liệu từ nhiều tệp nhật ký khác nhau của hệ thống, bao gồm cả thông tin cá nhân và riêng tư. Chỉ chia sẻ báo cáo lỗi với các ứng dụng và những người mà bạn tin tưởng."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Hiển thị thông báo này vào lần tới"</string>
- <!-- no translation found for bugreport_storage_title (5332488144740527109) -->
- <skip />
+ <string name="bugreport_storage_title" msgid="5332488144740527109">"Báo cáo lỗi"</string>
</resources>
diff --git a/packages/Shell/res/values-zh-rCN/strings.xml b/packages/Shell/res/values-zh-rCN/strings.xml
index 02ca707..17fdedd 100644
--- a/packages/Shell/res/values-zh-rCN/strings.xml
+++ b/packages/Shell/res/values-zh-rCN/strings.xml
@@ -22,6 +22,5 @@
<string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"触摸即可分享您的错误报告"</string>
<string name="bugreport_confirm" msgid="5130698467795669780">"错误报告包含的数据来自于系统的各个日志文件,其中包含个人信息和隐私信息。请务必只与您信任的应用和用户分享错误报告。"</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"下次再显示这条讯息"</string>
- <!-- no translation found for bugreport_storage_title (5332488144740527109) -->
- <skip />
+ <string name="bugreport_storage_title" msgid="5332488144740527109">"错误报告"</string>
</resources>
diff --git a/packages/Shell/res/values-zh-rHK/strings.xml b/packages/Shell/res/values-zh-rHK/strings.xml
index df86fdb..0f70bab 100644
--- a/packages/Shell/res/values-zh-rHK/strings.xml
+++ b/packages/Shell/res/values-zh-rHK/strings.xml
@@ -22,6 +22,5 @@
<string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"輕觸即可分享您的錯誤報告"</string>
<string name="bugreport_confirm" msgid="5130698467795669780">"錯誤報告中有來自系統各個記錄檔案的資料,包括個人和私人資料。請只與您信任的應用程式和使用者分享錯誤報告。"</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"下次再顯示這則訊息"</string>
- <!-- no translation found for bugreport_storage_title (5332488144740527109) -->
- <skip />
+ <string name="bugreport_storage_title" msgid="5332488144740527109">"錯誤報告"</string>
</resources>
diff --git a/packages/Shell/res/values-zh-rTW/strings.xml b/packages/Shell/res/values-zh-rTW/strings.xml
index fd16ab2..d7f7507 100644
--- a/packages/Shell/res/values-zh-rTW/strings.xml
+++ b/packages/Shell/res/values-zh-rTW/strings.xml
@@ -22,6 +22,5 @@
<string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"輕觸即可分享您的錯誤報告"</string>
<string name="bugreport_confirm" msgid="5130698467795669780">"錯誤報告的資料來自系統各個紀錄檔,包括個人和私密資訊。請務必只與您信任的應用程式和使用者分享錯誤報告。"</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"下次仍顯示這則訊息"</string>
- <!-- no translation found for bugreport_storage_title (5332488144740527109) -->
- <skip />
+ <string name="bugreport_storage_title" msgid="5332488144740527109">"錯誤報告"</string>
</resources>
diff --git a/packages/Shell/res/values-zu/strings.xml b/packages/Shell/res/values-zu/strings.xml
index 707cee96..c82fb93 100644
--- a/packages/Shell/res/values-zu/strings.xml
+++ b/packages/Shell/res/values-zu/strings.xml
@@ -22,6 +22,5 @@
<string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Thinta ukuze wabelane ngombiko wakho wesiphazamisi"</string>
<string name="bugreport_confirm" msgid="5130698467795669780">"Imibiko yeziphazamisi iqukethe idatha yamafayela wokungena ahlukile wesistimu, afaka ulwazi lomuntu siqu noma lobumfihlo. Yabelana kuphela ngemibiko yeziphazamisi nezinhlelo zokusebenza nabantu obathembayo."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Bonisa lo mlayezo ngesikhathi esilandelayo"</string>
- <!-- no translation found for bugreport_storage_title (5332488144740527109) -->
- <skip />
+ <string name="bugreport_storage_title" msgid="5332488144740527109">"Imibiko yeziphazamiso"</string>
</resources>
diff --git a/packages/SystemUI/res/layout-sw600dp/recents_task_resize_dialog.xml b/packages/SystemUI/res/layout-sw600dp-land/recents_task_resize_dialog.xml
similarity index 84%
copy from packages/SystemUI/res/layout-sw600dp/recents_task_resize_dialog.xml
copy to packages/SystemUI/res/layout-sw600dp-land/recents_task_resize_dialog.xml
index 29e4bce..26c9b1a 100644
--- a/packages/SystemUI/res/layout-sw600dp/recents_task_resize_dialog.xml
+++ b/packages/SystemUI/res/layout-sw600dp-land/recents_task_resize_dialog.xml
@@ -41,20 +41,6 @@
android:layout_margin="10dp"
android:background="@drawable/vector_drawable_place_right" />
<Button
- android:id="@+id/place_top"
- android:layout_width="36dp"
- android:layout_height="36dp"
- android:layout_weight="1"
- android:layout_margin="10dp"
- android:background="@drawable/vector_drawable_place_top" />
- <Button
- android:id="@+id/place_bottom"
- android:layout_width="36dp"
- android:layout_height="36dp"
- android:layout_weight="1"
- android:layout_margin="10dp"
- android:background="@drawable/vector_drawable_place_bottom" />
- <Button
android:id="@+id/place_top_left"
android:layout_width="36dp"
android:layout_height="36dp"
diff --git a/packages/SystemUI/res/layout-sw600dp/recents_task_resize_dialog.xml b/packages/SystemUI/res/layout-sw600dp-port/recents_task_resize_dialog.xml
similarity index 84%
rename from packages/SystemUI/res/layout-sw600dp/recents_task_resize_dialog.xml
rename to packages/SystemUI/res/layout-sw600dp-port/recents_task_resize_dialog.xml
index 29e4bce..e180daa 100644
--- a/packages/SystemUI/res/layout-sw600dp/recents_task_resize_dialog.xml
+++ b/packages/SystemUI/res/layout-sw600dp-port/recents_task_resize_dialog.xml
@@ -27,20 +27,6 @@
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
- android:id="@+id/place_left"
- android:layout_width="36dp"
- android:layout_height="36dp"
- android:layout_weight="1"
- android:layout_margin="10dp"
- android:background="@drawable/vector_drawable_place_left" />
- <Button
- android:id="@+id/place_right"
- android:layout_width="36dp"
- android:layout_height="36dp"
- android:layout_weight="1"
- android:layout_margin="10dp"
- android:background="@drawable/vector_drawable_place_right" />
- <Button
android:id="@+id/place_top"
android:layout_width="36dp"
android:layout_height="36dp"
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index 4413185..a74e120 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -88,8 +88,7 @@
<string name="unlock_label" msgid="8779712358041029439">"ontsluit"</string>
<string name="phone_label" msgid="2320074140205331708">"maak foon oop"</string>
<string name="camera_label" msgid="7261107956054836961">"maak kamera oop"</string>
- <!-- no translation found for recents_caption_resize (3517056471774958200) -->
- <skip />
+ <string name="recents_caption_resize" msgid="3517056471774958200">"Kies nuwe taakuitleg"</string>
<string name="cancel" msgid="6442560571259935130">"Kanselleer"</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>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index 789df15..0c9aa53 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -88,8 +88,7 @@
<string name="unlock_label" msgid="8779712358041029439">"ክፈት"</string>
<string name="phone_label" msgid="2320074140205331708">"ስልክ ክፈት"</string>
<string name="camera_label" msgid="7261107956054836961">"ካሜራ ክፈት"</string>
- <!-- no translation found for recents_caption_resize (3517056471774958200) -->
- <skip />
+ <string name="recents_caption_resize" msgid="3517056471774958200">"የአዲስ ተግባር አቀማመጥን ይምረጡ"</string>
<string name="cancel" msgid="6442560571259935130">"ይቅር"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"የተኳኋኝአጉላ አዝራር።"</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"አነስተኛውን ማያ ወደ ትልቅ አጉላ።"</string>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index 081ce59..de7250d 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -92,8 +92,7 @@
<string name="unlock_label" msgid="8779712358041029439">"إلغاء القفل"</string>
<string name="phone_label" msgid="2320074140205331708">"فتح الهاتف"</string>
<string name="camera_label" msgid="7261107956054836961">"فتح الكاميرا"</string>
- <!-- no translation found for recents_caption_resize (3517056471774958200) -->
- <skip />
+ <string name="recents_caption_resize" msgid="3517056471774958200">"تحديد تنسيق جديد للمهمة"</string>
<string name="cancel" msgid="6442560571259935130">"إلغاء"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"زر تكبير/تصغير للتوافق."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"استخدام التكبير/التصغير لتحويل شاشة صغيرة إلى شاشة أكبر"</string>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index 5d57b45..1f69b11 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -88,8 +88,7 @@
<string name="unlock_label" msgid="8779712358041029439">"отключване"</string>
<string name="phone_label" msgid="2320074140205331708">"отваряне на телефона"</string>
<string name="camera_label" msgid="7261107956054836961">"отваряне на камерата"</string>
- <!-- no translation found for recents_caption_resize (3517056471774958200) -->
- <skip />
+ <string name="recents_caption_resize" msgid="3517056471774958200">"Избиране на ново оформление за задачите"</string>
<string name="cancel" msgid="6442560571259935130">"Отказ"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Бутон за промяна на мащаба с цел съвместимост."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Промяна на мащаба на екрана от по-малък до по-голям."</string>
diff --git a/packages/SystemUI/res/values-bn-rBD/strings.xml b/packages/SystemUI/res/values-bn-rBD/strings.xml
index 7edb583..26fd0f4 100644
--- a/packages/SystemUI/res/values-bn-rBD/strings.xml
+++ b/packages/SystemUI/res/values-bn-rBD/strings.xml
@@ -88,8 +88,7 @@
<string name="unlock_label" msgid="8779712358041029439">"আনলক করুন"</string>
<string name="phone_label" msgid="2320074140205331708">"ফোন খুলুন"</string>
<string name="camera_label" msgid="7261107956054836961">"ক্যামেরা খুলুন"</string>
- <!-- no translation found for recents_caption_resize (3517056471774958200) -->
- <skip />
+ <string name="recents_caption_resize" msgid="3517056471774958200">"নতুন কার্য লেআউট নির্বাচন করুন"</string>
<string name="cancel" msgid="6442560571259935130">"বাতিল করুন"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"সামঞ্জস্যের জুম বোতাম৷"</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"ছোট থেকে বৃহৎ স্ক্রীণে জুম করুন৷"</string>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index 02c4adc..2454adb 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -88,8 +88,7 @@
<string name="unlock_label" msgid="8779712358041029439">"desbloqueja"</string>
<string name="phone_label" msgid="2320074140205331708">"obre el telèfon"</string>
<string name="camera_label" msgid="7261107956054836961">"obre la càmera"</string>
- <!-- no translation found for recents_caption_resize (3517056471774958200) -->
- <skip />
+ <string name="recents_caption_resize" msgid="3517056471774958200">"Selecciona el disseny de la tasca nova"</string>
<string name="cancel" msgid="6442560571259935130">"Cancel·la"</string>
<string name="accessibility_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>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index 4ba18f4..a3ba579 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -90,8 +90,7 @@
<string name="unlock_label" msgid="8779712358041029439">"odemknout"</string>
<string name="phone_label" msgid="2320074140205331708">"otevřít telefon"</string>
<string name="camera_label" msgid="7261107956054836961">"spustit fotoaparát"</string>
- <!-- no translation found for recents_caption_resize (3517056471774958200) -->
- <skip />
+ <string name="recents_caption_resize" msgid="3517056471774958200">"Vybrat nové rozvržení úkolů"</string>
<string name="cancel" msgid="6442560571259935130">"Zrušit"</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>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index 1790319..2d4fa0f 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -88,8 +88,7 @@
<string name="unlock_label" msgid="8779712358041029439">"lås op"</string>
<string name="phone_label" msgid="2320074140205331708">"åbn telefon"</string>
<string name="camera_label" msgid="7261107956054836961">"åbn kamera"</string>
- <!-- no translation found for recents_caption_resize (3517056471774958200) -->
- <skip />
+ <string name="recents_caption_resize" msgid="3517056471774958200">"Vælg nyt opgavelayout"</string>
<string name="cancel" msgid="6442560571259935130">"Annuller"</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>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index d73a775..d97a063 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -88,8 +88,7 @@
<string name="unlock_label" msgid="8779712358041029439">"Entsperren"</string>
<string name="phone_label" msgid="2320074140205331708">"Telefon öffnen"</string>
<string name="camera_label" msgid="7261107956054836961">"Kamera öffnen"</string>
- <!-- no translation found for recents_caption_resize (3517056471774958200) -->
- <skip />
+ <string name="recents_caption_resize" msgid="3517056471774958200">"Neues Aufgabenlayout auswählen"</string>
<string name="cancel" msgid="6442560571259935130">"Abbrechen"</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>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index e4f0edb..dffe8ce 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -88,8 +88,7 @@
<string name="unlock_label" msgid="8779712358041029439">"unlock"</string>
<string name="phone_label" msgid="2320074140205331708">"open phone"</string>
<string name="camera_label" msgid="7261107956054836961">"open camera"</string>
- <!-- no translation found for recents_caption_resize (3517056471774958200) -->
- <skip />
+ <string name="recents_caption_resize" msgid="3517056471774958200">"Select new task layout"</string>
<string name="cancel" msgid="6442560571259935130">"Cancel"</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>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index e4f0edb..dffe8ce 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -88,8 +88,7 @@
<string name="unlock_label" msgid="8779712358041029439">"unlock"</string>
<string name="phone_label" msgid="2320074140205331708">"open phone"</string>
<string name="camera_label" msgid="7261107956054836961">"open camera"</string>
- <!-- no translation found for recents_caption_resize (3517056471774958200) -->
- <skip />
+ <string name="recents_caption_resize" msgid="3517056471774958200">"Select new task layout"</string>
<string name="cancel" msgid="6442560571259935130">"Cancel"</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>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index e4f0edb..dffe8ce 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -88,8 +88,7 @@
<string name="unlock_label" msgid="8779712358041029439">"unlock"</string>
<string name="phone_label" msgid="2320074140205331708">"open phone"</string>
<string name="camera_label" msgid="7261107956054836961">"open camera"</string>
- <!-- no translation found for recents_caption_resize (3517056471774958200) -->
- <skip />
+ <string name="recents_caption_resize" msgid="3517056471774958200">"Select new task layout"</string>
<string name="cancel" msgid="6442560571259935130">"Cancel"</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>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 121004b..af28b22 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -88,8 +88,7 @@
<string name="unlock_label" msgid="8779712358041029439">"desbloquear"</string>
<string name="phone_label" msgid="2320074140205331708">"abrir teléfono"</string>
<string name="camera_label" msgid="7261107956054836961">"abrir cámara"</string>
- <!-- no translation found for recents_caption_resize (3517056471774958200) -->
- <skip />
+ <string name="recents_caption_resize" msgid="3517056471774958200">"Selecciona el nuevo diseño de la tarea."</string>
<string name="cancel" msgid="6442560571259935130">"Cancelar"</string>
<string name="accessibility_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>
diff --git a/packages/SystemUI/res/values-et-rEE/strings.xml b/packages/SystemUI/res/values-et-rEE/strings.xml
index 3ba9df9..110ee53 100644
--- a/packages/SystemUI/res/values-et-rEE/strings.xml
+++ b/packages/SystemUI/res/values-et-rEE/strings.xml
@@ -88,8 +88,7 @@
<string name="unlock_label" msgid="8779712358041029439">"ava lukk"</string>
<string name="phone_label" msgid="2320074140205331708">"ava telefon"</string>
<string name="camera_label" msgid="7261107956054836961">"ava kaamera"</string>
- <!-- no translation found for recents_caption_resize (3517056471774958200) -->
- <skip />
+ <string name="recents_caption_resize" msgid="3517056471774958200">"Uue toimingu paigutuse valimine"</string>
<string name="cancel" msgid="6442560571259935130">"Tühista"</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>
diff --git a/packages/SystemUI/res/values-eu-rES/strings.xml b/packages/SystemUI/res/values-eu-rES/strings.xml
index e69b79d..220a5f0 100644
--- a/packages/SystemUI/res/values-eu-rES/strings.xml
+++ b/packages/SystemUI/res/values-eu-rES/strings.xml
@@ -88,8 +88,7 @@
<string name="unlock_label" msgid="8779712358041029439">"desblokeatu"</string>
<string name="phone_label" msgid="2320074140205331708">"ireki telefonoan"</string>
<string name="camera_label" msgid="7261107956054836961">"ireki kamera"</string>
- <!-- no translation found for recents_caption_resize (3517056471774958200) -->
- <skip />
+ <string name="recents_caption_resize" msgid="3517056471774958200">"Hautatu zereginen diseinua"</string>
<string name="cancel" msgid="6442560571259935130">"Utzi"</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>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index c97da70..b6a6ecb 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -88,8 +88,7 @@
<string name="unlock_label" msgid="8779712358041029439">"بازکردن قفل"</string>
<string name="phone_label" msgid="2320074140205331708">"باز کردن تلفن"</string>
<string name="camera_label" msgid="7261107956054836961">"باز کردن دوربین"</string>
- <!-- no translation found for recents_caption_resize (3517056471774958200) -->
- <skip />
+ <string name="recents_caption_resize" msgid="3517056471774958200">"انتخاب طرحبندی جدید کار"</string>
<string name="cancel" msgid="6442560571259935130">"لغو"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"دکمه بزرگنمایی سازگار."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"بزرگنمایی از صفحههای کوچک تا بزرگ."</string>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index c39f57c..4edd4c2 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -88,8 +88,7 @@
<string name="unlock_label" msgid="8779712358041029439">"avaa lukitus"</string>
<string name="phone_label" msgid="2320074140205331708">"avaa puhelin"</string>
<string name="camera_label" msgid="7261107956054836961">"avaa kamera"</string>
- <!-- no translation found for recents_caption_resize (3517056471774958200) -->
- <skip />
+ <string name="recents_caption_resize" msgid="3517056471774958200">"Valitse uusi tehtävien asettelu"</string>
<string name="cancel" msgid="6442560571259935130">"Peruuta"</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>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index 37857e7..a369dfd 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -88,8 +88,7 @@
<string name="unlock_label" msgid="8779712358041029439">"déverrouiller"</string>
<string name="phone_label" msgid="2320074140205331708">"Ouvrir le téléphone"</string>
<string name="camera_label" msgid="7261107956054836961">"Ouvrir l\'appareil photo"</string>
- <!-- no translation found for recents_caption_resize (3517056471774958200) -->
- <skip />
+ <string name="recents_caption_resize" msgid="3517056471774958200">"Sélectionner un nouveau format de tâche"</string>
<string name="cancel" msgid="6442560571259935130">"Annuler"</string>
<string name="accessibility_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>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index f309545..18dac4c 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -88,8 +88,7 @@
<string name="unlock_label" msgid="8779712358041029439">"déverrouiller"</string>
<string name="phone_label" msgid="2320074140205331708">"ouvrir le téléphone"</string>
<string name="camera_label" msgid="7261107956054836961">"ouvrir l\'appareil photo"</string>
- <!-- no translation found for recents_caption_resize (3517056471774958200) -->
- <skip />
+ <string name="recents_caption_resize" msgid="3517056471774958200">"Sélectionner un nouveau plan de tâche"</string>
<string name="cancel" msgid="6442560571259935130">"Annuler"</string>
<string name="accessibility_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>
diff --git a/packages/SystemUI/res/values-gl-rES/strings.xml b/packages/SystemUI/res/values-gl-rES/strings.xml
index 2d1b31a..75c0cb9 100644
--- a/packages/SystemUI/res/values-gl-rES/strings.xml
+++ b/packages/SystemUI/res/values-gl-rES/strings.xml
@@ -88,8 +88,7 @@
<string name="unlock_label" msgid="8779712358041029439">"desbloquear"</string>
<string name="phone_label" msgid="2320074140205331708">"abrir teléfono"</string>
<string name="camera_label" msgid="7261107956054836961">"abrir cámara"</string>
- <!-- no translation found for recents_caption_resize (3517056471774958200) -->
- <skip />
+ <string name="recents_caption_resize" msgid="3517056471774958200">"Seleccionar novo deseño de tarefas"</string>
<string name="cancel" msgid="6442560571259935130">"Cancelar"</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>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index 390a8c5..5c45c8b 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -88,8 +88,7 @@
<string name="unlock_label" msgid="8779712358041029439">"अनलॉक करें"</string>
<string name="phone_label" msgid="2320074140205331708">"फ़ोन खोलें"</string>
<string name="camera_label" msgid="7261107956054836961">"कैमरा खोलें"</string>
- <!-- no translation found for recents_caption_resize (3517056471774958200) -->
- <skip />
+ <string name="recents_caption_resize" msgid="3517056471774958200">"नया कार्य लेआउट चुनें"</string>
<string name="cancel" msgid="6442560571259935130">"अभी नहीं"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"संगतता ज़ूम बटन."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"छोटी से बड़ी स्क्रीन पर ज़ूम करें."</string>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index d9ef59f..4210738 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -89,8 +89,7 @@
<string name="unlock_label" msgid="8779712358041029439">"otključavanje"</string>
<string name="phone_label" msgid="2320074140205331708">"otvaranje telefona"</string>
<string name="camera_label" msgid="7261107956054836961">"otvaranje fotoaparata"</string>
- <!-- no translation found for recents_caption_resize (3517056471774958200) -->
- <skip />
+ <string name="recents_caption_resize" msgid="3517056471774958200">"Odaberite novi izgled zadataka"</string>
<string name="cancel" msgid="6442560571259935130">"Odustani"</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>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index 34e63cd..2e8c4f3 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -88,8 +88,7 @@
<string name="unlock_label" msgid="8779712358041029439">"feloldás"</string>
<string name="phone_label" msgid="2320074140205331708">"telefon megnyitása"</string>
<string name="camera_label" msgid="7261107956054836961">"kamera megnyitása"</string>
- <!-- no translation found for recents_caption_resize (3517056471774958200) -->
- <skip />
+ <string name="recents_caption_resize" msgid="3517056471774958200">"Új feladatelrendezés kiválasztása"</string>
<string name="cancel" msgid="6442560571259935130">"Mégse"</string>
<string name="accessibility_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>
diff --git a/packages/SystemUI/res/values-hy-rAM/strings.xml b/packages/SystemUI/res/values-hy-rAM/strings.xml
index 8241049..91d36da 100644
--- a/packages/SystemUI/res/values-hy-rAM/strings.xml
+++ b/packages/SystemUI/res/values-hy-rAM/strings.xml
@@ -88,8 +88,7 @@
<string name="unlock_label" msgid="8779712358041029439">"ապակողպել"</string>
<string name="phone_label" msgid="2320074140205331708">"բացել հեռախոսը"</string>
<string name="camera_label" msgid="7261107956054836961">"բացել ֆոտոխցիկը"</string>
- <!-- no translation found for recents_caption_resize (3517056471774958200) -->
- <skip />
+ <string name="recents_caption_resize" msgid="3517056471774958200">"Ընտրել առաջադրանքի նոր դասավորություն"</string>
<string name="cancel" msgid="6442560571259935130">"Չեղարկել"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Համատեղելիության խոշորացման կոճակը:"</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Դիտափոխել փոքրից ավելի մեծ էկրան:"</string>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index 9c5b82b..bacca07 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -88,8 +88,7 @@
<string name="unlock_label" msgid="8779712358041029439">"buka kunci"</string>
<string name="phone_label" msgid="2320074140205331708">"buka ponsel"</string>
<string name="camera_label" msgid="7261107956054836961">"buka kamera"</string>
- <!-- no translation found for recents_caption_resize (3517056471774958200) -->
- <skip />
+ <string name="recents_caption_resize" msgid="3517056471774958200">"Pilih tata letak tugas baru"</string>
<string name="cancel" msgid="6442560571259935130">"Batal"</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>
diff --git a/packages/SystemUI/res/values-is-rIS/strings.xml b/packages/SystemUI/res/values-is-rIS/strings.xml
index db68c96..ffd3361 100644
--- a/packages/SystemUI/res/values-is-rIS/strings.xml
+++ b/packages/SystemUI/res/values-is-rIS/strings.xml
@@ -88,8 +88,7 @@
<string name="unlock_label" msgid="8779712358041029439">"taka úr lás"</string>
<string name="phone_label" msgid="2320074140205331708">"opna síma"</string>
<string name="camera_label" msgid="7261107956054836961">"opna myndavél"</string>
- <!-- no translation found for recents_caption_resize (3517056471774958200) -->
- <skip />
+ <string name="recents_caption_resize" msgid="3517056471774958200">"Velja nýtt útlit verkefna"</string>
<string name="cancel" msgid="6442560571259935130">"Hætta við"</string>
<string name="accessibility_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>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index ba966d0..f7c66f7 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -90,8 +90,7 @@
<string name="unlock_label" msgid="8779712358041029439">"בטל את הנעילה"</string>
<string name="phone_label" msgid="2320074140205331708">"פתח את הטלפון"</string>
<string name="camera_label" msgid="7261107956054836961">"פתח את המצלמה"</string>
- <!-- no translation found for recents_caption_resize (3517056471774958200) -->
- <skip />
+ <string name="recents_caption_resize" msgid="3517056471774958200">"בחר פריסה חדשה להצגת משימות"</string>
<string name="cancel" msgid="6442560571259935130">"ביטול"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"לחצן מרחק מתצוגה של תאימות."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"שנה מרחק מתצוגה של מסך קטן לגדול יותר."</string>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index ce77d20..1d8a2f7 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -88,8 +88,7 @@
<string name="unlock_label" msgid="8779712358041029439">"ロック解除"</string>
<string name="phone_label" msgid="2320074140205331708">"電話を起動"</string>
<string name="camera_label" msgid="7261107956054836961">"カメラを起動"</string>
- <!-- no translation found for recents_caption_resize (3517056471774958200) -->
- <skip />
+ <string name="recents_caption_resize" msgid="3517056471774958200">"新しいタスクレイアウトの選択"</string>
<string name="cancel" msgid="6442560571259935130">"キャンセル"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"互換ズームボタン。"</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"小さい画面から大きい画面に拡大。"</string>
diff --git a/packages/SystemUI/res/values-ka-rGE/strings.xml b/packages/SystemUI/res/values-ka-rGE/strings.xml
index 5ce23e9..9de70e8 100644
--- a/packages/SystemUI/res/values-ka-rGE/strings.xml
+++ b/packages/SystemUI/res/values-ka-rGE/strings.xml
@@ -88,8 +88,7 @@
<string name="unlock_label" msgid="8779712358041029439">"განბლოკვა"</string>
<string name="phone_label" msgid="2320074140205331708">"ტელეფონის გახსნა"</string>
<string name="camera_label" msgid="7261107956054836961">"კამერის გახსნა"</string>
- <!-- no translation found for recents_caption_resize (3517056471774958200) -->
- <skip />
+ <string name="recents_caption_resize" msgid="3517056471774958200">"ახალი ამოცანის განლაგების არჩევა"</string>
<string name="cancel" msgid="6442560571259935130">"გაუქმება"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"თავსებადი მასშტაბირების ღილაკი."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"შეცვალეთ პატარა ეკრანი უფრო დიდით."</string>
diff --git a/packages/SystemUI/res/values-kk-rKZ/strings.xml b/packages/SystemUI/res/values-kk-rKZ/strings.xml
index bf28e29..a340624 100644
--- a/packages/SystemUI/res/values-kk-rKZ/strings.xml
+++ b/packages/SystemUI/res/values-kk-rKZ/strings.xml
@@ -88,8 +88,7 @@
<string name="unlock_label" msgid="8779712358041029439">"бекітпесін ашу"</string>
<string name="phone_label" msgid="2320074140205331708">"телефонды ашу"</string>
<string name="camera_label" msgid="7261107956054836961">"камераны ашу"</string>
- <!-- no translation found for recents_caption_resize (3517056471774958200) -->
- <skip />
+ <string name="recents_caption_resize" msgid="3517056471774958200">"Жаңа тапсырма пішімін таңдау"</string>
<string name="cancel" msgid="6442560571259935130">"Бас тарту"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Үйлесімділік ұлғайту түймесі."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Үлкендеу экранда кішірейту."</string>
diff --git a/packages/SystemUI/res/values-km-rKH/strings.xml b/packages/SystemUI/res/values-km-rKH/strings.xml
index c13c835..4fb737c 100644
--- a/packages/SystemUI/res/values-km-rKH/strings.xml
+++ b/packages/SystemUI/res/values-km-rKH/strings.xml
@@ -88,8 +88,7 @@
<string name="unlock_label" msgid="8779712358041029439">"ដោះសោ"</string>
<string name="phone_label" msgid="2320074140205331708">"បើកទូរស័ព្ទ"</string>
<string name="camera_label" msgid="7261107956054836961">"បើកម៉ាស៊ីនថត"</string>
- <!-- no translation found for recents_caption_resize (3517056471774958200) -->
- <skip />
+ <string name="recents_caption_resize" msgid="3517056471774958200">"ជ្រើសប្លង់ភារកិច្ចថ្មី"</string>
<string name="cancel" msgid="6442560571259935130">"បោះបង់"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"ប៊ូតុងពង្រីកត្រូវគ្នា។"</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"ពង្រីក/បង្រួមអេក្រង់ពីទៅធំ"</string>
diff --git a/packages/SystemUI/res/values-kn-rIN/strings.xml b/packages/SystemUI/res/values-kn-rIN/strings.xml
index 43d242e..87a1bb4 100644
--- a/packages/SystemUI/res/values-kn-rIN/strings.xml
+++ b/packages/SystemUI/res/values-kn-rIN/strings.xml
@@ -88,8 +88,7 @@
<string name="unlock_label" msgid="8779712358041029439">"ಅನ್ಲಾಕ್ ಮಾಡು"</string>
<string name="phone_label" msgid="2320074140205331708">"ಫೋನ್ ತೆರೆಯಿರಿ"</string>
<string name="camera_label" msgid="7261107956054836961">"ಕ್ಯಾಮರಾ ತೆರೆಯಿರಿ"</string>
- <!-- no translation found for recents_caption_resize (3517056471774958200) -->
- <skip />
+ <string name="recents_caption_resize" msgid="3517056471774958200">"ಹೊಸ ಕಾರ್ಯ ವಿನ್ಯಾಸವನ್ನು ಆಯ್ಕೆಮಾಡಿ"</string>
<string name="cancel" msgid="6442560571259935130">"ರದ್ದುಮಾಡು"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"ಹೊಂದಾಣಿಕೆಯ ಝೂಮ್ ಬಟನ್."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"ಚಿಕ್ಕ ಪರದೆಯಿಂದ ದೊಡ್ಡ ಪರದೆಗೆ ಝೂಮ್ ಮಾಡು."</string>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index 2abec1d..884272d 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -88,8 +88,7 @@
<string name="unlock_label" msgid="8779712358041029439">"잠금 해제"</string>
<string name="phone_label" msgid="2320074140205331708">"휴대전화 열기"</string>
<string name="camera_label" msgid="7261107956054836961">"카메라 열기"</string>
- <!-- no translation found for recents_caption_resize (3517056471774958200) -->
- <skip />
+ <string name="recents_caption_resize" msgid="3517056471774958200">"새 작업 레이아웃 선택"</string>
<string name="cancel" msgid="6442560571259935130">"취소"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"호환성 확대/축소 버튼입니다."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"작은 화면을 큰 화면으로 확대합니다."</string>
diff --git a/packages/SystemUI/res/values-ky-rKG/strings.xml b/packages/SystemUI/res/values-ky-rKG/strings.xml
index e20a386..1a7aa8b 100644
--- a/packages/SystemUI/res/values-ky-rKG/strings.xml
+++ b/packages/SystemUI/res/values-ky-rKG/strings.xml
@@ -113,8 +113,7 @@
<string name="unlock_label" msgid="8779712358041029439">"кулпуну ачуу"</string>
<string name="phone_label" msgid="2320074140205331708">"телефонду ачуу"</string>
<string name="camera_label" msgid="7261107956054836961">"камераны ачуу"</string>
- <!-- no translation found for recents_caption_resize (3517056471774958200) -->
- <skip />
+ <string name="recents_caption_resize" msgid="3517056471774958200">"Жаңы тапшырманын планын тандаңыз"</string>
<!-- no translation found for cancel (6442560571259935130) -->
<skip />
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Масштабды сыйыштыруу баскычы."</string>
diff --git a/packages/SystemUI/res/values-lo-rLA/strings.xml b/packages/SystemUI/res/values-lo-rLA/strings.xml
index 7aebf2c..4d215e3 100644
--- a/packages/SystemUI/res/values-lo-rLA/strings.xml
+++ b/packages/SystemUI/res/values-lo-rLA/strings.xml
@@ -88,8 +88,7 @@
<string name="unlock_label" msgid="8779712358041029439">"ປົດລັອກ"</string>
<string name="phone_label" msgid="2320074140205331708">"ເປີດແປ້ນໂທລະສັບ"</string>
<string name="camera_label" msgid="7261107956054836961">"ເປີດກ້ອງ"</string>
- <!-- no translation found for recents_caption_resize (3517056471774958200) -->
- <skip />
+ <string name="recents_caption_resize" msgid="3517056471774958200">"ເລືອກແຜນຜັງໜ້າວຽກໃໝ່"</string>
<string name="cancel" msgid="6442560571259935130">"ຍົກເລີກ"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"ປຸ່ມຊູມທີ່ໃຊ້ຮ່ວມກັນໄດ້."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"ຊູມຈໍນ້ອຍໄປເປັນຈໍຂະຫນາດໃຫຍ່."</string>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index 25539af..1cb5980 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -90,8 +90,7 @@
<string name="unlock_label" msgid="8779712358041029439">"atrakinti"</string>
<string name="phone_label" msgid="2320074140205331708">"atidaryti telefoną"</string>
<string name="camera_label" msgid="7261107956054836961">"atidaryti fotoaparatą"</string>
- <!-- no translation found for recents_caption_resize (3517056471774958200) -->
- <skip />
+ <string name="recents_caption_resize" msgid="3517056471774958200">"Pasirinkti naują užduoties išdėstymą"</string>
<string name="cancel" msgid="6442560571259935130">"Atšaukti"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Suderinamumo priartinimo mygtukas."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Padidinti ekraną."</string>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index 223132b..5fdc0cd 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -89,8 +89,7 @@
<string name="unlock_label" msgid="8779712358041029439">"atbloķēt"</string>
<string name="phone_label" msgid="2320074140205331708">"atvērt tālruni"</string>
<string name="camera_label" msgid="7261107956054836961">"atvērt kameru"</string>
- <!-- no translation found for recents_caption_resize (3517056471774958200) -->
- <skip />
+ <string name="recents_caption_resize" msgid="3517056471774958200">"Atlasiet jaunu uzdevumu izkārtojumu"</string>
<string name="cancel" msgid="6442560571259935130">"Atcelt"</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>
diff --git a/packages/SystemUI/res/values-mk-rMK/strings.xml b/packages/SystemUI/res/values-mk-rMK/strings.xml
index f29d251..83988c0 100644
--- a/packages/SystemUI/res/values-mk-rMK/strings.xml
+++ b/packages/SystemUI/res/values-mk-rMK/strings.xml
@@ -88,8 +88,7 @@
<string name="unlock_label" msgid="8779712358041029439">"отклучи"</string>
<string name="phone_label" msgid="2320074140205331708">"отвори телефон"</string>
<string name="camera_label" msgid="7261107956054836961">"отвори камера"</string>
- <!-- no translation found for recents_caption_resize (3517056471774958200) -->
- <skip />
+ <string name="recents_caption_resize" msgid="3517056471774958200">"Изберете нов распоред на задача"</string>
<string name="cancel" msgid="6442560571259935130">"Откажи"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Копче за компатибилност на зум."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Зумот е помал на поголем екран."</string>
diff --git a/packages/SystemUI/res/values-ml-rIN/strings.xml b/packages/SystemUI/res/values-ml-rIN/strings.xml
index 1413cd1..ee090f9 100644
--- a/packages/SystemUI/res/values-ml-rIN/strings.xml
+++ b/packages/SystemUI/res/values-ml-rIN/strings.xml
@@ -88,8 +88,7 @@
<string name="unlock_label" msgid="8779712358041029439">"അൺലോക്കുചെയ്യുക"</string>
<string name="phone_label" msgid="2320074140205331708">"ഫോൺ തുറക്കുക"</string>
<string name="camera_label" msgid="7261107956054836961">"ക്യാമറ തുറക്കുക"</string>
- <!-- no translation found for recents_caption_resize (3517056471774958200) -->
- <skip />
+ <string name="recents_caption_resize" msgid="3517056471774958200">"പുതിയ ടാസ്ക് ലേഔട്ട് തിരഞ്ഞെടുക്കുക"</string>
<string name="cancel" msgid="6442560571259935130">"റദ്ദാക്കുക"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"അനുയോജ്യതാ സൂം ബട്ടൺ."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"ചെറുതിൽ നിന്ന് വലിയ സ്ക്രീനിലേക്ക് സൂം ചെയ്യുക."</string>
diff --git a/packages/SystemUI/res/values-mn-rMN/strings.xml b/packages/SystemUI/res/values-mn-rMN/strings.xml
index 15334e4..66f287a 100644
--- a/packages/SystemUI/res/values-mn-rMN/strings.xml
+++ b/packages/SystemUI/res/values-mn-rMN/strings.xml
@@ -86,8 +86,7 @@
<string name="unlock_label" msgid="8779712358041029439">"тайлах"</string>
<string name="phone_label" msgid="2320074140205331708">"утас нээх"</string>
<string name="camera_label" msgid="7261107956054836961">"камер нээх"</string>
- <!-- no translation found for recents_caption_resize (3517056471774958200) -->
- <skip />
+ <string name="recents_caption_resize" msgid="3517056471774958200">"Шинэ ажиллах талбарыг сонгоно уу"</string>
<string name="cancel" msgid="6442560571259935130">"Цуцлах"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Тохиромжтой өсгөх товч."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Жижгээс том дэлгэцрүү өсгөх."</string>
diff --git a/packages/SystemUI/res/values-ms-rMY/strings.xml b/packages/SystemUI/res/values-ms-rMY/strings.xml
index 721084b..5ee867f 100644
--- a/packages/SystemUI/res/values-ms-rMY/strings.xml
+++ b/packages/SystemUI/res/values-ms-rMY/strings.xml
@@ -88,8 +88,7 @@
<string name="unlock_label" msgid="8779712358041029439">"buka kunci"</string>
<string name="phone_label" msgid="2320074140205331708">"buka telefon"</string>
<string name="camera_label" msgid="7261107956054836961">"buka kamera"</string>
- <!-- no translation found for recents_caption_resize (3517056471774958200) -->
- <skip />
+ <string name="recents_caption_resize" msgid="3517056471774958200">"Pilih reka letak tugas baharu"</string>
<string name="cancel" msgid="6442560571259935130">"Batal"</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>
diff --git a/packages/SystemUI/res/values-my-rMM/strings.xml b/packages/SystemUI/res/values-my-rMM/strings.xml
index d84290a..8880f93 100644
--- a/packages/SystemUI/res/values-my-rMM/strings.xml
+++ b/packages/SystemUI/res/values-my-rMM/strings.xml
@@ -88,8 +88,7 @@
<string name="unlock_label" msgid="8779712358041029439">"သော့ဖွင့်ရန်"</string>
<string name="phone_label" msgid="2320074140205331708">"ဖုန်းကို ဖွင့်ရန်"</string>
<string name="camera_label" msgid="7261107956054836961">"ကင်မရာ ဖွင့်ရန်"</string>
- <!-- no translation found for recents_caption_resize (3517056471774958200) -->
- <skip />
+ <string name="recents_caption_resize" msgid="3517056471774958200">"အလုပ်သစ်စီစဥ်မှုကို ရွေးပါ။"</string>
<string name="cancel" msgid="6442560571259935130">"ထားတော့"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"အံ့ဝင်သောချုံ့ချဲ့ခလုတ်"</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"ဖန်သားပြင်ပေါ်တွင် အသေးမှအကြီးသို့ချဲ့ခြင်း"</string>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 2a7c6a1..07cf096 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -88,8 +88,7 @@
<string name="unlock_label" msgid="8779712358041029439">"lås opp"</string>
<string name="phone_label" msgid="2320074140205331708">"åpne telefonen"</string>
<string name="camera_label" msgid="7261107956054836961">"åpne kamera"</string>
- <!-- no translation found for recents_caption_resize (3517056471774958200) -->
- <skip />
+ <string name="recents_caption_resize" msgid="3517056471774958200">"Velg en ny utforming for oppgaver"</string>
<string name="cancel" msgid="6442560571259935130">"Avbryt"</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>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index 3b9bb85..4f2bcc5 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -88,8 +88,7 @@
<string name="unlock_label" msgid="8779712358041029439">"ontgrendelen"</string>
<string name="phone_label" msgid="2320074140205331708">"telefoon openen"</string>
<string name="camera_label" msgid="7261107956054836961">"camera openen"</string>
- <!-- no translation found for recents_caption_resize (3517056471774958200) -->
- <skip />
+ <string name="recents_caption_resize" msgid="3517056471774958200">"Nieuwe taakindeling selecteren"</string>
<string name="cancel" msgid="6442560571259935130">"Annuleren"</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>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 1989c8f..7e7253c 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -90,8 +90,7 @@
<string name="unlock_label" msgid="8779712358041029439">"odblokuj"</string>
<string name="phone_label" msgid="2320074140205331708">"otwórz telefon"</string>
<string name="camera_label" msgid="7261107956054836961">"otwórz aparat"</string>
- <!-- no translation found for recents_caption_resize (3517056471774958200) -->
- <skip />
+ <string name="recents_caption_resize" msgid="3517056471774958200">"Wybierz nowy układ zadań"</string>
<string name="cancel" msgid="6442560571259935130">"Anuluj"</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>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index cfe9290..14f60dc 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -88,8 +88,7 @@
<string name="unlock_label" msgid="8779712358041029439">"desbloquear"</string>
<string name="phone_label" msgid="2320074140205331708">"abrir telemóvel"</string>
<string name="camera_label" msgid="7261107956054836961">"abrir câmara"</string>
- <!-- no translation found for recents_caption_resize (3517056471774958200) -->
- <skip />
+ <string name="recents_caption_resize" msgid="3517056471774958200">"Selecionar novo esquema de tarefa"</string>
<string name="cancel" msgid="6442560571259935130">"Cancelar"</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>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index 5d6fe7b..15d869a 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -88,8 +88,7 @@
<string name="unlock_label" msgid="8779712358041029439">"desbloquear"</string>
<string name="phone_label" msgid="2320074140205331708">"abrir telefone"</string>
<string name="camera_label" msgid="7261107956054836961">"abrir câmera"</string>
- <!-- no translation found for recents_caption_resize (3517056471774958200) -->
- <skip />
+ <string name="recents_caption_resize" msgid="3517056471774958200">"Selecionar novo layout da tarefa"</string>
<string name="cancel" msgid="6442560571259935130">"Cancelar"</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>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index 1b67bb4..d517a42 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -89,8 +89,7 @@
<string name="unlock_label" msgid="8779712358041029439">"deblocați"</string>
<string name="phone_label" msgid="2320074140205331708">"deschideți telefonul"</string>
<string name="camera_label" msgid="7261107956054836961">"deschideți camera foto"</string>
- <!-- no translation found for recents_caption_resize (3517056471774958200) -->
- <skip />
+ <string name="recents_caption_resize" msgid="3517056471774958200">"Selectați noul aspect pentru activitate"</string>
<string name="cancel" msgid="6442560571259935130">"Anulați"</string>
<string name="accessibility_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>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index 1b6b2d2..b53b581 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -90,8 +90,7 @@
<string name="unlock_label" msgid="8779712358041029439">"Разблокировать."</string>
<string name="phone_label" msgid="2320074140205331708">"Открыть телефон."</string>
<string name="camera_label" msgid="7261107956054836961">"Открыть камеру."</string>
- <!-- no translation found for recents_caption_resize (3517056471774958200) -->
- <skip />
+ <string name="recents_caption_resize" msgid="3517056471774958200">"Выберите другой макет"</string>
<string name="cancel" msgid="6442560571259935130">"Отмена"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Кнопка масштабирования (режим совместимости)"</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Уменьшение изображения для увеличения свободного места на экране."</string>
diff --git a/packages/SystemUI/res/values-si-rLK/strings.xml b/packages/SystemUI/res/values-si-rLK/strings.xml
index 28bedf5..8bf3a29 100644
--- a/packages/SystemUI/res/values-si-rLK/strings.xml
+++ b/packages/SystemUI/res/values-si-rLK/strings.xml
@@ -88,8 +88,7 @@
<string name="unlock_label" msgid="8779712358041029439">"අඟුල අරින්න"</string>
<string name="phone_label" msgid="2320074140205331708">"දුරකථනය විවෘත කරන්න"</string>
<string name="camera_label" msgid="7261107956054836961">"කැමරාව විවෘත කරන්න"</string>
- <!-- no translation found for recents_caption_resize (3517056471774958200) -->
- <skip />
+ <string name="recents_caption_resize" msgid="3517056471774958200">"නව කාර්යය සැකැස්ම තෝරන්න"</string>
<string name="cancel" msgid="6442560571259935130">"අවලංගු කරන්න"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"ගැළපෙන විශාලන බොත්තම."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"විශාල තිරය වෙත කුඩාව විශාලනය කරන්න."</string>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index ab7d99b..a17b8ec 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -90,8 +90,7 @@
<string name="unlock_label" msgid="8779712358041029439">"odomknúť"</string>
<string name="phone_label" msgid="2320074140205331708">"otvoriť telefón"</string>
<string name="camera_label" msgid="7261107956054836961">"spustiť fotoaparát"</string>
- <!-- no translation found for recents_caption_resize (3517056471774958200) -->
- <skip />
+ <string name="recents_caption_resize" msgid="3517056471774958200">"Vyberte nové rozloženie úlohy"</string>
<string name="cancel" msgid="6442560571259935130">"Zrušiť"</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>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index a75f166..177ef1e 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -90,8 +90,7 @@
<string name="unlock_label" msgid="8779712358041029439">"odkleni"</string>
<string name="phone_label" msgid="2320074140205331708">"odpri telefon"</string>
<string name="camera_label" msgid="7261107956054836961">"odpri fotoaparat"</string>
- <!-- no translation found for recents_caption_resize (3517056471774958200) -->
- <skip />
+ <string name="recents_caption_resize" msgid="3517056471774958200">"Izberite novo postavitev opravil"</string>
<string name="cancel" msgid="6442560571259935130">"Prekliči"</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>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index 1b442ac..0262dd7 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -89,8 +89,7 @@
<string name="unlock_label" msgid="8779712358041029439">"откључај"</string>
<string name="phone_label" msgid="2320074140205331708">"отвори телефон"</string>
<string name="camera_label" msgid="7261107956054836961">"отвори камеру"</string>
- <!-- no translation found for recents_caption_resize (3517056471774958200) -->
- <skip />
+ <string name="recents_caption_resize" msgid="3517056471774958200">"Изабери нови распоред задатака"</string>
<string name="cancel" msgid="6442560571259935130">"Откажи"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Дугме Зум компатибилности."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Зумирање са мањег на већи екран."</string>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index 22c7bbd..71d0596 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -88,8 +88,7 @@
<string name="unlock_label" msgid="8779712358041029439">"lås upp"</string>
<string name="phone_label" msgid="2320074140205331708">"öppna mobilen"</string>
<string name="camera_label" msgid="7261107956054836961">"öppna kameran"</string>
- <!-- no translation found for recents_caption_resize (3517056471774958200) -->
- <skip />
+ <string name="recents_caption_resize" msgid="3517056471774958200">"Välj en ny layout för uppgiften"</string>
<string name="cancel" msgid="6442560571259935130">"Avbryt"</string>
<string name="accessibility_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>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index 4ecb297..1811864 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -88,8 +88,7 @@
<string name="unlock_label" msgid="8779712358041029439">"fungua"</string>
<string name="phone_label" msgid="2320074140205331708">"fungua simu"</string>
<string name="camera_label" msgid="7261107956054836961">"fungua kamera"</string>
- <!-- no translation found for recents_caption_resize (3517056471774958200) -->
- <skip />
+ <string name="recents_caption_resize" msgid="3517056471774958200">"Chagua muundo mpya wa kazi"</string>
<string name="cancel" msgid="6442560571259935130">"Ghairi"</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>
diff --git a/packages/SystemUI/res/values-ta-rIN/strings.xml b/packages/SystemUI/res/values-ta-rIN/strings.xml
index eb80921..1c246c5 100644
--- a/packages/SystemUI/res/values-ta-rIN/strings.xml
+++ b/packages/SystemUI/res/values-ta-rIN/strings.xml
@@ -88,8 +88,7 @@
<string name="unlock_label" msgid="8779712358041029439">"திற"</string>
<string name="phone_label" msgid="2320074140205331708">"ஃபோனைத் திற"</string>
<string name="camera_label" msgid="7261107956054836961">"கேமராவைத் திற"</string>
- <!-- no translation found for recents_caption_resize (3517056471774958200) -->
- <skip />
+ <string name="recents_caption_resize" msgid="3517056471774958200">"புதிய பணி தளவமைப்பைத் தேர்ந்தெடுக்கவும்"</string>
<string name="cancel" msgid="6442560571259935130">"ரத்துசெய்"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"பொருந்துமாறு அளவை மாற்றும் பொத்தான்."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"சிறியதிலிருந்து பெரிய திரைக்கு அளவை மாற்றும்."</string>
diff --git a/packages/SystemUI/res/values-te-rIN/strings.xml b/packages/SystemUI/res/values-te-rIN/strings.xml
index b2613ec..cc5d2ad 100644
--- a/packages/SystemUI/res/values-te-rIN/strings.xml
+++ b/packages/SystemUI/res/values-te-rIN/strings.xml
@@ -88,8 +88,7 @@
<string name="unlock_label" msgid="8779712358041029439">"అన్లాక్ చేయి"</string>
<string name="phone_label" msgid="2320074140205331708">"ఫోన్ను తెరువు"</string>
<string name="camera_label" msgid="7261107956054836961">"కెమెరాను తెరువు"</string>
- <!-- no translation found for recents_caption_resize (3517056471774958200) -->
- <skip />
+ <string name="recents_caption_resize" msgid="3517056471774958200">"కొత్త విధి లేఅవుట్ను ఎంచుకోండి"</string>
<string name="cancel" msgid="6442560571259935130">"రద్దు చేయండి"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"అనుకూలత జూమ్ బటన్."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"చిన్న స్క్రీన్ నుండి పెద్దదానికి జూమ్ చేయండి."</string>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index f4403b8..1c064e5 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -88,8 +88,7 @@
<string name="unlock_label" msgid="8779712358041029439">"ปลดล็อก"</string>
<string name="phone_label" msgid="2320074140205331708">"เปิดโทรศัพท์"</string>
<string name="camera_label" msgid="7261107956054836961">"เปิดกล้อง"</string>
- <!-- no translation found for recents_caption_resize (3517056471774958200) -->
- <skip />
+ <string name="recents_caption_resize" msgid="3517056471774958200">"เลือกรูปแบบงานใหม่"</string>
<string name="cancel" msgid="6442560571259935130">"ยกเลิก"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"ปุ่มซูมที่ใช้งานร่วมกันได้"</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"ซูมหน้าจอให้มีขนาดใหญ่ขึ้น"</string>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index 4c3bb5d..6ecd402 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -88,8 +88,7 @@
<string name="unlock_label" msgid="8779712358041029439">"i-unlock"</string>
<string name="phone_label" msgid="2320074140205331708">"buksan ang telepono"</string>
<string name="camera_label" msgid="7261107956054836961">"buksan ang camera"</string>
- <!-- no translation found for recents_caption_resize (3517056471774958200) -->
- <skip />
+ <string name="recents_caption_resize" msgid="3517056471774958200">"Pumili ng bagong layout ng gawain"</string>
<string name="cancel" msgid="6442560571259935130">"Kanselahin"</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>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index 46cff6f..7209af0 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -88,8 +88,7 @@
<string name="unlock_label" msgid="8779712358041029439">"kilidi aç"</string>
<string name="phone_label" msgid="2320074140205331708">"telefonu aç"</string>
<string name="camera_label" msgid="7261107956054836961">"kamerayı aç"</string>
- <!-- no translation found for recents_caption_resize (3517056471774958200) -->
- <skip />
+ <string name="recents_caption_resize" msgid="3517056471774958200">"Yeni görev düzenini seçin"</string>
<string name="cancel" msgid="6442560571259935130">"İptal"</string>
<string name="accessibility_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>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index 257884d..8c582f8 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -90,8 +90,7 @@
<string name="unlock_label" msgid="8779712358041029439">"розблокувати"</string>
<string name="phone_label" msgid="2320074140205331708">"відкрити телефон"</string>
<string name="camera_label" msgid="7261107956054836961">"відкрити камеру"</string>
- <!-- no translation found for recents_caption_resize (3517056471774958200) -->
- <skip />
+ <string name="recents_caption_resize" msgid="3517056471774958200">"Виберіть новий макет завдання"</string>
<string name="cancel" msgid="6442560571259935130">"Скасувати"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Кнопка масштабування сумісності."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Збільшення екрана."</string>
diff --git a/packages/SystemUI/res/values-ur-rPK/strings.xml b/packages/SystemUI/res/values-ur-rPK/strings.xml
index 3efe14c3..f402071 100644
--- a/packages/SystemUI/res/values-ur-rPK/strings.xml
+++ b/packages/SystemUI/res/values-ur-rPK/strings.xml
@@ -88,8 +88,7 @@
<string name="unlock_label" msgid="8779712358041029439">"غیر مقفل کریں"</string>
<string name="phone_label" msgid="2320074140205331708">"فون کھولیں"</string>
<string name="camera_label" msgid="7261107956054836961">"کیمرا کھولیں"</string>
- <!-- no translation found for recents_caption_resize (3517056471774958200) -->
- <skip />
+ <string name="recents_caption_resize" msgid="3517056471774958200">"نئے کام کا لے آؤٹ منتخب کریں"</string>
<string name="cancel" msgid="6442560571259935130">"منسوخ کریں"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"مطابقت پذیری زوم بٹن۔"</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"چھوٹی سے بڑی اسکرین پر زوم کریں۔"</string>
diff --git a/packages/SystemUI/res/values-uz-rUZ/strings.xml b/packages/SystemUI/res/values-uz-rUZ/strings.xml
index c532f28..782d5e1 100644
--- a/packages/SystemUI/res/values-uz-rUZ/strings.xml
+++ b/packages/SystemUI/res/values-uz-rUZ/strings.xml
@@ -88,8 +88,7 @@
<string name="unlock_label" msgid="8779712358041029439">"qulfdan chiqarish"</string>
<string name="phone_label" msgid="2320074140205331708">"telefonni ochish"</string>
<string name="camera_label" msgid="7261107956054836961">"kamerani ochish"</string>
- <!-- no translation found for recents_caption_resize (3517056471774958200) -->
- <skip />
+ <string name="recents_caption_resize" msgid="3517056471774958200">"Yangi vazifa tartibini tanlash"</string>
<string name="cancel" msgid="6442560571259935130">"Bekor qilish"</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>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index f6f3cae..7343c6f 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -88,8 +88,7 @@
<string name="unlock_label" msgid="8779712358041029439">"mở khóa"</string>
<string name="phone_label" msgid="2320074140205331708">"mở điện thoại"</string>
<string name="camera_label" msgid="7261107956054836961">"mở máy ảnh"</string>
- <!-- no translation found for recents_caption_resize (3517056471774958200) -->
- <skip />
+ <string name="recents_caption_resize" msgid="3517056471774958200">"Chọn bố cục tác vụ mới"</string>
<string name="cancel" msgid="6442560571259935130">"Hủy"</string>
<string name="accessibility_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>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index 03ba9a5..ca2cfef 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -88,8 +88,7 @@
<string name="unlock_label" msgid="8779712358041029439">"解锁"</string>
<string name="phone_label" msgid="2320074140205331708">"打开电话"</string>
<string name="camera_label" msgid="7261107956054836961">"打开相机"</string>
- <!-- no translation found for recents_caption_resize (3517056471774958200) -->
- <skip />
+ <string name="recents_caption_resize" msgid="3517056471774958200">"选择新的任务布局"</string>
<string name="cancel" msgid="6442560571259935130">"取消"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"兼容性缩放按钮。"</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"将小屏幕的图片放大在较大屏幕上显示。"</string>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index f501815..04a841d 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -88,8 +88,7 @@
<string name="unlock_label" msgid="8779712358041029439">"解鎖"</string>
<string name="phone_label" msgid="2320074140205331708">"開啟電話"</string>
<string name="camera_label" msgid="7261107956054836961">"開啟相機"</string>
- <!-- no translation found for recents_caption_resize (3517056471774958200) -->
- <skip />
+ <string name="recents_caption_resize" msgid="3517056471774958200">"選取新的工作版面配置"</string>
<string name="cancel" msgid="6442560571259935130">"取消"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"相容性縮放按鈕。"</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"將較小螢幕的畫面放大在較大螢幕上顯示。"</string>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index d51d1fb..98ae37c 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -88,8 +88,7 @@
<string name="unlock_label" msgid="8779712358041029439">"解除鎖定"</string>
<string name="phone_label" msgid="2320074140205331708">"開啟電話"</string>
<string name="camera_label" msgid="7261107956054836961">"開啟攝影機"</string>
- <!-- no translation found for recents_caption_resize (3517056471774958200) -->
- <skip />
+ <string name="recents_caption_resize" msgid="3517056471774958200">"選取新工作版面配置"</string>
<string name="cancel" msgid="6442560571259935130">"取消"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"相容性縮放按鈕。"</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"將較小螢幕的畫面放大在較大螢幕上顯示。"</string>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index c17d723..0136eb1 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -88,8 +88,7 @@
<string name="unlock_label" msgid="8779712358041029439">"vula"</string>
<string name="phone_label" msgid="2320074140205331708">"vula ifoni"</string>
<string name="camera_label" msgid="7261107956054836961">"vula ikhamera"</string>
- <!-- no translation found for recents_caption_resize (3517056471774958200) -->
- <skip />
+ <string name="recents_caption_resize" msgid="3517056471774958200">"Khetha isakhiwo somsebenzi omusha"</string>
<string name="cancel" msgid="6442560571259935130">"Khansela"</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>
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index e81a1d0..3a97a41 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -815,7 +815,9 @@
}
// Start dozing
- mUIDozeTrigger.startDozing();
+ if (!mConfig.multiStackEnabled) {
+ mUIDozeTrigger.startDozing();
+ }
}
/** Requests this task stacks to start it's enter-recents animation */
@@ -1219,7 +1221,7 @@
RecentsTaskLoader.getInstance().loadTaskData(task);
// If the doze trigger has already fired, then update the state for this task view
- if (mUIDozeTrigger.hasTriggered()) {
+ if (mConfig.multiStackEnabled || mUIDozeTrigger.hasTriggered()) {
tv.setNoUserInteractionState();
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
index eb262536..60a91bf 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
@@ -219,7 +219,7 @@
Rect display = mSsp.getWindowRect();
Rect taskRect = mSsp.getTaskBounds(t.key.stackId);
int resId = R.drawable.star;
- if (display.equals(taskRect)) {
+ if (display.equals(taskRect) || taskRect.isEmpty()) {
resId = R.drawable.vector_drawable_place_fullscreen;
} else {
boolean top = display.top == taskRect.top;
diff --git a/rs/java/android/renderscript/Element.java b/rs/java/android/renderscript/Element.java
index 287b3f1..4b3e30f 100644
--- a/rs/java/android/renderscript/Element.java
+++ b/rs/java/android/renderscript/Element.java
@@ -114,13 +114,11 @@
* MATRIX the three matrix types contain FLOAT_32 elements and are treated
* as 32 bits for alignment purposes.
*
- * RS_* objects. 32 bit opaque handles.
+ * RS_* objects: opaque handles with implementation dependent
+ * sizes.
*/
public enum DataType {
NONE (0, 0),
- /**
- * @hide
- */
FLOAT_16 (1, 2),
FLOAT_32 (2, 4),
FLOAT_64 (3, 8),
@@ -389,9 +387,6 @@
return rs.mElement_I64;
}
- /**
- * @hide
- */
public static Element F16(RenderScript rs) {
if(rs.mElement_F16 == null) {
rs.mElement_F16 = createUser(rs, DataType.FLOAT_16);
@@ -533,9 +528,6 @@
return rs.mElement_RGBA_8888;
}
- /**
- * @hide
- */
public static Element F16_2(RenderScript rs) {
if(rs.mElement_HALF_2 == null) {
rs.mElement_HALF_2 = createVector(rs, DataType.FLOAT_16, 2);
@@ -543,9 +535,6 @@
return rs.mElement_HALF_2;
}
- /**
- * @hide
- */
public static Element F16_3(RenderScript rs) {
if(rs.mElement_FLOAT_3 == null) {
rs.mElement_FLOAT_3 = createVector(rs, DataType.FLOAT_16, 3);
@@ -553,9 +542,6 @@
return rs.mElement_HALF_3;
}
- /**
- * @hide
- */
public static Element F16_4(RenderScript rs) {
if(rs.mElement_HALF_4 == null) {
rs.mElement_HALF_4 = createVector(rs, DataType.FLOAT_16, 4);
diff --git a/rs/java/android/renderscript/RenderScript.java b/rs/java/android/renderscript/RenderScript.java
index e8e942c..7ef17a7 100644
--- a/rs/java/android/renderscript/RenderScript.java
+++ b/rs/java/android/renderscript/RenderScript.java
@@ -139,7 +139,8 @@
* Returns an identifier that can be used to identify a particular
* minor version of RS.
*
- * @hide
+ * @return The minor RenderScript version number
+ *
*/
public static long getMinorID() {
return sMinorID;
@@ -1321,7 +1322,6 @@
/**
* Create a RenderScript context.
*
- * @hide
* @param ctx The context.
* @return RenderScript
*/
@@ -1418,14 +1418,13 @@
/**
* Gets or creates a RenderScript context of the specified type.
*
- * @hide
* @param ctx The context.
* @param ct The type of context to be created.
* @param sdkVersion The target SDK Version.
* @param flags The OR of the CREATE_FLAG_* options desired
* @return RenderScript
*/
- public static RenderScript create(Context ctx, int sdkVersion, ContextType ct, int flags) {
+ private static RenderScript create(Context ctx, int sdkVersion, ContextType ct, int flags) {
if (sdkVersion < 23) {
return internalCreate(ctx, sdkVersion, ct, flags);
}
@@ -1448,8 +1447,6 @@
}
/**
- * @hide
- *
* Releases all the process contexts. This is the same as
* calling .destroy() on each unique context retreived with
* create(...). If no contexts have been created this
@@ -1486,7 +1483,6 @@
*
* If you need a single context please use create()
*
- * @hide
* @param ctx The context.
* @return RenderScript
*/
diff --git a/rs/java/android/renderscript/Script.java b/rs/java/android/renderscript/Script.java
index 65056ac..6a1efee 100644
--- a/rs/java/android/renderscript/Script.java
+++ b/rs/java/android/renderscript/Script.java
@@ -66,7 +66,6 @@
}
/**
- * @hide Pending API review
* InvokeID is an identifier for an invoke function. It is used
* as an identifier for ScriptGroup creation.
*
@@ -86,7 +85,6 @@
private final SparseArray<InvokeID> mIIDs = new SparseArray<InvokeID>();
/**
- * @hide Pending API review
* Only to be used by generated reflected classes.
*/
protected InvokeID createInvokeID(int slot) {
@@ -222,22 +220,21 @@
/**
* Only intended for use by generated reflected code.
- *
- * @hide
*/
protected void forEach(int slot, Allocation[] ains, Allocation aout,
FieldPacker v) {
+
+ // FieldPacker is kept here to support regular params in the future.
forEach(slot, ains, aout, v, null);
}
/**
* Only intended for use by generated reflected code.
- *
- * @hide
*/
protected void forEach(int slot, Allocation[] ains, Allocation aout,
FieldPacker v, LaunchOptions sc) {
// TODO: Is this necessary if nScriptForEach calls validate as well?
+ // FieldPacker is kept here to support regular params in the future.
mRS.validate();
if (ains != null) {
for (Allocation ain : ains) {
@@ -476,7 +473,23 @@
/**
- * Class used to specify clipping for a kernel launch.
+ * Class for specifying the specifics about how a kernel will be
+ * launched
+ *
+ * This class can specify a potential range of cells on which to
+ * run a kernel. If no set is called for a dimension then this
+ * class will have no impact on that dimension when the kernel
+ * is executed.
+ *
+ * The forEach launch will operate over the intersection of the
+ * dimensions.
+ *
+ * Example:
+ * LaunchOptions with setX(5, 15)
+ * Allocation with dimension X=10, Y=10
+ * The resulting forEach run would execute over x = 5 to 10 and
+ * y = 0 to 10.
+ *
*
*/
public static final class LaunchOptions {
diff --git a/rs/java/android/renderscript/Type.java b/rs/java/android/renderscript/Type.java
index a58e42c..cc9b58b 100644
--- a/rs/java/android/renderscript/Type.java
+++ b/rs/java/android/renderscript/Type.java
@@ -150,24 +150,29 @@
}
/**
- * @hide
- */
- public int getArray(int dim) {
- if ((dim < 0) || (dim >= mMaxArrays)) {
+ * Return the dimension of the specified array.
+ *
+ * @param arrayNum The array dimension to query
+ * @return int
+ */
+ public int getArray(int arrayNum) {
+ if ((arrayNum < 0) || (arrayNum >= mMaxArrays)) {
throw new RSIllegalArgumentException("Array dimension out of range.");
}
- if (mArrays == null || dim >= mArrays.length) {
+ if (mArrays == null || arrayNum >= mArrays.length) {
// Dimension in range but no array for that dimension allocated
return 0;
}
- return mArrays[dim];
+ return mArrays[arrayNum];
}
/**
- * @hide
- */
+ * Return the number of array dimensions.
+ *
+ * @return int
+ */
public int getArrayCount() {
if (mArrays != null) return mArrays.length;
return 0;
@@ -377,7 +382,7 @@
}
/**
- * @hide
+ * Adds an array dimension to the builder
*
* @param dim
* @param value
diff --git a/services/Android.mk b/services/Android.mk
index 8777085..1918db5 100644
--- a/services/Android.mk
+++ b/services/Android.mk
@@ -24,6 +24,7 @@
appwidget \
backup \
devicepolicy \
+ midi \
net \
print \
restrictions \
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 31f9e22..5cc59e5 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -29,6 +29,7 @@
import android.app.backup.BackupDataOutput;
import android.app.backup.BackupTransport;
import android.app.backup.FullBackup;
+import android.app.backup.FullBackupDataOutput;
import android.app.backup.RestoreDescription;
import android.app.backup.RestoreSet;
import android.app.backup.IBackupManager;
@@ -134,6 +135,7 @@
import java.util.Random;
import java.util.Set;
import java.util.TreeMap;
+import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.zip.Deflater;
@@ -369,7 +371,7 @@
// we're now good to go, so start the backup alarms
if (MORE_DEBUG) Slog.d(TAG, "Now provisioned, so starting backups");
KeyValueBackupJob.schedule(mContext);
- scheduleNextFullBackupJob();
+ scheduleNextFullBackupJob(0);
}
}
}
@@ -726,7 +728,7 @@
{
try {
BackupRestoreTask task = (BackupRestoreTask) msg.obj;
- task.operationComplete();
+ task.operationComplete(msg.arg1);
} catch (ClassCastException e) {
Slog.e(TAG, "Invalid completion in flight, obj=" + msg.obj);
}
@@ -1784,7 +1786,7 @@
PackageInfo app = mPackageManager.getPackageInfo(packageName, 0);
if (appGetsFullBackup(app) && appIsEligibleForBackup(app.applicationInfo)) {
enqueueFullBackup(packageName, now);
- scheduleNextFullBackupJob();
+ scheduleNextFullBackupJob(0);
}
// Transport maintenance: rebind to known existing transports that have
@@ -2224,7 +2226,7 @@
void execute();
// An operation that wanted a callback has completed
- void operationComplete();
+ void operationComplete(int result);
// An operation that wanted a callback has timed out
void handleTimeout();
@@ -2792,7 +2794,7 @@
}
@Override
- public void operationComplete() {
+ public void operationComplete(int unusedResult) {
// The agent reported back to us!
if (mBackupData == null) {
@@ -3128,8 +3130,23 @@
// Core logic for performing one package's full backup, gathering the tarball from the
// application and emitting it to the designated OutputStream.
+
+ // Callout from the engine to an interested participant that might need to communicate
+ // with the agent prior to asking it to move data
+ interface FullBackupPreflight {
+ /**
+ * Perform the preflight operation necessary for the given package.
+ * @param pkg The name of the package being proposed for full-data backup
+ * @param agent Live BackupAgent binding to the target app's agent
+ * @return BackupTransport.TRANSPORT_OK to proceed with the backup operation,
+ * or one of the other BackupTransport.* error codes as appropriate
+ */
+ int preflightFullBackup(PackageInfo pkg, IBackupAgent agent);
+ };
+
class FullBackupEngine {
OutputStream mOutput;
+ FullBackupPreflight mPreflightHook;
IFullBackupRestoreObserver mObserver;
File mFilesDir;
File mManifestFile;
@@ -3160,8 +3177,7 @@
@Override
public void run() {
try {
- BackupDataOutput output = new BackupDataOutput(
- mPipe.getFileDescriptor());
+ FullBackupDataOutput output = new FullBackupDataOutput(mPipe);
if (mWriteManifest) {
final boolean writeWidgetData = mWidgetData != null;
@@ -3204,15 +3220,16 @@
}
}
- FullBackupEngine(OutputStream output, String packageName, boolean alsoApks) {
+ FullBackupEngine(OutputStream output, String packageName, FullBackupPreflight preflightHook,
+ boolean alsoApks) {
mOutput = output;
+ mPreflightHook = preflightHook;
mIncludeApks = alsoApks;
mFilesDir = new File("/data/system");
mManifestFile = new File(mFilesDir, BACKUP_MANIFEST_FILENAME);
mMetadataFile = new File(mFilesDir, BACKUP_METADATA_FILENAME);
}
-
public int backupOnePackage(PackageInfo pkg) throws RemoteException {
int result = BackupTransport.TRANSPORT_OK;
Slog.d(TAG, "Binding to full backup agent : " + pkg.packageName);
@@ -3222,42 +3239,52 @@
if (agent != null) {
ParcelFileDescriptor[] pipes = null;
try {
- pipes = ParcelFileDescriptor.createPipe();
+ // Call the preflight hook, if any
+ if (mPreflightHook != null) {
+ result = mPreflightHook.preflightFullBackup(pkg, agent);
+ if (MORE_DEBUG) {
+ Slog.v(TAG, "preflight returned " + result);
+ }
+ }
- ApplicationInfo app = pkg.applicationInfo;
- final boolean isSharedStorage = pkg.packageName.equals(SHARED_BACKUP_AGENT_PACKAGE);
- final boolean sendApk = mIncludeApks
- && !isSharedStorage
- && ((app.privateFlags & ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK) == 0)
- && ((app.flags & ApplicationInfo.FLAG_SYSTEM) == 0 ||
+ // If we're still good to go after preflighting, start moving data
+ if (result == BackupTransport.TRANSPORT_OK) {
+ pipes = ParcelFileDescriptor.createPipe();
+
+ ApplicationInfo app = pkg.applicationInfo;
+ final boolean isSharedStorage = pkg.packageName.equals(SHARED_BACKUP_AGENT_PACKAGE);
+ final boolean sendApk = mIncludeApks
+ && !isSharedStorage
+ && ((app.privateFlags & ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK) == 0)
+ && ((app.flags & ApplicationInfo.FLAG_SYSTEM) == 0 ||
(app.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0);
- byte[] widgetBlob = AppWidgetBackupBridge.getWidgetState(pkg.packageName,
- UserHandle.USER_OWNER);
+ byte[] widgetBlob = AppWidgetBackupBridge.getWidgetState(pkg.packageName,
+ UserHandle.USER_OWNER);
- final int token = generateToken();
- FullBackupRunner runner = new FullBackupRunner(pkg, agent, pipes[1],
- token, sendApk, !isSharedStorage, widgetBlob);
- pipes[1].close(); // the runner has dup'd it
- pipes[1] = null;
- Thread t = new Thread(runner, "app-data-runner");
- t.start();
+ final int token = generateToken();
+ FullBackupRunner runner = new FullBackupRunner(pkg, agent, pipes[1],
+ token, sendApk, !isSharedStorage, widgetBlob);
+ pipes[1].close(); // the runner has dup'd it
+ pipes[1] = null;
+ Thread t = new Thread(runner, "app-data-runner");
+ t.start();
- // Now pull data from the app and stuff it into the output
- try {
- routeSocketDataToOutput(pipes[0], mOutput);
- } catch (IOException e) {
- Slog.i(TAG, "Caught exception reading from agent", e);
- result = BackupTransport.AGENT_ERROR;
+ // Now pull data from the app and stuff it into the output
+ try {
+ routeSocketDataToOutput(pipes[0], mOutput);
+ } catch (IOException e) {
+ Slog.i(TAG, "Caught exception reading from agent", e);
+ result = BackupTransport.AGENT_ERROR;
+ }
+
+ if (!waitUntilOperationComplete(token)) {
+ Slog.e(TAG, "Full backup failed on package " + pkg.packageName);
+ result = BackupTransport.AGENT_ERROR;
+ } else {
+ if (DEBUG) Slog.d(TAG, "Full package backup success: " + pkg.packageName);
+ }
}
-
- if (!waitUntilOperationComplete(token)) {
- Slog.e(TAG, "Full backup failed on package " + pkg.packageName);
- result = BackupTransport.AGENT_ERROR;
- } else {
- if (DEBUG) Slog.d(TAG, "Full package backup success: " + pkg.packageName);
- }
-
} catch (IOException e) {
Slog.e(TAG, "Error backing up " + pkg.packageName, e);
result = BackupTransport.AGENT_ERROR;
@@ -3282,7 +3309,7 @@
return result;
}
- private void writeApkToBackup(PackageInfo pkg, BackupDataOutput output) {
+ private void writeApkToBackup(PackageInfo pkg, FullBackupDataOutput output) {
// Forward-locked apps, system-bundled .apks, etc are filtered out before we get here
// TODO: handle backing up split APKs
final String appSourceDir = pkg.applicationInfo.getBaseCodePath();
@@ -3781,7 +3808,7 @@
final boolean isSharedStorage =
pkg.packageName.equals(SHARED_BACKUP_AGENT_PACKAGE);
- mBackupEngine = new FullBackupEngine(out, pkg.packageName, mIncludeApks);
+ mBackupEngine = new FullBackupEngine(out, pkg.packageName, null, mIncludeApks);
sendOnBackupPackage(isSharedStorage ? "Shared storage" : pkg.packageName);
mBackupEngine.backupOnePackage(pkg);
@@ -3828,13 +3855,13 @@
static final String TAG = "PFTBT";
ArrayList<PackageInfo> mPackages;
boolean mUpdateSchedule;
- AtomicBoolean mLatch;
+ CountDownLatch mLatch;
AtomicBoolean mKeepRunning; // signal from job scheduler
FullBackupJob mJob; // if a scheduled job needs to be finished afterwards
PerformFullTransportBackupTask(IFullBackupRestoreObserver observer,
String[] whichPackages, boolean updateSchedule,
- FullBackupJob runningJob, AtomicBoolean latch) {
+ FullBackupJob runningJob, CountDownLatch latch) {
super(observer);
mUpdateSchedule = updateSchedule;
mLatch = latch;
@@ -3892,6 +3919,7 @@
ParcelFileDescriptor[] transportPipes = null;
PackageInfo currentPackage;
+ long backoff = 0;
try {
if (!mEnabled || !mProvisioned) {
@@ -3910,16 +3938,6 @@
return;
}
- // Don't proceed unless we have already established package metadata
- // for the current dataset via a key/value backup pass.
- File stateDir = new File(mBaseStateDir, transport.transportDirName());
- File pmState = new File(stateDir, PACKAGE_MANAGER_SENTINEL);
- if (pmState.length() <= 0) {
- Slog.i(TAG, "Full backup requested but dataset not yet initialized "
- + "via k/v backup pass; ignoring");
- return;
- }
-
// Set up to send data to the transport
final int N = mPackages.size();
for (int i = 0; i < N; i++) {
@@ -3944,10 +3962,10 @@
// Now set up the backup engine / data source end of things
enginePipes = ParcelFileDescriptor.createPipe();
- AtomicBoolean runnerLatch = new AtomicBoolean(false);
+ CountDownLatch runnerLatch = new CountDownLatch(1);
SinglePackageBackupRunner backupRunner =
new SinglePackageBackupRunner(enginePipes[1], currentPackage,
- runnerLatch);
+ transport, runnerLatch);
// The runner dup'd the pipe half, so we close it here
enginePipes[1].close();
enginePipes[1] = null;
@@ -3972,6 +3990,9 @@
break;
}
nRead = in.read(buffer);
+ if (MORE_DEBUG) {
+ Slog.v(TAG, "in.read(buffer) from app: " + nRead);
+ }
if (nRead > 0) {
out.write(buffer, 0, nRead);
result = transport.sendBackupData(nRead);
@@ -4003,6 +4024,14 @@
Slog.e(TAG, "Error " + result
+ " backing up " + currentPackage.packageName);
}
+
+ // Also ask the transport how long it wants us to wait before
+ // moving on to the next package, if any.
+ backoff = transport.requestFullBackupTime();
+ if (DEBUG_SCHEDULING) {
+ Slog.i(TAG, "Transport suggested backoff=" + backoff);
+ }
+
}
// Roll this package to the end of the backup queue if we're
@@ -4055,15 +4084,12 @@
mRunningFullBackupTask = null;
}
- synchronized (mLatch) {
- mLatch.set(true);
- mLatch.notifyAll();
- }
+ mLatch.countDown();
// Now that we're actually done with schedule-driven work, reschedule
// the next pass based on the new queue state.
if (mUpdateSchedule) {
- scheduleNextFullBackupJob();
+ scheduleNextFullBackupJob(backoff);
}
}
}
@@ -4094,15 +4120,79 @@
// Run the backup and pipe it back to the given socket -- expects to run on
// a standalone thread. The runner owns this half of the pipe, and closes
// it to indicate EOD to the other end.
+ class SinglePackageBackupPreflight implements BackupRestoreTask, FullBackupPreflight {
+ final AtomicInteger mResult = new AtomicInteger();
+ final CountDownLatch mLatch = new CountDownLatch(1);
+ final IBackupTransport mTransport;
+
+ public SinglePackageBackupPreflight(IBackupTransport transport) {
+ mTransport = transport;
+ }
+
+ @Override
+ public int preflightFullBackup(PackageInfo pkg, IBackupAgent agent) {
+ int result;
+ try {
+ final int token = generateToken();
+ prepareOperationTimeout(token, TIMEOUT_FULL_BACKUP_INTERVAL, this);
+ addBackupTrace("preflighting");
+ if (MORE_DEBUG) {
+ Slog.d(TAG, "Preflighting full payload of " + pkg.packageName);
+ }
+ agent.doMeasureFullBackup(token, mBackupManagerBinder);
+
+ // now wait to get our result back
+ mLatch.await();
+ int totalSize = mResult.get();
+ if (MORE_DEBUG) {
+ Slog.v(TAG, "Got preflight response; size=" + totalSize);
+ }
+
+ result = mTransport.checkFullBackupSize(totalSize);
+ } catch (Exception e) {
+ Slog.w(TAG, "Exception preflighting " + pkg.packageName + ": " + e.getMessage());
+ result = BackupTransport.AGENT_ERROR;
+ }
+ return result;
+ }
+
+ @Override
+ public void execute() {
+ // Unused in this case
+ }
+
+ @Override
+ public void operationComplete(int result) {
+ // got the callback, and our preflightFullBackup() method is waiting for the result
+ if (MORE_DEBUG) {
+ Slog.i(TAG, "Preflight op complete, result=" + result);
+ }
+ mResult.set(result);
+ mLatch.countDown();
+ }
+
+ @Override
+ public void handleTimeout() {
+ if (MORE_DEBUG) {
+ Slog.i(TAG, "Preflight timeout; failing");
+ }
+ mResult.set(BackupTransport.AGENT_ERROR);
+ mLatch.countDown();
+ }
+
+ }
+
class SinglePackageBackupRunner implements Runnable {
final ParcelFileDescriptor mOutput;
final PackageInfo mTarget;
- final AtomicBoolean mLatch;
+ final FullBackupPreflight mPreflight;
+ final CountDownLatch mLatch;
SinglePackageBackupRunner(ParcelFileDescriptor output, PackageInfo target,
- AtomicBoolean latch) throws IOException {
+ IBackupTransport transport, CountDownLatch latch) throws IOException {
mOutput = ParcelFileDescriptor.dup(output.getFileDescriptor());
mTarget = target;
+ mPreflight = new SinglePackageBackupPreflight(transport);
mLatch = latch;
}
@@ -4110,15 +4200,13 @@
public void run() {
try {
FileOutputStream out = new FileOutputStream(mOutput.getFileDescriptor());
- FullBackupEngine engine = new FullBackupEngine(out, mTarget.packageName, false);
+ FullBackupEngine engine = new FullBackupEngine(out, mTarget.packageName,
+ mPreflight, false);
engine.backupOnePackage(mTarget);
} catch (Exception e) {
Slog.e(TAG, "Exception during full package backup of " + mTarget);
} finally {
- synchronized (mLatch) {
- mLatch.set(true);
- mLatch.notifyAll();
- }
+ mLatch.countDown();
try {
mOutput.close();
} catch (IOException e) {
@@ -4126,7 +4214,6 @@
}
}
}
-
}
}
@@ -4135,16 +4222,17 @@
/**
* Schedule a job to tell us when it's a good time to run a full backup
*/
- void scheduleNextFullBackupJob() {
+ void scheduleNextFullBackupJob(long transportMinLatency) {
synchronized (mQueueLock) {
if (mFullBackupQueue.size() > 0) {
// schedule the next job at the point in the future when the least-recently
// backed up app comes due for backup again; or immediately if it's already
// due.
- long upcomingLastBackup = mFullBackupQueue.get(0).lastBackup;
- long timeSinceLast = System.currentTimeMillis() - upcomingLastBackup;
- final long latency = (timeSinceLast < MIN_FULL_BACKUP_INTERVAL)
+ final long upcomingLastBackup = mFullBackupQueue.get(0).lastBackup;
+ final long timeSinceLast = System.currentTimeMillis() - upcomingLastBackup;
+ final long appLatency = (timeSinceLast < MIN_FULL_BACKUP_INTERVAL)
? (MIN_FULL_BACKUP_INTERVAL - timeSinceLast) : 0;
+ final long latency = Math.min(transportMinLatency, appLatency);
Runnable r = new Runnable() {
@Override public void run() {
FullBackupJob.schedule(mContext, latency);
@@ -4198,6 +4286,31 @@
writeFullBackupScheduleAsync();
}
+ private boolean fullBackupAllowable(IBackupTransport transport) {
+ if (transport == null) {
+ Slog.w(TAG, "Transport not present; full data backup not performed");
+ return false;
+ }
+
+ // Don't proceed unless we have already established package metadata
+ // for the current dataset via a key/value backup pass.
+ try {
+ File stateDir = new File(mBaseStateDir, transport.transportDirName());
+ File pmState = new File(stateDir, PACKAGE_MANAGER_SENTINEL);
+ if (pmState.length() <= 0) {
+ if (DEBUG) {
+ Slog.i(TAG, "Full backup requested but dataset not yet initialized");
+ }
+ return false;
+ }
+ } catch (Exception e) {
+ Slog.w(TAG, "Unable to contact transport");
+ return false;
+ }
+
+ return true;
+ }
+
/**
* Conditions are right for a full backup operation, so run one. The model we use is
* to perform one app backup per scheduled job execution, and to reschedule the job
@@ -4209,6 +4322,7 @@
boolean beginFullBackup(FullBackupJob scheduledJob) {
long now = System.currentTimeMillis();
FullBackupEntry entry = null;
+ long latency = MIN_FULL_BACKUP_INTERVAL;
if (!mEnabled || !mProvisioned) {
// Backups are globally disabled, so don't proceed. We also don't reschedule
@@ -4240,17 +4354,41 @@
return false;
}
- entry = mFullBackupQueue.get(0);
- long timeSinceRun = now - entry.lastBackup;
- if (timeSinceRun < MIN_FULL_BACKUP_INTERVAL) {
- // It's too early to back up the next thing in the queue, so bow out
+ // At this point we know that we have work to do, just not right now. Any
+ // exit without actually running backups will also require that we
+ // reschedule the job.
+ boolean runBackup = true;
+
+ if (!fullBackupAllowable(getTransport(mCurrentTransport))) {
if (MORE_DEBUG) {
- Slog.i(TAG, "Device ready but too early to back up next app");
+ Slog.i(TAG, "Preconditions not met; not running full backup");
}
- final long latency = MIN_FULL_BACKUP_INTERVAL - timeSinceRun;
+ runBackup = false;
+ // Typically this means we haven't run a key/value backup yet. Back off
+ // full-backup operations by the key/value job's run interval so that
+ // next time we run, we are likely to be able to make progress.
+ latency = KeyValueBackupJob.BATCH_INTERVAL;
+ }
+
+ if (runBackup) {
+ entry = mFullBackupQueue.get(0);
+ long timeSinceRun = now - entry.lastBackup;
+ runBackup = (timeSinceRun >= MIN_FULL_BACKUP_INTERVAL);
+ if (!runBackup) {
+ // It's too early to back up the next thing in the queue, so bow out
+ if (MORE_DEBUG) {
+ Slog.i(TAG, "Device ready but too early to back up next app");
+ }
+ // Wait until the next app in the queue falls due for a full data backup
+ latency = MIN_FULL_BACKUP_INTERVAL - timeSinceRun;
+ }
+ }
+
+ if (!runBackup) {
+ final long deferTime = latency; // pin for the closure
mBackupHandler.post(new Runnable() {
@Override public void run() {
- FullBackupJob.schedule(mContext, latency);
+ FullBackupJob.schedule(mContext, deferTime);
}
});
return false;
@@ -4258,7 +4396,7 @@
// Okay, the top thing is runnable now. Pop it off and get going.
mFullBackupQueue.remove(0);
- AtomicBoolean latch = new AtomicBoolean(false);
+ CountDownLatch latch = new CountDownLatch(1);
String[] pkg = new String[] {entry.packageName};
mRunningFullBackupTask = new PerformFullTransportBackupTask(null, pkg, true,
scheduledJob, latch);
@@ -7895,7 +8033,7 @@
}
@Override
- public void operationComplete() {
+ public void operationComplete(int unusedResult) {
if (MORE_DEBUG) {
Slog.i(TAG, "operationComplete() during restore: target="
+ mCurrentPackage.packageName
@@ -8391,21 +8529,33 @@
throw new IllegalStateException("Restore supported only for the device owner");
}
- if (DEBUG) {
- Slog.d(TAG, "fullTransportBackup()");
+ if (!fullBackupAllowable(getTransport(mCurrentTransport))) {
+ Slog.i(TAG, "Full backup not currently possible -- key/value backup not yet run?");
+ } else {
+ if (DEBUG) {
+ Slog.d(TAG, "fullTransportBackup()");
+ }
+
+ CountDownLatch latch = new CountDownLatch(1);
+ PerformFullTransportBackupTask task =
+ new PerformFullTransportBackupTask(null, pkgNames, false, null, latch);
+ (new Thread(task, "full-transport-master")).start();
+ do {
+ try {
+ latch.await();
+ break;
+ } catch (InterruptedException e) {
+ // Just go back to waiting for the latch to indicate completion
+ }
+ } while (true);
+
+ // We just ran a backup on these packages, so kick them to the end of the queue
+ final long now = System.currentTimeMillis();
+ for (String pkg : pkgNames) {
+ enqueueFullBackup(pkg, now);
+ }
}
- AtomicBoolean latch = new AtomicBoolean(false);
- PerformFullTransportBackupTask task =
- new PerformFullTransportBackupTask(null, pkgNames, false, null, latch);
- (new Thread(task, "full-transport-master")).start();
- synchronized (latch) {
- try {
- while (latch.get() == false) {
- latch.wait();
- }
- } catch (InterruptedException e) {}
- }
if (DEBUG) {
Slog.d(TAG, "Done with full transport backup.");
}
@@ -8575,7 +8725,7 @@
if (enable && !wasEnabled && mProvisioned) {
// if we've just been enabled, start scheduling backup passes
KeyValueBackupJob.schedule(mContext);
- scheduleNextFullBackupJob();
+ scheduleNextFullBackupJob(0);
} else if (!enable) {
// No longer enabled, so stop running backups
if (DEBUG) Slog.i(TAG, "Opting out of backup");
@@ -8948,8 +9098,10 @@
// Note that a currently-active backup agent has notified us that it has
// completed the given outstanding asynchronous backup/restore operation.
- public void opComplete(int token) {
- if (MORE_DEBUG) Slog.v(TAG, "opComplete: " + Integer.toHexString(token));
+ public void opComplete(int token, long result) {
+ if (MORE_DEBUG) {
+ Slog.v(TAG, "opComplete: " + Integer.toHexString(token) + " result=" + result);
+ }
Operation op = null;
synchronized (mCurrentOpLock) {
op = mCurrentOperations.get(token);
@@ -8962,6 +9114,8 @@
// The completion callback, if any, is invoked on the handler
if (op != null && op.callback != null) {
Message msg = mBackupHandler.obtainMessage(MSG_OP_COMPLETE, op.callback);
+ // NB: this cannot distinguish between results > 2 gig
+ msg.arg1 = (result > Integer.MAX_VALUE) ? Integer.MAX_VALUE : (int) result;
mBackupHandler.sendMessage(msg);
}
}
diff --git a/services/backup/java/com/android/server/backup/KeyValueBackupJob.java b/services/backup/java/com/android/server/backup/KeyValueBackupJob.java
index dc1c9d5..a4489c1 100644
--- a/services/backup/java/com/android/server/backup/KeyValueBackupJob.java
+++ b/services/backup/java/com/android/server/backup/KeyValueBackupJob.java
@@ -41,7 +41,7 @@
// Once someone asks for a backup, this is how long we hold off, batching
// up additional requests, before running the actual backup pass. Privileged
// callers can always trigger an immediate pass via BackupManager.backupNow().
- private static final long BATCH_INTERVAL = 4 * AlarmManager.INTERVAL_HOUR;
+ static final long BATCH_INTERVAL = 4 * AlarmManager.INTERVAL_HOUR;
// Random variation in next-backup scheduling time to avoid server load spikes
private static final int FUZZ_MILLIS = 10 * 60 * 1000;
diff --git a/services/backup/java/com/android/server/backup/Trampoline.java b/services/backup/java/com/android/server/backup/Trampoline.java
index 2e84fbe..99bbdae 100644
--- a/services/backup/java/com/android/server/backup/Trampoline.java
+++ b/services/backup/java/com/android/server/backup/Trampoline.java
@@ -309,10 +309,10 @@
}
@Override
- public void opComplete(int token) throws RemoteException {
+ public void opComplete(int token, long result) throws RemoteException {
BackupManagerService svc = mService;
if (svc != null) {
- svc.opComplete(token);
+ svc.opComplete(token, result);
}
}
diff --git a/services/core/Android.mk b/services/core/Android.mk
index 1a0fa34..64b6134 100644
--- a/services/core/Android.mk
+++ b/services/core/Android.mk
@@ -10,5 +10,6 @@
java/com/android/server/am/EventLogTags.logtags
LOCAL_JAVA_LIBRARIES := telephony-common
+LOCAL_STATIC_JAVA_LIBRARIES := tzdata_update
include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/services/core/java/com/android/server/EventLogTags.logtags b/services/core/java/com/android/server/EventLogTags.logtags
index 983d83a..694e851 100644
--- a/services/core/java/com/android/server/EventLogTags.logtags
+++ b/services/core/java/com/android/server/EventLogTags.logtags
@@ -221,3 +221,4 @@
# AudioService.java
# ---------------------------
40000 volume_changed (stream|1), (prev_level|1), (level|1), (max_level|1), (caller|3)
+40001 stream_devices_changed (stream|1), (prev_devices|1), (devices|1)
diff --git a/services/core/java/com/android/server/GraphicsStatsService.java b/services/core/java/com/android/server/GraphicsStatsService.java
new file mode 100644
index 0000000..c79fdfc
--- /dev/null
+++ b/services/core/java/com/android/server/GraphicsStatsService.java
@@ -0,0 +1,256 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.MemoryFile;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.IGraphicsStats;
+import android.view.ThreadedRenderer;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
+/**
+ * This service's job is to collect aggregate rendering profile data. It
+ * does this by allowing rendering processes to request an ashmem buffer
+ * to place their stats into. This buffer will be pre-initialized with historical
+ * data for that process if it exists (if the userId & packageName match a buffer
+ * in the historical log)
+ *
+ * This service does not itself attempt to understand the data in the buffer,
+ * its primary job is merely to manage distributing these buffers. However,
+ * it is assumed that this buffer is for ThreadedRenderer and delegates
+ * directly to ThreadedRenderer for dumping buffers.
+ *
+ * MEMORY USAGE:
+ *
+ * This class consumes UP TO:
+ * 1) [active rendering processes] * (ASHMEM_SIZE * 2)
+ * 2) ASHMEM_SIZE (for scratch space used during dumping)
+ * 3) ASHMEM_SIZE * HISTORY_SIZE
+ *
+ * Currently ASHMEM_SIZE is 256 bytes and HISTORY_SIZE is 10. Assuming
+ * the system then also has 10 active rendering processes in the worst case
+ * this would end up using under 10KiB (8KiB for the buffers, plus some overhead
+ * for userId, pid, package name, and a couple other objects)
+ *
+ * @hide */
+public class GraphicsStatsService extends IGraphicsStats.Stub {
+ public static final String GRAPHICS_STATS_SERVICE = "graphicsstats";
+
+ private static final String TAG = "GraphicsStatsService";
+ private static final int ASHMEM_SIZE = 256;
+ private static final int HISTORY_SIZE = 10;
+
+ private final Context mContext;
+ private final Object mLock = new Object();
+ private ArrayList<ActiveBuffer> mActive = new ArrayList<>();
+ private HistoricalData[] mHistoricalLog = new HistoricalData[HISTORY_SIZE];
+ private int mNextHistoricalSlot = 0;
+ private byte[] mTempBuffer = new byte[ASHMEM_SIZE];
+
+ public GraphicsStatsService(Context context) {
+ mContext = context;
+ }
+
+ private boolean isValid(int uid, String packageName) {
+ try {
+ PackageInfo info = mContext.getPackageManager().getPackageInfo(packageName, 0);
+ return info.applicationInfo.uid == uid;
+ } catch (NameNotFoundException e) {
+ }
+ return false;
+ }
+
+ @Override
+ public ParcelFileDescriptor requestBufferForProcess(String packageName, IBinder token)
+ throws RemoteException {
+ int uid = Binder.getCallingUid();
+ int pid = Binder.getCallingPid();
+ ParcelFileDescriptor pfd = null;
+ long callingIdentity = Binder.clearCallingIdentity();
+ try {
+ if (!isValid(uid, packageName)) {
+ throw new RemoteException("Invalid package name");
+ }
+ synchronized (mLock) {
+ pfd = requestBufferForProcessLocked(token, uid, pid, packageName);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(callingIdentity);
+ }
+ return pfd;
+ }
+
+ private ParcelFileDescriptor getPfd(MemoryFile file) {
+ try {
+ return new ParcelFileDescriptor(file.getFileDescriptor());
+ } catch (IOException ex) {
+ throw new IllegalStateException("Failed to get PFD from memory file", ex);
+ }
+ }
+
+ private ParcelFileDescriptor requestBufferForProcessLocked(IBinder token,
+ int uid, int pid, String packageName) throws RemoteException {
+ ActiveBuffer buffer = fetchActiveBuffersLocked(token, uid, pid, packageName);
+ return getPfd(buffer.mProcessBuffer);
+ }
+
+ private void processDied(ActiveBuffer buffer) {
+ synchronized (mLock) {
+ mActive.remove(buffer);
+ Log.d("GraphicsStats", "Buffer count: " + mActive.size());
+ }
+ HistoricalData data = buffer.mPreviousData;
+ buffer.mPreviousData = null;
+ if (data == null) {
+ data = mHistoricalLog[mNextHistoricalSlot];
+ if (data == null) {
+ data = new HistoricalData();
+ }
+ }
+ data.update(buffer.mPackageName, buffer.mUid, buffer.mProcessBuffer);
+ buffer.closeAllBuffers();
+
+ mHistoricalLog[mNextHistoricalSlot] = data;
+ mNextHistoricalSlot = (mNextHistoricalSlot + 1) % mHistoricalLog.length;
+ }
+
+ private ActiveBuffer fetchActiveBuffersLocked(IBinder token, int uid, int pid,
+ String packageName) throws RemoteException {
+ int size = mActive.size();
+ for (int i = 0; i < size; i++) {
+ ActiveBuffer buffers = mActive.get(i);
+ if (buffers.mPid == pid
+ && buffers.mUid == uid) {
+ return buffers;
+ }
+ }
+ // Didn't find one, need to create it
+ try {
+ ActiveBuffer buffers = new ActiveBuffer(token, uid, pid, packageName);
+ mActive.add(buffers);
+ return buffers;
+ } catch (IOException ex) {
+ throw new RemoteException("Failed to allocate space");
+ }
+ }
+
+ private HistoricalData removeHistoricalDataLocked(int uid, String packageName) {
+ for (int i = 0; i < mHistoricalLog.length; i++) {
+ final HistoricalData data = mHistoricalLog[i];
+ if (data != null && data.mUid == uid
+ && data.mPackageName.equals(packageName)) {
+ if (i == mNextHistoricalSlot) {
+ mHistoricalLog[i] = null;
+ } else {
+ mHistoricalLog[i] = mHistoricalLog[mNextHistoricalSlot];
+ mHistoricalLog[mNextHistoricalSlot] = null;
+ }
+ return data;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
+ synchronized (mLock) {
+ for (int i = 0; i < mActive.size(); i++) {
+ final ActiveBuffer buffer = mActive.get(i);
+ fout.print("Package: ");
+ fout.print(buffer.mPackageName);
+ fout.flush();
+ try {
+ buffer.mProcessBuffer.readBytes(mTempBuffer, 0, 0, ASHMEM_SIZE);
+ ThreadedRenderer.dumpProfileData(mTempBuffer, fd);
+ } catch (IOException e) {
+ fout.println("Failed to dump");
+ }
+ fout.println();
+ }
+ for (HistoricalData buffer : mHistoricalLog) {
+ if (buffer == null) continue;
+ fout.print("Package: ");
+ fout.print(buffer.mPackageName);
+ fout.flush();
+ ThreadedRenderer.dumpProfileData(buffer.mBuffer, fd);
+ fout.println();
+ }
+ }
+ }
+
+ private final class ActiveBuffer implements DeathRecipient {
+ final int mUid;
+ final int mPid;
+ final String mPackageName;
+ final IBinder mToken;
+ MemoryFile mProcessBuffer;
+ HistoricalData mPreviousData;
+
+ ActiveBuffer(IBinder token, int uid, int pid, String packageName)
+ throws RemoteException, IOException {
+ mUid = uid;
+ mPid = pid;
+ mPackageName = packageName;
+ mToken = token;
+ mToken.linkToDeath(this, 0);
+ mProcessBuffer = new MemoryFile("GFXStats-" + uid, ASHMEM_SIZE);
+ mPreviousData = removeHistoricalDataLocked(mUid, mPackageName);
+ if (mPreviousData != null) {
+ mProcessBuffer.writeBytes(mPreviousData.mBuffer, 0, 0, ASHMEM_SIZE);
+ }
+ }
+
+ @Override
+ public void binderDied() {
+ mToken.unlinkToDeath(this, 0);
+ processDied(this);
+ }
+
+ void closeAllBuffers() {
+ if (mProcessBuffer != null) {
+ mProcessBuffer.close();
+ mProcessBuffer = null;
+ }
+ }
+ }
+
+ private final static class HistoricalData {
+ final byte[] mBuffer = new byte[ASHMEM_SIZE];
+ int mUid;
+ String mPackageName;
+
+ void update(String packageName, int uid, MemoryFile file) {
+ mUid = uid;
+ mPackageName = packageName;
+ try {
+ file.readBytes(mBuffer, 0, 0, ASHMEM_SIZE);
+ } catch (IOException e) {}
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/IntentResolver.java b/services/core/java/com/android/server/IntentResolver.java
index cea1ebe..744156b 100644
--- a/services/core/java/com/android/server/IntentResolver.java
+++ b/services/core/java/com/android/server/IntentResolver.java
@@ -47,6 +47,7 @@
final private static String TAG = "IntentResolver";
final private static boolean DEBUG = false;
final private static boolean localLOGV = DEBUG || false;
+ final private static boolean localVerificationLOGV = DEBUG || false;
public void addFilter(F f) {
if (localLOGV) {
@@ -478,7 +479,7 @@
/**
* Returns whether the object associated with the given filter is
- * "stopped," that is whether it should not be included in the result
+ * "stopped", that is whether it should not be included in the result
* if the intent requests to excluded stopped objects.
*/
protected boolean isFilterStopped(F filter, int userId) {
@@ -486,6 +487,22 @@
}
/**
+ * Returns whether the given filter is "verified" that is whether it has been verified against
+ * its data URIs.
+ *
+ * The verification would happen only and only if the Intent action is
+ * {@link android.content.Intent#ACTION_VIEW} and the Intent category is
+ * {@link android.content.Intent#CATEGORY_BROWSABLE} and the Intent data scheme
+ * is "http" or "https".
+ *
+ * @see android.content.IntentFilter#setAutoVerify(boolean)
+ * @see android.content.IntentFilter#getAutoVerify()
+ */
+ protected boolean isFilterVerified(F filter) {
+ return filter.isVerified();
+ }
+
+ /**
* Returns whether this filter is owned by this package. This must be
* implemented to provide correct filtering of Intents that have
* specified a package name they are to be delivered to.
@@ -710,6 +727,13 @@
continue;
}
+ // Are we verified ?
+ if (filter.getAutoVerify()) {
+ if (localVerificationLOGV || debug) {
+ Slog.v(TAG, " Filter verified: " + isFilterVerified(filter));
+ }
+ }
+
// Do we already have this one?
if (!allowFilterResult(filter, dest)) {
if (debug) {
diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java
index b8d9ec5..61286e8 100644
--- a/services/core/java/com/android/server/MountService.java
+++ b/services/core/java/com/android/server/MountService.java
@@ -16,29 +16,21 @@
package com.android.server;
-import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-
import android.Manifest;
import android.app.ActivityManagerNative;
import android.app.AppOpsManager;
-import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
-import android.content.IntentFilter;
import android.content.ServiceConnection;
-import android.content.pm.PackageManager;
-import android.content.pm.UserInfo;
import android.content.res.Configuration;
import android.content.res.ObbInfo;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.content.res.XmlResourceParser;
-import android.hardware.usb.UsbManager;
+import android.mtp.MtpStorage;
import android.net.Uri;
import android.os.Binder;
import android.os.Environment;
import android.os.Environment.UserEnvironment;
+import android.os.FileUtils;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
@@ -59,26 +51,24 @@
import android.os.storage.StorageResultCode;
import android.os.storage.StorageVolume;
import android.text.TextUtils;
-import android.util.AttributeSet;
+import android.util.ArrayMap;
+import android.util.DebugUtils;
import android.util.Slog;
-import android.util.Xml;
+import android.util.SparseArray;
+
+import libcore.util.EmptyArray;
+import libcore.util.HexEncoding;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IMediaContainerService;
+import com.android.internal.util.ArrayUtils;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
-import com.android.internal.util.XmlUtils;
import com.android.server.NativeDaemonConnector.Command;
import com.android.server.NativeDaemonConnector.SensitiveArg;
-import com.android.server.am.ActivityManagerService;
import com.android.server.pm.PackageManagerService;
-import com.android.server.pm.UserManagerService;
import com.google.android.collect.Lists;
-import com.google.android.collect.Maps;
-import libcore.util.HexEncoding;
-
-import org.xmlpull.v1.XmlPullParserException;
import java.io.File;
import java.io.FileDescriptor;
@@ -101,7 +91,6 @@
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
-import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -110,19 +99,18 @@
import javax.crypto.spec.PBEKeySpec;
/**
- * MountService implements back-end services for platform storage
- * management.
- * @hide - Applications should use android.os.storage.StorageManager
- * to access the MountService.
+ * Service responsible for various storage media. Connects to {@code vold} to
+ * watch for and manage dynamically added storage, such as SD cards and USB mass
+ * storage. Also decides how storage should be presented to users on the device.
*/
class MountService extends IMountService.Stub
implements INativeDaemonConnectorCallbacks, Watchdog.Monitor {
+ // TODO: finish enforcing UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA
+
// Static direct instance pointer for the tightly-coupled idle service to use
static MountService sSelf = null;
- // TODO: listen for user creation/deletion
-
public static class Lifecycle extends SystemService {
private MountService mMountService;
@@ -142,6 +130,16 @@
mMountService.systemReady();
}
}
+
+ @Override
+ public void onStartUser(int userHandle) {
+ mMountService.onStartUser(userHandle);
+ }
+
+ @Override
+ public void onCleanupUser(int userHandle) {
+ mMountService.onCleanupUser(userHandle);
+ }
}
private static final boolean LOCAL_LOGD = false;
@@ -159,21 +157,7 @@
/** Maximum number of ASEC containers allowed to be mounted. */
private static final int MAX_CONTAINERS = 250;
- /*
- * Internal vold volume state constants
- */
- class VolumeState {
- public static final int Init = -1;
- public static final int NoMedia = 0;
- public static final int Idle = 1;
- public static final int Pending = 2;
- public static final int Checking = 3;
- public static final int Mounted = 4;
- public static final int Unmounting = 5;
- public static final int Formatting = 6;
- public static final int Shared = 7;
- public static final int SharedMnt = 8;
- }
+ private static final String PROP_PRIMARY_PHYSICAL = "ro.vold.primary_physical";
/*
* Internal vold response code constants
@@ -209,12 +193,19 @@
/*
* 600 series - Unsolicited broadcasts.
*/
- public static final int VolumeStateChange = 605;
- public static final int VolumeUuidChange = 613;
- public static final int VolumeUserLabelChange = 614;
- public static final int VolumeDiskInserted = 630;
- public static final int VolumeDiskRemoved = 631;
- public static final int VolumeBadRemoval = 632;
+ public static final int DISK_CREATED = 640;
+ public static final int DISK_SIZE_CHANGED = 641;
+ public static final int DISK_LABEL_CHANGED = 642;
+ public static final int DISK_VOLUME_CREATED = 643;
+ public static final int DISK_DESTROYED = 649;
+
+ public static final int VOLUME_CREATED = 650;
+ public static final int VOLUME_STATE_CHANGED = 651;
+ public static final int VOLUME_FS_TYPE_CHANGED = 652;
+ public static final int VOLUME_FS_UUID_CHANGED = 653;
+ public static final int VOLUME_FS_LABEL_CHANGED = 654;
+ public static final int VOLUME_PATH_CHANGED = 655;
+ public static final int VOLUME_DESTROYED = 659;
/*
* 700 series - fstrim
@@ -222,6 +213,243 @@
public static final int FstrimCompleted = 700;
}
+ private static SparseArray<String> sStateToEnvironment = new SparseArray<>();
+ private static ArrayMap<String, String> sEnvironmentToBroadcast = new ArrayMap<>();
+
+ static {
+ sStateToEnvironment.put(Volume.STATE_UNMOUNTED, Environment.MEDIA_UNMOUNTED);
+ sStateToEnvironment.put(Volume.STATE_MOUNTING, Environment.MEDIA_CHECKING);
+ sStateToEnvironment.put(Volume.STATE_MOUNTED, Environment.MEDIA_MOUNTED);
+ sStateToEnvironment.put(Volume.STATE_FORMATTING, Environment.MEDIA_UNMOUNTED);
+ sStateToEnvironment.put(Volume.STATE_UNMOUNTING, Environment.MEDIA_EJECTING);
+
+ sEnvironmentToBroadcast.put(Environment.MEDIA_UNMOUNTED, Intent.ACTION_MEDIA_UNMOUNTED);
+ sEnvironmentToBroadcast.put(Environment.MEDIA_CHECKING, Intent.ACTION_MEDIA_CHECKING);
+ sEnvironmentToBroadcast.put(Environment.MEDIA_MOUNTED, Intent.ACTION_MEDIA_MOUNTED);
+ sEnvironmentToBroadcast.put(Environment.MEDIA_EJECTING, Intent.ACTION_MEDIA_EJECT);
+ }
+
+ /**
+ * <em>Never</em> hold the lock while performing downcalls into vold, since
+ * unsolicited events can suddenly appear to update data structures.
+ */
+ private final Object mLock = new Object();
+
+ @GuardedBy("mLock")
+ private int[] mStartedUsers = EmptyArray.INT;
+ @GuardedBy("mLock")
+ private ArrayMap<String, Disk> mDisks = new ArrayMap<>();
+ @GuardedBy("mLock")
+ private ArrayMap<String, Volume> mVolumes = new ArrayMap<>();
+
+ @Deprecated
+ private Volume findVolumeByLegacyPath(String legacyPath) {
+ synchronized (mLock) {
+ for (Volume vol : mVolumes.values()) {
+ if (vol.path != null && legacyPath.startsWith(vol.path)) {
+ return vol;
+ }
+ }
+ }
+ Slog.w(TAG, "Failed to find volume for path " + legacyPath);
+ return null;
+ }
+
+ /**
+ * Framework-side twin of android::vold::Disk
+ */
+ private class Disk {
+ public static final int FLAG_ADOPTABLE = 1 << 0;
+ public static final int FLAG_DEFAULT_PRIMARY = 1 << 1;
+ public static final int FLAG_SD = 1 << 2;
+ public static final int FLAG_USB = 1 << 3;
+
+ public final String id;
+ public final int flags;
+ public long size;
+ public String label;
+
+ public ArrayList<Volume> volumes = new ArrayList<>();
+
+ public Disk(String id, int flags) {
+ this.id = id;
+ this.flags = flags;
+ }
+
+ public void partitionPublic() throws NativeDaemonConnectorException {
+ mConnector.execute("volume", "partition", id, "public");
+ }
+
+ public void partitionPrivate() throws NativeDaemonConnectorException {
+ mConnector.execute("volume", "partition", id, "private");
+ }
+
+ public void partitionMixed(int frac) throws NativeDaemonConnectorException {
+ mConnector.execute("volume", "partition", id, "mixed", frac);
+ }
+
+ public void dump(IndentingPrintWriter pw) {
+ pw.println("Disk:");
+ pw.increaseIndent();
+ pw.printPair("id", id);
+ pw.printPair("flags", DebugUtils.flagsToString(getClass(), "FLAG_", flags));
+ pw.printPair("size", size);
+ pw.printPair("label", label);
+ pw.decreaseIndent();
+ pw.println();
+ }
+ }
+
+ private static int sNextMtpIndex = 1;
+
+ /**
+ * Framework-side twin of android::vold::VolumeBase
+ */
+ private class Volume {
+ public static final String ID_EMULATED_INTERNAL = "emulated";
+
+ public static final int TYPE_PUBLIC = 0;
+ public static final int TYPE_PRIVATE = 1;
+ public static final int TYPE_EMULATED = 2;
+ public static final int TYPE_ASEC = 3;
+ public static final int TYPE_OBB = 4;
+
+ public static final int STATE_UNMOUNTED = 0;
+ public static final int STATE_MOUNTING = 1;
+ public static final int STATE_MOUNTED = 2;
+ public static final int STATE_FORMATTING = 3;
+ public static final int STATE_UNMOUNTING = 4;
+
+ public static final int FLAG_PRIMARY = 1 << 0;
+ public static final int FLAG_VISIBLE = 1 << 1;
+
+ /** vold state */
+ public final String id;
+ public final int type;
+ public int flags = 0;
+ public int userId = -1;
+ public int state = STATE_UNMOUNTED;
+ public String fsType;
+ public String fsUuid;
+ public String fsLabel;
+ public String path = "/dev/null";
+
+ /** Framework state */
+ public final int mtpIndex;
+
+ public Disk disk;
+
+ public Volume(String id, int type) {
+ this.id = id;
+ this.type = type;
+
+ if (ID_EMULATED_INTERNAL.equals(id)) {
+ mtpIndex = 0;
+ } else {
+ mtpIndex = sNextMtpIndex++;
+ }
+ }
+
+ public boolean isPrimary() {
+ return (flags & FLAG_PRIMARY) != 0;
+ }
+
+ public boolean isVisible() {
+ return (flags & FLAG_VISIBLE) != 0;
+ }
+
+ public boolean isVisibleToUser(int userId) {
+ if (type == TYPE_PUBLIC && this.userId == userId) {
+ return isVisible();
+ } else if (type == TYPE_EMULATED) {
+ return isVisible();
+ } else {
+ return false;
+ }
+ }
+
+ public void mount() throws NativeDaemonConnectorException {
+ mConnector.execute("volume", "mount", id, flags, userId);
+ }
+
+ public void unmount() throws NativeDaemonConnectorException {
+ mConnector.execute("volume", "unmount", id);
+ }
+
+ public void format() throws NativeDaemonConnectorException {
+ mConnector.execute("volume", "format", id);
+ }
+
+ public StorageVolume buildVolumeForUser(int userId) {
+ final File userPath;
+ final boolean removable;
+ final boolean emulated;
+ final boolean allowMassStorage = false;
+ final int mtpStorageId = MtpStorage.getStorageIdForIndex(mtpIndex);
+ final String envState = sStateToEnvironment.get(state);
+
+ int descriptionId = com.android.internal.R.string.unknownName;
+ long mtpReserveSize = 0;
+ long maxFileSize = 0;
+
+ if (type == TYPE_EMULATED) {
+ userPath = new File(path, Integer.toString(userId));
+ emulated = true;
+ mtpReserveSize = StorageManager.from(mContext).getStorageLowBytes(userPath);
+ descriptionId = com.android.internal.R.string.storage_internal;
+
+ if (ID_EMULATED_INTERNAL.equals(id)) {
+ removable = false;
+ } else {
+ removable = true;
+ }
+
+ } else if (type == TYPE_PUBLIC) {
+ userPath = new File(path);
+ emulated = false;
+ removable = true;
+
+ if (disk != null) {
+ if ((disk.flags & Disk.FLAG_SD) != 0) {
+ descriptionId = com.android.internal.R.string.storage_sd_card;
+ } else if ((disk.flags & Disk.FLAG_USB) != 0) {
+ descriptionId = com.android.internal.R.string.storage_usb;
+ }
+ }
+
+ if ("vfat".equals(fsType)) {
+ maxFileSize = 4294967295L;
+ }
+
+ } else {
+ throw new IllegalStateException("Unexpected volume type " + type);
+ }
+
+ return new StorageVolume(id, mtpStorageId, userPath, descriptionId, isPrimary(),
+ removable, emulated, mtpReserveSize, allowMassStorage, maxFileSize,
+ new UserHandle(userId), fsUuid, fsLabel, envState);
+ }
+
+ public void dump(IndentingPrintWriter pw) {
+ pw.println("Volume:");
+ pw.increaseIndent();
+ pw.printPair("id", id);
+ pw.printPair("type", DebugUtils.valueToString(getClass(), "TYPE_", type));
+ pw.printPair("flags", DebugUtils.flagsToString(getClass(), "FLAG_", flags));
+ pw.printPair("userId", userId);
+ pw.printPair("state", DebugUtils.valueToString(getClass(), "STATE_", state));
+ pw.println();
+ pw.printPair("fsType", fsType);
+ pw.printPair("fsUuid", fsUuid);
+ pw.printPair("fsLabel", fsLabel);
+ pw.println();
+ pw.printPair("path", path);
+ pw.printPair("mtpIndex", mtpIndex);
+ pw.decreaseIndent();
+ pw.println();
+ }
+ }
+
/** List of crypto types.
* These must match CRYPT_TYPE_XXX in cryptfs.h AND their
* corresponding commands in CommandListener.cpp */
@@ -231,33 +459,20 @@
private final Context mContext;
private final NativeDaemonConnector mConnector;
- private final Object mVolumesLock = new Object();
-
- /** When defined, base template for user-specific {@link StorageVolume}. */
- private StorageVolume mEmulatedTemplate;
-
- // TODO: separate storage volumes on per-user basis
-
- @GuardedBy("mVolumesLock")
- private final ArrayList<StorageVolume> mVolumes = Lists.newArrayList();
- /** Map from path to {@link StorageVolume} */
- @GuardedBy("mVolumesLock")
- private final HashMap<String, StorageVolume> mVolumesByPath = Maps.newHashMap();
- /** Map from path to state */
- @GuardedBy("mVolumesLock")
- private final HashMap<String, String> mVolumeStates = Maps.newHashMap();
-
private volatile boolean mSystemReady = false;
+ private volatile boolean mDaemonConnected = false;
private PackageManagerService mPms;
- private boolean mUmsEnabling;
- private boolean mUmsAvailable = false;
// Used as a lock for methods that register/unregister listeners.
final private ArrayList<MountServiceBinderListener> mListeners =
new ArrayList<MountServiceBinderListener>();
+
private final CountDownLatch mConnectedSignal = new CountDownLatch(1);
private final CountDownLatch mAsecsScanned = new CountDownLatch(1);
- private boolean mSendUmsConnectedOnBoot = false;
+
+ private final Object mUnmountLock = new Object();
+ @GuardedBy("mUnmountLock")
+ private CountDownLatch mUnmountSignal;
/**
* Private hash of currently mounted secure containers.
@@ -366,6 +581,7 @@
final private DefaultContainerConnection mDefContainerConn = new DefaultContainerConnection();
class DefaultContainerConnection implements ServiceConnection {
+ @Override
public void onServiceConnected(ComponentName name, IBinder service) {
if (DEBUG_OBB)
Slog.i(TAG, "onServiceConnected");
@@ -373,6 +589,7 @@
mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_MCS_BOUND, imcs));
}
+ @Override
public void onServiceDisconnected(ComponentName name) {
if (DEBUG_OBB)
Slog.i(TAG, "onServiceDisconnected");
@@ -388,177 +605,27 @@
private long mLastMaintenance;
// Handler messages
- private static final int H_UNMOUNT_PM_UPDATE = 1;
- private static final int H_UNMOUNT_PM_DONE = 2;
- private static final int H_UNMOUNT_MS = 3;
- private static final int H_SYSTEM_READY = 4;
- private static final int H_FSTRIM = 5;
-
- private static final int RETRY_UNMOUNT_DELAY = 30; // in ms
- private static final int MAX_UNMOUNT_RETRIES = 4;
-
- class UnmountCallBack {
- final String path;
- final boolean force;
- final boolean removeEncryption;
- int retries;
-
- UnmountCallBack(String path, boolean force, boolean removeEncryption) {
- retries = 0;
- this.path = path;
- this.force = force;
- this.removeEncryption = removeEncryption;
- }
-
- void handleFinished() {
- if (DEBUG_UNMOUNT) Slog.i(TAG, "Unmounting " + path);
- doUnmountVolume(path, true, removeEncryption);
- }
- }
-
- class UmsEnableCallBack extends UnmountCallBack {
- final String method;
-
- UmsEnableCallBack(String path, String method, boolean force) {
- super(path, force, false);
- this.method = method;
- }
-
- @Override
- void handleFinished() {
- super.handleFinished();
- doShareUnshareVolume(path, method, true);
- }
- }
-
- class ShutdownCallBack extends UnmountCallBack {
- MountShutdownLatch mMountShutdownLatch;
- ShutdownCallBack(String path, final MountShutdownLatch mountShutdownLatch) {
- super(path, true, false);
- mMountShutdownLatch = mountShutdownLatch;
- }
-
- @Override
- void handleFinished() {
- int ret = doUnmountVolume(path, true, removeEncryption);
- Slog.i(TAG, "Unmount completed: " + path + ", result code: " + ret);
- mMountShutdownLatch.countDown();
- }
- }
-
- static class MountShutdownLatch {
- private IMountShutdownObserver mObserver;
- private AtomicInteger mCount;
-
- MountShutdownLatch(final IMountShutdownObserver observer, int count) {
- mObserver = observer;
- mCount = new AtomicInteger(count);
- }
-
- void countDown() {
- boolean sendShutdown = false;
- if (mCount.decrementAndGet() == 0) {
- sendShutdown = true;
- }
- if (sendShutdown && mObserver != null) {
- try {
- mObserver.onShutDownComplete(StorageResultCode.OperationSucceeded);
- } catch (RemoteException e) {
- Slog.w(TAG, "RemoteException when shutting down");
- }
- }
- }
- }
+ private static final int H_SYSTEM_READY = 1;
+ private static final int H_DAEMON_CONNECTED = 2;
+ private static final int H_SHUTDOWN = 3;
+ private static final int H_FSTRIM = 4;
+ private static final int H_VOLUME_MOUNT = 5;
+ private static final int H_VOLUME_BROADCAST = 6;
class MountServiceHandler extends Handler {
- ArrayList<UnmountCallBack> mForceUnmounts = new ArrayList<UnmountCallBack>();
- boolean mUpdatingStatus = false;
-
- MountServiceHandler(Looper l) {
- super(l);
+ public MountServiceHandler(Looper looper) {
+ super(looper);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
- case H_UNMOUNT_PM_UPDATE: {
- if (DEBUG_UNMOUNT) Slog.i(TAG, "H_UNMOUNT_PM_UPDATE");
- UnmountCallBack ucb = (UnmountCallBack) msg.obj;
- mForceUnmounts.add(ucb);
- if (DEBUG_UNMOUNT) Slog.i(TAG, " registered = " + mUpdatingStatus);
- // Register only if needed.
- if (!mUpdatingStatus) {
- if (DEBUG_UNMOUNT) Slog.i(TAG, "Updating external media status on PackageManager");
- mUpdatingStatus = true;
- mPms.updateExternalMediaStatus(false, true);
- }
- break;
- }
- case H_UNMOUNT_PM_DONE: {
- if (DEBUG_UNMOUNT) Slog.i(TAG, "H_UNMOUNT_PM_DONE");
- if (DEBUG_UNMOUNT) Slog.i(TAG, "Updated status. Processing requests");
- mUpdatingStatus = false;
- int size = mForceUnmounts.size();
- int sizeArr[] = new int[size];
- int sizeArrN = 0;
- // Kill processes holding references first
- ActivityManagerService ams = (ActivityManagerService)
- ServiceManager.getService("activity");
- for (int i = 0; i < size; i++) {
- UnmountCallBack ucb = mForceUnmounts.get(i);
- String path = ucb.path;
- boolean done = false;
- if (!ucb.force) {
- done = true;
- } else {
- int pids[] = getStorageUsers(path);
- if (pids == null || pids.length == 0) {
- done = true;
- } else {
- // Eliminate system process here?
- ams.killPids(pids, "unmount media", true);
- // Confirm if file references have been freed.
- pids = getStorageUsers(path);
- if (pids == null || pids.length == 0) {
- done = true;
- }
- }
- }
- if (!done && (ucb.retries < MAX_UNMOUNT_RETRIES)) {
- // Retry again
- Slog.i(TAG, "Retrying to kill storage users again");
- mHandler.sendMessageDelayed(
- mHandler.obtainMessage(H_UNMOUNT_PM_DONE,
- ucb.retries++),
- RETRY_UNMOUNT_DELAY);
- } else {
- if (ucb.retries >= MAX_UNMOUNT_RETRIES) {
- Slog.i(TAG, "Failed to unmount media inspite of " +
- MAX_UNMOUNT_RETRIES + " retries. Forcibly killing processes now");
- }
- sizeArr[sizeArrN++] = i;
- mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_MS,
- ucb));
- }
- }
- // Remove already processed elements from list.
- for (int i = (sizeArrN-1); i >= 0; i--) {
- mForceUnmounts.remove(sizeArr[i]);
- }
- break;
- }
- case H_UNMOUNT_MS: {
- if (DEBUG_UNMOUNT) Slog.i(TAG, "H_UNMOUNT_MS");
- UnmountCallBack ucb = (UnmountCallBack) msg.obj;
- ucb.handleFinished();
- break;
- }
case H_SYSTEM_READY: {
- try {
- handleSystemReady();
- } catch (Exception ex) {
- Slog.e(TAG, "Boot-time mount exception", ex);
- }
+ handleSystemReady();
+ break;
+ }
+ case H_DAEMON_CONNECTED: {
+ handleDaemonConnected();
break;
}
case H_FSTRIM: {
@@ -589,29 +656,68 @@
}
break;
}
+ case H_SHUTDOWN: {
+ final IMountShutdownObserver obs = (IMountShutdownObserver) msg.obj;
+ boolean success = false;
+ try {
+ success = mConnector.execute("volume", "shutdown").isClassOk();
+ } catch (NativeDaemonConnectorException ignored) {
+ }
+ if (obs != null) {
+ try {
+ obs.onShutDownComplete(success ? 0 : -1);
+ } catch (RemoteException ignored) {
+ }
+ }
+ break;
+ }
+ case H_VOLUME_MOUNT: {
+ final Volume vol = (Volume) msg.obj;
+ try {
+ vol.mount();
+ } catch (NativeDaemonConnectorException ignored) {
+ }
+ break;
+ }
+ case H_VOLUME_BROADCAST: {
+ final StorageVolume userVol = (StorageVolume) msg.obj;
+ final String state = userVol.getState();
+ Slog.d(TAG, "Volume " + userVol.getId() + " broadcasting " + state + " to "
+ + userVol.getOwner());
+
+ final String action = sEnvironmentToBroadcast.get(state);
+ if (action != null) {
+ final Intent intent = new Intent(action,
+ Uri.fromFile(userVol.getPathFile()));
+ intent.putExtra(StorageVolume.EXTRA_STORAGE_VOLUME, userVol);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ mContext.sendBroadcastAsUser(intent, userVol.getOwner());
+ }
+ break;
+ }
}
}
- };
+ }
private final Handler mHandler;
@Override
public void waitForAsecScan() {
- waitForLatch(mAsecsScanned);
+ waitForLatch(mAsecsScanned, "mAsecsScanned");
}
private void waitForReady() {
- waitForLatch(mConnectedSignal);
+ waitForLatch(mConnectedSignal, "mConnectedSignal");
}
- private void waitForLatch(CountDownLatch latch) {
+ private void waitForLatch(CountDownLatch latch, String condition) {
for (;;) {
try {
if (latch.await(5000, TimeUnit.MILLISECONDS)) {
return;
} else {
Slog.w(TAG, "Thread " + Thread.currentThread().getName()
- + " still waiting for MountService ready...");
+ + " still waiting for " + condition + "...");
}
} catch (InterruptedException e) {
Slog.w(TAG, "Interrupt while waiting for MountService to be ready.");
@@ -628,103 +734,72 @@
}
private void handleSystemReady() {
- // Snapshot current volume states since it's not safe to call into vold
- // while holding locks.
- final HashMap<String, String> snapshot;
- synchronized (mVolumesLock) {
- snapshot = new HashMap<String, String>(mVolumeStates);
- }
+ resetIfReadyAndConnected();
- for (Map.Entry<String, String> entry : snapshot.entrySet()) {
- final String path = entry.getKey();
- final String state = entry.getValue();
-
- if (state.equals(Environment.MEDIA_UNMOUNTED)) {
- int rc = doMountVolume(path);
- if (rc != StorageResultCode.OperationSucceeded) {
- Slog.e(TAG, String.format("Boot-time mount failed (%d)",
- rc));
- }
- } else if (state.equals(Environment.MEDIA_SHARED)) {
- /*
- * Bootstrap UMS enabled state since vold indicates
- * the volume is shared (runtime restart while ums enabled)
- */
- notifyVolumeStateChange(null, path, VolumeState.NoMedia,
- VolumeState.Shared);
- }
- }
-
- // Push mounted state for all emulated storage
- synchronized (mVolumesLock) {
- for (StorageVolume volume : mVolumes) {
- if (volume.isEmulated()) {
- updatePublicVolumeState(volume, Environment.MEDIA_MOUNTED);
- }
- }
- }
-
- /*
- * If UMS was connected on boot, send the connected event
- * now that we're up.
- */
- if (mSendUmsConnectedOnBoot) {
- sendUmsIntent(true);
- mSendUmsConnectedOnBoot = false;
- }
-
- /*
- * Start scheduling nominally-daily fstrim operations
- */
+ // Start scheduling nominally-daily fstrim operations
MountServiceIdler.scheduleIdlePass(mContext);
}
- private final BroadcastReceiver mUserReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
- if (userId == -1) return;
- final UserHandle user = new UserHandle(userId);
+ private void resetIfReadyAndConnected() {
+ Slog.d(TAG, "Thinking about reset, mSystemReady=" + mSystemReady
+ + ", mDaemonConnected=" + mDaemonConnected);
+ if (mSystemReady && mDaemonConnected) {
+ mDisks.clear();
+ mVolumes.clear();
- final String action = intent.getAction();
- if (Intent.ACTION_USER_ADDED.equals(action)) {
- synchronized (mVolumesLock) {
- createEmulatedVolumeForUserLocked(user);
- }
-
- } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
- synchronized (mVolumesLock) {
- final List<StorageVolume> toRemove = Lists.newArrayList();
- for (StorageVolume volume : mVolumes) {
- if (user.equals(volume.getOwner())) {
- toRemove.add(volume);
- }
- }
- for (StorageVolume volume : toRemove) {
- removeVolumeLocked(volume);
- }
- }
+ try {
+ mConnector.execute("volume", "reset");
+ } catch (NativeDaemonConnectorException e) {
+ Slog.w(TAG, "Failed to reset vold", e);
}
}
- };
+ }
- private final BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- boolean available = (intent.getBooleanExtra(UsbManager.USB_CONNECTED, false) &&
- intent.getBooleanExtra(UsbManager.USB_FUNCTION_MASS_STORAGE, false));
- notifyShareAvailabilityChange(available);
+ private void onStartUser(int userId) {
+ Slog.d(TAG, "onStartUser " + userId);
+
+ // We purposefully block here to make sure that user-specific
+ // staging area is ready so it's ready for zygote-forked apps to
+ // bind mount against.
+ try {
+ mConnector.execute("volume", "start_user", userId);
+ } catch (NativeDaemonConnectorException ignored) {
}
- };
+
+ // Record user as started so newly mounted volumes kick off events
+ // correctly, then synthesize events for any already-mounted volumes.
+ synchronized (mVolumes) {
+ for (Volume vol : mVolumes.values()) {
+ if (vol.isVisibleToUser(userId) && vol.state == Volume.STATE_MOUNTED) {
+ final StorageVolume userVol = vol.buildVolumeForUser(userId);
+ mHandler.obtainMessage(H_VOLUME_BROADCAST, userVol).sendToTarget();
+ }
+ }
+ mStartedUsers = ArrayUtils.appendInt(mStartedUsers, userId);
+ }
+ }
+
+ private void onCleanupUser(int userId) {
+ Slog.d(TAG, "onCleanupUser " + userId);
+
+ try {
+ mConnector.execute("volume", "cleanup_user", userId);
+ } catch (NativeDaemonConnectorException ignored) {
+ }
+
+ synchronized (mVolumes) {
+ mStartedUsers = ArrayUtils.removeInt(mStartedUsers, userId);
+ }
+ }
private final class MountServiceBinderListener implements IBinder.DeathRecipient {
final IMountServiceListener mListener;
MountServiceBinderListener(IMountServiceListener listener) {
mListener = listener;
-
}
+ @Override
public void binderDied() {
if (LOCAL_LOGD) Slog.d(TAG, "An IMountServiceListener has died!");
synchronized (mListeners) {
@@ -741,7 +816,7 @@
// Binder entry point for kicking off an immediate fstrim
@Override
public void runMaintenance() {
- validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
+ enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
runIdleMaintenance(null);
}
@@ -750,144 +825,35 @@
return mLastMaintenance;
}
- private void doShareUnshareVolume(String path, String method, boolean enable) {
- // TODO: Add support for multiple share methods
- if (!method.equals("ums")) {
- throw new IllegalArgumentException(String.format("Method %s not supported", method));
- }
-
- try {
- mConnector.execute("volume", enable ? "share" : "unshare", path, method);
- } catch (NativeDaemonConnectorException e) {
- Slog.e(TAG, "Failed to share/unshare", e);
- }
- }
-
- private void updatePublicVolumeState(StorageVolume volume, String state) {
- final String path = volume.getPath();
- final String oldState;
- synchronized (mVolumesLock) {
- oldState = mVolumeStates.put(path, state);
- volume.setState(state);
- }
-
- if (state.equals(oldState)) {
- Slog.w(TAG, String.format("Duplicate state transition (%s -> %s) for %s",
- state, state, path));
- return;
- }
-
- Slog.d(TAG, "volume state changed for " + path + " (" + oldState + " -> " + state + ")");
-
- // Tell PackageManager about changes to primary volume state, but only
- // when not emulated.
- if (volume.isPrimary() && !volume.isEmulated()) {
- if (Environment.MEDIA_UNMOUNTED.equals(state)) {
- mPms.updateExternalMediaStatus(false, false);
-
- /*
- * Some OBBs might have been unmounted when this volume was
- * unmounted, so send a message to the handler to let it know to
- * remove those from the list of mounted OBBS.
- */
- mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(
- OBB_FLUSH_MOUNT_STATE, path));
- } else if (Environment.MEDIA_MOUNTED.equals(state)) {
- mPms.updateExternalMediaStatus(true, false);
- }
- }
-
- synchronized (mListeners) {
- for (int i = mListeners.size() -1; i >= 0; i--) {
- MountServiceBinderListener bl = mListeners.get(i);
- try {
- bl.mListener.onStorageStateChanged(path, oldState, state);
- } catch (RemoteException rex) {
- Slog.e(TAG, "Listener dead");
- mListeners.remove(i);
- } catch (Exception ex) {
- Slog.e(TAG, "Listener failed", ex);
- }
- }
- }
- }
-
/**
* Callback from NativeDaemonConnector
*/
+ @Override
public void onDaemonConnected() {
+ mDaemonConnected = true;
+ mHandler.obtainMessage(H_DAEMON_CONNECTED).sendToTarget();
+ }
+
+ private void handleDaemonConnected() {
+ resetIfReadyAndConnected();
+
/*
- * Since we'll be calling back into the NativeDaemonConnector,
- * we need to do our work in a new thread.
+ * Now that we've done our initialization, release
+ * the hounds!
*/
- new Thread("MountService#onDaemonConnected") {
- @Override
- public void run() {
- /**
- * Determine media state and UMS detection status
- */
- try {
- final String[] vols = NativeDaemonEvent.filterMessageList(
- mConnector.executeForList("volume", "list", "broadcast"),
- VoldResponseCode.VolumeListResult);
- for (String volstr : vols) {
- String[] tok = volstr.split(" ");
- // FMT: <label> <mountpoint> <state>
- String path = tok[1];
- String state = Environment.MEDIA_REMOVED;
+ mConnectedSignal.countDown();
- final StorageVolume volume;
- synchronized (mVolumesLock) {
- volume = mVolumesByPath.get(path);
- }
+ // On an encrypted device we can't see system properties yet, so pull
+ // the system locale out of the mount service.
+ if ("".equals(SystemProperties.get("vold.encrypt_progress"))) {
+ copyLocaleFromMountService();
+ }
- int st = Integer.parseInt(tok[2]);
- if (st == VolumeState.NoMedia) {
- state = Environment.MEDIA_REMOVED;
- } else if (st == VolumeState.Idle) {
- state = Environment.MEDIA_UNMOUNTED;
- } else if (st == VolumeState.Mounted) {
- state = Environment.MEDIA_MOUNTED;
- Slog.i(TAG, "Media already mounted on daemon connection");
- } else if (st == VolumeState.Shared) {
- state = Environment.MEDIA_SHARED;
- Slog.i(TAG, "Media shared on daemon connection");
- } else {
- throw new Exception(String.format("Unexpected state %d", st));
- }
+ // Let package manager load internal ASECs.
+ mPms.scanAvailableAsecs();
- if (state != null) {
- if (DEBUG_EVENTS) Slog.i(TAG, "Updating valid state " + state);
- updatePublicVolumeState(volume, state);
- }
- }
- } catch (Exception e) {
- Slog.e(TAG, "Error processing initial volume state", e);
- final StorageVolume primary = getPrimaryPhysicalVolume();
- if (primary != null) {
- updatePublicVolumeState(primary, Environment.MEDIA_REMOVED);
- }
- }
-
- /*
- * Now that we've done our initialization, release
- * the hounds!
- */
- mConnectedSignal.countDown();
-
- // On an encrypted device we can't see system properties yet, so pull
- // the system locale out of the mount service.
- if ("".equals(SystemProperties.get("vold.encrypt_progress"))) {
- copyLocaleFromMountService();
- }
-
- // Let package manager load internal ASECs.
- mPms.scanAvailableAsecs();
-
- // Notify people waiting for ASECs to be scanned that it's done.
- mAsecsScanned.countDown();
- }
- }.start();
+ // Notify people waiting for ASECs to be scanned that it's done.
+ mAsecsScanned.countDown();
}
private void copyLocaleFromMountService() {
@@ -919,6 +885,7 @@
/**
* Callback from NativeDaemonConnector
*/
+ @Override
public boolean onCheckHoldWakeLock(int code) {
return false;
}
@@ -926,347 +893,182 @@
/**
* Callback from NativeDaemonConnector
*/
+ @Override
public boolean onEvent(int code, String raw, String[] cooked) {
- if (DEBUG_EVENTS) {
- StringBuilder builder = new StringBuilder();
- builder.append("onEvent::");
- builder.append(" raw= " + raw);
- if (cooked != null) {
- builder.append(" cooked = " );
- for (String str : cooked) {
- builder.append(" " + str);
- }
- }
- Slog.i(TAG, builder.toString());
+ synchronized (mLock) {
+ return onEventLocked(code, raw, cooked);
}
- if (code == VoldResponseCode.VolumeStateChange) {
- /*
- * One of the volumes we're managing has changed state.
- * Format: "NNN Volume <label> <path> state changed
- * from <old_#> (<old_str>) to <new_#> (<new_str>)"
- */
- notifyVolumeStateChange(
- cooked[2], cooked[3], Integer.parseInt(cooked[7]),
- Integer.parseInt(cooked[10]));
- } else if (code == VoldResponseCode.VolumeUuidChange) {
- // Format: nnn <label> <path> <uuid>
- final String path = cooked[2];
- final String uuid = (cooked.length > 3) ? cooked[3] : null;
+ }
- final StorageVolume vol = mVolumesByPath.get(path);
- if (vol != null) {
- vol.setUuid(uuid);
+ private boolean onEventLocked(int code, String raw, String[] cooked) {
+ switch (code) {
+ case VoldResponseCode.DISK_CREATED: {
+ if (cooked.length != 3) break;
+ final String id = cooked[1];
+ final int flags = Integer.parseInt(cooked[2]);
+ mDisks.put(id, new Disk(id, flags));
+ break;
}
-
- } else if (code == VoldResponseCode.VolumeUserLabelChange) {
- // Format: nnn <label> <path> <label>
- final String path = cooked[2];
- final String userLabel = (cooked.length > 3) ? cooked[3] : null;
-
- final StorageVolume vol = mVolumesByPath.get(path);
- if (vol != null) {
- vol.setUserLabel(userLabel);
- }
-
- } else if ((code == VoldResponseCode.VolumeDiskInserted) ||
- (code == VoldResponseCode.VolumeDiskRemoved) ||
- (code == VoldResponseCode.VolumeBadRemoval)) {
- // FMT: NNN Volume <label> <mountpoint> disk inserted (<major>:<minor>)
- // FMT: NNN Volume <label> <mountpoint> disk removed (<major>:<minor>)
- // FMT: NNN Volume <label> <mountpoint> bad removal (<major>:<minor>)
- String action = null;
- final String label = cooked[2];
- final String path = cooked[3];
- int major = -1;
- int minor = -1;
-
- try {
- String devComp = cooked[6].substring(1, cooked[6].length() -1);
- String[] devTok = devComp.split(":");
- major = Integer.parseInt(devTok[0]);
- minor = Integer.parseInt(devTok[1]);
- } catch (Exception ex) {
- Slog.e(TAG, "Failed to parse major/minor", ex);
- }
-
- final StorageVolume volume;
- final String state;
- synchronized (mVolumesLock) {
- volume = mVolumesByPath.get(path);
- state = mVolumeStates.get(path);
- }
-
- if (code == VoldResponseCode.VolumeDiskInserted) {
- new Thread("MountService#VolumeDiskInserted") {
- @Override
- public void run() {
- try {
- int rc;
- if ((rc = doMountVolume(path)) != StorageResultCode.OperationSucceeded) {
- Slog.w(TAG, String.format("Insertion mount failed (%d)", rc));
- }
- } catch (Exception ex) {
- Slog.w(TAG, "Failed to mount media on insertion", ex);
- }
- }
- }.start();
- } else if (code == VoldResponseCode.VolumeDiskRemoved) {
- /*
- * This event gets trumped if we're already in BAD_REMOVAL state
- */
- if (getVolumeState(path).equals(Environment.MEDIA_BAD_REMOVAL)) {
- return true;
+ case VoldResponseCode.DISK_SIZE_CHANGED: {
+ if (cooked.length != 3) break;
+ final Disk disk = mDisks.get(cooked[1]);
+ if (disk != null) {
+ disk.size = Long.parseLong(cooked[2]);
}
- /* Send the media unmounted event first */
- if (DEBUG_EVENTS) Slog.i(TAG, "Sending unmounted event first");
- updatePublicVolumeState(volume, Environment.MEDIA_UNMOUNTED);
- sendStorageIntent(Intent.ACTION_MEDIA_UNMOUNTED, volume, UserHandle.ALL);
+ break;
+ }
+ case VoldResponseCode.DISK_LABEL_CHANGED: {
+ if (cooked.length != 3) break;
+ final Disk disk = mDisks.get(cooked[1]);
+ if (disk != null) {
+ disk.label = cooked[2];
+ }
+ break;
+ }
+ case VoldResponseCode.DISK_VOLUME_CREATED: {
+ if (cooked.length != 3) break;
+ final Disk disk = mDisks.get(cooked[1]);
+ final Volume vol = mVolumes.get(cooked[2]);
+ if (disk != null && vol != null) {
+ disk.volumes.add(vol);
+ }
+ break;
+ }
+ case VoldResponseCode.DISK_DESTROYED: {
+ if (cooked.length != 2) break;
+ mDisks.remove(cooked[1]);
+ break;
+ }
- if (DEBUG_EVENTS) Slog.i(TAG, "Sending media removed");
- updatePublicVolumeState(volume, Environment.MEDIA_REMOVED);
- action = Intent.ACTION_MEDIA_REMOVED;
- } else if (code == VoldResponseCode.VolumeBadRemoval) {
- if (DEBUG_EVENTS) Slog.i(TAG, "Sending unmounted event first");
- /* Send the media unmounted event first */
- updatePublicVolumeState(volume, Environment.MEDIA_UNMOUNTED);
- sendStorageIntent(Intent.ACTION_MEDIA_UNMOUNTED, volume, UserHandle.ALL);
+ case VoldResponseCode.VOLUME_CREATED: {
+ if (cooked.length != 3) break;
+ final String id = cooked[1];
+ final int type = Integer.parseInt(cooked[2]);
+ final Volume vol = new Volume(id, type);
+ mVolumes.put(id, vol);
+ onVolumeCreatedLocked(vol);
+ break;
+ }
+ case VoldResponseCode.VOLUME_STATE_CHANGED: {
+ if (cooked.length != 3) break;
+ final Volume vol = mVolumes.get(cooked[1]);
+ if (vol != null) {
+ final int oldState = vol.state;
+ final int newState = Integer.parseInt(cooked[2]);
+ vol.state = newState;
+ onVolumeStateChangedLocked(vol, oldState, newState);
+ }
+ break;
+ }
+ case VoldResponseCode.VOLUME_FS_TYPE_CHANGED: {
+ if (cooked.length != 3) break;
+ final Volume vol = mVolumes.get(cooked[1]);
+ if (vol != null) {
+ vol.fsType = cooked[2];
+ }
+ break;
+ }
+ case VoldResponseCode.VOLUME_FS_UUID_CHANGED: {
+ if (cooked.length != 3) break;
+ final Volume vol = mVolumes.get(cooked[1]);
+ if (vol != null) {
+ vol.fsUuid = cooked[2];
+ }
+ break;
+ }
+ case VoldResponseCode.VOLUME_FS_LABEL_CHANGED: {
+ if (cooked.length != 3) break;
+ final Volume vol = mVolumes.get(cooked[1]);
+ if (vol != null) {
+ vol.fsLabel = cooked[2];
+ }
+ break;
+ }
+ case VoldResponseCode.VOLUME_PATH_CHANGED: {
+ if (cooked.length != 3) break;
+ final Volume vol = mVolumes.get(cooked[1]);
+ if (vol != null) {
+ vol.path = cooked[2];
+ }
+ break;
+ }
+ case VoldResponseCode.VOLUME_DESTROYED: {
+ if (cooked.length != 2) break;
+ mVolumes.remove(cooked[1]);
+ break;
+ }
- if (DEBUG_EVENTS) Slog.i(TAG, "Sending media bad removal");
- updatePublicVolumeState(volume, Environment.MEDIA_BAD_REMOVAL);
- action = Intent.ACTION_MEDIA_BAD_REMOVAL;
- } else if (code == VoldResponseCode.FstrimCompleted) {
+ case VoldResponseCode.FstrimCompleted: {
EventLogTags.writeFstrimFinish(SystemClock.elapsedRealtime());
- } else {
- Slog.e(TAG, String.format("Unknown code {%d}", code));
+ break;
}
-
- if (action != null) {
- sendStorageIntent(action, volume, UserHandle.ALL);
+ default: {
+ Slog.d(TAG, "Unhandled vold event " + code);
}
- } else {
- return false;
}
return true;
}
- private void notifyVolumeStateChange(String label, String path, int oldState, int newState) {
- final StorageVolume volume;
- final String state;
- synchronized (mVolumesLock) {
- volume = mVolumesByPath.get(path);
- state = getVolumeState(path);
- }
+ private void onVolumeCreatedLocked(Volume vol) {
+ final boolean primaryPhysical = SystemProperties.getBoolean(PROP_PRIMARY_PHYSICAL, false);
+ if (vol.type == Volume.TYPE_EMULATED && !primaryPhysical) {
+ vol.flags |= Volume.FLAG_PRIMARY;
+ vol.flags |= Volume.FLAG_VISIBLE;
+ mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget();
- if (DEBUG_EVENTS) Slog.i(TAG, "notifyVolumeStateChange::" + state);
-
- String action = null;
-
- if (oldState == VolumeState.Shared && newState != oldState) {
- if (LOCAL_LOGD) Slog.d(TAG, "Sending ACTION_MEDIA_UNSHARED intent");
- sendStorageIntent(Intent.ACTION_MEDIA_UNSHARED, volume, UserHandle.ALL);
- }
-
- if (newState == VolumeState.Init) {
- } else if (newState == VolumeState.NoMedia) {
- // NoMedia is handled via Disk Remove events
- } else if (newState == VolumeState.Idle) {
- /*
- * Don't notify if we're in BAD_REMOVAL, NOFS, UNMOUNTABLE, or
- * if we're in the process of enabling UMS
- */
- if (!state.equals(
- Environment.MEDIA_BAD_REMOVAL) && !state.equals(
- Environment.MEDIA_NOFS) && !state.equals(
- Environment.MEDIA_UNMOUNTABLE) && !getUmsEnabling()) {
- if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state for media bad removal nofs and unmountable");
- updatePublicVolumeState(volume, Environment.MEDIA_UNMOUNTED);
- action = Intent.ACTION_MEDIA_UNMOUNTED;
+ } else if (vol.type == Volume.TYPE_PUBLIC) {
+ if (primaryPhysical) {
+ vol.flags |= Volume.FLAG_PRIMARY;
}
- } else if (newState == VolumeState.Pending) {
- } else if (newState == VolumeState.Checking) {
- if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state checking");
- updatePublicVolumeState(volume, Environment.MEDIA_CHECKING);
- action = Intent.ACTION_MEDIA_CHECKING;
- } else if (newState == VolumeState.Mounted) {
- if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state mounted");
- updatePublicVolumeState(volume, Environment.MEDIA_MOUNTED);
- action = Intent.ACTION_MEDIA_MOUNTED;
- } else if (newState == VolumeState.Unmounting) {
- action = Intent.ACTION_MEDIA_EJECT;
- } else if (newState == VolumeState.Formatting) {
- } else if (newState == VolumeState.Shared) {
- if (DEBUG_EVENTS) Slog.i(TAG, "Updating volume state media mounted");
- /* Send the media unmounted event first */
- updatePublicVolumeState(volume, Environment.MEDIA_UNMOUNTED);
- sendStorageIntent(Intent.ACTION_MEDIA_UNMOUNTED, volume, UserHandle.ALL);
+ vol.flags |= Volume.FLAG_VISIBLE;
+ vol.userId = UserHandle.USER_OWNER;
+ mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget();
- if (DEBUG_EVENTS) Slog.i(TAG, "Updating media shared");
- updatePublicVolumeState(volume, Environment.MEDIA_SHARED);
- action = Intent.ACTION_MEDIA_SHARED;
- if (LOCAL_LOGD) Slog.d(TAG, "Sending ACTION_MEDIA_SHARED intent");
- } else if (newState == VolumeState.SharedMnt) {
- Slog.e(TAG, "Live shared mounts not supported yet!");
- return;
} else {
- Slog.e(TAG, "Unhandled VolumeState {" + newState + "}");
- }
-
- if (action != null) {
- sendStorageIntent(action, volume, UserHandle.ALL);
+ Slog.d(TAG, "Skipping automatic mounting of " + vol);
}
}
- private int doMountVolume(String path) {
- int rc = StorageResultCode.OperationSucceeded;
-
- final StorageVolume volume;
- synchronized (mVolumesLock) {
- volume = mVolumesByPath.get(path);
+ private void onVolumeStateChangedLocked(Volume vol, int oldState, int newState) {
+ // Kick state changed event towards all started users. Any users
+ // started after this point will trigger additional
+ // user-specific broadcasts.
+ for (int userId : mStartedUsers) {
+ if (vol.isVisibleToUser(userId)) {
+ final StorageVolume userVol = vol.buildVolumeForUser(userId);
+ mHandler.obtainMessage(H_VOLUME_BROADCAST, userVol).sendToTarget();
+ }
}
- if (!volume.isEmulated() && hasUserRestriction(UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA)) {
- Slog.w(TAG, "User has restriction DISALLOW_MOUNT_PHYSICAL_MEDIA; cannot mount volume.");
- return StorageResultCode.OperationFailedInternalError;
- }
+ // Tell PackageManager about changes to primary volume state, but only
+ // when not emulated.
+ if (vol.isPrimary() && vol.type == Volume.TYPE_PUBLIC) {
+ if (vol.state == Volume.STATE_MOUNTED) {
+ mPms.updateExternalMediaStatus(true, false);
- if (DEBUG_EVENTS) Slog.i(TAG, "doMountVolume: Mouting " + path);
- try {
- mConnector.execute("volume", "mount", path);
- } catch (NativeDaemonConnectorException e) {
- /*
- * Mount failed for some reason
- */
- String action = null;
- int code = e.getCode();
- if (code == VoldResponseCode.OpFailedNoMedia) {
+ } else if (vol.state == Volume.STATE_UNMOUNTING) {
+ mPms.updateExternalMediaStatus(false, false);
+
+ // TODO: this should eventually be handled by new ObbVolume state changes
/*
- * Attempt to mount but no media inserted
+ * Some OBBs might have been unmounted when this volume was
+ * unmounted, so send a message to the handler to let it know to
+ * remove those from the list of mounted OBBS.
*/
- rc = StorageResultCode.OperationFailedNoMedia;
- } else if (code == VoldResponseCode.OpFailedMediaBlank) {
- if (DEBUG_EVENTS) Slog.i(TAG, " updating volume state :: media nofs");
- /*
- * Media is blank or does not contain a supported filesystem
- */
- updatePublicVolumeState(volume, Environment.MEDIA_NOFS);
- action = Intent.ACTION_MEDIA_NOFS;
- rc = StorageResultCode.OperationFailedMediaBlank;
- } else if (code == VoldResponseCode.OpFailedMediaCorrupt) {
- if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state media corrupt");
- /*
- * Volume consistency check failed
- */
- updatePublicVolumeState(volume, Environment.MEDIA_UNMOUNTABLE);
- action = Intent.ACTION_MEDIA_UNMOUNTABLE;
- rc = StorageResultCode.OperationFailedMediaCorrupt;
- } else {
- rc = StorageResultCode.OperationFailedInternalError;
- }
-
- /*
- * Send broadcast intent (if required for the failure)
- */
- if (action != null) {
- sendStorageIntent(action, volume, UserHandle.ALL);
+ mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(
+ OBB_FLUSH_MOUNT_STATE, vol.path));
}
}
- return rc;
- }
+ final String oldEnvState = sStateToEnvironment.get(oldState);
+ final String newEnvState = sStateToEnvironment.get(newState);
- /*
- * If force is not set, we do not unmount if there are
- * processes holding references to the volume about to be unmounted.
- * If force is set, all the processes holding references need to be
- * killed via the ActivityManager before actually unmounting the volume.
- * This might even take a while and might be retried after timed delays
- * to make sure we dont end up in an instable state and kill some core
- * processes.
- * If removeEncryption is set, force is implied, and the system will remove any encryption
- * mapping set on the volume when unmounting.
- */
- private int doUnmountVolume(String path, boolean force, boolean removeEncryption) {
- if (!getVolumeState(path).equals(Environment.MEDIA_MOUNTED)) {
- return VoldResponseCode.OpFailedVolNotMounted;
- }
-
- /*
- * Force a GC to make sure AssetManagers in other threads of the
- * system_server are cleaned up. We have to do this since AssetManager
- * instances are kept as a WeakReference and it's possible we have files
- * open on the external storage.
- */
- Runtime.getRuntime().gc();
-
- // Redundant probably. But no harm in updating state again.
- mPms.updateExternalMediaStatus(false, false);
- try {
- final Command cmd = new Command("volume", "unmount", path);
- if (removeEncryption) {
- cmd.appendArg("force_and_revert");
- } else if (force) {
- cmd.appendArg("force");
- }
- mConnector.execute(cmd);
- // We unmounted the volume. None of the asec containers are available now.
- synchronized (mAsecMountSet) {
- mAsecMountSet.clear();
- }
- return StorageResultCode.OperationSucceeded;
- } catch (NativeDaemonConnectorException e) {
- // Don't worry about mismatch in PackageManager since the
- // call back will handle the status changes any way.
- int code = e.getCode();
- if (code == VoldResponseCode.OpFailedVolNotMounted) {
- return StorageResultCode.OperationFailedStorageNotMounted;
- } else if (code == VoldResponseCode.OpFailedStorageBusy) {
- return StorageResultCode.OperationFailedStorageBusy;
- } else {
- return StorageResultCode.OperationFailedInternalError;
- }
- }
- }
-
- private int doFormatVolume(String path) {
- try {
- mConnector.execute("volume", "format", path);
- return StorageResultCode.OperationSucceeded;
- } catch (NativeDaemonConnectorException e) {
- int code = e.getCode();
- if (code == VoldResponseCode.OpFailedNoMedia) {
- return StorageResultCode.OperationFailedNoMedia;
- } else if (code == VoldResponseCode.OpFailedMediaCorrupt) {
- return StorageResultCode.OperationFailedMediaCorrupt;
- } else {
- return StorageResultCode.OperationFailedInternalError;
- }
- }
- }
-
- private boolean doGetVolumeShared(String path, String method) {
- final NativeDaemonEvent event;
- try {
- event = mConnector.execute("volume", "shared", path, method);
- } catch (NativeDaemonConnectorException ex) {
- Slog.e(TAG, "Failed to read response to volume shared " + path + " " + method);
- return false;
- }
-
- if (event.getCode() == VoldResponseCode.ShareEnabledResult) {
- return event.getMessage().endsWith("enabled");
- } else {
- return false;
- }
- }
-
- private void notifyShareAvailabilityChange(final boolean avail) {
synchronized (mListeners) {
- mUmsAvailable = avail;
for (int i = mListeners.size() -1; i >= 0; i--) {
MountServiceBinderListener bl = mListeners.get(i);
try {
- bl.mListener.onUsbMassStorageConnectionChanged(avail);
+ bl.mListener.onStorageStateChanged(vol.path, oldEnvState, newEnvState);
} catch (RemoteException rex) {
Slog.e(TAG, "Listener dead");
mListeners.remove(i);
@@ -1275,221 +1077,19 @@
}
}
}
-
- if (mSystemReady == true) {
- sendUmsIntent(avail);
- } else {
- mSendUmsConnectedOnBoot = avail;
- }
-
- final StorageVolume primary = getPrimaryPhysicalVolume();
- if (avail == false && primary != null
- && Environment.MEDIA_SHARED.equals(getVolumeState(primary.getPath()))) {
- final String path = primary.getPath();
- /*
- * USB mass storage disconnected while enabled
- */
- new Thread("MountService#AvailabilityChange") {
- @Override
- public void run() {
- try {
- int rc;
- Slog.w(TAG, "Disabling UMS after cable disconnect");
- doShareUnshareVolume(path, "ums", false);
- if ((rc = doMountVolume(path)) != StorageResultCode.OperationSucceeded) {
- Slog.e(TAG, String.format(
- "Failed to remount {%s} on UMS enabled-disconnect (%d)",
- path, rc));
- }
- } catch (Exception ex) {
- Slog.w(TAG, "Failed to mount media on UMS enabled-disconnect", ex);
- }
- }
- }.start();
- }
}
- private void sendStorageIntent(String action, StorageVolume volume, UserHandle user) {
- final Intent intent = new Intent(action, Uri.parse("file://" + volume.getPath()));
- intent.putExtra(StorageVolume.EXTRA_STORAGE_VOLUME, volume);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
- Slog.d(TAG, "sendStorageIntent " + intent + " to " + user);
- mContext.sendBroadcastAsUser(intent, user);
+ private void enforcePermission(String perm) {
+ mContext.enforceCallingOrSelfPermission(perm, perm);
}
- private void sendUmsIntent(boolean c) {
- mContext.sendBroadcastAsUser(
- new Intent((c ? Intent.ACTION_UMS_CONNECTED : Intent.ACTION_UMS_DISCONNECTED)),
- UserHandle.ALL);
- }
-
- private void validatePermission(String perm) {
- if (mContext.checkCallingOrSelfPermission(perm) != PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException(String.format("Requires %s permission", perm));
- }
- }
-
- private boolean hasUserRestriction(String restriction) {
+ private void enforceUserRestriction(String restriction) {
UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
- return um.hasUserRestriction(restriction, Binder.getCallingUserHandle());
- }
-
- private void validateUserRestriction(String restriction) {
- if (hasUserRestriction(restriction)) {
+ if (um.hasUserRestriction(restriction, Binder.getCallingUserHandle())) {
throw new SecurityException("User has restriction " + restriction);
}
}
- // Storage list XML tags
- private static final String TAG_STORAGE_LIST = "StorageList";
- private static final String TAG_STORAGE = "storage";
-
- private void readStorageListLocked() {
- mVolumes.clear();
- mVolumeStates.clear();
-
- Resources resources = mContext.getResources();
-
- int id = com.android.internal.R.xml.storage_list;
- XmlResourceParser parser = resources.getXml(id);
- AttributeSet attrs = Xml.asAttributeSet(parser);
-
- try {
- XmlUtils.beginDocument(parser, TAG_STORAGE_LIST);
- while (true) {
- XmlUtils.nextElement(parser);
-
- String element = parser.getName();
- if (element == null) break;
-
- if (TAG_STORAGE.equals(element)) {
- TypedArray a = resources.obtainAttributes(attrs,
- com.android.internal.R.styleable.Storage);
-
- String path = a.getString(
- com.android.internal.R.styleable.Storage_mountPoint);
- int descriptionId = a.getResourceId(
- com.android.internal.R.styleable.Storage_storageDescription, -1);
- CharSequence description = a.getText(
- com.android.internal.R.styleable.Storage_storageDescription);
- boolean primary = a.getBoolean(
- com.android.internal.R.styleable.Storage_primary, false);
- boolean removable = a.getBoolean(
- com.android.internal.R.styleable.Storage_removable, false);
- boolean emulated = a.getBoolean(
- com.android.internal.R.styleable.Storage_emulated, false);
- int mtpReserve = a.getInt(
- com.android.internal.R.styleable.Storage_mtpReserve, 0);
- boolean allowMassStorage = a.getBoolean(
- com.android.internal.R.styleable.Storage_allowMassStorage, false);
- // resource parser does not support longs, so XML value is in megabytes
- long maxFileSize = a.getInt(
- com.android.internal.R.styleable.Storage_maxFileSize, 0) * 1024L * 1024L;
-
- Slog.d(TAG, "got storage path: " + path + " description: " + description +
- " primary: " + primary + " removable: " + removable +
- " emulated: " + emulated + " mtpReserve: " + mtpReserve +
- " allowMassStorage: " + allowMassStorage +
- " maxFileSize: " + maxFileSize);
-
- if (emulated) {
- // For devices with emulated storage, we create separate
- // volumes for each known user.
- mEmulatedTemplate = new StorageVolume(null, descriptionId, true, false,
- true, mtpReserve, false, maxFileSize, null);
-
- final UserManagerService userManager = UserManagerService.getInstance();
- for (UserInfo user : userManager.getUsers(false)) {
- createEmulatedVolumeForUserLocked(user.getUserHandle());
- }
-
- } else {
- if (path == null || description == null) {
- Slog.e(TAG, "Missing storage path or description in readStorageList");
- } else {
- final StorageVolume volume = new StorageVolume(new File(path),
- descriptionId, primary, removable, emulated, mtpReserve,
- allowMassStorage, maxFileSize, null);
- addVolumeLocked(volume);
-
- // Until we hear otherwise, treat as unmounted
- mVolumeStates.put(volume.getPath(), Environment.MEDIA_UNMOUNTED);
- volume.setState(Environment.MEDIA_UNMOUNTED);
- }
- }
-
- a.recycle();
- }
- }
- } catch (XmlPullParserException e) {
- throw new RuntimeException(e);
- } catch (IOException e) {
- throw new RuntimeException(e);
- } finally {
- // Compute storage ID for each physical volume; emulated storage is
- // always 0 when defined.
- int index = isExternalStorageEmulated() ? 1 : 0;
- for (StorageVolume volume : mVolumes) {
- if (!volume.isEmulated()) {
- volume.setStorageId(index++);
- }
- }
- parser.close();
- }
- }
-
- /**
- * Create and add new {@link StorageVolume} for given {@link UserHandle}
- * using {@link #mEmulatedTemplate} as template.
- */
- private void createEmulatedVolumeForUserLocked(UserHandle user) {
- if (mEmulatedTemplate == null) {
- throw new IllegalStateException("Missing emulated volume multi-user template");
- }
-
- final UserEnvironment userEnv = new UserEnvironment(user.getIdentifier());
- final File path = userEnv.getExternalStorageDirectory();
- final StorageVolume volume = StorageVolume.fromTemplate(mEmulatedTemplate, path, user);
- volume.setStorageId(0);
- addVolumeLocked(volume);
-
- if (mSystemReady) {
- updatePublicVolumeState(volume, Environment.MEDIA_MOUNTED);
- } else {
- // Place stub status for early callers to find
- mVolumeStates.put(volume.getPath(), Environment.MEDIA_MOUNTED);
- volume.setState(Environment.MEDIA_MOUNTED);
- }
- }
-
- private void addVolumeLocked(StorageVolume volume) {
- Slog.d(TAG, "addVolumeLocked() " + volume);
- mVolumes.add(volume);
- final StorageVolume existing = mVolumesByPath.put(volume.getPath(), volume);
- if (existing != null) {
- throw new IllegalStateException(
- "Volume at " + volume.getPath() + " already exists: " + existing);
- }
- }
-
- private void removeVolumeLocked(StorageVolume volume) {
- Slog.d(TAG, "removeVolumeLocked() " + volume);
- mVolumes.remove(volume);
- mVolumesByPath.remove(volume.getPath());
- mVolumeStates.remove(volume.getPath());
- }
-
- private StorageVolume getPrimaryPhysicalVolume() {
- synchronized (mVolumesLock) {
- for (StorageVolume volume : mVolumes) {
- if (volume.isPrimary() && !volume.isEmulated()) {
- return volume;
- }
- }
- }
- return null;
- }
-
/**
* Constructs a new MountService instance
*
@@ -1500,10 +1100,6 @@
mContext = context;
- synchronized (mVolumesLock) {
- readStorageListLocked();
- }
-
// XXX: This will go away soon in favor of IMountServiceObserver
mPms = (PackageManagerService) ServiceManager.getService("package");
@@ -1511,19 +1107,6 @@
hthread.start();
mHandler = new MountServiceHandler(hthread.getLooper());
- // Watch for user changes
- final IntentFilter userFilter = new IntentFilter();
- userFilter.addAction(Intent.ACTION_USER_ADDED);
- userFilter.addAction(Intent.ACTION_USER_REMOVED);
- mContext.registerReceiver(mUserReceiver, userFilter, null, mHandler);
-
- // Watch for USB changes on primary volume
- final StorageVolume primary = getPrimaryPhysicalVolume();
- if (primary != null && primary.allowMassStorage()) {
- mContext.registerReceiver(
- mUsbReceiver, new IntentFilter(UsbManager.ACTION_USB_STATE), null, mHandler);
- }
-
// Add OBB Action Handler to MountService thread.
mObbActionHandler = new ObbActionHandler(IoThread.get().getLooper());
@@ -1550,6 +1133,7 @@
*/
mConnector = new NativeDaemonConnector(this, "vold", MAX_CONTAINERS * 2, VOLD_TAG, 25,
null);
+ mConnector.setDebug(true);
Thread thread = new Thread(mConnector, VOLD_TAG);
thread.start();
@@ -1593,201 +1177,118 @@
}
}
+ @Override
public void shutdown(final IMountShutdownObserver observer) {
- validatePermission(android.Manifest.permission.SHUTDOWN);
+ enforcePermission(android.Manifest.permission.SHUTDOWN);
Slog.i(TAG, "Shutting down");
- synchronized (mVolumesLock) {
- // Get all volumes to be unmounted.
- MountShutdownLatch mountShutdownLatch = new MountShutdownLatch(observer,
- mVolumeStates.size());
-
- for (String path : mVolumeStates.keySet()) {
- String state = mVolumeStates.get(path);
-
- if (state.equals(Environment.MEDIA_SHARED)) {
- /*
- * If the media is currently shared, unshare it.
- * XXX: This is still dangerous!. We should not
- * be rebooting at *all* if UMS is enabled, since
- * the UMS host could have dirty FAT cache entries
- * yet to flush.
- */
- setUsbMassStorageEnabled(false);
- } else if (state.equals(Environment.MEDIA_CHECKING)) {
- /*
- * If the media is being checked, then we need to wait for
- * it to complete before being able to proceed.
- */
- // XXX: @hackbod - Should we disable the ANR timer here?
- int retries = 30;
- while (state.equals(Environment.MEDIA_CHECKING) && (retries-- >=0)) {
- try {
- Thread.sleep(1000);
- } catch (InterruptedException iex) {
- Slog.e(TAG, "Interrupted while waiting for media", iex);
- break;
- }
- state = Environment.getExternalStorageState();
- }
- if (retries == 0) {
- Slog.e(TAG, "Timed out waiting for media to check");
- }
- }
-
- if (state.equals(Environment.MEDIA_MOUNTED)) {
- // Post a unmount message.
- ShutdownCallBack ucb = new ShutdownCallBack(path, mountShutdownLatch);
- mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_PM_UPDATE, ucb));
- } else if (observer != null) {
- /*
- * Count down, since nothing will be done. The observer will be
- * notified when we are done so shutdown sequence can continue.
- */
- mountShutdownLatch.countDown();
- Slog.i(TAG, "Unmount completed: " + path +
- ", result code: " + StorageResultCode.OperationSucceeded);
- }
- }
- }
+ mHandler.obtainMessage(H_SHUTDOWN, observer).sendToTarget();
}
- private boolean getUmsEnabling() {
- synchronized (mListeners) {
- return mUmsEnabling;
- }
- }
-
- private void setUmsEnabling(boolean enable) {
- synchronized (mListeners) {
- mUmsEnabling = enable;
- }
- }
-
+ @Override
+ @Deprecated
public boolean isUsbMassStorageConnected() {
- waitForReady();
-
- if (getUmsEnabling()) {
- return true;
- }
- synchronized (mListeners) {
- return mUmsAvailable;
- }
+ return false;
}
+ @Override
+ @Deprecated
public void setUsbMassStorageEnabled(boolean enable) {
- waitForReady();
- validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
- validateUserRestriction(UserManager.DISALLOW_USB_FILE_TRANSFER);
-
- final StorageVolume primary = getPrimaryPhysicalVolume();
- if (primary == null) return;
-
- // TODO: Add support for multiple share methods
-
- /*
- * If the volume is mounted and we're enabling then unmount it
- */
- String path = primary.getPath();
- String vs = getVolumeState(path);
- String method = "ums";
- if (enable && vs.equals(Environment.MEDIA_MOUNTED)) {
- // Override for isUsbMassStorageEnabled()
- setUmsEnabling(enable);
- UmsEnableCallBack umscb = new UmsEnableCallBack(path, method, true);
- mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_PM_UPDATE, umscb));
- // Clear override
- setUmsEnabling(false);
- }
- /*
- * If we disabled UMS then mount the volume
- */
- if (!enable) {
- doShareUnshareVolume(path, method, enable);
- if (doMountVolume(path) != StorageResultCode.OperationSucceeded) {
- Slog.e(TAG, "Failed to remount " + path +
- " after disabling share method " + method);
- /*
- * Even though the mount failed, the unshare didn't so don't indicate an error.
- * The mountVolume() call will have set the storage state and sent the necessary
- * broadcasts.
- */
- }
- }
+ throw new UnsupportedOperationException();
}
+ @Override
+ @Deprecated
public boolean isUsbMassStorageEnabled() {
- waitForReady();
-
- final StorageVolume primary = getPrimaryPhysicalVolume();
- if (primary != null) {
- return doGetVolumeShared(primary.getPath(), "ums");
- } else {
- return false;
- }
+ return false;
}
/**
* @return state of the volume at the specified mount point
*/
+ @Override
+ @Deprecated
public String getVolumeState(String mountPoint) {
- synchronized (mVolumesLock) {
- String state = mVolumeStates.get(mountPoint);
- if (state == null) {
- Slog.w(TAG, "getVolumeState(" + mountPoint + "): Unknown volume");
- if (SystemProperties.get("vold.encrypt_progress").length() != 0) {
- state = Environment.MEDIA_REMOVED;
- } else {
- throw new IllegalArgumentException();
- }
- }
+ // TODO: pretend that we're unmounted when encrypting?
+ // SystemProperties.get("vold.encrypt_progress")
- return state;
- }
+ final Volume vol = findVolumeByLegacyPath(mountPoint);
+ return sStateToEnvironment.get(vol.state);
}
@Override
public boolean isExternalStorageEmulated() {
- return mEmulatedTemplate != null;
+ return Environment.isExternalStorageEmulated();
}
+ @Override
public int mountVolume(String path) {
- validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
+ enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
waitForReady();
- return doMountVolume(path);
+
+ final Volume vol = findVolumeByLegacyPath(path);
+ if (vol != null) {
+ if (vol.type == Volume.TYPE_PUBLIC || vol.type == Volume.TYPE_PRIVATE) {
+ enforceUserRestriction(UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA);
+ }
+ try {
+ vol.mount();
+ return 0;
+ } catch (NativeDaemonConnectorException ignored) {
+ }
+ } else {
+ Slog.w(TAG, "Unknown volume for path " + path);
+ }
+ return -1;
}
+ @Override
public void unmountVolume(String path, boolean force, boolean removeEncryption) {
- validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
+ enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
waitForReady();
- String volState = getVolumeState(path);
- if (DEBUG_UNMOUNT) {
- Slog.i(TAG, "Unmounting " + path
- + " force = " + force
- + " removeEncryption = " + removeEncryption);
+ final Volume vol = findVolumeByLegacyPath(path);
+ if (vol != null) {
+ // TODO: expand PMS to know about multiple volumes
+ if (vol.isPrimary()) {
+ synchronized (mUnmountLock) {
+ mUnmountSignal = new CountDownLatch(1);
+ mPms.updateExternalMediaStatus(false, true);
+ waitForLatch(mUnmountSignal, "mUnmountSignal");
+ mUnmountSignal = null;
+ }
+ }
+
+ try {
+ vol.unmount();
+ } catch (NativeDaemonConnectorException ignored) {
+ }
+ } else {
+ Slog.w(TAG, "Unknown volume for path " + path);
}
- if (Environment.MEDIA_UNMOUNTED.equals(volState) ||
- Environment.MEDIA_REMOVED.equals(volState) ||
- Environment.MEDIA_SHARED.equals(volState) ||
- Environment.MEDIA_UNMOUNTABLE.equals(volState)) {
- // Media already unmounted or cannot be unmounted.
- // TODO return valid return code when adding observer call back.
- return;
- }
- UnmountCallBack ucb = new UnmountCallBack(path, force, removeEncryption);
- mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_PM_UPDATE, ucb));
}
+ @Override
public int formatVolume(String path) {
- validatePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);
+ enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);
waitForReady();
- return doFormatVolume(path);
+ final Volume vol = findVolumeByLegacyPath(path);
+ if (vol != null) {
+ try {
+ vol.format();
+ return 0;
+ } catch (NativeDaemonConnectorException ignored) {
+ }
+ } else {
+ Slog.w(TAG, "Unknown volume for path " + path);
+ }
+ return -1;
}
+ @Override
public int[] getStorageUsers(String path) {
- validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
+ enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
waitForReady();
try {
final String[] r = NativeDaemonEvent.filterMessageList(
@@ -1813,22 +1314,20 @@
}
private void warnOnNotMounted() {
- final StorageVolume primary = getPrimaryPhysicalVolume();
- if (primary != null) {
- boolean mounted = false;
- try {
- mounted = Environment.MEDIA_MOUNTED.equals(getVolumeState(primary.getPath()));
- } catch (IllegalArgumentException e) {
- }
-
- if (!mounted) {
- Slog.w(TAG, "getSecureContainerList() called when storage not mounted");
+ synchronized (mLock) {
+ for (Volume vol : mVolumes.values()) {
+ if (vol.isPrimary() && vol.state == Volume.STATE_MOUNTED) {
+ // Cool beans, we have a mounted primary volume
+ return;
+ }
}
}
+
+ Slog.w(TAG, "No primary storage mounted!");
}
public String[] getSecureContainerList() {
- validatePermission(android.Manifest.permission.ASEC_ACCESS);
+ enforcePermission(android.Manifest.permission.ASEC_ACCESS);
waitForReady();
warnOnNotMounted();
@@ -1842,7 +1341,7 @@
public int createSecureContainer(String id, int sizeMb, String fstype, String key,
int ownerUid, boolean external) {
- validatePermission(android.Manifest.permission.ASEC_CREATE);
+ enforcePermission(android.Manifest.permission.ASEC_CREATE);
waitForReady();
warnOnNotMounted();
@@ -1864,7 +1363,7 @@
@Override
public int resizeSecureContainer(String id, int sizeMb, String key) {
- validatePermission(android.Manifest.permission.ASEC_CREATE);
+ enforcePermission(android.Manifest.permission.ASEC_CREATE);
waitForReady();
warnOnNotMounted();
@@ -1878,7 +1377,7 @@
}
public int finalizeSecureContainer(String id) {
- validatePermission(android.Manifest.permission.ASEC_CREATE);
+ enforcePermission(android.Manifest.permission.ASEC_CREATE);
warnOnNotMounted();
int rc = StorageResultCode.OperationSucceeded;
@@ -1895,7 +1394,7 @@
}
public int fixPermissionsSecureContainer(String id, int gid, String filename) {
- validatePermission(android.Manifest.permission.ASEC_CREATE);
+ enforcePermission(android.Manifest.permission.ASEC_CREATE);
warnOnNotMounted();
int rc = StorageResultCode.OperationSucceeded;
@@ -1912,7 +1411,7 @@
}
public int destroySecureContainer(String id, boolean force) {
- validatePermission(android.Manifest.permission.ASEC_DESTROY);
+ enforcePermission(android.Manifest.permission.ASEC_DESTROY);
waitForReady();
warnOnNotMounted();
@@ -1952,7 +1451,7 @@
}
public int mountSecureContainer(String id, String key, int ownerUid, boolean readOnly) {
- validatePermission(android.Manifest.permission.ASEC_MOUNT_UNMOUNT);
+ enforcePermission(android.Manifest.permission.ASEC_MOUNT_UNMOUNT);
waitForReady();
warnOnNotMounted();
@@ -1982,7 +1481,7 @@
}
public int unmountSecureContainer(String id, boolean force) {
- validatePermission(android.Manifest.permission.ASEC_MOUNT_UNMOUNT);
+ enforcePermission(android.Manifest.permission.ASEC_MOUNT_UNMOUNT);
waitForReady();
warnOnNotMounted();
@@ -2025,7 +1524,7 @@
}
public boolean isSecureContainerMounted(String id) {
- validatePermission(android.Manifest.permission.ASEC_ACCESS);
+ enforcePermission(android.Manifest.permission.ASEC_ACCESS);
waitForReady();
warnOnNotMounted();
@@ -2035,7 +1534,7 @@
}
public int renameSecureContainer(String oldId, String newId) {
- validatePermission(android.Manifest.permission.ASEC_RENAME);
+ enforcePermission(android.Manifest.permission.ASEC_RENAME);
waitForReady();
warnOnNotMounted();
@@ -2060,7 +1559,7 @@
}
public String getSecureContainerPath(String id) {
- validatePermission(android.Manifest.permission.ASEC_ACCESS);
+ enforcePermission(android.Manifest.permission.ASEC_ACCESS);
waitForReady();
warnOnNotMounted();
@@ -2081,7 +1580,7 @@
}
public String getSecureContainerFilesystemPath(String id) {
- validatePermission(android.Manifest.permission.ASEC_ACCESS);
+ enforcePermission(android.Manifest.permission.ASEC_ACCESS);
waitForReady();
warnOnNotMounted();
@@ -2101,8 +1600,13 @@
}
}
+ @Override
public void finishMediaUpdate() {
- mHandler.sendEmptyMessage(H_UNMOUNT_PM_DONE);
+ if (mUnmountSignal != null) {
+ mUnmountSignal.countDown();
+ } else {
+ Slog.w(TAG, "Odd, nobody asked to unmount?");
+ }
}
private boolean isUidOwnerOfPackageOrSystem(String packageName, int callerUid) {
@@ -2477,109 +1981,84 @@
Context.APP_OPS_SERVICE);
appOps.checkPackage(Binder.getCallingUid(), callingPkg);
+ File appFile = null;
try {
- appPath = new File(appPath).getCanonicalPath();
+ appFile = new File(appPath).getCanonicalFile();
} catch (IOException e) {
Slog.e(TAG, "Failed to resolve " + appPath + ": " + e);
return -1;
}
- if (!appPath.endsWith("/")) {
- appPath = appPath + "/";
- }
-
// Try translating the app path into a vold path, but require that it
// belong to the calling package.
- String voldPath = maybeTranslatePathForVold(appPath,
- userEnv.buildExternalStorageAppDataDirs(callingPkg),
- userEnv.buildExternalStorageAppDataDirsForVold(callingPkg));
- if (voldPath != null) {
+ if (FileUtils.contains(userEnv.buildExternalStorageAppDataDirs(callingPkg), appFile) ||
+ FileUtils.contains(userEnv.buildExternalStorageAppObbDirs(callingPkg), appFile) ||
+ FileUtils.contains(userEnv.buildExternalStorageAppMediaDirs(callingPkg), appFile)) {
+ appPath = appFile.getAbsolutePath();
+ if (!appPath.endsWith("/")) {
+ appPath = appPath + "/";
+ }
+
try {
- mConnector.execute("volume", "mkdirs", voldPath);
+ mConnector.execute("volume", "mkdirs", appPath);
return 0;
} catch (NativeDaemonConnectorException e) {
return e.getCode();
}
}
- voldPath = maybeTranslatePathForVold(appPath,
- userEnv.buildExternalStorageAppObbDirs(callingPkg),
- userEnv.buildExternalStorageAppObbDirsForVold(callingPkg));
- if (voldPath != null) {
- try {
- mConnector.execute("volume", "mkdirs", voldPath);
- return 0;
- } catch (NativeDaemonConnectorException e) {
- return e.getCode();
- }
- }
-
- voldPath = maybeTranslatePathForVold(appPath,
- userEnv.buildExternalStorageAppMediaDirs(callingPkg),
- userEnv.buildExternalStorageAppMediaDirsForVold(callingPkg));
- if (voldPath != null) {
- try {
- mConnector.execute("volume", "mkdirs", voldPath);
- return 0;
- } catch (NativeDaemonConnectorException e) {
- return e.getCode();
- }
- }
-
- throw new SecurityException("Invalid mkdirs path: " + appPath);
- }
-
- /**
- * Translate the given path from an app-visible path to a vold-visible path,
- * but only if it's under the given whitelisted paths.
- *
- * @param path a canonicalized app-visible path.
- * @param appPaths list of app-visible paths that are allowed.
- * @param voldPaths list of vold-visible paths directly corresponding to the
- * allowed app-visible paths argument.
- * @return a vold-visible path representing the original path, or
- * {@code null} if the given path didn't have an app-to-vold
- * mapping.
- */
- @VisibleForTesting
- public static String maybeTranslatePathForVold(
- String path, File[] appPaths, File[] voldPaths) {
- if (appPaths.length != voldPaths.length) {
- throw new IllegalStateException("Paths must be 1:1 mapping");
- }
-
- for (int i = 0; i < appPaths.length; i++) {
- final String appPath = appPaths[i].getAbsolutePath() + "/";
- if (path.startsWith(appPath)) {
- path = new File(voldPaths[i], path.substring(appPath.length()))
- .getAbsolutePath();
- if (!path.endsWith("/")) {
- path = path + "/";
- }
- return path;
- }
- }
- return null;
+ throw new SecurityException("Invalid mkdirs path: " + appFile);
}
@Override
- public StorageVolume[] getVolumeList() {
- final int callingUserId = UserHandle.getCallingUserId();
- final boolean accessAll = (mContext.checkPermission(
- android.Manifest.permission.ACCESS_ALL_EXTERNAL_STORAGE,
- Binder.getCallingPid(), Binder.getCallingUid()) == PERMISSION_GRANTED);
+ public StorageVolume[] getVolumeList(int userId) {
+ if (UserHandle.getCallingUserId() != userId) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.ACCESS_ALL_EXTERNAL_STORAGE, "getVolumeList");
+ }
- synchronized (mVolumesLock) {
- final ArrayList<StorageVolume> filtered = Lists.newArrayList();
- for (StorageVolume volume : mVolumes) {
- final UserHandle owner = volume.getOwner();
- final boolean ownerMatch = owner == null || owner.getIdentifier() == callingUserId;
- if (accessAll || ownerMatch) {
- filtered.add(volume);
+ final ArrayList<StorageVolume> res = Lists.newArrayList();
+ boolean foundPrimary = false;
+ synchronized (mLock) {
+ for (Volume vol : mVolumes.values()) {
+ if (vol.isVisibleToUser(userId)) {
+ final StorageVolume userVol = vol.buildVolumeForUser(userId);
+ if (vol.isPrimary()) {
+ res.add(0, userVol);
+ foundPrimary = true;
+ } else {
+ res.add(userVol);
+ }
}
}
- return filtered.toArray(new StorageVolume[filtered.size()]);
}
+
+ if (!foundPrimary) {
+ Slog.w(TAG, "No primary storage defined yet; hacking together a stub");
+
+ final boolean primaryPhysical = SystemProperties.getBoolean(
+ PROP_PRIMARY_PHYSICAL, false);
+
+ final String id = "stub_primary";
+ final File path = Environment.getLegacyExternalStorageDirectory();
+ final int descriptionId = android.R.string.unknownName;
+ final boolean primary = true;
+ final boolean removable = primaryPhysical;
+ final boolean emulated = !primaryPhysical;
+ final long mtpReserveSize = 0L;
+ final boolean allowMassStorage = false;
+ final long maxFileSize = 0L;
+ final UserHandle owner = new UserHandle(userId);
+ final String uuid = null;
+ final String userLabel = null;
+ final String state = Environment.MEDIA_REMOVED;
+
+ res.add(0, new StorageVolume(id, MtpStorage.getStorageIdForIndex(0), path,
+ descriptionId, primary, removable, emulated, mtpReserveSize,
+ allowMassStorage, maxFileSize, owner, uuid, userLabel, state));
+ }
+
+ return res.toArray(new StorageVolume[res.size()]);
}
private void addObbStateLocked(ObbState obbState) throws RemoteException {
@@ -3073,21 +2552,13 @@
if (path.startsWith(obbPath)) {
path = path.substring(obbPath.length() + 1);
- if (forVold) {
- return new File(Environment.getEmulatedStorageObbSource(), path).getAbsolutePath();
- } else {
- final UserEnvironment ownerEnv = new UserEnvironment(UserHandle.USER_OWNER);
- return new File(ownerEnv.buildExternalStorageAndroidObbDirs()[0], path)
- .getAbsolutePath();
- }
+ final UserEnvironment ownerEnv = new UserEnvironment(UserHandle.USER_OWNER);
+ return new File(ownerEnv.buildExternalStorageAndroidObbDirs()[0], path)
+ .getAbsolutePath();
}
// Handle normal external storage paths
- if (forVold) {
- return new File(Environment.getEmulatedStorageSource(userId), path).getAbsolutePath();
- } else {
- return new File(userEnv.getExternalDirsForApp()[0], path).getAbsolutePath();
- }
+ return new File(userEnv.getExternalStorageDirectory(), path).getAbsolutePath();
}
@Override
@@ -3126,15 +2597,20 @@
pw.decreaseIndent();
}
- synchronized (mVolumesLock) {
+ synchronized (mLock) {
pw.println();
- pw.println("mVolumes:");
+ pw.println("Disks:");
pw.increaseIndent();
- for (StorageVolume volume : mVolumes) {
- pw.println(volume);
- pw.increaseIndent();
- pw.println("Current state: " + mVolumeStates.get(volume.getPath()));
- pw.decreaseIndent();
+ for (Disk disk : mDisks.values()) {
+ disk.dump(pw);
+ }
+ pw.decreaseIndent();
+
+ pw.println();
+ pw.println("Volumes:");
+ pw.increaseIndent();
+ for (Volume vol : mVolumes.values()) {
+ vol.dump(pw);
}
pw.decreaseIndent();
}
@@ -3153,6 +2629,7 @@
}
/** {@inheritDoc} */
+ @Override
public void monitor() {
if (mConnector != null) {
mConnector.monitor();
diff --git a/services/core/java/com/android/server/NativeDaemonConnector.java b/services/core/java/com/android/server/NativeDaemonConnector.java
index d2dfc7b..78c7f38 100644
--- a/services/core/java/com/android/server/NativeDaemonConnector.java
+++ b/services/core/java/com/android/server/NativeDaemonConnector.java
@@ -48,8 +48,6 @@
* {@code libsysutils} FrameworkListener protocol.
*/
final class NativeDaemonConnector implements Runnable, Handler.Callback, Watchdog.Monitor {
- private static final boolean LOGD = false;
-
private final static boolean VDBG = false;
private final String TAG;
@@ -58,6 +56,8 @@
private OutputStream mOutputStream;
private LocalLog mLocalLog;
+ private volatile boolean mDebug = false;
+
private final ResponseQueue mResponseQueue;
private final PowerManager.WakeLock mWakeLock;
@@ -99,6 +99,14 @@
mLocalLog = new LocalLog(maxLogSize);
}
+ /**
+ * Enable Set debugging mode, which causes messages to also be written to both
+ * {@link Slog} in addition to internal log.
+ */
+ public void setDebug(boolean debug) {
+ mDebug = debug;
+ }
+
@Override
public void run() {
mCallbackHandler = new Handler(mLooper, this);
@@ -513,7 +521,7 @@
}
private void log(String logstring) {
- if (LOGD) Slog.d(TAG, logstring);
+ if (mDebug) Slog.d(TAG, logstring);
mLocalLog.log(logstring);
}
diff --git a/services/core/java/com/android/server/PersistentDataBlockService.java b/services/core/java/com/android/server/PersistentDataBlockService.java
index 97d16c0..b36f515 100644
--- a/services/core/java/com/android/server/PersistentDataBlockService.java
+++ b/services/core/java/com/android/server/PersistentDataBlockService.java
@@ -70,6 +70,7 @@
// Limit to 100k as blocks larger than this might cause strain on Binder.
private static final int MAX_DATA_BLOCK_SIZE = 1024 * 100;
public static final int DIGEST_SIZE_BYTES = 32;
+ private static final String OEM_UNLOCK_PROP = "sys.oem_unlock_allowed";
private final Context mContext;
private final String mDataBlockFile;
@@ -108,11 +109,14 @@
}
private void formatIfOemUnlockEnabled() {
- if (doGetOemUnlockEnabled()) {
+ boolean enabled = doGetOemUnlockEnabled();
+ if (enabled) {
synchronized (mLock) {
formatPartitionLocked(true);
}
}
+
+ SystemProperties.set(OEM_UNLOCK_PROP, enabled ? "1" : "0");
}
private void enforceOemUnlockPermission() {
@@ -132,7 +136,6 @@
throw new SecurityException("Only the Owner is allowed to change OEM unlock state");
}
}
-
private int getTotalDataSizeLocked(DataInputStream inputStream) throws IOException {
// skip over checksum
inputStream.skipBytes(DIGEST_SIZE_BYTES);
@@ -290,6 +293,7 @@
Slog.e(TAG, "unable to access persistent partition", e);
return;
} finally {
+ SystemProperties.set(OEM_UNLOCK_PROP, enabled ? "1" : "0");
IoUtils.closeQuietly(outputStream);
}
}
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index a64e6c3..1b32f57 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -109,7 +109,7 @@
private static final int TIMEOUT_DELAY_MS = 1000 * 60;
private static final String DATABASE_NAME = "accounts.db";
- private static final int DATABASE_VERSION = 6;
+ private static final int DATABASE_VERSION = 7;
private final Context mContext;
@@ -131,6 +131,8 @@
private static final String ACCOUNTS_TYPE_COUNT = "count(type)";
private static final String ACCOUNTS_PASSWORD = "password";
private static final String ACCOUNTS_PREVIOUS_NAME = "previous_name";
+ private static final String ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS =
+ "last_password_entry_time_millis_epoch";
private static final String TABLE_AUTHTOKENS = "authtokens";
private static final String AUTHTOKENS_ID = "_id";
@@ -697,7 +699,8 @@
long identityToken = clearCallingIdentity();
try {
new Session(fromAccounts, response, account.type, false,
- false /* stripAuthTokenFromResult */) {
+ false /* stripAuthTokenFromResult */, account.name,
+ false /* authDetailsRequired */) {
@Override
protected String toDebugString(long now) {
return super.toDebugString(now) + ", getAccountCredentialsForClone"
@@ -725,12 +728,43 @@
}
}
+ @Override
+ public boolean accountAuthenticated(final Account account) {
+ if (account == null) {
+ throw new IllegalArgumentException("account is null");
+ }
+ checkAuthenticateAccountsPermission(account);
+
+ final UserAccounts accounts = getUserAccountsForCaller();
+ int userId = Binder.getCallingUserHandle().getIdentifier();
+ if (!canUserModifyAccounts(userId) || !canUserModifyAccountsForType(userId, account.type)) {
+ return false;
+ }
+ synchronized (accounts.cacheLock) {
+ final ContentValues values = new ContentValues();
+ values.put(ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS, System.currentTimeMillis());
+ final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
+ int i = db.update(
+ TABLE_ACCOUNTS,
+ values,
+ ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE + "=?",
+ new String[] {
+ account.name, account.type
+ });
+ if (i > 0) {
+ return true;
+ }
+ }
+ return false;
+ }
+
private void completeCloningAccount(IAccountManagerResponse response,
final Bundle accountCredentials, final Account account, final UserAccounts targetUser) {
long id = clearCallingIdentity();
try {
new Session(targetUser, response, account.type, false,
- false /* stripAuthTokenFromResult */) {
+ false /* stripAuthTokenFromResult */, account.name,
+ false /* authDetailsRequired */) {
@Override
protected String toDebugString(long now) {
return super.toDebugString(now) + ", getAccountCredentialsForClone"
@@ -795,6 +829,7 @@
values.put(ACCOUNTS_NAME, account.name);
values.put(ACCOUNTS_TYPE, account.type);
values.put(ACCOUNTS_PASSWORD, password);
+ values.put(ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS, System.currentTimeMillis());
long accountId = db.insert(TABLE_ACCOUNTS, ACCOUNTS_NAME, values);
if (accountId < 0) {
Log.w(TAG, "insertAccountIntoDatabase: " + account
@@ -885,7 +920,8 @@
public TestFeaturesSession(UserAccounts accounts, IAccountManagerResponse response,
Account account, String[] features) {
super(accounts, response, account.type, false /* expectActivityLaunch */,
- true /* stripAuthTokenFromResult */);
+ true /* stripAuthTokenFromResult */, account.name,
+ false /* authDetailsRequired */);
mFeatures = features;
mAccount = account;
}
@@ -1184,7 +1220,8 @@
public RemoveAccountSession(UserAccounts accounts, IAccountManagerResponse response,
Account account, boolean expectActivityLaunch) {
super(accounts, response, account.type, expectActivityLaunch,
- true /* stripAuthTokenFromResult */);
+ true /* stripAuthTokenFromResult */, account.name,
+ false /* authDetailsRequired */);
mAccount = account;
}
@@ -1419,6 +1456,13 @@
try {
final ContentValues values = new ContentValues();
values.put(ACCOUNTS_PASSWORD, password);
+ long time = 0;
+ // Only set current time, if it is a valid password. For clear password case, it
+ // should not be set.
+ if (password != null) {
+ time = System.currentTimeMillis();
+ }
+ values.put(ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS, time);
final long accountId = getAccountIdLocked(db, account);
if (accountId >= 0) {
final String[] argsAccountId = {String.valueOf(accountId)};
@@ -1547,8 +1591,9 @@
UserAccounts accounts = getUserAccounts(UserHandle.getUserId(callingUid));
long identityToken = clearCallingIdentity();
try {
- new Session(accounts, response, accountType, false,
- false /* stripAuthTokenFromResult */) {
+ new Session(accounts, response, accountType, false /* expectActivityLaunch */,
+ false /* stripAuthTokenFromResult */, null /* accountName */,
+ false /* authDetailsRequired */) {
@Override
protected String toDebugString(long now) {
return super.toDebugString(now) + ", getAuthTokenLabel"
@@ -1648,7 +1693,8 @@
}
new Session(accounts, response, account.type, expectActivityLaunch,
- false /* stripAuthTokenFromResult */) {
+ false /* stripAuthTokenFromResult */, account.name,
+ false /* authDetailsRequired */) {
@Override
protected String toDebugString(long now) {
if (loginOptions != null) loginOptions.keySet();
@@ -1842,7 +1888,8 @@
long identityToken = clearCallingIdentity();
try {
new Session(accounts, response, accountType, expectActivityLaunch,
- true /* stripAuthTokenFromResult */) {
+ true /* stripAuthTokenFromResult */, null /* accountName */,
+ false /* authDetailsRequired */) {
@Override
public void run() throws RemoteException {
mAuthenticator.addAccount(this, mAccountType, authTokenType, requiredFeatures,
@@ -1917,7 +1964,8 @@
long identityToken = clearCallingIdentity();
try {
new Session(accounts, response, accountType, expectActivityLaunch,
- true /* stripAuthTokenFromResult */) {
+ true /* stripAuthTokenFromResult */, null /* accountName */,
+ false /* authDetailsRequired */) {
@Override
public void run() throws RemoteException {
mAuthenticator.addAccount(this, mAccountType, authTokenType, requiredFeatures,
@@ -1973,7 +2021,8 @@
long identityToken = clearCallingIdentity();
try {
new Session(accounts, response, account.type, expectActivityLaunch,
- true /* stripAuthTokenFromResult */) {
+ true /* stripAuthTokenFromResult */, account.name,
+ true /* authDetailsRequired */) {
@Override
public void run() throws RemoteException {
mAuthenticator.confirmCredentials(this, account, options);
@@ -2009,7 +2058,8 @@
long identityToken = clearCallingIdentity();
try {
new Session(accounts, response, account.type, expectActivityLaunch,
- true /* stripAuthTokenFromResult */) {
+ true /* stripAuthTokenFromResult */, account.name,
+ false /* authDetailsRequired */) {
@Override
public void run() throws RemoteException {
mAuthenticator.updateCredentials(this, account, authTokenType, loginOptions);
@@ -2045,7 +2095,8 @@
long identityToken = clearCallingIdentity();
try {
new Session(accounts, response, accountType, expectActivityLaunch,
- true /* stripAuthTokenFromResult */) {
+ true /* stripAuthTokenFromResult */, null /* accountName */,
+ false /* authDetailsRequired */) {
@Override
public void run() throws RemoteException {
mAuthenticator.editProperties(this, mAccountType);
@@ -2071,7 +2122,8 @@
public GetAccountsByTypeAndFeatureSession(UserAccounts accounts,
IAccountManagerResponse response, String type, String[] features, int callingUid) {
super(accounts, response, type, false /* expectActivityLaunch */,
- true /* stripAuthTokenFromResult */);
+ true /* stripAuthTokenFromResult */, null /* accountName */,
+ false /* authDetailsRequired */);
mCallingUid = callingUid;
mFeatures = features;
}
@@ -2437,6 +2489,9 @@
final String mAccountType;
final boolean mExpectActivityLaunch;
final long mCreationTime;
+ final String mAccountName;
+ // Indicates if we need to add auth details(like last credential time)
+ final boolean mAuthDetailsRequired;
public int mNumResults = 0;
private int mNumRequestContinued = 0;
@@ -2448,7 +2503,8 @@
protected final UserAccounts mAccounts;
public Session(UserAccounts accounts, IAccountManagerResponse response, String accountType,
- boolean expectActivityLaunch, boolean stripAuthTokenFromResult) {
+ boolean expectActivityLaunch, boolean stripAuthTokenFromResult, String accountName,
+ boolean authDetailsRequired) {
super();
//if (response == null) throw new IllegalArgumentException("response is null");
if (accountType == null) throw new IllegalArgumentException("accountType is null");
@@ -2458,6 +2514,9 @@
mAccountType = accountType;
mExpectActivityLaunch = expectActivityLaunch;
mCreationTime = SystemClock.elapsedRealtime();
+ mAccountName = accountName;
+ mAuthDetailsRequired = authDetailsRequired;
+
synchronized (mSessions) {
mSessions.put(toString(), this);
}
@@ -2592,6 +2651,16 @@
public void onResult(Bundle result) {
mNumResults++;
Intent intent = null;
+ if (result != null && mAuthDetailsRequired) {
+ long lastAuthenticatedTime = DatabaseUtils.longForQuery(
+ mAccounts.openHelper.getReadableDatabase(),
+ "select " + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS + " from " +
+ TABLE_ACCOUNTS + " WHERE " + ACCOUNTS_NAME + "=? AND "
+ + ACCOUNTS_TYPE + "=?",
+ new String[]{mAccountName, mAccountType});
+ result.putLong(AccountManager.KEY_LAST_AUTHENTICATE_TIME_MILLIS_EPOCH,
+ lastAuthenticatedTime);
+ }
if (result != null
&& (intent = result.getParcelable(AccountManager.KEY_INTENT)) != null) {
/*
@@ -2798,6 +2867,7 @@
+ ACCOUNTS_TYPE + " TEXT NOT NULL, "
+ ACCOUNTS_PASSWORD + " TEXT, "
+ ACCOUNTS_PREVIOUS_NAME + " TEXT, "
+ + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS + " INTEGER DEFAULT 0, "
+ "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))");
db.execSQL("CREATE TABLE " + TABLE_AUTHTOKENS + " ( "
@@ -2833,6 +2903,11 @@
+ "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))");
}
+ private void addLastSuccessfullAuthenticatedTimeColumn(SQLiteDatabase db) {
+ db.execSQL("ALTER TABLE " + TABLE_ACCOUNTS + " ADD COLUMN "
+ + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS + " DEFAULT 0");
+ }
+
private void addOldAccountNameColumn(SQLiteDatabase db) {
db.execSQL("ALTER TABLE " + TABLE_ACCOUNTS + " ADD COLUMN " + ACCOUNTS_PREVIOUS_NAME);
}
@@ -2892,6 +2967,11 @@
oldVersion++;
}
+ if (oldVersion == 6) {
+ addLastSuccessfullAuthenticatedTimeColumn(db);
+ oldVersion++;
+ }
+
if (oldVersion != newVersion) {
Log.e(TAG, "failed to upgrade version " + oldVersion + " to version " + newVersion);
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 3b779b7..c318370 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -2188,7 +2188,7 @@
systemDir.mkdirs();
mBatteryStatsService = new BatteryStatsService(systemDir, mHandler);
mBatteryStatsService.getActiveStatistics().readLocked();
- mBatteryStatsService.getActiveStatistics().writeAsyncLocked();
+ mBatteryStatsService.scheduleWriteToDisk();
mOnBattery = DEBUG_POWER ? true
: mBatteryStatsService.getActiveStatistics().getIsOnBattery();
mBatteryStatsService.getActiveStatistics().setCallback(this);
@@ -2432,7 +2432,7 @@
if (mLastWriteTime < (now-BATTERY_STATS_TIME)) {
mLastWriteTime = now;
- mBatteryStatsService.getActiveStatistics().writeAsyncLocked();
+ mBatteryStatsService.scheduleWriteToDisk();
}
}
}
@@ -3041,24 +3041,13 @@
int uid = app.uid;
int[] gids = null;
- int mountExternal = Zygote.MOUNT_EXTERNAL_NONE;
+ int mountExternal = Zygote.MOUNT_EXTERNAL_DEFAULT;
if (!app.isolated) {
int[] permGids = null;
try {
checkTime(startTime, "startProcess: getting gids from package manager");
permGids = AppGlobals.getPackageManager().getPackageGids(app.info.packageName,
app.userId);
-
- if (Environment.isExternalStorageEmulated()) {
- checkTime(startTime, "startProcess: checking external storage perm");
- if (mContext.getPackageManager().checkPermission(
- android.Manifest.permission.ACCESS_ALL_EXTERNAL_STORAGE,
- app.info.packageName) == PERMISSION_GRANTED) {
- mountExternal = Zygote.MOUNT_EXTERNAL_MULTIUSER_ALL;
- } else {
- mountExternal = Zygote.MOUNT_EXTERNAL_MULTIUSER;
- }
- }
} catch (RemoteException e) {
Slog.w(TAG, "Unable to retrieve gids", e);
}
@@ -17505,8 +17494,12 @@
mFullPssPending = true;
mPendingPssProcesses.ensureCapacity(mLruProcesses.size());
mPendingPssProcesses.clear();
- for (int i=mLruProcesses.size()-1; i>=0; i--) {
+ for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
ProcessRecord app = mLruProcesses.get(i);
+ if (app.thread == null
+ || app.curProcState == ActivityManager.PROCESS_STATE_NONEXISTENT) {
+ continue;
+ }
if (memLowered || now > (app.lastStateTime+ProcessList.PSS_ALL_INTERVAL)) {
app.pssProcState = app.setProcState;
app.nextPssTime = ProcessList.computeNextPssTime(app.curProcState, true,
@@ -17822,8 +17815,8 @@
}
}
}
- if (app.setProcState < 0 || ProcessList.procStatesDifferForMem(app.curProcState,
- app.setProcState)) {
+ if (app.setProcState == ActivityManager.PROCESS_STATE_NONEXISTENT
+ || ProcessList.procStatesDifferForMem(app.curProcState, app.setProcState)) {
if (false && mTestPssMode && app.setProcState >= 0 && app.lastStateTime <= (now-200)) {
// Experimental code to more aggressively collect pss while
// running test... the problem is that this tends to collect
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 066ff37..8ba34e2 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -1018,6 +1018,10 @@
if (DEBUG_PAUSE) Slog.v(TAG, "App died during pause, not stopping: " + prev);
prev = null;
}
+ // It is possible the activity was freezing the screen before it was paused.
+ // In that case go ahead and remove the freeze this activity has on the screen
+ // since it is no longer visible.
+ prev.stopFreezingScreenLocked(true /*force*/);
mPausingActivity = null;
}
@@ -1246,6 +1250,11 @@
TAG, "ensureActivitiesVisible behind " + top
+ " configChanges=0x" + Integer.toHexString(configChanges));
+ if (DEBUG_STATES && starting != null && starting.task.stack == this) {
+ Slog.d(TAG, "ensureActivitiesVisibleLocked: starting=" + starting + " state="
+ + starting.state + " fullscreen=" + starting.fullscreen + " top=" + top
+ + " state=" + top.state + " fullscreen=" + top.fullscreen);
+ }
if (mTranslucentActivityWaiting != top) {
mUndrawnActivitiesBelowTopTranslucent.clear();
if (mTranslucentActivityWaiting != null) {
@@ -3915,9 +3924,8 @@
mStackSupervisor.removeChildActivityContainers(r);
try {
- if (DEBUG_SWITCH || DEBUG_STATES) Slog.i(TAG,
- (andResume ? "Relaunching to RESUMED " : "Relaunching to PAUSED ")
- + r);
+ if (DEBUG_SWITCH || DEBUG_STATES) Slog.i(TAG, "Moving to " +
+ (andResume ? "RESUMED" : "PAUSED") + " Relaunching " + r);
r.forceNewConfig = false;
r.app.thread.scheduleRelaunchActivity(r.appToken, results, newIntents, changes,
!andResume, new Configuration(mService.mConfiguration),
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 80101f5..c8db3be 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -16,20 +16,26 @@
package com.android.server.am;
+import android.bluetooth.BluetoothActivityEnergyInfo;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothHeadset;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
+import android.net.wifi.IWifiManager;
+import android.net.wifi.WifiActivityEnergyInfo;
import android.os.BatteryStats;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
import android.os.PowerManagerInternal;
import android.os.Process;
+import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserHandle;
@@ -38,10 +44,12 @@
import android.telephony.TelephonyManager;
import android.util.Slog;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.IBatteryStats;
import com.android.internal.os.BatteryStatsHelper;
import com.android.internal.os.BatteryStatsImpl;
import com.android.internal.os.PowerProfile;
+import com.android.server.FgThread;
import com.android.server.LocalServices;
import java.io.File;
@@ -59,15 +67,52 @@
static final String TAG = "BatteryStatsService";
static IBatteryStats sService;
-
final BatteryStatsImpl mStats;
+ final BatteryStatsHandler mHandler;
Context mContext;
private boolean mBluetoothPendingStats;
private BluetoothHeadset mBluetoothHeadset;
PowerManagerInternal mPowerManagerInternal;
+ class BatteryStatsHandler extends Handler implements BatteryStatsImpl.ExternalStatsSync {
+ public static final int MSG_SYNC_EXTERNAL_STATS = 1;
+ public static final int MSG_WRITE_TO_DISK = 2;
+
+ public BatteryStatsHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_SYNC_EXTERNAL_STATS:
+ updateExternalStats();
+ break;
+
+ case MSG_WRITE_TO_DISK:
+ updateExternalStats();
+ synchronized (mStats) {
+ mStats.writeAsyncLocked();
+ }
+ break;
+ }
+ }
+
+ @Override
+ public void scheduleSync() {
+ if (!hasMessages(MSG_SYNC_EXTERNAL_STATS)) {
+ sendEmptyMessage(MSG_SYNC_EXTERNAL_STATS);
+ }
+ }
+ }
+
BatteryStatsService(File systemDir, Handler handler) {
- mStats = new BatteryStatsImpl(systemDir, handler);
+ // Our handler here will be accessing the disk, use a different thread than
+ // what the ActivityManagerService gave us (no I/O on that one!).
+ mHandler = new BatteryStatsHandler(FgThread.getHandler().getLooper());
+
+ // BatteryStatsImpl expects the ActivityManagerService handler, so pass that one through.
+ mStats = new BatteryStatsImpl(systemDir, handler, mHandler);
}
public void publish(Context context) {
@@ -92,6 +137,8 @@
public void shutdown() {
Slog.w("BatteryStats", "Writing battery stats before shutdown...");
+
+ updateExternalStats();
synchronized (mStats) {
mStats.shutdownLocked();
}
@@ -122,6 +169,14 @@
return mStats;
}
+ /**
+ * Schedules a write to disk to occur. This will cause the BatteryStatsImpl
+ * object to update with the latest info, then write to disk.
+ */
+ public void scheduleWriteToDisk() {
+ mHandler.sendEmptyMessage(BatteryStatsHandler.MSG_WRITE_TO_DISK);
+ }
+
// These are for direct use by the activity manager...
void addIsolatedUid(int isolatedUid, int appUid) {
@@ -174,7 +229,10 @@
//Slog.i("foo", "SENDING BATTERY INFO:");
//mStats.dumpLocked(new LogPrinter(Log.INFO, "foo", Log.LOG_ID_SYSTEM));
Parcel out = Parcel.obtain();
- mStats.writeToParcel(out, 0);
+ updateExternalStats();
+ synchronized (mStats) {
+ mStats.writeToParcel(out, 0);
+ }
byte[] data = out.marshall();
out.recycle();
return data;
@@ -186,7 +244,10 @@
//Slog.i("foo", "SENDING BATTERY INFO:");
//mStats.dumpLocked(new LogPrinter(Log.INFO, "foo", Log.LOG_ID_SYSTEM));
Parcel out = Parcel.obtain();
- mStats.writeToParcel(out, 0);
+ updateExternalStats();
+ synchronized (mStats) {
+ mStats.writeToParcel(out, 0);
+ }
byte[] data = out.marshall();
out.recycle();
try {
@@ -663,6 +724,7 @@
}
}
+ @Override
public void noteWifiMulticastDisabledFromSource(WorkSource ws) {
enforceCallingPermission();
synchronized (mStats) {
@@ -671,10 +733,10 @@
}
@Override
- public void noteNetworkInterfaceType(String iface, int type) {
+ public void noteNetworkInterfaceType(String iface, int networkType) {
enforceCallingPermission();
synchronized (mStats) {
- mStats.noteNetworkInterfaceTypeLocked(iface, type);
+ mStats.noteNetworkInterfaceTypeLocked(iface, networkType);
}
}
@@ -715,7 +777,22 @@
public void setBatteryState(int status, int health, int plugType, int level,
int temp, int volt) {
enforceCallingPermission();
- mStats.setBatteryState(status, health, plugType, level, temp, volt);
+ synchronized (mStats) {
+ final boolean onBattery = plugType == BatteryStatsImpl.BATTERY_PLUGGED_NONE;
+ if (mStats.isOnBattery() == onBattery) {
+ // The battery state has not changed, so we don't need to sync external
+ // stats immediately.
+ mStats.setBatteryStateLocked(status, health, plugType, level, temp, volt);
+ return;
+ }
+ }
+
+ // Sync external stats first as the battery has changed states. If we don't sync
+ // immediately here, we may not collect the relevant data later.
+ updateExternalStats();
+ synchronized (mStats) {
+ mStats.setBatteryStateLocked(status, health, plugType, level, temp, volt);
+ }
}
public long getAwakeTimeBattery() {
@@ -817,6 +894,7 @@
return i;
}
+
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
@@ -865,7 +943,9 @@
pw.println("Battery stats reset.");
noOutput = true;
}
+ updateExternalStats();
} else if ("--write".equals(arg)) {
+ updateExternalStats();
synchronized (mStats) {
mStats.writeSyncLocked();
pw.println("Battery stats written.");
@@ -934,6 +1014,10 @@
flags &= ~BatteryStats.DUMP_INCLUDE_HISTORY;
}
}
+
+ // Fetch data from external sources and update the BatteryStatsImpl object with them.
+ updateExternalStats();
+
if (useCheckinFormat) {
List<ApplicationInfo> apps = mContext.getPackageManager().getInstalledApplications(0);
if (isRealCheckin) {
@@ -948,7 +1032,7 @@
in.unmarshall(raw, 0, raw.length);
in.setDataPosition(0);
BatteryStatsImpl checkinStats = new BatteryStatsImpl(
- null, mStats.mHandler);
+ null, mStats.mHandler, null);
checkinStats.readSummaryFromParcel(in);
in.recycle();
checkinStats.dumpCheckinLocked(mContext, pw, apps, flags,
@@ -978,4 +1062,85 @@
}
}
}
+
+ // Objects for extracting data from external sources.
+ private final Object mExternalStatsLock = new Object();
+
+ @GuardedBy("mExternalStatsLock")
+ private IWifiManager mWifiManager;
+
+ // WiFi keeps an accumulated total of stats, unlike Bluetooth.
+ // Keep the last WiFi stats so we can compute a delta.
+ @GuardedBy("mExternalStatsLock")
+ private WifiActivityEnergyInfo mLastInfo = new WifiActivityEnergyInfo(0, 0, 0, 0, 0, 0);
+
+ @GuardedBy("mExternalStatsLock")
+ private WifiActivityEnergyInfo pullWifiEnergyInfoLocked() {
+ if (mWifiManager == null) {
+ mWifiManager = IWifiManager.Stub.asInterface(
+ ServiceManager.getService(Context.WIFI_SERVICE));
+ if (mWifiManager == null) {
+ return null;
+ }
+ }
+
+ try {
+ // We read the data even if we are not on battery. This is so that we keep the
+ // correct delta from when we should start reading (aka when we are on battery).
+ WifiActivityEnergyInfo info = mWifiManager.reportActivityInfo();
+ if (info != null && info.isValid()) {
+ // We will modify the last info object to be the delta, and store the new
+ // WifiActivityEnergyInfo object as our last one.
+ final WifiActivityEnergyInfo result = mLastInfo;
+ result.mTimestamp = info.getTimeStamp();
+ result.mStackState = info.getStackState();
+ result.mControllerTxTimeMs =
+ info.getControllerTxTimeMillis()- mLastInfo.mControllerTxTimeMs;
+ result.mControllerRxTimeMs =
+ info.getControllerRxTimeMillis() - mLastInfo.mControllerRxTimeMs;
+ result.mControllerIdleTimeMs =
+ info.getControllerIdleTimeMillis() - mLastInfo.mControllerIdleTimeMs;
+ result.mControllerEnergyUsed =
+ info.getControllerEnergyUsed() - mLastInfo.mControllerEnergyUsed;
+ mLastInfo = info;
+ return result;
+ }
+ } catch (RemoteException e) {
+ // Nothing to report, WiFi is dead.
+ }
+ return null;
+ }
+
+ @GuardedBy("mExternalStatsLock")
+ private BluetoothActivityEnergyInfo pullBluetoothEnergyInfoLocked() {
+ BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+ if (adapter != null) {
+ BluetoothActivityEnergyInfo info = adapter.getControllerActivityEnergyInfo(
+ BluetoothAdapter.ACTIVITY_ENERGY_INFO_REFRESHED);
+ if (info != null && info.isValid()) {
+ return info;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Fetches data from external sources (WiFi controller, bluetooth chipset) and updates
+ * batterystats with that information.
+ *
+ * We first grab a lock specific to this method, then once all the data has been collected,
+ * we grab the mStats lock and update the data.
+ */
+ void updateExternalStats() {
+ synchronized (mExternalStatsLock) {
+ final WifiActivityEnergyInfo wifiEnergyInfo = pullWifiEnergyInfoLocked();
+ final BluetoothActivityEnergyInfo bluetoothEnergyInfo = pullBluetoothEnergyInfoLocked();
+ synchronized (mStats) {
+ mStats.updateKernelWakelocksLocked();
+ mStats.updateMobileRadioStateLocked(SystemClock.elapsedRealtime());
+ mStats.updateWifiStateLocked(wifiEnergyInfo);
+ mStats.updateBluetoothStateLocked(bluetoothEnergyInfo);
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 7cf3b51..7c921ac 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -16,6 +16,7 @@
package com.android.server.am;
+import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -89,10 +90,10 @@
int curSchedGroup; // Currently desired scheduling class
int setSchedGroup; // Last set to background scheduling class
int trimMemoryLevel; // Last selected memory trimming level
- int curProcState = -1; // Currently computed process state: ActivityManager.PROCESS_STATE_*
- int repProcState = -1; // Last reported process state
- int setProcState = -1; // Last set process state in process tracker
- int pssProcState = -1; // The proc state we are currently requesting pss for
+ int curProcState = PROCESS_STATE_NONEXISTENT; // Currently computed process state
+ int repProcState = PROCESS_STATE_NONEXISTENT; // Last reported process state
+ int setProcState = PROCESS_STATE_NONEXISTENT; // Last set process state in process tracker
+ int pssProcState = PROCESS_STATE_NONEXISTENT; // Currently requesting pss for
boolean serviceb; // Process currently is on the service B list
boolean serviceHighRam; // We are forcing to service B list due to its RAM use
boolean setIsForeground; // Running foreground UI when last set?
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index d8d361b..3b34541 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -16,6 +16,8 @@
package com.android.server.am;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
+import static android.content.Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS;
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.ActivityRecord.HOME_ACTIVITY_TYPE;
@@ -346,8 +348,8 @@
if ((info.flags & ActivityInfo.FLAG_AUTO_REMOVE_FROM_RECENTS) != 0) {
// If the activity itself has requested auto-remove, then just always do it.
autoRemoveRecents = true;
- } else if ((intentFlags & (Intent.FLAG_ACTIVITY_NEW_DOCUMENT
- | Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS)) == Intent.FLAG_ACTIVITY_NEW_DOCUMENT) {
+ } else if ((intentFlags & (FLAG_ACTIVITY_NEW_DOCUMENT | FLAG_ACTIVITY_RETAIN_IN_RECENTS))
+ == FLAG_ACTIVITY_NEW_DOCUMENT) {
// If the caller has not asked for the document to be retained, then we may
// want to turn on auto-remove, depending on whether the target has set its
// own document launch mode.
@@ -879,7 +881,8 @@
for (int activityNdx = 0; activityNdx < numActivities; ++activityNdx) {
final ActivityRecord r = activities.get(activityNdx);
if (r.info.persistableMode == ActivityInfo.PERSIST_ROOT_ONLY || !r.isPersistable() ||
- ((r.intent.getFlags() & Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0) &&
+ ((r.intent.getFlags() & FLAG_ACTIVITY_NEW_DOCUMENT
+ | FLAG_ACTIVITY_RETAIN_IN_RECENTS) == FLAG_ACTIVITY_NEW_DOCUMENT) &&
activityNdx > 0) {
// Stop at first non-persistable or first break in task (CLEAR_WHEN_TASK_RESET).
break;
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 65b2ae2..1eddc8e 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -234,9 +234,6 @@
private final Object mSoundEffectsLock = new Object();
private static final int NUM_SOUNDPOOL_CHANNELS = 4;
- // Maximum volume adjust steps allowed in a single batch call.
- private static final int MAX_BATCH_VOLUME_ADJUST_STEPS = 4;
-
/* Sound effect file names */
private static final String SOUND_EFFECTS_PATH = "/media/audio/ui/";
private static final List<String> SOUND_EFFECT_FILES = new ArrayList<String>();
@@ -988,6 +985,7 @@
} else {
streamType = getActiveStreamType(suggestedStreamType);
}
+ ensureValidStreamType(streamType);
final int resolvedStream = mStreamVolumeAlias[streamType];
// Play sounds on STREAM_RING only.
@@ -1421,6 +1419,8 @@
private void sendVolumeUpdate(int streamType, int oldIndex, int index, int flags) {
if (!isPlatformVoice() && (streamType == AudioSystem.STREAM_RING)) {
streamType = AudioSystem.STREAM_NOTIFICATION;
+ } else {
+ streamType = mStreamVolumeAlias[streamType];
}
if (streamType == AudioSystem.STREAM_MUSIC) {
@@ -3131,12 +3131,6 @@
}
}
- private void ensureValidSteps(int steps) {
- if (Math.abs(steps) > MAX_BATCH_VOLUME_ADJUST_STEPS) {
- throw new IllegalArgumentException("Bad volume adjust steps " + steps);
- }
- }
-
private void ensureValidStreamType(int streamType) {
if (streamType < 0 || streamType >= mStreamStates.length) {
throw new IllegalArgumentException("Bad stream type " + streamType);
@@ -3305,7 +3299,7 @@
}
private int getDeviceForStream(int stream) {
- int device = AudioSystem.getDevicesForStream(stream);
+ int device = getDevicesForStream(stream);
if ((device & (device - 1)) != 0) {
// Multiple device selection is either:
// - speaker + one other device: give priority to speaker in this case.
@@ -3328,6 +3322,27 @@
return device;
}
+ private int getDevicesForStream(int stream) {
+ return getDevicesForStream(stream, true /*checkOthers*/);
+ }
+
+ private int getDevicesForStream(int stream, boolean checkOthers) {
+ ensureValidStreamType(stream);
+ synchronized (VolumeStreamState.class) {
+ return mStreamStates[stream].observeDevicesForStream_syncVSS(checkOthers);
+ }
+ }
+
+ private void observeDevicesForStreams(int skipStream) {
+ synchronized (VolumeStreamState.class) {
+ for (int stream = 0; stream < mStreamStates.length; stream++) {
+ if (stream != skipStream) {
+ mStreamStates[stream].observeDevicesForStream_syncVSS(false /*checkOthers*/);
+ }
+ }
+ }
+ }
+
/*
* A class just for packaging up a set of connection parameters.
*/
@@ -3406,9 +3421,11 @@
private boolean mIsMuted;
private String mVolumeIndexSettingName;
+ private int mObservedDevices;
private final SparseIntArray mIndexMap = new SparseIntArray(8);
private final Intent mVolumeChanged;
+ private final Intent mStreamDevicesChanged;
private VolumeStreamState(String settingName, int streamType) {
@@ -3422,6 +3439,29 @@
readSettings();
mVolumeChanged = new Intent(AudioManager.VOLUME_CHANGED_ACTION);
mVolumeChanged.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, mStreamType);
+ mStreamDevicesChanged = new Intent(AudioManager.STREAM_DEVICES_CHANGED_ACTION);
+ mStreamDevicesChanged.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, mStreamType);
+ }
+
+ public int observeDevicesForStream_syncVSS(boolean checkOthers) {
+ final int devices = AudioSystem.getDevicesForStream(mStreamType);
+ if (devices == mObservedDevices) {
+ return devices;
+ }
+ final int prevDevices = mObservedDevices;
+ mObservedDevices = devices;
+ if (checkOthers) {
+ // one stream's devices have changed, check the others
+ observeDevicesForStreams(mStreamType);
+ }
+ // log base stream changes to the event log
+ if (mStreamVolumeAlias[mStreamType] == mStreamType) {
+ EventLogTags.writeStreamDevicesChanged(mStreamType, prevDevices, devices);
+ }
+ sendBroadcastToAll(mStreamDevicesChanged
+ .putExtra(AudioManager.EXTRA_PREV_VOLUME_STREAM_DEVICES, prevDevices)
+ .putExtra(AudioManager.EXTRA_VOLUME_STREAM_DEVICES, devices));
+ return devices;
}
public String getSettingNameForDevice(int device) {
@@ -3716,7 +3756,7 @@
}
pw.println();
pw.print(" Devices: ");
- final int devices = AudioSystem.getDevicesForStream(mStreamType);
+ final int devices = getDevicesForStream(mStreamType);
int device, i = 0, n = 0;
// iterate all devices from 1 to DEVICE_OUT_DEFAULT exclusive
// (the default device is not returned by getDevicesForStream)
@@ -4250,6 +4290,7 @@
}
}
mRoutesObservers.finishBroadcast();
+ observeDevicesForStreams(-1);
break;
}
@@ -5348,7 +5389,7 @@
on ? AudioSystem.FORCE_HDMI_SYSTEM_AUDIO_ENFORCED :
AudioSystem.FORCE_NONE);
}
- device = AudioSystem.getDevicesForStream(AudioSystem.STREAM_MUSIC);
+ device = getDevicesForStream(AudioSystem.STREAM_MUSIC);
}
}
}
diff --git a/services/core/java/com/android/server/hdmi/RequestArcAction.java b/services/core/java/com/android/server/hdmi/RequestArcAction.java
index cbbf91b..75a79cb 100644
--- a/services/core/java/com/android/server/hdmi/RequestArcAction.java
+++ b/services/core/java/com/android/server/hdmi/RequestArcAction.java
@@ -58,14 +58,16 @@
// received without <Request ARC Initiation> or <Request ARC Termination>.
case Constants.MESSAGE_FEATURE_ABORT:
int originalOpcode = cmd.getParams()[0] & 0xFF;
- if (originalOpcode == Constants.MESSAGE_REQUEST_ARC_INITIATION
- || originalOpcode == Constants.MESSAGE_REQUEST_ARC_TERMINATION) {
+ if (originalOpcode == Constants.MESSAGE_REQUEST_ARC_TERMINATION) {
disableArcTransmission();
finish();
return true;
- } else {
- return false;
+ } else if (originalOpcode == Constants.MESSAGE_REQUEST_ARC_INITIATION) {
+ tv().setArcStatus(false);
+ finish();
+ return true;
}
+ return false;
}
return false;
}
@@ -82,7 +84,7 @@
if (mState != state || state != STATE_WATING_FOR_REQUEST_ARC_REQUEST_RESPONSE) {
return;
}
- HdmiLogger.debug("[T]RequestArcAction.");
+ HdmiLogger.debug("[T] RequestArcAction.");
disableArcTransmission();
finish();
}
diff --git a/services/core/java/com/android/server/hdmi/RequestArcInitiationAction.java b/services/core/java/com/android/server/hdmi/RequestArcInitiationAction.java
index d9e1f24..f69f975 100644
--- a/services/core/java/com/android/server/hdmi/RequestArcInitiationAction.java
+++ b/services/core/java/com/android/server/hdmi/RequestArcInitiationAction.java
@@ -35,6 +35,7 @@
@Override
boolean start() {
+ // Seq #38
mState = STATE_WATING_FOR_REQUEST_ARC_REQUEST_RESPONSE;
addTimer(mState, HdmiConfig.TIMEOUT_MS);
@@ -44,9 +45,8 @@
@Override
public void onSendCompleted(int error) {
if (error != Constants.SEND_RESULT_SUCCESS) {
- // If failed to send <Request ARC Initiation>, start "Disabled"
- // ARC transmission action.
- disableArcTransmission();
+ // Turn off ARC status if <Request ARC Initiation> fails.
+ tv().setArcStatus(false);
finish();
}
}
diff --git a/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java b/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java
index bffa854..d200d35 100644
--- a/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java
+++ b/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java
@@ -52,6 +52,7 @@
@Override
boolean start() {
+ // Seq #37.
if (mEnabled) {
// Enable ARC status immediately after sending <Report Arc Initiated>.
// If AVR responds with <Feature Abort>, disable ARC status again.
diff --git a/services/core/java/com/android/server/net/NetworkStatsCollection.java b/services/core/java/com/android/server/net/NetworkStatsCollection.java
index 01c6662..a415a84 100644
--- a/services/core/java/com/android/server/net/NetworkStatsCollection.java
+++ b/services/core/java/com/android/server/net/NetworkStatsCollection.java
@@ -182,8 +182,7 @@
for (int i = 0; i < mStats.size(); i++) {
final Key key = mStats.keyAt(i);
- final boolean setMatches = set == SET_ALL || key.set == set;
- if (key.uid == uid && setMatches && key.tag == tag
+ if (key.uid == uid && NetworkStats.setMatches(set, key.set) && key.tag == tag
&& templateMatches(template, key.ident)) {
final NetworkStatsHistory value = mStats.valueAt(i);
combined.recordHistory(value, start, end);
@@ -209,7 +208,8 @@
final int callerUid = Binder.getCallingUid();
for (int i = 0; i < mStats.size(); i++) {
final Key key = mStats.keyAt(i);
- if (templateMatches(template, key.ident) && isAccessibleToUser(key.uid, callerUid)) {
+ if (templateMatches(template, key.ident) && isAccessibleToUser(key.uid, callerUid)
+ && key.set < NetworkStats.SET_DEBUG_START) {
final NetworkStatsHistory value = mStats.valueAt(i);
historyEntry = value.getValues(start, end, now, historyEntry);
@@ -542,6 +542,7 @@
final NetworkStatsHistory value = mStats.valueAt(i);
if (!templateMatches(groupTemplate, key.ident)) continue;
+ if (key.set >= NetworkStats.SET_DEBUG_START) continue;
final Key groupKey = new Key(null, key.uid, key.set, key.tag);
NetworkStatsHistory groupHistory = grouped.get(groupKey);
diff --git a/services/core/java/com/android/server/pm/IntentFilterVerificationKey.java b/services/core/java/com/android/server/pm/IntentFilterVerificationKey.java
new file mode 100644
index 0000000..399b03c
--- /dev/null
+++ b/services/core/java/com/android/server/pm/IntentFilterVerificationKey.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import java.util.Arrays;
+
+/**
+ * This is the key for the map of {@link android.content.pm.IntentFilterVerificationInfo}s
+ * maintained by the {@link com.android.server.pm.PackageManagerService}
+ */
+class IntentFilterVerificationKey {
+ public String domains;
+ public String packageName;
+ public String className;
+
+ public IntentFilterVerificationKey(String[] domains, String packageName, String className) {
+ StringBuilder sb = new StringBuilder();
+ for (String host : domains) {
+ sb.append(host);
+ }
+ this.domains = sb.toString();
+ this.packageName = packageName;
+ this.className = className;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ IntentFilterVerificationKey that = (IntentFilterVerificationKey) o;
+
+ if (domains != null ? !domains.equals(that.domains) : that.domains != null) return false;
+ if (className != null ? !className.equals(that.className) : that.className != null)
+ return false;
+ if (packageName != null ? !packageName.equals(that.packageName) : that.packageName != null)
+ return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = domains != null ? domains.hashCode() : 0;
+ result = 31 * result + (packageName != null ? packageName.hashCode() : 0);
+ result = 31 * result + (className != null ? className.hashCode() : 0);
+ return result;
+ }
+}
diff --git a/services/core/java/com/android/server/pm/IntentFilterVerificationResponse.java b/services/core/java/com/android/server/pm/IntentFilterVerificationResponse.java
new file mode 100644
index 0000000..ead399b
--- /dev/null
+++ b/services/core/java/com/android/server/pm/IntentFilterVerificationResponse.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+
+import java.util.List;
+
+/* package private */ class IntentFilterVerificationResponse {
+ public final int callerUid;
+ public final int code;
+ public final List<String> failedDomains;
+
+ public IntentFilterVerificationResponse(int callerUid, int code, List<String> failedDomains) {
+ this.callerUid = callerUid;
+ this.code = code;
+ this.failedDomains = failedDomains;
+ }
+
+ public String getFailedDomainsString() {
+ StringBuilder sb = new StringBuilder();
+ for (String domain : failedDomains) {
+ if (sb.length() > 0) {
+ sb.append(" ");
+ }
+ sb.append(domain);
+ }
+ return sb.toString();
+ }
+}
diff --git a/services/core/java/com/android/server/pm/IntentFilterVerificationState.java b/services/core/java/com/android/server/pm/IntentFilterVerificationState.java
new file mode 100644
index 0000000..c09d6ae
--- /dev/null
+++ b/services/core/java/com/android/server/pm/IntentFilterVerificationState.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import android.content.pm.PackageManager;
+import android.content.pm.PackageParser;
+import android.util.ArraySet;
+import android.util.Log;
+
+import java.util.ArrayList;
+
+public class IntentFilterVerificationState {
+ static final String TAG = IntentFilterVerificationState.class.getName();
+
+ public final static int STATE_UNDEFINED = 0;
+ public final static int STATE_VERIFICATION_PENDING = 1;
+ public final static int STATE_VERIFICATION_SUCCESS = 2;
+ public final static int STATE_VERIFICATION_FAILURE = 3;
+
+ private int mRequiredVerifierUid = 0;
+
+ private int mState;
+
+ private ArrayList<PackageParser.ActivityIntentInfo> mFilters = new ArrayList<>();
+ private ArraySet<String> mHosts = new ArraySet<>();
+ private int mUserId;
+
+ private String mPackageName;
+ private boolean mVerificationComplete;
+
+ public IntentFilterVerificationState(int verifierUid, int userId, String packageName) {
+ mRequiredVerifierUid = verifierUid;
+ mUserId = userId;
+ mPackageName = packageName;
+ mState = STATE_UNDEFINED;
+ mVerificationComplete = false;
+ }
+
+ public void setState(int state) {
+ if (state > STATE_VERIFICATION_FAILURE || state < STATE_UNDEFINED) {
+ mState = STATE_UNDEFINED;
+ } else {
+ mState = state;
+ }
+ }
+
+ public int getState() {
+ return mState;
+ }
+
+ public void setPendingState() {
+ setState(STATE_VERIFICATION_PENDING);
+ }
+
+ public ArrayList<PackageParser.ActivityIntentInfo> getFilters() {
+ return mFilters;
+ }
+
+ public boolean isVerificationComplete() {
+ return mVerificationComplete;
+ }
+
+ public boolean isVerified() {
+ if (mVerificationComplete) {
+ return (mState == STATE_VERIFICATION_SUCCESS);
+ }
+ return false;
+ }
+
+ public int getUserId() {
+ return mUserId;
+ }
+
+ public String getPackageName() {
+ return mPackageName;
+ }
+
+ public String getHostsString() {
+ StringBuilder sb = new StringBuilder();
+ final int count = mHosts.size();
+ for (int i=0; i<count; i++) {
+ if (i > 0) {
+ sb.append(" ");
+ }
+ sb.append(mHosts.valueAt(i));
+ }
+ return sb.toString();
+ }
+
+ public boolean setVerifierResponse(int callerUid, int code) {
+ if (mRequiredVerifierUid == callerUid) {
+ int state = STATE_UNDEFINED;
+ if (code == PackageManager.INTENT_FILTER_VERIFICATION_SUCCESS) {
+ state = STATE_VERIFICATION_SUCCESS;
+ } else if (code == PackageManager.INTENT_FILTER_VERIFICATION_FAILURE) {
+ state = STATE_VERIFICATION_FAILURE;
+ }
+ mVerificationComplete = true;
+ setState(state);
+ return true;
+ }
+ Log.d(TAG, "Cannot set verifier response with callerUid:" + callerUid + " and code:" +
+ code + " as required verifierUid is:" + mRequiredVerifierUid);
+ return false;
+ }
+
+ public void addFilter(PackageParser.ActivityIntentInfo filter) {
+ mFilters.add(filter);
+ mHosts.addAll(filter.getHostsList());
+ }
+}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 4dfb161..b9dfc21 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -44,6 +44,10 @@
import static android.content.pm.PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE;
import static android.content.pm.PackageManager.INSTALL_FORWARD_LOCK;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
+import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER;
+import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
+import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK;
+import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
import static android.content.pm.PackageParser.isApkFile;
import static android.os.Process.PACKAGE_INFO_GID;
import static android.os.Process.SYSTEM_UID;
@@ -59,6 +63,8 @@
import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets;
import static com.android.server.pm.InstructionSets.getPreferredInstructionSet;
+import android.Manifest;
+import android.content.pm.IntentFilterVerificationInfo;
import android.util.ArrayMap;
import com.android.internal.R;
@@ -248,8 +254,8 @@
private static final boolean DEBUG_DEXOPT = false;
private static final boolean DEBUG_ABI_SELECTION = false;
- private static final boolean RUNTIME_PERMISSIONS_ENABLED =
- SystemProperties.getInt("ro.runtime.premissions.enabled", 0) == 1;
+ static final boolean RUNTIME_PERMISSIONS_ENABLED =
+ SystemProperties.getInt("ro.runtime.permissions.enabled", 0) == 1;
private static final int RADIO_UID = Process.PHONE_UID;
private static final int LOG_UID = Process.LOG_UID;
@@ -275,6 +281,7 @@
static final int SCAN_TRUSTED_OVERLAY = 1<<9;
static final int SCAN_DELETE_DATA_ON_FAILURES = 1<<10;
static final int SCAN_REPLACING = 1<<11;
+ static final int SCAN_REQUIRE_KNOWN = 1<<12;
static final int REMOVE_CHATTY = 1<<16;
@@ -503,6 +510,231 @@
boolean mResolverReplaced = false;
+ private final ComponentName mIntentFilterVerifierComponent;
+ private int mIntentFilterVerificationToken = 0;
+
+ final SparseArray<IntentFilterVerificationState> mIntentFilterVerificationStates
+ = new SparseArray<IntentFilterVerificationState>();
+
+ private interface IntentFilterVerifier<T extends IntentFilter> {
+ boolean addOneIntentFilterVerification(int verifierId, int userId, int verificationId,
+ T filter, String packageName);
+ void startVerifications(int userId);
+ void receiveVerificationResponse(int verificationId);
+ }
+
+ private class IntentVerifierProxy implements IntentFilterVerifier<ActivityIntentInfo> {
+ private Context mContext;
+ private ComponentName mIntentFilterVerifierComponent;
+ private ArrayList<Integer> mCurrentIntentFilterVerifications = new ArrayList<Integer>();
+
+ public IntentVerifierProxy(Context context, ComponentName verifierComponent) {
+ mContext = context;
+ mIntentFilterVerifierComponent = verifierComponent;
+ }
+
+ private String getDefaultScheme() {
+ // TODO: replace SCHEME_HTTP with SCHEME_HTTPS
+ return IntentFilter.SCHEME_HTTP;
+ }
+
+ @Override
+ public void startVerifications(int userId) {
+ // Launch verifications requests
+ int count = mCurrentIntentFilterVerifications.size();
+ for (int n=0; n<count; n++) {
+ int verificationId = mCurrentIntentFilterVerifications.get(n);
+ final IntentFilterVerificationState ivs =
+ mIntentFilterVerificationStates.get(verificationId);
+
+ String packageName = ivs.getPackageName();
+ boolean modified = false;
+
+ ArrayList<PackageParser.ActivityIntentInfo> filters = ivs.getFilters();
+ final int filterCount = filters.size();
+ for (int m=0; m<filterCount; m++) {
+ PackageParser.ActivityIntentInfo filter = filters.get(m);
+ synchronized (mPackages) {
+ modified = mSettings.createIntentFilterVerificationIfNeededLPw(
+ packageName, filter.getHosts());
+ }
+ }
+ synchronized (mPackages) {
+ if (modified) {
+ scheduleWriteSettingsLocked();
+ }
+ }
+ sendVerificationRequest(userId, verificationId, ivs);
+ }
+ mCurrentIntentFilterVerifications.clear();
+ }
+
+ private void sendVerificationRequest(int userId, int verificationId,
+ IntentFilterVerificationState ivs) {
+
+ Intent verificationIntent = new Intent(Intent.ACTION_INTENT_FILTER_NEEDS_VERIFICATION);
+ verificationIntent.putExtra(
+ PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_ID,
+ verificationId);
+ verificationIntent.putExtra(
+ PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_URI_SCHEME,
+ getDefaultScheme());
+ verificationIntent.putExtra(
+ PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_HOSTS,
+ ivs.getHostsString());
+ verificationIntent.putExtra(
+ PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_PACKAGE_NAME,
+ ivs.getPackageName());
+ verificationIntent.setComponent(mIntentFilterVerifierComponent);
+ verificationIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+
+ UserHandle user = new UserHandle(userId);
+ mContext.sendBroadcastAsUser(verificationIntent, user);
+ Slog.d(TAG, "Sending IntenFilter verification broadcast");
+ }
+
+ public void receiveVerificationResponse(int verificationId) {
+ IntentFilterVerificationState ivs = mIntentFilterVerificationStates.get(verificationId);
+
+ final boolean verified = ivs.isVerified();
+
+ ArrayList<PackageParser.ActivityIntentInfo> filters = ivs.getFilters();
+ final int count = filters.size();
+ for (int n=0; n<count; n++) {
+ PackageParser.ActivityIntentInfo filter = filters.get(n);
+ filter.setVerified(verified);
+
+ Slog.d(TAG, "IntentFilter " + filter.toString() + " verified with result:"
+ + verified + " and hosts:" + ivs.getHostsString());
+ }
+
+ mIntentFilterVerificationStates.remove(verificationId);
+
+ final String packageName = ivs.getPackageName();
+ IntentFilterVerificationInfo ivi = null;
+
+ synchronized (mPackages) {
+ ivi = mSettings.getIntentFilterVerificationLPr(packageName);
+ }
+ if (ivi == null) {
+ Slog.w(TAG, "IntentFilterVerificationInfo not found for verificationId:"
+ + verificationId + " packageName:" + packageName);
+ return;
+ }
+ Slog.d(TAG, "Updating IntentFilterVerificationInfo for verificationId: "
+ + verificationId);
+
+ synchronized (mPackages) {
+ if (verified) {
+ ivi.setStatus(INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS);
+ } else {
+ ivi.setStatus(INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK);
+ }
+ scheduleWriteSettingsLocked();
+
+ final int userId = ivs.getUserId();
+ if (userId != UserHandle.USER_ALL) {
+ final int userStatus =
+ mSettings.getIntentFilterVerificationStatusLPr(packageName, userId);
+
+ int updatedStatus = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
+ boolean needUpdate = false;
+
+ // We cannot override the STATUS_ALWAYS / STATUS_NEVER states if they have
+ // already been set by the User thru the Disambiguation dialog
+ switch (userStatus) {
+ case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED:
+ if (verified) {
+ updatedStatus = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
+ } else {
+ updatedStatus = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK;
+ }
+ needUpdate = true;
+ break;
+
+ case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK:
+ if (verified) {
+ updatedStatus = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
+ needUpdate = true;
+ }
+ break;
+
+ default:
+ // Nothing to do
+ }
+
+ if (needUpdate) {
+ mSettings.updateIntentFilterVerificationStatusLPw(
+ packageName, updatedStatus, userId);
+ scheduleWritePackageRestrictionsLocked(userId);
+ }
+ }
+ }
+ }
+
+ @Override
+ public boolean addOneIntentFilterVerification(int verifierId, int userId, int verificationId,
+ ActivityIntentInfo filter, String packageName) {
+ if (!(filter.hasDataScheme(IntentFilter.SCHEME_HTTP) ||
+ filter.hasDataScheme(IntentFilter.SCHEME_HTTPS))) {
+ Slog.d(TAG, "IntentFilter does not contain HTTP nor HTTPS data scheme");
+ return false;
+ }
+ IntentFilterVerificationState ivs = mIntentFilterVerificationStates.get(verificationId);
+ if (ivs == null) {
+ ivs = createDomainVerificationState(verifierId, userId, verificationId,
+ packageName);
+ }
+ ArrayList<String> hosts = filter.getHostsList();
+ if (!hasValidHosts(hosts)) {
+ return false;
+ }
+ ivs.addFilter(filter);
+ return true;
+ }
+
+ private IntentFilterVerificationState createDomainVerificationState(int verifierId,
+ int userId, int verificationId, String packageName) {
+ IntentFilterVerificationState ivs = new IntentFilterVerificationState(
+ verifierId, userId, packageName);
+ ivs.setPendingState();
+ synchronized (mPackages) {
+ mIntentFilterVerificationStates.append(verificationId, ivs);
+ mCurrentIntentFilterVerifications.add(verificationId);
+ }
+ return ivs;
+ }
+
+ private boolean hasValidHosts(ArrayList<String> hosts) {
+ if (hosts.size() == 0) {
+ Slog.d(TAG, "IntentFilter does not contain any data hosts");
+ return false;
+ }
+ String hostEndBase = null;
+ for (String host : hosts) {
+ String[] hostParts = host.split("\\.");
+ // Should be at minimum a host like "example.com"
+ if (hostParts.length < 2) {
+ Slog.d(TAG, "IntentFilter does not contain a valid data host name: " + host);
+ return false;
+ }
+ // Verify that we have the same ending domain
+ int length = hostParts.length;
+ String hostEnd = hostParts[length - 1] + hostParts[length - 2];
+ if (hostEndBase == null) {
+ hostEndBase = hostEnd;
+ }
+ if (!hostEnd.equalsIgnoreCase(hostEndBase)) {
+ Slog.d(TAG, "IntentFilter does not contain the same data domains");
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+
+ private IntentFilterVerifier mIntentFilterVerifier;
+
// Set of pending broadcasts for aggregating enable/disable of components.
static class PendingPackageBroadcasts {
// for each user id, a map of <package name -> components within that package>
@@ -589,6 +821,8 @@
static final int WRITE_PACKAGE_RESTRICTIONS = 14;
static final int PACKAGE_VERIFIED = 15;
static final int CHECK_PENDING_VERIFICATION = 16;
+ static final int START_INTENT_FILTER_VERIFICATIONS = 17;
+ static final int INTENT_FILTER_VERIFIED = 18;
static final int WRITE_SETTINGS_DELAY = 10*1000; // 10 seconds
@@ -1239,6 +1473,54 @@
break;
}
+ case START_INTENT_FILTER_VERIFICATIONS: {
+ int userId = msg.arg1;
+ int verifierUid = msg.arg2;
+ PackageParser.Package pkg = (PackageParser.Package)msg.obj;
+
+ verifyIntentFiltersIfNeeded(userId, verifierUid, pkg);
+ break;
+ }
+ case INTENT_FILTER_VERIFIED: {
+ final int verificationId = msg.arg1;
+
+ final IntentFilterVerificationState state = mIntentFilterVerificationStates.get(
+ verificationId);
+ if (state == null) {
+ Slog.w(TAG, "Invalid IntentFilter verification token "
+ + verificationId + " received");
+ break;
+ }
+
+ final int userId = state.getUserId();
+
+ Slog.d(TAG, "Processing IntentFilter verification with token:"
+ + verificationId + " and userId:" + userId);
+
+ final IntentFilterVerificationResponse response =
+ (IntentFilterVerificationResponse) msg.obj;
+
+ state.setVerifierResponse(response.callerUid, response.code);
+
+ Slog.d(TAG, "IntentFilter verification with token:" + verificationId
+ + " and userId:" + userId
+ + " is settings verifier response with response code:"
+ + response.code);
+
+ if (response.code == PackageManager.INTENT_FILTER_VERIFICATION_FAILURE) {
+ Slog.d(TAG, "Domains failing verification: "
+ + response.getFailedDomainsString());
+ }
+
+ if (state.isVerificationComplete()) {
+ mIntentFilterVerifier.receiveVerificationResponse(verificationId);
+ } else {
+ Slog.d(TAG, "IntentFilter verification with token:" + verificationId
+ + " was not said to be complete");
+ }
+
+ break;
+ }
}
}
}
@@ -1348,7 +1630,7 @@
mOnlyCore = onlyCore;
mLazyDexOpt = "eng".equals(SystemProperties.get("ro.build.type"));
mMetrics = new DisplayMetrics();
- mSettings = new Settings(mContext, mPackages);
+ mSettings = new Settings(mPackages);
mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.phone", RADIO_UID,
@@ -1699,10 +1981,10 @@
if (!mOnlyCore) {
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START,
SystemClock.uptimeMillis());
- scanDirLI(mAppInstallDir, 0, scanFlags, 0);
+ scanDirLI(mAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0);
scanDirLI(mDrmAppPrivateInstallDir, PackageParser.PARSE_FORWARD_LOCK,
- scanFlags, 0);
+ scanFlags | SCAN_REQUIRE_KNOWN, 0);
/**
* Remove disable package settings for any updated system
@@ -1810,7 +2092,14 @@
+ mSettings.mInternalSdkPlatform + " to " + mSdkVersion
+ "; regranting permissions for internal storage");
mSettings.mInternalSdkPlatform = mSdkVersion;
-
+
+ // For now runtime permissions are toggled via a system property.
+ if (!RUNTIME_PERMISSIONS_ENABLED) {
+ // Remove the runtime permissions state if the feature
+ // was disabled by flipping the system property.
+ mSettings.deleteRuntimePermissionsFiles();
+ }
+
updatePermissionsLPw(null, null, UPDATE_PERMISSIONS_ALL
| (regrantPermissions
? (UPDATE_PERMISSIONS_REPLACE_PKG|UPDATE_PERMISSIONS_REPLACE_ALL)
@@ -1842,13 +2131,17 @@
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_READY,
SystemClock.uptimeMillis());
-
mRequiredVerifierPackage = getRequiredVerifierLPr();
+
+ mInstallerService = new PackageInstallerService(context, this, mAppInstallDir);
+
+ mIntentFilterVerifierComponent = getIntentFilterVerifierComponentNameLPr();
+ mIntentFilterVerifier = new IntentVerifierProxy(mContext,
+ mIntentFilterVerifierComponent);
+
} // synchronized (mPackages)
} // synchronized (mInstallLock)
- mInstallerService = new PackageInstallerService(context, this, mAppInstallDir);
-
// Now after opening every single application zip, make sure they
// are all flushed. Not really needed, but keeps things nice and
// tidy.
@@ -1902,6 +2195,46 @@
return requiredVerifier;
}
+ private ComponentName getIntentFilterVerifierComponentNameLPr() {
+ final Intent verification = new Intent(Intent.ACTION_INTENT_FILTER_NEEDS_VERIFICATION);
+ final List<ResolveInfo> receivers = queryIntentReceivers(verification, PACKAGE_MIME_TYPE,
+ PackageManager.GET_DISABLED_COMPONENTS, 0 /* userId */);
+
+ ComponentName verifierComponentName = null;
+
+ int priority = -1000;
+ final int N = receivers.size();
+ for (int i = 0; i < N; i++) {
+ final ResolveInfo info = receivers.get(i);
+
+ if (info.activityInfo == null) {
+ continue;
+ }
+
+ final String packageName = info.activityInfo.packageName;
+
+ final PackageSetting ps = mSettings.mPackages.get(packageName);
+ if (ps == null) {
+ continue;
+ }
+
+ if (checkPermission(android.Manifest.permission.INTENT_FILTER_VERIFICATION_AGENT,
+ packageName, UserHandle.USER_OWNER) != PackageManager.PERMISSION_GRANTED) {
+ continue;
+ }
+
+ // Select the IntentFilterVerifier with the highest priority
+ if (priority < info.priority) {
+ priority = info.priority;
+ verifierComponentName = new ComponentName(packageName, info.activityInfo.name);
+ Slog.d(TAG, "Selecting IntentFilterVerifier: " + verifierComponentName +
+ " with priority: " + info.priority);
+ }
+ }
+
+ return verifierComponentName;
+ }
+
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
throws RemoteException {
@@ -2695,6 +3028,10 @@
@Override
public boolean grantPermission(String packageName, String name, int userId) {
+ if (!RUNTIME_PERMISSIONS_ENABLED) {
+ return false;
+ }
+
if (!sUserManager.exists(userId)) {
return false;
}
@@ -2706,6 +3043,9 @@
enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false,
"grantPermission");
+ boolean gidsChanged = false;
+ final SettingBase sb;
+
synchronized (mPackages) {
final PackageParser.Package pkg = mPackages.get(packageName);
if (pkg == null) {
@@ -2719,7 +3059,7 @@
enforceDeclaredAsUsedAndRuntimePermission(pkg, bp);
- final SettingBase sb = (SettingBase) pkg.mExtras;
+ sb = (SettingBase) pkg.mExtras;
if (sb == null) {
throw new IllegalArgumentException("Unknown package: " + packageName);
}
@@ -2733,19 +3073,27 @@
}
case PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED: {
- killSettingPackagesForUser(sb, userId, KILL_APP_REASON_GIDS_CHANGED);
+ gidsChanged = true;
} break;
}
// Not critical if that is lost - app has to request again.
mSettings.writeRuntimePermissionsForUserLPr(userId, false);
-
- return true;
}
+
+ if (gidsChanged) {
+ killSettingPackagesForUser(sb, userId, KILL_APP_REASON_GIDS_CHANGED);
+ }
+
+ return true;
}
@Override
public boolean revokePermission(String packageName, String name, int userId) {
+ if (!RUNTIME_PERMISSIONS_ENABLED) {
+ return false;
+ }
+
if (!sUserManager.exists(userId)) {
return false;
}
@@ -2757,6 +3105,8 @@
enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false,
"revokePermission");
+ final SettingBase sb;
+
synchronized (mPackages) {
final PackageParser.Package pkg = mPackages.get(packageName);
if (pkg == null) {
@@ -2770,7 +3120,7 @@
enforceDeclaredAsUsedAndRuntimePermission(pkg, bp);
- final SettingBase sb = (SettingBase) pkg.mExtras;
+ sb = (SettingBase) pkg.mExtras;
if (sb == null) {
throw new IllegalArgumentException("Unknown package: " + packageName);
}
@@ -2782,13 +3132,13 @@
return false;
}
- killSettingPackagesForUser(sb, userId, KILL_APP_REASON_PERMISSIONS_REVOKED);
-
// Critical, after this call all should never have the permission.
mSettings.writeRuntimePermissionsForUserLPr(userId, true);
-
- return true;
}
+
+ killSettingPackagesForUser(sb, userId, KILL_APP_REASON_PERMISSIONS_REVOKED);
+
+ return true;
}
@Override
@@ -3508,14 +3858,20 @@
resolveInfo = queryCrossProfileIntents(
matchingFilters, intent, resolvedType, flags, userId);
- // Check for results in the current profile.
+ // Check for results in the current profile. Adding GET_RESOLVED_FILTER flags
+ // as we need it later
List<ResolveInfo> result = mActivities.queryIntent(
intent, resolvedType, flags, userId);
if (resolveInfo != null) {
result.add(resolveInfo);
Collections.sort(result, mResolvePrioritySorter);
}
- return filterIfNotPrimaryUser(result, userId);
+ result = filterIfNotPrimaryUser(result, userId);
+ if (result.size() > 1) {
+ return filterCandidatesWithDomainPreferedActivitiesLPw(result);
+ }
+
+ return result;
}
final PackageParser.Package pkg = mPackages.get(pkgName);
if (pkg != null) {
@@ -3546,6 +3902,49 @@
return resolveInfos;
}
+ private List<ResolveInfo> filterCandidatesWithDomainPreferedActivitiesLPw(
+ List<ResolveInfo> candidates) {
+ if (DEBUG_PREFERRED) {
+ Slog.v("TAG", "Filtering results with prefered activities. Candidates count: " +
+ candidates.size());
+ }
+ final int userId = UserHandle.getCallingUserId();
+ ArrayList<ResolveInfo> result = new ArrayList<ResolveInfo>(candidates);
+ synchronized (mPackages) {
+ final int count = result.size();
+ for (int n = count-1; n >= 0; n--) {
+ ResolveInfo info = result.get(n);
+ if (!info.filterNeedsVerification) {
+ continue;
+ }
+ String packageName = info.activityInfo.packageName;
+ PackageSetting ps = mSettings.mPackages.get(packageName);
+ if (ps != null) {
+ // Try to get the status from User settings first
+ int status = ps.getDomainVerificationStatusForUser(userId);
+ // if none available, get the master status
+ if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED) {
+ if (ps.getIntentFilterVerificationInfo() != null) {
+ status = ps.getIntentFilterVerificationInfo().getStatus();
+ }
+ }
+ if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS) {
+ result.clear();
+ result.add(info);
+ // We break the for loop as we are good to go
+ break;
+ } else if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER) {
+ result.remove(n);
+ }
+ }
+ }
+ }
+ if (DEBUG_PREFERRED) {
+ Slog.v("TAG", "Filtered results with prefered activities. New candidates count: " +
+ result.size());
+ }
+ return result;
+ }
private ResolveInfo querySkipCurrentProfileIntents(
List<CrossProfileIntentFilter> matchingFilters, Intent intent, String resolvedType,
@@ -5259,6 +5658,28 @@
+ " already installed. Skipping duplicate.");
}
+ // If we're only installing presumed-existing packages, require that the
+ // scanned APK is both already known and at the path previously established
+ // for it. Previously unknown packages we pick up normally, but if we have an
+ // a priori expectation about this package's install presence, enforce it.
+ if ((scanFlags & SCAN_REQUIRE_KNOWN) != 0) {
+ PackageSetting known = mSettings.peekPackageLPr(pkg.packageName);
+ if (known != null) {
+ if (DEBUG_PACKAGE_SCANNING) {
+ Log.d(TAG, "Examining " + pkg.codePath
+ + " and requiring known paths " + known.codePathString
+ + " & " + known.resourcePathString);
+ }
+ if (!pkg.applicationInfo.getCodePath().equals(known.codePathString)
+ || !pkg.applicationInfo.getResourcePath().equals(known.resourcePathString)) {
+ throw new PackageManagerException(INSTALL_FAILED_PACKAGE_CHANGED,
+ "Application package " + pkg.packageName
+ + " found at " + pkg.applicationInfo.getCodePath()
+ + " but expected at " + known.codePathString + "; ignoring.");
+ }
+ }
+ }
+
// Initialize package source and resource directories
File destCodeFile = new File(pkg.applicationInfo.getCodePath());
File destResourceFile = new File(pkg.applicationInfo.getResourcePath());
@@ -6970,11 +7391,12 @@
final int[] currentUserIds = UserManagerService.getInstance().getUserIds();
int[] upgradeUserIds = PermissionsState.USERS_NONE;
+ int[] changedRuntimePermissionUserIds = PermissionsState.USERS_NONE;
- boolean changedPermission = false;
+ boolean changedInstallPermission = false;
if (replace) {
- ps.permissionsFixed = false;
+ ps.installPermissionsFixed = false;
origPermissions = new PermissionsState(permissionsState);
permissionsState.reset();
}
@@ -7025,7 +7447,7 @@
<= Build.VERSION_CODES.LOLLIPOP_MR1) {
// For legacy apps dangerous permissions are install time ones.
grant = GRANT_INSTALL;
- } else if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
+ } else if (ps.isSystem()) {
final int[] updatedUserIds = ps.getPermissionsUpdatedForUserIds();
if (origPermissions.hasInstallPermission(bp.name)) {
// If a system app had an install permission, then the app was
@@ -7069,7 +7491,7 @@
}
if (grant != GRANT_DENIED) {
- if (!isSystemApp(ps) && ps.permissionsFixed) {
+ if (!isSystemApp(ps) && ps.installPermissionsFixed) {
// If this is an existing, non-system package, then
// we can't add any new permissions to it.
if (!allowedSig && !origPermissions.hasInstallPermission(perm)) {
@@ -7087,7 +7509,7 @@
// Grant an install permission.
if (permissionsState.grantInstallPermission(bp) !=
PermissionsState.PERMISSION_OPERATION_FAILURE) {
- changedPermission = true;
+ changedInstallPermission = true;
}
} break;
@@ -7095,9 +7517,11 @@
// Grant previously granted runtime permissions.
for (int userId : UserManagerService.getInstance().getUserIds()) {
if (origPermissions.hasRuntimePermission(bp.name, userId)) {
- if (permissionsState.grantRuntimePermission(bp, userId) !=
+ if (permissionsState.grantRuntimePermission(bp, userId) ==
PermissionsState.PERMISSION_OPERATION_FAILURE) {
- changedPermission = true;
+ // If we cannot put the permission as it was, we have to write.
+ changedRuntimePermissionUserIds = ArrayUtils.appendInt(
+ changedRuntimePermissionUserIds, userId);
}
}
}
@@ -7109,7 +7533,9 @@
for (int userId : upgradeUserIds) {
if (permissionsState.grantRuntimePermission(bp, userId) !=
PermissionsState.PERMISSION_OPERATION_FAILURE) {
- changedPermission = true;
+ // If we granted the permission, we have to write.
+ changedRuntimePermissionUserIds = ArrayUtils.appendInt(
+ changedRuntimePermissionUserIds, userId);
}
}
} break;
@@ -7126,7 +7552,7 @@
} else {
if (permissionsState.revokeInstallPermission(bp) !=
PermissionsState.PERMISSION_OPERATION_FAILURE) {
- changedPermission = true;
+ changedInstallPermission = true;
Slog.i(TAG, "Un-granting permission " + perm
+ " from package " + pkg.packageName
+ " (protectionLevel=" + bp.protectionLevel
@@ -7146,15 +7572,22 @@
}
}
- if ((changedPermission || replace) && !ps.permissionsFixed &&
+ if ((changedInstallPermission || replace) && !ps.installPermissionsFixed &&
!isSystemApp(ps) || isUpdatedSystemApp(ps)){
// This is the first that we have heard about this package, so the
// permissions we have now selected are fixed until explicitly
// changed.
- ps.permissionsFixed = true;
+ ps.installPermissionsFixed = true;
}
ps.setPermissionsUpdatedForUserIds(currentUserIds);
+
+ // Persist the runtime permissions state for users with changes.
+ if (RUNTIME_PERMISSIONS_ENABLED) {
+ for (int userId : changedRuntimePermissionUserIds) {
+ mSettings.writeRuntimePermissionsForUserLPr(userId, true);
+ }
+ }
}
private boolean isNewPlatformPermissionForPackage(String perm, PackageParser.Package pkg) {
@@ -7386,6 +7819,9 @@
if ((mFlags&PackageManager.GET_RESOLVED_FILTER) != 0) {
res.filter = info;
}
+ if (info != null) {
+ res.filterNeedsVerification = info.needsVerification();
+ }
res.priority = info.getPriority();
res.preferredOrder = activity.owner.mPreferredOrder;
//System.out.println("Result: " + res.activityInfo.className +
@@ -7608,8 +8044,6 @@
}
res.priority = info.getPriority();
res.preferredOrder = service.owner.mPreferredOrder;
- //System.out.println("Result: " + res.activityInfo.className +
- // " = " + res.priority);
res.match = match;
res.isDefault = info.hasDefault;
res.labelRes = info.labelRes;
@@ -8486,6 +8920,45 @@
android.provider.Settings.Global.PACKAGE_VERIFIER_ENABLE, 1) == 1;
}
+ @Override
+ public void verifyIntentFilter(int id, int verificationCode, List<String> outFailedDomains)
+ throws RemoteException {
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.INTENT_FILTER_VERIFICATION_AGENT,
+ "Only intentfilter verification agents can verify applications");
+
+ final Message msg = mHandler.obtainMessage(INTENT_FILTER_VERIFIED);
+ final IntentFilterVerificationResponse response = new IntentFilterVerificationResponse(
+ Binder.getCallingUid(), verificationCode, outFailedDomains);
+ msg.arg1 = id;
+ msg.obj = response;
+ mHandler.sendMessage(msg);
+ }
+
+ @Override
+ public int getIntentVerificationStatus(String packageName, int userId) {
+ synchronized (mPackages) {
+ return mSettings.getIntentFilterVerificationStatusLPr(packageName, userId);
+ }
+ }
+
+ @Override
+ public boolean updateIntentVerificationStatus(String packageName, int status, int userId) {
+ boolean result = false;
+ synchronized (mPackages) {
+ result = mSettings.updateIntentFilterVerificationStatusLPw(packageName, status, userId);
+ }
+ scheduleWritePackageRestrictionsLocked(userId);
+ return result;
+ }
+
+ @Override
+ public List<IntentFilterVerificationInfo> getIntentFilterVerifications(String packageName) {
+ synchronized (mPackages) {
+ return mSettings.getIntentFilterVerificationsLPr(packageName);
+ }
+ }
+
/**
* Get the "allow unknown sources" setting.
*
@@ -10650,6 +11123,8 @@
return;
}
+ startIntentFilterVerifications(args.user.getIdentifier(), pkg);
+
if (replace) {
replacePackageLI(pkg, parseFlags, scanFlags | SCAN_REPLACING, args.user,
installerPackageName, res);
@@ -10665,6 +11140,86 @@
}
}
+ private void startIntentFilterVerifications(int userId, PackageParser.Package pkg) {
+ if (mIntentFilterVerifierComponent == null) {
+ Slog.d(TAG, "No IntentFilter verification will not be done as "
+ + "there is no IntentFilterVerifier available!");
+ return;
+ }
+
+ final int verifierUid = getPackageUid(
+ mIntentFilterVerifierComponent.getPackageName(),
+ (userId == UserHandle.USER_ALL) ? UserHandle.USER_OWNER : userId);
+
+ mHandler.removeMessages(START_INTENT_FILTER_VERIFICATIONS);
+ final Message msg = mHandler.obtainMessage(START_INTENT_FILTER_VERIFICATIONS);
+ msg.obj = pkg;
+ msg.arg1 = userId;
+ msg.arg2 = verifierUid;
+
+ mHandler.sendMessage(msg);
+ }
+
+ private void verifyIntentFiltersIfNeeded(int userId, int verifierUid,
+ PackageParser.Package pkg) {
+ int size = pkg.activities.size();
+ if (size == 0) {
+ Slog.d(TAG, "No activity, so no need to verify any IntentFilter!");
+ return;
+ }
+
+ Slog.d(TAG, "Checking for userId:" + userId + " if any IntentFilter from the " + size
+ + " Activities needs verification ...");
+
+ final int verificationId = mIntentFilterVerificationToken++;
+ int count = 0;
+ synchronized (mPackages) {
+ for (PackageParser.Activity a : pkg.activities) {
+ for (ActivityIntentInfo filter : a.intents) {
+ boolean needFilterVerification = filter.needsVerification() &&
+ !filter.isVerified();
+ if (needFilterVerification && needNetworkVerificationLPr(filter)) {
+ Slog.d(TAG, "Verification needed for IntentFilter:" + filter.toString());
+ mIntentFilterVerifier.addOneIntentFilterVerification(
+ verifierUid, userId, verificationId, filter, pkg.packageName);
+ count++;
+ } else {
+ Slog.d(TAG, "No verification needed for IntentFilter:" + filter.toString());
+ }
+ }
+ }
+ }
+
+ if (count > 0) {
+ mIntentFilterVerifier.startVerifications(userId);
+ Slog.d(TAG, "Started " + count + " IntentFilter verification"
+ + (count > 1 ? "s" : "") + " for userId:" + userId + "!");
+ } else {
+ Slog.d(TAG, "No need to start any IntentFilter verification!");
+ }
+ }
+
+ private boolean needNetworkVerificationLPr(ActivityIntentInfo filter) {
+ final ComponentName cn = filter.activity.getComponentName();
+ final String packageName = cn.getPackageName();
+
+ IntentFilterVerificationInfo ivi = mSettings.getIntentFilterVerificationLPr(
+ packageName);
+ if (ivi == null) {
+ return true;
+ }
+ int status = ivi.getStatus();
+ switch (status) {
+ case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED:
+ case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK:
+ return true;
+
+ default:
+ // Nothing to do
+ return false;
+ }
+ }
+
private static boolean isMultiArch(PackageSetting ps) {
return (ps.pkgFlags & ApplicationInfo.FLAG_MULTIARCH) != 0;
}
@@ -10992,19 +11547,23 @@
for (int userId : UserManagerService.getInstance().getUserIds()) {
final int userIdToKill = mSettings.updateSharedUserPermsLPw(deletedPs,
userId);
- if (userIdToKill == userId) {
+ if (userIdToKill == UserHandle.USER_ALL
+ || userIdToKill >= UserHandle.USER_OWNER) {
// If gids changed for this user, kill all affected packages.
- killSettingPackagesForUser(deletedPs, userIdToKill,
- KILL_APP_REASON_GIDS_CHANGED);
- } else if (userIdToKill == UserHandle.USER_ALL) {
- // If gids changed for all users, kill them all - done.
- killSettingPackagesForUser(deletedPs, userIdToKill,
- KILL_APP_REASON_GIDS_CHANGED);
- break;
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ // This has to happen with no lock held.
+ killSettingPackagesForUser(deletedPs, userIdToKill,
+ KILL_APP_REASON_GIDS_CHANGED);
+ }
+ });
+ break;
}
}
}
clearPackagePreferredActivitiesLPw(deletedPs.name, UserHandle.USER_ALL);
+ clearIntentFilterVerificationsLPw(deletedPs.name, UserHandle.USER_ALL);
}
// make sure to preserve per-user disabled state if this removal was just
// a downgrade of a system app to the factory package
@@ -11233,8 +11792,8 @@
true, //notLaunched
false, //hidden
null, null, null,
- false // blockUninstall
- );
+ false, // blockUninstall
+ INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED);
if (!isSystemApp(ps)) {
if (ps.isAnyInstalled(sUserManager.getUserIds())) {
// Other user still have this package installed, so all
@@ -11857,6 +12416,19 @@
return changed;
}
+ /** This method takes a specific user id as well as UserHandle.USER_ALL. */
+ void clearIntentFilterVerificationsLPw(String packageName, int userId) {
+ if (userId == UserHandle.USER_ALL) {
+ mSettings.removeIntentFilterVerificationLPw(packageName, sUserManager.getUserIds());
+ for (int oneUserId : sUserManager.getUserIds()) {
+ scheduleWritePackageRestrictionsLocked(oneUserId);
+ }
+ } else {
+ mSettings.removeIntentFilterVerificationLPw(packageName, userId);
+ scheduleWritePackageRestrictionsLocked(userId);
+ }
+ }
+
@Override
public void resetPreferredActivities(int userId) {
/* TODO: Actually use userId. Why is it being passed in? */
@@ -12367,6 +12939,8 @@
public static final int DUMP_KEYSETS = 1 << 11;
public static final int DUMP_VERSION = 1 << 12;
public static final int DUMP_INSTALLS = 1 << 13;
+ public static final int DUMP_INTENT_FILTER_VERIFIERS = 1 << 14;
+ public static final int DUMP_DOMAIN_PREFERRED = 1 << 15;
public static final int OPTION_SHOW_FILTERS = 1 << 0;
@@ -12472,6 +13046,8 @@
pw.println(" write: write current settings now");
pw.println(" <package.name>: info about given package");
pw.println(" installs: details about install sessions");
+ pw.println(" d[omain-preferred-apps]: print domains preferred apps");
+ pw.println(" i[ntent-filter-verifiers]|ifv: print intent filter verifier info");
return;
} else if ("--checkin".equals(opt)) {
checkin = true;
@@ -12508,6 +13084,8 @@
fullPreferred = true;
opti++;
}
+ } else if ("d".equals(cmd) || "domain-preferred-apps".equals(cmd)) {
+ dumpState.setDump(DumpState.DUMP_DOMAIN_PREFERRED);
} else if ("p".equals(cmd) || "packages".equals(cmd)) {
dumpState.setDump(DumpState.DUMP_PACKAGES);
} else if ("s".equals(cmd) || "shared-users".equals(cmd)) {
@@ -12518,6 +13096,9 @@
dumpState.setDump(DumpState.DUMP_MESSAGES);
} else if ("v".equals(cmd) || "verifiers".equals(cmd)) {
dumpState.setDump(DumpState.DUMP_VERIFIERS);
+ } else if ("i".equals(cmd) || "ifv".equals(cmd)
+ || "intent-filter-verifiers".equals(cmd)) {
+ dumpState.setDump(DumpState.DUMP_INTENT_FILTER_VERIFIERS);
} else if ("version".equals(cmd)) {
dumpState.setDump(DumpState.DUMP_VERSION);
} else if ("k".equals(cmd) || "keysets".equals(cmd)) {
@@ -12573,6 +13154,29 @@
}
}
+ if (dumpState.isDumping(DumpState.DUMP_INTENT_FILTER_VERIFIERS) &&
+ packageName == null) {
+ if (mIntentFilterVerifierComponent != null) {
+ String verifierPackageName = mIntentFilterVerifierComponent.getPackageName();
+ if (!checkin) {
+ if (dumpState.onTitlePrinted())
+ pw.println();
+ pw.println("Intent Filter Verifier:");
+ pw.print(" Using: ");
+ pw.print(verifierPackageName);
+ pw.print(" (uid=");
+ pw.print(getPackageUid(verifierPackageName, 0));
+ pw.println(")");
+ } else if (verifierPackageName != null) {
+ pw.print("ifv,"); pw.print(verifierPackageName);
+ pw.print(","); pw.println(getPackageUid(verifierPackageName, 0));
+ }
+ } else {
+ pw.println();
+ pw.println("No Intent Filter Verifier available!");
+ }
+ }
+
if (dumpState.isDumping(DumpState.DUMP_LIBS) && packageName == null) {
boolean printedHeader = false;
final Iterator<String> it = mSharedLibraries.keySet().iterator();
@@ -12692,6 +13296,65 @@
}
}
+ if (!checkin && dumpState.isDumping(DumpState.DUMP_DOMAIN_PREFERRED)) {
+ pw.println();
+ int count = mSettings.mPackages.size();
+ if (count == 0) {
+ pw.println("No domain preferred apps!");
+ pw.println();
+ } else {
+ final String prefix = " ";
+ Collection<PackageSetting> allPackageSettings = mSettings.mPackages.values();
+ if (allPackageSettings.size() == 0) {
+ pw.println("No domain preferred apps!");
+ pw.println();
+ } else {
+ pw.println("Domain preferred apps status:");
+ pw.println();
+ count = 0;
+ for (PackageSetting ps : allPackageSettings) {
+ IntentFilterVerificationInfo ivi = ps.getIntentFilterVerificationInfo();
+ if (ivi == null || ivi.getPackageName() == null) continue;
+ pw.println(prefix + "Package Name: " + ivi.getPackageName());
+ pw.println(prefix + "Domains: " + ivi.getDomainsString());
+ pw.println(prefix + "Status: " + ivi.getStatusString());
+ pw.println();
+ count++;
+ }
+ if (count == 0) {
+ pw.println(prefix + "No domain preferred app status!");
+ pw.println();
+ }
+ for (int userId : sUserManager.getUserIds()) {
+ pw.println("Domain preferred apps for User " + userId + ":");
+ pw.println();
+ count = 0;
+ for (PackageSetting ps : allPackageSettings) {
+ IntentFilterVerificationInfo ivi = ps.getIntentFilterVerificationInfo();
+ if (ivi == null || ivi.getPackageName() == null) {
+ continue;
+ }
+ final int status = ps.getDomainVerificationStatusForUser(userId);
+ if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED) {
+ continue;
+ }
+ pw.println(prefix + "Package Name: " + ivi.getPackageName());
+ pw.println(prefix + "Domains: " + ivi.getDomainsString());
+ String statusStr = IntentFilterVerificationInfo.
+ getStatusStringFromValue(status);
+ pw.println(prefix + "Status: " + statusStr);
+ pw.println();
+ count++;
+ }
+ if (count == 0) {
+ pw.println(prefix + "No domain preferred apps!");
+ pw.println();
+ }
+ }
+ }
+ }
+ }
+
if (!checkin && dumpState.isDumping(DumpState.DUMP_PERMISSIONS)) {
mSettings.dumpPermissionsLPr(pw, packageName, dumpState);
if (packageName == null) {
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index 889164c..a3f4c0b 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -70,4 +70,8 @@
public boolean isForwardLocked() {
return (pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK) != 0;
}
+
+ public boolean isSystem() {
+ return (pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0;
+ }
}
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index c40784b..20120de 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -20,6 +20,8 @@
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
+import android.content.pm.IntentFilterVerificationInfo;
+import android.content.pm.PackageManager;
import android.content.pm.PackageUserState;
import android.util.ArraySet;
import android.util.SparseArray;
@@ -92,7 +94,7 @@
PackageSignatures signatures = new PackageSignatures();
- boolean permissionsFixed;
+ boolean installPermissionsFixed;
PackageKeySetData keySetData = new PackageKeySetData();
@@ -108,6 +110,9 @@
/* package name of the app that installed this package */
String installerPackageName;
+
+ IntentFilterVerificationInfo verificationInfo;
+
PackageSettingBase(String name, String realName, File codePath, File resourcePath,
String legacyNativeLibraryPathString, String primaryCpuAbiString,
String secondaryCpuAbiString, String cpuAbiOverrideString,
@@ -145,7 +150,7 @@
signatures = new PackageSignatures(base.signatures);
- permissionsFixed = base.permissionsFixed;
+ installPermissionsFixed = base.installPermissionsFixed;
userState.clear();
for (int i=0; i<base.userState.size(); i++) {
userState.put(base.userState.keyAt(i),
@@ -207,13 +212,14 @@
firstInstallTime = base.firstInstallTime;
lastUpdateTime = base.lastUpdateTime;
signatures = base.signatures;
- permissionsFixed = base.permissionsFixed;
+ installPermissionsFixed = base.installPermissionsFixed;
userState.clear();
for (int i=0; i<base.userState.size(); i++) {
userState.put(base.userState.keyAt(i), base.userState.valueAt(i));
}
installStatus = base.installStatus;
keySetData = base.keySetData;
+ verificationInfo = base.verificationInfo;
}
private PackageUserState modifyUserState(int userId) {
@@ -317,7 +323,7 @@
void setUserState(int userId, int enabled, boolean installed, boolean stopped,
boolean notLaunched, boolean hidden,
String lastDisableAppCaller, ArraySet<String> enabledComponents,
- ArraySet<String> disabledComponents, boolean blockUninstall) {
+ ArraySet<String> disabledComponents, boolean blockUninstall, int domainVerifState) {
PackageUserState state = modifyUserState(userId);
state.enabled = enabled;
state.installed = installed;
@@ -328,6 +334,7 @@
state.enabledComponents = enabledComponents;
state.disabledComponents = disabledComponents;
state.blockUninstall = blockUninstall;
+ state.domainVerificationStatus = domainVerifState;
}
ArraySet<String> getEnabledComponents(int userId) {
@@ -415,4 +422,25 @@
void removeUser(int userId) {
userState.delete(userId);
}
+
+ public IntentFilterVerificationInfo getIntentFilterVerificationInfo() {
+ return verificationInfo;
+ }
+
+ public void setIntentFilterVerificationInfo(IntentFilterVerificationInfo info) {
+ verificationInfo = info;
+ }
+
+ public int getDomainVerificationStatusForUser(int userId) {
+ return readUserState(userId).domainVerificationStatus;
+ }
+
+ public void setDomainVerificationStatusForUser(int status, int userId) {
+ modifyUserState(userId).domainVerificationStatus = status;
+ }
+
+ public void clearDomainVerificationStatusForUser(int userId) {
+ modifyUserState(userId).domainVerificationStatus =
+ PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
+ }
}
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 95ee990..dd58813 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -22,12 +22,13 @@
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
+import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
import static android.os.Process.SYSTEM_UID;
import static android.os.Process.PACKAGE_INFO_GID;
-import android.content.Context;
import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
+import android.content.pm.IntentFilterVerificationInfo;
import android.content.pm.ResolveInfo;
import android.net.Uri;
import android.os.Binder;
@@ -42,6 +43,7 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.util.AtomicFile;
+import android.text.TextUtils;
import android.util.LogPrinter;
import android.util.SparseBooleanArray;
@@ -89,6 +91,7 @@
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
@@ -161,6 +164,7 @@
"persistent-preferred-activities";
static final String TAG_CROSS_PROFILE_INTENT_FILTERS =
"crossProfile-intent-filters";
+ public static final String TAG_DOMAIN_VERIFICATION = "domain-verification";
private static final String ATTR_NAME = "name";
private static final String ATTR_USER = "user";
@@ -175,9 +179,9 @@
private static final String ATTR_HIDDEN = "hidden";
private static final String ATTR_INSTALLED = "inst";
private static final String ATTR_BLOCK_UNINSTALL = "blockUninstall";
+ private static final String ATTR_DOMAIN_VERIFICATON_STATE = "domainVerificationStatus";
private final Object mLock;
- private final Context mContext;
private final RuntimePermissionPersistence mRuntimePermissionsPersistence;
@@ -277,12 +281,11 @@
public final KeySetManagerService mKeySetManagerService = new KeySetManagerService(mPackages);
- Settings(Context context, Object lock) {
- this(context, Environment.getDataDirectory(), lock);
+ Settings(Object lock) {
+ this(Environment.getDataDirectory(), lock);
}
- Settings(Context context, File dataDir, Object lock) {
- mContext = context;
+ Settings(File dataDir, Object lock) {
mLock = lock;
mRuntimePermissionsPersistence = new RuntimePermissionPersistence(mLock);
@@ -593,8 +596,8 @@
true, // notLaunched
false, // hidden
null, null, null,
- false // blockUninstall
- );
+ false, // blockUninstall
+ INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED);
writePackageRestrictionsLPr(user.id);
}
}
@@ -868,7 +871,7 @@
if (mOtherUserIds.get(uid) != null) {
PackageManagerService.reportSettingsProblem(Log.ERROR,
"Adding duplicate shared id: " + uid
- + " name=" + name);
+ + " name=" + name);
return false;
}
mOtherUserIds.put(uid, obj);
@@ -934,6 +937,96 @@
return cpir;
}
+ /**
+ * The following functions suppose that you have a lock for managing access to the
+ * mIntentFiltersVerifications map.
+ */
+
+ /* package protected */
+ IntentFilterVerificationInfo getIntentFilterVerificationLPr(String packageName) {
+ PackageSetting ps = mPackages.get(packageName);
+ if (ps == null) {
+ Slog.w(PackageManagerService.TAG, "No package known for name: " + packageName);
+ return null;
+ }
+ return ps.getIntentFilterVerificationInfo();
+ }
+
+ /* package protected */
+ boolean createIntentFilterVerificationIfNeededLPw(String packageName, String[] domains) {
+ PackageSetting ps = mPackages.get(packageName);
+ if (ps == null) {
+ Slog.w(PackageManagerService.TAG, "No package known for name: " + packageName);
+ return false;
+ }
+ if (ps.getIntentFilterVerificationInfo() == null) {
+ IntentFilterVerificationInfo ivi = new IntentFilterVerificationInfo(packageName, domains);
+ ps.setIntentFilterVerificationInfo(ivi);
+ return false;
+ }
+ return true;
+ }
+
+ int getIntentFilterVerificationStatusLPr(String packageName, int userId) {
+ PackageSetting ps = mPackages.get(packageName);
+ if (ps == null) {
+ Slog.w(PackageManagerService.TAG, "No package known for name: " + packageName);
+ return INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
+ }
+ int status = ps.getDomainVerificationStatusForUser(userId);
+ if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED) {
+ if (ps.getIntentFilterVerificationInfo() != null) {
+ status = ps.getIntentFilterVerificationInfo().getStatus();
+ }
+ }
+ return status;
+ }
+
+ boolean updateIntentFilterVerificationStatusLPw(String packageName, int status, int userId) {
+ PackageSetting ps = mPackages.get(packageName);
+ if (ps == null) {
+ Slog.w(PackageManagerService.TAG, "No package known for name: " + packageName);
+ return false;
+ }
+ ps.setDomainVerificationStatusForUser(status, userId);
+ return true;
+ }
+
+ /**
+ * Used for dump. Should be read only.
+ */
+ List<IntentFilterVerificationInfo> getIntentFilterVerificationsLPr(
+ String packageName) {
+ if (packageName == null) {
+ return Collections.<IntentFilterVerificationInfo>emptyList();
+ }
+ ArrayList<IntentFilterVerificationInfo> result = new ArrayList<>();
+ for (PackageSetting ps : mPackages.values()) {
+ IntentFilterVerificationInfo ivi = ps.getIntentFilterVerificationInfo();
+ if (ivi == null || TextUtils.isEmpty(ivi.getPackageName()) ||
+ !ivi.getPackageName().equalsIgnoreCase(packageName)) {
+ continue;
+ }
+ result.add(ivi);
+ }
+ return result;
+ }
+
+ void removeIntentFilterVerificationLPw(String packageName, int userId) {
+ PackageSetting ps = mPackages.get(packageName);
+ if (ps == null) {
+ Slog.w(PackageManagerService.TAG, "No package known for name: " + packageName);
+ return;
+ }
+ ps.clearDomainVerificationStatusForUser(userId);
+ }
+
+ void removeIntentFilterVerificationLPw(String packageName, int[] userIds) {
+ for (int userId : userIds) {
+ removeIntentFilterVerificationLPw(packageName, userId);
+ }
+ }
+
private File getUserPackagesStateFile(int userId) {
// TODO: Implement a cleaner solution when adding tests.
// This instead of Environment.getUserSystemDirectory(userId) to support testing.
@@ -948,6 +1041,17 @@
return new File(userDir, RUNTIME_PERMISSIONS_FILE_NAME);
}
+ boolean isFirstRuntimePermissionsBoot() {
+ return !getUserRuntimePermissionsFile(UserHandle.USER_OWNER).exists();
+ }
+
+ void deleteRuntimePermissionsFiles() {
+ for (int userId : UserManagerService.getInstance().getUserIds()) {
+ File file = getUserRuntimePermissionsFile(userId);
+ file.delete();
+ }
+ }
+
private File getUserPackagesStateBackupFile(int userId) {
return new File(Environment.getUserSystemDirectory(userId),
"package-restrictions-backup.xml");
@@ -1075,6 +1179,25 @@
}
}
+ private void readDomainVerificationLPw(XmlPullParser parser, PackageSettingBase packageSetting)
+ throws XmlPullParserException, IOException {
+ IntentFilterVerificationInfo ivi = new IntentFilterVerificationInfo(parser);
+ packageSetting.setIntentFilterVerificationInfo(ivi);
+ Log.d(TAG, "Read domain verification for package:" + ivi.getPackageName());
+ }
+
+ void writeDomainVerificationsLPr(XmlSerializer serializer, String packageName,
+ IntentFilterVerificationInfo verificationInfo)
+ throws IllegalArgumentException, IllegalStateException, IOException {
+ if (verificationInfo != null && verificationInfo.getPackageName() != null) {
+ serializer.startTag(null, TAG_DOMAIN_VERIFICATION);
+ verificationInfo.writeToXml(serializer);
+ Log.d(TAG, "Wrote domain verification for package: "
+ + verificationInfo.getPackageName());
+ serializer.endTag(null, TAG_DOMAIN_VERIFICATION);
+ }
+ }
+
void readPackageRestrictionsLPr(int userId) {
if (DEBUG_MU) {
Log.i(TAG, "Reading package restrictions for user=" + userId);
@@ -1119,8 +1242,8 @@
false, // notLaunched
false, // hidden
null, null, null,
- false // blockUninstall
- );
+ false, // blockUninstall
+ INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED);
}
return;
}
@@ -1189,6 +1312,12 @@
final boolean blockUninstall = blockUninstallStr == null
? false : Boolean.parseBoolean(blockUninstallStr);
+ final String verifStateStr =
+ parser.getAttributeValue(null, ATTR_DOMAIN_VERIFICATON_STATE);
+ final int verifState = (verifStateStr == null) ?
+ PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED :
+ Integer.parseInt(verifStateStr);
+
ArraySet<String> enabledComponents = null;
ArraySet<String> disabledComponents = null;
@@ -1209,7 +1338,8 @@
}
ps.setUserState(userId, enabled, installed, stopped, notLaunched, hidden,
- enabledCaller, enabledComponents, disabledComponents, blockUninstall);
+ enabledCaller, enabledComponents, disabledComponents, blockUninstall,
+ verifState);
} else if (tagName.equals("preferred-activities")) {
readPreferredActivitiesLPw(parser, userId);
} else if (tagName.equals(TAG_PERSISTENT_PREFERRED_ACTIVITIES)) {
@@ -1356,7 +1486,9 @@
&& ustate.enabledComponents.size() > 0)
|| (ustate.disabledComponents != null
&& ustate.disabledComponents.size() > 0)
- || ustate.blockUninstall) {
+ || ustate.blockUninstall
+ || (ustate.domainVerificationStatus !=
+ PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED)) {
serializer.startTag(null, TAG_PACKAGE);
serializer.attribute(null, ATTR_NAME, pkg.name);
if (DEBUG_MU) Log.i(TAG, " pkg=" + pkg.name + ", state=" + ustate.enabled);
@@ -1384,6 +1516,11 @@
ustate.lastDisableAppCaller);
}
}
+ if (ustate.domainVerificationStatus !=
+ PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED) {
+ serializer.attribute(null, ATTR_DOMAIN_VERIFICATON_STATE,
+ Integer.toString(ustate.domainVerificationStatus));
+ }
if (ustate.enabledComponents != null
&& ustate.enabledComponents.size() > 0) {
serializer.startTag(null, TAG_ENABLED_COMPONENTS);
@@ -1410,9 +1547,7 @@
}
writePreferredActivitiesLPr(serializer, userId, true);
-
writePersistentPreferredActivitiesLPr(serializer, userId);
-
writeCrossProfileIntentFiltersLPr(serializer, userId);
serializer.endTag(null, TAG_PACKAGE_RESTRICTIONS);
@@ -1925,6 +2060,7 @@
writeSigningKeySetsLPr(serializer, pkg.keySetData);
writeUpgradeKeySetsLPr(serializer, pkg.keySetData);
writeKeySetAliasesLPr(serializer, pkg.keySetData);
+ writeDomainVerificationsLPr(serializer, pkg.name, pkg.verificationInfo);
serializer.endTag(null, "package");
}
@@ -2101,7 +2237,8 @@
// TODO: check whether this is okay! as it is very
// similar to how preferred-activities are treated
readCrossProfileIntentFiltersLPw(parser, 0);
- } else if (tagName.equals("updated-package")) {
+ }
+ else if (tagName.equals("updated-package")) {
readDisabledSysPackageLPw(parser);
} else if (tagName.equals("cleaning-package")) {
String name = parser.getAttributeValue(null, ATTR_NAME);
@@ -2253,17 +2390,6 @@
mReadMessages.append("Read completed successfully: " + mPackages.size() + " packages, "
+ mSharedUsers.size() + " shared uids\n");
- // The persisted state we just read was generated after a permissions
- // update for all users, update each package and shared user setting
- // with the device users ids to start from were we left off.
- final int[] userIds = UserManagerService.getInstance().getUserIds();
- for (PackageSetting ps : mPackages.values()) {
- ps.setPermissionsUpdatedForUserIds(userIds);
- }
- for (SharedUserSetting sus : mSharedUsers.values()) {
- sus.setPermissionsUpdatedForUserIds(userIds);
- }
-
return true;
}
@@ -2741,6 +2867,18 @@
}
}
+ // We keep track for which users we granted permissions to be able
+ // to grant runtime permissions to system apps for newly appeared
+ // users or newly appeared system apps. If we supported runtime
+ // permissions during the previous boot, then we already granted
+ // permissions for all device users. In such a case we set the users
+ // for which we granted permissions to avoid clobbering of runtime
+ // permissions we granted to system apps but the user revoked later.
+ if (!isFirstRuntimePermissionsBoot()) {
+ final int[] userIds = UserManagerService.getInstance().getUserIds();
+ ps.setPermissionsUpdatedForUserIds(userIds);
+ }
+
mDisabledSysPackages.put(name, ps);
}
@@ -3001,7 +3139,7 @@
} else if (tagName.equals(TAG_PERMISSIONS)) {
readInstallPermissionsLPr(parser,
packageSetting.getPermissionsState());
- packageSetting.permissionsFixed = true;
+ packageSetting.installPermissionsFixed = true;
} else if (tagName.equals("proper-signing-keyset")) {
long id = Long.parseLong(parser.getAttributeValue(null, "identifier"));
packageSetting.keySetData.setProperSigningKeySet(id);
@@ -3015,12 +3153,26 @@
long id = Long.parseLong(parser.getAttributeValue(null, "identifier"));
String alias = parser.getAttributeValue(null, "alias");
packageSetting.keySetData.addDefinedKeySet(id, alias);
+ } else if (tagName.equals(TAG_DOMAIN_VERIFICATION)) {
+ readDomainVerificationLPw(parser, packageSetting);
} else {
PackageManagerService.reportSettingsProblem(Log.WARN,
"Unknown element under <package>: " + parser.getName());
XmlUtils.skipCurrentTag(parser);
}
}
+
+ // We keep track for which users we granted permissions to be able
+ // to grant runtime permissions to system apps for newly appeared
+ // users or newly appeared system apps. If we supported runtime
+ // permissions during the previous boot, then we already granted
+ // permissions for all device users. In such a case we set the users
+ // for which we granted permissions to avoid clobbering of runtime
+ // permissions we granted to system apps but the user revoked later.
+ if (!isFirstRuntimePermissionsBoot()) {
+ final int[] userIds = UserManagerService.getInstance().getUserIds();
+ packageSetting.setPermissionsUpdatedForUserIds(userIds);
+ }
} else {
XmlUtils.skipCurrentTag(parser);
}
@@ -3138,6 +3290,18 @@
XmlUtils.skipCurrentTag(parser);
}
}
+
+ // We keep track for which users we granted permissions to be able
+ // to grant runtime permissions to system apps for newly appeared
+ // users or newly appeared system apps. If we supported runtime
+ // permissions during the previous boot, then we already granted
+ // permissions for all device users. In such a case we set the users
+ // for which we granted permissions to avoid clobbering of runtime
+ // permissions we granted to system apps but the user revoked later.
+ if (!isFirstRuntimePermissionsBoot()) {
+ final int[] userIds = UserManagerService.getInstance().getUserIds();
+ su.setPermissionsUpdatedForUserIds(userIds);
+ }
} else {
XmlUtils.skipCurrentTag(parser);
}
@@ -3355,7 +3519,7 @@
return false;
}
- private List<UserInfo> getAllUsers() {
+ List<UserInfo> getAllUsers() {
long id = Binder.clearCallingIdentity();
try {
return UserManagerService.getInstance().getUsers(false);
@@ -3574,7 +3738,8 @@
pw.println(ps.installerPackageName);
}
pw.print(prefix); pw.print(" signatures="); pw.println(ps.signatures);
- pw.print(prefix); pw.print(" permissionsFixed="); pw.print(ps.permissionsFixed);
+ pw.print(prefix); pw.print(" installPermissionsFixed=");
+ pw.print(ps.installPermissionsFixed);
pw.print(" installStatus="); pw.println(ps.installStatus);
pw.print(prefix); pw.print(" pkgFlags="); printFlags(pw, ps.pkgFlags, FLAG_DUMP_SPEC);
pw.println();
@@ -3851,11 +4016,19 @@
}
public void writePermissionsForUserSyncLPr(int userId) {
+ if (!PackageManagerService.RUNTIME_PERMISSIONS_ENABLED) {
+ return;
+ }
+
mHandler.removeMessages(userId);
writePermissionsSync(userId);
}
public void writePermissionsForUserAsyncLPr(int userId) {
+ if (!PackageManagerService.RUNTIME_PERMISSIONS_ENABLED) {
+ return;
+ }
+
final long currentTimeMillis = SystemClock.uptimeMillis();
if (mWriteScheduled.get(userId)) {
@@ -4015,128 +4188,67 @@
private void parseRuntimePermissionsLPr(XmlPullParser parser, int userId)
throws IOException, XmlPullParserException {
- parser.next();
- skipEmptyTextTags(parser);
- if (!accept(parser, XmlPullParser.START_TAG, TAG_RUNTIME_PERMISSIONS)) {
- return;
- }
+ final int outerDepth = parser.getDepth();
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+ if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+ continue;
+ }
- parser.next();
+ switch (parser.getName()) {
+ case TAG_PACKAGE: {
+ String name = parser.getAttributeValue(null, ATTR_NAME);
+ PackageSetting ps = mPackages.get(name);
+ if (ps == null) {
+ Slog.w(PackageManagerService.TAG, "Unknown package:" + name);
+ XmlUtils.skipCurrentTag(parser);
+ continue;
+ }
+ parsePermissionsLPr(parser, ps.getPermissionsState(), userId);
+ } break;
- while (parsePackageLPr(parser, userId)
- || parseSharedUserLPr(parser, userId)) {
- parser.next();
- }
-
- skipEmptyTextTags(parser);
- expect(parser, XmlPullParser.END_TAG, TAG_RUNTIME_PERMISSIONS);
- }
-
- private boolean parsePackageLPr(XmlPullParser parser, int userId)
- throws IOException, XmlPullParserException {
- skipEmptyTextTags(parser);
- if (!accept(parser, XmlPullParser.START_TAG, TAG_PACKAGE)) {
- return false;
- }
-
- String name = parser.getAttributeValue(null, ATTR_NAME);
-
- parser.next();
-
- PackageSetting ps = mPackages.get(name);
- if (ps != null) {
- while (parsePermissionLPr(parser, ps.getPermissionsState(), userId)) {
- parser.next();
+ case TAG_SHARED_USER: {
+ String name = parser.getAttributeValue(null, ATTR_NAME);
+ SharedUserSetting sus = mSharedUsers.get(name);
+ if (sus == null) {
+ Slog.w(PackageManagerService.TAG, "Unknown shared user:" + name);
+ XmlUtils.skipCurrentTag(parser);
+ continue;
+ }
+ parsePermissionsLPr(parser, sus.getPermissionsState(), userId);
+ } break;
}
}
-
- skipEmptyTextTags(parser);
- expect(parser, XmlPullParser.END_TAG, TAG_PACKAGE);
-
- return true;
}
- private boolean parseSharedUserLPr(XmlPullParser parser, int userId)
- throws IOException, XmlPullParserException {
- skipEmptyTextTags(parser);
- if (!accept(parser, XmlPullParser.START_TAG, TAG_SHARED_USER)) {
- return false;
- }
-
- String name = parser.getAttributeValue(null, ATTR_NAME);
-
- parser.next();
-
- SharedUserSetting sus = mSharedUsers.get(name);
- if (sus != null) {
- while (parsePermissionLPr(parser, sus.getPermissionsState(), userId)) {
- parser.next();
- }
- }
-
- skipEmptyTextTags(parser);
- expect(parser, XmlPullParser.END_TAG, TAG_SHARED_USER);
-
- return true;
- }
-
- private boolean parsePermissionLPr(XmlPullParser parser, PermissionsState permissionsState,
+ private void parsePermissionsLPr(XmlPullParser parser, PermissionsState permissionsState,
int userId) throws IOException, XmlPullParserException {
- skipEmptyTextTags(parser);
- if (!accept(parser, XmlPullParser.START_TAG, TAG_ITEM)) {
- return false;
- }
-
- String name = parser.getAttributeValue(null, ATTR_NAME);
-
- parser.next();
-
- BasePermission bp = mPermissions.get(name);
- if (bp != null) {
- if (permissionsState.grantRuntimePermission(bp, userId) ==
- PermissionsState.PERMISSION_OPERATION_FAILURE) {
- Slog.w(PackageManagerService.TAG, "Duplicate permission:" + name);
+ final int outerDepth = parser.getDepth();
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+ if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+ continue;
}
- } else {
- Slog.w(PackageManagerService.TAG, "Unknown permission:" + name);
- }
- skipEmptyTextTags(parser);
- expect(parser, XmlPullParser.END_TAG, TAG_ITEM);
+ switch (parser.getName()) {
+ case TAG_ITEM: {
+ String name = parser.getAttributeValue(null, ATTR_NAME);
+ BasePermission bp = mPermissions.get(name);
+ if (bp == null) {
+ Slog.w(PackageManagerService.TAG, "Unknown permission:" + name);
+ XmlUtils.skipCurrentTag(parser);
+ continue;
+ }
- return true;
- }
-
- private void expect(XmlPullParser parser, int type, String tag)
- throws IOException, XmlPullParserException {
- if (!accept(parser, type, tag)) {
- throw new XmlPullParserException("Expected event: " + type
- + " and tag: " + tag + " but got event: " + parser.getEventType()
- + " and tag:" + parser.getName());
- }
- }
-
- private void skipEmptyTextTags(XmlPullParser parser)
- throws IOException, XmlPullParserException {
- while (accept(parser, XmlPullParser.TEXT, null)
- && parser.isWhitespace()) {
- parser.next();
- }
- }
-
- private boolean accept(XmlPullParser parser, int type, String tag)
- throws IOException, XmlPullParserException {
- if (parser.getEventType() != type) {
- return false;
- }
- if (tag != null) {
- if (!tag.equals(parser.getName())) {
- return false;
+ if (permissionsState.grantRuntimePermission(bp, userId) ==
+ PermissionsState.PERMISSION_OPERATION_FAILURE) {
+ Slog.w(PackageManagerService.TAG, "Duplicate permission:" + name);
+ }
+ } break;
}
- } else if (parser.getName() != null) {
- return false;
}
- return true;
}
private void writePermissions(XmlSerializer serializer, Set<String> permissions)
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index be6550c..87cf06e 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -1398,6 +1398,14 @@
public void onDebug() {
// no-op
}
+ @Override
+ public void onDown() {
+ mOrientationListener.onTouchStart();
+ }
+ @Override
+ public void onUpOrCancel() {
+ mOrientationListener.onTouchEnd();
+ }
});
mImmersiveModeConfirmation = new ImmersiveModeConfirmation(mContext);
mWindowManagerFuncs.registerPointerEventListener(mSystemGestures);
diff --git a/services/core/java/com/android/server/policy/SystemGesturesPointerEventListener.java b/services/core/java/com/android/server/policy/SystemGesturesPointerEventListener.java
index cfa631f..627b328 100644
--- a/services/core/java/com/android/server/policy/SystemGesturesPointerEventListener.java
+++ b/services/core/java/com/android/server/policy/SystemGesturesPointerEventListener.java
@@ -75,6 +75,7 @@
mDebugFireable = true;
mDownPointers = 0;
captureDown(event, 0);
+ mCallbacks.onDown();
break;
case MotionEvent.ACTION_POINTER_DOWN:
captureDown(event, event.getActionIndex());
@@ -106,6 +107,7 @@
case MotionEvent.ACTION_CANCEL:
mSwipeFireable = false;
mDebugFireable = false;
+ mCallbacks.onUpOrCancel();
break;
default:
if (DEBUG) Slog.d(TAG, "Ignoring " + event);
@@ -192,6 +194,8 @@
void onSwipeFromTop();
void onSwipeFromBottom();
void onSwipeFromRight();
+ void onDown();
+ void onUpOrCancel();
void onDebug();
}
}
diff --git a/services/core/java/com/android/server/policy/WindowOrientationListener.java b/services/core/java/com/android/server/policy/WindowOrientationListener.java
index 0118127..c8fd82e 100644
--- a/services/core/java/com/android/server/policy/WindowOrientationListener.java
+++ b/services/core/java/com/android/server/policy/WindowOrientationListener.java
@@ -22,6 +22,7 @@
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Handler;
+import android.os.SystemClock;
import android.os.SystemProperties;
import android.util.Log;
import android.util.Slog;
@@ -133,6 +134,24 @@
}
}
+ public void onTouchStart() {
+ synchronized (mLock) {
+ if (mSensorEventListener != null) {
+ mSensorEventListener.onTouchStartLocked();
+ }
+ }
+ }
+
+ public void onTouchEnd() {
+ long whenElapsedNanos = SystemClock.elapsedRealtimeNanos();
+
+ synchronized (mLock) {
+ if (mSensorEventListener != null) {
+ mSensorEventListener.onTouchEndLocked(whenElapsedNanos);
+ }
+ }
+ }
+
/**
* Sets the current rotation.
*
@@ -269,6 +288,11 @@
private static final long PROPOSAL_MIN_TIME_SINCE_ACCELERATION_ENDED_NANOS =
500 * NANOS_PER_MS;
+ // The minimum amount of time that must have elapsed since the screen was last touched
+ // before the proposed rotation can change.
+ private static final long PROPOSAL_MIN_TIME_SINCE_TOUCH_END_NANOS =
+ 500 * NANOS_PER_MS;
+
// If the tilt angle remains greater than the specified angle for a minimum of
// the specified time, then the device is deemed to be lying flat
// (just chillin' on a table).
@@ -398,6 +422,10 @@
private long mAccelerationTimestampNanos;
private boolean mAccelerating;
+ // Timestamp when the last touch to the touch screen ended
+ private long mTouchEndedTimestampNanos = Long.MIN_VALUE;
+ private boolean mTouched;
+
// Whether we are locked into an overhead usage mode.
private boolean mOverhead;
@@ -422,6 +450,7 @@
pw.println(prefix + "mSwinging=" + mSwinging);
pw.println(prefix + "mAccelerating=" + mAccelerating);
pw.println(prefix + "mOverhead=" + mOverhead);
+ pw.println(prefix + "mTouched=" + mTouched);
}
@Override
@@ -601,6 +630,7 @@
+ ", isFlat=" + isFlat
+ ", isSwinging=" + isSwinging
+ ", isOverhead=" + mOverhead
+ + ", isTouched=" + mTouched
+ ", timeUntilSettledMS=" + remainingMS(now,
mPredictedRotationTimestampNanos + PROPOSAL_SETTLE_TIME_NANOS)
+ ", timeUntilAccelerationDelayExpiredMS=" + remainingMS(now,
@@ -608,7 +638,9 @@
+ ", timeUntilFlatDelayExpiredMS=" + remainingMS(now,
mFlatTimestampNanos + PROPOSAL_MIN_TIME_SINCE_FLAT_ENDED_NANOS)
+ ", timeUntilSwingDelayExpiredMS=" + remainingMS(now,
- mSwingTimestampNanos + PROPOSAL_MIN_TIME_SINCE_SWING_ENDED_NANOS));
+ mSwingTimestampNanos + PROPOSAL_MIN_TIME_SINCE_SWING_ENDED_NANOS)
+ + ", timeUntilTouchDelayExpiredMS=" + remainingMS(now,
+ mTouchEndedTimestampNanos + PROPOSAL_MIN_TIME_SINCE_TOUCH_END_NANOS));
}
}
@@ -710,6 +742,12 @@
return false;
}
+ // The last touch must have ended sufficiently long ago.
+ if (mTouched || now < mTouchEndedTimestampNanos
+ + PROPOSAL_MIN_TIME_SINCE_TOUCH_END_NANOS) {
+ return false;
+ }
+
// Looks good!
return true;
}
@@ -796,5 +834,14 @@
private float remainingMS(long now, long until) {
return now >= until ? 0 : (until - now) * 0.000001f;
}
+
+ private void onTouchStartLocked() {
+ mTouched = true;
+ }
+
+ private void onTouchEndLocked(long whenElapsedNanos) {
+ mTouched = false;
+ mTouchEndedTimestampNanos = whenElapsedNanos;
+ }
}
}
diff --git a/services/core/java/com/android/server/trust/TrustAgentWrapper.java b/services/core/java/com/android/server/trust/TrustAgentWrapper.java
index 0109313..dec195d 100644
--- a/services/core/java/com/android/server/trust/TrustAgentWrapper.java
+++ b/services/core/java/com/android/server/trust/TrustAgentWrapper.java
@@ -272,13 +272,14 @@
alarmFilter.addDataScheme(mAlarmIntent.getScheme());
final String pathUri = mAlarmIntent.toUri(Intent.URI_INTENT_SCHEME);
alarmFilter.addDataPath(pathUri, PatternMatcher.PATTERN_LITERAL);
- mContext.registerReceiver(mBroadcastReceiver, alarmFilter, PERMISSION, null);
// Schedules a restart for when connecting times out. If the connection succeeds,
// the restart is canceled in mCallback's onConnected.
scheduleRestart();
mBound = context.bindServiceAsUser(intent, mConnection, Context.BIND_AUTO_CREATE, user);
- if (!mBound) {
+ if (mBound) {
+ mContext.registerReceiver(mBroadcastReceiver, alarmFilter, PERMISSION, null);
+ } else {
Log.e(TAG, "Can't bind to TrustAgent " + mName.flattenToShortString());
}
}
@@ -398,7 +399,6 @@
}
public void destroy() {
- mContext.unregisterReceiver(mBroadcastReceiver);
mHandler.removeMessages(MSG_RESTART_TIMEOUT);
if (!mBound) {
@@ -408,6 +408,7 @@
mTrustManagerService.mArchive.logAgentStopped(mUserId, mName);
mContext.unbindService(mConnection);
mBound = false;
+ mContext.unregisterReceiver(mBroadcastReceiver);
mTrustAgentService = null;
mSetTrustAgentFeaturesToken = null;
mHandler.sendEmptyMessage(MSG_REVOKE_TRUST);
diff --git a/services/core/java/com/android/server/updates/TZInfoInstallReceiver.java b/services/core/java/com/android/server/updates/TZInfoInstallReceiver.java
deleted file mode 100644
index 2fe68f8..0000000
--- a/services/core/java/com/android/server/updates/TZInfoInstallReceiver.java
+++ /dev/null
@@ -1,33 +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.
- */
-
-package com.android.server.updates;
-
-import android.util.Base64;
-
-import java.io.IOException;
-
-public class TZInfoInstallReceiver extends ConfigUpdateInstallReceiver {
-
- public TZInfoInstallReceiver() {
- super("/data/misc/zoneinfo/", "tzdata", "metadata/", "version");
- }
-
- @Override
- protected void install(byte[] encodedContent, int version) throws IOException {
- super.install(Base64.decode(encodedContent, Base64.DEFAULT), version);
- }
-}
diff --git a/services/core/java/com/android/server/updates/TzDataInstallReceiver.java b/services/core/java/com/android/server/updates/TzDataInstallReceiver.java
new file mode 100644
index 0000000..b260e4e
--- /dev/null
+++ b/services/core/java/com/android/server/updates/TzDataInstallReceiver.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.updates;
+
+import android.util.Slog;
+
+import java.io.File;
+import java.io.IOException;
+import libcore.tzdata.update.TzDataBundleInstaller;
+
+/**
+ * An install receiver responsible for installing timezone data updates.
+ */
+public class TzDataInstallReceiver extends ConfigUpdateInstallReceiver {
+
+ private static final String TAG = "TZDataInstallReceiver";
+
+ private static final File TZ_DATA_DIR = new File("/data/misc/zoneinfo");
+ private static final String UPDATE_DIR_NAME = TZ_DATA_DIR.getPath() + "/updates/";
+ private static final String UPDATE_METADATA_DIR_NAME = "metadata/";
+ private static final String UPDATE_VERSION_FILE_NAME = "version";
+ private static final String UPDATE_CONTENT_FILE_NAME = "tzdata_bundle.zip";
+
+ private final TzDataBundleInstaller installer;
+
+ public TzDataInstallReceiver() {
+ super(UPDATE_DIR_NAME, UPDATE_CONTENT_FILE_NAME, UPDATE_METADATA_DIR_NAME,
+ UPDATE_VERSION_FILE_NAME);
+ installer = new TzDataBundleInstaller(TAG, TZ_DATA_DIR);
+ }
+
+ @Override
+ protected void install(byte[] content, int version) throws IOException {
+ boolean valid = installer.install(content);
+ Slog.i(TAG, "Timezone data install valid for this device: " + valid);
+ // Even if !valid, we call super.install(). Only in the event of an exception should we
+ // not. If we didn't do this we could attempt to install repeatedly.
+ super.install(content, version);
+ }
+}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 957eb9e..dcd233f 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -4608,8 +4608,7 @@
if (DEBUG_APP_TRANSITIONS || DEBUG_ORIENTATION) Slog.v(TAG, "setAppVisibility(" +
token + ", visible=" + visible + "): " + mAppTransition +
" hidden=" + wtoken.hidden + " hiddenRequested=" +
- wtoken.hiddenRequested, HIDE_STACK_CRAWLS ?
- null : new RuntimeException("here").fillInStackTrace());
+ wtoken.hiddenRequested + " Callers=" + Debug.getCallers(6));
// If we are preparing an app transition, then delay changing
// the visibility of this token until we execute that transition.
@@ -9060,39 +9059,41 @@
goodToGo = false;
}
}
- if (goodToGo && isWallpaperVisible(mWallpaperTarget)) {
- boolean wallpaperGoodToGo = true;
- for (int curTokenIndex = mWallpaperTokens.size() - 1;
- curTokenIndex >= 0 && wallpaperGoodToGo; curTokenIndex--) {
- WindowToken token = mWallpaperTokens.get(curTokenIndex);
- for (int curWallpaperIndex = token.windows.size() - 1; curWallpaperIndex >= 0;
- curWallpaperIndex--) {
- WindowState wallpaper = token.windows.get(curWallpaperIndex);
- if (wallpaper.mWallpaperVisible && !wallpaper.isDrawnLw()) {
- // We've told this wallpaper to be visible, but it is not drawn yet
- wallpaperGoodToGo = false;
- if (mWallpaperDrawState != WALLPAPER_DRAW_TIMEOUT) {
- // wait for this wallpaper until it is drawn or timeout
- goodToGo = false;
- }
- if (mWallpaperDrawState == WALLPAPER_DRAW_NORMAL) {
- mWallpaperDrawState = WALLPAPER_DRAW_PENDING;
- mH.removeMessages(H.WALLPAPER_DRAW_PENDING_TIMEOUT);
- mH.sendEmptyMessageDelayed(H.WALLPAPER_DRAW_PENDING_TIMEOUT,
- WALLPAPER_DRAW_PENDING_TIMEOUT_DURATION);
- }
- if (DEBUG_APP_TRANSITIONS || DEBUG_WALLPAPER) Slog.v(TAG,
- "Wallpaper should be visible but has not been drawn yet. " +
- "mWallpaperDrawState=" + mWallpaperDrawState);
- break;
- }
- }
- }
- if (wallpaperGoodToGo) {
- mWallpaperDrawState = WALLPAPER_DRAW_NORMAL;
- mH.removeMessages(H.WALLPAPER_DRAW_PENDING_TIMEOUT);
- }
- }
+// Stuck in a state with mWallpaperDrawState == WALLPAPER_DRAW_PENDING without a timeout. Leave
+// commented out until that is understood.
+// if (goodToGo && isWallpaperVisible(mWallpaperTarget)) {
+// boolean wallpaperGoodToGo = true;
+// for (int curTokenIndex = mWallpaperTokens.size() - 1;
+// curTokenIndex >= 0 && wallpaperGoodToGo; curTokenIndex--) {
+// WindowToken token = mWallpaperTokens.get(curTokenIndex);
+// for (int curWallpaperIndex = token.windows.size() - 1; curWallpaperIndex >= 0;
+// curWallpaperIndex--) {
+// WindowState wallpaper = token.windows.get(curWallpaperIndex);
+// if (wallpaper.mWallpaperVisible && !wallpaper.isDrawnLw()) {
+// // We've told this wallpaper to be visible, but it is not drawn yet
+// wallpaperGoodToGo = false;
+// if (mWallpaperDrawState != WALLPAPER_DRAW_TIMEOUT) {
+// // wait for this wallpaper until it is drawn or timeout
+// goodToGo = false;
+// }
+// if (mWallpaperDrawState == WALLPAPER_DRAW_NORMAL) {
+// mWallpaperDrawState = WALLPAPER_DRAW_PENDING;
+// mH.removeMessages(H.WALLPAPER_DRAW_PENDING_TIMEOUT);
+// mH.sendEmptyMessageDelayed(H.WALLPAPER_DRAW_PENDING_TIMEOUT,
+// WALLPAPER_DRAW_PENDING_TIMEOUT_DURATION);
+// }
+// if (DEBUG_APP_TRANSITIONS || DEBUG_WALLPAPER) Slog.v(TAG,
+// "Wallpaper should be visible but has not been drawn yet. " +
+// "mWallpaperDrawState=" + mWallpaperDrawState);
+// break;
+// }
+// }
+// }
+// if (wallpaperGoodToGo) {
+// mWallpaperDrawState = WALLPAPER_DRAW_NORMAL;
+// mH.removeMessages(H.WALLPAPER_DRAW_PENDING_TIMEOUT);
+// }
+// }
}
if (goodToGo) {
if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "**** GOOD TO GO");
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 73b5de1..0c58aef 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -78,6 +78,8 @@
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
+import android.provider.ContactsContract.QuickContact;
+import android.provider.ContactsInternal;
import android.provider.Settings;
import android.security.Credentials;
import android.security.IKeyChainAliasCallback;
@@ -146,6 +148,8 @@
private static final String LOG_TAG = "DevicePolicyManagerService";
+ private static final boolean VERBOSE_LOG = false; // DO NOT SUBMIT WITH TRUE
+
private static final String DEVICE_POLICIES_XML = "device_policies.xml";
private static final String LOCK_TASK_COMPONENTS_XML = "lock-task-component";
@@ -5435,6 +5439,59 @@
}
}
+ @Override
+ public void startManagedQuickContact(String actualLookupKey, long actualContactId,
+ Intent originalIntent) {
+ final Intent intent = QuickContact.rebuildManagedQuickContactsIntent(
+ actualLookupKey, actualContactId, originalIntent);
+ final int callingUserId = UserHandle.getCallingUserId();
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ synchronized (this) {
+ final int managedUserId = getManagedUserId(callingUserId);
+ if (managedUserId < 0) {
+ return;
+ }
+ if (getCrossProfileCallerIdDisabledForUser(managedUserId)) {
+ if (VERBOSE_LOG) {
+ Log.v(LOG_TAG,
+ "Cross-profile contacts access disabled for user " + managedUserId);
+ }
+ return;
+ }
+ ContactsInternal.startQuickContactWithErrorToastForUser(
+ mContext, intent, new UserHandle(managedUserId));
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ /**
+ * @return the user ID of the managed user that is linked to the current user, if any.
+ * Otherwise -1.
+ */
+ public int getManagedUserId(int callingUserId) {
+ if (VERBOSE_LOG) {
+ Log.v(LOG_TAG, "getManagedUserId: callingUserId=" + callingUserId);
+ }
+
+ for (UserInfo ui : mUserManager.getProfiles(callingUserId)) {
+ if (ui.id == callingUserId || !ui.isManagedProfile()) {
+ continue; // Caller user self, or not a managed profile. Skip.
+ }
+ if (VERBOSE_LOG) {
+ Log.v(LOG_TAG, "Managed user=" + ui.id);
+ }
+ return ui.id;
+ }
+ if (VERBOSE_LOG) {
+ Log.v(LOG_TAG, "Managed user not found.");
+ }
+ return -1;
+ }
+
/**
* Sets which packages may enter lock task mode.
*
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 65e3534..090c0f8 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -124,6 +124,8 @@
"com.android.server.print.PrintManagerService";
private static final String USB_SERVICE_CLASS =
"com.android.server.usb.UsbService$Lifecycle";
+ private static final String MIDI_SERVICE_CLASS =
+ "com.android.server.midi.MidiService$Lifecycle";
private static final String WIFI_SERVICE_CLASS =
"com.android.server.wifi.WifiService";
private static final String WIFI_P2P_SERVICE_CLASS =
@@ -810,6 +812,11 @@
}
if (!disableNonCoreServices) {
+ if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_MIDI)) {
+ // Start MIDI Manager service
+ mSystemServiceManager.startService(MIDI_SERVICE_CLASS);
+ }
+
if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_USB_HOST)
|| mPackageManager.hasSystemFeature(
PackageManager.FEATURE_USB_ACCESSORY)) {
@@ -827,16 +834,6 @@
}
}
- if (!disableNonCoreServices) {
- try {
- Slog.i(TAG, "MIDI Service");
- ServiceManager.addService(Context.MIDI_SERVICE,
- new MidiService(context));
- } catch (Throwable e) {
- reportWtf("starting MIDI Service", e);
- }
- }
-
mSystemServiceManager.startService(TwilightService.class);
mSystemServiceManager.startService(JobSchedulerService.class);
@@ -915,6 +912,11 @@
}
}
+ if (!disableNonCoreServices) {
+ ServiceManager.addService(GraphicsStatsService.GRAPHICS_STATS_SERVICE,
+ new GraphicsStatsService(context));
+ }
+
if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_PRINTING)) {
mSystemServiceManager.startService(PRINT_MANAGER_SERVICE_CLASS);
}
diff --git a/services/midi/Android.mk b/services/midi/Android.mk
new file mode 100644
index 0000000..faac01c
--- /dev/null
+++ b/services/midi/Android.mk
@@ -0,0 +1,12 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := services.midi
+
+LOCAL_SRC_FILES += \
+ $(call all-java-files-under,java)
+
+LOCAL_JAVA_LIBRARIES := services.core
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/services/core/java/com/android/server/MidiService.java b/services/midi/java/com/android/server/midi/MidiService.java
similarity index 97%
rename from services/core/java/com/android/server/MidiService.java
rename to services/midi/java/com/android/server/midi/MidiService.java
index e753664..1d2180e 100644
--- a/services/core/java/com/android/server/MidiService.java
+++ b/services/midi/java/com/android/server/midi/MidiService.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server;
+package com.android.server.midi;
import android.content.Context;
import android.content.Intent;
@@ -40,6 +40,7 @@
import com.android.internal.content.PackageMonitor;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.XmlUtils;
+import com.android.server.SystemService;
import org.xmlpull.v1.XmlPullParser;
@@ -51,6 +52,21 @@
import java.util.List;
public class MidiService extends IMidiManager.Stub {
+
+ public static class Lifecycle extends SystemService {
+ private MidiService mMidiService;
+
+ public Lifecycle(Context context) {
+ super(context);
+ }
+
+ @Override
+ public void onStart() {
+ mMidiService = new MidiService(getContext());
+ publishBinderService(Context.MIDI_SERVICE, mMidiService);
+ }
+ }
+
private static final String TAG = "MidiService";
private final Context mContext;
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
index 4dc1131..a3f3a5d 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -134,7 +134,7 @@
public void testSettingsReadOld() {
// Write the package files and make sure they're parsed properly the first time
writeOldFiles();
- Settings settings = new Settings(getContext(), getContext().getFilesDir(), new Object());
+ Settings settings = new Settings(getContext().getFilesDir(), new Object());
assertEquals(true, settings.readLPw(null, null, 0, false));
assertNotNull(settings.peekPackageLPr(PACKAGE_NAME_3));
assertNotNull(settings.peekPackageLPr(PACKAGE_NAME_1));
@@ -152,12 +152,12 @@
public void testNewPackageRestrictionsFile() {
// Write the package files and make sure they're parsed properly the first time
writeOldFiles();
- Settings settings = new Settings(getContext(), getContext().getFilesDir(), new Object());
+ Settings settings = new Settings(getContext().getFilesDir(), new Object());
assertEquals(true, settings.readLPw(null, null, 0, false));
settings.writeLPr();
// Create Settings again to make it read from the new files
- settings = new Settings(getContext(), getContext().getFilesDir(), new Object());
+ settings = new Settings(getContext().getFilesDir(), new Object());
assertEquals(true, settings.readLPw(null, null, 0, false));
PackageSetting ps = settings.peekPackageLPr(PACKAGE_NAME_2);
@@ -168,7 +168,7 @@
public void testEnableDisable() {
// Write the package files and make sure they're parsed properly the first time
writeOldFiles();
- Settings settings = new Settings(getContext(), getContext().getFilesDir(), new Object());
+ Settings settings = new Settings(getContext().getFilesDir(), new Object());
assertEquals(true, settings.readLPw(null, null, 0, false));
// Enable/Disable a package
diff --git a/services/usb/java/com/android/server/usb/UsbAlsaManager.java b/services/usb/java/com/android/server/usb/UsbAlsaManager.java
index c041029..2728af1 100644
--- a/services/usb/java/com/android/server/usb/UsbAlsaManager.java
+++ b/services/usb/java/com/android/server/usb/UsbAlsaManager.java
@@ -17,6 +17,7 @@
package com.android.server.usb;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.hardware.usb.UsbConstants;
import android.hardware.usb.UsbDevice;
@@ -55,6 +56,7 @@
private final Context mContext;
private IAudioService mAudioService;
+ private final boolean mHasMidiFeature;
private final AlsaCardsParser mCardsParser = new AlsaCardsParser();
private final AlsaDevicesParser mDevicesParser = new AlsaDevicesParser();
@@ -126,6 +128,7 @@
/* package */ UsbAlsaManager(Context context) {
mContext = context;
+ mHasMidiFeature = context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_MIDI);
// initial scan
mCardsParser.scan();
@@ -389,7 +392,7 @@
// mDevicesParser.scan()
boolean hasMidi = mDevicesParser.hasMIDIDevices(addedCard);
- if (hasMidi) {
+ if (hasMidi && mHasMidiFeature) {
int device = mDevicesParser.getDefaultDeviceNum(addedCard);
AlsaDevice alsaDevice = waitForAlsaDevice(addedCard, device, AlsaDevice.TYPE_MIDI);
if (alsaDevice != null) {
@@ -459,6 +462,10 @@
}
/* package */ void setPeripheralMidiState(boolean enabled, int card, int device) {
+ if (!mHasMidiFeature) {
+ return;
+ }
+
if (enabled && mPeripheralMidiDevice == null) {
Bundle properties = new Bundle();
Resources r = mContext.getResources();
diff --git a/services/usb/java/com/android/server/usb/UsbMidiDevice.java b/services/usb/java/com/android/server/usb/UsbMidiDevice.java
index 3b65709..6ece888 100644
--- a/services/usb/java/com/android/server/usb/UsbMidiDevice.java
+++ b/services/usb/java/com/android/server/usb/UsbMidiDevice.java
@@ -19,7 +19,6 @@
import android.content.Context;
import android.media.midi.MidiDeviceInfo;
import android.media.midi.MidiDeviceServer;
-import android.media.midi.MidiDispatcher;
import android.media.midi.MidiManager;
import android.media.midi.MidiReceiver;
import android.media.midi.MidiSender;
@@ -30,6 +29,9 @@
import android.system.StructPollfd;
import android.util.Log;
+import com.android.internal.midi.MidiEventScheduler;
+import com.android.internal.midi.MidiEventScheduler.MidiEvent;
+
import libcore.io.IoUtils;
import java.io.Closeable;
@@ -43,7 +45,7 @@
private MidiDeviceServer mServer;
- private final MidiReceiver[] mInputPortReceivers;
+ private final MidiEventScheduler mEventScheduler;
private static final int BUFFER_SIZE = 512;
@@ -100,19 +102,7 @@
for (int i = 0; i < outputCount; i++) {
mOutputStreams[i] = new FileOutputStream(fileDescriptors[i]);
}
-
- mInputPortReceivers = new MidiReceiver[inputCount];
- for (int port = 0; port < inputCount; port++) {
- final int portF = port;
- mInputPortReceivers[port] = new MidiReceiver() {
- @Override
- public void onReceive(byte[] data, int offset, int count, long timestamp)
- throws IOException {
- // FIXME - timestamps are ignored, future posting not supported yet.
- mOutputStreams[portF].write(data, offset, count);
- }
- };
- }
+ mEventScheduler = new MidiEventScheduler(inputCount);
}
private boolean register(Context context, Bundle properties) {
@@ -122,16 +112,22 @@
return false;
}
+ int inputCount = mInputStreams.length;
int outputCount = mOutputStreams.length;
- mServer = midiManager.createDeviceServer(mInputPortReceivers, outputCount,
+ MidiReceiver[] inputPortReceivers = new MidiReceiver[inputCount];
+ for (int port = 0; port < inputCount; port++) {
+ inputPortReceivers[port] = mEventScheduler.getReceiver(port);
+ }
+
+ mServer = midiManager.createDeviceServer(inputPortReceivers, outputCount,
null, null, properties, MidiDeviceInfo.TYPE_USB, null);
if (mServer == null) {
return false;
}
final MidiReceiver[] outputReceivers = mServer.getOutputPortReceivers();
- // FIXME can we only run this when we have a dispatcher that has listeners?
- new Thread() {
+ // Create input thread
+ new Thread("UsbMidiDevice input thread") {
@Override
public void run() {
byte[] buffer = new byte[BUFFER_SIZE];
@@ -161,6 +157,33 @@
} catch (ErrnoException e) {
Log.d(TAG, "reader thread exiting");
}
+ Log.d(TAG, "input thread exit");
+ }
+ }.start();
+
+ // Create output thread
+ new Thread("UsbMidiDevice output thread") {
+ @Override
+ public void run() {
+ while (true) {
+ MidiEvent event;
+ try {
+ event = (MidiEvent)mEventScheduler.waitNextEvent();
+ } catch (InterruptedException e) {
+ // try again
+ continue;
+ }
+ if (event == null) {
+ break;
+ }
+ try {
+ mOutputStreams[event.portNumber].write(event.data, 0, event.count);
+ } catch (IOException e) {
+ Log.e(TAG, "write failed for port " + event.portNumber);
+ }
+ mEventScheduler.addEventToPool(event);
+ }
+ Log.d(TAG, "output thread exit");
}
}.start();
@@ -169,6 +192,8 @@
@Override
public void close() throws IOException {
+ mEventScheduler.close();
+
if (mServer != null) {
mServer.close();
}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 386b6aa..6bc6de63 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -258,11 +258,19 @@
* <p>
* Output: nothing.
*/
- @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ @SdkConstant(SdkConstantType.SERVICE_ACTION)
public static final String ACTION_RESPOND_VIA_MESSAGE =
"android.intent.action.RESPOND_VIA_MESSAGE";
/**
+ * The emergency dialer may choose to present activities with intent filters for this
+ * action as emergency assistance buttons that launch the activity when clicked.
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_EMERGENCY_ASSISTANCE =
+ "android.telephony.action.EMERGENCY_ASSISTANCE";
+
+ /**
* The lookup key used with the {@link #ACTION_PHONE_STATE_CHANGED} broadcast
* for a String containing the new call state.
*
@@ -3308,11 +3316,11 @@
* @return the preferred network type, defined in RILConstants.java.
* @hide
*/
- public int getPreferredNetworkType() {
+ public int getPreferredNetworkType(int subId) {
try {
ITelephony telephony = getITelephony();
if (telephony != null)
- return telephony.getPreferredNetworkType();
+ return telephony.getPreferredNetworkType(subId);
} catch (RemoteException ex) {
Rlog.e(TAG, "getPreferredNetworkType RemoteException", ex);
} catch (NullPointerException ex) {
@@ -3322,6 +3330,27 @@
}
/**
+ * Sets the network selection mode to automatic.
+ * <p>
+ * Requires Permission:
+ * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
+ * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+ *
+ * @hide
+ */
+ public void setNetworkSelectionModeAutomatic(int subId) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null)
+ telephony.setNetworkSelectionModeAutomatic(subId);
+ } catch (RemoteException ex) {
+ Rlog.e(TAG, "setNetworkSelectionModeAutomatic RemoteException", ex);
+ } catch (NullPointerException ex) {
+ Rlog.e(TAG, "setNetworkSelectionModeAutomatic NPE", ex);
+ }
+ }
+
+ /**
* Set the preferred network type.
* Used for device configuration by some CDMA operators.
* <p>
@@ -3329,15 +3358,16 @@
* {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
* Or the calling app has carrier privileges. @see #hasCarrierPrivileges
*
+ * @param subId the id of the subscription to set the preferred network type for.
* @param networkType the preferred network type, defined in RILConstants.java.
* @return true on success; false on any failure.
* @hide
*/
- public boolean setPreferredNetworkType(int networkType) {
+ public boolean setPreferredNetworkType(int subId, int networkType) {
try {
ITelephony telephony = getITelephony();
if (telephony != null)
- return telephony.setPreferredNetworkType(networkType);
+ return telephony.setPreferredNetworkType(subId, networkType);
} catch (RemoteException ex) {
Rlog.e(TAG, "setPreferredNetworkType RemoteException", ex);
} catch (NullPointerException ex) {
@@ -3356,7 +3386,8 @@
* @return true on success; false on any failure.
*/
public boolean setPreferredNetworkTypeToGlobal() {
- return setPreferredNetworkType(RILConstants.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA);
+ return setPreferredNetworkType(getDefaultSubscription(),
+ RILConstants.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA);
}
/**
@@ -4440,4 +4471,22 @@
return retval;
}
+
+ /**
+ * Resets telephony manager settings back to factory defaults.
+ *
+ * @hide
+ */
+ public void factoryReset(int subId) {
+ if (SubscriptionManager.isUsableSubIdValue(subId)) {
+ // Enable data
+ setDataEnabled(subId, true);
+ // Set network selection mode to automatic
+ setNetworkSelectionModeAutomatic(subId);
+ // Set preferred mobile network type to the best available
+ setPreferredNetworkType(subId, RILConstants.PREFERRED_NETWORK_MODE);
+ // Turn off roaming
+ SubscriptionManager.from(mContext).setDataRoaming(0, subId);
+ }
+ }
}
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index c18e3b6..a24859b 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -653,9 +653,10 @@
* Get the preferred network type.
* Used for device configuration by some CDMA operators.
*
+ * @param subId the id of the subscription to query.
* @return the preferred network type, defined in RILConstants.java.
*/
- int getPreferredNetworkType();
+ int getPreferredNetworkType(int subId);
/**
* Check TETHER_DUN_REQUIRED and TETHER_DUN_APN settings, net.tethering.noprovisioning
@@ -667,13 +668,21 @@
int getTetherApnRequired();
/**
+ * Set the network selection mode to automatic.
+ *
+ * @param subId the id of the subscription to update.
+ */
+ void setNetworkSelectionModeAutomatic(int subId);
+
+ /**
* Set the preferred network type.
* Used for device configuration by some CDMA operators.
*
+ * @param subId the id of the subscription to update.
* @param networkType the preferred network type, defined in RILConstants.java.
* @return true on success; false on any failure.
*/
- boolean setPreferredNetworkType(int networkType);
+ boolean setPreferredNetworkType(int subId, int networkType);
/**
* User enable/disable Mobile Data.
diff --git a/test-runner/src/android/test/mock/MockPackageManager.java b/test-runner/src/android/test/mock/MockPackageManager.java
index 67a8c2b..a204376 100644
--- a/test-runner/src/android/test/mock/MockPackageManager.java
+++ b/test-runner/src/android/test/mock/MockPackageManager.java
@@ -31,6 +31,7 @@
import android.content.pm.IPackageMoveObserver;
import android.content.pm.IPackageStatsObserver;
import android.content.pm.InstrumentationInfo;
+import android.content.pm.IntentFilterVerificationInfo;
import android.content.pm.KeySet;
import android.content.pm.ManifestDigest;
import android.content.pm.PackageInfo;
@@ -725,6 +726,38 @@
* @hide
*/
@Override
+ public void verifyIntentFilter(int id, int verificationCode, List<String> outFailedDomains) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ public int getIntentVerificationStatus(String packageName, int userId) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ public boolean updateIntentVerificationStatus(String packageName, int status, int userId) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ public List<IntentFilterVerificationInfo> getIntentFilterVerifications(String packageName) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * @hide
+ */
+ @Override
public VerifierDeviceIdentity getVerifierDeviceIdentity() {
throw new UnsupportedOperationException();
}
diff --git a/tools/apilint/apilint.py b/tools/apilint/apilint.py
index 5c6d870..72ee343 100644
--- a/tools/apilint/apilint.py
+++ b/tools/apilint/apilint.py
@@ -26,14 +26,17 @@
$ apilint.py /tmp/currentblame.txt previous.txt --no-color
"""
-import re, sys, collections, traceback
+import re, sys, collections, traceback, argparse
BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = range(8)
+ALLOW_GOOGLE = False
+USE_COLOR = True
+
def format(fg=None, bg=None, bright=False, bold=False, dim=False, reset=False):
# manually derived from http://en.wikipedia.org/wiki/ANSI_escape_code#Codes
- if "--no-color" in sys.argv: return ""
+ if not USE_COLOR: return ""
codes = []
if reset: codes.append("0")
else:
@@ -976,7 +979,7 @@
verify_collections(clazz)
verify_flags(clazz)
verify_exception(clazz)
- verify_google(clazz)
+ if not ALLOW_GOOGLE: verify_google(clazz)
verify_bitset(clazz)
verify_manager(clazz)
verify_boxed(clazz)
@@ -1061,11 +1064,30 @@
if __name__ == "__main__":
- with open(sys.argv[1]) as f:
- cur_fail = examine_stream(f)
+ parser = argparse.ArgumentParser(description="Enforces common Android public API design \
+ patterns. It ignores lint messages from a previous API level, if provided.")
+ parser.add_argument("current.txt", type=argparse.FileType('r'), help="current.txt")
+ parser.add_argument("previous.txt", nargs='?', type=argparse.FileType('r'), default=None,
+ help="previous.txt")
+ parser.add_argument("--no-color", action='store_const', const=True,
+ help="Disable terminal colors")
+ parser.add_argument("--allow-google", action='store_const', const=True,
+ help="Allow references to Google")
+ args = vars(parser.parse_args())
- if len(sys.argv) > 2:
- with open(sys.argv[2]) as f:
+ if args['no_color']:
+ USE_COLOR = False
+
+ if args['allow_google']:
+ ALLOW_GOOGLE = True
+
+ current_file = args['current.txt']
+ previous_file = args['previous.txt']
+
+ with current_file as f:
+ cur_fail = examine_stream(f)
+ if not previous_file is None:
+ with previous_file as f:
prev_fail = examine_stream(f)
# ignore errors from previous API level
diff --git a/tools/layoutlib/.idea/libraries/guava.xml b/tools/layoutlib/.idea/libraries/guava.xml
index d47fc06..eb60719 100644
--- a/tools/layoutlib/.idea/libraries/guava.xml
+++ b/tools/layoutlib/.idea/libraries/guava.xml
@@ -1,11 +1,11 @@
<component name="libraryTable">
<library name="guava">
<CLASSES>
- <root url="jar://$PROJECT_DIR$/../../../../out/host/common/obj/JAVA_LIBRARIES/guavalib_intermediates/javalib.jar!/" />
+ <root url="jar://$PROJECT_DIR$/../../../../prebuilts/tools/common/m2/repository/com/google/guava/guava/15.0/guava-15.0.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
- <root url="file://$PROJECT_DIR$/../../../../external/guava/guava/src" />
+ <root url="jar://$PROJECT_DIR$/../../../../prebuilts/tools/common/m2/repository/com/google/guava/guava/15.0/guava-15.0-sources.jar!/" />
</SOURCES>
</library>
</component>
\ No newline at end of file
diff --git a/tools/layoutlib/.idea/runConfigurations/All_in_bridge.xml b/tools/layoutlib/.idea/runConfigurations/All_in_bridge.xml
index d97d82c..0b22717 100644
--- a/tools/layoutlib/.idea/runConfigurations/All_in_bridge.xml
+++ b/tools/layoutlib/.idea/runConfigurations/All_in_bridge.xml
@@ -2,8 +2,8 @@
<configuration default="false" name="All in bridge" type="JUnit" factoryName="JUnit" singleton="true">
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
<module name="bridge" />
- <option name="ALTERNATIVE_JRE_PATH_ENABLED" value="true" />
- <option name="ALTERNATIVE_JRE_PATH" value="1.7" />
+ <option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
+ <option name="ALTERNATIVE_JRE_PATH" value="" />
<option name="PACKAGE_NAME" value="" />
<option name="MAIN_CLASS_NAME" value="" />
<option name="METHOD_NAME" value="" />
diff --git a/tools/layoutlib/bridge/src/android/text/GreedyLineBreaker.java b/tools/layoutlib/bridge/src/android/text/GreedyLineBreaker.java
index 24e4b54..c72efc2 100644
--- a/tools/layoutlib/bridge/src/android/text/GreedyLineBreaker.java
+++ b/tools/layoutlib/bridge/src/android/text/GreedyLineBreaker.java
@@ -166,7 +166,7 @@
if (lineBreaks.breaks.length != mBreaksList.size()) {
lineBreaks.breaks = new int[mBreaksList.size()];
lineBreaks.widths = new float[mWidthsList.size()];
- lineBreaks.flags = new boolean[mFlagsList.size()];
+ lineBreaks.flags = new int[mFlagsList.size()];
}
int i = 0;
@@ -181,7 +181,7 @@
}
i = 0;
for (boolean b : mFlagsList) {
- lineBreaks.flags[i] = b;
+ lineBreaks.flags[i] = b ? TAB_MASK : 0;
i++;
}
diff --git a/tools/layoutlib/bridge/src/android/text/LineBreaker.java b/tools/layoutlib/bridge/src/android/text/LineBreaker.java
index 8be3635..54445a42 100644
--- a/tools/layoutlib/bridge/src/android/text/LineBreaker.java
+++ b/tools/layoutlib/bridge/src/android/text/LineBreaker.java
@@ -26,6 +26,8 @@
// frameworks/base/core/jni/android_text_StaticLayout.cpp revision b808260
public abstract class LineBreaker {
+ protected static final int TAB_MASK = 0x20000000; // keep in sync with StaticLayout
+
protected final @NonNull List<Primitive> mPrimitives;
protected final @NonNull LineWidth mLineWidth;
protected final @NonNull TabStops mTabStops;
diff --git a/tools/layoutlib/bridge/src/android/text/OptimizingLineBreaker.java b/tools/layoutlib/bridge/src/android/text/OptimizingLineBreaker.java
index d5d7798..cd92581 100644
--- a/tools/layoutlib/bridge/src/android/text/OptimizingLineBreaker.java
+++ b/tools/layoutlib/bridge/src/android/text/OptimizingLineBreaker.java
@@ -51,7 +51,7 @@
assert p.type == PrimitiveType.PENALTY;
breakInfo.breaks = new int[]{0};
breakInfo.widths = new float[]{p.width};
- breakInfo.flags = new boolean[]{false};
+ breakInfo.flags = new int[]{0};
return;
}
Node[] opt = new Node[numBreaks];
@@ -129,7 +129,7 @@
breakInfo.breaks[count] = mPrimitives.get(idx).location;
breakInfo.widths[count] = opt[idx].mWidth;
- breakInfo.flags [count] = opt[idx].mHasTabs;
+ breakInfo.flags [count] = opt[idx].mHasTabs ? TAB_MASK : 0;
idx = opt[idx].mPrev;
}
}
@@ -140,7 +140,7 @@
}
int[] breaks = new int[size];
float[] widths = new float[size];
- boolean[] flags = new boolean[size];
+ int[] flags = new int[size];
int toCopy = Math.min(size, lineBreaks.breaks.length);
System.arraycopy(lineBreaks.breaks, 0, breaks, 0, toCopy);
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build.gradle b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build.gradle
index 491dee8..0f37fce 100644
--- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build.gradle
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build.gradle
@@ -34,6 +34,10 @@
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_6
+ targetCompatibility JavaVersion.VERSION_1_6
+ }
}
dependencies {
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/androidTest/debug/com/android/layoutlib/test/myapplication/test/BuildConfig.class b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/androidTest/debug/com/android/layoutlib/test/myapplication/test/BuildConfig.class
new file mode 100644
index 0000000..1ca7e01
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/androidTest/debug/com/android/layoutlib/test/myapplication/test/BuildConfig.class
Binary files differ
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/BuildConfig.class b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/BuildConfig.class
index e29e490..ceb56bf 100644
--- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/BuildConfig.class
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/BuildConfig.class
Binary files differ
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/CustomCalendar.class b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/CustomCalendar.class
index 4ae0da7..c363055 100644
--- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/CustomCalendar.class
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/CustomCalendar.class
Binary files differ
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/CustomDate.class b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/CustomDate.class
index 6729eb4..edda3de 100644
--- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/CustomDate.class
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/CustomDate.class
Binary files differ
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/MyActivity.class b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/MyActivity.class
index 985d267..d252462 100644
--- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/MyActivity.class
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/MyActivity.class
Binary files differ
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$attr.class b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$attr.class
index 5142ca6..9bab801 100644
--- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$attr.class
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$attr.class
Binary files differ
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$dimen.class b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$dimen.class
index cb52ba5..7ad8605 100644
--- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$dimen.class
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$dimen.class
Binary files differ
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$drawable.class b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$drawable.class
index 5290cf6..e9e0a33 100644
--- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$drawable.class
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$drawable.class
Binary files differ
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$id.class b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$id.class
index 49b1df6..d109302 100644
--- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$id.class
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$id.class
Binary files differ
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$layout.class b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$layout.class
index 85b2029..816ecc8 100644
--- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$layout.class
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$layout.class
Binary files differ
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$menu.class b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$menu.class
index 428fdf4..b034b75 100644
--- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$menu.class
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$menu.class
Binary files differ
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$string.class b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$string.class
index 027d5d3..f86b1d3 100644
--- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$string.class
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$string.class
Binary files differ
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$style.class b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$style.class
index c7d64f8..8bbae90 100644
--- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$style.class
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$style.class
Binary files differ
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R.class b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R.class
index 8831b71..8af745d 100644
--- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R.class
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R.class
Binary files differ
diff --git a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/ImageUtils.java b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/ImageUtils.java
index e13ad72..d7e5486 100644
--- a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/ImageUtils.java
+++ b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/ImageUtils.java
@@ -56,7 +56,7 @@
private static final int THUMBNAIL_SIZE = 250;
- private static final double MAX_PERCENT_DIFFERENCE = 0.1;
+ private static final double MAX_PERCENT_DIFFERENCE = 0.3;
public static void requireSimilar(@NonNull String relativePath, @NonNull BufferedImage image)
throws IOException {
diff --git a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.java b/wifi/java/android/net/wifi/WifiActivityEnergyInfo.java
index 6263463..9284796 100644
--- a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.java
+++ b/wifi/java/android/net/wifi/WifiActivityEnergyInfo.java
@@ -26,12 +26,35 @@
* @hide
*/
public final class WifiActivityEnergyInfo implements Parcelable {
- private final long mTimestamp;
- private final int mStackState;
- private final int mControllerTxTimeMs;
- private final int mControllerRxTimeMs;
- private final int mControllerIdleTimeMs;
- private final int mControllerEnergyUsed;
+ /**
+ * @hide
+ */
+ public long mTimestamp;
+
+ /**
+ * @hide
+ */
+ public int mStackState;
+
+ /**
+ * @hide
+ */
+ public int mControllerTxTimeMs;
+
+ /**
+ * @hide
+ */
+ public int mControllerRxTimeMs;
+
+ /**
+ * @hide
+ */
+ public int mControllerIdleTimeMs;
+
+ /**
+ * @hide
+ */
+ public int mControllerEnergyUsed;
public static final int STACK_STATE_INVALID = 0;
public static final int STACK_STATE_STATE_ACTIVE = 1;
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index e1460ef..6371891 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -2621,4 +2621,25 @@
}
return false;
}
+
+ /**
+ * Resets all wifi manager settings back to factory defaults.
+ *
+ * @hide
+ */
+ public void factoryReset() {
+ // Enable wifi
+ setWifiEnabled(true);
+ // Delete all Wifi SSIDs
+ List<WifiConfiguration> networks = getConfiguredNetworks();
+ if (networks != null) {
+ for (WifiConfiguration config : networks) {
+ removeNetwork(config.networkId);
+ }
+ saveConfiguration();
+ }
+
+ // Turn mobile hotspot off
+ setWifiApEnabled(null, false);
+ }
}