Merge "Refactoring recents search bar widget logic." into mnc-dev
diff --git a/api/current.txt b/api/current.txt
index 8d70a1e..d951591 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -4729,7 +4729,6 @@
method public android.graphics.drawable.Icon getLargeIcon();
method public android.graphics.drawable.Icon getSmallIcon();
method public java.lang.String getSortKey();
- method public deprecated void setLatestEventInfo(android.content.Context, java.lang.CharSequence, java.lang.CharSequence, android.app.PendingIntent);
method public void writeToParcel(android.os.Parcel, int);
field public static final android.media.AudioAttributes AUDIO_ATTRIBUTES_DEFAULT;
field public static final java.lang.String CATEGORY_ALARM = "alarm";
@@ -30539,11 +30538,13 @@
field public static final java.lang.String KEY_ALLOW_LOCAL_DTMF_TONES_BOOL = "allow_local_dtmf_tones_bool";
field public static final java.lang.String KEY_APN_EXPAND_BOOL = "apn_expand_bool";
field public static final java.lang.String KEY_AUTO_RETRY_ENABLED_BOOL = "auto_retry_enabled_bool";
+ field public static final java.lang.String KEY_CARRIER_ALLOW_TURNOFF_IMS_BOOL = "carrier_allow_turnoff_ims_bool";
field public static final java.lang.String KEY_CARRIER_SETTINGS_ENABLE_BOOL = "carrier_settings_enable_bool";
field public static final java.lang.String KEY_CARRIER_VOLTE_AVAILABLE_BOOL = "carrier_volte_available_bool";
- field public static final java.lang.String KEY_CARRIER_VOLTE_PROVISIONED_BOOL = "carrier_volte_provisioned_bool";
+ field public static final java.lang.String KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL = "carrier_volte_provisioning_required_bool";
field public static final java.lang.String KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL = "carrier_volte_tty_supported_bool";
field public static final java.lang.String KEY_CARRIER_VVM_PACKAGE_NAME_STRING = "carrier_vvm_package_name_string";
+ field public static final java.lang.String KEY_CARRIER_VT_AVAILABLE_BOOL = "carrier_vt_available_bool";
field public static final java.lang.String KEY_CARRIER_WFC_IMS_AVAILABLE_BOOL = "carrier_wfc_ims_available_bool";
field public static final java.lang.String KEY_CDMA_NONROAMING_NETWORKS_STRING_ARRAY = "cdma_nonroaming_networks_string_array";
field public static final java.lang.String KEY_CDMA_ROAMING_NETWORKS_STRING_ARRAY = "cdma_roaming_networks_string_array";
diff --git a/api/removed.txt b/api/removed.txt
index 7fc927b..6d88cb6 100644
--- a/api/removed.txt
+++ b/api/removed.txt
@@ -1,3 +1,11 @@
+package android.app {
+
+ public class Notification implements android.os.Parcelable {
+ method public deprecated void setLatestEventInfo(android.content.Context, java.lang.CharSequence, java.lang.CharSequence, android.app.PendingIntent);
+ }
+
+}
+
package android.content.pm {
public class PackageInfo implements android.os.Parcelable {
diff --git a/api/system-current.txt b/api/system-current.txt
index 5a08bc3..78e0a16 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -4831,7 +4831,6 @@
method public android.graphics.drawable.Icon getLargeIcon();
method public android.graphics.drawable.Icon getSmallIcon();
method public java.lang.String getSortKey();
- method public deprecated void setLatestEventInfo(android.content.Context, java.lang.CharSequence, java.lang.CharSequence, android.app.PendingIntent);
method public void writeToParcel(android.os.Parcel, int);
field public static final android.media.AudioAttributes AUDIO_ATTRIBUTES_DEFAULT;
field public static final java.lang.String CATEGORY_ALARM = "alarm";
@@ -32762,11 +32761,13 @@
field public static final java.lang.String KEY_ALLOW_LOCAL_DTMF_TONES_BOOL = "allow_local_dtmf_tones_bool";
field public static final java.lang.String KEY_APN_EXPAND_BOOL = "apn_expand_bool";
field public static final java.lang.String KEY_AUTO_RETRY_ENABLED_BOOL = "auto_retry_enabled_bool";
+ field public static final java.lang.String KEY_CARRIER_ALLOW_TURNOFF_IMS_BOOL = "carrier_allow_turnoff_ims_bool";
field public static final java.lang.String KEY_CARRIER_SETTINGS_ENABLE_BOOL = "carrier_settings_enable_bool";
field public static final java.lang.String KEY_CARRIER_VOLTE_AVAILABLE_BOOL = "carrier_volte_available_bool";
- field public static final java.lang.String KEY_CARRIER_VOLTE_PROVISIONED_BOOL = "carrier_volte_provisioned_bool";
+ field public static final java.lang.String KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL = "carrier_volte_provisioning_required_bool";
field public static final java.lang.String KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL = "carrier_volte_tty_supported_bool";
field public static final java.lang.String KEY_CARRIER_VVM_PACKAGE_NAME_STRING = "carrier_vvm_package_name_string";
+ field public static final java.lang.String KEY_CARRIER_VT_AVAILABLE_BOOL = "carrier_vt_available_bool";
field public static final java.lang.String KEY_CARRIER_WFC_IMS_AVAILABLE_BOOL = "carrier_wfc_ims_available_bool";
field public static final java.lang.String KEY_CDMA_NONROAMING_NETWORKS_STRING_ARRAY = "cdma_nonroaming_networks_string_array";
field public static final java.lang.String KEY_CDMA_ROAMING_NETWORKS_STRING_ARRAY = "cdma_roaming_networks_string_array";
diff --git a/api/system-removed.txt b/api/system-removed.txt
index 7fc927b..6d88cb6 100644
--- a/api/system-removed.txt
+++ b/api/system-removed.txt
@@ -1,3 +1,11 @@
+package android.app {
+
+ public class Notification implements android.os.Parcelable {
+ method public deprecated void setLatestEventInfo(android.content.Context, java.lang.CharSequence, java.lang.CharSequence, android.app.PendingIntent);
+ }
+
+}
+
package android.content.pm {
public class PackageInfo implements android.os.Parcelable {
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index 69ba27c..13fda59 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -1688,7 +1688,7 @@
private void runPackageImportance() throws Exception {
String packageName = nextArgRequired();
try {
- int procState = mAm.getPackageProcessState(packageName);
+ int procState = mAm.getPackageProcessState(packageName, "com.android.shell");
System.out.println(
ActivityManager.RunningAppProcessInfo.procStateToImportance(procState));
} catch (RemoteException e) {
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index b65593d..9ca206a 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -2345,7 +2345,8 @@
@SystemApi
public int getPackageImportance(String packageName) {
try {
- int procState = ActivityManagerNative.getDefault().getPackageProcessState(packageName);
+ int procState = ActivityManagerNative.getDefault().getPackageProcessState(packageName,
+ mContext.getOpPackageName());
return RunningAppProcessInfo.procStateToImportance(procState);
} catch (RemoteException e) {
return RunningAppProcessInfo.IMPORTANCE_GONE;
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 680feae..3035e3d 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -2539,7 +2539,8 @@
case GET_PACKAGE_PROCESS_STATE_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
String pkg = data.readString();
- int res = getPackageProcessState(pkg);
+ String callingPackage = data.readString();
+ int res = getPackageProcessState(pkg, callingPackage);
reply.writeNoException();
reply.writeInt(res);
return true;
@@ -5868,11 +5869,13 @@
}
@Override
- public int getPackageProcessState(String packageName) throws RemoteException {
+ public int getPackageProcessState(String packageName, String callingPackage)
+ throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeString(packageName);
+ data.writeString(callingPackage);
mRemote.transact(GET_PACKAGE_PROCESS_STATE_TRANSACTION, data, reply, 0);
reply.readException();
int res = reply.readInt();
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index e7f7e13..0328708 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -503,7 +503,8 @@
public void updateLockTaskPackages(int userId, String[] packages) throws RemoteException;
public void updateDeviceOwner(String packageName) throws RemoteException;
- public int getPackageProcessState(String packageName) throws RemoteException;
+ public int getPackageProcessState(String packageName, String callingPackage)
+ throws RemoteException;
public boolean setProcessMemoryTrimLevel(String process, int uid, int level)
throws RemoteException;
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index aa68345..c3dece8 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -1749,6 +1749,7 @@
* Stack</a> document.
*
* @deprecated Use {@link Builder} instead.
+ * @removed
*/
@Deprecated
public void setLatestEventInfo(Context context,
diff --git a/core/java/android/content/IntentFilter.java b/core/java/android/content/IntentFilter.java
index d83dfc5..e267b52 100644
--- a/core/java/android/content/IntentFilter.java
+++ b/core/java/android/content/IntentFilter.java
@@ -16,7 +16,6 @@
package android.content;
-import android.content.pm.PackageParser;
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
@@ -534,13 +533,15 @@
*/
public final boolean handleAllWebDataURI() {
return hasCategory(Intent.CATEGORY_APP_BROWSER) ||
- (hasWebDataURI() && countDataAuthorities() == 0);
+ (hasOnlyWebDataURI() && countDataAuthorities() == 0);
}
/**
- * Return if this filter has any HTTP or HTTPS data URI or not.
+ * Return if this filter handles only HTTP or HTTPS data URIs.
*
- * @return True if the filter has any HTTP or HTTPS data URI. False otherwise.
+ * @return True if the filter handles ACTION_VIEW/CATEGORY_BROWSABLE,
+ * has at least one HTTP or HTTPS data URI pattern defined, and does not
+ * define any non-http/https data URI patterns.
*
* 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
@@ -548,10 +549,26 @@
*
* @hide
*/
- public final boolean hasWebDataURI() {
- return hasAction(Intent.ACTION_VIEW) &&
- hasCategory(Intent.CATEGORY_BROWSABLE) &&
- (hasDataScheme(SCHEME_HTTP) || hasDataScheme(SCHEME_HTTPS));
+ public final boolean hasOnlyWebDataURI() {
+ // Require ACTION_VIEW, CATEGORY_BROWSEABLE, and at least one scheme
+ if (!hasAction(Intent.ACTION_VIEW)
+ || !hasCategory(Intent.CATEGORY_BROWSABLE)
+ || mDataSchemes == null
+ || mDataSchemes.size() == 0) {
+ return false;
+ }
+
+ // Now allow only the schemes "http" and "https"
+ final int N = mDataSchemes.size();
+ for (int i = 0; i < N; i++) {
+ final String scheme = mDataSchemes.get(i);
+ if (!SCHEME_HTTP.equals(scheme) && !SCHEME_HTTPS.equals(scheme)) {
+ return false;
+ }
+ }
+
+ // Everything passed, so it's an only-web-URIs filter
+ return true;
}
/**
@@ -568,7 +585,7 @@
* @hide
*/
public final boolean needsVerification() {
- return hasWebDataURI() && getAutoVerify();
+ return getAutoVerify() && hasOnlyWebDataURI();
}
/**
diff --git a/core/java/android/security/keymaster/ExportResult.aidl b/core/java/android/security/keymaster/ExportResult.aidl
index f522355..4d9b2de 100644
--- a/core/java/android/security/keymaster/ExportResult.aidl
+++ b/core/java/android/security/keymaster/ExportResult.aidl
@@ -1,11 +1,11 @@
-/**
- * Copyright (c) 2015, The Android Open Source Project
+/*
+ * 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
+ * http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
diff --git a/core/java/android/security/keymaster/ExportResult.java b/core/java/android/security/keymaster/ExportResult.java
index bb44c03..2b3ccbc 100644
--- a/core/java/android/security/keymaster/ExportResult.java
+++ b/core/java/android/security/keymaster/ExportResult.java
@@ -1,11 +1,11 @@
-/**
- * Copyright (c) 2015, The Android Open Source Project
+/*
+ * 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
+ * http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
diff --git a/core/java/android/security/keymaster/KeyCharacteristics.aidl b/core/java/android/security/keymaster/KeyCharacteristics.aidl
index 15014b1..be739d3 100644
--- a/core/java/android/security/keymaster/KeyCharacteristics.aidl
+++ b/core/java/android/security/keymaster/KeyCharacteristics.aidl
@@ -1,11 +1,11 @@
-/**
- * Copyright (c) 2015, The Android Open Source Project
+/*
+ * 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
+ * http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
diff --git a/core/java/android/security/keymaster/KeyCharacteristics.java b/core/java/android/security/keymaster/KeyCharacteristics.java
index 03248e5..89300d1 100644
--- a/core/java/android/security/keymaster/KeyCharacteristics.java
+++ b/core/java/android/security/keymaster/KeyCharacteristics.java
@@ -1,11 +1,11 @@
-/**
- * Copyright (c) 2015, The Android Open Source Project
+/*
+ * 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
+ * http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
@@ -19,6 +19,7 @@
import android.os.Parcel;
import android.os.Parcelable;
+import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
@@ -30,8 +31,8 @@
public KeymasterArguments swEnforced;
public KeymasterArguments hwEnforced;
- public static final Parcelable.Creator<KeyCharacteristics> CREATOR = new
- Parcelable.Creator<KeyCharacteristics>() {
+ public static final Parcelable.Creator<KeyCharacteristics> CREATOR =
+ new Parcelable.Creator<KeyCharacteristics>() {
@Override
public KeyCharacteristics createFromParcel(Parcel in) {
return new KeyCharacteristics(in);
@@ -65,73 +66,85 @@
hwEnforced = KeymasterArguments.CREATOR.createFromParcel(in);
}
- public Integer getInteger(int tag) {
+ /**
+ * Returns the value of the specified enum tag or {@code defaultValue} if the tag is not
+ * present.
+ *
+ * @throws IllegalArgumentException if {@code tag} is not an enum tag.
+ */
+ public Integer getEnum(int tag) {
if (hwEnforced.containsTag(tag)) {
- return hwEnforced.getInt(tag, -1);
+ return hwEnforced.getEnum(tag, -1);
} else if (swEnforced.containsTag(tag)) {
- return swEnforced.getInt(tag, -1);
+ return swEnforced.getEnum(tag, -1);
} else {
return null;
}
}
- public int getInt(int tag, int defaultValue) {
- Integer result = getInteger(tag);
- return (result != null) ? result : defaultValue;
- }
-
- public List<Integer> getInts(int tag) {
+ /**
+ * Returns all values of the specified repeating enum tag.
+ *
+ * throws IllegalArgumentException if {@code tag} is not a repeating enum tag.
+ */
+ public List<Integer> getEnums(int tag) {
List<Integer> result = new ArrayList<Integer>();
- result.addAll(hwEnforced.getInts(tag));
- result.addAll(swEnforced.getInts(tag));
+ result.addAll(hwEnforced.getEnums(tag));
+ result.addAll(swEnforced.getEnums(tag));
return result;
}
- public Long getLong(int tag) {
+ /**
+ * Returns the value of the specified unsigned 32-bit int tag or {@code defaultValue} if the tag
+ * is not present.
+ *
+ * @throws IllegalArgumentException if {@code tag} is not an unsigned 32-bit int tag.
+ */
+ public long getUnsignedInt(int tag, long defaultValue) {
if (hwEnforced.containsTag(tag)) {
- return hwEnforced.getLong(tag, -1);
- } else if (swEnforced.containsTag(tag)) {
- return swEnforced.getLong(tag, -1);
+ return hwEnforced.getUnsignedInt(tag, defaultValue);
} else {
- return null;
+ return swEnforced.getUnsignedInt(tag, defaultValue);
}
}
- public long getLong(int tag, long defaultValue) {
- Long result = getLong(tag);
- return (result != null) ? result : defaultValue;
- }
-
- public List<Long> getLongs(int tag) {
- List<Long> result = new ArrayList<Long>();
- result.addAll(hwEnforced.getLongs(tag));
- result.addAll(swEnforced.getLongs(tag));
+ /**
+ * Returns all values of the specified repeating unsigned 64-bit long tag.
+ *
+ * @throws IllegalArgumentException if {@code tag} is not a repeating unsigned 64-bit long tag.
+ */
+ public List<BigInteger> getUnsignedLongs(int tag) {
+ List<BigInteger> result = new ArrayList<BigInteger>();
+ result.addAll(hwEnforced.getUnsignedLongs(tag));
+ result.addAll(swEnforced.getUnsignedLongs(tag));
return result;
}
+ /**
+ * Returns the value of the specified date tag or {@code null} if the tag is not present.
+ *
+ * @throws IllegalArgumentException if {@code tag} is not a date tag or if the tag's value
+ * represents a time instant which is after {@code 2^63 - 1} milliseconds since Unix
+ * epoch.
+ */
public Date getDate(int tag) {
- Date result = hwEnforced.getDate(tag, null);
- if (result == null) {
- result = swEnforced.getDate(tag, null);
+ Date result = swEnforced.getDate(tag, null);
+ if (result != null) {
+ return result;
}
- return result;
+ return hwEnforced.getDate(tag, null);
}
- public Date getDate(int tag, Date defaultValue) {
- if (hwEnforced.containsTag(tag)) {
- return hwEnforced.getDate(tag, null);
- } else if (hwEnforced.containsTag(tag)) {
- return swEnforced.getDate(tag, null);
- } else {
- return defaultValue;
- }
- }
-
+ /**
+ * Returns {@code true} if the provided boolean tag is present, {@code false} if absent.
+ *
+ * @throws IllegalArgumentException if {@code tag} is not a boolean tag.
+ */
public boolean getBoolean(int tag) {
if (hwEnforced.containsTag(tag)) {
- return hwEnforced.getBoolean(tag, false);
+ return hwEnforced.getBoolean(tag);
} else {
- return swEnforced.getBoolean(tag, false);
+ return swEnforced.getBoolean(tag);
}
}
}
diff --git a/core/java/android/security/keymaster/KeymasterArgument.java b/core/java/android/security/keymaster/KeymasterArgument.java
index 9adde35..6ad53a4 100644
--- a/core/java/android/security/keymaster/KeymasterArgument.java
+++ b/core/java/android/security/keymaster/KeymasterArgument.java
@@ -1,11 +1,11 @@
-/**
- * Copyright (c) 2015, The Android Open Source Project
+/*
+ * 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
+ * http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
@@ -32,17 +32,18 @@
public static final Parcelable.Creator<KeymasterArgument> CREATOR = new
Parcelable.Creator<KeymasterArgument>() {
+ @Override
public KeymasterArgument createFromParcel(Parcel in) {
final int pos = in.dataPosition();
final int tag = in.readInt();
switch (KeymasterDefs.getTagType(tag)) {
case KeymasterDefs.KM_ENUM:
case KeymasterDefs.KM_ENUM_REP:
- case KeymasterDefs.KM_INT:
- case KeymasterDefs.KM_INT_REP:
+ case KeymasterDefs.KM_UINT:
+ case KeymasterDefs.KM_UINT_REP:
return new KeymasterIntArgument(tag, in);
- case KeymasterDefs.KM_LONG:
- case KeymasterDefs.KM_LONG_REP:
+ case KeymasterDefs.KM_ULONG:
+ case KeymasterDefs.KM_ULONG_REP:
return new KeymasterLongArgument(tag, in);
case KeymasterDefs.KM_DATE:
return new KeymasterDateArgument(tag, in);
@@ -55,6 +56,8 @@
throw new ParcelFormatException("Bad tag: " + tag + " at " + pos);
}
}
+
+ @Override
public KeymasterArgument[] newArray(int size) {
return new KeymasterArgument[size];
}
diff --git a/core/java/android/security/keymaster/KeymasterArguments.aidl b/core/java/android/security/keymaster/KeymasterArguments.aidl
index 7aef5a6..1a73206 100644
--- a/core/java/android/security/keymaster/KeymasterArguments.aidl
+++ b/core/java/android/security/keymaster/KeymasterArguments.aidl
@@ -1,11 +1,11 @@
-/**
- * Copyright (c) 2015, The Android Open Source Project
+/*
+ * 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
+ * http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
diff --git a/core/java/android/security/keymaster/KeymasterArguments.java b/core/java/android/security/keymaster/KeymasterArguments.java
index 363376c..e862252 100644
--- a/core/java/android/security/keymaster/KeymasterArguments.java
+++ b/core/java/android/security/keymaster/KeymasterArguments.java
@@ -1,11 +1,11 @@
-/**
- * Copyright (c) 2015, The Android Open Source Project
+/*
+ * 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
+ * http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
@@ -19,6 +19,7 @@
import android.os.Parcel;
import android.os.Parcelable;
+import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
@@ -30,7 +31,14 @@
* @hide
*/
public class KeymasterArguments implements Parcelable {
- List<KeymasterArgument> mArguments;
+
+ private static final long UINT32_RANGE = 1L << 32;
+ public static final long UINT32_MAX_VALUE = UINT32_RANGE - 1;
+
+ private static final BigInteger UINT64_RANGE = BigInteger.ONE.shiftLeft(64);
+ public static final BigInteger UINT64_MAX_VALUE = UINT64_RANGE.subtract(BigInteger.ONE);
+
+ private List<KeymasterArgument> mArguments;
public static final Parcelable.Creator<KeymasterArguments> CREATOR = new
Parcelable.Creator<KeymasterArguments>() {
@@ -53,42 +61,279 @@
mArguments = in.createTypedArrayList(KeymasterArgument.CREATOR);
}
- public void addInt(int tag, int value) {
+ /**
+ * Adds an enum tag with the provided value.
+ *
+ * @throws IllegalArgumentException if {@code tag} is not an enum tag.
+ */
+ public void addEnum(int tag, int value) {
+ int tagType = KeymasterDefs.getTagType(tag);
+ if ((tagType != KeymasterDefs.KM_ENUM) && (tagType != KeymasterDefs.KM_ENUM_REP)) {
+ throw new IllegalArgumentException("Not an enum or repeating enum tag: " + tag);
+ }
+ addEnumTag(tag, value);
+ }
+
+ /**
+ * Adds a repeated enum tag with the provided values.
+ *
+ * @throws IllegalArgumentException if {@code tag} is not a repeating enum tag.
+ */
+ public void addEnums(int tag, int... values) {
+ if (KeymasterDefs.getTagType(tag) != KeymasterDefs.KM_ENUM_REP) {
+ throw new IllegalArgumentException("Not a repeating enum tag: " + tag);
+ }
+ for (int value : values) {
+ addEnumTag(tag, value);
+ }
+ }
+
+ /**
+ * Returns the value of the specified enum tag or {@code defaultValue} if the tag is not
+ * present.
+ *
+ * @throws IllegalArgumentException if {@code tag} is not an enum tag.
+ */
+ public int getEnum(int tag, int defaultValue) {
+ if (KeymasterDefs.getTagType(tag) != KeymasterDefs.KM_ENUM) {
+ throw new IllegalArgumentException("Not an enum tag: " + tag);
+ }
+ KeymasterArgument arg = getArgumentByTag(tag);
+ if (arg == null) {
+ return defaultValue;
+ }
+ return getEnumTagValue(arg);
+ }
+
+ /**
+ * Returns all values of the specified repeating enum tag.
+ *
+ * throws IllegalArgumentException if {@code tag} is not a repeating enum tag.
+ */
+ public List<Integer> getEnums(int tag) {
+ if (KeymasterDefs.getTagType(tag) != KeymasterDefs.KM_ENUM_REP) {
+ throw new IllegalArgumentException("Not a repeating enum tag: " + tag);
+ }
+ List<Integer> values = new ArrayList<Integer>();
+ for (KeymasterArgument arg : mArguments) {
+ if (arg.tag == tag) {
+ values.add(getEnumTagValue(arg));
+ }
+ }
+ return values;
+ }
+
+ private void addEnumTag(int tag, int value) {
mArguments.add(new KeymasterIntArgument(tag, value));
}
- public void addInts(int tag, int... values) {
- for (int value : values) {
- addInt(tag, value);
- }
+ private int getEnumTagValue(KeymasterArgument arg) {
+ return ((KeymasterIntArgument) arg).value;
}
- public void addLongs(int tag, long... values) {
- for (long value : values) {
- addLong(tag, value);
+ /**
+ * Adds an unsigned 32-bit int tag with the provided value.
+ *
+ * @throws IllegalArgumentException if {@code tag} is not an unsigned 32-bit int tag or if
+ * {@code value} is outside of the permitted range [0; 2^32).
+ */
+ public void addUnsignedInt(int tag, long value) {
+ int tagType = KeymasterDefs.getTagType(tag);
+ if ((tagType != KeymasterDefs.KM_UINT) && (tagType != KeymasterDefs.KM_UINT_REP)) {
+ throw new IllegalArgumentException("Not an int or repeating int tag: " + tag);
}
+ // Keymaster's KM_UINT is unsigned 32 bit.
+ if ((value < 0) || (value > UINT32_MAX_VALUE)) {
+ throw new IllegalArgumentException("Int tag value out of range: " + value);
+ }
+ mArguments.add(new KeymasterIntArgument(tag, (int) value));
}
+ /**
+ * Returns the value of the specified unsigned 32-bit int tag or {@code defaultValue} if the tag
+ * is not present.
+ *
+ * @throws IllegalArgumentException if {@code tag} is not an unsigned 32-bit int tag.
+ */
+ public long getUnsignedInt(int tag, long defaultValue) {
+ if (KeymasterDefs.getTagType(tag) != KeymasterDefs.KM_UINT) {
+ throw new IllegalArgumentException("Not an int tag: " + tag);
+ }
+ KeymasterArgument arg = getArgumentByTag(tag);
+ if (arg == null) {
+ return defaultValue;
+ }
+ // Keymaster's KM_UINT is unsigned 32 bit.
+ return ((KeymasterIntArgument) arg).value & 0xffffffffL;
+ }
+
+ /**
+ * Adds an unsigned 64-bit long tag with the provided value.
+ *
+ * @throws IllegalArgumentException if {@code tag} is not an unsigned 64-bit long tag or if
+ * {@code value} is outside of the permitted range [0; 2^64).
+ */
+ public void addUnsignedLong(int tag, BigInteger value) {
+ int tagType = KeymasterDefs.getTagType(tag);
+ if ((tagType != KeymasterDefs.KM_ULONG) && (tagType != KeymasterDefs.KM_ULONG_REP)) {
+ throw new IllegalArgumentException("Not a long or repeating long tag: " + tag);
+ }
+ addLongTag(tag, value);
+ }
+
+ /**
+ * Returns all values of the specified repeating unsigned 64-bit long tag.
+ *
+ * @throws IllegalArgumentException if {@code tag} is not a repeating unsigned 64-bit long tag.
+ */
+ public List<BigInteger> getUnsignedLongs(int tag) {
+ if (KeymasterDefs.getTagType(tag) != KeymasterDefs.KM_ULONG_REP) {
+ throw new IllegalArgumentException("Tag is not a repeating long: " + tag);
+ }
+ List<BigInteger> values = new ArrayList<BigInteger>();
+ for (KeymasterArgument arg : mArguments) {
+ if (arg.tag == tag) {
+ values.add(getLongTagValue(arg));
+ }
+ }
+ return values;
+ }
+
+ private void addLongTag(int tag, BigInteger value) {
+ // Keymaster's KM_ULONG is unsigned 64 bit.
+ if ((value.signum() == -1) || (value.compareTo(UINT64_MAX_VALUE) > 0)) {
+ throw new IllegalArgumentException("Long tag value out of range: " + value);
+ }
+ mArguments.add(new KeymasterLongArgument(tag, value.longValue()));
+ }
+
+ private BigInteger getLongTagValue(KeymasterArgument arg) {
+ // Keymaster's KM_ULONG is unsigned 64 bit. We're forced to use BigInteger for type safety
+ // because there's no unsigned long type.
+ return toUint64(((KeymasterLongArgument) arg).value);
+ }
+
+ /**
+ * Adds the provided boolean tag. Boolean tags are considered to be set to {@code true} if
+ * present and {@code false} if absent.
+ *
+ * @throws IllegalArgumentException if {@code tag} is not a boolean tag.
+ */
public void addBoolean(int tag) {
+ if (KeymasterDefs.getTagType(tag) != KeymasterDefs.KM_BOOL) {
+ throw new IllegalArgumentException("Not a boolean tag: " + tag);
+ }
mArguments.add(new KeymasterBooleanArgument(tag));
}
- public void addLong(int tag, long value) {
- mArguments.add(new KeymasterLongArgument(tag, value));
+ /**
+ * Returns {@code true} if the provided boolean tag is present, {@code false} if absent.
+ *
+ * @throws IllegalArgumentException if {@code tag} is not a boolean tag.
+ */
+ public boolean getBoolean(int tag) {
+ if (KeymasterDefs.getTagType(tag) != KeymasterDefs.KM_BOOL) {
+ throw new IllegalArgumentException("Not a boolean tag: " + tag);
+ }
+ KeymasterArgument arg = getArgumentByTag(tag);
+ if (arg == null) {
+ return false;
+ }
+ return true;
}
- public void addBlob(int tag, byte[] value) {
+ /**
+ * Adds a bytes tag with the provided value.
+ *
+ * @throws IllegalArgumentException if {@code tag} is not a bytes tag.
+ */
+ public void addBytes(int tag, byte[] value) {
+ if (KeymasterDefs.getTagType(tag) != KeymasterDefs.KM_BYTES) {
+ throw new IllegalArgumentException("Not a bytes tag: " + tag);
+ }
+ if (value == null) {
+ throw new NullPointerException("value == nulll");
+ }
mArguments.add(new KeymasterBlobArgument(tag, value));
}
+ /**
+ * Returns the value of the specified bytes tag or {@code defaultValue} if the tag is not
+ * present.
+ *
+ * @throws IllegalArgumentException if {@code tag} is not a bytes tag.
+ */
+ public byte[] getBytes(int tag, byte[] defaultValue) {
+ if (KeymasterDefs.getTagType(tag) != KeymasterDefs.KM_BYTES) {
+ throw new IllegalArgumentException("Not a bytes tag: " + tag);
+ }
+ KeymasterArgument arg = getArgumentByTag(tag);
+ if (arg == null) {
+ return defaultValue;
+ }
+ return ((KeymasterBlobArgument) arg).blob;
+ }
+
+ /**
+ * Adds a date tag with the provided value.
+ *
+ * @throws IllegalArgumentException if {@code tag} is not a date tag or if {@code value} is
+ * before the start of Unix epoch.
+ */
public void addDate(int tag, Date value) {
+ if (KeymasterDefs.getTagType(tag) != KeymasterDefs.KM_DATE) {
+ throw new IllegalArgumentException("Not a date tag: " + tag);
+ }
+ if (value == null) {
+ throw new NullPointerException("value == nulll");
+ }
+ // Keymaster's KM_DATE is unsigned, but java.util.Date is signed, thus preventing us from
+ // using values larger than 2^63 - 1.
+ if (value.getTime() < 0) {
+ throw new IllegalArgumentException("Date tag value out of range: " + value);
+ }
mArguments.add(new KeymasterDateArgument(tag, value));
}
+ /**
+ * Adds a date tag with the provided value, if the value is not {@code null}. Does nothing if
+ * the {@code value} is null.
+ *
+ * @throws IllegalArgumentException if {@code tag} is not a date tag or if {@code value} is
+ * before the start of Unix epoch.
+ */
public void addDateIfNotNull(int tag, Date value) {
- if (value != null) {
- mArguments.add(new KeymasterDateArgument(tag, value));
+ if (KeymasterDefs.getTagType(tag) != KeymasterDefs.KM_DATE) {
+ throw new IllegalArgumentException("Not a date tag: " + tag);
}
+ if (value != null) {
+ addDate(tag, value);
+ }
+ }
+
+ /**
+ * Returns the value of the specified date tag or {@code defaultValue} if the tag is not
+ * present.
+ *
+ * @throws IllegalArgumentException if {@code tag} is not a date tag or if the tag's value
+ * represents a time instant which is after {@code 2^63 - 1} milliseconds since Unix
+ * epoch.
+ */
+ public Date getDate(int tag, Date defaultValue) {
+ if (KeymasterDefs.getTagType(tag) != KeymasterDefs.KM_DATE) {
+ throw new IllegalArgumentException("Tag is not a date type: " + tag);
+ }
+ KeymasterArgument arg = getArgumentByTag(tag);
+ if (arg == null) {
+ return defaultValue;
+ }
+ Date result = ((KeymasterDateArgument) arg).date;
+ // Keymaster's KM_DATE is unsigned, but java.util.Date is signed, thus preventing us from
+ // using values larger than 2^63 - 1.
+ if (result.getTime() < 0) {
+ throw new IllegalArgumentException("Tag value too large. Tag: " + tag);
+ }
+ return result;
}
private KeymasterArgument getArgumentByTag(int tag) {
@@ -104,107 +349,6 @@
return getArgumentByTag(tag) != null;
}
- public int getInt(int tag, int defaultValue) {
- switch (KeymasterDefs.getTagType(tag)) {
- case KeymasterDefs.KM_ENUM:
- case KeymasterDefs.KM_INT:
- break; // Accepted types
- case KeymasterDefs.KM_INT_REP:
- case KeymasterDefs.KM_ENUM_REP:
- throw new IllegalArgumentException("Repeatable tags must use getInts: " + tag);
- default:
- throw new IllegalArgumentException("Tag is not an int type: " + tag);
- }
- KeymasterArgument arg = getArgumentByTag(tag);
- if (arg == null) {
- return defaultValue;
- }
- return ((KeymasterIntArgument) arg).value;
- }
-
- public long getLong(int tag, long defaultValue) {
- switch (KeymasterDefs.getTagType(tag)) {
- case KeymasterDefs.KM_LONG:
- break; // Accepted type
- case KeymasterDefs.KM_LONG_REP:
- throw new IllegalArgumentException("Repeatable tags must use getLongs: " + tag);
- default:
- throw new IllegalArgumentException("Tag is not a long type: " + tag);
- }
- KeymasterArgument arg = getArgumentByTag(tag);
- if (arg == null) {
- return defaultValue;
- }
- return ((KeymasterLongArgument) arg).value;
- }
-
- public Date getDate(int tag, Date defaultValue) {
- if (KeymasterDefs.getTagType(tag) != KeymasterDefs.KM_DATE) {
- throw new IllegalArgumentException("Tag is not a date type: " + tag);
- }
- KeymasterArgument arg = getArgumentByTag(tag);
- if (arg == null) {
- return defaultValue;
- }
- return ((KeymasterDateArgument) arg).date;
- }
-
- public boolean getBoolean(int tag, boolean defaultValue) {
- if (KeymasterDefs.getTagType(tag) != KeymasterDefs.KM_BOOL) {
- throw new IllegalArgumentException("Tag is not a boolean type: " + tag);
- }
- KeymasterArgument arg = getArgumentByTag(tag);
- if (arg == null) {
- return defaultValue;
- }
- return true;
- }
-
- public byte[] getBlob(int tag, byte[] defaultValue) {
- switch (KeymasterDefs.getTagType(tag)) {
- case KeymasterDefs.KM_BYTES:
- case KeymasterDefs.KM_BIGNUM:
- break; // Allowed types.
- default:
- throw new IllegalArgumentException("Tag is not a blob type: " + tag);
- }
- KeymasterArgument arg = getArgumentByTag(tag);
- if (arg == null) {
- return defaultValue;
- }
- return ((KeymasterBlobArgument) arg).blob;
- }
-
- public List<Integer> getInts(int tag) {
- switch (KeymasterDefs.getTagType(tag)) {
- case KeymasterDefs.KM_INT_REP:
- case KeymasterDefs.KM_ENUM_REP:
- break; // Allowed types.
- default:
- throw new IllegalArgumentException("Tag is not a repeating type: " + tag);
- }
- List<Integer> values = new ArrayList<Integer>();
- for (KeymasterArgument arg : mArguments) {
- if (arg.tag == tag) {
- values.add(((KeymasterIntArgument) arg).value);
- }
- }
- return values;
- }
-
- public List<Long> getLongs(int tag) {
- if (KeymasterDefs.getTagType(tag) != KeymasterDefs.KM_LONG_REP) {
- throw new IllegalArgumentException("Tag is not a repeating long: " + tag);
- }
- List<Long> values = new ArrayList<Long>();
- for (KeymasterArgument arg : mArguments) {
- if (arg.tag == tag) {
- values.add(((KeymasterLongArgument) arg).value);
- }
- }
- return values;
- }
-
public int size() {
return mArguments.size();
}
@@ -222,4 +366,16 @@
public int describeContents() {
return 0;
}
+
+ /**
+ * Converts the provided value to non-negative {@link BigInteger}, treating the sign bit of the
+ * provided value as the most significant bit of the result.
+ */
+ public static BigInteger toUint64(long value) {
+ if (value >= 0) {
+ return BigInteger.valueOf(value);
+ } else {
+ return BigInteger.valueOf(value).add(UINT64_RANGE);
+ }
+ }
}
diff --git a/core/java/android/security/keymaster/KeymasterBlob.aidl b/core/java/android/security/keymaster/KeymasterBlob.aidl
index 8f70f7c..b7cd1c9 100644
--- a/core/java/android/security/keymaster/KeymasterBlob.aidl
+++ b/core/java/android/security/keymaster/KeymasterBlob.aidl
@@ -1,11 +1,11 @@
-/**
- * Copyright (c) 2015, The Android Open Source Project
+/*
+ * 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
+ * http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
diff --git a/core/java/android/security/keymaster/KeymasterBlob.java b/core/java/android/security/keymaster/KeymasterBlob.java
index cb95604..cd36870 100644
--- a/core/java/android/security/keymaster/KeymasterBlob.java
+++ b/core/java/android/security/keymaster/KeymasterBlob.java
@@ -1,11 +1,11 @@
-/**
- * Copyright (c) 2015, The Android Open Source Project
+/*
+ * 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
+ * http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
diff --git a/core/java/android/security/keymaster/KeymasterBlobArgument.java b/core/java/android/security/keymaster/KeymasterBlobArgument.java
index 7d587bf..541d33e 100644
--- a/core/java/android/security/keymaster/KeymasterBlobArgument.java
+++ b/core/java/android/security/keymaster/KeymasterBlobArgument.java
@@ -1,11 +1,11 @@
-/**
- * Copyright (c) 2015, The Android Open Source Project
+/*
+ * 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
+ * http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
diff --git a/core/java/android/security/keymaster/KeymasterBooleanArgument.java b/core/java/android/security/keymaster/KeymasterBooleanArgument.java
index 9c03674..67b3281 100644
--- a/core/java/android/security/keymaster/KeymasterBooleanArgument.java
+++ b/core/java/android/security/keymaster/KeymasterBooleanArgument.java
@@ -1,11 +1,11 @@
-/**
- * Copyright (c) 2015, The Android Open Source Project
+/*
+ * 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
+ * http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
diff --git a/core/java/android/security/keymaster/KeymasterDateArgument.java b/core/java/android/security/keymaster/KeymasterDateArgument.java
index bffd24d..aa15e34 100644
--- a/core/java/android/security/keymaster/KeymasterDateArgument.java
+++ b/core/java/android/security/keymaster/KeymasterDateArgument.java
@@ -1,11 +1,11 @@
-/**
- * Copyright (c) 2015, The Android Open Source Project
+/*
+ * 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
+ * http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
diff --git a/core/java/android/security/keymaster/KeymasterDefs.java b/core/java/android/security/keymaster/KeymasterDefs.java
index 62c28ac..bae5455 100644
--- a/core/java/android/security/keymaster/KeymasterDefs.java
+++ b/core/java/android/security/keymaster/KeymasterDefs.java
@@ -1,11 +1,11 @@
-/**
- * Copyright (c) 2015, The Android Open Source Project
+/*
+ * 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
+ * http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
@@ -33,20 +33,20 @@
public static final int KM_INVALID = 0 << 28;
public static final int KM_ENUM = 1 << 28;
public static final int KM_ENUM_REP = 2 << 28;
- public static final int KM_INT = 3 << 28;
- public static final int KM_INT_REP = 4 << 28;
- public static final int KM_LONG = 5 << 28;
+ public static final int KM_UINT = 3 << 28;
+ public static final int KM_UINT_REP = 4 << 28;
+ public static final int KM_ULONG = 5 << 28;
public static final int KM_DATE = 6 << 28;
public static final int KM_BOOL = 7 << 28;
public static final int KM_BIGNUM = 8 << 28;
public static final int KM_BYTES = 9 << 28;
- public static final int KM_LONG_REP = 10 << 28;
+ public static final int KM_ULONG_REP = 10 << 28;
// Tag values.
public static final int KM_TAG_INVALID = KM_INVALID | 0;
public static final int KM_TAG_PURPOSE = KM_ENUM_REP | 1;
public static final int KM_TAG_ALGORITHM = KM_ENUM | 2;
- public static final int KM_TAG_KEY_SIZE = KM_INT | 3;
+ public static final int KM_TAG_KEY_SIZE = KM_UINT | 3;
public static final int KM_TAG_BLOCK_MODE = KM_ENUM_REP | 4;
public static final int KM_TAG_DIGEST = KM_ENUM_REP | 5;
public static final int KM_TAG_PADDING = KM_ENUM_REP | 6;
@@ -56,19 +56,19 @@
public static final int KM_TAG_RESCOPING_DEL = KM_ENUM_REP | 102;
public static final int KM_TAG_BLOB_USAGE_REQUIREMENTS = KM_ENUM | 705;
- public static final int KM_TAG_RSA_PUBLIC_EXPONENT = KM_LONG | 200;
+ public static final int KM_TAG_RSA_PUBLIC_EXPONENT = KM_ULONG | 200;
public static final int KM_TAG_ACTIVE_DATETIME = KM_DATE | 400;
public static final int KM_TAG_ORIGINATION_EXPIRE_DATETIME = KM_DATE | 401;
public static final int KM_TAG_USAGE_EXPIRE_DATETIME = KM_DATE | 402;
- public static final int KM_TAG_MIN_SECONDS_BETWEEN_OPS = KM_INT | 403;
- public static final int KM_TAG_MAX_USES_PER_BOOT = KM_INT | 404;
+ public static final int KM_TAG_MIN_SECONDS_BETWEEN_OPS = KM_UINT | 403;
+ public static final int KM_TAG_MAX_USES_PER_BOOT = KM_UINT | 404;
public static final int KM_TAG_ALL_USERS = KM_BOOL | 500;
- public static final int KM_TAG_USER_ID = KM_INT | 501;
- public static final int KM_TAG_USER_SECURE_ID = KM_LONG_REP | 502;
+ public static final int KM_TAG_USER_ID = KM_UINT | 501;
+ public static final int KM_TAG_USER_SECURE_ID = KM_ULONG_REP | 502;
public static final int KM_TAG_NO_AUTH_REQUIRED = KM_BOOL | 503;
public static final int KM_TAG_USER_AUTH_TYPE = KM_ENUM | 504;
- public static final int KM_TAG_AUTH_TIMEOUT = KM_INT | 505;
+ public static final int KM_TAG_AUTH_TIMEOUT = KM_UINT | 505;
public static final int KM_TAG_ALL_APPLICATIONS = KM_BOOL | 600;
public static final int KM_TAG_APPLICATION_ID = KM_BYTES | 601;
@@ -82,7 +82,7 @@
public static final int KM_TAG_ASSOCIATED_DATA = KM_BYTES | 1000;
public static final int KM_TAG_NONCE = KM_BYTES | 1001;
public static final int KM_TAG_AUTH_TOKEN = KM_BYTES | 1002;
- public static final int KM_TAG_MAC_LENGTH = KM_INT | 1003;
+ public static final int KM_TAG_MAC_LENGTH = KM_UINT | 1003;
// Algorithm values.
public static final int KM_ALGORITHM_RSA = 1;
diff --git a/core/java/android/security/keymaster/KeymasterIntArgument.java b/core/java/android/security/keymaster/KeymasterIntArgument.java
index da81715..578d249 100644
--- a/core/java/android/security/keymaster/KeymasterIntArgument.java
+++ b/core/java/android/security/keymaster/KeymasterIntArgument.java
@@ -1,11 +1,11 @@
-/**
- * Copyright (c) 2015, The Android Open Source Project
+/*
+ * 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
+ * http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
@@ -27,8 +27,8 @@
public KeymasterIntArgument(int tag, int value) {
super(tag);
switch (KeymasterDefs.getTagType(tag)) {
- case KeymasterDefs.KM_INT:
- case KeymasterDefs.KM_INT_REP:
+ case KeymasterDefs.KM_UINT:
+ case KeymasterDefs.KM_UINT_REP:
case KeymasterDefs.KM_ENUM:
case KeymasterDefs.KM_ENUM_REP:
break; // OK.
diff --git a/core/java/android/security/keymaster/KeymasterLongArgument.java b/core/java/android/security/keymaster/KeymasterLongArgument.java
index eb17b7e..d3d40ba 100644
--- a/core/java/android/security/keymaster/KeymasterLongArgument.java
+++ b/core/java/android/security/keymaster/KeymasterLongArgument.java
@@ -1,11 +1,11 @@
-/**
- * Copyright (c) 2015, The Android Open Source Project
+/*
+ * 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
+ * http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
@@ -27,8 +27,8 @@
public KeymasterLongArgument(int tag, long value) {
super(tag);
switch (KeymasterDefs.getTagType(tag)) {
- case KeymasterDefs.KM_LONG:
- case KeymasterDefs.KM_LONG_REP:
+ case KeymasterDefs.KM_ULONG:
+ case KeymasterDefs.KM_ULONG_REP:
break; // OK.
default:
throw new IllegalArgumentException("Bad long tag " + tag);
diff --git a/core/java/android/security/keymaster/OperationResult.aidl b/core/java/android/security/keymaster/OperationResult.aidl
index 699e8d0..ed26c8d 100644
--- a/core/java/android/security/keymaster/OperationResult.aidl
+++ b/core/java/android/security/keymaster/OperationResult.aidl
@@ -1,11 +1,11 @@
-/**
- * Copyright (c) 2015, The Android Open Source Project
+/*
+ * 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
+ * http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
diff --git a/core/java/android/security/keymaster/OperationResult.java b/core/java/android/security/keymaster/OperationResult.java
index 30659662..4c962ec 100644
--- a/core/java/android/security/keymaster/OperationResult.java
+++ b/core/java/android/security/keymaster/OperationResult.java
@@ -1,11 +1,11 @@
-/**
- * Copyright (c) 2015, The Android Open Source Project
+/*
+ * 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
+ * http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 502a6bc..92dae2e 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -21041,9 +21041,6 @@
if (sUseBrokenMakeMeasureSpec) {
return size + mode;
} else {
- if (size < 0) {
- throw new IllegalArgumentException("Measure spec size must be >= 0");
- }
return (size & ~MODE_MASK) | (mode & MODE_MASK);
}
}
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index a916887..48e69a1 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -393,7 +393,7 @@
}
mPreserveDetachedSelection = true;
- hideControllers();
+ hideCursorAndSpanControllers();
stopTextActionMode();
mPreserveDetachedSelection = false;
mTemporaryDetach = false;
@@ -605,9 +605,9 @@
}
/**
- * Hides the insertion controller and stops text selection mode, hiding the selection controller
+ * Hides the insertion and span controllers.
*/
- void hideControllers() {
+ void hideCursorAndSpanControllers() {
hideCursorControllers();
hideSpanControllers();
}
@@ -1104,12 +1104,12 @@
// ExtractEditText goes out of focus.
final int selStart = mTextView.getSelectionStart();
final int selEnd = mTextView.getSelectionEnd();
- hideControllers();
+ hideCursorAndSpanControllers();
stopTextActionMode();
Selection.setSelection((Spannable) mTextView.getText(), selStart, selEnd);
} else {
if (mTemporaryDetach) mPreserveDetachedSelection = true;
- hideControllers();
+ hideCursorAndSpanControllers();
stopTextActionMode();
if (mTemporaryDetach) mPreserveDetachedSelection = false;
downgradeEasyCorrectionSpans();
@@ -1182,6 +1182,12 @@
mBlink.uncancel();
makeBlink();
}
+ final InputMethodManager imm = InputMethodManager.peekInstance();
+ final boolean immFullScreen = (imm != null && imm.isFullscreenMode());
+ if (mSelectionModifierCursorController != null && mTextView.hasSelection()
+ && !immFullScreen) {
+ mSelectionModifierCursorController.show();
+ }
} else {
if (mBlink != null) {
mBlink.cancel();
@@ -1190,7 +1196,10 @@
mInputContentType.enterDown = false;
}
// Order matters! Must be done before onParentLostFocus to rely on isShowingUp
- hideControllers();
+ hideCursorAndSpanControllers();
+ if (mSelectionModifierCursorController != null) {
+ mSelectionModifierCursorController.hide();
+ }
if (mSuggestionsPopupWindow != null) {
mSuggestionsPopupWindow.onParentLostFocus();
}
@@ -1913,7 +1922,7 @@
void onTouchUpEvent(MotionEvent event) {
boolean selectAllGotFocus = mSelectAllOnFocus && mTextView.didTouchFocusSelect();
- hideControllers();
+ hideCursorAndSpanControllers();
stopTextActionMode();
CharSequence text = mTextView.getText();
if (!selectAllGotFocus && text.length() > 0) {
@@ -2034,7 +2043,7 @@
if (mSuggestionsPopupWindow == null) {
mSuggestionsPopupWindow = new SuggestionsPopupWindow();
}
- hideControllers();
+ hideCursorAndSpanControllers();
stopTextActionMode();
mSuggestionsPopupWindow.show();
}
diff --git a/core/java/android/widget/FastScroller.java b/core/java/android/widget/FastScroller.java
index 3ee273c..c40289e 100644
--- a/core/java/android/widget/FastScroller.java
+++ b/core/java/android/widget/FastScroller.java
@@ -660,10 +660,11 @@
maxWidth = containerWidth - adjacent.getRight();
}
+ final int adjMaxHeight = Math.max(0, container.height());
final int adjMaxWidth = Math.max(0, maxWidth - marginLeft - marginRight);
final int widthMeasureSpec = MeasureSpec.makeMeasureSpec(adjMaxWidth, MeasureSpec.AT_MOST);
- final int heightMeasureSpec = MeasureSpec.makeSafeMeasureSpec(container.height(),
- MeasureSpec.UNSPECIFIED);
+ final int heightMeasureSpec = MeasureSpec.makeSafeMeasureSpec(
+ adjMaxHeight, MeasureSpec.UNSPECIFIED);
view.measure(widthMeasureSpec, heightMeasureSpec);
// Align to the left or right.
@@ -700,10 +701,11 @@
final Rect container = mContainerRect;
final int containerWidth = container.width();
- final int adjMaxWidth = containerWidth - marginLeft - marginRight;
+ final int adjMaxHeight = Math.max(0, container.height());
+ final int adjMaxWidth = Math.max(0, containerWidth - marginLeft - marginRight);
final int widthMeasureSpec = MeasureSpec.makeMeasureSpec(adjMaxWidth, MeasureSpec.AT_MOST);
- final int heightMeasureSpec = MeasureSpec.makeSafeMeasureSpec(container.height(),
- MeasureSpec.UNSPECIFIED);
+ final int heightMeasureSpec = MeasureSpec.makeSafeMeasureSpec(
+ adjMaxHeight, MeasureSpec.UNSPECIFIED);
preview.measure(widthMeasureSpec, heightMeasureSpec);
// Align at the vertical center, 10% from the top.
@@ -766,10 +768,11 @@
final View track = mTrackImage;
final View thumb = mThumbImage;
final Rect container = mContainerRect;
- final int maxWidth = container.width();
+ final int maxWidth = Math.max(0, container.width());
+ final int maxHeight = Math.max(0, container.height());
final int widthMeasureSpec = MeasureSpec.makeMeasureSpec(maxWidth, MeasureSpec.AT_MOST);
- final int heightMeasureSpec = MeasureSpec.makeSafeMeasureSpec(container.height(),
- MeasureSpec.UNSPECIFIED);
+ final int heightMeasureSpec = MeasureSpec.makeSafeMeasureSpec(
+ maxHeight, MeasureSpec.UNSPECIFIED);
track.measure(widthMeasureSpec, heightMeasureSpec);
final int top;
diff --git a/core/java/android/widget/RelativeLayout.java b/core/java/android/widget/RelativeLayout.java
index 339038e..dac02fa 100644
--- a/core/java/android/widget/RelativeLayout.java
+++ b/core/java/android/widget/RelativeLayout.java
@@ -763,19 +763,19 @@
}
// Figure out maximum size available to this view
- int maxAvailable = tempEnd - tempStart;
+ final int maxAvailable = tempEnd - tempStart;
if (childStart != VALUE_NOT_SET && childEnd != VALUE_NOT_SET) {
- // Constraints fixed both edges, so child must be an exact size
+ // Constraints fixed both edges, so child must be an exact size.
childSpecMode = MeasureSpec.EXACTLY;
- childSpecSize = maxAvailable;
+ childSpecSize = Math.max(0, maxAvailable);
} else {
if (childSize >= 0) {
- // Child wanted an exact size. Give as much as possible
+ // Child wanted an exact size. Give as much as possible.
childSpecMode = MeasureSpec.EXACTLY;
if (maxAvailable >= 0) {
- // We have a maxmum size in this dimension.
+ // We have a maximum size in this dimension.
childSpecSize = Math.min(maxAvailable, childSize);
} else {
// We can grow in this dimension.
@@ -783,20 +783,19 @@
}
} else if (childSize == LayoutParams.MATCH_PARENT) {
// Child wanted to be as big as possible. Give all available
- // space
+ // space.
childSpecMode = MeasureSpec.EXACTLY;
- childSpecSize = maxAvailable;
+ childSpecSize = Math.max(0, maxAvailable);
} else if (childSize == LayoutParams.WRAP_CONTENT) {
- // Child wants to wrap content. Use AT_MOST
- // to communicate available space if we know
- // our max size
+ // Child wants to wrap content. Use AT_MOST to communicate
+ // available space if we know our max size.
if (maxAvailable >= 0) {
// We have a maximum size in this dimension.
childSpecMode = MeasureSpec.AT_MOST;
childSpecSize = maxAvailable;
} else {
// We can grow in this dimension. Child can be as big as it
- // wants
+ // wants.
childSpecMode = MeasureSpec.UNSPECIFIED;
childSpecSize = 0;
}
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index e84ba99..207605e 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -5230,7 +5230,8 @@
// Phone specific code (there is no ExtractEditText on tablets).
// ExtractEditText does not call onFocus when it is displayed, and mHasSelectionOnFocus can
// not be set. Do the test here instead.
- if (this instanceof ExtractEditText && hasSelection() && mEditor != null) {
+ if (isInExtractedMode() && hasSelection() && mEditor != null
+ && mEditor.mTextActionMode == null) {
mEditor.startSelectionActionMode();
}
@@ -6363,7 +6364,7 @@
// This would stop a possible selection mode, but no such mode is started in case
// extracted mode will start. Some text is selected though, and will trigger an action mode
// in the extracted view.
- mEditor.hideControllers();
+ mEditor.hideCursorAndSpanControllers();
stopTextActionMode();
}
@@ -8192,7 +8193,7 @@
protected void onVisibilityChanged(View changedView, int visibility) {
super.onVisibilityChanged(changedView, visibility);
if (mEditor != null && visibility != VISIBLE) {
- mEditor.hideControllers();
+ mEditor.hideCursorAndSpanControllers();
stopTextActionMode();
}
}
@@ -9644,7 +9645,7 @@
// since we are doing so explicitlty by other means and these
// controllers interact with how selection behaves.
if (mEditor != null) {
- mEditor.hideControllers();
+ mEditor.hideCursorAndSpanControllers();
}
CharSequence text = getIterableTextForAccessibility();
if (Math.min(start, end) >= 0 && Math.max(start, end) <= text.length()) {
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index fe3ab9e..e5ff51c 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -74,6 +74,7 @@
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
+import java.util.Objects;
import java.util.Set;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
@@ -867,6 +868,16 @@
}
}
+ /**
+ * Check a simple match for the component of two ResolveInfos.
+ */
+ static boolean resolveInfoMatch(ResolveInfo lhs, ResolveInfo rhs) {
+ return lhs == null ? rhs == null
+ : lhs.activityInfo == null ? rhs.activityInfo == null
+ : Objects.equals(lhs.activityInfo.name, rhs.activityInfo.name)
+ && Objects.equals(lhs.activityInfo.packageName, rhs.activityInfo.packageName);
+ }
+
final class DisplayResolveInfo implements TargetInfo {
private final ResolveInfo mResolveInfo;
private final CharSequence mDisplayLabel;
@@ -1462,7 +1473,7 @@
public boolean hasResolvedTarget(ResolveInfo info) {
for (int i = 0, N = mDisplayList.size(); i < N; i++) {
- if (info.equals(mDisplayList.get(i).getResolveInfo())) {
+ if (resolveInfoMatch(info, mDisplayList.get(i).getResolveInfo())) {
return true;
}
}
diff --git a/core/java/com/android/internal/os/InstallerConnection.java b/core/java/com/android/internal/os/InstallerConnection.java
index 671bf24..dcc6a5e 100644
--- a/core/java/com/android/internal/os/InstallerConnection.java
+++ b/core/java/com/android/internal/os/InstallerConnection.java
@@ -18,6 +18,7 @@
import android.net.LocalSocket;
import android.net.LocalSocketAddress;
+import android.os.SystemClock;
import android.util.Slog;
import libcore.io.IoUtils;
import libcore.io.Streams;
@@ -206,4 +207,14 @@
}
return true;
}
+
+ public void waitForConnection() {
+ for (;;) {
+ if (execute("ping") >= 0) {
+ return;
+ }
+ Slog.w(TAG, "installd not ready");
+ SystemClock.sleep(1000);
+ }
+ }
}
diff --git a/core/java/com/android/internal/os/KernelUidCpuTimeReader.java b/core/java/com/android/internal/os/KernelUidCpuTimeReader.java
index 62926d1..41efd2c 100644
--- a/core/java/com/android/internal/os/KernelUidCpuTimeReader.java
+++ b/core/java/com/android/internal/os/KernelUidCpuTimeReader.java
@@ -51,7 +51,7 @@
private SparseLongArray mLastUserTimeUs = new SparseLongArray();
private SparseLongArray mLastSystemTimeUs = new SparseLongArray();
- private long mLastTimeRead = 0;
+ private long mLastTimeReadUs = 0;
/**
* Reads the proc file, calling into the callback with a delta of time for each UID.
@@ -60,7 +60,7 @@
* a fresh delta.
*/
public void readDelta(@Nullable Callback callback) {
- long now = SystemClock.elapsedRealtime();
+ long nowUs = SystemClock.elapsedRealtime() * 1000;
try (BufferedReader reader = new BufferedReader(new FileReader(sProcFile))) {
TextUtils.SimpleStringSplitter splitter = new TextUtils.SimpleStringSplitter(' ');
String line;
@@ -79,12 +79,12 @@
userTimeDeltaUs -= mLastUserTimeUs.valueAt(index);
systemTimeDeltaUs -= mLastSystemTimeUs.valueAt(index);
- final long timeDiffMs = (now - mLastTimeRead) * 1000;
+ final long timeDiffUs = nowUs - mLastTimeReadUs;
if (userTimeDeltaUs < 0 || systemTimeDeltaUs < 0 ||
- userTimeDeltaUs > timeDiffMs || systemTimeDeltaUs > timeDiffMs ) {
+ userTimeDeltaUs > timeDiffUs || systemTimeDeltaUs > timeDiffUs) {
StringBuilder sb = new StringBuilder("Malformed cpu data!\n");
sb.append("Time between reads: ");
- TimeUtils.formatDuration(timeDiffMs, sb);
+ TimeUtils.formatDuration(timeDiffUs / 1000, sb);
sb.append("ms\n");
sb.append("Previous times: u=");
TimeUtils.formatDuration(mLastUserTimeUs.valueAt(index) / 1000, sb);
@@ -118,7 +118,7 @@
} catch (IOException e) {
Slog.e(TAG, "Failed to read uid_cputime", e);
}
- mLastTimeRead = now;
+ mLastTimeReadUs = nowUs;
}
/**
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 971da77..06919e1 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -468,6 +468,7 @@
private static void performSystemServerDexOpt(String classPath) {
final String[] classPathElements = classPath.split(":");
final InstallerConnection installer = new InstallerConnection();
+ installer.waitForConnection();
final String instructionSet = VMRuntime.getRuntime().vmInstructionSet();
try {
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index faf926c..5e5450e 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -16,6 +16,10 @@
LOCAL_CFLAGS += -DPACKED=""
endif
+ifneq ($(ENABLE_CPUSETS),)
+ LOCAL_CFLAGS += -DENABLE_CPUSETS
+endif
+
LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES
LOCAL_CFLAGS += -DU_USING_ICU_NAMESPACE=0
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 2c35a8b..bffbab7 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -588,6 +588,8 @@
char lockProfThresholdBuf[sizeof("-Xlockprofthreshold:")-1 + PROPERTY_VALUE_MAX];
char nativeBridgeLibrary[sizeof("-XX:NativeBridge=") + PROPERTY_VALUE_MAX];
char cpuAbiListBuf[sizeof("--cpu-abilist=") + PROPERTY_VALUE_MAX];
+ char methodTraceFileBuf[sizeof("-Xmethod-trace-file:") + PROPERTY_VALUE_MAX];
+ char methodTraceFileSizeBuf[sizeof("-Xmethod-trace-file-size:") + PROPERTY_VALUE_MAX];
bool checkJni = false;
property_get("dalvik.vm.checkjni", propBuf, "");
@@ -848,6 +850,24 @@
profileMaxStackDepth,
"-Xprofile-max-stack-depth:");
+ /*
+ * Tracing options.
+ */
+ property_get("dalvik.vm.method-trace", propBuf, "false");
+ if (strcmp(propBuf, "true") == 0) {
+ addOption("-Xmethod-trace");
+ parseRuntimeOption("dalvik.vm.method-trace-file",
+ methodTraceFileBuf,
+ "-Xmethod-trace-file:");
+ parseRuntimeOption("dalvik.vm.method-trace-file-siz",
+ methodTraceFileSizeBuf,
+ "-Xmethod-trace-file-size:");
+ property_get("dalvik.vm.method-trace-stream", propBuf, "false");
+ if (strcmp(propBuf, "true") == 0) {
+ addOption("-Xmethod-trace-stream");
+ }
+ }
+
// Native bridge library. "0" means that native bridge is disabled.
property_get("ro.dalvik.vm.native.bridge", propBuf, "");
if (propBuf[0] == '\0') {
diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp
index d14fc0f..b9fd65f 100644
--- a/core/jni/android/graphics/Paint.cpp
+++ b/core/jni/android/graphics/Paint.cpp
@@ -606,7 +606,9 @@
Layout layout;
TypefaceImpl* typeface = getNativeTypeface(env, jpaint);
- MinikinUtils::doLayout(&layout, paint, bidiFlags, typeface, textArray, start, count, textLength);
+ // Only the substring is used for measurement, so no additional context is passed in. This
+ // behavior is consistent between char[] and String specializations.
+ MinikinUtils::doLayout(&layout, paint, bidiFlags, typeface, textArray + start, 0, count, count);
width = layout.getAdvance();
env->ReleaseStringChars(text, textArray);
diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp
index 2830724..ee8fb19 100644
--- a/core/jni/android_util_Process.cpp
+++ b/core/jni/android_util_Process.cpp
@@ -239,7 +239,8 @@
if (t_pri <= ANDROID_PRIORITY_AUDIO) {
int scheduler = sched_getscheduler(t_pid);
if ((scheduler == SCHED_FIFO) || (scheduler == SCHED_RR)) {
- // This task wants to stay in it's current audio group so it can keep it's budget
+ // This task wants to stay in its current audio group so it can keep its budget
+ // don't update its cpuset or cgroup
continue;
}
}
@@ -247,15 +248,33 @@
if (isDefault) {
if (t_pri >= ANDROID_PRIORITY_BACKGROUND) {
// This task wants to stay at background
+ // update its cpuset so it doesn't only run on bg core(s)
+#ifdef ENABLE_CPUSETS
+ int err = set_cpuset_policy(t_pid, sp);
+ if (err != NO_ERROR) {
+ signalExceptionForGroupError(env, -err);
+ break;
+ }
+#endif
continue;
}
}
-
- int err = set_sched_policy(t_pid, sp);
+ int err;
+#ifdef ENABLE_CPUSETS
+ // set both cpuset and cgroup for general threads
+ err = set_cpuset_policy(t_pid, sp);
if (err != NO_ERROR) {
signalExceptionForGroupError(env, -err);
break;
}
+#endif
+
+ err = set_sched_policy(t_pid, sp);
+ if (err != NO_ERROR) {
+ signalExceptionForGroupError(env, -err);
+ break;
+ }
+
}
closedir(d);
}
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 246e43a33..5c974bd 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -586,7 +586,7 @@
<!-- Title of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permgrouplab_sensors">Sensors</string>
<!-- Description of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
- <string name="permgroupdesc_sensors">access information about your vital signs and physical activity</string>
+ <string name="permgroupdesc_sensors">access sensor data about your vital signs</string>
<!-- Title for the capability of an accessibility service to retrieve window content. -->
<string name="capability_title_canRetrieveWindowContent">Retrieve window content</string>
diff --git a/docs/html/jd_collections.js b/docs/html/jd_collections.js
index e677f64..c5b0b85 100644
--- a/docs/html/jd_collections.js
+++ b/docs/html/jd_collections.js
@@ -1611,7 +1611,8 @@
"title": "",
"resources": [
"training/enterprise/work-policy-ctrl.html",
- "samples/BasicManagedProfile/index.html"
+ "samples/BasicManagedProfile/index.html",
+ "https://www.youtube.com/watch?v=j3QC6hcpy90"
]
},
"tools/performance/rendering": {
diff --git a/docs/html/support.jd b/docs/html/support.jd
index bbed7df..94d6478 100644
--- a/docs/html/support.jd
+++ b/docs/html/support.jd
@@ -8,80 +8,96 @@
<div class="wrap" style="width:940px;">
- <h1>Developer Support Resources</h1>
-<!--
-<p>A variety of support resources are available to help you report and resolve issues while you are developing apps for Android. </p>
--->
- <div style="margin: 20px 0 0;">
+<h1>Developer Support Resources</h1>
- <div class="col-8" style="margin-left:0">
- <h3 style="font-size: 14px;line-height: 21px;color: #555;text-transform: uppercase;border-bottom: 1px solid #CCC;margin: 0 0 20px;">Code-Level Support</h3>
-
-<h5>Community and Office Hours</h5>
-<p style="color:#888">
+<div style="margin: 20px 0 0;">
-<a href="https://plus.google.com/+AndroidDevelopers">Android Developers</a> on Google+<br />
-<a href="https://plus.google.com/communities/105153134372062985968">Android Development community</a> on Google+<br />
-<a href="http://groups.google.com/group/android-developers">android-developers</a> support forum<br />
-<a href="http://groups.google.com/group/android-ndk">android-ndk</a> support forum<br />
-<a href="http://groups.google.com/group/android-security-discuss">android-security-discuss</a> support forum<br />
+<div class="col-8" style="margin-left:0">
- <a href="http://webchat.freenode.net/?channels=android">#android</a>, <a href="http://webchat.freenode.net/?channels=android-dev">#android-dev</a> <span style="color:#888">(IRC via irc.freenode.net)</span><br />
-</p>
+ <h3 style="font-size: 14px;line-height: 21px;color: #555;text-transform:
+ uppercase;border-bottom: 1px solid #CCC;margin: 0 0 20px;">
+ Code-Level Support</h3>
-<p><b>
-<a target="_blank"
-href="https://helpouts.google.com/partner/ask?vertical=programming&tags=android&origin=http:%2F%2Fdeveloper.android.com%2Fsupport.html">Ask a question in Google Helpouts</a>
-</b></p>
+ <h5>Community Resources</h5>
+ <p style="color:#888">
+
+ <a href="https://plus.google.com/+AndroidDevelopers">
+ Android Developers</a> on Google+<br />
+ <a href="https://plus.google.com/communities/105153134372062985968">
+ Android Development community</a> on Google+<br />
+ <a href="http://groups.google.com/group/android-developers">
+ android-developers</a> support forum<br />
+ <a href="http://groups.google.com/group/android-ndk">
+ android-ndk</a> support forum<br />
+ <a href="http://groups.google.com/group/android-security-discuss">
+ android-security-discuss</a> support forum<br />
+
+ <a href="http://webchat.freenode.net/?channels=android">#android</a>,
+ <a href="http://webchat.freenode.net/?channels=android-dev">#android-dev</a>
+ <span style="color:#888">(IRC via irc.freenode.net)</span><br />
+ </p>
-<h5>Send Feedback</h5>
-<p>
- <a href="http://code.google.com/p/android/issues/entry?template=Developer%20Documentation">Report documentation bug</a><br />
- <a href="https://code.google.com/p/android/issues/entry?template=User%20bug%20report">Report device bug</a><br />
- <a href="https://code.google.com/p/android/issues/entry?template=Developer%20bug%20report">Report platform bug</a><br />
-</p>
-
-
- </div>
-
-
-
- <div class="col-8" style="margin-right:0">
- <h3 style="font-size: 14px;line-height: 21px;color: #555;text-transform:
-uppercase;border-bottom: 1px solid #CCC;margin: 0 0 20px;">Google Play Support</h3>
-<h5>Help center</h5>
-<p style="color:#888">
- <a href="http://support.google.com/googleplay/android-developer/">Help Center Home</a><br />
- <a href="http://support.google.com/googleplay/android-developer/bin/static.py?hl=en&page=known_issues.cs">Known Issues</a><br />
-</p>
-
-<h5 id="contact">Direct support contacts for developers</h5>
-<p style="color:#888">
- <a href=" https://support.google.com/googleplay/android-developer/troubleshooter/3049653">Registration, account issues</a><br />
- <a href="https://support.google.com/googleplay/android-developer/troubleshooter/3055196">Publishing, app distribution issues</a><br />
- <a href="https://support.google.com/googleplay/android-developer/troubleshooter/3055329">App visibility and discoverability</a><br />
- <a href="https://support.google.com/googleplay/android-developer/troubleshooter/3076003">Billing and reporting</a><br />
- <a href="http://support.google.com/googleplay/android-developer/bin/request.py?contact_type=takedown">Inappropriate apps</a><br />
- <a href="http://support.google.com/googleplay/android-developer/bin/answer.py?hl=en&answer=1085703&topic=15868&ctx=topic">Report a Google Play policy violation</a>
-</p>
-
-<h5>End-user support</h5>
-<p style="color:#888">
- <a href="http://support.google.com/googleplay/bin/request.py?contact_type=contact_policy&policy=apps">Click-to-call and email support for Google Play end users</a><br />
-</p>
-
-
-
- <h5>Payment and Merchant Issues</h5>
-
-<p style="color:#888;margin-bottom:1.5em;">
- <a href="http://support.google.com/checkout/sell/">Merchant Help Center Home<br />
- <a href="http://support.google.com/checkout/sell/bin/static.py?hl=en&page=ts.cs&ts=2472700">Issue reporting tool<br />
- <a href="https://productforums.google.com/forum/#!forum/checkout-merchant">checkout-merchant</a> support forum<br />
- <a href="http://support.google.com/googleplay/android-developer/bin/request.py?contact_type=survey">Feedback survey</a>
-</p>
+ <h5>Send Feedback</h5>
+ <p>
+ <a href="http://code.google.com/p/android/issues/entry?template=Developer%20Documentation">
+ Report documentation bug</a><br />
+ <a href="https://code.google.com/p/android/issues/entry?template=User%20bug%20report">
+ Report device bug</a><br />
+ <a href="https://code.google.com/p/android/issues/entry?template=Developer%20bug%20report">
+ Report platform bug</a><br />
+ </p>
</div>
+
+
+
+<div class="col-8" style="margin-right:0">
+ <h3 style="font-size: 14px;line-height: 21px;color: #555;text-transform:
+ uppercase;border-bottom: 1px solid #CCC;margin: 0 0 20px;">
+ Google Play Support</h3>
+
+ <h5>Help center</h5>
+ <p>
+ <a href="http://support.google.com/googleplay/android-developer/">Help Center Home</a><br />
+ </p>
+
+ <h5 id="contact">Direct support contacts for developers</h5>
+ <p>
+ <a href=" https://support.google.com/googleplay/android-developer/troubleshooter/3049653">
+ Registration, account issues</a><br />
+ <a href="https://support.google.com/googleplay/android-developer/troubleshooter/3055196">
+ Publishing, app distribution issues</a><br />
+ <a href="https://support.google.com/googleplay/android-developer/troubleshooter/3055329">
+ App visibility and discoverability</a><br />
+ <a href="https://support.google.com/googleplay/android-developer/troubleshooter/3076003">
+ Billing and reporting</a><br />
+ <a href="http://support.google.com/googleplay/android-developer/bin/request.py?contact_type=takedown">
+ Inappropriate apps</a><br />
+ <a href="http://support.google.com/googleplay/android-developer/bin/answer.py?hl=en&answer=1085703&topic=15868&ctx=topic">
+ Report a Google Play policy violation</a>
+ </p>
+
+ <h5>End-user support</h5>
+ <p>
+ <a href="http://support.google.com/googleplay/bin/request.py?contact_type=contact_policy&policy=apps">
+ Support for Google Play end users</a><br />
+ </p>
+
+
+
+ <h5>Payment and Merchant Issues</h5>
+
+ <p style="margin-bottom:1.5em;">
+ <a href="http://support.google.com/checkout/sell/">
+ Merchant Help Center Home<br />
+ <a href="http://support.google.com/googleplay/android-developer/bin/request.py?contact_type=survey">
+ Feedback survey</a>
+ </p>
+
+
</div>
+
+</div> <!-- end margin: 20px -->
+</div> <!-- end class:wrap -->
diff --git a/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java b/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java
index 2c603e2..e235a99 100644
--- a/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java
@@ -575,7 +575,7 @@
}
}
- private void mutate() {
+ void mutate() {
mTransitions = mTransitions.clone();
mStateIds = mStateIds.clone();
}
diff --git a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
index 1ae10d3..1857345 100644
--- a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
@@ -170,7 +170,7 @@
public Drawable mutate() {
if (!mMutated && super.mutate() == this) {
mAnimatedVectorState = new AnimatedVectorDrawableState(
- mAnimatedVectorState, mCallback, null);
+ mAnimatedVectorState, mCallback, mRes);
mMutated = true;
}
return this;
diff --git a/graphics/java/android/graphics/drawable/DrawableContainer.java b/graphics/java/android/graphics/drawable/DrawableContainer.java
index b344b86..1915dd7 100644
--- a/graphics/java/android/graphics/drawable/DrawableContainer.java
+++ b/graphics/java/android/graphics/drawable/DrawableContainer.java
@@ -730,7 +730,7 @@
if (origDf != null) {
mDrawableFutures = origDf.clone();
} else {
- mDrawableFutures = new SparseArray<ConstantStateFuture>(mNumChildren);
+ mDrawableFutures = new SparseArray<>(mNumChildren);
}
// Create futures for drawables with constant states. If a
@@ -823,6 +823,9 @@
final Drawable prepared = mDrawableFutures.valueAt(keyIndex).get(this);
mDrawables[index] = prepared;
mDrawableFutures.removeAt(keyIndex);
+ if (mDrawableFutures.size() == 0) {
+ mDrawableFutures = null;
+ }
return prepared;
}
}
diff --git a/graphics/java/android/graphics/drawable/LayerDrawable.java b/graphics/java/android/graphics/drawable/LayerDrawable.java
index 8373c7f..d9469d4 100644
--- a/graphics/java/android/graphics/drawable/LayerDrawable.java
+++ b/graphics/java/android/graphics/drawable/LayerDrawable.java
@@ -133,6 +133,7 @@
mLayerState.mChildren = r;
ensurePadding();
+ refreshPadding();
}
LayerDrawable() {
@@ -143,6 +144,7 @@
mLayerState = createConstantState(state, res);
if (mLayerState.mNum > 0) {
ensurePadding();
+ refreshPadding();
}
}
@@ -162,6 +164,7 @@
inflateLayers(r, parser, attrs, theme);
ensurePadding();
+ refreshPadding();
}
/**
@@ -431,6 +434,7 @@
final ChildDrawable layer = createLayer(dr);
final int index = addLayer(layer);
ensurePadding();
+ refreshChildPadding(index, layer);
return index;
}
@@ -568,6 +572,8 @@
childDrawable.mDrawable = drawable;
mLayerState.invalidateCache();
+
+ refreshChildPadding(index, childDrawable);
}
/**
@@ -1623,6 +1629,14 @@
mPaddingB = new int[N];
}
+ void refreshPadding() {
+ final int N = mLayerState.mNum;
+ final ChildDrawable[] array = mLayerState.mChildren;
+ for (int i = 0; i < N; i++) {
+ refreshChildPadding(i, array[i]);
+ }
+ }
+
@Override
public ConstantState getConstantState() {
if (mLayerState.canConstantState()) {
diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java
index 6369833..bf069d3 100644
--- a/graphics/java/android/graphics/drawable/RippleDrawable.java
+++ b/graphics/java/android/graphics/drawable/RippleDrawable.java
@@ -198,6 +198,7 @@
setColor(color);
ensurePadding();
+ refreshPadding();
updateLocalState();
}
@@ -1013,6 +1014,7 @@
if (mState.mNum > 0) {
ensurePadding();
+ refreshPadding();
}
if (res != null) {
diff --git a/graphics/java/android/graphics/drawable/StateListDrawable.java b/graphics/java/android/graphics/drawable/StateListDrawable.java
index c83af11..758410a 100644
--- a/graphics/java/android/graphics/drawable/StateListDrawable.java
+++ b/graphics/java/android/graphics/drawable/StateListDrawable.java
@@ -59,22 +59,10 @@
* @attr ref android.R.styleable#DrawableStates_state_pressed
*/
public class StateListDrawable extends DrawableContainer {
- private static final String TAG = StateListDrawable.class.getSimpleName();
+ private static final String TAG = "StateListDrawable";
private static final boolean DEBUG = false;
- /**
- * To be proper, we should have a getter for dither (and alpha, etc.)
- * so that proxy classes like this can save/restore their delegates'
- * values, but we don't have getters. Since we do have setters
- * (e.g. setDither), which this proxy forwards on, we have to have some
- * default/initial setting.
- *
- * The initial setting for dither is now true, since it almost always seems
- * to improve the quality at negligible cost.
- */
- private static final boolean DEFAULT_DITHER = true;
-
private StateListState mStateListState;
private boolean mMutated;
@@ -104,16 +92,16 @@
@Override
protected boolean onStateChange(int[] stateSet) {
+ final boolean changed = super.onStateChange(stateSet);
+
int idx = mStateListState.indexOfStateSet(stateSet);
if (DEBUG) android.util.Log.i(TAG, "onStateChange " + this + " states "
+ Arrays.toString(stateSet) + " found " + idx);
if (idx < 0) {
idx = mStateListState.indexOfStateSet(StateSet.WILD_CARD);
}
- if (selectDrawable(idx)) {
- return true;
- }
- return super.onStateChange(stateSet);
+
+ return selectDrawable(idx) || changed;
}
@Override
@@ -326,13 +314,14 @@
}
}
- private void mutate() {
+ void mutate() {
mThemeAttrs = mThemeAttrs != null ? mThemeAttrs.clone() : null;
final int[][] stateSets = new int[mStateSets.length][];
for (int i = mStateSets.length - 1; i >= 0; i--) {
stateSets[i] = mStateSets[i] != null ? mStateSets[i].clone() : null;
}
+ mStateSets = stateSets;
}
int addStateSet(int[] stateSet, Drawable drawable) {
diff --git a/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java
index 5e205bd..24cb055 100644
--- a/graphics/java/android/graphics/drawable/VectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/VectorDrawable.java
@@ -15,6 +15,7 @@
package android.graphics.drawable;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.content.res.Resources.Theme;
@@ -31,10 +32,10 @@
import android.graphics.PixelFormat;
import android.graphics.PorterDuffColorFilter;
import android.graphics.Rect;
-import android.graphics.Region;
import android.graphics.PorterDuff.Mode;
import android.util.ArrayMap;
import android.util.AttributeSet;
+import android.util.DisplayMetrics;
import android.util.LayoutDirection;
import android.util.Log;
import android.util.MathUtils;
@@ -212,13 +213,24 @@
// caching the bitmap by default is allowed.
private boolean mAllowCaching = true;
+ // Given the virtual display setup, the dpi can be different than the inflation's dpi.
+ // Therefore, we need to scale the values we got from the getDimension*().
+ private int mDpiScaledWidth = 0;
+ private int mDpiScaledHeight = 0;
+ private Insets mDpiScaleInsets = Insets.NONE;
+
public VectorDrawable() {
- mVectorState = new VectorDrawableState();
+ this(null, null);
}
- private VectorDrawable(@NonNull VectorDrawableState state) {
- mVectorState = state;
- mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode);
+ private VectorDrawable(@NonNull VectorDrawableState state, @Nullable Resources res) {
+ if (state == null) {
+ mVectorState = new VectorDrawableState();
+ } else {
+ mVectorState = state;
+ mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode);
+ }
+ updateDimensionInfo(res, false);
}
@Override
@@ -359,18 +371,66 @@
@Override
public int getIntrinsicWidth() {
- return (int) mVectorState.mVPathRenderer.mBaseWidth;
+ return mDpiScaledWidth;
}
@Override
public int getIntrinsicHeight() {
- return (int) mVectorState.mVPathRenderer.mBaseHeight;
+ return mDpiScaledHeight;
}
/** @hide */
@Override
public Insets getOpticalInsets() {
- return mVectorState.mVPathRenderer.mOpticalInsets;
+ return mDpiScaleInsets;
+ }
+
+ /*
+ * Update the VectorDrawable dimension since the res can be in different Dpi now.
+ * Basically, when a new instance is created or getDimension() is called, we should update
+ * the current VectorDrawable's dimension information.
+ * Only after updateStateFromTypedArray() is called, we should called this and update the
+ * constant state's dpi info, i.e. updateConstantStateDensity == true.
+ */
+ void updateDimensionInfo(@Nullable Resources res, boolean updateConstantStateDensity) {
+ if (res != null) {
+ final int densityDpi = res.getDisplayMetrics().densityDpi;
+ final int targetDensity = densityDpi == 0 ? DisplayMetrics.DENSITY_DEFAULT : densityDpi;
+
+ if (updateConstantStateDensity) {
+ mVectorState.mVPathRenderer.mTargetDensity = targetDensity;
+ } else {
+ final int constantStateDensity = mVectorState.mVPathRenderer.mTargetDensity;
+ if (targetDensity != constantStateDensity && constantStateDensity != 0) {
+ mDpiScaledWidth = Bitmap.scaleFromDensity(
+ (int) mVectorState.mVPathRenderer.mBaseWidth, constantStateDensity,
+ targetDensity);
+ mDpiScaledHeight = Bitmap.scaleFromDensity(
+ (int) mVectorState.mVPathRenderer.mBaseHeight,constantStateDensity,
+ targetDensity);
+ final int left = Bitmap.scaleFromDensity(
+ mVectorState.mVPathRenderer.mOpticalInsets.left, constantStateDensity,
+ targetDensity);
+ final int right = Bitmap.scaleFromDensity(
+ mVectorState.mVPathRenderer.mOpticalInsets.right, constantStateDensity,
+ targetDensity);
+ final int top = Bitmap.scaleFromDensity(
+ mVectorState.mVPathRenderer.mOpticalInsets.top, constantStateDensity,
+ targetDensity);
+ final int bottom = Bitmap.scaleFromDensity(
+ mVectorState.mVPathRenderer.mOpticalInsets.bottom, constantStateDensity,
+ targetDensity);
+ mDpiScaleInsets = Insets.of(left, top, right, bottom);
+ return;
+ }
+ }
+ }
+ // For all the other cases, like either res is null, constant state is not initialized or
+ // target density is the same as the constant state, we will just use the constant state
+ // dimensions.
+ mDpiScaledWidth = (int) mVectorState.mVPathRenderer.mBaseWidth;
+ mDpiScaledHeight = (int) mVectorState.mVPathRenderer.mBaseHeight;
+ mDpiScaleInsets = mVectorState.mVPathRenderer.mOpticalInsets;
}
@Override
@@ -393,6 +453,7 @@
try {
state.mCacheDirty = true;
updateStateFromTypedArray(a);
+ updateDimensionInfo(t.getResources(), true /* update constant state */);
} catch (XmlPullParserException e) {
throw new RuntimeException(e);
} finally {
@@ -485,6 +546,7 @@
inflateInternal(res, parser, attrs, theme);
mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode);
+ updateDimensionInfo(res, true /* update constant state */);
}
private void updateStateFromTypedArray(TypedArray a) throws XmlPullParserException {
@@ -687,7 +749,6 @@
int mCachedRootAlpha;
boolean mCachedAutoMirrored;
boolean mCacheDirty;
-
/** Temporary paint object used to draw cached bitmaps. */
Paint mTempPaint;
@@ -797,12 +858,12 @@
@Override
public Drawable newDrawable() {
- return new VectorDrawable(this);
+ return new VectorDrawable(this, null);
}
@Override
public Drawable newDrawable(Resources res) {
- return new VectorDrawable(this);
+ return new VectorDrawable(this, res);
}
@Override
@@ -847,6 +908,8 @@
int mRootAlpha = 0xFF;
String mRootName = null;
+ int mTargetDensity = DisplayMetrics.DENSITY_DEFAULT;
+
final ArrayMap<String, Object> mVGTargetsMap = new ArrayMap<String, Object>();
public VPathRenderer() {
@@ -886,6 +949,7 @@
mChangingConfigurations = copy.mChangingConfigurations;
mRootAlpha = copy.mRootAlpha;
mRootName = copy.mRootName;
+ mTargetDensity = copy.mTargetDensity;
if (copy.mRootName != null) {
mVGTargetsMap.put(copy.mRootName, this);
}
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index 6a08368..98b44dc 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -40,6 +40,7 @@
import android.security.keystore.UserNotAuthenticatedException;
import android.util.Log;
+import java.math.BigInteger;
import java.security.InvalidKeyException;
import java.util.List;
import java.util.Locale;
@@ -663,14 +664,14 @@
"Failed to obtained key characteristics",
getKeyStoreException(getKeyCharacteristicsErrorCode));
}
- List<Long> keySids =
- keyCharacteristics.getLongs(KeymasterDefs.KM_TAG_USER_SECURE_ID);
+ List<BigInteger> keySids =
+ keyCharacteristics.getUnsignedLongs(KeymasterDefs.KM_TAG_USER_SECURE_ID);
if (keySids.isEmpty()) {
// Key is not bound to any SIDs -- no amount of authentication will help here.
return new KeyPermanentlyInvalidatedException();
}
long rootSid = GateKeeper.getSecureUserId();
- if ((rootSid != 0) && (keySids.contains(Long.valueOf(rootSid)))) {
+ if ((rootSid != 0) && (keySids.contains(KeymasterArguments.toUint64(rootSid)))) {
// One of the key's SIDs is the current root SID -- user can be authenticated
// against that SID.
return new UserNotAuthenticatedException();
@@ -678,7 +679,7 @@
long fingerprintOnlySid = getFingerprintOnlySid();
if ((fingerprintOnlySid != 0)
- && (keySids.contains(Long.valueOf(fingerprintOnlySid)))) {
+ && (keySids.contains(KeymasterArguments.toUint64(fingerprintOnlySid)))) {
// One of the key's SIDs is the current fingerprint SID -- user can be
// authenticated against that SID.
return new UserNotAuthenticatedException();
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreAuthenticatedAESCipherSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreAuthenticatedAESCipherSpi.java
index f412743..6411066 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreAuthenticatedAESCipherSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreAuthenticatedAESCipherSpi.java
@@ -207,7 +207,7 @@
protected final void addAlgorithmSpecificParametersToBegin(
@NonNull KeymasterArguments keymasterArgs) {
super.addAlgorithmSpecificParametersToBegin(keymasterArgs);
- keymasterArgs.addInt(KeymasterDefs.KM_TAG_MAC_LENGTH, mTagLengthBits);
+ keymasterArgs.addUnsignedInt(KeymasterDefs.KM_TAG_MAC_LENGTH, mTagLengthBits);
}
protected final int getTagLengthBits() {
@@ -288,11 +288,11 @@
+ " practices.");
}
- keymasterArgs.addInt(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_AES);
- keymasterArgs.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE, mKeymasterBlockMode);
- keymasterArgs.addInt(KeymasterDefs.KM_TAG_PADDING, mKeymasterPadding);
+ keymasterArgs.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_AES);
+ keymasterArgs.addEnum(KeymasterDefs.KM_TAG_BLOCK_MODE, mKeymasterBlockMode);
+ keymasterArgs.addEnum(KeymasterDefs.KM_TAG_PADDING, mKeymasterPadding);
if (mIv != null) {
- keymasterArgs.addBlob(KeymasterDefs.KM_TAG_NONCE, mIv);
+ keymasterArgs.addBytes(KeymasterDefs.KM_TAG_NONCE, mIv);
}
}
@@ -302,7 +302,7 @@
mIvHasBeenUsed = true;
// NOTE: Keymaster doesn't always return an IV, even if it's used.
- byte[] returnedIv = keymasterArgs.getBlob(KeymasterDefs.KM_TAG_NONCE, null);
+ byte[] returnedIv = keymasterArgs.getBytes(KeymasterDefs.KM_TAG_NONCE, null);
if ((returnedIv != null) && (returnedIv.length == 0)) {
returnedIv = null;
}
@@ -363,8 +363,9 @@
@Override
public byte[] doFinal(byte[] input, int inputOffset, int inputLength,
- byte[] additionalEntropy) throws KeyStoreException {
- byte[] output = mDelegate.doFinal(input, inputOffset, inputLength, additionalEntropy);
+ byte[] signature, byte[] additionalEntropy) throws KeyStoreException {
+ byte[] output = mDelegate.doFinal(input, inputOffset, inputLength, signature,
+ additionalEntropy);
if (output != null) {
try {
mBufferedOutput.write(output);
@@ -406,7 +407,7 @@
@Override
public OperationResult update(byte[] input) {
KeymasterArguments keymasterArgs = new KeymasterArguments();
- keymasterArgs.addBlob(KeymasterDefs.KM_TAG_ASSOCIATED_DATA, input);
+ keymasterArgs.addBytes(KeymasterDefs.KM_TAG_ASSOCIATED_DATA, input);
// KeyStore does not reflect AAD in inputConsumed, but users of Stream rely on this
// field. We fix this discrepancy here. KeyStore.update contract is that all of AAD
@@ -425,7 +426,7 @@
}
@Override
- public OperationResult finish(byte[] additionalEntropy) {
+ public OperationResult finish(byte[] signature, byte[] additionalEntropy) {
if ((additionalEntropy != null) && (additionalEntropy.length > 0)) {
throw new ProviderException("AAD stream does not support additional entropy");
}
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreCipherSpiBase.java b/keystore/java/android/security/keystore/AndroidKeyStoreCipherSpiBase.java
index 83131ed..38cacd0 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreCipherSpiBase.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreCipherSpiBase.java
@@ -353,6 +353,7 @@
try {
output = mAdditionalAuthenticationDataStreamer.doFinal(
EmptyArray.BYTE, 0, 0,
+ null, // no signature
null // no additional entropy needed flushing AAD
);
} finally {
@@ -469,7 +470,10 @@
byte[] additionalEntropy =
KeyStoreCryptoOperationUtils.getRandomBytesToMixIntoKeystoreRng(
mRng, getAdditionalEntropyAmountForFinish());
- output = mMainDataStreamer.doFinal(input, inputOffset, inputLen, additionalEntropy);
+ output = mMainDataStreamer.doFinal(
+ input, inputOffset, inputLen,
+ null, // no signature involved
+ additionalEntropy);
} catch (KeyStoreException e) {
switch (e.getErrorCode()) {
case KeymasterDefs.KM_ERROR_INVALID_INPUT_LENGTH:
@@ -727,6 +731,21 @@
return mMainDataStreamer.getProducedOutputSizeBytes();
}
+ static String opmodeToString(int opmode) {
+ switch (opmode) {
+ case Cipher.ENCRYPT_MODE:
+ return "ENCRYPT_MODE";
+ case Cipher.DECRYPT_MODE:
+ return "DECRYPT_MODE";
+ case Cipher.WRAP_MODE:
+ return "WRAP_MODE";
+ case Cipher.UNWRAP_MODE:
+ return "UNWRAP_MODE";
+ default:
+ return String.valueOf(opmode);
+ }
+ }
+
// The methods below need to be implemented by subclasses.
/**
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreECDSASignatureSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreECDSASignatureSpi.java
index d19a766..10aab7e 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreECDSASignatureSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreECDSASignatureSpi.java
@@ -17,11 +17,16 @@
package android.security.keystore;
import android.annotation.NonNull;
+import android.os.IBinder;
import android.security.KeyStore;
+import android.security.KeyStoreException;
import android.security.keymaster.KeyCharacteristics;
import android.security.keymaster.KeymasterArguments;
import android.security.keymaster.KeymasterDefs;
+import libcore.util.EmptyArray;
+
+import java.io.ByteArrayOutputStream;
import java.security.InvalidKeyException;
import java.security.SignatureSpi;
@@ -36,6 +41,71 @@
public NONE() {
super(KeymasterDefs.KM_DIGEST_NONE);
}
+
+ @Override
+ protected KeyStoreCryptoOperationStreamer createMainDataStreamer(KeyStore keyStore,
+ IBinder operationToken) {
+ return new TruncateToFieldSizeMessageStreamer(
+ super.createMainDataStreamer(keyStore, operationToken),
+ getGroupSizeBits());
+ }
+
+ /**
+ * Streamer which buffers all input, then truncates it to field size, and then sends it into
+ * KeyStore via the provided delegate streamer.
+ */
+ private static class TruncateToFieldSizeMessageStreamer
+ implements KeyStoreCryptoOperationStreamer {
+
+ private final KeyStoreCryptoOperationStreamer mDelegate;
+ private final int mGroupSizeBits;
+ private final ByteArrayOutputStream mInputBuffer = new ByteArrayOutputStream();
+ private long mConsumedInputSizeBytes;
+
+ private TruncateToFieldSizeMessageStreamer(
+ KeyStoreCryptoOperationStreamer delegate,
+ int groupSizeBits) {
+ mDelegate = delegate;
+ mGroupSizeBits = groupSizeBits;
+ }
+
+ @Override
+ public byte[] update(byte[] input, int inputOffset, int inputLength)
+ throws KeyStoreException {
+ if (inputLength > 0) {
+ mInputBuffer.write(input, inputOffset, inputLength);
+ mConsumedInputSizeBytes += inputLength;
+ }
+ return EmptyArray.BYTE;
+ }
+
+ @Override
+ public byte[] doFinal(byte[] input, int inputOffset, int inputLength, byte[] signature,
+ byte[] additionalEntropy) throws KeyStoreException {
+ if (inputLength > 0) {
+ mConsumedInputSizeBytes += inputLength;
+ mInputBuffer.write(input, inputOffset, inputLength);
+ }
+
+ byte[] bufferedInput = mInputBuffer.toByteArray();
+ mInputBuffer.reset();
+ // Truncate input at field size (bytes)
+ return mDelegate.doFinal(bufferedInput,
+ 0,
+ Math.min(bufferedInput.length, ((mGroupSizeBits + 7) / 8)),
+ signature, additionalEntropy);
+ }
+
+ @Override
+ public long getConsumedInputSizeBytes() {
+ return mConsumedInputSizeBytes;
+ }
+
+ @Override
+ public long getProducedOutputSizeBytes() {
+ return mDelegate.getProducedOutputSizeBytes();
+ }
+ }
}
public final static class SHA1 extends AndroidKeyStoreECDSASignatureSpi {
@@ -70,7 +140,7 @@
private final int mKeymasterDigest;
- private int mGroupSizeBytes = -1;
+ private int mGroupSizeBits = -1;
AndroidKeyStoreECDSASignatureSpi(int keymasterDigest) {
mKeymasterDigest = keymasterDigest;
@@ -89,18 +159,20 @@
if (errorCode != KeyStore.NO_ERROR) {
throw getKeyStore().getInvalidKeyException(key.getAlias(), errorCode);
}
- int keySizeBits = keyCharacteristics.getInt(KeymasterDefs.KM_TAG_KEY_SIZE, -1);
+ long keySizeBits = keyCharacteristics.getUnsignedInt(KeymasterDefs.KM_TAG_KEY_SIZE, -1);
if (keySizeBits == -1) {
throw new InvalidKeyException("Size of key not known");
+ } else if (keySizeBits > Integer.MAX_VALUE) {
+ throw new InvalidKeyException("Key too large: " + keySizeBits + " bits");
}
- mGroupSizeBytes = (keySizeBits + 7) / 8;
+ mGroupSizeBits = (int) keySizeBits;
super.initKey(key);
}
@Override
protected final void resetAll() {
- mGroupSizeBytes = -1;
+ mGroupSizeBits = -1;
super.resetAll();
}
@@ -110,14 +182,21 @@
}
@Override
- protected void addAlgorithmSpecificParametersToBegin(
+ protected final void addAlgorithmSpecificParametersToBegin(
@NonNull KeymasterArguments keymasterArgs) {
- keymasterArgs.addInt(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_EC);
- keymasterArgs.addInt(KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigest);
+ keymasterArgs.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_EC);
+ keymasterArgs.addEnum(KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigest);
}
@Override
- protected int getAdditionalEntropyAmountForSign() {
- return mGroupSizeBytes;
+ protected final int getAdditionalEntropyAmountForSign() {
+ return (mGroupSizeBits + 7) / 8;
+ }
+
+ protected final int getGroupSizeBits() {
+ if (mGroupSizeBits == -1) {
+ throw new IllegalStateException("Not initialized");
+ }
+ return mGroupSizeBits;
}
}
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreHmacSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreHmacSpi.java
index f7c184c..d20e3af 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreHmacSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreHmacSpi.java
@@ -159,9 +159,9 @@
}
KeymasterArguments keymasterArgs = new KeymasterArguments();
- keymasterArgs.addInt(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_HMAC);
- keymasterArgs.addInt(KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigest);
- keymasterArgs.addInt(KeymasterDefs.KM_TAG_MAC_LENGTH, mMacSizeBits);
+ keymasterArgs.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_HMAC);
+ keymasterArgs.addEnum(KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigest);
+ keymasterArgs.addUnsignedInt(KeymasterDefs.KM_TAG_MAC_LENGTH, mMacSizeBits);
OperationResult opResult = mKeyStore.begin(
mKey.getAlias(),
@@ -234,6 +234,7 @@
try {
result = mChunkedStreamer.doFinal(
null, 0, 0,
+ null, // no signature provided -- this invocation will generate one
null // no additional entropy needed -- HMAC is deterministic
);
} catch (KeyStoreException e) {
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
index 66509e2..258133d 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
@@ -268,12 +268,12 @@
}
KeymasterArguments args = new KeymasterArguments();
- args.addInt(KeymasterDefs.KM_TAG_KEY_SIZE, mKeySizeBits);
- args.addInt(KeymasterDefs.KM_TAG_ALGORITHM, mKeymasterAlgorithm);
- args.addInts(KeymasterDefs.KM_TAG_PURPOSE, mKeymasterPurposes);
- args.addInts(KeymasterDefs.KM_TAG_BLOCK_MODE, mKeymasterBlockModes);
- args.addInts(KeymasterDefs.KM_TAG_PADDING, mKeymasterPaddings);
- args.addInts(KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigests);
+ args.addUnsignedInt(KeymasterDefs.KM_TAG_KEY_SIZE, mKeySizeBits);
+ args.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, mKeymasterAlgorithm);
+ args.addEnums(KeymasterDefs.KM_TAG_PURPOSE, mKeymasterPurposes);
+ args.addEnums(KeymasterDefs.KM_TAG_BLOCK_MODE, mKeymasterBlockModes);
+ args.addEnums(KeymasterDefs.KM_TAG_PADDING, mKeymasterPaddings);
+ args.addEnums(KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigests);
KeymasterUtils.addUserAuthArgs(args,
spec.isUserAuthenticationRequired(),
spec.getUserAuthenticationValidityDurationSeconds());
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
index ff265cf..3058bd3 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
@@ -160,7 +160,7 @@
private int[] mKeymasterSignaturePaddings;
private int[] mKeymasterDigests;
- private long mRSAPublicExponent;
+ private BigInteger mRSAPublicExponent;
protected AndroidKeyStoreKeyPairGeneratorSpi(int keymasterAlgorithm) {
mOriginalKeymasterAlgorithm = keymasterAlgorithm;
@@ -320,7 +320,7 @@
mKeymasterDigests = null;
mKeySizeBits = 0;
mSpec = null;
- mRSAPublicExponent = -1;
+ mRSAPublicExponent = null;
mEncryptionAtRestRequired = false;
mRng = null;
mKeyStore = null;
@@ -353,12 +353,12 @@
throw new InvalidAlgorithmParameterException(
"RSA public exponent must be positive: " + publicExponent);
}
- if (publicExponent.compareTo(BigInteger.valueOf(Long.MAX_VALUE)) > 0) {
+ if (publicExponent.compareTo(KeymasterArguments.UINT64_MAX_VALUE) > 0) {
throw new InvalidAlgorithmParameterException(
"Unsupported RSA public exponent: " + publicExponent
- + ". Only exponents <= " + Long.MAX_VALUE + " supported");
+ + ". Maximum supported value: " + KeymasterArguments.UINT64_MAX_VALUE);
}
- mRSAPublicExponent = publicExponent.longValue();
+ mRSAPublicExponent = publicExponent;
break;
}
case KeymasterDefs.KM_ALGORITHM_EC:
@@ -404,13 +404,13 @@
}
KeymasterArguments args = new KeymasterArguments();
- args.addInt(KeymasterDefs.KM_TAG_KEY_SIZE, mKeySizeBits);
- args.addInt(KeymasterDefs.KM_TAG_ALGORITHM, mKeymasterAlgorithm);
- args.addInts(KeymasterDefs.KM_TAG_PURPOSE, mKeymasterPurposes);
- args.addInts(KeymasterDefs.KM_TAG_BLOCK_MODE, mKeymasterBlockModes);
- args.addInts(KeymasterDefs.KM_TAG_PADDING, mKeymasterEncryptionPaddings);
- args.addInts(KeymasterDefs.KM_TAG_PADDING, mKeymasterSignaturePaddings);
- args.addInts(KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigests);
+ args.addUnsignedInt(KeymasterDefs.KM_TAG_KEY_SIZE, mKeySizeBits);
+ args.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, mKeymasterAlgorithm);
+ args.addEnums(KeymasterDefs.KM_TAG_PURPOSE, mKeymasterPurposes);
+ args.addEnums(KeymasterDefs.KM_TAG_BLOCK_MODE, mKeymasterBlockModes);
+ args.addEnums(KeymasterDefs.KM_TAG_PADDING, mKeymasterEncryptionPaddings);
+ args.addEnums(KeymasterDefs.KM_TAG_PADDING, mKeymasterSignaturePaddings);
+ args.addEnums(KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigests);
KeymasterUtils.addUserAuthArgs(args,
mSpec.isUserAuthenticationRequired(),
@@ -493,7 +493,8 @@
private void addAlgorithmSpecificParameters(KeymasterArguments keymasterArgs) {
switch (mKeymasterAlgorithm) {
case KeymasterDefs.KM_ALGORITHM_RSA:
- keymasterArgs.addLong(KeymasterDefs.KM_TAG_RSA_PUBLIC_EXPONENT, mRSAPublicExponent);
+ keymasterArgs.addUnsignedLong(
+ KeymasterDefs.KM_TAG_RSA_PUBLIC_EXPONENT, mRSAPublicExponent);
break;
case KeymasterDefs.KM_ALGORITHM_EC:
break;
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
index 967319a..ba39ba7 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
@@ -226,8 +226,8 @@
}
final byte[] x509EncodedPublicKey = exportResult.exportData;
- int keymasterAlgorithm = keyCharacteristics.getInt(KeymasterDefs.KM_TAG_ALGORITHM, -1);
- if (keymasterAlgorithm == -1) {
+ Integer keymasterAlgorithm = keyCharacteristics.getEnum(KeymasterDefs.KM_TAG_ALGORITHM);
+ if (keymasterAlgorithm == null) {
throw new UnrecoverableKeyException("Key algorithm unknown");
}
@@ -277,13 +277,12 @@
.initCause(KeyStore.getKeyStoreException(errorCode));
}
- int keymasterAlgorithm = keyCharacteristics.getInt(KeymasterDefs.KM_TAG_ALGORITHM, -1);
- if (keymasterAlgorithm == -1) {
+ Integer keymasterAlgorithm = keyCharacteristics.getEnum(KeymasterDefs.KM_TAG_ALGORITHM);
+ if (keymasterAlgorithm == null) {
throw new UnrecoverableKeyException("Key algorithm unknown");
}
- List<Integer> keymasterDigests =
- keyCharacteristics.getInts(KeymasterDefs.KM_TAG_DIGEST);
+ List<Integer> keymasterDigests = keyCharacteristics.getEnums(KeymasterDefs.KM_TAG_DIGEST);
int keymasterDigest;
if (keymasterDigests.isEmpty()) {
keymasterDigest = -1;
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreRSACipherSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreRSACipherSpi.java
index 38e216d..94ed8b4 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreRSACipherSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreRSACipherSpi.java
@@ -60,9 +60,10 @@
}
@Override
- protected boolean isEncryptingUsingPrivateKeyPermitted() {
- // RSA encryption with no padding using private key is is a way to implement raw RSA
- // signatures. We have to support this.
+ protected boolean adjustConfigForEncryptingWithPrivateKey() {
+ // RSA encryption with no padding using private key is a way to implement raw RSA
+ // signatures which JCA does not expose via Signature. We thus have to support this.
+ setKeymasterPurposeOverride(KeymasterDefs.KM_PURPOSE_SIGN);
return true;
}
@@ -150,8 +151,7 @@
@Override
public byte[] doFinal(byte[] input, int inputOffset, int inputLength,
- byte[] additionalEntropy)
- throws KeyStoreException {
+ byte[] signature, byte[] additionalEntropy) throws KeyStoreException {
if (inputLength > 0) {
mConsumedInputSizeBytes += inputLength;
mInputBuffer.write(input, inputOffset, inputLength);
@@ -174,7 +174,8 @@
"Message size (" + bufferedInput.length + " bytes) must be smaller than"
+ " modulus (" + mModulusSizeBytes + " bytes)");
}
- return mDelegate.doFinal(paddedInput, 0, paddedInput.length, additionalEntropy);
+ return mDelegate.doFinal(paddedInput, 0, paddedInput.length, signature,
+ additionalEntropy);
}
@Override
@@ -198,6 +199,15 @@
}
@Override
+ protected boolean adjustConfigForEncryptingWithPrivateKey() {
+ // RSA encryption with PCKS#1 padding using private key is a way to implement RSA
+ // signatures with PKCS#1 padding. We have to support this for legacy reasons.
+ setKeymasterPurposeOverride(KeymasterDefs.KM_PURPOSE_SIGN);
+ setKeymasterPaddingOverride(KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_SIGN);
+ return true;
+ }
+
+ @Override
protected void initAlgorithmSpecificParameters() throws InvalidKeyException {}
@Override
@@ -374,7 +384,7 @@
protected final void addAlgorithmSpecificParametersToBegin(
KeymasterArguments keymasterArgs) {
super.addAlgorithmSpecificParametersToBegin(keymasterArgs);
- keymasterArgs.addInt(KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigest);
+ keymasterArgs.addEnum(KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigest);
}
@Override
@@ -425,6 +435,7 @@
}
private final int mKeymasterPadding;
+ private int mKeymasterPaddingOverride;
private int mModulusSizeBytes = -1;
@@ -458,20 +469,15 @@
// Permitted
break;
case Cipher.ENCRYPT_MODE:
- if (!isEncryptingUsingPrivateKeyPermitted()) {
+ case Cipher.WRAP_MODE:
+ if (!adjustConfigForEncryptingWithPrivateKey()) {
throw new InvalidKeyException(
- "RSA private keys cannot be used with Cipher.ENCRYPT_MODE"
+ "RSA private keys cannot be used with " + opmodeToString(opmode)
+ + " and padding "
+ + KeyProperties.EncryptionPadding.fromKeymaster(mKeymasterPadding)
+ ". Only RSA public keys supported for this mode");
}
- // JCA doesn't provide a way to generate raw RSA signatures (with arbitrary
- // padding). Thus, encrypting with private key is used instead.
- setKeymasterPurposeOverride(KeymasterDefs.KM_PURPOSE_SIGN);
break;
- case Cipher.WRAP_MODE:
- throw new InvalidKeyException(
- "RSA private keys cannot be used with Cipher.WRAP_MODE"
- + ". Only RSA public keys supported for this mode");
- // break;
default:
throw new InvalidKeyException(
"RSA private keys cannot be used with opmode: " + opmode);
@@ -485,12 +491,15 @@
break;
case Cipher.DECRYPT_MODE:
case Cipher.UNWRAP_MODE:
- throw new InvalidKeyException("RSA public keys cannot be used with opmode: "
- + opmode + ". Only RSA private keys supported for this opmode.");
+ throw new InvalidKeyException(
+ "RSA public keys cannot be used with " + opmodeToString(opmode)
+ + " and padding "
+ + KeyProperties.EncryptionPadding.fromKeymaster(mKeymasterPadding)
+ + ". Only RSA private keys supported for this opmode.");
// break;
default:
throw new InvalidKeyException(
- "RSA public keys cannot be used with opmode: " + opmode);
+ "RSA public keys cannot be used with " + opmodeToString(opmode));
}
}
@@ -500,22 +509,33 @@
if (errorCode != KeyStore.NO_ERROR) {
throw getKeyStore().getInvalidKeyException(keystoreKey.getAlias(), errorCode);
}
- int keySizeBits = keyCharacteristics.getInt(KeymasterDefs.KM_TAG_KEY_SIZE, -1);
+ long keySizeBits = keyCharacteristics.getUnsignedInt(KeymasterDefs.KM_TAG_KEY_SIZE, -1);
if (keySizeBits == -1) {
throw new InvalidKeyException("Size of key not known");
+ } else if (keySizeBits > Integer.MAX_VALUE) {
+ throw new InvalidKeyException("Key too large: " + keySizeBits + " bits");
}
- mModulusSizeBytes = (keySizeBits + 7) / 8;
+ mModulusSizeBytes = (int) ((keySizeBits + 7) / 8);
setKey(keystoreKey);
}
- protected boolean isEncryptingUsingPrivateKeyPermitted() {
+ /**
+ * Adjusts the configuration of this cipher for encrypting using the private key.
+ *
+ * <p>The default implementation does nothing and refuses to adjust the configuration.
+ *
+ * @return {@code true} if the configuration has been adjusted, {@code false} if encrypting
+ * using private key is not permitted for this cipher.
+ */
+ protected boolean adjustConfigForEncryptingWithPrivateKey() {
return false;
}
@Override
protected final void resetAll() {
mModulusSizeBytes = -1;
+ mKeymasterPaddingOverride = -1;
super.resetAll();
}
@@ -527,14 +547,18 @@
@Override
protected void addAlgorithmSpecificParametersToBegin(
@NonNull KeymasterArguments keymasterArgs) {
- keymasterArgs.addInt(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_RSA);
- keymasterArgs.addInt(KeymasterDefs.KM_TAG_PADDING, mKeymasterPadding);
+ keymasterArgs.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_RSA);
+ int keymasterPadding = getKeymasterPaddingOverride();
+ if (keymasterPadding == -1) {
+ keymasterPadding = mKeymasterPadding;
+ }
+ keymasterArgs.addEnum(KeymasterDefs.KM_TAG_PADDING, keymasterPadding);
int purposeOverride = getKeymasterPurposeOverride();
if ((purposeOverride != -1)
&& ((purposeOverride == KeymasterDefs.KM_PURPOSE_SIGN)
|| (purposeOverride == KeymasterDefs.KM_PURPOSE_VERIFY))) {
// Keymaster sign/verify requires digest to be specified. For raw sign/verify it's NONE.
- keymasterArgs.addInt(KeymasterDefs.KM_TAG_DIGEST, KeymasterDefs.KM_DIGEST_NONE);
+ keymasterArgs.addEnum(KeymasterDefs.KM_TAG_DIGEST, KeymasterDefs.KM_DIGEST_NONE);
}
}
@@ -566,4 +590,15 @@
}
return mModulusSizeBytes;
}
+
+ /**
+ * Overrides the default padding of the crypto operation.
+ */
+ protected final void setKeymasterPaddingOverride(int keymasterPadding) {
+ mKeymasterPaddingOverride = keymasterPadding;
+ }
+
+ protected final int getKeymasterPaddingOverride() {
+ return mKeymasterPaddingOverride;
+ }
}
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreRSASignatureSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreRSASignatureSpi.java
index 954b71a..ecfc97e 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreRSASignatureSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreRSASignatureSpi.java
@@ -157,8 +157,8 @@
@Override
protected final void addAlgorithmSpecificParametersToBegin(
@NonNull KeymasterArguments keymasterArgs) {
- keymasterArgs.addInt(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_RSA);
- keymasterArgs.addInt(KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigest);
- keymasterArgs.addInt(KeymasterDefs.KM_TAG_PADDING, mKeymasterPadding);
+ keymasterArgs.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_RSA);
+ keymasterArgs.addEnum(KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigest);
+ keymasterArgs.addEnum(KeymasterDefs.KM_TAG_PADDING, mKeymasterPadding);
}
}
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java
index 7887923..9a2f908 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java
@@ -93,26 +93,29 @@
if (keyCharacteristics.hwEnforced.containsTag(KeymasterDefs.KM_TAG_ORIGIN)) {
insideSecureHardware = true;
origin = KeyProperties.Origin.fromKeymaster(
- keyCharacteristics.hwEnforced.getInt(KeymasterDefs.KM_TAG_ORIGIN, -1));
+ keyCharacteristics.hwEnforced.getEnum(KeymasterDefs.KM_TAG_ORIGIN, -1));
} else if (keyCharacteristics.swEnforced.containsTag(KeymasterDefs.KM_TAG_ORIGIN)) {
insideSecureHardware = false;
origin = KeyProperties.Origin.fromKeymaster(
- keyCharacteristics.swEnforced.getInt(KeymasterDefs.KM_TAG_ORIGIN, -1));
+ keyCharacteristics.swEnforced.getEnum(KeymasterDefs.KM_TAG_ORIGIN, -1));
} else {
throw new ProviderException("Key origin not available");
}
- Integer keySizeInteger = keyCharacteristics.getInteger(KeymasterDefs.KM_TAG_KEY_SIZE);
- if (keySizeInteger == null) {
+ long keySizeUnsigned =
+ keyCharacteristics.getUnsignedInt(KeymasterDefs.KM_TAG_KEY_SIZE, -1);
+ if (keySizeUnsigned == -1) {
throw new ProviderException("Key size not available");
+ } else if (keySizeUnsigned > Integer.MAX_VALUE) {
+ throw new ProviderException("Key too large: " + keySizeUnsigned + " bits");
}
- keySize = keySizeInteger;
+ keySize = (int) keySizeUnsigned;
purposes = KeyProperties.Purpose.allFromKeymaster(
- keyCharacteristics.getInts(KeymasterDefs.KM_TAG_PURPOSE));
+ keyCharacteristics.getEnums(KeymasterDefs.KM_TAG_PURPOSE));
List<String> encryptionPaddingsList = new ArrayList<String>();
List<String> signaturePaddingsList = new ArrayList<String>();
// Keymaster stores both types of paddings in the same array -- we split it into two.
- for (int keymasterPadding : keyCharacteristics.getInts(KeymasterDefs.KM_TAG_PADDING)) {
+ for (int keymasterPadding : keyCharacteristics.getEnums(KeymasterDefs.KM_TAG_PADDING)) {
try {
@KeyProperties.EncryptionPaddingEnum String jcaPadding =
KeyProperties.EncryptionPadding.fromKeymaster(keymasterPadding);
@@ -135,13 +138,13 @@
signaturePaddingsList.toArray(new String[signaturePaddingsList.size()]);
digests = KeyProperties.Digest.allFromKeymaster(
- keyCharacteristics.getInts(KeymasterDefs.KM_TAG_DIGEST));
+ keyCharacteristics.getEnums(KeymasterDefs.KM_TAG_DIGEST));
blockModes = KeyProperties.BlockMode.allFromKeymaster(
- keyCharacteristics.getInts(KeymasterDefs.KM_TAG_BLOCK_MODE));
+ keyCharacteristics.getEnums(KeymasterDefs.KM_TAG_BLOCK_MODE));
keymasterSwEnforcedUserAuthenticators =
- keyCharacteristics.swEnforced.getInt(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, 0);
+ keyCharacteristics.swEnforced.getEnum(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, 0);
keymasterHwEnforcedUserAuthenticators =
- keyCharacteristics.hwEnforced.getInt(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, 0);
+ keyCharacteristics.hwEnforced.getEnum(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, 0);
} catch (IllegalArgumentException e) {
throw new ProviderException("Unsupported key characteristic", e);
}
@@ -153,8 +156,12 @@
keyCharacteristics.getDate(KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME);
boolean userAuthenticationRequired =
!keyCharacteristics.getBoolean(KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED);
- int userAuthenticationValidityDurationSeconds =
- keyCharacteristics.getInt(KeymasterDefs.KM_TAG_AUTH_TIMEOUT, -1);
+ long userAuthenticationValidityDurationSeconds =
+ keyCharacteristics.getUnsignedInt(KeymasterDefs.KM_TAG_AUTH_TIMEOUT, -1);
+ if (userAuthenticationValidityDurationSeconds > Integer.MAX_VALUE) {
+ throw new ProviderException("User authentication timeout validity too long: "
+ + userAuthenticationValidityDurationSeconds + " seconds");
+ }
boolean userAuthenticationRequirementEnforcedBySecureHardware = (userAuthenticationRequired)
&& (keymasterHwEnforcedUserAuthenticators != 0)
&& (keymasterSwEnforcedUserAuthenticators == 0);
@@ -172,7 +179,7 @@
digests,
blockModes,
userAuthenticationRequired,
- userAuthenticationValidityDurationSeconds,
+ (int) userAuthenticationValidityDurationSeconds,
userAuthenticationRequirementEnforcedBySecureHardware);
}
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreSignatureSpiBase.java b/keystore/java/android/security/keystore/AndroidKeyStoreSignatureSpiBase.java
index 5cdcc41..76240dd 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreSignatureSpiBase.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreSignatureSpiBase.java
@@ -58,7 +58,7 @@
*/
private IBinder mOperationToken;
private long mOperationHandle;
- private KeyStoreCryptoOperationChunkedStreamer mMessageStreamer;
+ private KeyStoreCryptoOperationStreamer mMessageStreamer;
/**
* Encountered exception which could not be immediately thrown because it was encountered inside
@@ -229,9 +229,20 @@
throw new ProviderException("Keystore returned invalid operation handle");
}
- mMessageStreamer = new KeyStoreCryptoOperationChunkedStreamer(
+ mMessageStreamer = createMainDataStreamer(mKeyStore, opResult.token);
+ }
+
+ /**
+ * Creates a streamer which sends the message to be signed/verified into the provided KeyStore
+ *
+ * <p>This implementation returns a working streamer.
+ */
+ @NonNull
+ protected KeyStoreCryptoOperationStreamer createMainDataStreamer(
+ KeyStore keyStore, IBinder operationToken) {
+ return new KeyStoreCryptoOperationChunkedStreamer(
new KeyStoreCryptoOperationChunkedStreamer.MainDataStream(
- mKeyStore, opResult.token));
+ keyStore, operationToken));
}
@Override
@@ -314,7 +325,10 @@
byte[] additionalEntropy =
KeyStoreCryptoOperationUtils.getRandomBytesToMixIntoKeystoreRng(
appRandom, getAdditionalEntropyAmountForSign());
- signature = mMessageStreamer.doFinal(EmptyArray.BYTE, 0, 0, additionalEntropy);
+ signature = mMessageStreamer.doFinal(
+ EmptyArray.BYTE, 0, 0,
+ null, // no signature provided -- it'll be generated by this invocation
+ additionalEntropy);
} catch (InvalidKeyException | KeyStoreException e) {
throw new SignatureException(e);
}
@@ -329,31 +343,37 @@
throw new SignatureException(mCachedException);
}
- boolean result;
try {
ensureKeystoreOperationInitialized();
- mMessageStreamer.flush();
- OperationResult opResult = mKeyStore.finish(mOperationToken, null, signature);
- if (opResult == null) {
- throw new KeyStoreConnectException();
- }
- switch (opResult.resultCode) {
- case KeyStore.NO_ERROR:
- result = true;
- break;
- case KeymasterDefs.KM_ERROR_VERIFICATION_FAILED:
- result = false;
- break;
- default:
- throw new SignatureException(
- KeyStore.getKeyStoreException(opResult.resultCode));
- }
- } catch (InvalidKeyException | KeyStoreException e) {
+ } catch (InvalidKeyException e) {
throw new SignatureException(e);
}
+ boolean verified;
+ try {
+ byte[] output = mMessageStreamer.doFinal(
+ EmptyArray.BYTE, 0, 0,
+ signature,
+ null // no additional entropy needed -- verification is deterministic
+ );
+ if (output.length != 0) {
+ throw new ProviderException(
+ "Signature verification unexpected produced output: " + output.length
+ + " bytes");
+ }
+ verified = true;
+ } catch (KeyStoreException e) {
+ switch (e.getErrorCode()) {
+ case KeymasterDefs.KM_ERROR_VERIFICATION_FAILED:
+ verified = false;
+ break;
+ default:
+ throw new SignatureException(e);
+ }
+ }
+
resetWhilePreservingInitState();
- return result;
+ return verified;
}
@Override
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java
index 084e30e..de483f4 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java
@@ -398,18 +398,18 @@
importArgs = new KeymasterArguments();
try {
- importArgs.addInt(KeymasterDefs.KM_TAG_ALGORITHM,
+ importArgs.addEnum(KeymasterDefs.KM_TAG_ALGORITHM,
KeyProperties.KeyAlgorithm.toKeymasterAsymmetricKeyAlgorithm(
key.getAlgorithm()));
@KeyProperties.PurposeEnum int purposes = spec.getPurposes();
- importArgs.addInts(KeymasterDefs.KM_TAG_PURPOSE,
+ importArgs.addEnums(KeymasterDefs.KM_TAG_PURPOSE,
KeyProperties.Purpose.allToKeymaster(purposes));
if (spec.isDigestsSpecified()) {
- importArgs.addInts(KeymasterDefs.KM_TAG_DIGEST,
+ importArgs.addEnums(KeymasterDefs.KM_TAG_DIGEST,
KeyProperties.Digest.allToKeymaster(spec.getDigests()));
}
- importArgs.addInts(KeymasterDefs.KM_TAG_BLOCK_MODE,
+ importArgs.addEnums(KeymasterDefs.KM_TAG_BLOCK_MODE,
KeyProperties.BlockMode.allToKeymaster(spec.getBlockModes()));
int[] keymasterEncryptionPaddings =
KeyProperties.EncryptionPadding.allToKeymaster(
@@ -429,8 +429,8 @@
}
}
}
- importArgs.addInts(KeymasterDefs.KM_TAG_PADDING, keymasterEncryptionPaddings);
- importArgs.addInts(KeymasterDefs.KM_TAG_PADDING,
+ importArgs.addEnums(KeymasterDefs.KM_TAG_PADDING, keymasterEncryptionPaddings);
+ importArgs.addEnums(KeymasterDefs.KM_TAG_PADDING,
KeyProperties.SignaturePadding.allToKeymaster(spec.getSignaturePaddings()));
KeymasterUtils.addUserAuthArgs(importArgs,
spec.isUserAuthenticationRequired(),
@@ -567,7 +567,7 @@
}
KeymasterArguments args = new KeymasterArguments();
- args.addInt(KeymasterDefs.KM_TAG_ALGORITHM, keymasterAlgorithm);
+ args.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, keymasterAlgorithm);
int[] keymasterDigests;
if (params.isDigestsSpecified()) {
@@ -606,7 +606,7 @@
keymasterDigests = EmptyArray.INT;
}
}
- args.addInts(KeymasterDefs.KM_TAG_DIGEST, keymasterDigests);
+ args.addEnums(KeymasterDefs.KM_TAG_DIGEST, keymasterDigests);
if (keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_HMAC) {
if (keymasterDigests.length == 0) {
throw new KeyStoreException("At least one digest algorithm must be specified"
@@ -630,14 +630,14 @@
}
}
}
- args.addInts(KeymasterDefs.KM_TAG_PURPOSE, KeyProperties.Purpose.allToKeymaster(purposes));
- args.addInts(KeymasterDefs.KM_TAG_BLOCK_MODE, keymasterBlockModes);
+ args.addEnums(KeymasterDefs.KM_TAG_PURPOSE, KeyProperties.Purpose.allToKeymaster(purposes));
+ args.addEnums(KeymasterDefs.KM_TAG_BLOCK_MODE, keymasterBlockModes);
if (params.getSignaturePaddings().length > 0) {
throw new KeyStoreException("Signature paddings not supported for symmetric keys");
}
int[] keymasterPaddings = KeyProperties.EncryptionPadding.allToKeymaster(
params.getEncryptionPaddings());
- args.addInts(KeymasterDefs.KM_TAG_PADDING, keymasterPaddings);
+ args.addEnums(KeymasterDefs.KM_TAG_PADDING, keymasterPaddings);
KeymasterUtils.addUserAuthArgs(args,
params.isUserAuthenticationRequired(),
params.getUserAuthenticationValidityDurationSeconds());
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreUnauthenticatedAESCipherSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreUnauthenticatedAESCipherSpi.java
index 6c53c6a..486519c 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreUnauthenticatedAESCipherSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreUnauthenticatedAESCipherSpi.java
@@ -240,11 +240,11 @@
+ " practices.");
}
- keymasterArgs.addInt(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_AES);
- keymasterArgs.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE, mKeymasterBlockMode);
- keymasterArgs.addInt(KeymasterDefs.KM_TAG_PADDING, mKeymasterPadding);
+ keymasterArgs.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_AES);
+ keymasterArgs.addEnum(KeymasterDefs.KM_TAG_BLOCK_MODE, mKeymasterBlockMode);
+ keymasterArgs.addEnum(KeymasterDefs.KM_TAG_PADDING, mKeymasterPadding);
if ((mIvRequired) && (mIv != null)) {
- keymasterArgs.addBlob(KeymasterDefs.KM_TAG_NONCE, mIv);
+ keymasterArgs.addBytes(KeymasterDefs.KM_TAG_NONCE, mIv);
}
}
@@ -254,7 +254,7 @@
mIvHasBeenUsed = true;
// NOTE: Keymaster doesn't always return an IV, even if it's used.
- byte[] returnedIv = keymasterArgs.getBlob(KeymasterDefs.KM_TAG_NONCE, null);
+ byte[] returnedIv = keymasterArgs.getBytes(KeymasterDefs.KM_TAG_NONCE, null);
if ((returnedIv != null) && (returnedIv.length == 0)) {
returnedIv = null;
}
diff --git a/keystore/java/android/security/keystore/KeyStoreCryptoOperationChunkedStreamer.java b/keystore/java/android/security/keystore/KeyStoreCryptoOperationChunkedStreamer.java
index 894d52a..ea0f4b9 100644
--- a/keystore/java/android/security/keystore/KeyStoreCryptoOperationChunkedStreamer.java
+++ b/keystore/java/android/security/keystore/KeyStoreCryptoOperationChunkedStreamer.java
@@ -35,8 +35,8 @@
* 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, byte[]) doFinal} operations which can be used to conveniently
- * implement various JCA crypto primitives.
+ * {@link #doFinal(byte[], int, int, byte[], byte[]) doFinal} operations which can be used to
+ * conveniently implement various JCA crypto primitives.
*
* <p>Bidirectional chunked streaming of data via a KeyStore crypto operation is abstracted away as
* a {@link Stream} to avoid having this class deal with operation tokens and occasional additional
@@ -60,7 +60,7 @@
* Returns the result of the KeyStore {@code finish} operation or null if keystore couldn't
* be reached.
*/
- OperationResult finish(byte[] additionalEntropy);
+ OperationResult finish(byte[] siganture, byte[] additionalEntropy);
}
// Binder buffer is about 1MB, but it's shared between all active transactions of the process.
@@ -201,8 +201,8 @@
}
@Override
- public byte[] doFinal(byte[] input, int inputOffset, int inputLength, byte[] additionalEntropy)
- throws KeyStoreException {
+ public byte[] doFinal(byte[] input, int inputOffset, int inputLength,
+ byte[] signature, byte[] additionalEntropy) throws KeyStoreException {
if (inputLength == 0) {
// No input provided -- simplify the rest of the code
input = EmptyArray.BYTE;
@@ -213,7 +213,7 @@
byte[] output = update(input, inputOffset, inputLength);
output = ArrayUtils.concat(output, flush());
- OperationResult opResult = mKeyStoreStream.finish(additionalEntropy);
+ OperationResult opResult = mKeyStoreStream.finish(signature, additionalEntropy);
if (opResult == null) {
throw new KeyStoreConnectException();
} else if (opResult.resultCode != KeyStore.NO_ERROR) {
@@ -286,8 +286,8 @@
}
@Override
- public OperationResult finish(byte[] additionalEntropy) {
- return mKeyStore.finish(mOperationToken, null, null, additionalEntropy);
+ public OperationResult finish(byte[] signature, byte[] additionalEntropy) {
+ return mKeyStore.finish(mOperationToken, null, signature, additionalEntropy);
}
}
}
diff --git a/keystore/java/android/security/keystore/KeyStoreCryptoOperationStreamer.java b/keystore/java/android/security/keystore/KeyStoreCryptoOperationStreamer.java
index 897bd71..062c2d4 100644
--- a/keystore/java/android/security/keystore/KeyStoreCryptoOperationStreamer.java
+++ b/keystore/java/android/security/keystore/KeyStoreCryptoOperationStreamer.java
@@ -28,15 +28,15 @@
* 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, byte[]) doFinal} operations which can be used to conveniently
- * implement various JCA crypto primitives.
+ * {@link #doFinal(byte[], int, int, byte[], byte[]) doFinal} operations which can be used to
+ * conveniently implement various JCA crypto primitives.
*
* @hide
*/
interface KeyStoreCryptoOperationStreamer {
byte[] update(byte[] input, int inputOffset, int inputLength) throws KeyStoreException;
- byte[] doFinal(byte[] input, int inputOffset, int inputLength, byte[] additionalEntropy)
- throws KeyStoreException;
+ byte[] doFinal(byte[] input, int inputOffset, int inputLength, byte[] signature,
+ byte[] additionalEntropy) throws KeyStoreException;
long getConsumedInputSizeBytes();
long getProducedOutputSizeBytes();
}
diff --git a/keystore/java/android/security/keystore/KeymasterUtils.java b/keystore/java/android/security/keystore/KeymasterUtils.java
index 4b37d90..0006601 100644
--- a/keystore/java/android/security/keystore/KeymasterUtils.java
+++ b/keystore/java/android/security/keystore/KeymasterUtils.java
@@ -110,8 +110,9 @@
"At least one fingerprint must be enrolled to create keys requiring user"
+ " authentication for every use");
}
- args.addLong(KeymasterDefs.KM_TAG_USER_SECURE_ID, fingerprintOnlySid);
- args.addInt(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, KeymasterDefs.HW_AUTH_FINGERPRINT);
+ args.addUnsignedLong(KeymasterDefs.KM_TAG_USER_SECURE_ID,
+ KeymasterArguments.toUint64(fingerprintOnlySid));
+ args.addEnum(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, KeymasterDefs.HW_AUTH_FINGERPRINT);
} else {
// The key is authorized for use for the specified amount of time after the user has
// authenticated. Whatever unlocks the secure lock screen should authorize this key.
@@ -120,10 +121,11 @@
throw new IllegalStateException("Secure lock screen must be enabled"
+ " to create keys requiring user authentication");
}
- args.addLong(KeymasterDefs.KM_TAG_USER_SECURE_ID, rootSid);
- args.addInt(KeymasterDefs.KM_TAG_USER_AUTH_TYPE,
+ args.addUnsignedLong(KeymasterDefs.KM_TAG_USER_SECURE_ID,
+ KeymasterArguments.toUint64(rootSid));
+ args.addEnum(KeymasterDefs.KM_TAG_USER_AUTH_TYPE,
KeymasterDefs.HW_AUTH_PASSWORD | KeymasterDefs.HW_AUTH_FINGERPRINT);
- args.addInt(KeymasterDefs.KM_TAG_AUTH_TIMEOUT,
+ args.addUnsignedInt(KeymasterDefs.KM_TAG_AUTH_TIMEOUT,
userAuthenticationValidityDurationSeconds);
}
}
diff --git a/keystore/tests/src/android/security/KeyStoreTest.java b/keystore/tests/src/android/security/KeyStoreTest.java
index 0b60c62..319cf32 100644
--- a/keystore/tests/src/android/security/KeyStoreTest.java
+++ b/keystore/tests/src/android/security/KeyStoreTest.java
@@ -702,14 +702,13 @@
private KeyCharacteristics generateRsaKey(String name) throws Exception {
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.addEnum(KeymasterDefs.KM_TAG_PURPOSE, KeymasterDefs.KM_PURPOSE_ENCRYPT);
+ args.addEnum(KeymasterDefs.KM_TAG_PURPOSE, KeymasterDefs.KM_PURPOSE_DECRYPT);
+ args.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_RSA);
+ args.addEnum(KeymasterDefs.KM_TAG_PADDING, KeymasterDefs.KM_PAD_NONE);
args.addBoolean(KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED);
- args.addInt(KeymasterDefs.KM_TAG_KEY_SIZE, 2048);
- args.addLong(KeymasterDefs.KM_TAG_RSA_PUBLIC_EXPONENT,
- RSAKeyGenParameterSpec.F4.longValue());
+ args.addUnsignedInt(KeymasterDefs.KM_TAG_KEY_SIZE, 2048);
+ args.addUnsignedLong(KeymasterDefs.KM_TAG_RSA_PUBLIC_EXPONENT, RSAKeyGenParameterSpec.F4);
KeyCharacteristics outCharacteristics = new KeyCharacteristics();
int result = mKeyStore.generateKey(name, args, null, 0, outCharacteristics);
@@ -726,14 +725,13 @@
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.addEnum(KeymasterDefs.KM_TAG_PURPOSE, KeymasterDefs.KM_PURPOSE_ENCRYPT);
+ args.addEnum(KeymasterDefs.KM_TAG_PURPOSE, KeymasterDefs.KM_PURPOSE_DECRYPT);
+ args.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_RSA);
+ args.addEnum(KeymasterDefs.KM_TAG_PADDING, KeymasterDefs.KM_PAD_NONE);
args.addBoolean(KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED);
- args.addInt(KeymasterDefs.KM_TAG_KEY_SIZE, 2048);
- args.addLong(KeymasterDefs.KM_TAG_RSA_PUBLIC_EXPONENT,
- RSAKeyGenParameterSpec.F4.longValue());
+ args.addUnsignedInt(KeymasterDefs.KM_TAG_KEY_SIZE, 2048);
+ args.addUnsignedLong(KeymasterDefs.KM_TAG_RSA_PUBLIC_EXPONENT, RSAKeyGenParameterSpec.F4);
KeyCharacteristics outCharacteristics = new KeyCharacteristics();
int result = mKeyStore.generateKey(name, args, entropy, 0, outCharacteristics);
@@ -759,16 +757,15 @@
String name = "test";
byte[] id = new byte[] {0x01, 0x02, 0x03};
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.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE, KeymasterDefs.KM_MODE_ECB);
+ args.addEnum(KeymasterDefs.KM_TAG_PURPOSE, KeymasterDefs.KM_PURPOSE_ENCRYPT);
+ args.addEnum(KeymasterDefs.KM_TAG_PURPOSE, KeymasterDefs.KM_PURPOSE_DECRYPT);
+ args.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_RSA);
+ args.addEnum(KeymasterDefs.KM_TAG_PADDING, KeymasterDefs.KM_PAD_NONE);
+ args.addUnsignedInt(KeymasterDefs.KM_TAG_KEY_SIZE, 2048);
+ args.addEnum(KeymasterDefs.KM_TAG_BLOCK_MODE, KeymasterDefs.KM_MODE_ECB);
args.addBoolean(KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED);
- args.addBlob(KeymasterDefs.KM_TAG_APPLICATION_ID, id);
- args.addLong(KeymasterDefs.KM_TAG_RSA_PUBLIC_EXPONENT,
- RSAKeyGenParameterSpec.F4.longValue());
+ args.addBytes(KeymasterDefs.KM_TAG_APPLICATION_ID, id);
+ args.addUnsignedLong(KeymasterDefs.KM_TAG_RSA_PUBLIC_EXPONENT, RSAKeyGenParameterSpec.F4);
KeyCharacteristics outCharacteristics = new KeyCharacteristics();
int result = mKeyStore.generateKey(name, args, null, 0, outCharacteristics);
@@ -795,12 +792,12 @@
public void testAesGcmEncryptSuccess() throws Exception {
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_AES);
- args.addInt(KeymasterDefs.KM_TAG_PADDING, KeymasterDefs.KM_PAD_NONE);
- args.addInt(KeymasterDefs.KM_TAG_KEY_SIZE, 256);
- args.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE, KeymasterDefs.KM_MODE_GCM);
+ args.addEnum(KeymasterDefs.KM_TAG_PURPOSE, KeymasterDefs.KM_PURPOSE_ENCRYPT);
+ args.addEnum(KeymasterDefs.KM_TAG_PURPOSE, KeymasterDefs.KM_PURPOSE_DECRYPT);
+ args.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_AES);
+ args.addEnum(KeymasterDefs.KM_TAG_PADDING, KeymasterDefs.KM_PAD_NONE);
+ args.addUnsignedInt(KeymasterDefs.KM_TAG_KEY_SIZE, 256);
+ args.addEnum(KeymasterDefs.KM_TAG_BLOCK_MODE, KeymasterDefs.KM_MODE_GCM);
args.addBoolean(KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED);
KeyCharacteristics outCharacteristics = new KeyCharacteristics();
@@ -808,10 +805,10 @@
assertEquals("Generate should succeed", KeyStore.NO_ERROR, rc);
args = new KeymasterArguments();
- args.addInt(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_AES);
- args.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE, KeymasterDefs.KM_MODE_GCM);
- args.addInt(KeymasterDefs.KM_TAG_PADDING, KeymasterDefs.KM_PAD_NONE);
- args.addInt(KeymasterDefs.KM_TAG_MAC_LENGTH, 128);
+ args.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_AES);
+ args.addEnum(KeymasterDefs.KM_TAG_BLOCK_MODE, KeymasterDefs.KM_MODE_GCM);
+ args.addEnum(KeymasterDefs.KM_TAG_PADDING, KeymasterDefs.KM_PAD_NONE);
+ args.addUnsignedInt(KeymasterDefs.KM_TAG_MAC_LENGTH, 128);
OperationResult result = mKeyStore.begin(name, KeymasterDefs.KM_PURPOSE_ENCRYPT,
true, args, null);
IBinder token = result.token;
@@ -832,12 +829,12 @@
private int importAesKey(String name, byte[] key, int size, int mode) {
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_AES);
- args.addInt(KeymasterDefs.KM_TAG_PADDING, KeymasterDefs.KM_PAD_NONE);
- args.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE, mode);
- args.addInt(KeymasterDefs.KM_TAG_KEY_SIZE, size);
+ args.addEnum(KeymasterDefs.KM_TAG_PURPOSE, KeymasterDefs.KM_PURPOSE_ENCRYPT);
+ args.addEnum(KeymasterDefs.KM_TAG_PURPOSE, KeymasterDefs.KM_PURPOSE_DECRYPT);
+ args.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_AES);
+ args.addEnum(KeymasterDefs.KM_TAG_PADDING, KeymasterDefs.KM_PAD_NONE);
+ args.addEnum(KeymasterDefs.KM_TAG_BLOCK_MODE, mode);
+ args.addUnsignedInt(KeymasterDefs.KM_TAG_KEY_SIZE, size);
args.addBoolean(KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED);
return mKeyStore.importKey(name, args, KeymasterDefs.KM_KEY_FORMAT_RAW, key, 0,
new KeyCharacteristics());
@@ -877,9 +874,9 @@
hexToBytes("b6ed21b99ca6f4f9f153e7b1beafed1d"),
hexToBytes("23304b7a39f9f3ff067d8d8f9e24ecc7")};
KeymasterArguments beginArgs = new KeymasterArguments();
- beginArgs.addInt(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_AES);
- beginArgs.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE, KeymasterDefs.KM_MODE_ECB);
- beginArgs.addInt(KeymasterDefs.KM_TAG_PADDING, KeymasterDefs.KM_PAD_NONE);
+ beginArgs.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_AES);
+ beginArgs.addEnum(KeymasterDefs.KM_TAG_BLOCK_MODE, KeymasterDefs.KM_MODE_ECB);
+ beginArgs.addEnum(KeymasterDefs.KM_TAG_PADDING, KeymasterDefs.KM_PAD_NONE);
for (int i = 0; i < testVectors.length; i++) {
byte[] cipherText = doOperation(name, KeymasterDefs.KM_PURPOSE_ENCRYPT, testVectors[i],
beginArgs);
@@ -897,12 +894,12 @@
public void testOperationPruning() throws Exception {
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_AES);
- args.addInt(KeymasterDefs.KM_TAG_PADDING, KeymasterDefs.KM_PAD_NONE);
- args.addInt(KeymasterDefs.KM_TAG_KEY_SIZE, 256);
- args.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE, KeymasterDefs.KM_MODE_CTR);
+ args.addEnum(KeymasterDefs.KM_TAG_PURPOSE, KeymasterDefs.KM_PURPOSE_ENCRYPT);
+ args.addEnum(KeymasterDefs.KM_TAG_PURPOSE, KeymasterDefs.KM_PURPOSE_DECRYPT);
+ args.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_AES);
+ args.addEnum(KeymasterDefs.KM_TAG_PADDING, KeymasterDefs.KM_PAD_NONE);
+ args.addUnsignedInt(KeymasterDefs.KM_TAG_KEY_SIZE, 256);
+ args.addEnum(KeymasterDefs.KM_TAG_BLOCK_MODE, KeymasterDefs.KM_MODE_CTR);
args.addBoolean(KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED);
KeyCharacteristics outCharacteristics = new KeyCharacteristics();
@@ -910,9 +907,9 @@
assertEquals("Generate should succeed", KeyStore.NO_ERROR, rc);
args = new KeymasterArguments();
- args.addInt(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_AES);
- args.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE, KeymasterDefs.KM_MODE_CTR);
- args.addInt(KeymasterDefs.KM_TAG_PADDING, KeymasterDefs.KM_PAD_NONE);
+ args.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_AES);
+ args.addEnum(KeymasterDefs.KM_TAG_BLOCK_MODE, KeymasterDefs.KM_MODE_CTR);
+ args.addEnum(KeymasterDefs.KM_TAG_PADDING, KeymasterDefs.KM_PAD_NONE);
OperationResult result = mKeyStore.begin(name, KeymasterDefs.KM_PURPOSE_ENCRYPT,
true, args, null);
assertEquals("Begin should succeed", KeyStore.NO_ERROR, result.resultCode);
@@ -930,13 +927,13 @@
public void testAuthNeeded() throws Exception {
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_AES);
- args.addInt(KeymasterDefs.KM_TAG_PADDING, KeymasterDefs.KM_PAD_PKCS7);
- args.addInt(KeymasterDefs.KM_TAG_KEY_SIZE, 256);
- args.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE, KeymasterDefs.KM_MODE_ECB);
- args.addInt(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, 1);
+ args.addEnum(KeymasterDefs.KM_TAG_PURPOSE, KeymasterDefs.KM_PURPOSE_ENCRYPT);
+ args.addEnum(KeymasterDefs.KM_TAG_PURPOSE, KeymasterDefs.KM_PURPOSE_DECRYPT);
+ args.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_AES);
+ args.addEnum(KeymasterDefs.KM_TAG_PADDING, KeymasterDefs.KM_PAD_PKCS7);
+ args.addUnsignedInt(KeymasterDefs.KM_TAG_KEY_SIZE, 256);
+ args.addEnum(KeymasterDefs.KM_TAG_BLOCK_MODE, KeymasterDefs.KM_MODE_ECB);
+ args.addEnum(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, 1);
KeyCharacteristics outCharacteristics = new KeyCharacteristics();
int rc = mKeyStore.generateKey(name, args, null, 0, outCharacteristics);
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityContainer.java
index f529ac0..23bd238 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -329,7 +329,9 @@
case SimPuk:
// Shortcut for SIM PIN/PUK to go to directly to user's security screen or home
SecurityMode securityMode = mSecurityModel.getSecurityMode();
- if (securityMode != SecurityMode.None) {
+ if (securityMode != SecurityMode.None
+ || !mLockPatternUtils.isLockScreenDisabled(
+ KeyguardUpdateMonitor.getCurrentUser())) {
showSecurityScreen(securityMode);
} else {
finish = true;
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
index 9a2f71c..bf92fda 100755
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
@@ -93,6 +93,7 @@
addHandler(BluetoothDevice.ACTION_FOUND, new DeviceFoundHandler());
addHandler(BluetoothDevice.ACTION_DISAPPEARED, new DeviceDisappearedHandler());
addHandler(BluetoothDevice.ACTION_NAME_CHANGED, new NameChangedHandler());
+ addHandler(BluetoothDevice.ACTION_ALIAS_CHANGED, new NameChangedHandler());
// Pairing broadcasts
addHandler(BluetoothDevice.ACTION_BOND_STATE_CHANGED, new BondStateChangedHandler());
diff --git a/packages/SettingsProvider/AndroidManifest.xml b/packages/SettingsProvider/AndroidManifest.xml
index 469b776..71aefad 100644
--- a/packages/SettingsProvider/AndroidManifest.xml
+++ b/packages/SettingsProvider/AndroidManifest.xml
@@ -15,7 +15,6 @@
<provider android:name="SettingsProvider" android:authorities="settings"
android:multiprocess="false"
android:exported="true"
- android:writePermission="android.permission.WRITE_SETTINGS"
android:singleUser="true"
android:initOrder="100" />
</application>
diff --git a/packages/SystemUI/res/layout/volume_dialog.xml b/packages/SystemUI/res/layout/volume_dialog.xml
index 0ed1e2a..7617ed4 100644
--- a/packages/SystemUI/res/layout/volume_dialog.xml
+++ b/packages/SystemUI/res/layout/volume_dialog.xml
@@ -18,7 +18,7 @@
android:id="@+id/volume_dialog"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginBottom="4dp"
+ android:layout_marginBottom="@dimen/volume_dialog_margin_bottom"
android:layout_marginLeft="@dimen/notification_side_padding"
android:layout_marginRight="@dimen/notification_side_padding"
android:background="@drawable/volume_dialog_background"
diff --git a/packages/SystemUI/res/layout/volume_dialog_row.xml b/packages/SystemUI/res/layout/volume_dialog_row.xml
index c6aa588..1a6d34e 100644
--- a/packages/SystemUI/res/layout/volume_dialog_row.xml
+++ b/packages/SystemUI/res/layout/volume_dialog_row.xml
@@ -17,6 +17,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clipChildren="false"
+ android:id="@+id/volume_dialog_row"
android:paddingEnd="8dp"
android:paddingStart="8dp" >
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 869b03a..005077f 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -576,6 +576,9 @@
<!-- Standard image button size for volume dialog buttons -->
<dimen name="volume_button_size">48dp</dimen>
+ <!-- Volume dialog root view bottom margin, at rest -->
+ <dimen name="volume_dialog_margin_bottom">4dp</dimen>
+
<!-- Padding between icon and text for managed profile toast -->
<dimen name="managed_profile_toast_padding">4dp</dimen>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 67d3312..1889862 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -291,11 +291,6 @@
<item name="android:textColor">#ffb0b3c5</item>
</style>
- <style name="VolumeDialogAnimations">
- <item name="android:windowEnterAnimation">@android:anim/fade_in</item>
- <item name="android:windowExitAnimation">@android:anim/fade_out</item>
- </style>
-
<style name="VolumeButtons" parent="@android:style/Widget.Material.Button.Borderless">
<item name="android:background">@drawable/btn_borderless_rect</item>
</style>
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 80761d8..7f61fc1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -622,9 +622,8 @@
synchronized (this) {
if (DEBUG) Log.d(TAG, "onSystemReady");
mSystemReady = true;
- mUpdateMonitor.registerCallback(mUpdateCallback);
-
doKeyguardLocked(null);
+ mUpdateMonitor.registerCallback(mUpdateCallback);
}
// Most services aren't available until the system reaches the ready state, so we
// send it here when the device first boots.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index a3bb129..a7afec4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -200,6 +200,7 @@
* notifications on Keyguard, like SIM PIN/PUK.
*/
public boolean needsFullscreenBouncer() {
+ ensureView();
if (mKeyguardView != null) {
SecurityMode mode = mKeyguardView.getSecurityMode();
return mode == SecurityMode.SimPin || mode == SecurityMode.SimPuk;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index c3719d4..bacf890 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -137,6 +137,12 @@
scheduleUpdate();
}
+ public void abortKeyguardFadingOut() {
+ if (mAnimateKeyguardFadingOut) {
+ endAnimateKeyguardFadingOut();
+ }
+ }
+
public void animateGoingToFullShade(long delay, long duration) {
mDurationOverride = duration;
mAnimationDelay = delay;
@@ -321,17 +327,21 @@
mScrimBehind.getViewTreeObserver().removeOnPreDrawListener(this);
mUpdatePending = false;
updateScrims();
- mAnimateKeyguardFadingOut = false;
mDurationOverride = -1;
mAnimationDelay = 0;
// Make sure that we always call the listener even if we didn't start an animation.
+ endAnimateKeyguardFadingOut();
+ mAnimationStarted = false;
+ return true;
+ }
+
+ private void endAnimateKeyguardFadingOut() {
+ mAnimateKeyguardFadingOut = false;
if (!mAnimationStarted && mOnAnimationFinished != null) {
mOnAnimationFinished.run();
mOnAnimationFinished = null;
}
- mAnimationStarted = false;
- return true;
}
public void setBackDropView(BackDropView backDropView) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index a7e8406..a69416a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -100,6 +100,7 @@
public void show(Bundle options) {
mShowing = true;
mStatusBarWindowManager.setKeyguardShowing(true);
+ mScrimController.abortKeyguardFadingOut();
reset();
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java
index 065523f..0ab0392 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java
@@ -111,6 +111,7 @@
private final Accessibility mAccessibility = new Accessibility();
private final ColorStateList mActiveSliderTint;
private final ColorStateList mInactiveSliderTint;
+ private final VolumeDialogMotion mMotion;
private boolean mShowing;
private boolean mExpanded;
@@ -120,9 +121,12 @@
private boolean mSilentMode = VolumePrefs.DEFAULT_ENABLE_SILENT_MODE;
private State mState;
private int mExpandButtonRes;
- private boolean mExpanding;
+ private boolean mExpandButtonAnimationRunning;
private SafetyWarningDialog mSafetyWarning;
private Callback mCallback;
+ private boolean mPendingStateChanged;
+ private boolean mPendingRecheckAll;
+ private long mCollapseTime;
public VolumeDialog(Context context, int windowType, VolumeDialogController controller,
ZenModeController zenModeController, Callback callback) {
@@ -151,7 +155,6 @@
lp.format = PixelFormat.TRANSLUCENT;
lp.setTitle(VolumeDialog.class.getSimpleName());
lp.gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL;
- lp.windowAnimations = R.style.VolumeDialogAnimations;
lp.y = res.getDimensionPixelSize(R.dimen.volume_offset_top);
lp.gravity = Gravity.TOP;
window.setAttributes(lp);
@@ -168,9 +171,22 @@
updateExpandButtonH();
mLayoutTransition = new LayoutTransition();
mLayoutTransition.setDuration(new ValueAnimator().getDuration() / 2);
- mLayoutTransition.disableTransitionType(LayoutTransition.DISAPPEARING);
- mLayoutTransition.disableTransitionType(LayoutTransition.CHANGE_DISAPPEARING);
mDialogContentView.setLayoutTransition(mLayoutTransition);
+ mMotion = new VolumeDialogMotion(mDialog, mDialogView, mDialogContentView, mExpandButton,
+ new VolumeDialogMotion.Callback() {
+ @Override
+ public void onAnimatingChanged(boolean animating) {
+ if (animating) return;
+ if (mPendingStateChanged) {
+ mHandler.sendEmptyMessage(H.STATE_CHANGED);
+ mPendingStateChanged = false;
+ }
+ if (mPendingRecheckAll) {
+ mHandler.sendEmptyMessage(H.RECHECK_ALL);
+ mPendingRecheckAll = false;
+ }
+ }
+ });
addRow(AudioManager.STREAM_RING,
R.drawable.ic_volume_ringer, R.drawable.ic_volume_ringer_mute, true);
@@ -242,6 +258,7 @@
final VolumeRow row = initRow(stream, iconRes, iconMuteRes, important);
if (!mRows.isEmpty()) {
final View v = new View(mContext);
+ v.setId(android.R.id.background);
final int h = mContext.getResources()
.getDimensionPixelSize(R.dimen.volume_slider_interspacing);
final LinearLayout.LayoutParams lp =
@@ -253,10 +270,11 @@
@Override
public void onLayoutChange(View v, int left, int top, int right, int bottom,
int oldLeft, int oldTop, int oldRight, int oldBottom) {
- if (D.BUG) Log.d(TAG, "onLayoutChange"
+ final boolean moved = oldLeft != left || oldTop != top;
+ if (D.BUG) Log.d(TAG, "onLayoutChange moved=" + moved
+ " old=" + new Rect(oldLeft, oldTop, oldRight, oldBottom).toShortString()
+ " new=" + new Rect(left,top,right,bottom).toShortString());
- if (oldLeft != left || oldTop != top) {
+ if (moved) {
for (int i = 0; i < mDialogContentView.getChildCount(); i++) {
final View c = mDialogContentView.getChildAt(i);
if (!c.isShown()) continue;
@@ -302,18 +320,21 @@
if (D.BUG) Log.d(TAG, "repositionExpandAnim x=" + x + " y=" + y);
mExpandButton.setTranslationX(x);
mExpandButton.setTranslationY(y);
+ mExpandButton.setTag((Integer) y);
}
public void dump(PrintWriter writer) {
writer.println(VolumeDialog.class.getSimpleName() + " state:");
writer.print(" mShowing: "); writer.println(mShowing);
writer.print(" mExpanded: "); writer.println(mExpanded);
- writer.print(" mExpanding: "); writer.println(mExpanding);
+ writer.print(" mExpandButtonAnimationRunning: ");
+ writer.println(mExpandButtonAnimationRunning);
writer.print(" mActiveStream: "); writer.println(mActiveStream);
writer.print(" mDynamic: "); writer.println(mDynamic);
writer.print(" mShowHeaders: "); writer.println(mShowHeaders);
writer.print(" mAutomute: "); writer.println(mAutomute);
writer.print(" mSilentMode: "); writer.println(mSilentMode);
+ writer.print(" mCollapseTime: "); writer.println(mCollapseTime);
writer.print(" mAccessibility.mFeedbackEnabled: ");
writer.println(mAccessibility.mFeedbackEnabled);
}
@@ -412,12 +433,13 @@
}
private void showH(int reason) {
+ if (D.BUG) Log.d(TAG, "showH r=" + Events.DISMISS_REASONS[reason]);
mHandler.removeMessages(H.SHOW);
mHandler.removeMessages(H.DISMISS);
rescheduleTimeoutH();
if (mShowing) return;
mShowing = true;
- mDialog.show();
+ mMotion.startShow();
Events.writeEvent(mContext, Events.EVENT_SHOW_DIALOG, reason, mKeyguard.isKeyguardLocked());
mController.notifyVisible(true);
}
@@ -434,7 +456,7 @@
private int computeTimeoutH() {
if (mAccessibility.mFeedbackEnabled) return 20000;
if (mSafetyWarning != null) return 5000;
- if (mExpanded || mExpanding) return 5000;
+ if (mExpanded || mExpandButtonAnimationRunning) return 5000;
if (mActiveStream == AudioManager.STREAM_MUSIC) return 1500;
return 3000;
}
@@ -444,9 +466,13 @@
mHandler.removeMessages(H.SHOW);
if (!mShowing) return;
mShowing = false;
- mDialog.dismiss();
+ mMotion.startDismiss(new Runnable() {
+ @Override
+ public void run() {
+ setExpandedH(false);
+ }
+ });
Events.writeEvent(mContext, Events.EVENT_DISMISS_DIALOG, reason);
- setExpandedH(false);
mController.notifyVisible(false);
synchronized (mSafetyWarningLock) {
if (mSafetyWarning != null) {
@@ -456,13 +482,40 @@
}
}
+ private void updateDialogBottomMarginH() {
+ final long diff = System.currentTimeMillis() - mCollapseTime;
+ final boolean collapsing = mCollapseTime != 0 && diff < getConservativeCollapseDuration();
+ final ViewGroup.MarginLayoutParams mlp = (MarginLayoutParams) mDialogView.getLayoutParams();
+ final int bottomMargin = collapsing ? mDialogContentView.getHeight() :
+ mContext.getResources().getDimensionPixelSize(R.dimen.volume_dialog_margin_bottom);
+ if (bottomMargin != mlp.bottomMargin) {
+ if (D.BUG) Log.d(TAG, "bottomMargin " + mlp.bottomMargin + " -> " + bottomMargin);
+ mlp.bottomMargin = bottomMargin;
+ mDialogView.setLayoutParams(mlp);
+ }
+ }
+
+ private long getConservativeCollapseDuration() {
+ return mExpandButtonAnimationDuration * 3;
+ }
+
+ private void prepareForCollapse() {
+ mHandler.removeMessages(H.UPDATE_BOTTOM_MARGIN);
+ mCollapseTime = System.currentTimeMillis();
+ updateDialogBottomMarginH();
+ mHandler.sendEmptyMessageDelayed(H.UPDATE_BOTTOM_MARGIN, getConservativeCollapseDuration());
+ }
+
private void setExpandedH(boolean expanded) {
if (mExpanded == expanded) return;
mExpanded = expanded;
- mExpanding = isAttached();
+ mExpandButtonAnimationRunning = isAttached();
if (D.BUG) Log.d(TAG, "setExpandedH " + expanded);
+ if (!mExpanded && mExpandButtonAnimationRunning) {
+ prepareForCollapse();
+ }
updateRowsH();
- if (mExpanding) {
+ if (mExpandButtonAnimationRunning) {
final Drawable d = mExpandButton.getDrawable();
if (d instanceof AnimatedVectorDrawable) {
// workaround to reset drawable
@@ -473,7 +526,7 @@
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
- mExpanding = false;
+ mExpandButtonAnimationRunning = false;
updateExpandButtonH();
rescheduleTimeoutH();
}
@@ -484,8 +537,9 @@
}
private void updateExpandButtonH() {
- mExpandButton.setClickable(!mExpanding);
- if (mExpanding && isAttached()) return;
+ if (D.BUG) Log.d(TAG, "updateExpandButtonH");
+ mExpandButton.setClickable(!mExpandButtonAnimationRunning);
+ if (mExpandButtonAnimationRunning && isAttached()) return;
final int res = mExpanded ? R.drawable.ic_volume_collapse_animation
: R.drawable.ic_volume_expand_animation;
if (res == mExpandButtonRes) return;
@@ -502,6 +556,7 @@
}
private void updateRowsH() {
+ if (D.BUG) Log.d(TAG, "updateRowsH");
final VolumeRow activeRow = getActiveRow();
updateFooterH();
updateExpandButtonH();
@@ -531,6 +586,7 @@
}
private void trimObsoleteH() {
+ if (D.BUG) Log.d(TAG, "trimObsoleteH");
for (int i = mRows.size() -1; i >= 0; i--) {
final VolumeRow row = mRows.get(i);
if (row.ss == null || !row.ss.dynamic) continue;
@@ -543,7 +599,13 @@
}
private void onStateChangedH(State state) {
+ final boolean animating = mMotion.isAnimating();
+ if (D.BUG) Log.d(TAG, "onStateChangedH animating=" + animating);
mState = state;
+ if (animating) {
+ mPendingStateChanged = true;
+ return;
+ }
mDynamic.clear();
// add any new dynamic rows
for (int i = 0; i < state.states.size(); i++) {
@@ -568,11 +630,18 @@
}
private void updateFooterH() {
- Util.setVisOrGone(mZenFooter, mState.zenMode != Global.ZEN_MODE_OFF);
+ if (D.BUG) Log.d(TAG, "updateFooterH");
+ final boolean wasVisible = mZenFooter.getVisibility() == View.VISIBLE;
+ final boolean visible = mState.zenMode != Global.ZEN_MODE_OFF;
+ if (wasVisible != visible && !visible) {
+ prepareForCollapse();
+ }
+ Util.setVisOrGone(mZenFooter, visible);
mZenFooter.update();
}
private void updateVolumeRowH(VolumeRow row) {
+ if (D.BUG) Log.d(TAG, "updateVolumeRowH s=" + row.stream);
if (mState == null) return;
final StreamState ss = mState.states.get(row.stream);
if (ss == null) return;
@@ -841,7 +910,7 @@
private final OnClickListener mClickExpand = new OnClickListener() {
@Override
public void onClick(View v) {
- if (mExpanding) return;
+ if (mExpandButtonAnimationRunning) return;
final boolean newExpand = !mExpanded;
Events.writeEvent(mContext, Events.EVENT_EXPAND, newExpand);
setExpandedH(newExpand);
@@ -870,6 +939,8 @@
private static final int RECHECK_ALL = 4;
private static final int SET_STREAM_IMPORTANT = 5;
private static final int RESCHEDULE_TIMEOUT = 6;
+ private static final int STATE_CHANGED = 7;
+ private static final int UPDATE_BOTTOM_MARGIN = 8;
public H() {
super(Looper.getMainLooper());
@@ -884,6 +955,8 @@
case RECHECK_ALL: recheckH(null); break;
case SET_STREAM_IMPORTANT: setStreamImportantH(msg.arg1, msg.arg2 != 0); break;
case RESCHEDULE_TIMEOUT: rescheduleTimeoutH(); break;
+ case STATE_CHANGED: onStateChangedH(mState); break;
+ case UPDATE_BOTTOM_MARGIN: updateDialogBottomMarginH(); break;
}
}
}
@@ -902,6 +975,12 @@
@Override
protected void onStop() {
super.onStop();
+ final boolean animating = mMotion.isAnimating();
+ if (D.BUG) Log.d(TAG, "onStop animating=" + animating);
+ if (animating) {
+ mPendingRecheckAll = true;
+ return;
+ }
mHandler.sendEmptyMessage(H.RECHECK_ALL);
}
@@ -978,11 +1057,13 @@
mDialogView.addOnAttachStateChangeListener(new OnAttachStateChangeListener() {
@Override
public void onViewDetachedFromWindow(View v) {
+ if (D.BUG) Log.d(TAG, "onViewDetachedFromWindow");
// noop
}
@Override
public void onViewAttachedToWindow(View v) {
+ if (D.BUG) Log.d(TAG, "onViewAttachedToWindow");
updateFeedbackEnabled();
}
});
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogMotion.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogMotion.java
new file mode 100644
index 0000000..fdf1840
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogMotion.java
@@ -0,0 +1,304 @@
+/*
+ * 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.systemui.volume;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.TimeInterpolator;
+import android.animation.ValueAnimator;
+import android.animation.ValueAnimator.AnimatorUpdateListener;
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnDismissListener;
+import android.content.DialogInterface.OnShowListener;
+import android.os.Handler;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.animation.PathInterpolator;
+
+public class VolumeDialogMotion {
+ private static final String TAG = Util.logTag(VolumeDialogMotion.class);
+
+ private static final float ANIMATION_SCALE = 1.0f;
+ private static final int PRE_DISMISS_DELAY = 50;
+ private static final int POST_SHOW_DELAY = 200;
+
+ private final Dialog mDialog;
+ private final View mDialogView;
+ private final ViewGroup mContents; // volume rows + zen footer
+ private final View mChevron;
+ private final Handler mHandler = new Handler();
+ private final Callback mCallback;
+
+ private boolean mAnimating; // show or dismiss animation is running
+ private boolean mShowing; // show animation is running
+ private boolean mDismissing; // dismiss animation is running
+ private ValueAnimator mChevronPositionAnimator;
+ private ValueAnimator mContentsPositionAnimator;
+
+ public VolumeDialogMotion(Dialog dialog, View dialogView, ViewGroup contents, View chevron,
+ Callback callback) {
+ mDialog = dialog;
+ mDialogView = dialogView;
+ mContents = contents;
+ mChevron = chevron;
+ mCallback = callback;
+ mDialog.setOnDismissListener(new OnDismissListener() {
+ @Override
+ public void onDismiss(DialogInterface dialog) {
+ if (D.BUG) Log.d(TAG, "mDialog.onDismiss");
+ }
+ });
+ mDialog.setOnShowListener(new OnShowListener() {
+ @Override
+ public void onShow(DialogInterface dialog) {
+ if (D.BUG) Log.d(TAG, "mDialog.onShow");
+ final int h = mDialogView.getHeight();
+ mDialogView.setTranslationY(-h);
+ mHandler.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ startShowAnimation();
+ }
+ }, POST_SHOW_DELAY);
+ }
+ });
+ }
+
+ public boolean isAnimating() {
+ return mAnimating;
+ }
+
+ private void setShowing(boolean showing) {
+ if (showing == mShowing) return;
+ mShowing = showing;
+ if (D.BUG) Log.d(TAG, "mShowing = " + mShowing);
+ updateAnimating();
+ }
+
+ private void setDismissing(boolean dismissing) {
+ if (dismissing == mDismissing) return;
+ mDismissing = dismissing;
+ if (D.BUG) Log.d(TAG, "mDismissing = " + mDismissing);
+ updateAnimating();
+ }
+
+ private void updateAnimating() {
+ final boolean animating = mShowing || mDismissing;
+ if (animating == mAnimating) return;
+ mAnimating = animating;
+ if (D.BUG) Log.d(TAG, "mAnimating = " + mAnimating);
+ if (mCallback != null) {
+ mCallback.onAnimatingChanged(mAnimating);
+ }
+ }
+
+ public void startShow() {
+ if (D.BUG) Log.d(TAG, "startShow");
+ if (mShowing) return;
+ setShowing(true);
+ if (mDismissing) {
+ mDialogView.animate().cancel();
+ setDismissing(false);
+ startShowAnimation();
+ return;
+ }
+ if (D.BUG) Log.d(TAG, "mDialog.show()");
+ mDialog.show();
+ }
+
+ private int chevronDistance() {
+ return mChevron.getHeight() / 6;
+ }
+
+ private void startShowAnimation() {
+ if (D.BUG) Log.d(TAG, "startShowAnimation");
+ mDialogView.animate()
+ .translationY(0)
+ .setDuration(scaledDuration(300))
+ .setInterpolator(new LogDecelerateInterpolator())
+ .setListener(null)
+ .setUpdateListener(new AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ if (mChevronPositionAnimator == null) return;
+ // reposition chevron
+ final float v = (Float) mChevronPositionAnimator.getAnimatedValue();
+ final int posY = (Integer) mChevron.getTag();
+ mChevron.setTranslationY(posY + v + -mDialogView.getTranslationY());
+ }})
+ .start();
+
+ mContentsPositionAnimator = ValueAnimator.ofFloat(-chevronDistance(), 0)
+ .setDuration(scaledDuration(400));
+ mContentsPositionAnimator.addListener(new AnimatorListenerAdapter() {
+ private boolean mCancelled;
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (mCancelled) return;
+ if (D.BUG) Log.d(TAG, "show.onAnimationEnd");
+ setShowing(false);
+ }
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ if (D.BUG) Log.d(TAG, "show.onAnimationCancel");
+ mCancelled = true;
+ }
+ });
+ mContentsPositionAnimator.addUpdateListener(new AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ float v = (Float) animation.getAnimatedValue();
+ mContents.setTranslationY(v + -mDialogView.getTranslationY());
+ }
+ });
+ mContentsPositionAnimator.setInterpolator(new LogDecelerateInterpolator());
+ mContentsPositionAnimator.start();
+
+ mContents.setAlpha(0);
+ mContents.animate()
+ .alpha(1)
+ .setDuration(scaledDuration(150))
+ .setInterpolator(new PathInterpolator(0f, 0f, .2f, 1f))
+ .start();
+
+ mChevronPositionAnimator = ValueAnimator.ofFloat(-chevronDistance(), 0)
+ .setDuration(scaledDuration(250));
+ mChevronPositionAnimator.setInterpolator(new PathInterpolator(.4f, 0f, .2f, 1f));
+ mChevronPositionAnimator.start();
+
+ mChevron.setAlpha(0);
+ mChevron.animate()
+ .alpha(1)
+ .setStartDelay(scaledDuration(50))
+ .setDuration(scaledDuration(150))
+ .setInterpolator(new PathInterpolator(.4f, 0f, 1f, 1f))
+ .start();
+ }
+
+ public void startDismiss(final Runnable onComplete) {
+ if (D.BUG) Log.d(TAG, "startDismiss");
+ if (mDismissing) return;
+ setDismissing(true);
+ if (mShowing) {
+ mDialogView.animate().cancel();
+ mContentsPositionAnimator.cancel();
+ mContents.animate().cancel();
+ mChevronPositionAnimator.cancel();
+ mChevron.animate().cancel();
+ setShowing(false);
+ }
+ mDialogView.animate()
+ .translationY(-mDialogView.getHeight())
+ .setDuration(scaledDuration(250))
+ .setInterpolator(new LogAccelerateInterpolator())
+ .setUpdateListener(new AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ mContents.setTranslationY(-mDialogView.getTranslationY());
+ int posY = (Integer) mChevron.getTag();
+ mChevron.setTranslationY(posY + -mDialogView.getTranslationY());
+ }
+ })
+ .setListener(new AnimatorListenerAdapter() {
+ private boolean mCancelled;
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (mCancelled) return;
+ if (D.BUG) Log.d(TAG, "dismiss.onAnimationEnd");
+ mHandler.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ if (D.BUG) Log.d(TAG, "mDialog.dismiss()");
+ mDialog.dismiss();
+ onComplete.run();
+ setDismissing(false);
+ }
+ }, PRE_DISMISS_DELAY);
+
+ }
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ if (D.BUG) Log.d(TAG, "dismiss.onAnimationCancel");
+ mCancelled = true;
+ }
+ }).start();
+ }
+
+ private static int scaledDuration(int base) {
+ return (int) (base * ANIMATION_SCALE);
+ }
+
+ private static final class LogDecelerateInterpolator implements TimeInterpolator {
+ private final float mBase;
+ private final float mDrift;
+ private final float mTimeScale;
+ private final float mOutputScale;
+
+ private LogDecelerateInterpolator() {
+ this(400f, 1.4f, 0);
+ }
+
+ private LogDecelerateInterpolator(float base, float timeScale, float drift) {
+ mBase = base;
+ mDrift = drift;
+ mTimeScale = 1f / timeScale;
+
+ mOutputScale = 1f / computeLog(1f);
+ }
+
+ private float computeLog(float t) {
+ return 1f - (float) Math.pow(mBase, -t * mTimeScale) + (mDrift * t);
+ }
+
+ @Override
+ public float getInterpolation(float t) {
+ return computeLog(t) * mOutputScale;
+ }
+ }
+
+ private static final class LogAccelerateInterpolator implements TimeInterpolator {
+ private final int mBase;
+ private final int mDrift;
+ private final float mLogScale;
+
+ private LogAccelerateInterpolator() {
+ this(100, 0);
+ }
+
+ private LogAccelerateInterpolator(int base, int drift) {
+ mBase = base;
+ mDrift = drift;
+ mLogScale = 1f / computeLog(1, mBase, mDrift);
+ }
+
+ private static float computeLog(float t, int base, int drift) {
+ return (float) -Math.pow(base, -t) + 1 + (drift * t);
+ }
+
+ @Override
+ public float getInterpolation(float t) {
+ return 1 - computeLog(1 - t, mBase, mDrift) * mLogScale;
+ }
+ }
+
+ public interface Callback {
+ void onAnimatingChanged(boolean animating);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/ZenFooter.java b/packages/SystemUI/src/com/android/systemui/volume/ZenFooter.java
index 3f6294d..af7ee08 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/ZenFooter.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/ZenFooter.java
@@ -16,6 +16,7 @@
package com.android.systemui.volume;
import android.animation.LayoutTransition;
+import android.animation.ValueAnimator;
import android.content.Context;
import android.provider.Settings.Global;
import android.service.notification.ZenModeConfig;
@@ -51,7 +52,9 @@
super(context, attrs);
mContext = context;
mSpTexts = new SpTexts(mContext);
- setLayoutTransition(new LayoutTransition());
+ final LayoutTransition layoutTransition = new LayoutTransition();
+ layoutTransition.setDuration(new ValueAnimator().getDuration() / 2);
+ setLayoutTransition(layoutTransition);
}
@Override
diff --git a/services/core/java/com/android/server/LockSettingsService.java b/services/core/java/com/android/server/LockSettingsService.java
index c5ea8bc..ebbd9e5 100644
--- a/services/core/java/com/android/server/LockSettingsService.java
+++ b/services/core/java/com/android/server/LockSettingsService.java
@@ -700,6 +700,15 @@
final KeyStore ks = KeyStore.getInstance();
ks.onUserRemoved(userId);
+
+ try {
+ final IGateKeeperService gk = getGateKeeperService();
+ if (gk != null) {
+ gk.clearSecureUserId(userId);
+ }
+ } catch (RemoteException ex) {
+ Slog.w(TAG, "unable to clear GK secure user id");
+ }
}
private static final String[] VALID_SETTINGS = new String[] {
diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java
index 7204aec..4fe8fb9 100644
--- a/services/core/java/com/android/server/MountService.java
+++ b/services/core/java/com/android/server/MountService.java
@@ -1082,6 +1082,7 @@
case VolumeInfo.STATE_EJECTING:
case VolumeInfo.STATE_UNMOUNTED:
case VolumeInfo.STATE_UNMOUNTABLE:
+ case VolumeInfo.STATE_BAD_REMOVAL:
break;
default:
return false;
@@ -1118,6 +1119,7 @@
final Intent intent = new Intent(VolumeInfo.ACTION_VOLUME_STATE_CHANGED);
intent.putExtra(VolumeInfo.EXTRA_VOLUME_ID, vol.id);
intent.putExtra(VolumeInfo.EXTRA_VOLUME_STATE, newState);
+ intent.putExtra(VolumeRecord.EXTRA_FS_UUID, vol.fsUuid);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
android.Manifest.permission.WRITE_MEDIA_STORAGE);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 1ef1375..f031694 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -3583,10 +3583,23 @@
}
}
+ private boolean hasUsageStatsPermission(String callingPackage) {
+ final int mode = mAppOpsService.checkOperation(AppOpsManager.OP_GET_USAGE_STATS,
+ Binder.getCallingUid(), callingPackage);
+ if (mode == AppOpsManager.MODE_DEFAULT) {
+ return checkCallingPermission(Manifest.permission.PACKAGE_USAGE_STATS)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+ return mode == AppOpsManager.MODE_ALLOWED;
+ }
+
@Override
- public int getPackageProcessState(String packageName) {
- enforceCallingPermission(android.Manifest.permission.GET_PACKAGE_IMPORTANCE,
- "getPackageProcessState");
+ public int getPackageProcessState(String packageName, String callingPackage) {
+ if (!hasUsageStatsPermission(callingPackage)) {
+ enforceCallingPermission(android.Manifest.permission.GET_PACKAGE_IMPORTANCE,
+ "getPackageProcessState");
+ }
+
int procState = ActivityManager.PROCESS_STATE_NONEXISTENT;
synchronized (this) {
for (int i=mLruProcesses.size()-1; i>=0; i--) {
diff --git a/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java b/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java
index 5d56d4a..74ba404 100644
--- a/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java
+++ b/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java
@@ -113,18 +113,18 @@
public void recordSuccess(String msg) {
maybeFixupTimes();
+ result = SUCCEEDED + ": " + msg;
if (mCountDownLatch != null) {
mCountDownLatch.countDown();
}
- result = SUCCEEDED + ": " + msg;
}
public void recordFailure(String msg) {
maybeFixupTimes();
+ result = FAILED + ": " + msg;
if (mCountDownLatch != null) {
mCountDownLatch.countDown();
}
- result = FAILED + ": " + msg;
}
private void maybeFixupTimes() {
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index ef7be30..8dc4bd3 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -41,7 +41,7 @@
@Override
public void onStart() {
Slog.i(TAG, "Waiting for installd to be ready.");
- ping();
+ mInstaller.waitForConnection();
}
private static String escapeNull(String arg) {
@@ -310,14 +310,6 @@
return mInstaller.execute(builder.toString());
}
- public boolean ping() {
- if (mInstaller.execute("ping") < 0) {
- return false;
- } else {
- return true;
- }
- }
-
@Deprecated
public int freeCache(long freeStorageSize) {
return freeCache(null, freeStorageSize);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index dbf0f17..be1afa8 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -12020,8 +12020,7 @@
final int verificationId = mIntentFilterVerificationToken++;
for (PackageParser.Activity a : pkg.activities) {
for (ActivityIntentInfo filter : a.intents) {
- boolean needsFilterVerification = filter.hasWebDataURI();
- if (needsFilterVerification && needsNetworkVerificationLPr(filter)) {
+ if (filter.hasOnlyWebDataURI() && needsNetworkVerificationLPr(filter)) {
if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG,
"Verification needed for IntentFilter:" + filter.toString());
mIntentFilterVerifier.addOneIntentFilterVerification(
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 169f6de..dcd7799 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -1072,7 +1072,9 @@
ArraySet<String> currentDomains = current.getIntentFilterVerificationInfo().getDomains();
if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS) {
for (PackageSetting ps : mPackages.values()) {
- if (ps == null || ps.pkg.packageName.equals(packageName)) continue;
+ if (ps == null || ps.pkg == null || packageName.equals(ps.pkg.packageName)) {
+ continue;
+ }
IntentFilterVerificationInfo ivi = ps.getIntentFilterVerificationInfo();
if (ivi == null) {
continue;
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 4b8a10f..06e6a62 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -218,20 +218,34 @@
public static final String KEY_CARRIER_VOLTE_AVAILABLE_BOOL = "carrier_volte_available_bool";
/**
+ * Flag specifying whether video telephony is available for carrier. If false: hard disabled.
+ * If true: then depends on carrier provisioning, availability, etc.
+ */
+ public static final String KEY_CARRIER_VT_AVAILABLE_BOOL = "carrier_vt_available_bool";
+
+ /**
* Flag specifying whether WFC over IMS should be available for carrier: independent of
* carrier provisioning. If false: hard disabled. If true: then depends on carrier
* provisioning, availability etc.
*/
public static final String KEY_CARRIER_WFC_IMS_AVAILABLE_BOOL = "carrier_wfc_ims_available_bool";
- /** Flag specifying whether VoLTE availability is based on provisioning. */
- public static final String KEY_CARRIER_VOLTE_PROVISIONED_BOOL = "carrier_volte_provisioned_bool";
+ /** Flag specifying whether provisioning is required for VOLTE. */
+ public static final String KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL
+ = "carrier_volte_provisioning_required_bool";
/** Flag specifying whether VoLTE TTY is supported. */
public static final String KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL
= "carrier_volte_tty_supported_bool";
/**
+ * Flag specifying whether IMS service can be turned off. If false then the service will not be
+ * turned-off completely, but individual features can be disabled.
+ */
+ public static final String KEY_CARRIER_ALLOW_TURNOFF_IMS_BOOL
+ = "carrier_allow_turnoff_ims_bool";
+
+ /**
* If Voice Radio Technology is RIL_RADIO_TECHNOLOGY_LTE:14 or RIL_RADIO_TECHNOLOGY_UNKNOWN:0
* this is the value that should be used instead. A configuration value of
* RIL_RADIO_TECHNOLOGY_UNKNOWN:0 means there is no replacement value and that the default
@@ -352,9 +366,11 @@
sDefaults.putBoolean(KEY_AUTO_RETRY_ENABLED_BOOL, false);
sDefaults.putBoolean(KEY_CARRIER_SETTINGS_ENABLE_BOOL, false);
sDefaults.putBoolean(KEY_CARRIER_VOLTE_AVAILABLE_BOOL, false);
+ sDefaults.putBoolean(KEY_CARRIER_VT_AVAILABLE_BOOL, false);
sDefaults.putBoolean(KEY_CARRIER_WFC_IMS_AVAILABLE_BOOL, false);
- sDefaults.putBoolean(KEY_CARRIER_VOLTE_PROVISIONED_BOOL, false);
+ sDefaults.putBoolean(KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL, false);
sDefaults.putBoolean(KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL, true);
+ sDefaults.putBoolean(KEY_CARRIER_ALLOW_TURNOFF_IMS_BOOL, true);
sDefaults.putBoolean(KEY_DISABLE_CDMA_ACTIVATION_CODE_BOOL, false);
sDefaults.putBoolean(KEY_DTMF_TYPE_ENABLED_BOOL, false);
sDefaults.putBoolean(KEY_ENABLE_DIALER_KEY_VIBRATION_BOOL, true);
diff --git a/tests/VectorDrawableTest/res/anim/animation_favorite.xml b/tests/VectorDrawableTest/res/anim/animation_favorite.xml
index 2e2d9bb..13bd6f5 100644
--- a/tests/VectorDrawableTest/res/anim/animation_favorite.xml
+++ b/tests/VectorDrawableTest/res/anim/animation_favorite.xml
@@ -45,12 +45,6 @@
android:valueTo="#FF00FF00" />
<objectAnimator
android:duration="8000"
- android:propertyName="strokeWidth"
- android:repeatCount="-1"
- android:valueFrom="5"
- android:valueTo="20" />
- <objectAnimator
- android:duration="8000"
android:propertyName="fillColor"
android:repeatCount="-1"
android:valueFrom="#FFFF0000"
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable_favorite.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable_favorite.xml
index 7be49a9..f93486e 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_drawable_favorite.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable_favorite.xml
@@ -26,6 +26,7 @@
<path
android:name="favorite"
android:fillColor="#ff000000"
+ android:strokeWidth="2"
android:pathData="M2.100006104,-6
C0.1449127197,-6,1.600006104,-5.975006104,0,-5.975006104
C-1.574996948,-5.975006104,0.00309753418,-6-1.949996948-6
diff --git a/tools/aapt/Android.mk b/tools/aapt/Android.mk
index bbe6860..d551c8e 100644
--- a/tools/aapt/Android.mk
+++ b/tools/aapt/Android.mk
@@ -50,9 +50,11 @@
aaptTests := \
tests/AaptConfig_test.cpp \
tests/AaptGroupEntry_test.cpp \
+ tests/Pseudolocales_test.cpp \
tests/ResourceFilter_test.cpp
aaptCIncludes := \
+ system/core/base/include \
external/libpng \
external/zlib
@@ -99,7 +101,6 @@
include $(BUILD_HOST_STATIC_LIBRARY)
-
# ==========================================================
# Build the host executable: aapt
# ==========================================================
diff --git a/tools/aapt/XMLNode.cpp b/tools/aapt/XMLNode.cpp
index 6902a30..ca3f687 100644
--- a/tools/aapt/XMLNode.cpp
+++ b/tools/aapt/XMLNode.cpp
@@ -213,16 +213,14 @@
Vector<StringPool::entry_style_span> spanStack;
String16 curString;
String16 rawString;
+ Pseudolocalizer pseudo(pseudolocalize);
const char* errorMsg;
int xliffDepth = 0;
bool firstTime = true;
size_t len;
ResXMLTree::event_code_t code;
- // Bracketing if pseudolocalization accented method specified.
- if (pseudolocalize == PSEUDO_ACCENTED) {
- curString.append(String16(String8("[")));
- }
+ curString.append(pseudo.start());
while ((code=inXml->next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
if (code == ResXMLTree::TEXT) {
String16 text(inXml->getText(&len));
@@ -231,18 +229,12 @@
if (text.string()[0] == '@') {
// If this is a resource reference, don't do the pseudoloc.
pseudolocalize = NO_PSEUDOLOCALIZATION;
+ pseudo.setMethod(pseudolocalize);
+ curString = String16();
}
}
if (xliffDepth == 0 && pseudolocalize > 0) {
- String16 pseudo;
- if (pseudolocalize == PSEUDO_ACCENTED) {
- pseudo = pseudolocalize_string(text);
- } else if (pseudolocalize == PSEUDO_BIDI) {
- pseudo = pseudobidi_string(text);
- } else {
- pseudo = text;
- }
- curString.append(pseudo);
+ curString.append(pseudo.text(text));
} else {
if (isFormatted && hasSubstitutionErrors(fileName, inXml, text) != NO_ERROR) {
return UNKNOWN_ERROR;
@@ -382,24 +374,7 @@
}
}
- // Bracketing if pseudolocalization accented method specified.
- if (pseudolocalize == PSEUDO_ACCENTED) {
- const char16_t* str = outString->string();
- const char16_t* p = str;
- const char16_t* e = p + outString->size();
- int words_cnt = 0;
- while (p < e) {
- if (isspace(*p)) {
- words_cnt++;
- }
- p++;
- }
- unsigned int length = words_cnt > 3 ? outString->size() :
- outString->size() / 2;
- curString.append(String16(String8(" ")));
- curString.append(pseudo_generate_expansion(length));
- curString.append(String16(String8("]")));
- }
+ curString.append(pseudo.end());
if (code == ResXMLTree::BAD_DOCUMENT) {
SourcePos(String8(fileName), inXml->getLineNumber()).error(
diff --git a/tools/aapt/pseudolocalize.cpp b/tools/aapt/pseudolocalize.cpp
index 60aa2b2..c7fee2c 100644
--- a/tools/aapt/pseudolocalize.cpp
+++ b/tools/aapt/pseudolocalize.cpp
@@ -16,6 +16,80 @@
static const String16 k_placeholder_open = String16("\xc2\xbb");
static const String16 k_placeholder_close = String16("\xc2\xab");
+static const char16_t k_arg_start = '{';
+static const char16_t k_arg_end = '}';
+
+Pseudolocalizer::Pseudolocalizer(PseudolocalizationMethod m)
+ : mImpl(nullptr), mLastDepth(0) {
+ setMethod(m);
+}
+
+void Pseudolocalizer::setMethod(PseudolocalizationMethod m) {
+ if (mImpl) {
+ delete mImpl;
+ }
+ if (m == PSEUDO_ACCENTED) {
+ mImpl = new PseudoMethodAccent();
+ } else if (m == PSEUDO_BIDI) {
+ mImpl = new PseudoMethodBidi();
+ } else {
+ mImpl = new PseudoMethodNone();
+ }
+}
+
+String16 Pseudolocalizer::text(const String16& text) {
+ String16 out;
+ size_t depth = mLastDepth;
+ size_t lastpos, pos;
+ const size_t length= text.size();
+ const char16_t* str = text.string();
+ bool escaped = false;
+ for (lastpos = pos = 0; pos < length; pos++) {
+ char16_t c = str[pos];
+ if (escaped) {
+ escaped = false;
+ continue;
+ }
+ if (c == '\'') {
+ escaped = true;
+ continue;
+ }
+
+ if (c == k_arg_start) {
+ depth++;
+ } else if (c == k_arg_end && depth) {
+ depth--;
+ }
+
+ if (mLastDepth != depth || pos == length - 1) {
+ bool pseudo = ((mLastDepth % 2) == 0);
+ size_t nextpos = pos;
+ if (!pseudo || depth == mLastDepth) {
+ nextpos++;
+ }
+ size_t size = nextpos - lastpos;
+ if (size) {
+ String16 chunk = String16(text, size, lastpos);
+ if (pseudo) {
+ chunk = mImpl->text(chunk);
+ } else if (str[lastpos] == k_arg_start &&
+ str[nextpos - 1] == k_arg_end) {
+ chunk = mImpl->placeholder(chunk);
+ }
+ out.append(chunk);
+ }
+ if (pseudo && depth < mLastDepth) { // End of message
+ out.append(mImpl->end());
+ } else if (!pseudo && depth > mLastDepth) { // Start of message
+ out.append(mImpl->start());
+ }
+ lastpos = nextpos;
+ mLastDepth = depth;
+ }
+ }
+ return out;
+}
+
static const char*
pseudolocalize_char(const char16_t c)
{
@@ -78,8 +152,7 @@
}
}
-static bool
-is_possible_normal_placeholder_end(const char16_t c) {
+static bool is_possible_normal_placeholder_end(const char16_t c) {
switch (c) {
case 's': return true;
case 'S': return true;
@@ -106,8 +179,7 @@
}
}
-String16
-pseudo_generate_expansion(const unsigned int length) {
+static String16 pseudo_generate_expansion(const unsigned int length) {
String16 result = k_expansion_string;
const char16_t* s = result.string();
if (result.size() < length) {
@@ -127,18 +199,47 @@
return result;
}
+static bool is_space(const char16_t c) {
+ return (c == ' ' || c == '\t' || c == '\n');
+}
+
+String16 PseudoMethodAccent::start() {
+ String16 result;
+ if (mDepth == 0) {
+ result = String16(String8("["));
+ }
+ mWordCount = mLength = 0;
+ mDepth++;
+ return result;
+}
+
+String16 PseudoMethodAccent::end() {
+ String16 result;
+ if (mLength) {
+ result.append(String16(String8(" ")));
+ result.append(pseudo_generate_expansion(
+ mWordCount > 3 ? mLength : mLength / 2));
+ }
+ mWordCount = mLength = 0;
+ mDepth--;
+ if (mDepth == 0) {
+ result.append(String16(String8("]")));
+ }
+ return result;
+}
+
/**
* Converts characters so they look like they've been localized.
*
* Note: This leaves escape sequences untouched so they can later be
* processed by ResTable::collectString in the normal way.
*/
-String16
-pseudolocalize_string(const String16& source)
+String16 PseudoMethodAccent::text(const String16& source)
{
const char16_t* s = source.string();
String16 result;
const size_t I = source.size();
+ bool lastspace = true;
for (size_t i=0; i<I; i++) {
char16_t c = s[i];
if (c == '\\') {
@@ -170,23 +271,24 @@
}
} else if (c == '%') {
// Placeholder syntax, no need to pseudolocalize
- result += k_placeholder_open;
+ String16 chunk;
bool end = false;
- result.append(&c, 1);
+ chunk.append(&c, 1);
while (!end && i < I) {
++i;
c = s[i];
- result.append(&c, 1);
+ chunk.append(&c, 1);
if (is_possible_normal_placeholder_end(c)) {
end = true;
} else if (c == 't') {
++i;
c = s[i];
- result.append(&c, 1);
+ chunk.append(&c, 1);
end = true;
}
}
- result += k_placeholder_close;
+ // Treat chunk as a placeholder unless it ends with %.
+ result += ((c == '%') ? chunk : placeholder(chunk));
} else if (c == '<' || c == '&') {
// html syntax, no need to pseudolocalize
bool tag_closed = false;
@@ -234,35 +336,52 @@
if (p != NULL) {
result += String16(p);
} else {
+ bool space = is_space(c);
+ if (lastspace && !space) {
+ mWordCount++;
+ }
+ lastspace = space;
result.append(&c, 1);
}
+ // Count only pseudolocalizable chars and delimiters
+ mLength++;
}
}
return result;
}
+String16 PseudoMethodAccent::placeholder(const String16& source) {
+ // Surround a placeholder with brackets
+ return k_placeholder_open + source + k_placeholder_close;
+}
-String16
-pseudobidi_string(const String16& source)
+String16 PseudoMethodBidi::text(const String16& source)
{
const char16_t* s = source.string();
String16 result;
- result += k_rlm;
- result += k_rlo;
+ bool lastspace = true;
+ bool space = true;
for (size_t i=0; i<source.size(); i++) {
char16_t c = s[i];
- switch(c) {
- case ' ': result += k_pdf;
- result += k_rlm;
- result.append(&c, 1);
- result += k_rlm;
- result += k_rlo;
- break;
- default: result.append(&c, 1);
- break;
+ space = is_space(c);
+ if (lastspace && !space) {
+ // Word start
+ result += k_rlm + k_rlo;
+ } else if (!lastspace && space) {
+ // Word end
+ result += k_pdf + k_rlm;
}
+ lastspace = space;
+ result.append(&c, 1);
}
- result += k_pdf;
- result += k_rlm;
+ if (!lastspace) {
+ // End of last word
+ result += k_pdf + k_rlm;
+ }
return result;
}
+String16 PseudoMethodBidi::placeholder(const String16& source) {
+ // Surround a placeholder with directionality change sequence
+ return k_rlm + k_rlo + source + k_pdf + k_rlm;
+}
+
diff --git a/tools/aapt/pseudolocalize.h b/tools/aapt/pseudolocalize.h
index e6ab18e..71b974b 100644
--- a/tools/aapt/pseudolocalize.h
+++ b/tools/aapt/pseudolocalize.h
@@ -1,18 +1,58 @@
#ifndef HOST_PSEUDOLOCALIZE_H
#define HOST_PSEUDOLOCALIZE_H
+#include <base/macros.h>
#include "StringPool.h"
-#include <string>
+class PseudoMethodImpl {
+ public:
+ virtual ~PseudoMethodImpl() {}
+ virtual String16 start() { return String16(); }
+ virtual String16 end() { return String16(); }
+ virtual String16 text(const String16& text) = 0;
+ virtual String16 placeholder(const String16& text) = 0;
+};
-String16 pseudolocalize_string(const String16& source);
-// Surrounds every word in the sentance with specific characters that makes
-// the word directionality RTL.
-String16 pseudobidi_string(const String16& source);
-// Generates expansion string based on the specified lenght.
-// Generated string could not be shorter that length, but it could be slightly
-// longer.
-String16 pseudo_generate_expansion(const unsigned int length);
+class PseudoMethodNone : public PseudoMethodImpl {
+ public:
+ PseudoMethodNone() {}
+ String16 text(const String16& text) { return text; }
+ String16 placeholder(const String16& text) { return text; }
+ private:
+ DISALLOW_COPY_AND_ASSIGN(PseudoMethodNone);
+};
+
+class PseudoMethodBidi : public PseudoMethodImpl {
+ public:
+ String16 text(const String16& text);
+ String16 placeholder(const String16& text);
+};
+
+class PseudoMethodAccent : public PseudoMethodImpl {
+ public:
+ PseudoMethodAccent() : mDepth(0), mWordCount(0), mLength(0) {}
+ String16 start();
+ String16 end();
+ String16 text(const String16& text);
+ String16 placeholder(const String16& text);
+ private:
+ size_t mDepth;
+ size_t mWordCount;
+ size_t mLength;
+};
+
+class Pseudolocalizer {
+ public:
+ Pseudolocalizer(PseudolocalizationMethod m);
+ ~Pseudolocalizer() { if (mImpl) delete mImpl; }
+ void setMethod(PseudolocalizationMethod m);
+ String16 start() { return mImpl->start(); }
+ String16 end() { return mImpl->end(); }
+ String16 text(const String16& text);
+ private:
+ PseudoMethodImpl *mImpl;
+ size_t mLastDepth;
+};
#endif // HOST_PSEUDOLOCALIZE_H
diff --git a/tools/aapt/tests/Pseudolocales_test.cpp b/tools/aapt/tests/Pseudolocales_test.cpp
new file mode 100644
index 0000000..4670e9f
--- /dev/null
+++ b/tools/aapt/tests/Pseudolocales_test.cpp
@@ -0,0 +1,217 @@
+/*
+ * 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.
+ */
+
+#include <androidfw/ResourceTypes.h>
+#include <utils/String8.h>
+#include <gtest/gtest.h>
+
+#include "Bundle.h"
+#include "pseudolocalize.h"
+
+using android::String8;
+
+// In this context, 'Axis' represents a particular field in the configuration,
+// such as language or density.
+
+static void simple_helper(const char* input, const char* expected, PseudolocalizationMethod method) {
+ Pseudolocalizer pseudo(method);
+ String16 result = pseudo.start() + pseudo.text(String16(String8(input))) + pseudo.end();
+ //std::cout << String8(result).string() << std::endl;
+ ASSERT_EQ(String8(expected), String8(result));
+}
+
+static void compound_helper(const char* in1, const char* in2, const char *in3,
+ const char* expected, PseudolocalizationMethod method) {
+ Pseudolocalizer pseudo(method);
+ String16 result = pseudo.start() + \
+ pseudo.text(String16(String8(in1))) + \
+ pseudo.text(String16(String8(in2))) + \
+ pseudo.text(String16(String8(in3))) + \
+ pseudo.end();
+ ASSERT_EQ(String8(expected), String8(result));
+}
+
+TEST(Pseudolocales, NoPseudolocalization) {
+ simple_helper("", "", NO_PSEUDOLOCALIZATION);
+ simple_helper("Hello, world", "Hello, world", NO_PSEUDOLOCALIZATION);
+
+ compound_helper("Hello,", " world", "",
+ "Hello, world", NO_PSEUDOLOCALIZATION);
+}
+
+TEST(Pseudolocales, PlaintextAccent) {
+ simple_helper("", "[]", PSEUDO_ACCENTED);
+ simple_helper("Hello, world",
+ "[Ĥéļļö, ŵöŕļð one two]", PSEUDO_ACCENTED);
+
+ simple_helper("Hello, %1d",
+ "[Ĥéļļö, »%1d« one two]", PSEUDO_ACCENTED);
+
+ simple_helper("Battery %1d%%",
+ "[βåţţéŕý »%1d«%% one two]", PSEUDO_ACCENTED);
+
+ compound_helper("", "", "", "[]", PSEUDO_ACCENTED);
+ compound_helper("Hello,", " world", "",
+ "[Ĥéļļö, ŵöŕļð one two]", PSEUDO_ACCENTED);
+}
+
+TEST(Pseudolocales, PlaintextBidi) {
+ simple_helper("", "", PSEUDO_BIDI);
+ simple_helper("word",
+ "\xe2\x80\x8f\xE2\x80\xaeword\xE2\x80\xac\xe2\x80\x8f",
+ PSEUDO_BIDI);
+ simple_helper(" word ",
+ " \xe2\x80\x8f\xE2\x80\xaeword\xE2\x80\xac\xe2\x80\x8f ",
+ PSEUDO_BIDI);
+ simple_helper(" word ",
+ " \xe2\x80\x8f\xE2\x80\xaeword\xE2\x80\xac\xe2\x80\x8f ",
+ PSEUDO_BIDI);
+ simple_helper("hello\n world\n",
+ "\xe2\x80\x8f\xE2\x80\xaehello\xE2\x80\xac\xe2\x80\x8f\n" \
+ " \xe2\x80\x8f\xE2\x80\xaeworld\xE2\x80\xac\xe2\x80\x8f\n",
+ PSEUDO_BIDI);
+ compound_helper("hello", "\n ", " world\n",
+ "\xe2\x80\x8f\xE2\x80\xaehello\xE2\x80\xac\xe2\x80\x8f\n" \
+ " \xe2\x80\x8f\xE2\x80\xaeworld\xE2\x80\xac\xe2\x80\x8f\n",
+ PSEUDO_BIDI);
+}
+
+TEST(Pseudolocales, SimpleICU) {
+ // Single-fragment messages
+ simple_helper("{placeholder}", "[»{placeholder}«]", PSEUDO_ACCENTED);
+ simple_helper("{USER} is offline",
+ "[»{USER}« îš öƒƒļîñé one two]", PSEUDO_ACCENTED);
+ simple_helper("Copy from {path1} to {path2}",
+ "[Çöþý ƒŕöḿ »{path1}« ţö »{path2}« one two three]", PSEUDO_ACCENTED);
+ simple_helper("Today is {1,date} {1,time}",
+ "[Ţöðåý îš »{1,date}« »{1,time}« one two]", PSEUDO_ACCENTED);
+
+ // Multi-fragment messages
+ compound_helper("{USER}", " ", "is offline",
+ "[»{USER}« îš öƒƒļîñé one two]",
+ PSEUDO_ACCENTED);
+ compound_helper("Copy from ", "{path1}", " to {path2}",
+ "[Çöþý ƒŕöḿ »{path1}« ţö »{path2}« one two three]",
+ PSEUDO_ACCENTED);
+}
+
+TEST(Pseudolocales, ICUBidi) {
+ // Single-fragment messages
+ simple_helper("{placeholder}",
+ "\xe2\x80\x8f\xE2\x80\xae{placeholder}\xE2\x80\xac\xe2\x80\x8f",
+ PSEUDO_BIDI);
+ simple_helper(
+ "{COUNT, plural, one {one} other {other}}",
+ "{COUNT, plural, " \
+ "one {\xe2\x80\x8f\xE2\x80\xaeone\xE2\x80\xac\xe2\x80\x8f} " \
+ "other {\xe2\x80\x8f\xE2\x80\xaeother\xE2\x80\xac\xe2\x80\x8f}}",
+ PSEUDO_BIDI
+ );
+}
+
+TEST(Pseudolocales, Escaping) {
+ // Single-fragment messages
+ simple_helper("'{USER'} is offline",
+ "['{ÛŠÉŔ'} îš öƒƒļîñé one two three]", PSEUDO_ACCENTED);
+
+ // Multi-fragment messages
+ compound_helper("'{USER}", " ", "''is offline",
+ "['{ÛŠÉŔ} ''îš öƒƒļîñé one two three]", PSEUDO_ACCENTED);
+}
+
+TEST(Pseudolocales, PluralsAndSelects) {
+ simple_helper(
+ "{COUNT, plural, one {Delete a file} other {Delete {COUNT} files}}",
+ "[{COUNT, plural, one {Ðéļéţé å ƒîļé one two} " \
+ "other {Ðéļéţé »{COUNT}« ƒîļéš one two}}]",
+ PSEUDO_ACCENTED
+ );
+ simple_helper(
+ "Distance is {COUNT, plural, one {# mile} other {# miles}}",
+ "[Ðîšţåñçé îš {COUNT, plural, one {# ḿîļé one two} " \
+ "other {# ḿîļéš one two}}]",
+ PSEUDO_ACCENTED
+ );
+ simple_helper(
+ "{1, select, female {{1} added you} " \
+ "male {{1} added you} other {{1} added you}}",
+ "[{1, select, female {»{1}« åððéð ýöû one two} " \
+ "male {»{1}« åððéð ýöû one two} other {»{1}« åððéð ýöû one two}}]",
+ PSEUDO_ACCENTED
+ );
+
+ compound_helper(
+ "{COUNT, plural, one {Delete a file} " \
+ "other {Delete ", "{COUNT}", " files}}",
+ "[{COUNT, plural, one {Ðéļéţé å ƒîļé one two} " \
+ "other {Ðéļéţé »{COUNT}« ƒîļéš one two}}]",
+ PSEUDO_ACCENTED
+ );
+}
+
+TEST(Pseudolocales, NestedICU) {
+ simple_helper(
+ "{person, select, " \
+ "female {" \
+ "{num_circles, plural," \
+ "=0{{person} didn't add you to any of her circles.}" \
+ "=1{{person} added you to one of her circles.}" \
+ "other{{person} added you to her # circles.}}}" \
+ "male {" \
+ "{num_circles, plural," \
+ "=0{{person} didn't add you to any of his circles.}" \
+ "=1{{person} added you to one of his circles.}" \
+ "other{{person} added you to his # circles.}}}" \
+ "other {" \
+ "{num_circles, plural," \
+ "=0{{person} didn't add you to any of their circles.}" \
+ "=1{{person} added you to one of their circles.}" \
+ "other{{person} added you to their # circles.}}}}",
+ "[{person, select, " \
+ "female {" \
+ "{num_circles, plural," \
+ "=0{»{person}« ðîðñ'ţ åðð ýöû ţö åñý öƒ ĥéŕ çîŕçļéš." \
+ " one two three four five}" \
+ "=1{»{person}« åððéð ýöû ţö öñé öƒ ĥéŕ çîŕçļéš." \
+ " one two three four}" \
+ "other{»{person}« åððéð ýöû ţö ĥéŕ # çîŕçļéš." \
+ " one two three four}}}" \
+ "male {" \
+ "{num_circles, plural," \
+ "=0{»{person}« ðîðñ'ţ åðð ýöû ţö åñý öƒ ĥîš çîŕçļéš." \
+ " one two three four five}" \
+ "=1{»{person}« åððéð ýöû ţö öñé öƒ ĥîš çîŕçļéš." \
+ " one two three four}" \
+ "other{»{person}« åððéð ýöû ţö ĥîš # çîŕçļéš." \
+ " one two three four}}}" \
+ "other {{num_circles, plural," \
+ "=0{»{person}« ðîðñ'ţ åðð ýöû ţö åñý öƒ ţĥéîŕ çîŕçļéš." \
+ " one two three four five}" \
+ "=1{»{person}« åððéð ýöû ţö öñé öƒ ţĥéîŕ çîŕçļéš." \
+ " one two three four}" \
+ "other{»{person}« åððéð ýöû ţö ţĥéîŕ # çîŕçļéš." \
+ " one two three four}}}}]",
+ PSEUDO_ACCENTED
+ );
+}
+
+TEST(Pseudolocales, RedefineMethod) {
+ Pseudolocalizer pseudo(PSEUDO_ACCENTED);
+ String16 result = pseudo.text(String16(String8("Hello, ")));
+ pseudo.setMethod(NO_PSEUDOLOCALIZATION);
+ result.append(pseudo.text(String16(String8("world!"))));
+ ASSERT_EQ(String8("Ĥéļļö, world!"), String8(result));
+}