diff options
60 files changed, 2926 insertions, 599 deletions
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 6ea9f7e..bdd9e41 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -75,6 +75,7 @@ import android.os.SystemProperties; import android.os.Trace; import android.os.UserHandle; import android.provider.Settings; +import android.security.NetworkSecurityPolicy; import android.util.AndroidRuntimeException; import android.util.ArrayMap; import android.util.DisplayMetrics; @@ -4435,6 +4436,9 @@ public final class ActivityThread { StrictMode.enableDeathOnNetwork(); } + NetworkSecurityPolicy.getInstance().setCleartextTrafficPermitted( + (data.appInfo.flags & ApplicationInfo.FLAG_USES_CLEARTEXT_TRAFFIC) != 0); + if (data.debugMode != IApplicationThread.DEBUG_OFF) { // XXX should have option to change the port. Debug.changeDebugPort(8100); diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java index e1a2aa9..05c19db 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -334,6 +334,18 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { public static final int FLAG_FULL_BACKUP_ONLY = 1<<26; /** + * Value for {@link #flags}: {@code true} if the application may use cleartext network traffic + * (e.g., HTTP rather than HTTPS; WebSockets rather than WebSockets Secure; XMPP, IMAP, STMP + * without STARTTLS or TLS). If {@code false}, the app declares that it does not intend to use + * cleartext network traffic, in which case platform components (e.g., HTTP stacks, + * {@code WebView}, {@code MediaPlayer}) will refuse app's requests to use cleartext traffic. + * Third-party libraries are encouraged to honor this flag as well. + * + * @hide + */ + public static final int FLAG_USES_CLEARTEXT_TRAFFIC = 1<<27; + + /** * Value for {@link #flags}: true if code from this application will need to be * loaded into other applications' processes. On devices that support multiple * instruction sets, this implies the code might be loaded into a process that's diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 4d9445d..4952ba1 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -2550,6 +2550,12 @@ public class PackageParser { } if (sa.getBoolean( + com.android.internal.R.styleable.AndroidManifestApplication_usesCleartextTraffic, + true)) { + ai.flags |= ApplicationInfo.FLAG_USES_CLEARTEXT_TRAFFIC; + } + + if (sa.getBoolean( com.android.internal.R.styleable.AndroidManifestApplication_supportsRtl, false /* default is no RTL support*/)) { ai.flags |= ApplicationInfo.FLAG_SUPPORTS_RTL; diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java index 4834f97..0de9c70 100644 --- a/core/java/android/os/Process.java +++ b/core/java/android/os/Process.java @@ -631,6 +631,9 @@ public class Process { if ((debugFlags & Zygote.DEBUG_ENABLE_CHECKJNI) != 0) { argsForZygote.add("--enable-checkjni"); } + if ((debugFlags & Zygote.DEBUG_ENABLE_JIT) != 0) { + argsForZygote.add("--enable-jit"); + } if ((debugFlags & Zygote.DEBUG_ENABLE_ASSERT) != 0) { argsForZygote.add("--enable-assert"); } diff --git a/core/java/android/security/IKeystoreService.aidl b/core/java/android/security/IKeystoreService.aidl index ac6bbb7..14b5748 100644 --- a/core/java/android/security/IKeystoreService.aidl +++ b/core/java/android/security/IKeystoreService.aidl @@ -19,6 +19,7 @@ package android.security; import android.security.keymaster.ExportResult; import android.security.keymaster.KeyCharacteristics; import android.security.keymaster.KeymasterArguments; +import android.security.keymaster.KeymasterBlob; import android.security.keymaster.OperationResult; import android.security.KeystoreArguments; @@ -59,15 +60,16 @@ interface IKeystoreService { // Keymaster 0.4 methods int addRngEntropy(in byte[] data); - int generateKey(String alias, in KeymasterArguments arguments, int uid, int flags, + int generateKey(String alias, in KeymasterArguments arguments, in byte[] entropy, int uid, + int flags, out KeyCharacteristics characteristics); + int getKeyCharacteristics(String alias, in KeymasterBlob clientId, in KeymasterBlob appId, out KeyCharacteristics characteristics); - int getKeyCharacteristics(String alias, in byte[] clientId, - in byte[] appId, out KeyCharacteristics characteristics); int importKey(String alias, in KeymasterArguments arguments, int format, in byte[] keyData, int uid, int flags, out KeyCharacteristics characteristics); - ExportResult exportKey(String alias, int format, in byte[] clientId, in byte[] appId); + ExportResult exportKey(String alias, int format, in KeymasterBlob clientId, + in KeymasterBlob appId); OperationResult begin(IBinder appToken, String alias, int purpose, boolean pruneable, - in KeymasterArguments params, out KeymasterArguments operationParams); + in KeymasterArguments params, in byte[] entropy, out KeymasterArguments operationParams); OperationResult update(IBinder token, in KeymasterArguments params, in byte[] input); OperationResult finish(IBinder token, in KeymasterArguments params, in byte[] signature); int abort(IBinder handle); diff --git a/core/java/android/security/NetworkSecurityPolicy.java b/core/java/android/security/NetworkSecurityPolicy.java new file mode 100644 index 0000000..0626bbc --- /dev/null +++ b/core/java/android/security/NetworkSecurityPolicy.java @@ -0,0 +1,77 @@ +/** + * Copyright (c) 2015, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.security; + +/** + * Network security policy. + * + * <p>Network stacks/components should honor this policy to make it possible to centrally control + * the relevant aspects of network security behavior. + * + * <p>The policy currently consists of a single flag: whether cleartext network traffic is + * permitted. See {@link #isCleartextTrafficPermitted()}. + * + * @hide + */ +public class NetworkSecurityPolicy { + + private static final NetworkSecurityPolicy INSTANCE = new NetworkSecurityPolicy(); + + private NetworkSecurityPolicy() {} + + /** + * Gets the policy for this process. + * + * <p>It's fine to cache this reference. Any changes to the policy will be immediately visible + * through the reference. + */ + public static NetworkSecurityPolicy getInstance() { + return INSTANCE; + } + + /** + * Returns whether cleartext network traffic (e.g. HTTP, FTP, WebSockets, XMPP, IMAP, SMTP -- + * without TLS or STARTTLS) is permitted for this process. + * + * <p>When cleartext network traffic is not permitted, the platform's components (e.g. HTTP and + * FTP stacks, {@code WebView}, {@code MediaPlayer}) will refuse this process's requests to use + * cleartext traffic. Third-party libraries are strongly encouraged to honor this setting as + * well. + * + * <p>This flag is honored on a best effort basis because it's impossible to prevent all + * cleartext traffic from Android applications given the level of access provided to them. For + * example, there's no expectation that the {@link java.net.Socket} API will honor this flag + * because it cannot determine whether its traffic is in cleartext. However, most network + * traffic from applications is handled by higher-level network stacks/components which can + * honor this aspect of the policy. + */ + public boolean isCleartextTrafficPermitted() { + return libcore.net.NetworkSecurityPolicy.isCleartextTrafficPermitted(); + } + + /** + * Sets whether cleartext network traffic is permitted for this process. + * + * <p>This method is used by the platform early on in the application's initialization to set + * the policy. + * + * @hide + */ + public void setCleartextTrafficPermitted(boolean permitted) { + libcore.net.NetworkSecurityPolicy.setCleartextTrafficPermitted(permitted); + } +} diff --git a/core/java/android/security/keymaster/KeymasterBlob.aidl b/core/java/android/security/keymaster/KeymasterBlob.aidl new file mode 100644 index 0000000..8f70f7c --- /dev/null +++ b/core/java/android/security/keymaster/KeymasterBlob.aidl @@ -0,0 +1,20 @@ +/** + * Copyright (c) 2015, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.security.keymaster; + +/* @hide */ +parcelable KeymasterBlob; diff --git a/core/java/android/security/keymaster/KeymasterBlob.java b/core/java/android/security/keymaster/KeymasterBlob.java new file mode 100644 index 0000000..cb95604 --- /dev/null +++ b/core/java/android/security/keymaster/KeymasterBlob.java @@ -0,0 +1,55 @@ +/** + * Copyright (c) 2015, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.security.keymaster; + +import android.os.Parcel; +import android.os.Parcelable; + +/** + * @hide + */ +public class KeymasterBlob implements Parcelable { + public byte[] blob; + + public KeymasterBlob(byte[] blob) { + this.blob = blob; + } + public static final Parcelable.Creator<KeymasterBlob> CREATOR = new + Parcelable.Creator<KeymasterBlob>() { + public KeymasterBlob createFromParcel(Parcel in) { + return new KeymasterBlob(in); + } + + public KeymasterBlob[] newArray(int length) { + return new KeymasterBlob[length]; + } + }; + + protected KeymasterBlob(Parcel in) { + blob = in.createByteArray(); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeByteArray(blob); + } +} diff --git a/core/java/android/security/keymaster/KeymasterBlobArgument.java b/core/java/android/security/keymaster/KeymasterBlobArgument.java index 27f1153..a9085c4 100644 --- a/core/java/android/security/keymaster/KeymasterBlobArgument.java +++ b/core/java/android/security/keymaster/KeymasterBlobArgument.java @@ -27,6 +27,13 @@ class KeymasterBlobArgument extends KeymasterArgument { public KeymasterBlobArgument(int tag, byte[] blob) { super(tag); + switch (KeymasterDefs.getTagType(tag)) { + case KeymasterDefs.KM_BIGNUM: + case KeymasterDefs.KM_BYTES: + break; // OK. + default: + throw new IllegalArgumentException("Bad blob tag " + tag); + } this.blob = blob; } diff --git a/core/java/android/security/keymaster/KeymasterBooleanArgument.java b/core/java/android/security/keymaster/KeymasterBooleanArgument.java index 8e17db4..cc04bb6 100644 --- a/core/java/android/security/keymaster/KeymasterBooleanArgument.java +++ b/core/java/android/security/keymaster/KeymasterBooleanArgument.java @@ -29,6 +29,12 @@ class KeymasterBooleanArgument extends KeymasterArgument { public KeymasterBooleanArgument(int tag) { super(tag); + switch (KeymasterDefs.getTagType(tag)) { + case KeymasterDefs.KM_BOOL: + break; // OK. + default: + throw new IllegalArgumentException("Bad bool tag " + tag); + } } public KeymasterBooleanArgument(int tag, Parcel in) { diff --git a/core/java/android/security/keymaster/KeymasterDateArgument.java b/core/java/android/security/keymaster/KeymasterDateArgument.java index e8f4055..47db6ea 100644 --- a/core/java/android/security/keymaster/KeymasterDateArgument.java +++ b/core/java/android/security/keymaster/KeymasterDateArgument.java @@ -29,6 +29,12 @@ class KeymasterDateArgument extends KeymasterArgument { public KeymasterDateArgument(int tag, Date date) { super(tag); + switch (KeymasterDefs.getTagType(tag)) { + case KeymasterDefs.KM_DATE: + break; // OK. + default: + throw new IllegalArgumentException("Bad date tag " + tag); + } this.date = date; } diff --git a/core/java/android/security/keymaster/KeymasterDefs.java b/core/java/android/security/keymaster/KeymasterDefs.java index 88cad79..c2ebbc6 100644 --- a/core/java/android/security/keymaster/KeymasterDefs.java +++ b/core/java/android/security/keymaster/KeymasterDefs.java @@ -16,6 +16,9 @@ package android.security.keymaster; +import java.util.HashMap; +import java.util.Map; + /** * Class tracking all the keymaster enum values needed for the binder API to keystore. * This must be kept in sync with hardware/libhardware/include/hardware/keymaster_defs.h @@ -37,6 +40,7 @@ public final class KeymasterDefs { 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; // Tag values. public static final int KM_TAG_INVALID = KM_INVALID | 0; @@ -66,9 +70,10 @@ public final class KeymasterDefs { 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_NO_AUTH_REQUIRED = KM_BOOL | 502; - public static final int KM_TAG_USER_AUTH_ID = KM_INT_REP | 503; - public static final int KM_TAG_AUTH_TIMEOUT = KM_INT | 504; + public static final int KM_TAG_USER_SECURE_ID = KM_LONG_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_ALL_APPLICATIONS = KM_BOOL | 600; public static final int KM_TAG_APPLICATION_ID = KM_BYTES | 601; @@ -82,6 +87,7 @@ public final class KeymasterDefs { 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_CHUNK_LENGTH = KM_INT | 1002; + public static final int KM_TAG_AUTH_TOKEN = KM_BYTES | 1003; // Algorithm values. public static final int KM_ALGORITHM_RSA = 1; @@ -221,7 +227,53 @@ public final class KeymasterDefs { public static final int KM_ERROR_VERSION_MISMATCH = -101; public static final int KM_ERROR_UNKNOWN_ERROR = -1000; + public static final Map<Integer, String> sErrorCodeToString = new HashMap<Integer, String>(); + static { + sErrorCodeToString.put(KM_ERROR_OK, "OK"); + sErrorCodeToString.put(KM_ERROR_UNSUPPORTED_PURPOSE, "Unsupported purpose"); + sErrorCodeToString.put(KM_ERROR_INCOMPATIBLE_PURPOSE, "Incompatible purpose"); + sErrorCodeToString.put(KM_ERROR_UNSUPPORTED_ALGORITHM, "Unsupported algorithm"); + sErrorCodeToString.put(KM_ERROR_INCOMPATIBLE_ALGORITHM, "Incompatible algorithm"); + sErrorCodeToString.put(KM_ERROR_UNSUPPORTED_KEY_SIZE, "Unsupported key size"); + sErrorCodeToString.put(KM_ERROR_UNSUPPORTED_BLOCK_MODE, "Unsupported block mode"); + sErrorCodeToString.put(KM_ERROR_INCOMPATIBLE_BLOCK_MODE, "Incompatible block mode"); + sErrorCodeToString.put(KM_ERROR_UNSUPPORTED_TAG_LENGTH, + "Unsupported authentication tag length"); + sErrorCodeToString.put(KM_ERROR_UNSUPPORTED_PADDING_MODE, "Unsupported padding mode"); + sErrorCodeToString.put(KM_ERROR_INCOMPATIBLE_PADDING_MODE, "Incompatible padding mode"); + sErrorCodeToString.put(KM_ERROR_UNSUPPORTED_DIGEST, "Unsupported digest"); + sErrorCodeToString.put(KM_ERROR_INCOMPATIBLE_DIGEST, "Incompatible digest"); + sErrorCodeToString.put(KM_ERROR_INVALID_EXPIRATION_TIME, "Invalid expiration time"); + sErrorCodeToString.put(KM_ERROR_INVALID_USER_ID, "Invalid user ID"); + sErrorCodeToString.put(KM_ERROR_INVALID_AUTHORIZATION_TIMEOUT, + "Invalid user authorization timeout"); + sErrorCodeToString.put(KM_ERROR_UNSUPPORTED_KEY_FORMAT, "Unsupported key format"); + sErrorCodeToString.put(KM_ERROR_INCOMPATIBLE_KEY_FORMAT, "Incompatible key format"); + sErrorCodeToString.put(KM_ERROR_INVALID_INPUT_LENGTH, "Invalid input length"); + sErrorCodeToString.put(KM_ERROR_KEY_NOT_YET_VALID, "Key not yet valid"); + sErrorCodeToString.put(KM_ERROR_KEY_EXPIRED, "Key expired"); + sErrorCodeToString.put(KM_ERROR_KEY_USER_NOT_AUTHENTICATED, "Key user not authenticated"); + sErrorCodeToString.put(KM_ERROR_INVALID_OPERATION_HANDLE, "Invalid operation handle"); + sErrorCodeToString.put(KM_ERROR_VERIFICATION_FAILED, "Signature/MAC verification failed"); + sErrorCodeToString.put(KM_ERROR_TOO_MANY_OPERATIONS, "Too many operations"); + sErrorCodeToString.put(KM_ERROR_INVALID_KEY_BLOB, "Invalid key blob"); + sErrorCodeToString.put(KM_ERROR_INVALID_ARGUMENT, "Invalid argument"); + sErrorCodeToString.put(KM_ERROR_UNSUPPORTED_TAG, "Unsupported tag"); + sErrorCodeToString.put(KM_ERROR_INVALID_TAG, "Invalid tag"); + sErrorCodeToString.put(KM_ERROR_MEMORY_ALLOCATION_FAILED, "Memory allocation failed"); + sErrorCodeToString.put(KM_ERROR_UNIMPLEMENTED, "Not implemented"); + sErrorCodeToString.put(KM_ERROR_UNKNOWN_ERROR, "Unknown error"); + } + public static int getTagType(int tag) { return tag & (0xF << 28); } + + public static String getErrorMessage(int errorCode) { + String result = sErrorCodeToString.get(errorCode); + if (result != null) { + return result; + } + return String.valueOf(errorCode); + } } diff --git a/core/java/android/security/keymaster/KeymasterIntArgument.java b/core/java/android/security/keymaster/KeymasterIntArgument.java index 71797ae..94ff87e 100644 --- a/core/java/android/security/keymaster/KeymasterIntArgument.java +++ b/core/java/android/security/keymaster/KeymasterIntArgument.java @@ -27,6 +27,15 @@ class KeymasterIntArgument extends KeymasterArgument { public KeymasterIntArgument(int tag, int value) { super(tag); + switch (KeymasterDefs.getTagType(tag)) { + case KeymasterDefs.KM_INT: + case KeymasterDefs.KM_INT_REP: + case KeymasterDefs.KM_ENUM: + case KeymasterDefs.KM_ENUM_REP: + break; // OK. + default: + throw new IllegalArgumentException("Bad int tag " + tag); + } this.value = value; } diff --git a/core/java/android/security/keymaster/KeymasterLongArgument.java b/core/java/android/security/keymaster/KeymasterLongArgument.java index 781b1ab..d51d7e6 100644 --- a/core/java/android/security/keymaster/KeymasterLongArgument.java +++ b/core/java/android/security/keymaster/KeymasterLongArgument.java @@ -27,6 +27,12 @@ class KeymasterLongArgument extends KeymasterArgument { public KeymasterLongArgument(int tag, long value) { super(tag); + switch (KeymasterDefs.getTagType(tag)) { + case KeymasterDefs.KM_LONG: + break; // OK. + default: + throw new IllegalArgumentException("Bad long tag " + tag); + } this.value = value; } diff --git a/core/java/android/text/style/URLSpan.java b/core/java/android/text/style/URLSpan.java index d29bfb6..0669b6f 100644 --- a/core/java/android/text/style/URLSpan.java +++ b/core/java/android/text/style/URLSpan.java @@ -16,6 +16,7 @@ package android.text.style; +import android.content.ActivityNotFoundException; import android.content.Context; import android.content.Intent; import android.net.Uri; @@ -23,6 +24,7 @@ import android.os.Parcel; import android.provider.Browser; import android.text.ParcelableSpan; import android.text.TextUtils; +import android.util.Log; import android.view.View; public class URLSpan extends ClickableSpan implements ParcelableSpan { @@ -59,6 +61,10 @@ public class URLSpan extends ClickableSpan implements ParcelableSpan { Context context = widget.getContext(); Intent intent = new Intent(Intent.ACTION_VIEW, uri); intent.putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName()); - context.startActivity(intent); + try { + context.startActivity(intent); + } catch (ActivityNotFoundException e) { + Log.w("URLSpan", "Actvity was not found for intent, " + intent.toString()); + } } } diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index 325ffdd..1416e1b 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -1993,8 +1993,8 @@ public final class InputMethodManager { List<Object> info = mService.getShortcutInputMethodsAndSubtypes(); // "info" has imi1, subtype1, subtype2, imi2, subtype2, imi3, subtype3..in the list ArrayList<InputMethodSubtype> subtypes = null; - final int N = info.size(); - if (info != null && N > 0) { + if (info != null && !info.isEmpty()) { + final int N = info.size(); for (int i = 0; i < N; ++i) { Object o = info.get(i); if (o instanceof InputMethodInfo) { diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index 936da32..2aaad7a 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -3110,7 +3110,7 @@ public class Editor { if (isTopLeftVisible || isBottomRightVisible) { characterBoundsFlags |= CursorAnchorInfo.FLAG_HAS_VISIBLE_REGION; } - if (!isTopLeftVisible || !isTopLeftVisible) { + if (!isTopLeftVisible || !isBottomRightVisible) { characterBoundsFlags |= CursorAnchorInfo.FLAG_HAS_INVISIBLE_REGION; } if (isRtl) { diff --git a/core/java/android/widget/Gallery.java b/core/java/android/widget/Gallery.java index f7c839f..b4a003a 100644 --- a/core/java/android/widget/Gallery.java +++ b/core/java/android/widget/Gallery.java @@ -1210,13 +1210,13 @@ public class Gallery extends AbsSpinner implements GestureDetector.OnGestureList switch (keyCode) { case KeyEvent.KEYCODE_DPAD_LEFT: - if (movePrevious()) { + if (moveDirection(-1)) { playSoundEffect(SoundEffectConstants.NAVIGATION_LEFT); return true; } break; case KeyEvent.KEYCODE_DPAD_RIGHT: - if (moveNext()) { + if (moveDirection(1)) { playSoundEffect(SoundEffectConstants.NAVIGATION_RIGHT); return true; } @@ -1256,18 +1256,12 @@ public class Gallery extends AbsSpinner implements GestureDetector.OnGestureList return super.onKeyUp(keyCode, event); } - boolean movePrevious() { - if (mItemCount > 0 && mSelectedPosition > 0) { - scrollToChild(mSelectedPosition - mFirstPosition - 1); - return true; - } else { - return false; - } - } + boolean moveDirection(int direction) { + direction = isLayoutRtl() ? -direction : direction; + int targetPosition = mSelectedPosition + direction; - boolean moveNext() { - if (mItemCount > 0 && mSelectedPosition < mItemCount - 1) { - scrollToChild(mSelectedPosition - mFirstPosition + 1); + if (mItemCount > 0 && targetPosition >= 0 && targetPosition < mItemCount) { + scrollToChild(targetPosition - mFirstPosition); return true; } else { return false; diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java index fced092..8674a21 100644 --- a/core/java/com/android/internal/os/Zygote.java +++ b/core/java/com/android/internal/os/Zygote.java @@ -34,10 +34,13 @@ public final class Zygote { public static final int DEBUG_ENABLE_CHECKJNI = 1 << 1; /** enable Java programming language "assert" statements */ public static final int DEBUG_ENABLE_ASSERT = 1 << 2; - /** disable the JIT compiler */ + /** disable the AOT compiler and JIT */ public static final int DEBUG_ENABLE_SAFEMODE = 1 << 3; /** Enable logging of third-party JNI activity. */ public static final int DEBUG_ENABLE_JNI_LOGGING = 1 << 4; + /** enable the JIT compiler */ + public static final int DEBUG_ENABLE_JIT = 1 << 5; + /** No external storage should be mounted. */ public static final int MOUNT_EXTERNAL_NONE = 0; diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java index 9c97cc1..0dc242d 100644 --- a/core/java/com/android/internal/os/ZygoteConnection.java +++ b/core/java/com/android/internal/os/ZygoteConnection.java @@ -322,7 +322,7 @@ class ZygoteConnection { /** * From --enable-debugger, --enable-checkjni, --enable-assert, - * --enable-safemode, and --enable-jni-logging. + * --enable-safemode, --enable-jit, and --enable-jni-logging. */ int debugFlags; @@ -432,6 +432,8 @@ class ZygoteConnection { debugFlags |= Zygote.DEBUG_ENABLE_SAFEMODE; } else if (arg.equals("--enable-checkjni")) { debugFlags |= Zygote.DEBUG_ENABLE_CHECKJNI; + } else if (arg.equals("--enable-jit")) { + debugFlags |= Zygote.DEBUG_ENABLE_JIT; } else if (arg.equals("--enable-jni-logging")) { debugFlags |= Zygote.DEBUG_ENABLE_JNI_LOGGING; } else if (arg.equals("--enable-assert")) { diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index 516bb65..ce50d96 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -648,9 +648,9 @@ int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv) /* * JIT related options. */ - parseRuntimeOption("debug.dalvik.vm.usejit", usejitOptsBuf, "-Xusejit:"); - parseRuntimeOption("debug.dalvik.vm.jitcodecachesize", jitcodecachesizeOptsBuf, "-Xjitcodecachesize:"); - parseRuntimeOption("debug.dalvik.vm.jitthreshold", jitthresholdOptsBuf, "-Xjitthreshold:"); + parseRuntimeOption("dalvik.vm.usejit", usejitOptsBuf, "-Xusejit:"); + parseRuntimeOption("dalvik.vm.jitcodecachesize", jitcodecachesizeOptsBuf, "-Xjitcodecachesize:"); + parseRuntimeOption("dalvik.vm.jitthreshold", jitthresholdOptsBuf, "-Xjitthreshold:"); property_get("ro.config.low_ram", propBuf, ""); if (strcmp(propBuf, "true") == 0) { diff --git a/core/res/res/drawable/pointer_arrow_icon.xml b/core/res/res/drawable/pointer_arrow_icon.xml index 8f7d658..72af0c1 100644 --- a/core/res/res/drawable/pointer_arrow_icon.xml +++ b/core/res/res/drawable/pointer_arrow_icon.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> <pointer-icon xmlns:android="http://schemas.android.com/apk/res/android" android:bitmap="@drawable/pointer_arrow" - android:hotSpotX="6dp" - android:hotSpotY="6dp" /> + android:hotSpotX="5dp" + android:hotSpotY="5dp" /> diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index 0c3fb9a..ea592cf 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -389,6 +389,15 @@ with the same {@link android.R.attr#taskAffinity} as it has. --> <attr name="allowTaskReparenting" format="boolean" /> + <!-- Declare that this application may use cleartext traffic (e.g., HTTP rather than HTTPS; + WebSockets rather than WebSockets Secure; XMPP, IMAP, STMP without STARTTLS or TLS). + Defaults to true. If set to false {@code false}, the app declares that it does not + intend to use cleartext network traffic, in which case platform components (e.g., + HTTP stacks, {@code WebView}, {@code MediaPlayer}) will refuse app's requests to use + cleartext traffic. Third-party libraries are encouraged to honor this flag as well. + @hide --> + <attr name="usesCleartextTraffic" format="boolean" /> + <!-- Declare that code from this application will need to be loaded into other applications' processes. On devices that support multiple instruction sets, this implies the code might be loaded into a process that's using any of the devices @@ -1133,6 +1142,14 @@ "com.google". --> <attr name="requiredAccountType" format="string"/> <attr name="isGame" /> + <!-- Declare that this application may use cleartext traffic (e.g., HTTP rather than HTTPS; + WebSockets rather than WebSockets Secure; XMPP, IMAP, STMP without STARTTLS or TLS). + Defaults to true. If set to false {@code false}, the app declares that it does not + intend to use cleartext network traffic, in which case platform components (e.g., + HTTP stacks, {@code WebView}, {@code MediaPlayer}) will refuse app's requests to use + cleartext traffic. Third-party libraries are encouraged to honor this flag as well. + @hide --> + <attr name="usesCleartextTraffic" /> <attr name="multiArch" /> </declare-styleable> diff --git a/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java index dd26019..a64f0ce 100644 --- a/graphics/java/android/graphics/drawable/VectorDrawable.java +++ b/graphics/java/android/graphics/drawable/VectorDrawable.java @@ -791,7 +791,6 @@ public class VectorDrawable extends Drawable { // is no need for deep copying. private final Path mPath; private final Path mRenderPath; - private static final Matrix IDENTITY_MATRIX = new Matrix(); private final Matrix mFinalPathMatrix = new Matrix(); private Paint mStrokePaint; @@ -932,7 +931,7 @@ public class VectorDrawable extends Drawable { public void draw(Canvas canvas, int w, int h, ColorFilter filter) { // Travese the tree in pre-order to draw. - drawGroupTree(mRootGroup, IDENTITY_MATRIX, canvas, w, h, filter); + drawGroupTree(mRootGroup, Matrix.IDENTITY_MATRIX, canvas, w, h, filter); } private void drawPath(VGroup vGroup, VPath vPath, Canvas canvas, int w, int h, diff --git a/keystore/java/android/security/AndroidKeyPairGenerator.java b/keystore/java/android/security/AndroidKeyPairGenerator.java index 9d9a173..5fae831 100644 --- a/keystore/java/android/security/AndroidKeyPairGenerator.java +++ b/keystore/java/android/security/AndroidKeyPairGenerator.java @@ -136,6 +136,8 @@ public abstract class AndroidKeyPairGenerator extends KeyPairGeneratorSpi { throw new IllegalStateException("could not generate key in keystore"); } + Credentials.deleteSecretKeyTypeForAlias(mKeyStore, alias); + final PrivateKey privKey; final OpenSSLEngine engine = OpenSSLEngine.getInstance("keystore"); try { diff --git a/keystore/java/android/security/AndroidKeyStore.java b/keystore/java/android/security/AndroidKeyStore.java index acbae8f..f3eb317 100644 --- a/keystore/java/android/security/AndroidKeyStore.java +++ b/keystore/java/android/security/AndroidKeyStore.java @@ -19,6 +19,9 @@ package android.security; import com.android.org.conscrypt.OpenSSLEngine; import com.android.org.conscrypt.OpenSSLKeyHolder; +import android.security.keymaster.KeyCharacteristics; +import android.security.keymaster.KeymasterArguments; +import android.security.keymaster.KeymasterDefs; import android.util.Log; import java.io.ByteArrayInputStream; @@ -31,6 +34,7 @@ import java.security.KeyStore.Entry; import java.security.KeyStore.PrivateKeyEntry; import java.security.KeyStore.ProtectionParameter; import java.security.KeyStore; +import java.security.KeyStore.SecretKeyEntry; import java.security.KeyStoreException; import java.security.KeyStoreSpi; import java.security.NoSuchAlgorithmException; @@ -50,6 +54,8 @@ import java.util.HashSet; import java.util.Iterator; import java.util.Set; +import javax.crypto.SecretKey; + /** * A java.security.KeyStore interface for the Android KeyStore. An instance of * it can be created via the {@link java.security.KeyStore#getInstance(String) @@ -77,18 +83,72 @@ public class AndroidKeyStore extends KeyStoreSpi { @Override public Key engineGetKey(String alias, char[] password) throws NoSuchAlgorithmException, UnrecoverableKeyException { - if (!isKeyEntry(alias)) { - return null; - } + if (isPrivateKeyEntry(alias)) { + final OpenSSLEngine engine = OpenSSLEngine.getInstance("keystore"); + try { + return engine.getPrivateKeyById(Credentials.USER_PRIVATE_KEY + alias); + } catch (InvalidKeyException e) { + UnrecoverableKeyException t = new UnrecoverableKeyException("Can't get key"); + t.initCause(e); + throw t; + } + } else if (isSecretKeyEntry(alias)) { + KeyCharacteristics keyCharacteristics = new KeyCharacteristics(); + String keyAliasInKeystore = Credentials.USER_SECRET_KEY + alias; + int errorCode = mKeyStore.getKeyCharacteristics( + keyAliasInKeystore, null, null, keyCharacteristics); + if ((errorCode != KeymasterDefs.KM_ERROR_OK) + && (errorCode != android.security.KeyStore.NO_ERROR)) { + throw new UnrecoverableKeyException("Failed to load information about key." + + " Error code: " + errorCode); + } - final OpenSSLEngine engine = OpenSSLEngine.getInstance("keystore"); - try { - return engine.getPrivateKeyById(Credentials.USER_PRIVATE_KEY + alias); - } catch (InvalidKeyException e) { - UnrecoverableKeyException t = new UnrecoverableKeyException("Can't get key"); - t.initCause(e); - throw t; + int keymasterAlgorithm = + keyCharacteristics.hwEnforced.getInt(KeymasterDefs.KM_TAG_ALGORITHM, -1); + if (keymasterAlgorithm == -1) { + keymasterAlgorithm = + keyCharacteristics.swEnforced.getInt(KeymasterDefs.KM_TAG_ALGORITHM, -1); + } + if (keymasterAlgorithm == -1) { + throw new UnrecoverableKeyException("Key algorithm unknown"); + } + @KeyStoreKeyConstraints.AlgorithmEnum int keyAlgorithm; + try { + keyAlgorithm = KeyStoreKeyConstraints.Algorithm.fromKeymaster(keymasterAlgorithm); + } catch (IllegalArgumentException e) { + throw (UnrecoverableKeyException) + new UnrecoverableKeyException("Unsupported key algorithm").initCause(e); + } + + int keymasterDigest = + keyCharacteristics.hwEnforced.getInt(KeymasterDefs.KM_TAG_DIGEST, -1); + if (keymasterDigest == -1) { + keymasterDigest = + keyCharacteristics.swEnforced.getInt(KeymasterDefs.KM_TAG_DIGEST, -1); + } + @KeyStoreKeyConstraints.DigestEnum Integer digest = null; + if (keymasterDigest != -1) { + try { + digest = KeyStoreKeyConstraints.Digest.fromKeymaster(keymasterDigest); + } catch (IllegalArgumentException e) { + throw (UnrecoverableKeyException) + new UnrecoverableKeyException("Unsupported digest").initCause(e); + } + } + + String keyAlgorithmString; + try { + keyAlgorithmString = KeyStoreKeyConstraints.Algorithm.toJCASecretKeyAlgorithm( + keyAlgorithm, digest); + } catch (IllegalArgumentException e) { + throw (UnrecoverableKeyException) + new UnrecoverableKeyException("Unsupported secret key type").initCause(e); + } + + return new KeyStoreSecretKey(keyAliasInKeystore, keyAlgorithmString); } + + return null; } @Override @@ -186,6 +246,11 @@ public class AndroidKeyStore extends KeyStoreSpi { return d; } + d = getModificationDate(Credentials.USER_SECRET_KEY + alias); + if (d != null) { + return d; + } + d = getModificationDate(Credentials.USER_CERTIFICATE + alias); if (d != null) { return d; @@ -203,8 +268,10 @@ public class AndroidKeyStore extends KeyStoreSpi { if (key instanceof PrivateKey) { setPrivateKeyEntry(alias, (PrivateKey) key, chain, null); + } else if (key instanceof SecretKey) { + setSecretKeyEntry(alias, (SecretKey) key, null); } else { - throw new KeyStoreException("Only PrivateKeys are supported"); + throw new KeyStoreException("Only PrivateKey and SecretKey are supported"); } } @@ -319,6 +386,7 @@ public class AndroidKeyStore extends KeyStoreSpi { Credentials.deleteAllTypesForAlias(mKeyStore, alias); } else { Credentials.deleteCertificateTypesForAlias(mKeyStore, alias); + Credentials.deleteSecretKeyTypeForAlias(mKeyStore, alias); } final int flags = (params == null) ? 0 : params.getFlags(); @@ -340,6 +408,160 @@ public class AndroidKeyStore extends KeyStoreSpi { } } + private void setSecretKeyEntry(String entryAlias, SecretKey key, KeyStoreParameter params) + throws KeyStoreException { + if (key instanceof KeyStoreSecretKey) { + // KeyStore-backed secret key. It cannot be duplicated into another entry and cannot + // overwrite its own entry. + String keyAliasInKeystore = ((KeyStoreSecretKey) key).getAlias(); + if (keyAliasInKeystore == null) { + throw new KeyStoreException("KeyStore-backed secret key does not have an alias"); + } + if (!keyAliasInKeystore.startsWith(Credentials.USER_SECRET_KEY)) { + throw new KeyStoreException("KeyStore-backed secret key has invalid alias: " + + keyAliasInKeystore); + } + String keyEntryAlias = + keyAliasInKeystore.substring(Credentials.USER_SECRET_KEY.length()); + if (!entryAlias.equals(keyEntryAlias)) { + throw new KeyStoreException("Can only replace KeyStore-backed keys with same" + + " alias: " + entryAlias + " != " + keyEntryAlias); + } + // This is the entry where this key is already stored. No need to do anything. + if (params != null) { + throw new KeyStoreException("Modifying KeyStore-backed key using protection" + + " parameters not supported"); + } + return; + } + + if (params == null) { + throw new KeyStoreException( + "Protection parameters must be specified when importing a symmetric key"); + } + + // Not a KeyStore-backed secret key -- import its key material into keystore. + String keyExportFormat = key.getFormat(); + if (keyExportFormat == null) { + throw new KeyStoreException( + "Only secret keys that export their key material are supported"); + } else if (!"RAW".equals(keyExportFormat)) { + throw new KeyStoreException( + "Unsupported secret key material export format: " + keyExportFormat); + } + byte[] keyMaterial = key.getEncoded(); + if (keyMaterial == null) { + throw new KeyStoreException("Key did not export its key material despite supporting" + + " RAW format export"); + } + + String keyAlgorithmString = key.getAlgorithm(); + @KeyStoreKeyConstraints.AlgorithmEnum int keyAlgorithm; + @KeyStoreKeyConstraints.AlgorithmEnum Integer digest; + try { + keyAlgorithm = + KeyStoreKeyConstraints.Algorithm.fromJCASecretKeyAlgorithm(keyAlgorithmString); + digest = KeyStoreKeyConstraints.Digest.fromJCASecretKeyAlgorithm(keyAlgorithmString); + } catch (IllegalArgumentException e) { + throw new KeyStoreException("Unsupported secret key algorithm: " + keyAlgorithmString); + } + + if ((params.getAlgorithm() != null) && (params.getAlgorithm() != keyAlgorithm)) { + throw new KeyStoreException("Key algorithm mismatch. Key: " + keyAlgorithmString + + ", parameter spec: " + + KeyStoreKeyConstraints.Algorithm.toString(params.getAlgorithm())); + } + + KeymasterArguments args = new KeymasterArguments(); + args.addInt(KeymasterDefs.KM_TAG_ALGORITHM, + KeyStoreKeyConstraints.Algorithm.toKeymaster(keyAlgorithm)); + + if (digest != null) { + // Digest available from JCA key algorithm + if (params.getDigest() != null) { + // Digest also specified in parameters -- check that these two match + if (digest != params.getDigest()) { + throw new KeyStoreException("Key digest mismatch. Key: " + keyAlgorithmString + + ", parameter spec: " + + KeyStoreKeyConstraints.Digest.toString(params.getDigest())); + } + } + } else { + // Digest not available from JCA key algorithm + digest = params.getDigest(); + } + if (digest != null) { + args.addInt(KeymasterDefs.KM_TAG_DIGEST, + KeyStoreKeyConstraints.Digest.toKeymaster(digest)); + } + + @KeyStoreKeyConstraints.PurposeEnum int purposes = (params.getPurposes() != null) + ? params.getPurposes() + : (KeyStoreKeyConstraints.Purpose.ENCRYPT + | KeyStoreKeyConstraints.Purpose.DECRYPT + | KeyStoreKeyConstraints.Purpose.SIGN + | KeyStoreKeyConstraints.Purpose.VERIFY); + for (int keymasterPurpose : + KeyStoreKeyConstraints.Purpose.allToKeymaster(purposes)) { + args.addInt(KeymasterDefs.KM_TAG_PURPOSE, keymasterPurpose); + } + if (params.getBlockMode() != null) { + args.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE, + KeyStoreKeyConstraints.BlockMode.toKeymaster(params.getBlockMode())); + } + if (params.getPadding() != null) { + args.addInt(KeymasterDefs.KM_TAG_PADDING, + KeyStoreKeyConstraints.Padding.toKeymaster(params.getPadding())); + } + if (params.getMaxUsesPerBoot() != null) { + args.addInt(KeymasterDefs.KM_TAG_MAX_USES_PER_BOOT, params.getMaxUsesPerBoot()); + } + if (params.getMinSecondsBetweenOperations() != null) { + args.addInt(KeymasterDefs.KM_TAG_MIN_SECONDS_BETWEEN_OPS, + params.getMinSecondsBetweenOperations()); + } + if (params.getUserAuthenticators().isEmpty()) { + args.addBoolean(KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED); + } else { + // TODO: Pass-in user authenticator IDs once the Keymaster API has stabilized +// for (int userAuthenticatorId : params.getUserAuthenticators()) { +// args.addInt(KeymasterDefs.KM_TAG_USER_AUTH_ID, userAuthenticatorId); +// } + } + if (params.getUserAuthenticationValidityDurationSeconds() != null) { + args.addInt(KeymasterDefs.KM_TAG_AUTH_TIMEOUT, + params.getUserAuthenticationValidityDurationSeconds()); + } + if (params.getKeyValidityStart() != null) { + args.addDate(KeymasterDefs.KM_TAG_ACTIVE_DATETIME, params.getKeyValidityStart()); + } + if (params.getKeyValidityForOriginationEnd() != null) { + args.addDate(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME, + params.getKeyValidityForOriginationEnd()); + } + if (params.getKeyValidityForConsumptionEnd() != null) { + args.addDate(KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME, + params.getKeyValidityForConsumptionEnd()); + } + + // TODO: Remove this once keymaster does not require us to specify the size of imported key. + args.addInt(KeymasterDefs.KM_TAG_KEY_SIZE, keyMaterial.length * 8); + + Credentials.deleteAllTypesForAlias(mKeyStore, entryAlias); + String keyAliasInKeystore = Credentials.USER_SECRET_KEY + entryAlias; + int errorCode = mKeyStore.importKey( + keyAliasInKeystore, + args, + KeymasterDefs.KM_KEY_FORMAT_RAW, + keyMaterial, + params.getFlags(), + new KeyCharacteristics()); + if (errorCode != android.security.KeyStore.NO_ERROR) { + throw new KeyStoreException("Failed to import secret key. Keystore error code: " + + errorCode); + } + } + @Override public void engineSetKeyEntry(String alias, byte[] userKey, Certificate[] chain) throws KeyStoreException { @@ -413,6 +635,7 @@ public class AndroidKeyStore extends KeyStoreSpi { } return mKeyStore.contains(Credentials.USER_PRIVATE_KEY + alias) + || mKeyStore.contains(Credentials.USER_SECRET_KEY + alias) || mKeyStore.contains(Credentials.USER_CERTIFICATE + alias) || mKeyStore.contains(Credentials.CA_CERTIFICATE + alias); } @@ -428,6 +651,10 @@ public class AndroidKeyStore extends KeyStoreSpi { } private boolean isKeyEntry(String alias) { + return isPrivateKeyEntry(alias) || isSecretKeyEntry(alias); + } + + private boolean isPrivateKeyEntry(String alias) { if (alias == null) { throw new NullPointerException("alias == null"); } @@ -435,6 +662,14 @@ public class AndroidKeyStore extends KeyStoreSpi { return mKeyStore.contains(Credentials.USER_PRIVATE_KEY + alias); } + private boolean isSecretKeyEntry(String alias) { + if (alias == null) { + throw new NullPointerException("alias == null"); + } + + return mKeyStore.contains(Credentials.USER_SECRET_KEY + alias); + } + private boolean isCertificateEntry(String alias) { if (alias == null) { throw new NullPointerException("alias == null"); @@ -554,11 +789,14 @@ public class AndroidKeyStore extends KeyStoreSpi { PrivateKeyEntry prE = (PrivateKeyEntry) entry; setPrivateKeyEntry(alias, prE.getPrivateKey(), prE.getCertificateChain(), (KeyStoreParameter) param); - return; + } else if (entry instanceof SecretKeyEntry) { + SecretKeyEntry secE = (SecretKeyEntry) entry; + setSecretKeyEntry(alias, secE.getSecretKey(), (KeyStoreParameter) param); + } else { + throw new KeyStoreException( + "Entry must be a PrivateKeyEntry, SecretKeyEntry or TrustedCertificateEntry" + + "; was " + entry); } - - throw new KeyStoreException( - "Entry must be a PrivateKeyEntry or TrustedCertificateEntry; was " + entry); } } diff --git a/keystore/java/android/security/AndroidKeyStoreProvider.java b/keystore/java/android/security/AndroidKeyStoreProvider.java index 9081e92..598bcd8 100644 --- a/keystore/java/android/security/AndroidKeyStoreProvider.java +++ b/keystore/java/android/security/AndroidKeyStoreProvider.java @@ -35,5 +35,9 @@ public class AndroidKeyStoreProvider extends Provider { // java.security.KeyPairGenerator put("KeyPairGenerator.EC", AndroidKeyPairGenerator.EC.class.getName()); put("KeyPairGenerator.RSA", AndroidKeyPairGenerator.RSA.class.getName()); + + // javax.crypto.KeyGenerator + put("KeyGenerator.AES", KeyStoreKeyGeneratorSpi.AES.class.getName()); + put("KeyGenerator.HmacSHA256", KeyStoreKeyGeneratorSpi.HmacSHA256.class.getName()); } } diff --git a/keystore/java/android/security/Credentials.java b/keystore/java/android/security/Credentials.java index af76d9d..6283e02 100644 --- a/keystore/java/android/security/Credentials.java +++ b/keystore/java/android/security/Credentials.java @@ -61,6 +61,9 @@ public class Credentials { /** Key prefix for user private keys. */ public static final String USER_PRIVATE_KEY = "USRPKEY_"; + /** Key prefix for user secret keys. */ + public static final String USER_SECRET_KEY = "USRSKEY_"; + /** Key prefix for VPN. */ public static final String VPN = "VPN_"; @@ -218,7 +221,8 @@ public class Credentials { * Make sure every type is deleted. There can be all three types, so * don't use a conditional here. */ - return keystore.delKey(Credentials.USER_PRIVATE_KEY + alias) + return keystore.delete(Credentials.USER_PRIVATE_KEY + alias) + | keystore.delete(Credentials.USER_SECRET_KEY + alias) | deleteCertificateTypesForAlias(keystore, alias); } @@ -235,4 +239,20 @@ public class Credentials { return keystore.delete(Credentials.USER_CERTIFICATE + alias) | keystore.delete(Credentials.CA_CERTIFICATE + alias); } + + /** + * Delete private key for a particular {@code alias}. + * Returns {@code true} if an entry was was deleted. + */ + static boolean deletePrivateKeyTypeForAlias(KeyStore keystore, String alias) { + return keystore.delete(Credentials.USER_PRIVATE_KEY + alias); + } + + /** + * Delete secret key for a particular {@code alias}. + * Returns {@code true} if an entry was was deleted. + */ + static boolean deleteSecretKeyTypeForAlias(KeyStore keystore, String alias) { + return keystore.delete(Credentials.USER_SECRET_KEY + alias); + } } diff --git a/keystore/java/android/security/CryptoOperationException.java b/keystore/java/android/security/CryptoOperationException.java new file mode 100644 index 0000000..ce64455 --- /dev/null +++ b/keystore/java/android/security/CryptoOperationException.java @@ -0,0 +1,45 @@ +package android.security; + +/** + * Base class for exceptions during cryptographic operations which cannot throw a suitable checked + * exception. + * + * <p>The contract of the majority of crypto primitives/operations (e.g. {@code Cipher} or + * {@code Signature}) is that they can throw a checked exception during initialization, but are not + * permitted to throw a checked exception during operation. Because crypto operations can fail + * for a variety of reasons after initialization, this base class provides type-safety for unchecked + * exceptions that may be thrown in those cases. + * + * @hide + */ +public class CryptoOperationException extends RuntimeException { + + /** + * Constructs a new {@code CryptoOperationException} without detail message and cause. + */ + public CryptoOperationException() { + super(); + } + + /** + * Constructs a new {@code CryptoOperationException} with the provided detail message and no + * cause. + */ + public CryptoOperationException(String message) { + super(message); + } + + /** + * Constructs a new {@code CryptoOperationException} with the provided detail message and cause. + */ + public CryptoOperationException(String message, Throwable cause) { + super(message, cause); + } + + /** + * Constructs a new {@code CryptoOperationException} with the provided cause. + */ + public CryptoOperationException(Throwable cause) { + super(cause); + } +} diff --git a/keystore/java/android/security/KeyGeneratorSpec.java b/keystore/java/android/security/KeyGeneratorSpec.java new file mode 100644 index 0000000..6274b70 --- /dev/null +++ b/keystore/java/android/security/KeyGeneratorSpec.java @@ -0,0 +1,471 @@ +package android.security; + +import android.content.Context; +import android.text.TextUtils; + +import java.security.cert.Certificate; +import java.security.spec.AlgorithmParameterSpec; +import java.util.Collections; +import java.util.Date; +import java.util.HashSet; +import java.util.Set; + +import javax.crypto.KeyGenerator; +import javax.crypto.SecretKey; + +/** + * {@link AlgorithmParameterSpec} for initializing a {@code KeyGenerator} that works with + * <a href="{@docRoot}training/articles/keystore.html">Android KeyStore facility</a>. + * + * <p>The Android KeyStore facility is accessed through a {@link KeyGenerator} API + * using the {@code AndroidKeyStore} provider. The {@code context} passed in may be used to pop up + * some UI to ask the user to unlock or initialize the Android KeyStore facility. + * + * <p>After generation, the {@code keyStoreAlias} is used with the + * {@link java.security.KeyStore#getEntry(String, java.security.KeyStore.ProtectionParameter)} + * interface to retrieve the {@link SecretKey} and its associated {@link Certificate} chain. + * + * @hide + */ +public class KeyGeneratorSpec implements AlgorithmParameterSpec { + + private final Context mContext; + private final String mKeystoreAlias; + private final int mFlags; + private final Integer mKeySize; + private final Date mKeyValidityStart; + private final Date mKeyValidityForOriginationEnd; + private final Date mKeyValidityForConsumptionEnd; + private final @KeyStoreKeyConstraints.PurposeEnum Integer mPurposes; + private final @KeyStoreKeyConstraints.PaddingEnum Integer mPadding; + private final @KeyStoreKeyConstraints.BlockModeEnum Integer mBlockMode; + private final Integer mMinSecondsBetweenOperations; + private final Integer mMaxUsesPerBoot; + private final Set<Integer> mUserAuthenticators; + private final Integer mUserAuthenticationValidityDurationSeconds; + + private KeyGeneratorSpec( + Context context, + String keyStoreAlias, + int flags, + Integer keySize, + Date keyValidityStart, + Date keyValidityForOriginationEnd, + Date keyValidityForConsumptionEnd, + @KeyStoreKeyConstraints.PurposeEnum Integer purposes, + @KeyStoreKeyConstraints.PaddingEnum Integer padding, + @KeyStoreKeyConstraints.BlockModeEnum Integer blockMode, + Integer minSecondsBetweenOperations, + Integer maxUsesPerBoot, + Set<Integer> userAuthenticators, + Integer userAuthenticationValidityDurationSeconds) { + if (context == null) { + throw new IllegalArgumentException("context == null"); + } else if (TextUtils.isEmpty(keyStoreAlias)) { + throw new IllegalArgumentException("keyStoreAlias must not be empty"); + } else if ((userAuthenticationValidityDurationSeconds != null) + && (userAuthenticationValidityDurationSeconds < 0)) { + throw new IllegalArgumentException( + "userAuthenticationValidityDurationSeconds must not be negative"); + } + + mContext = context; + mKeystoreAlias = keyStoreAlias; + mFlags = flags; + mKeySize = keySize; + mKeyValidityStart = keyValidityStart; + mKeyValidityForOriginationEnd = keyValidityForOriginationEnd; + mKeyValidityForConsumptionEnd = keyValidityForConsumptionEnd; + mPurposes = purposes; + mPadding = padding; + mBlockMode = blockMode; + mMinSecondsBetweenOperations = minSecondsBetweenOperations; + mMaxUsesPerBoot = maxUsesPerBoot; + mUserAuthenticators = (userAuthenticators != null) + ? new HashSet<Integer>(userAuthenticators) + : Collections.<Integer>emptySet(); + mUserAuthenticationValidityDurationSeconds = userAuthenticationValidityDurationSeconds; + } + + /** + * Gets the Android context used for operations with this instance. + */ + public Context getContext() { + return mContext; + } + + /** + * Returns the alias that will be used in the {@code java.security.KeyStore} in conjunction with + * the {@code AndroidKeyStore}. + */ + public String getKeystoreAlias() { + return mKeystoreAlias; + } + + /** + * @hide + */ + public int getFlags() { + return mFlags; + } + + /** + * Gets the requested key size or {@code null} if the default size should be used. + */ + public Integer getKeySize() { + return mKeySize; + } + + /** + * Gets the time instant before which the key is not yet valid. + * + * @return instant or {@code null} if not restricted. + */ + public Date getKeyValidityStart() { + return mKeyValidityStart; + } + + /** + * Gets the time instant after which the key is no long valid for decryption and verification. + * + * @return instant or {@code null} if not restricted. + * + * @hide + */ + public Date getKeyValidityForConsumptionEnd() { + return mKeyValidityForConsumptionEnd; + } + + /** + * Gets the time instant after which the key is no long valid for encryption and signing. + * + * @return instant or {@code null} if not restricted. + */ + public Date getKeyValidityForOriginationEnd() { + return mKeyValidityForOriginationEnd; + } + + /** + * Gets the set of purposes for which the key can be used to the provided set of purposes. + * + * @return set of purposes or {@code null} if the key can be used for any purpose. + */ + public @KeyStoreKeyConstraints.PurposeEnum Integer getPurposes() { + return mPurposes; + } + + /** + * Gets the padding scheme to which the key is restricted. + * + * @return padding scheme or {@code null} if the padding scheme is not restricted. + */ + public @KeyStoreKeyConstraints.PaddingEnum Integer getPadding() { + return mPadding; + } + + /** + * Gets the block mode to which the key is restricted when used for encryption or decryption. + * + * @return block more or {@code null} if block mode is not restricted. + * + * @hide + */ + public @KeyStoreKeyConstraints.BlockModeEnum Integer getBlockMode() { + return mBlockMode; + } + + /** + * Gets the minimum number of seconds that must expire since the most recent use of the key + * before it can be used again. + * + * @return number of seconds or {@code null} if there is no restriction on how frequently a key + * can be used. + * + * @hide + */ + public Integer getMinSecondsBetweenOperations() { + return mMinSecondsBetweenOperations; + } + + /** + * Gets the number of times the key can be used without rebooting the device. + * + * @return maximum number of times or {@code null} if there is no restriction. + * @hide + */ + public Integer getMaxUsesPerBoot() { + return mMaxUsesPerBoot; + } + + /** + * Gets the user authenticators which protect access to this key. The key can only be used iff + * the user has authenticated to at least one of these user authenticators. + * + * @return user authenticators or empty set if the key can be used without user authentication. + * + * @hide + */ + public Set<Integer> getUserAuthenticators() { + return new HashSet<Integer>(mUserAuthenticators); + } + + /** + * Gets the duration of time (seconds) for which this key can be used after the user + * successfully authenticates to one of the associated user authenticators. + * + * @return duration in seconds or {@code null} if not restricted. {@code 0} means authentication + * is required for every use of the key. + * + * @hide + */ + public Integer getUserAuthenticationValidityDurationSeconds() { + return mUserAuthenticationValidityDurationSeconds; + } + + /** + * Returns {@code true} if the key must be encrypted in the {@link java.security.KeyStore}. + */ + public boolean isEncryptionRequired() { + return (mFlags & KeyStore.FLAG_ENCRYPTED) != 0; + } + + public static class Builder { + private final Context mContext; + private String mKeystoreAlias; + private int mFlags; + private Integer mKeySize; + private Date mKeyValidityStart; + private Date mKeyValidityForOriginationEnd; + private Date mKeyValidityForConsumptionEnd; + private @KeyStoreKeyConstraints.PurposeEnum Integer mPurposes; + private @KeyStoreKeyConstraints.PaddingEnum Integer mPadding; + private @KeyStoreKeyConstraints.BlockModeEnum Integer mBlockMode; + private Integer mMinSecondsBetweenOperations; + private Integer mMaxUsesPerBoot; + private Set<Integer> mUserAuthenticators; + private Integer mUserAuthenticationValidityDurationSeconds; + + /** + * Creates a new instance of the {@code Builder} with the given {@code context}. The + * {@code context} passed in may be used to pop up some UI to ask the user to unlock or + * initialize the Android KeyStore facility. + */ + public Builder(Context context) { + if (context == null) { + throw new NullPointerException("context == null"); + } + mContext = context; + } + + /** + * Sets the alias to be used to retrieve the key later from a {@link java.security.KeyStore} + * instance using the {@code AndroidKeyStore} provider. + * + * <p>The alias must be provided. There is no default. + */ + public Builder setAlias(String alias) { + if (alias == null) { + throw new NullPointerException("alias == null"); + } + mKeystoreAlias = alias; + return this; + } + + /** + * Sets the size (in bits) of the key to be generated. + * + * <p>By default, the key size will be determines based on the key algorithm. For example, + * for {@code HmacSHA256}, the key size will default to {@code 256}. + */ + public Builder setKeySize(int keySize) { + mKeySize = keySize; + return this; + } + + /** + * Indicates that this key must be encrypted at rest on storage. Note that enabling this + * will require that the user enable a strong lock screen (e.g., PIN, password) before + * creating or using the generated key is successful. + */ + public Builder setEncryptionRequired(boolean required) { + if (required) { + mFlags |= KeyStore.FLAG_ENCRYPTED; + } else { + mFlags &= ~KeyStore.FLAG_ENCRYPTED; + } + return this; + } + + /** + * Sets the time instant before which the key is not yet valid. + * + * <b>By default, the key is valid at any instant. + * + * @see #setKeyValidityEnd(Date) + * + * @hide + */ + public Builder setKeyValidityStart(Date startDate) { + mKeyValidityStart = startDate; + return this; + } + + /** + * Sets the time instant after which the key is no longer valid. + * + * <b>By default, the key is valid at any instant. + * + * @see #setKeyValidityStart(Date) + * @see #setKeyValidityForConsumptionEnd(Date) + * @see #setKeyValidityForOriginationEnd(Date) + * + * @hide + */ + public Builder setKeyValidityEnd(Date endDate) { + setKeyValidityForOriginationEnd(endDate); + setKeyValidityForConsumptionEnd(endDate); + return this; + } + + /** + * Sets the time instant after which the key is no longer valid for encryption and signing. + * + * <b>By default, the key is valid at any instant. + * + * @see #setKeyValidityForConsumptionEnd(Date) + * + * @hide + */ + public Builder setKeyValidityForOriginationEnd(Date endDate) { + mKeyValidityForOriginationEnd = endDate; + return this; + } + + /** + * Sets the time instant after which the key is no longer valid for decryption and + * verification. + * + * <b>By default, the key is valid at any instant. + * + * @see #setKeyValidityForOriginationEnd(Date) + * + * @hide + */ + public Builder setKeyValidityForConsumptionEnd(Date endDate) { + mKeyValidityForConsumptionEnd = endDate; + return this; + } + + /** + * Restricts the purposes for which the key can be used to the provided set of purposes. + * + * <p>By default, the key can be used for encryption, decryption, signing, and verification. + * + * @hide + */ + public Builder setPurposes(@KeyStoreKeyConstraints.PurposeEnum int purposes) { + mPurposes = purposes; + return this; + } + + /** + * Restricts the key to being used only with the provided padding scheme. Attempts to use + * the key with any other padding will be rejected. + * + * <p>This restriction must be specified for keys which are used for encryption/decryption. + * + * @hide + */ + public Builder setPadding(@KeyStoreKeyConstraints.PaddingEnum int padding) { + mPadding = padding; + return this; + } + + /** + * Restricts the key to being used only with the provided block mode when encrypting or + * decrypting. Attempts to use the key with any other block modes will be rejected. + * + * <p>This restriction must be specified for keys which are used for encryption/decryption. + * + * @hide + */ + public Builder setBlockMode(@KeyStoreKeyConstraints.BlockModeEnum int blockMode) { + mBlockMode = blockMode; + return this; + } + + /** + * Sets the minimum number of seconds that must expire since the most recent use of the key + * before it can be used again. + * + * <p>By default, there is no restriction on how frequently a key can be used. + * + * @hide + */ + public Builder setMinSecondsBetweenOperations(int seconds) { + mMinSecondsBetweenOperations = seconds; + return this; + } + + /** + * Sets the maximum number of times a key can be used without rebooting the device. + * + * <p>By default, the key can be used for an unlimited number of times. + * + * @hide + */ + public Builder setMaxUsesPerBoot(int count) { + mMaxUsesPerBoot = count; + return this; + } + + /** + * Sets the user authenticators which protect access to this key. The key can only be used + * iff the user has authenticated to at least one of these user authenticators. + * + * <p>By default, the key can be used without user authentication. + * + * @param userAuthenticators user authenticators or empty list if this key can be accessed + * without user authentication. + * + * @see #setUserAuthenticationValidityDurationSeconds(int) + * + * @hide + */ + public Builder setUserAuthenticators(Set<Integer> userAuthenticators) { + mUserAuthenticators = + (userAuthenticators != null) ? new HashSet<Integer>(userAuthenticators) : null; + return this; + } + + /** + * Sets the duration of time (seconds) for which this key can be used after the user + * successfully authenticates to one of the associated user authenticators. + * + * <p>By default, the user needs to authenticate for every use of the key. + * + * @param seconds duration in seconds or {@code 0} if the user needs to authenticate for + * every use of the key. + * + * @see #setUserAuthenticators(Set) + * + * @hide + */ + public Builder setUserAuthenticationValidityDurationSeconds(int seconds) { + mUserAuthenticationValidityDurationSeconds = seconds; + return this; + } + + /** + * Builds a new instance instance of {@code KeyGeneratorSpec}. + * + * @throws IllegalArgumentException if a required field is missing or violates a constraint. + */ + public KeyGeneratorSpec build() { + return new KeyGeneratorSpec(mContext, mKeystoreAlias, mFlags, mKeySize, + mKeyValidityStart, mKeyValidityForOriginationEnd, mKeyValidityForConsumptionEnd, + mPurposes, mPadding, mBlockMode, mMinSecondsBetweenOperations, mMaxUsesPerBoot, + mUserAuthenticators, mUserAuthenticationValidityDurationSeconds); + } + } +} diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java index bfbf028..f68b3f6 100644 --- a/keystore/java/android/security/KeyStore.java +++ b/keystore/java/android/security/KeyStore.java @@ -25,6 +25,7 @@ import android.os.ServiceManager; import android.security.keymaster.ExportResult; import android.security.keymaster.KeyCharacteristics; import android.security.keymaster.KeymasterArguments; +import android.security.keymaster.KeymasterBlob; import android.security.keymaster.OperationResult; import android.util.Log; @@ -388,22 +389,22 @@ public class KeyStore { } } - public int generateKey(String alias, KeymasterArguments args, int uid, int flags, - KeyCharacteristics outCharacteristics) { + public int generateKey(String alias, KeymasterArguments args, byte[] entropy, int uid, + int flags, KeyCharacteristics outCharacteristics) { try { - return mBinder.generateKey(alias, args, uid, flags, outCharacteristics); + return mBinder.generateKey(alias, args, entropy, uid, flags, outCharacteristics); } catch (RemoteException e) { Log.w(TAG, "Cannot connect to keystore", e); return SYSTEM_ERROR; } } - public int generateKey(String alias, KeymasterArguments args, int flags, + public int generateKey(String alias, KeymasterArguments args, byte[] entropy, int flags, KeyCharacteristics outCharacteristics) { - return generateKey(alias, args, UID_SELF, flags, outCharacteristics); + return generateKey(alias, args, entropy, UID_SELF, flags, outCharacteristics); } - public int getKeyCharacteristics(String alias, byte[] clientId, byte[] appId, + public int getKeyCharacteristics(String alias, KeymasterBlob clientId, KeymasterBlob appId, KeyCharacteristics outCharacteristics) { try { return mBinder.getKeyCharacteristics(alias, clientId, appId, outCharacteristics); @@ -429,7 +430,8 @@ public class KeyStore { return importKey(alias, args, format, keyData, UID_SELF, flags, outCharacteristics); } - public ExportResult exportKey(String alias, int format, byte[] clientId, byte[] appId) { + public ExportResult exportKey(String alias, int format, KeymasterBlob clientId, + KeymasterBlob appId) { try { return mBinder.exportKey(alias, format, clientId, appId); } catch (RemoteException e) { @@ -439,9 +441,9 @@ public class KeyStore { } public OperationResult begin(String alias, int purpose, boolean pruneable, - KeymasterArguments args, KeymasterArguments outArgs) { + KeymasterArguments args, byte[] entropy, KeymasterArguments outArgs) { try { - return mBinder.begin(getToken(), alias, purpose, pruneable, args, outArgs); + return mBinder.begin(getToken(), alias, purpose, pruneable, args, entropy, outArgs); } catch (RemoteException e) { Log.w(TAG, "Cannot connect to keystore", e); return null; diff --git a/keystore/java/android/security/KeyStoreKeyConstraints.java b/keystore/java/android/security/KeyStoreKeyConstraints.java new file mode 100644 index 0000000..47bb1cc --- /dev/null +++ b/keystore/java/android/security/KeyStoreKeyConstraints.java @@ -0,0 +1,457 @@ +package android.security; + +import android.annotation.IntDef; +import android.security.keymaster.KeymasterDefs; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.Arrays; +import java.util.Collection; +import java.util.Locale; + +/** + * Constraints for {@code AndroidKeyStore} keys. + * + * @hide + */ +public abstract class KeyStoreKeyConstraints { + private KeyStoreKeyConstraints() {} + + @Retention(RetentionPolicy.SOURCE) + @IntDef(flag=true, value={Purpose.ENCRYPT, Purpose.DECRYPT, Purpose.SIGN, Purpose.VERIFY}) + public @interface PurposeEnum {} + + /** + * Purpose of key. + */ + public static abstract class Purpose { + private Purpose() {} + + /** + * Purpose: encryption. + */ + public static final int ENCRYPT = 1 << 0; + + /** + * Purpose: decryption. + */ + public static final int DECRYPT = 1 << 1; + + /** + * Purpose: signing. + */ + public static final int SIGN = 1 << 2; + + /** + * Purpose: signature verification. + */ + public static final int VERIFY = 1 << 3; + + /** + * Number of flags defined above. Needs to be kept in sync with the flags above. + */ + private static final int VALUE_COUNT = 4; + + /** + * @hide + */ + public static int toKeymaster(@PurposeEnum int purpose) { + switch (purpose) { + case ENCRYPT: + return KeymasterDefs.KM_PURPOSE_ENCRYPT; + case DECRYPT: + return KeymasterDefs.KM_PURPOSE_DECRYPT; + case SIGN: + return KeymasterDefs.KM_PURPOSE_SIGN; + case VERIFY: + return KeymasterDefs.KM_PURPOSE_VERIFY; + default: + throw new IllegalArgumentException("Unknown purpose: " + purpose); + } + } + + /** + * @hide + */ + public static @PurposeEnum int fromKeymaster(int purpose) { + switch (purpose) { + case KeymasterDefs.KM_PURPOSE_ENCRYPT: + return ENCRYPT; + case KeymasterDefs.KM_PURPOSE_DECRYPT: + return DECRYPT; + case KeymasterDefs.KM_PURPOSE_SIGN: + return SIGN; + case KeymasterDefs.KM_PURPOSE_VERIFY: + return VERIFY; + default: + throw new IllegalArgumentException("Unknown purpose: " + purpose); + } + } + + /** + * @hide + */ + public static int[] allToKeymaster(int purposes) { + int[] result = new int[VALUE_COUNT]; + int resultCount = 0; + int purpose = 1; + for (int i = 0; i < 32; i++) { + if ((purposes & 1) != 0) { + result[resultCount] = toKeymaster(purpose); + resultCount++; + } + purposes >>>= 1; + purpose <<= 1; + if (purposes == 0) { + break; + } + } + return Arrays.copyOf(result, resultCount); + } + + /** + * @hide + */ + public static @PurposeEnum int allFromKeymaster(Collection<Integer> purposes) { + @PurposeEnum int result = 0; + for (int keymasterPurpose : purposes) { + result |= fromKeymaster(keymasterPurpose); + } + return result; + } + } + + @Retention(RetentionPolicy.SOURCE) + @IntDef({Algorithm.AES, Algorithm.HMAC}) + public @interface AlgorithmEnum {} + + /** + * Key algorithm. + */ + public static abstract class Algorithm { + private Algorithm() {} + + /** + * Key algorithm: AES. + */ + public static final int AES = 0; + + /** + * Key algorithm: HMAC. + */ + public static final int HMAC = 1; + + /** + * @hide + */ + public static int toKeymaster(@AlgorithmEnum int algorithm) { + switch (algorithm) { + case AES: + return KeymasterDefs.KM_ALGORITHM_AES; + case HMAC: + return KeymasterDefs.KM_ALGORITHM_HMAC; + default: + throw new IllegalArgumentException("Unknown algorithm: " + algorithm); + } + } + + /** + * @hide + */ + public static @AlgorithmEnum int fromKeymaster(int algorithm) { + switch (algorithm) { + case KeymasterDefs.KM_ALGORITHM_AES: + return AES; + case KeymasterDefs.KM_ALGORITHM_HMAC: + return HMAC; + default: + throw new IllegalArgumentException("Unknown algorithm: " + algorithm); + } + } + + /** + * @hide + */ + public static String toString(@AlgorithmEnum int algorithm) { + switch (algorithm) { + case AES: + return "AES"; + case HMAC: + return "HMAC"; + default: + throw new IllegalArgumentException("Unknown algorithm: " + algorithm); + } + } + + /** + * @hide + */ + public static @AlgorithmEnum int fromJCASecretKeyAlgorithm(String algorithm) { + if (algorithm == null) { + throw new NullPointerException("algorithm == null"); + } else if ("AES".equalsIgnoreCase(algorithm)) { + return AES; + } else if (algorithm.toLowerCase(Locale.US).startsWith("hmac")) { + return HMAC; + } else { + throw new IllegalArgumentException( + "Unsupported secret key algorithm: " + algorithm); + } + } + + /** + * @hide + */ + public static String toJCASecretKeyAlgorithm(@AlgorithmEnum int algorithm, + @DigestEnum Integer digest) { + switch (algorithm) { + case AES: + return "AES"; + case HMAC: + if (digest == null) { + throw new IllegalArgumentException("HMAC digest not specified"); + } + switch (digest) { + case Digest.SHA256: + return "HmacSHA256"; + default: + throw new IllegalArgumentException( + "Unsupported HMAC digest: " + digest); + } + default: + throw new IllegalArgumentException("Unsupported key algorithm: " + algorithm); + } + } + + /** + * @hide + */ + public static String toJCAKeyPairAlgorithm(@AlgorithmEnum int algorithm) { + switch (algorithm) { + default: + throw new IllegalArgumentException("Unsupported key alorithm: " + algorithm); + } + } + } + + @Retention(RetentionPolicy.SOURCE) + @IntDef({Padding.NONE, Padding.ZERO, Padding.PKCS7}) + public @interface PaddingEnum {} + + /** + * Padding for signing and encryption. + */ + public static abstract class Padding { + private Padding() {} + + /** + * No padding. + */ + public static final int NONE = 0; + + /** + * Pad with zeros. + */ + public static final int ZERO = 1; + + /** + * PKCS#7 padding. + */ + public static final int PKCS7 = 2; + + /** + * @hide + */ + public static int toKeymaster(int padding) { + switch (padding) { + case NONE: + return KeymasterDefs.KM_PAD_NONE; + case ZERO: + return KeymasterDefs.KM_PAD_ZERO; + case PKCS7: + return KeymasterDefs.KM_PAD_PKCS7; + default: + throw new IllegalArgumentException("Unknown padding: " + padding); + } + } + + /** + * @hide + */ + public static @PaddingEnum int fromKeymaster(int padding) { + switch (padding) { + case KeymasterDefs.KM_PAD_NONE: + return NONE; + case KeymasterDefs.KM_PAD_ZERO: + return ZERO; + case KeymasterDefs.KM_PAD_PKCS7: + return PKCS7; + default: + throw new IllegalArgumentException("Unknown padding: " + padding); + } + } + + /** + * @hide + */ + public static String toString(@PaddingEnum int padding) { + switch (padding) { + case NONE: + return "NONE"; + case ZERO: + return "ZERO"; + case PKCS7: + return "PKCS#7"; + default: + throw new IllegalArgumentException("Unknown padding: " + padding); + } + } + } + + @Retention(RetentionPolicy.SOURCE) + @IntDef({Digest.NONE, Digest.SHA256}) + public @interface DigestEnum {} + + /** + * Digests that can be used with a key when signing or generating Message Authentication + * Codes (MACs). + */ + public static abstract class Digest { + private Digest() {} + + /** + * No digest: sign/authenticate the raw message. + */ + public static final int NONE = 0; + + /** + * SHA-256 digest. + */ + public static final int SHA256 = 1; + + /** + * @hide + */ + public static String toString(@DigestEnum int digest) { + switch (digest) { + case NONE: + return "NONE"; + case SHA256: + return "SHA256"; + default: + throw new IllegalArgumentException("Unknown digest: " + digest); + } + } + + /** + * @hide + */ + public static int toKeymaster(@DigestEnum int digest) { + switch (digest) { + case NONE: + return KeymasterDefs.KM_DIGEST_NONE; + case SHA256: + return KeymasterDefs.KM_DIGEST_SHA_2_256; + default: + throw new IllegalArgumentException("Unknown digest: " + digest); + } + } + + /** + * @hide + */ + public static @DigestEnum int fromKeymaster(int digest) { + switch (digest) { + case KeymasterDefs.KM_DIGEST_NONE: + return NONE; + case KeymasterDefs.KM_DIGEST_SHA_2_256: + return SHA256; + default: + throw new IllegalArgumentException("Unknown digest: " + digest); + } + } + + /** + * @hide + */ + public static @DigestEnum Integer fromJCASecretKeyAlgorithm(String algorithm) { + String algorithmLower = algorithm.toLowerCase(Locale.US); + if (algorithmLower.startsWith("hmac")) { + if ("hmacsha256".equals(algorithmLower)) { + return SHA256; + } else { + throw new IllegalArgumentException("Unsupported digest: " + + algorithmLower.substring("hmac".length())); + } + } else { + return null; + } + } + + /** + * @hide + */ + public static String toJCASignatureAlgorithmDigest(@DigestEnum int digest) { + switch (digest) { + case NONE: + return "NONE"; + case SHA256: + return "SHA256"; + default: + throw new IllegalArgumentException("Unknown digest: " + digest); + } + } + } + + @Retention(RetentionPolicy.SOURCE) + @IntDef({BlockMode.ECB}) + public @interface BlockModeEnum {} + + /** + * Block modes that can be used when encrypting/decrypting using a key. + */ + public static abstract class BlockMode { + private BlockMode() {} + + /** + * Electronic Codebook (ECB) block mode. + */ + public static final int ECB = 0; + + /** + * @hide + */ + public static int toKeymaster(@BlockModeEnum int mode) { + switch (mode) { + case ECB: + return KeymasterDefs.KM_MODE_ECB; + default: + throw new IllegalArgumentException("Unknown block mode: " + mode); + } + } + + /** + * @hide + */ + public static @BlockModeEnum int fromKeymaster(int mode) { + switch (mode) { + case KeymasterDefs.KM_MODE_ECB: + return ECB; + default: + throw new IllegalArgumentException("Unknown block mode: " + mode); + } + } + + /** + * @hide + */ + public static String toString(@BlockModeEnum int mode) { + switch (mode) { + case ECB: + return "ECB"; + default: + throw new IllegalArgumentException("Unknown block mode: " + mode); + } + } + } +} diff --git a/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java b/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java new file mode 100644 index 0000000..86950dd --- /dev/null +++ b/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java @@ -0,0 +1,183 @@ +package android.security; + +import android.security.keymaster.KeyCharacteristics; +import android.security.keymaster.KeymasterArguments; +import android.security.keymaster.KeymasterDefs; + +import java.security.InvalidAlgorithmParameterException; +import java.security.SecureRandom; +import java.security.spec.AlgorithmParameterSpec; + +import javax.crypto.KeyGeneratorSpi; +import javax.crypto.SecretKey; + +/** + * {@link KeyGeneratorSpi} backed by Android KeyStore. + * + * @hide + */ +public abstract class KeyStoreKeyGeneratorSpi extends KeyGeneratorSpi { + + public static class AES extends KeyStoreKeyGeneratorSpi { + public AES() { + super(KeyStoreKeyConstraints.Algorithm.AES, 128); + } + } + + public static class HmacSHA256 extends KeyStoreKeyGeneratorSpi { + public HmacSHA256() { + super(KeyStoreKeyConstraints.Algorithm.HMAC, + KeyStoreKeyConstraints.Digest.SHA256, + 256); + } + } + + private final KeyStore mKeyStore = KeyStore.getInstance(); + private final @KeyStoreKeyConstraints.AlgorithmEnum int mAlgorithm; + private final @KeyStoreKeyConstraints.AlgorithmEnum Integer mDigest; + private final int mDefaultKeySizeBits; + + private KeyGeneratorSpec mSpec; + private SecureRandom mRng; + + protected KeyStoreKeyGeneratorSpi( + @KeyStoreKeyConstraints.AlgorithmEnum int algorithm, + int defaultKeySizeBits) { + this(algorithm, null, defaultKeySizeBits); + } + + protected KeyStoreKeyGeneratorSpi( + @KeyStoreKeyConstraints.AlgorithmEnum int algorithm, + @KeyStoreKeyConstraints.DigestEnum Integer digest, + int defaultKeySizeBits) { + mAlgorithm = algorithm; + mDigest = digest; + mDefaultKeySizeBits = defaultKeySizeBits; + } + + @Override + protected SecretKey engineGenerateKey() { + KeyGeneratorSpec spec = mSpec; + if (spec == null) { + throw new IllegalStateException("Not initialized"); + } + + if ((spec.isEncryptionRequired()) + && (mKeyStore.state() != KeyStore.State.UNLOCKED)) { + throw new IllegalStateException( + "Android KeyStore must be in initialized and unlocked state if encryption is" + + " required"); + } + + KeymasterArguments args = new KeymasterArguments(); + args.addInt(KeymasterDefs.KM_TAG_ALGORITHM, + KeyStoreKeyConstraints.Algorithm.toKeymaster(mAlgorithm)); + if (mDigest != null) { + args.addInt(KeymasterDefs.KM_TAG_DIGEST, + KeyStoreKeyConstraints.Digest.toKeymaster(mDigest)); + } + int keySizeBits = (spec.getKeySize() != null) ? spec.getKeySize() : mDefaultKeySizeBits; + args.addInt(KeymasterDefs.KM_TAG_KEY_SIZE, keySizeBits); + @KeyStoreKeyConstraints.PurposeEnum int purposes = (spec.getPurposes() != null) + ? spec.getPurposes() + : (KeyStoreKeyConstraints.Purpose.ENCRYPT + | KeyStoreKeyConstraints.Purpose.DECRYPT + | KeyStoreKeyConstraints.Purpose.SIGN + | KeyStoreKeyConstraints.Purpose.VERIFY); + for (int keymasterPurpose : + KeyStoreKeyConstraints.Purpose.allToKeymaster(purposes)) { + args.addInt(KeymasterDefs.KM_TAG_PURPOSE, keymasterPurpose); + } + if (spec.getBlockMode() != null) { + args.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE, + KeyStoreKeyConstraints.BlockMode.toKeymaster(spec.getBlockMode())); + } + if (spec.getPadding() != null) { + args.addInt(KeymasterDefs.KM_TAG_PADDING, + KeyStoreKeyConstraints.Padding.toKeymaster(spec.getPadding())); + } + if (spec.getMaxUsesPerBoot() != null) { + args.addInt(KeymasterDefs.KM_TAG_MAX_USES_PER_BOOT, spec.getMaxUsesPerBoot()); + } + if (spec.getMinSecondsBetweenOperations() != null) { + args.addInt(KeymasterDefs.KM_TAG_MIN_SECONDS_BETWEEN_OPS, + spec.getMinSecondsBetweenOperations()); + } + if (spec.getUserAuthenticators().isEmpty()) { + args.addBoolean(KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED); + } else { + // TODO: Pass-in user authenticator IDs once the Keymaster API has stabilized +// for (int userAuthenticatorId : spec.getUserAuthenticators()) { +// args.addInt(KeymasterDefs.KM_TAG_USER_AUTH_ID, userAuthenticatorId); +// } + } + if (spec.getUserAuthenticationValidityDurationSeconds() != null) { + args.addInt(KeymasterDefs.KM_TAG_AUTH_TIMEOUT, + spec.getUserAuthenticationValidityDurationSeconds()); + } + if (spec.getKeyValidityStart() != null) { + args.addDate(KeymasterDefs.KM_TAG_ACTIVE_DATETIME, spec.getKeyValidityStart()); + } + if (spec.getKeyValidityForOriginationEnd() != null) { + args.addDate(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME, + spec.getKeyValidityForOriginationEnd()); + } + if (spec.getKeyValidityForConsumptionEnd() != null) { + args.addDate(KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME, + spec.getKeyValidityForConsumptionEnd()); + } + + if (((purposes & KeyStoreKeyConstraints.Purpose.ENCRYPT) != 0) + || ((purposes & KeyStoreKeyConstraints.Purpose.DECRYPT) != 0)) { + // Permit caller-specified IV. This is needed due to the Cipher abstraction. + args.addBoolean(KeymasterDefs.KM_TAG_CALLER_NONCE); + } + + byte[] additionalEntropy = null; + SecureRandom rng = mRng; + if (rng != null) { + additionalEntropy = new byte[(keySizeBits + 7) / 8]; + rng.nextBytes(additionalEntropy); + } + + int flags = spec.getFlags(); + String keyAliasInKeystore = Credentials.USER_SECRET_KEY + spec.getKeystoreAlias(); + int errorCode = mKeyStore.generateKey( + keyAliasInKeystore, args, additionalEntropy, flags, new KeyCharacteristics()); + if (errorCode != KeyStore.NO_ERROR) { + throw new CryptoOperationException("Failed to generate key", + KeymasterUtils.getExceptionForKeymasterError(errorCode)); + } + String keyAlgorithmJCA = + KeyStoreKeyConstraints.Algorithm.toJCASecretKeyAlgorithm(mAlgorithm, mDigest); + return new KeyStoreSecretKey(keyAliasInKeystore, keyAlgorithmJCA); + } + + @Override + protected void engineInit(SecureRandom random) { + throw new UnsupportedOperationException("Cannot initialize without an " + + KeyGeneratorSpec.class.getName() + " parameter"); + } + + @Override + protected void engineInit(AlgorithmParameterSpec params, SecureRandom random) + throws InvalidAlgorithmParameterException { + if ((params == null) || (!(params instanceof KeyGeneratorSpec))) { + throw new InvalidAlgorithmParameterException("Cannot initialize without an " + + KeyGeneratorSpec.class.getName() + " parameter"); + } + KeyGeneratorSpec spec = (KeyGeneratorSpec) params; + if (spec.getKeystoreAlias() == null) { + throw new InvalidAlgorithmParameterException("KeyStore entry alias not provided"); + } + + mSpec = spec; + mRng = random; + } + + @Override + protected void engineInit(int keySize, SecureRandom random) { + throw new UnsupportedOperationException("Cannot initialize without a " + + KeyGeneratorSpec.class.getName() + " parameter"); + } +} diff --git a/keystore/java/android/security/KeyStoreParameter.java b/keystore/java/android/security/KeyStoreParameter.java index 2eeb6ad..2428c2a 100644 --- a/keystore/java/android/security/KeyStoreParameter.java +++ b/keystore/java/android/security/KeyStoreParameter.java @@ -20,6 +20,10 @@ import android.content.Context; import java.security.KeyPairGenerator; import java.security.KeyStore.ProtectionParameter; +import java.util.Collections; +import java.util.Date; +import java.util.HashSet; +import java.util.Set; /** * This provides the optional parameters that can be specified for @@ -43,9 +47,51 @@ import java.security.KeyStore.ProtectionParameter; */ public final class KeyStoreParameter implements ProtectionParameter { private int mFlags; + private final Date mKeyValidityStart; + private final Date mKeyValidityForOriginationEnd; + private final Date mKeyValidityForConsumptionEnd; + private final @KeyStoreKeyConstraints.PurposeEnum Integer mPurposes; + private final @KeyStoreKeyConstraints.AlgorithmEnum Integer mAlgorithm; + private final @KeyStoreKeyConstraints.PaddingEnum Integer mPadding; + private final @KeyStoreKeyConstraints.DigestEnum Integer mDigest; + private final @KeyStoreKeyConstraints.BlockModeEnum Integer mBlockMode; + private final Integer mMinSecondsBetweenOperations; + private final Integer mMaxUsesPerBoot; + private final Set<Integer> mUserAuthenticators; + private final Integer mUserAuthenticationValidityDurationSeconds; + + private KeyStoreParameter(int flags, Date keyValidityStart, + Date keyValidityForOriginationEnd, Date keyValidityForConsumptionEnd, + @KeyStoreKeyConstraints.PurposeEnum Integer purposes, + @KeyStoreKeyConstraints.AlgorithmEnum Integer algorithm, + @KeyStoreKeyConstraints.PaddingEnum Integer padding, + @KeyStoreKeyConstraints.DigestEnum Integer digest, + @KeyStoreKeyConstraints.BlockModeEnum Integer blockMode, + Integer minSecondsBetweenOperations, + Integer maxUsesPerBoot, + Set<Integer> userAuthenticators, + Integer userAuthenticationValidityDurationSeconds) { + if ((userAuthenticationValidityDurationSeconds != null) + && (userAuthenticationValidityDurationSeconds < 0)) { + throw new IllegalArgumentException( + "userAuthenticationValidityDurationSeconds must not be negative"); + } - private KeyStoreParameter(int flags) { mFlags = flags; + mKeyValidityStart = keyValidityStart; + mKeyValidityForOriginationEnd = keyValidityForOriginationEnd; + mKeyValidityForConsumptionEnd = keyValidityForConsumptionEnd; + mPurposes = purposes; + mAlgorithm = algorithm; + mPadding = padding; + mDigest = digest; + mBlockMode = blockMode; + mMinSecondsBetweenOperations = minSecondsBetweenOperations; + mMaxUsesPerBoot = maxUsesPerBoot; + mUserAuthenticators = (userAuthenticators != null) + ? new HashSet<Integer>(userAuthenticators) + : Collections.<Integer>emptySet(); + mUserAuthenticationValidityDurationSeconds = userAuthenticationValidityDurationSeconds; } /** @@ -64,6 +110,141 @@ public final class KeyStoreParameter implements ProtectionParameter { } /** + * Gets the time instant before which the key is not yet valid. + * + * @return instant or {@code null} if not restricted. + * @hide + */ + public Date getKeyValidityStart() { + return mKeyValidityStart; + } + + /** + * Gets the time instant after which the key is no long valid for decryption and verification. + * + * @return instant or {@code null} if not restricted. + * + * @hide + */ + public Date getKeyValidityForConsumptionEnd() { + return mKeyValidityForConsumptionEnd; + } + + /** + * Gets the time instant after which the key is no long valid for encryption and signing. + * + * @return instant or {@code null} if not restricted. + * + * @hide + */ + public Date getKeyValidityForOriginationEnd() { + return mKeyValidityForOriginationEnd; + } + + /** + * Gets the set of purposes for which the key can be used to the provided set of purposes. + * + * @return set of purposes or {@code null} if the key can be used for any purpose. + * + * @hide + */ + public @KeyStoreKeyConstraints.PurposeEnum Integer getPurposes() { + return mPurposes; + } + + /** + * Gets the algorithm to which the key is restricted. + * + * @return algorithm or {@code null} if it's not restricted. + * @hide + */ + public @KeyStoreKeyConstraints.AlgorithmEnum Integer getAlgorithm() { + return mAlgorithm; + } + + /** + * Gets the padding scheme to which the key is restricted. + * + * @return padding scheme or {@code null} if the padding scheme is not restricted. + * + * @hide + */ + public @KeyStoreKeyConstraints.PaddingEnum Integer getPadding() { + return mPadding; + } + + /** + * Gets the digest to which the key is restricted when generating Message Authentication Codes + * (MACs). + * + * @return digest or {@code null} if the digest is not restricted. + * + * @hide + */ + public @KeyStoreKeyConstraints.DigestEnum Integer getDigest() { + return mDigest; + } + + /** + * Gets the block mode to which the key is restricted when used for encryption or decryption. + * + * @return block more or {@code null} if block mode is not restricted. + * + * @hide + */ + public @KeyStoreKeyConstraints.BlockModeEnum Integer getBlockMode() { + return mBlockMode; + } + + /** + * Gets the minimum number of seconds that must expire since the most recent use of the key + * before it can be used again. + * + * @return number of seconds or {@code null} if there is no restriction on how frequently a key + * can be used. + * + * @hide + */ + public Integer getMinSecondsBetweenOperations() { + return mMinSecondsBetweenOperations; + } + + /** + * Gets the number of times the key can be used without rebooting the device. + * + * @return maximum number of times or {@code null} if there is no restriction. + * @hide + */ + public Integer getMaxUsesPerBoot() { + return mMaxUsesPerBoot; + } + + /** + * Gets the user authenticators which protect access to this key. The key can only be used iff + * the user has authenticated to at least one of these user authenticators. + * + * @return user authenticators or empty set if the key can be used without user authentication. + * + * @hide + */ + public Set<Integer> getUserAuthenticators() { + return new HashSet<Integer>(mUserAuthenticators); + } + + /** + * Gets the duration of time (seconds) for which this key can be used after the user + * successfully authenticates to one of the associated user authenticators. + * + * @return duration in seconds or {@code null} if not restricted. {@code 0} means authentication + * is required for every use of the key. + * + * @hide + */ + public Integer getUserAuthenticationValidityDurationSeconds() { + return mUserAuthenticationValidityDurationSeconds; + } + + /** * Builder class for {@link KeyStoreParameter} objects. * <p> * This will build protection parameters for use with the @@ -82,6 +263,18 @@ public final class KeyStoreParameter implements ProtectionParameter { */ public final static class Builder { private int mFlags; + private Date mKeyValidityStart; + private Date mKeyValidityForOriginationEnd; + private Date mKeyValidityForConsumptionEnd; + private @KeyStoreKeyConstraints.PurposeEnum Integer mPurposes; + private @KeyStoreKeyConstraints.AlgorithmEnum Integer mAlgorithm; + private @KeyStoreKeyConstraints.PaddingEnum Integer mPadding; + private @KeyStoreKeyConstraints.DigestEnum Integer mDigest; + private @KeyStoreKeyConstraints.BlockModeEnum Integer mBlockMode; + private Integer mMinSecondsBetweenOperations; + private Integer mMaxUsesPerBoot; + private Set<Integer> mUserAuthenticators; + private Integer mUserAuthenticationValidityDurationSeconds; /** * Creates a new instance of the {@code Builder} with the given @@ -113,13 +306,207 @@ public final class KeyStoreParameter implements ProtectionParameter { } /** - * Builds the instance of the {@code KeyPairGeneratorSpec}. + * Sets the time instant before which the key is not yet valid. + * + * <b>By default, the key is valid at any instant. + * + * @see #setKeyValidityEnd(Date) + * + * @hide + */ + public Builder setKeyValidityStart(Date startDate) { + mKeyValidityStart = startDate; + return this; + } + + /** + * Sets the time instant after which the key is no longer valid. + * + * <b>By default, the key is valid at any instant. + * + * @see #setKeyValidityStart(Date) + * @see #setKeyValidityForConsumptionEnd(Date) + * @see #setKeyValidityForOriginationEnd(Date) + * + * @hide + */ + public Builder setKeyValidityEnd(Date endDate) { + setKeyValidityForOriginationEnd(endDate); + setKeyValidityForConsumptionEnd(endDate); + return this; + } + + /** + * Sets the time instant after which the key is no longer valid for encryption and signing. + * + * <b>By default, the key is valid at any instant. + * + * @see #setKeyValidityForConsumptionEnd(Date) + * + * @hide + */ + public Builder setKeyValidityForOriginationEnd(Date endDate) { + mKeyValidityForOriginationEnd = endDate; + return this; + } + + /** + * Sets the time instant after which the key is no longer valid for decryption and + * verification. + * + * <b>By default, the key is valid at any instant. + * + * @see #setKeyValidityForOriginationEnd(Date) + * + * @hide + */ + public Builder setKeyValidityForConsumptionEnd(Date endDate) { + mKeyValidityForConsumptionEnd = endDate; + return this; + } + + /** + * Restricts the purposes for which the key can be used to the provided set of purposes. + * + * <p>By default, the key can be used for encryption, decryption, signing, and verification. + * + * @hide + */ + public Builder setPurposes(@KeyStoreKeyConstraints.PurposeEnum int purposes) { + mPurposes = purposes; + return this; + } + + /** + * Sets the algorithm of the key. + * + * <p>The algorithm of symmetric keys can be deduced from the key itself. Thus, explicitly + * specifying the algorithm of symmetric keys using this method is not necessary. + * + * @hide + */ + public Builder setAlgorithm(@KeyStoreKeyConstraints.AlgorithmEnum int algorithm) { + mAlgorithm = algorithm; + return this; + } + + /** + * Restricts the key to being used only with the provided padding scheme. Attempts to use + * the key with any other padding will be rejected. + * + * <p>This restriction must be specified for keys which are used for encryption/decryption. + * + * @hide + */ + public Builder setPadding(@KeyStoreKeyConstraints.PaddingEnum int padding) { + mPadding = padding; + return this; + } + + /** + * Restricts the key to being used only with the provided digest when generating Message + * Authentication Codes (MACs). Attempts to use the key with any other digest will be + * rejected. + * + * <p>For MAC keys, the default is to restrict to the digest specified in the key algorithm + * name. + * + * @see java.security.Key#getAlgorithm() + * + * @hide + */ + public Builder setDigest(@KeyStoreKeyConstraints.DigestEnum int digest) { + mDigest = digest; + return this; + } + + /** + * Restricts the key to being used only with the provided block mode when encrypting or + * decrypting. Attempts to use the key with any other block modes will be rejected. + * + * <p>This restriction must be specified for keys which are used for encryption/decryption. + * + * @hide + */ + public Builder setBlockMode(@KeyStoreKeyConstraints.BlockModeEnum int blockMode) { + mBlockMode = blockMode; + return this; + } + + /** + * Sets the minimum number of seconds that must expire since the most recent use of the key + * before it can be used again. + * + * <p>By default, there is no restriction on how frequently a key can be used. + * + * @hide + */ + public Builder setMinSecondsBetweenOperations(int seconds) { + mMinSecondsBetweenOperations = seconds; + return this; + } + + /** + * Sets the maximum number of times a key can be used without rebooting the device. + * + * <p>By default, the key can be used for an unlimited number of times. + * + * @hide + */ + public Builder setMaxUsesPerBoot(int count) { + mMaxUsesPerBoot = count; + return this; + } + + /** + * Sets the user authenticators which protect access to this key. The key can only be used + * iff the user has authenticated to at least one of these user authenticators. + * + * <p>By default, the key can be used without user authentication. + * + * @param userAuthenticators user authenticators or empty list if this key can be accessed + * without user authentication. + * + * @see #setUserAuthenticationValidityDurationSeconds(int) + * + * @hide + */ + public Builder setUserAuthenticators(Set<Integer> userAuthenticators) { + mUserAuthenticators = + (userAuthenticators != null) ? new HashSet<Integer>(userAuthenticators) : null; + return this; + } + + /** + * Sets the duration of time (seconds) for which this key can be used after the user + * successfully authenticates to one of the associated user authenticators. + * + * <p>By default, the user needs to authenticate for every use of the key. + * + * @param seconds duration in seconds or {@code 0} if the user needs to authenticate for + * every use of the key. + * + * @see #setUserAuthenticators(Set) + * + * @hide + */ + public Builder setUserAuthenticationValidityDurationSeconds(int seconds) { + mUserAuthenticationValidityDurationSeconds = seconds; + return this; + } + + /** + * Builds the instance of the {@code KeyStoreParameter}. * * @throws IllegalArgumentException if a required field is missing - * @return built instance of {@code KeyPairGeneratorSpec} + * @return built instance of {@code KeyStoreParameter} */ public KeyStoreParameter build() { - return new KeyStoreParameter(mFlags); + return new KeyStoreParameter(mFlags, mKeyValidityStart, + mKeyValidityForOriginationEnd, mKeyValidityForConsumptionEnd, mPurposes, + mAlgorithm, mPadding, mDigest, mBlockMode, mMinSecondsBetweenOperations, + mMaxUsesPerBoot, mUserAuthenticators, + mUserAuthenticationValidityDurationSeconds); } } } diff --git a/keystore/java/android/security/KeyStoreSecretKey.java b/keystore/java/android/security/KeyStoreSecretKey.java new file mode 100644 index 0000000..9410127 --- /dev/null +++ b/keystore/java/android/security/KeyStoreSecretKey.java @@ -0,0 +1,39 @@ +package android.security; + +import javax.crypto.SecretKey; + +/** + * {@link SecretKey} backed by keystore. + * + * @hide + */ +public class KeyStoreSecretKey implements SecretKey { + private final String mAlias; + private final String mAlgorithm; + + public KeyStoreSecretKey(String alias, String algorithm) { + mAlias = alias; + mAlgorithm = algorithm; + } + + String getAlias() { + return mAlias; + } + + @Override + public String getAlgorithm() { + return mAlgorithm; + } + + @Override + public String getFormat() { + // This key does not export its key material + return null; + } + + @Override + public byte[] getEncoded() { + // This key does not export its key material + return null; + } +} diff --git a/keystore/java/android/security/KeymasterException.java b/keystore/java/android/security/KeymasterException.java new file mode 100644 index 0000000..4ff7115 --- /dev/null +++ b/keystore/java/android/security/KeymasterException.java @@ -0,0 +1,13 @@ +package android.security; + +/** + * Keymaster exception. + * + * @hide + */ +public class KeymasterException extends Exception { + + public KeymasterException(String message) { + super(message); + } +} diff --git a/keystore/java/android/security/KeymasterUtils.java b/keystore/java/android/security/KeymasterUtils.java new file mode 100644 index 0000000..e6e88c7 --- /dev/null +++ b/keystore/java/android/security/KeymasterUtils.java @@ -0,0 +1,21 @@ +package android.security; + +import android.security.keymaster.KeymasterDefs; + +/** + * @hide + */ +public abstract class KeymasterUtils { + private KeymasterUtils() {} + + public static KeymasterException getExceptionForKeymasterError(int keymasterErrorCode) { + switch (keymasterErrorCode) { + case KeymasterDefs.KM_ERROR_INVALID_AUTHORIZATION_TIMEOUT: + // The name of this parameter significantly differs between Keymaster and framework + // APIs. Use the framework wording to make life easier for developers. + return new KeymasterException("Invalid user authentication validity duration"); + default: + return new KeymasterException(KeymasterDefs.getErrorMessage(keymasterErrorCode)); + } + } +} diff --git a/keystore/tests/src/android/security/AndroidKeyStoreTest.java b/keystore/tests/src/android/security/AndroidKeyStoreTest.java index 9775e64..7a88dee 100644 --- a/keystore/tests/src/android/security/AndroidKeyStoreTest.java +++ b/keystore/tests/src/android/security/AndroidKeyStoreTest.java @@ -2127,7 +2127,7 @@ public class AndroidKeyStoreTest extends AndroidTestCase { assertEquals("The keystore size should match expected", 2, mKeyStore.size()); assertAliases(new String[] { TEST_ALIAS_2, TEST_ALIAS_3 }); - assertTrue(mAndroidKeyStore.delKey(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_3)); + assertTrue(mAndroidKeyStore.delete(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_3)); assertEquals("The keystore size should match expected", 1, mKeyStore.size()); assertAliases(new String[] { TEST_ALIAS_2 }); diff --git a/keystore/tests/src/android/security/KeyStoreTest.java b/keystore/tests/src/android/security/KeyStoreTest.java index f935bb1..7468fb5 100644 --- a/keystore/tests/src/android/security/KeyStoreTest.java +++ b/keystore/tests/src/android/security/KeyStoreTest.java @@ -25,6 +25,7 @@ import android.security.KeyStore; import android.security.keymaster.ExportResult; import android.security.keymaster.KeyCharacteristics; import android.security.keymaster.KeymasterArguments; +import android.security.keymaster.KeymasterBlob; import android.security.keymaster.KeymasterDefs; import android.security.keymaster.OperationResult; import android.test.ActivityUnitTestCase; @@ -36,6 +37,7 @@ import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Date; import java.util.HashSet; +import java.security.spec.RSAKeyGenParameterSpec; import android.util.Log; import android.util.Base64; @@ -711,11 +713,11 @@ public class KeyStoreTest extends ActivityUnitTestCase<Activity> { 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.addBlob(KeymasterDefs.KM_TAG_APPLICATION_ID, null); - args.addBlob(KeymasterDefs.KM_TAG_APPLICATION_DATA, null); + args.addLong(KeymasterDefs.KM_TAG_RSA_PUBLIC_EXPONENT, + RSAKeyGenParameterSpec.F4.longValue()); KeyCharacteristics outCharacteristics = new KeyCharacteristics(); - int result = mKeyStore.generateKey(name, args, 0, outCharacteristics); + int result = mKeyStore.generateKey(name, args, null, 0, outCharacteristics); assertEquals("generateRsaKey should succeed", KeyStore.NO_ERROR, result); return outCharacteristics; } @@ -724,6 +726,24 @@ public class KeyStoreTest extends ActivityUnitTestCase<Activity> { generateRsaKey("test"); mKeyStore.delete("test"); } + + public void testGenerateRsaWithEntropy() throws Exception { + byte[] entropy = new byte[] {1,2,3,4,5}; + String name = "test"; + KeymasterArguments args = new KeymasterArguments(); + args.addInt(KeymasterDefs.KM_TAG_PURPOSE, KeymasterDefs.KM_PURPOSE_ENCRYPT); + args.addInt(KeymasterDefs.KM_TAG_PURPOSE, KeymasterDefs.KM_PURPOSE_DECRYPT); + args.addInt(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_RSA); + args.addInt(KeymasterDefs.KM_TAG_PADDING, KeymasterDefs.KM_PAD_NONE); + args.addInt(KeymasterDefs.KM_TAG_KEY_SIZE, 2048); + args.addLong(KeymasterDefs.KM_TAG_RSA_PUBLIC_EXPONENT, + RSAKeyGenParameterSpec.F4.longValue()); + + KeyCharacteristics outCharacteristics = new KeyCharacteristics(); + int result = mKeyStore.generateKey(name, args, entropy, 0, outCharacteristics); + assertEquals("generateKey should succeed", KeyStore.NO_ERROR, result); + } + public void testGenerateAndDelete() throws Exception { generateRsaKey("test"); assertTrue("delete should succeed", mKeyStore.delete("test")); @@ -741,6 +761,7 @@ public class KeyStoreTest extends ActivityUnitTestCase<Activity> { public void testAppId() throws Exception { 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); @@ -748,18 +769,19 @@ public class KeyStoreTest extends ActivityUnitTestCase<Activity> { 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.addBlob(KeymasterDefs.KM_TAG_APPLICATION_ID, new byte[] {0x01, 0x02, 0x03}); - args.addBlob(KeymasterDefs.KM_TAG_APPLICATION_DATA, null); + args.addBlob(KeymasterDefs.KM_TAG_APPLICATION_ID, id); + args.addLong(KeymasterDefs.KM_TAG_RSA_PUBLIC_EXPONENT, + RSAKeyGenParameterSpec.F4.longValue()); KeyCharacteristics outCharacteristics = new KeyCharacteristics(); - int result = mKeyStore.generateKey(name, args, 0, outCharacteristics); + int result = mKeyStore.generateKey(name, args, null, 0, outCharacteristics); assertEquals("generateRsaKey should succeed", KeyStore.NO_ERROR, result); assertEquals("getKeyCharacteristics should fail without application ID", KeymasterDefs.KM_ERROR_INVALID_KEY_BLOB, mKeyStore.getKeyCharacteristics(name, null, null, outCharacteristics)); assertEquals("getKeyCharacteristics should succeed with application ID", KeyStore.NO_ERROR, - mKeyStore.getKeyCharacteristics(name, new byte[] {0x01, 0x02, 0x03}, null, + mKeyStore.getKeyCharacteristics(name, new KeymasterBlob(id), null, outCharacteristics)); } @@ -784,19 +806,15 @@ public class KeyStoreTest extends ActivityUnitTestCase<Activity> { args.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE, KeymasterDefs.KM_MODE_OCB); args.addInt(KeymasterDefs.KM_TAG_CHUNK_LENGTH, 4096); args.addInt(KeymasterDefs.KM_TAG_MAC_LENGTH, 16); - args.addBlob(KeymasterDefs.KM_TAG_APPLICATION_ID, null); - args.addBlob(KeymasterDefs.KM_TAG_APPLICATION_DATA, null); KeyCharacteristics outCharacteristics = new KeyCharacteristics(); - int rc = mKeyStore.generateKey(name, args, 0, outCharacteristics); + int rc = mKeyStore.generateKey(name, args, null, 0, outCharacteristics); assertEquals("Generate should succeed", KeyStore.NO_ERROR, rc); KeymasterArguments out = new KeymasterArguments(); args = new KeymasterArguments(); - args.addBlob(KeymasterDefs.KM_TAG_APPLICATION_ID, null); - args.addBlob(KeymasterDefs.KM_TAG_APPLICATION_DATA, null); OperationResult result = mKeyStore.begin(name, KeymasterDefs.KM_PURPOSE_ENCRYPT, - true, args, out); + true, args, null, out); IBinder token = result.token; assertEquals("Begin should succeed", KeyStore.NO_ERROR, result.resultCode); result = mKeyStore.update(token, null, new byte[] {0x01, 0x02, 0x03, 0x04}); @@ -826,7 +844,7 @@ public class KeyStoreTest extends ActivityUnitTestCase<Activity> { private byte[] doOperation(String name, int purpose, byte[] in, KeymasterArguments beginArgs) { KeymasterArguments out = new KeymasterArguments(); OperationResult result = mKeyStore.begin(name, purpose, - true, beginArgs, out); + true, beginArgs, null, out); assertEquals("Begin should succeed", KeyStore.NO_ERROR, result.resultCode); IBinder token = result.token; result = mKeyStore.update(token, null, in); @@ -883,24 +901,21 @@ public class KeyStoreTest extends ActivityUnitTestCase<Activity> { args.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE, KeymasterDefs.KM_MODE_OCB); args.addInt(KeymasterDefs.KM_TAG_CHUNK_LENGTH, 4096); args.addInt(KeymasterDefs.KM_TAG_MAC_LENGTH, 16); - args.addBlob(KeymasterDefs.KM_TAG_APPLICATION_ID, null); - args.addBlob(KeymasterDefs.KM_TAG_APPLICATION_DATA, null); KeyCharacteristics outCharacteristics = new KeyCharacteristics(); - int rc = mKeyStore.generateKey(name, args, 0, outCharacteristics); + int rc = mKeyStore.generateKey(name, args, null, 0, outCharacteristics); assertEquals("Generate should succeed", KeyStore.NO_ERROR, rc); KeymasterArguments out = new KeymasterArguments(); args = new KeymasterArguments(); - args.addBlob(KeymasterDefs.KM_TAG_APPLICATION_ID, null); - args.addBlob(KeymasterDefs.KM_TAG_APPLICATION_DATA, null); OperationResult result = mKeyStore.begin(name, KeymasterDefs.KM_PURPOSE_ENCRYPT, - true, args, out); + true, args, null, out); assertEquals("Begin should succeed", KeyStore.NO_ERROR, result.resultCode); IBinder first = result.token; // Implementation detail: softkeymaster supports 16 concurrent operations for (int i = 0; i < 16; i++) { - result = mKeyStore.begin(name, KeymasterDefs.KM_PURPOSE_ENCRYPT, true, args, out); + result = mKeyStore.begin(name, KeymasterDefs.KM_PURPOSE_ENCRYPT, true, args, null, + out); assertEquals("Begin should succeed", KeyStore.NO_ERROR, result.resultCode); } // At this point the first operation should be pruned. diff --git a/libs/hwui/TessellationCache.cpp b/libs/hwui/TessellationCache.cpp index bc956be..1d6b7cc 100644 --- a/libs/hwui/TessellationCache.cpp +++ b/libs/hwui/TessellationCache.cpp @@ -380,6 +380,7 @@ void TessellationCache::precacheShadows(const Matrix4* drawTransform, const Rect const Vector3& lightCenter, float lightRadius) { ShadowDescription key(casterPerimeter, drawTransform); + if (mShadowCache.get(key)) return; sp<ShadowTask> task = new ShadowTask(drawTransform, localClip, opaque, casterPerimeter, transformXY, transformZ, lightCenter, lightRadius); if (mShadowProcessor == NULL) { diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp index 36ba3a9..1f61b23 100644 --- a/libs/hwui/renderthread/RenderProxy.cpp +++ b/libs/hwui/renderthread/RenderProxy.cpp @@ -395,6 +395,7 @@ CREATE_BRIDGE1(outputLogBuffer, int fd) { } void RenderProxy::outputLogBuffer(int fd) { + if (!RenderThread::hasInstance()) return; SETUP_TASK(outputLogBuffer); args->fd = fd; staticPostAndWait(task); diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index b606a6f..226a8ca 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -104,6 +104,9 @@ <uses-permission android:name="android.permission.ACCESS_KEYGUARD_SECURE_STORAGE" /> <uses-permission android:name="android.permission.TRUST_LISTENER" /> + <!-- Needed for WallpaperManager.clear in ImageWallpaper.updateWallpaperLocked --> + <uses-permission android:name="android.permission.SET_WALLPAPER"/> + <!-- Recents --> <uses-permission android:name="android.permission.BIND_APPWIDGET" /> diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothController.java index cbdd138..49693f5fe 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothController.java @@ -37,7 +37,7 @@ public interface BluetoothController { void onBluetoothPairedDevicesChanged(); } - public static final class PairedDevice { + public static final class PairedDevice implements Comparable<PairedDevice> { public static int STATE_DISCONNECTED = 0; public static int STATE_CONNECTING = 1; public static int STATE_CONNECTED = 2; @@ -55,5 +55,9 @@ public interface BluetoothController { if (state == STATE_DISCONNECTING) return "STATE_DISCONNECTING"; return "UNKNOWN"; } + + public int compareTo(PairedDevice another) { + return name.compareTo(another.name); + } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java index 81e1e45..894f82a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java @@ -45,7 +45,6 @@ import android.os.Looper; import android.os.Message; import android.os.ParcelUuid; import android.util.ArrayMap; -import android.util.ArraySet; import android.util.Log; import android.util.SparseArray; @@ -55,6 +54,7 @@ import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Set; +import java.util.TreeSet; public class BluetoothControllerImpl implements BluetoothController { private static final String TAG = "BluetoothController"; @@ -194,8 +194,8 @@ public class BluetoothControllerImpl implements BluetoothController { } @Override - public ArraySet<PairedDevice> getPairedDevices() { - final ArraySet<PairedDevice> rt = new ArraySet<>(); + public Set<PairedDevice> getPairedDevices() { + final Set<PairedDevice> rt = new TreeSet<>(); for (int i = 0; i < mDeviceInfo.size(); i++) { final BluetoothDevice device = mDeviceInfo.keyAt(i); final DeviceInfo info = mDeviceInfo.valueAt(i); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java index 2fbb812..f0dd943 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java @@ -190,7 +190,8 @@ public class SecurityControllerImpl implements SecurityController { NetworkCapabilities networkCapabilities = mConnectivityManager.getNetworkCapabilities(network); if (DEBUG) Log.d(TAG, "onAvailable " + network.netId + " : " + networkCapabilities); - if (networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_VPN)) { + if (networkCapabilities != null && + networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_VPN)) { setCurrentNetid(network.netId); } }; diff --git a/rs/java/android/renderscript/Allocation.java b/rs/java/android/renderscript/Allocation.java index c6afa2c..523c8fb 100644 --- a/rs/java/android/renderscript/Allocation.java +++ b/rs/java/android/renderscript/Allocation.java @@ -76,6 +76,8 @@ public class Allocation extends BaseObj { new HashMap<Long, Allocation>(); OnBufferAvailableListener mBufferNotifier; + private Surface mGetSurfaceSurface = null; + private Element.DataType validateObjectIsPrimitiveArray(Object d, boolean checkType) { final Class c = d.getClass(); if (!c.isArray()) { @@ -1990,7 +1992,12 @@ public class Allocation extends BaseObj { if ((mUsage & USAGE_IO_INPUT) == 0) { throw new RSInvalidStateException("Allocation is not a surface texture."); } - return mRS.nAllocationGetSurface(getID(mRS)); + + if (mGetSurfaceSurface == null) { + mGetSurfaceSurface = mRS.nAllocationGetSurface(getID(mRS)); + } + + return mGetSurfaceSurface; } /** diff --git a/rs/java/android/renderscript/Element.java b/rs/java/android/renderscript/Element.java index 287b3f1..60ff996 100644 --- a/rs/java/android/renderscript/Element.java +++ b/rs/java/android/renderscript/Element.java @@ -114,7 +114,8 @@ public class Element extends BaseObj { * MATRIX the three matrix types contain FLOAT_32 elements and are treated * as 32 bits for alignment purposes. * - * RS_* objects. 32 bit opaque handles. + * RS_* objects: opaque handles with implementation dependent + * sizes. */ public enum DataType { NONE (0, 0), diff --git a/rs/java/android/renderscript/FileA3D.java b/rs/java/android/renderscript/FileA3D.java index 4164810..9d8f162 100644 --- a/rs/java/android/renderscript/FileA3D.java +++ b/rs/java/android/renderscript/FileA3D.java @@ -145,6 +145,9 @@ public class FileA3D extends BaseObj { case MESH: entry.mLoadedObj = new Mesh(objectID, rs); break; + + default: + throw new RSRuntimeException("Unrecognized object type in file."); } entry.mLoadedObj.updateFromNative(); diff --git a/rs/java/android/renderscript/Mesh.java b/rs/java/android/renderscript/Mesh.java index 1a5dc9e..13c8e1c 100644 --- a/rs/java/android/renderscript/Mesh.java +++ b/rs/java/android/renderscript/Mesh.java @@ -363,6 +363,9 @@ public class Mesh extends BaseObj { alloc = Allocation.createTyped(mRS, entry.t, mUsage); } else if(entry.e != null) { alloc = Allocation.createSized(mRS, entry.e, entry.size, mUsage); + } else { + // Should never happen because the builder will always set one + throw new IllegalStateException("Builder corrupt, no valid element in entry."); } vertexBuffers[ct] = alloc; vtx[ct] = alloc.getID(mRS); @@ -375,6 +378,9 @@ public class Mesh extends BaseObj { alloc = Allocation.createTyped(mRS, entry.t, mUsage); } else if(entry.e != null) { alloc = Allocation.createSized(mRS, entry.e, entry.size, mUsage); + } else { + // Should never happen because the builder will always set one + throw new IllegalStateException("Builder corrupt, no valid element in entry."); } long allocID = (alloc == null) ? 0 : alloc.getID(mRS); indexBuffers[ct] = alloc; @@ -811,9 +817,7 @@ public class Mesh extends BaseObj { sm.getVertexAllocation(0).copy1DRangeFromUnchecked(0, mMaxIndex, mVtxData); if(uploadToBufferObject) { - if (uploadToBufferObject) { - sm.getVertexAllocation(0).syncAll(Allocation.USAGE_SCRIPT); - } + sm.getVertexAllocation(0).syncAll(Allocation.USAGE_SCRIPT); } sm.getIndexSetAllocation(0).copy1DRangeFromUnchecked(0, mIndexCount, mIndexData); diff --git a/rs/java/android/renderscript/RenderScript.java b/rs/java/android/renderscript/RenderScript.java index f08c985..45f0ca6 100644 --- a/rs/java/android/renderscript/RenderScript.java +++ b/rs/java/android/renderscript/RenderScript.java @@ -29,6 +29,7 @@ import android.util.Log; import android.view.Surface; import android.os.SystemProperties; import android.os.Trace; +import java.util.ArrayList; /** * This class provides access to a RenderScript context, which controls RenderScript @@ -49,6 +50,12 @@ public class RenderScript { @SuppressWarnings({"UnusedDeclaration", "deprecation"}) static final boolean LOG_ENABLED = false; + static private ArrayList<RenderScript> mProcessContextList = new ArrayList<RenderScript>(); + private boolean mIsProcessContext = false; + private int mContextFlags = 0; + private int mContextSdkVersion = 0; + + private Context mApplicationContext; /* @@ -1313,20 +1320,13 @@ public class RenderScript { } /** - * @hide - */ - public static RenderScript create(Context ctx, int sdkVersion) { - return create(ctx, sdkVersion, ContextType.NORMAL, CREATE_FLAG_NONE); - } - - /** * Create a RenderScript context. * * @hide * @param ctx The context. * @return RenderScript */ - public static RenderScript create(Context ctx, int sdkVersion, ContextType ct, int flags) { + private static RenderScript internalCreate(Context ctx, int sdkVersion, ContextType ct, int flags) { if (!sInitialized) { Log.e(LOG_TAG, "RenderScript.create() called when disabled; someone is likely to crash"); return null; @@ -1341,6 +1341,8 @@ public class RenderScript { rs.mDev = rs.nDeviceCreate(); rs.mContext = rs.nContextCreate(rs.mDev, flags, sdkVersion, ct.mID); rs.mContextType = ct; + rs.mContextFlags = flags; + rs.mContextSdkVersion = sdkVersion; if (rs.mContext == 0) { throw new RSDriverException("Failed to create RS context."); } @@ -1350,7 +1352,9 @@ public class RenderScript { } /** - * Create a RenderScript context. + * calls create(ctx, ContextType.NORMAL, CREATE_FLAG_NONE) + * + * See documentation for @create for details * * @param ctx The context. * @return RenderScript @@ -1360,21 +1364,33 @@ public class RenderScript { } /** - * Create a RenderScript context. + * calls create(ctx, ct, CREATE_FLAG_NONE) * + * See documentation for @create for details * * @param ctx The context. * @param ct The type of context to be created. * @return RenderScript */ public static RenderScript create(Context ctx, ContextType ct) { - int v = ctx.getApplicationInfo().targetSdkVersion; - return create(ctx, v, ct, CREATE_FLAG_NONE); + return create(ctx, ct, CREATE_FLAG_NONE); } - /** - * Create a RenderScript context. + + /** + * Gets or creates a RenderScript context of the specified type. + * + * The returned context will be cached for future reuse within + * the process. When an application is finished using + * RenderScript it should call releaseAllContexts() + * + * A process context is a context designed for easy creation and + * lifecycle management. Multiple calls to this function will + * return the same object provided they are called with the same + * options. This allows it to be used any time a RenderScript + * context is needed. * + * Prior to API 23 this always created a new context. * * @param ctx The context. * @param ct The type of context to be created. @@ -1387,6 +1403,100 @@ public class RenderScript { } /** + * calls create(ctx, sdkVersion, ContextType.NORMAL, CREATE_FLAG_NONE) + * + * Used by the RenderScriptThunker to maintain backward compatibility. + * + * @hide + * @param ctx The context. + * @param sdkVersion The target SDK Version. + * @return RenderScript + */ + public static RenderScript create(Context ctx, int sdkVersion) { + return create(ctx, sdkVersion, ContextType.NORMAL, CREATE_FLAG_NONE); + } + + /** + * Gets or creates a RenderScript context of the specified type. + * + * @hide + * @param ctx The context. + * @param ct The type of context to be created. + * @param sdkVersion The target SDK Version. + * @param flags The OR of the CREATE_FLAG_* options desired + * @return RenderScript + */ + public static RenderScript create(Context ctx, int sdkVersion, ContextType ct, int flags) { + if (sdkVersion < 23) { + return internalCreate(ctx, sdkVersion, ct, flags); + } + + synchronized (mProcessContextList) { + for (RenderScript prs : mProcessContextList) { + if ((prs.mContextType == ct) && + (prs.mContextFlags == flags) && + (prs.mContextSdkVersion == sdkVersion)) { + + return prs; + } + } + + RenderScript prs = internalCreate(ctx, sdkVersion, ct, flags); + prs.mIsProcessContext = true; + mProcessContextList.add(prs); + return prs; + } + } + + /** + * @hide + * + * Releases all the process contexts. This is the same as + * calling .destroy() on each unique context retreived with + * create(...). If no contexts have been created this + * function does nothing. + * + * Typically you call this when your application is losing focus + * and will not be using a context for some time. + * + * This has no effect on a context created with + * createMultiContext() + */ + public static void releaseAllContexts() { + ArrayList<RenderScript> oldList; + synchronized (mProcessContextList) { + oldList = mProcessContextList; + mProcessContextList = new ArrayList<RenderScript>(); + } + + for (RenderScript prs : oldList) { + prs.mIsProcessContext = false; + prs.destroy(); + } + oldList.clear(); + } + + + + /** + * Create a RenderScript context. + * + * This is an advanced function intended for applications which + * need to create more than one RenderScript context to be used + * at the same time. + * + * If you need a single context please use create() + * + * @hide + * @param ctx The context. + * @return RenderScript + */ + public static RenderScript createMultiContext(Context ctx, ContextType ct, int flags, int API_number) { + return internalCreate(ctx, API_number, ct, flags); + } + + + /** * Print the currently available debugging information about the state of * the RS context to the log. * @@ -1442,8 +1552,16 @@ public class RenderScript { * using this context or any objects belonging to this context is * illegal. * + * API 23+, this function is a NOP if the context was created + * with create(). Please use releaseAllContexts() to clean up + * contexts created with the create function. + * */ public void destroy() { + if (mIsProcessContext) { + // users cannot destroy a process context + return; + } validate(); helpDestroy(); } diff --git a/rs/java/android/renderscript/Script.java b/rs/java/android/renderscript/Script.java index 83aeedd..65056ac 100644 --- a/rs/java/android/renderscript/Script.java +++ b/rs/java/android/renderscript/Script.java @@ -251,9 +251,14 @@ public class Script extends BaseObj { "At least one of ain or aout is required to be non-null."); } - long[] in_ids = new long[ains.length]; - for (int index = 0; index < ains.length; ++index) { - in_ids[index] = ains[index].getID(mRS); + long[] in_ids; + if (ains != null) { + in_ids = new long[ains.length]; + for (int index = 0; index < ains.length; ++index) { + in_ids[index] = ains[index].getID(mRS); + } + } else { + in_ids = null; } long out_id = 0; diff --git a/rs/java/android/renderscript/ScriptGroup2.java b/rs/java/android/renderscript/ScriptGroup2.java index 113b896..4a56572 100644 --- a/rs/java/android/renderscript/ScriptGroup2.java +++ b/rs/java/android/renderscript/ScriptGroup2.java @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package android.renderscript; import android.util.Log; @@ -13,356 +29,410 @@ import java.util.Map; You have tried to change the API from what has been previously approved. To make these errors go away, you have two choices: - 1) You can add "@hide" javadoc comments to the methods, etc. listed in the - errors above. +1) You can add "@hide" javadoc comments to the methods, etc. listed in the +errors above. - 2) You can update current.txt by executing the following command: - make update-api +2) You can update current.txt by executing the following command: +make update-api To submit the revised current.txt to the main Android repository, you will need approval. ****************************** - @hide Pending Android public API approval. - */ +@hide Pending Android public API approval. +*/ public class ScriptGroup2 extends BaseObj { - public static class Closure extends BaseObj { - private Allocation mReturnValue; - private Map<Script.FieldID, Object> mBindings; + public static class Closure extends BaseObj { + private Allocation mReturnValue; + private Map<Script.FieldID, Object> mBindings; - private Future mReturnFuture; - private Map<Script.FieldID, Future> mGlobalFuture; + private Future mReturnFuture; + private Map<Script.FieldID, Future> mGlobalFuture; - private FieldPacker mFP; + private FieldPacker mFP; - private static final String TAG = "Closure"; + private static final String TAG = "Closure"; - public Closure(long id, RenderScript rs) { - super(id, rs); - } + public Closure(long id, RenderScript rs) { + super(id, rs); + } - public Closure(RenderScript rs, Script.KernelID kernelID, Type returnType, - Object[] args, Map<Script.FieldID, Object> globals) { - super(0, rs); - - mReturnValue = Allocation.createTyped(rs, returnType); - mBindings = new HashMap<Script.FieldID, Object>(); - mGlobalFuture = new HashMap<Script.FieldID, Future>(); - - int numValues = args.length + globals.size(); - - long[] fieldIDs = new long[numValues]; - long[] values = new long[numValues]; - int[] sizes = new int[numValues]; - long[] depClosures = new long[numValues]; - long[] depFieldIDs = new long[numValues]; - - int i; - for (i = 0; i < args.length; i++) { - Object obj = args[i]; - fieldIDs[i] = 0; - if (obj instanceof UnboundValue) { - UnboundValue unbound = (UnboundValue)obj; - unbound.addReference(this, i); - } else { - retrieveValueAndDependenceInfo(rs, i, args[i], values, sizes, - depClosures, depFieldIDs); + public Closure(RenderScript rs, Script.KernelID kernelID, Type returnType, + Object[] args, Map<Script.FieldID, Object> globals) { + super(0, rs); + + mReturnValue = Allocation.createTyped(rs, returnType); + mBindings = new HashMap<Script.FieldID, Object>(); + mGlobalFuture = new HashMap<Script.FieldID, Future>(); + + int numValues = args.length + globals.size(); + + long[] fieldIDs = new long[numValues]; + long[] values = new long[numValues]; + int[] sizes = new int[numValues]; + long[] depClosures = new long[numValues]; + long[] depFieldIDs = new long[numValues]; + + int i; + for (i = 0; i < args.length; i++) { + Object obj = args[i]; + fieldIDs[i] = 0; + if (obj instanceof UnboundValue) { + UnboundValue unbound = (UnboundValue)obj; + unbound.addReference(this, i); + } else { + retrieveValueAndDependenceInfo(rs, i, args[i], values, sizes, + depClosures, depFieldIDs); + } + } + + for (Map.Entry<Script.FieldID, Object> entry : globals.entrySet()) { + Object obj = entry.getValue(); + Script.FieldID fieldID = entry.getKey(); + fieldIDs[i] = fieldID.getID(rs); + if (obj instanceof UnboundValue) { + UnboundValue unbound = (UnboundValue)obj; + unbound.addReference(this, fieldID); + } else { + retrieveValueAndDependenceInfo(rs, i, obj, values, + sizes, depClosures, depFieldIDs); + } + i++; + } + + long id = rs.nClosureCreate(kernelID.getID(rs), mReturnValue.getID(rs), + fieldIDs, values, sizes, depClosures, depFieldIDs); + + setID(id); } - } - - for (Map.Entry<Script.FieldID, Object> entry : globals.entrySet()) { - Object obj = entry.getValue(); - Script.FieldID fieldID = entry.getKey(); - fieldIDs[i] = fieldID.getID(rs); - if (obj instanceof UnboundValue) { - UnboundValue unbound = (UnboundValue)obj; - unbound.addReference(this, fieldID); - } else { - retrieveValueAndDependenceInfo(rs, i, obj, values, - sizes, depClosures, depFieldIDs); + + public Closure(RenderScript rs, Script.InvokeID invokeID, + Object[] args, Map<Script.FieldID, Object> globals) { + super(0, rs); + mFP = FieldPacker.createFieldPack(args); + + mBindings = new HashMap<Script.FieldID, Object>(); + mGlobalFuture = new HashMap<Script.FieldID, Future>(); + + int numValues = globals.size(); + + long[] fieldIDs = new long[numValues]; + long[] values = new long[numValues]; + int[] sizes = new int[numValues]; + long[] depClosures = new long[numValues]; + long[] depFieldIDs = new long[numValues]; + + int i = 0; + for (Map.Entry<Script.FieldID, Object> entry : globals.entrySet()) { + Object obj = entry.getValue(); + Script.FieldID fieldID = entry.getKey(); + fieldIDs[i] = fieldID.getID(rs); + if (obj instanceof UnboundValue) { + UnboundValue unbound = (UnboundValue)obj; + unbound.addReference(this, fieldID); + } else { + // TODO(yangni): Verify obj not a future. + retrieveValueAndDependenceInfo(rs, i, obj, values, + sizes, depClosures, depFieldIDs); + } + i++; + } + + long id = rs.nInvokeClosureCreate(invokeID.getID(rs), mFP.getData(), fieldIDs, + values, sizes); + + setID(id); } - i++; - } - long id = rs.nClosureCreate(kernelID.getID(rs), mReturnValue.getID(rs), - fieldIDs, values, sizes, depClosures, depFieldIDs); + private static + void retrieveValueAndDependenceInfo(RenderScript rs, + int index, Object obj, + long[] values, int[] sizes, + long[] depClosures, + long[] depFieldIDs) { + + if (obj instanceof Future) { + Future f = (Future)obj; + obj = f.getValue(); + depClosures[index] = f.getClosure().getID(rs); + Script.FieldID fieldID = f.getFieldID(); + depFieldIDs[index] = fieldID != null ? fieldID.getID(rs) : 0; + if (obj == null) { + // Value is originally created by the owner closure + values[index] = 0; + sizes[index] = 0; + return; + } + } else { + depClosures[index] = 0; + depFieldIDs[index] = 0; + } + + ValueAndSize vs = new ValueAndSize(rs, obj); + values[index] = vs.value; + sizes[index] = vs.size; + } - setID(id); - } + public Future getReturn() { + if (mReturnFuture == null) { + mReturnFuture = new Future(this, null, mReturnValue); + } - public Closure(RenderScript rs, Script.InvokeID invokeID, - Object[] args, Map<Script.FieldID, Object> globals) { - super(0, rs); - mFP = FieldPacker.createFieldPack(args); - - mBindings = new HashMap<Script.FieldID, Object>(); - mGlobalFuture = new HashMap<Script.FieldID, Future>(); - - int numValues = globals.size(); - - long[] fieldIDs = new long[numValues]; - long[] values = new long[numValues]; - int[] sizes = new int[numValues]; - long[] depClosures = new long[numValues]; - long[] depFieldIDs = new long[numValues]; - - int i = 0; - for (Map.Entry<Script.FieldID, Object> entry : globals.entrySet()) { - Object obj = entry.getValue(); - Script.FieldID fieldID = entry.getKey(); - fieldIDs[i] = fieldID.getID(rs); - if (obj instanceof UnboundValue) { - UnboundValue unbound = (UnboundValue)obj; - unbound.addReference(this, fieldID); - } else { - // TODO(yangni): Verify obj not a future. - retrieveValueAndDependenceInfo(rs, i, obj, values, - sizes, depClosures, depFieldIDs); + return mReturnFuture; } - i++; - } - long id = rs.nInvokeClosureCreate(invokeID.getID(rs), mFP.getData(), fieldIDs, - values, sizes); + public Future getGlobal(Script.FieldID field) { + Future f = mGlobalFuture.get(field); - setID(id); - } + if (f == null) { + // If the field is not bound to this closure, this will return a future + // without an associated value (reference). So this is not working for + // cross-module (cross-script) linking in this case where a field not + // explicitly bound. + f = new Future(this, field, mBindings.get(field)); + mGlobalFuture.put(field, f); + } - private static void retrieveValueAndDependenceInfo(RenderScript rs, - int index, Object obj, long[] values, int[] sizes, long[] depClosures, - long[] depFieldIDs) { - - if (obj instanceof Future) { - Future f = (Future)obj; - obj = f.getValue(); - depClosures[index] = f.getClosure().getID(rs); - Script.FieldID fieldID = f.getFieldID(); - depFieldIDs[index] = fieldID != null ? fieldID.getID(rs) : 0; - if (obj == null) { - // Value is originally created by the owner closure - values[index] = 0; - sizes[index] = 0; - return; + return f; } - } else { - depClosures[index] = 0; - depFieldIDs[index] = 0; - } - - ValueAndSize vs = new ValueAndSize(rs, obj); - values[index] = vs.value; - sizes[index] = vs.size; - } - public Future getReturn() { - if (mReturnFuture == null) { - mReturnFuture = new Future(this, null, mReturnValue); - } + void setArg(int index, Object obj) { + ValueAndSize vs = new ValueAndSize(mRS, obj); + mRS.nClosureSetArg(getID(mRS), index, vs.value, vs.size); + } + + void setGlobal(Script.FieldID fieldID, Object obj) { + ValueAndSize vs = new ValueAndSize(mRS, obj); + mRS.nClosureSetGlobal(getID(mRS), fieldID.getID(mRS), vs.value, vs.size); + } - return mReturnFuture; + private static final class ValueAndSize { + public ValueAndSize(RenderScript rs, Object obj) { + if (obj instanceof Allocation) { + value = ((Allocation)obj).getID(rs); + size = -1; + } else if (obj instanceof Boolean) { + value = ((Boolean)obj).booleanValue() ? 1 : 0; + size = 4; + } else if (obj instanceof Integer) { + value = ((Integer)obj).longValue(); + size = 4; + } else if (obj instanceof Long) { + value = ((Long)obj).longValue(); + size = 8; + } else if (obj instanceof Float) { + value = ((Float)obj).longValue(); + size = 4; + } else if (obj instanceof Double) { + value = ((Double)obj).longValue(); + size = 8; + } + } + public long value; + public int size; + } } - public Future getGlobal(Script.FieldID field) { - Future f = mGlobalFuture.get(field); + public static class Future { + Closure mClosure; + Script.FieldID mFieldID; + Object mValue; - if (f == null) { - // If the field is not bound to this closure, this will return a future - // without an associated value (reference). So this is not working for - // cross-module (cross-script) linking in this case where a field not - // explicitly bound. - f = new Future(this, field, mBindings.get(field)); - mGlobalFuture.put(field, f); - } + Future(Closure closure, Script.FieldID fieldID, Object value) { + mClosure = closure; + mFieldID = fieldID; + mValue = value; + } - return f; + Closure getClosure() { return mClosure; } + Script.FieldID getFieldID() { return mFieldID; } + Object getValue() { return mValue; } } - void setArg(int index, Object obj) { - ValueAndSize vs = new ValueAndSize(mRS, obj); - mRS.nClosureSetArg(getID(mRS), index, vs.value, vs.size); - } + public static class UnboundValue { + // Either mFieldID or mArgIndex should be set but not both. + List<Pair<Closure, Script.FieldID>> mFieldID; + // -1 means unset. Legal values are 0 .. n-1, where n is the number of + // arguments for the referencing closure. + List<Pair<Closure, Integer>> mArgIndex; - void setGlobal(Script.FieldID fieldID, Object obj) { - ValueAndSize vs = new ValueAndSize(mRS, obj); - mRS.nClosureSetGlobal(getID(mRS), fieldID.getID(mRS), vs.value, vs.size); - } + UnboundValue() { + mFieldID = new ArrayList<Pair<Closure, Script.FieldID>>(); + mArgIndex = new ArrayList<Pair<Closure, Integer>>(); + } - private static final class ValueAndSize { - public ValueAndSize(RenderScript rs, Object obj) { - if (obj instanceof Allocation) { - value = ((Allocation)obj).getID(rs); - size = -1; - } else if (obj instanceof Boolean) { - value = ((Boolean)obj).booleanValue() ? 1 : 0; - size = 4; - } else if (obj instanceof Integer) { - value = ((Integer)obj).longValue(); - size = 4; - } else if (obj instanceof Long) { - value = ((Long)obj).longValue(); - size = 8; - } else if (obj instanceof Float) { - value = ((Float)obj).longValue(); - size = 4; - } else if (obj instanceof Double) { - value = ((Double)obj).longValue(); - size = 8; + void addReference(Closure closure, int index) { + mArgIndex.add(Pair.create(closure, Integer.valueOf(index))); } - } - public long value; - public int size; - } - } - public static class Future { - Closure mClosure; - Script.FieldID mFieldID; - Object mValue; + void addReference(Closure closure, Script.FieldID fieldID) { + mFieldID.add(Pair.create(closure, fieldID)); + } - Future(Closure closure, Script.FieldID fieldID, Object value) { - mClosure = closure; - mFieldID = fieldID; - mValue = value; + void set(Object value) { + for (Pair<Closure, Integer> p : mArgIndex) { + Closure closure = p.first; + int index = p.second.intValue(); + closure.setArg(index, value); + } + for (Pair<Closure, Script.FieldID> p : mFieldID) { + Closure closure = p.first; + Script.FieldID fieldID = p.second; + closure.setGlobal(fieldID, value); + } + } } - Closure getClosure() { return mClosure; } - Script.FieldID getFieldID() { return mFieldID; } - Object getValue() { return mValue; } - } - - public static class UnboundValue { - // Either mFieldID or mArgIndex should be set but not both. - List<Pair<Closure, Script.FieldID>> mFieldID; - // -1 means unset. Legal values are 0 .. n-1, where n is the number of - // arguments for the referencing closure. - List<Pair<Closure, Integer>> mArgIndex; - - UnboundValue() { - mFieldID = new ArrayList<Pair<Closure, Script.FieldID>>(); - mArgIndex = new ArrayList<Pair<Closure, Integer>>(); - } + List<Closure> mClosures; + List<UnboundValue> mInputs; + Future[] mOutputs; - void addReference(Closure closure, int index) { - mArgIndex.add(Pair.create(closure, Integer.valueOf(index))); - } + private static final String TAG = "ScriptGroup2"; - void addReference(Closure closure, Script.FieldID fieldID) { - mFieldID.add(Pair.create(closure, fieldID)); + public ScriptGroup2(long id, RenderScript rs) { + super(id, rs); } - void set(Object value) { - for (Pair<Closure, Integer> p : mArgIndex) { - Closure closure = p.first; - int index = p.second.intValue(); - closure.setArg(index, value); - } - for (Pair<Closure, Script.FieldID> p : mFieldID) { - Closure closure = p.first; - Script.FieldID fieldID = p.second; - closure.setGlobal(fieldID, value); - } + ScriptGroup2(RenderScript rs, List<Closure> closures, + List<UnboundValue> inputs, Future[] outputs) { + super(0, rs); + mClosures = closures; + mInputs = inputs; + mOutputs = outputs; + + long[] closureIDs = new long[closures.size()]; + for (int i = 0; i < closureIDs.length; i++) { + closureIDs[i] = closures.get(i).getID(rs); + } + long id = rs.nScriptGroup2Create(ScriptC.mCachePath, closureIDs); + setID(id); } - } - List<Closure> mClosures; - List<UnboundValue> mInputs; - Future[] mOutputs; + public Object[] execute(Object... inputs) { + if (inputs.length < mInputs.size()) { + Log.e(TAG, this.toString() + " receives " + inputs.length + " inputs, " + + "less than expected " + mInputs.size()); + return null; + } - private static final String TAG = "ScriptGroup2"; + if (inputs.length > mInputs.size()) { + Log.i(TAG, this.toString() + " receives " + inputs.length + " inputs, " + + "more than expected " + mInputs.size()); + } - public ScriptGroup2(long id, RenderScript rs) { - super(id, rs); - } + for (int i = 0; i < mInputs.size(); i++) { + Object obj = inputs[i]; + if (obj instanceof Future || obj instanceof UnboundValue) { + Log.e(TAG, this.toString() + ": input " + i + + " is a future or unbound value"); + return null; + } + UnboundValue unbound = mInputs.get(i); + unbound.set(obj); + } - ScriptGroup2(RenderScript rs, List<Closure> closures, - List<UnboundValue> inputs, Future[] outputs) { - super(0, rs); - mClosures = closures; - mInputs = inputs; - mOutputs = outputs; + mRS.nScriptGroup2Execute(getID(mRS)); - long[] closureIDs = new long[closures.size()]; - for (int i = 0; i < closureIDs.length; i++) { - closureIDs[i] = closures.get(i).getID(rs); - } - long id = rs.nScriptGroup2Create(ScriptC.mCachePath, closureIDs); - setID(id); - } - - public Object[] execute(Object... inputs) { - if (inputs.length < mInputs.size()) { - Log.e(TAG, this.toString() + " receives " + inputs.length + " inputs, " + - "less than expected " + mInputs.size()); - return null; + Object[] outputObjs = new Object[mOutputs.length]; + int i = 0; + for (Future f : mOutputs) { + outputObjs[i++] = f.getValue(); + } + return outputObjs; } - if (inputs.length > mInputs.size()) { - Log.i(TAG, this.toString() + " receives " + inputs.length + " inputs, " + - "more than expected " + mInputs.size()); + /** + @hide Pending Android public API approval. + */ + public static final class Binding { + public Script.FieldID mField; + public Object mValue; + public Binding(Script.FieldID field, Object value) { + mField = field; + mValue = value; + } } - for (int i = 0; i < mInputs.size(); i++) { - Object obj = inputs[i]; - if (obj instanceof Future || obj instanceof UnboundValue) { - Log.e(TAG, this.toString() + ": input " + i + - " is a future or unbound value"); - return null; - } - UnboundValue unbound = mInputs.get(i); - unbound.set(obj); - } + /** + @hide Pending Android public API approval. + */ + public static final class Builder { + RenderScript mRS; + List<Closure> mClosures; + List<UnboundValue> mInputs; + private static final String TAG = "ScriptGroup2.Builder"; + + public Builder(RenderScript rs) { + mRS = rs; + mClosures = new ArrayList<Closure>(); + mInputs = new ArrayList<UnboundValue>(); + } - mRS.nScriptGroup2Execute(getID(mRS)); + public Closure addKernel(Script.KernelID k, Type returnType, Object[] args, + Map<Script.FieldID, Object> globalBindings) { + Closure c = new Closure(mRS, k, returnType, args, globalBindings); + mClosures.add(c); + return c; + } - Object[] outputObjs = new Object[mOutputs.length]; - int i = 0; - for (Future f : mOutputs) { - outputObjs[i++] = f.getValue(); - } - return outputObjs; - } - - /** - @hide Pending Android public API approval. - */ - public static final class Builder { - RenderScript mRS; - List<Closure> mClosures; - List<UnboundValue> mInputs; + public Closure addInvoke(Script.InvokeID invoke, Object[] args, + Map<Script.FieldID, Object> globalBindings) { + Closure c = new Closure(mRS, invoke, args, globalBindings); + mClosures.add(c); + return c; + } - private static final String TAG = "ScriptGroup2.Builder"; + public UnboundValue addInput() { + UnboundValue unbound = new UnboundValue(); + mInputs.add(unbound); + return unbound; + } - public Builder(RenderScript rs) { - mRS = rs; - mClosures = new ArrayList<Closure>(); - mInputs = new ArrayList<UnboundValue>(); - } + public Closure addKernel(Script.KernelID k, Type returnType, Object... argsAndBindings) { + ArrayList<Object> args = new ArrayList<Object>(); + Map<Script.FieldID, Object> bindingMap = new HashMap<Script.FieldID, Object>(); + if (!seperateArgsAndBindings(argsAndBindings, args, bindingMap)) { + return null; + } + return addKernel(k, returnType, args.toArray(), bindingMap); + } - public Closure addKernel(Script.KernelID k, Type returnType, Object[] args, - Map<Script.FieldID, Object> globalBindings) { - Closure c = new Closure(mRS, k, returnType, args, globalBindings); - mClosures.add(c); - return c; - } + public Closure addInvoke(Script.InvokeID invoke, Object... argsAndBindings) { + ArrayList<Object> args = new ArrayList<Object>(); + Map<Script.FieldID, Object> bindingMap = new HashMap<Script.FieldID, Object>(); + if (!seperateArgsAndBindings(argsAndBindings, args, bindingMap)) { + return null; + } + return addInvoke(invoke, args.toArray(), bindingMap); + } - public Closure addInvoke(Script.InvokeID invoke, Object[] args, - Map<Script.FieldID, Object> globalBindings) { - Closure c = new Closure(mRS, invoke, args, globalBindings); - mClosures.add(c); - return c; - } + public ScriptGroup2 create(Future... outputs) { + ScriptGroup2 ret = new ScriptGroup2(mRS, mClosures, mInputs, outputs); + return ret; + } - public UnboundValue addInput() { - UnboundValue unbound = new UnboundValue(); - mInputs.add(unbound); - return unbound; - } + private boolean seperateArgsAndBindings(Object[] argsAndBindings, + ArrayList<Object> args, + Map<Script.FieldID, Object> bindingMap) { + int i; + for (i = 0; i < argsAndBindings.length; i++) { + if (argsAndBindings[i] instanceof Binding) { + break; + } + args.add(argsAndBindings[i]); + } + + for (; i < argsAndBindings.length; i++) { + if (!(argsAndBindings[i] instanceof Binding)) { + return false; + } + Binding b = (Binding)argsAndBindings[i]; + bindingMap.put(b.mField, b.mValue); + } + + return true; + } - public ScriptGroup2 create(Future... outputs) { - ScriptGroup2 ret = new ScriptGroup2(mRS, mClosures, mInputs, outputs); - return ret; } - - } } diff --git a/rs/jni/android_renderscript_RenderScript.cpp b/rs/jni/android_renderscript_RenderScript.cpp index 6d6757d..ba20881 100644 --- a/rs/jni/android_renderscript_RenderScript.cpp +++ b/rs/jni/android_renderscript_RenderScript.cpp @@ -40,7 +40,6 @@ #include <rsEnv.h> #include <gui/Surface.h> #include <gui/GLConsumer.h> -#include <gui/Surface.h> #include <android_runtime/android_graphics_SurfaceTexture.h> //#define LOG_API ALOGE diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 34a5249..ab1a1e8 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -1215,7 +1215,7 @@ public final class ActivityManagerService extends ActivityManagerNative TAG, "Death received in " + this + " for thread " + mAppThread.asBinder()); synchronized(ActivityManagerService.this) { - appDiedLocked(mApp, mPid, mAppThread); + appDiedLocked(mApp, mPid, mAppThread, true); } } } @@ -2991,6 +2991,15 @@ public final class ActivityManagerService extends ActivityManagerNative if ("1".equals(SystemProperties.get("debug.checkjni"))) { debugFlags |= Zygote.DEBUG_ENABLE_CHECKJNI; } + String jitDebugProperty = SystemProperties.get("debug.usejit"); + if ("true".equals(jitDebugProperty)) { + debugFlags |= Zygote.DEBUG_ENABLE_JIT; + } else if (!"false".equals(jitDebugProperty)) { + // If we didn't force disable by setting false, defer to the dalvik vm options. + if ("true".equals(SystemProperties.get("dalvik.vm.usejit"))) { + debugFlags |= Zygote.DEBUG_ENABLE_JIT; + } + } if ("1".equals(SystemProperties.get("debug.jni.logging"))) { debugFlags |= Zygote.DEBUG_ENABLE_JNI_LOGGING; } @@ -4678,10 +4687,11 @@ public final class ActivityManagerService extends ActivityManagerNative } final void appDiedLocked(ProcessRecord app) { - appDiedLocked(app, app.pid, app.thread); + appDiedLocked(app, app.pid, app.thread, false); } - final void appDiedLocked(ProcessRecord app, int pid, IApplicationThread thread) { + final void appDiedLocked(ProcessRecord app, int pid, IApplicationThread thread, + boolean fromBinderDied) { // First check if this ProcessRecord is actually active for the pid. synchronized (mPidsSelfLocked) { ProcessRecord curProc = mPidsSelfLocked.get(pid); @@ -4697,7 +4707,9 @@ public final class ActivityManagerService extends ActivityManagerNative } if (!app.killed) { - Process.killProcessQuiet(pid); + if (!fromBinderDied) { + Process.killProcessQuiet(pid); + } Process.killProcessGroup(app.info.uid, pid); app.killed = true; } diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java index 8533f69..3174e69 100644 --- a/services/core/java/com/android/server/connectivity/Vpn.java +++ b/services/core/java/com/android/server/connectivity/Vpn.java @@ -1009,6 +1009,14 @@ public class Vpn { public synchronized LegacyVpnInfo getLegacyVpnInfo() { // Check if the caller is authorized. enforceControlPermission(); + return getLegacyVpnInfoPrivileged(); + } + + /** + * Return the information of the current ongoing legacy VPN. + * Callers are responsible for checking permissions if needed. + */ + public synchronized LegacyVpnInfo getLegacyVpnInfoPrivileged() { if (mLegacyVpnRunner == null) return null; final LegacyVpnInfo info = new LegacyVpnInfo(); diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index d353494..b820d7e 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -1681,6 +1681,7 @@ final class Settings { // // DO NOT MODIFY THIS FORMAT UNLESS YOU CAN ALSO MODIFY ITS USERS // FROM NATIVE CODE. AT THE MOMENT, LOOK AT THE FOLLOWING SOURCES: + // system/core/logd/LogStatistics.cpp // system/core/run-as/run-as.c // system/core/sdcard/sdcard.c // external/libselinux/src/android.c:package_info_init() diff --git a/services/core/java/com/android/server/wm/DimLayer.java b/services/core/java/com/android/server/wm/DimLayer.java index c09ea5c..3f5ae56 100644 --- a/services/core/java/com/android/server/wm/DimLayer.java +++ b/services/core/java/com/android/server/wm/DimLayer.java @@ -140,10 +140,9 @@ public class DimLayer { } /** - * @param layer The new layer value. - * @param inTransaction Whether the call is made within a surface transaction. + * NOTE: Must be called with Surface transaction open. */ - void adjustSurface(int layer, boolean inTransaction) { + private void adjustBounds() { final int dw, dh; final float xPos, yPos; if (!mStack.isFullscreen()) { @@ -163,29 +162,31 @@ public class DimLayer { yPos = -1 * dh / 6; } - try { - if (!inTransaction) { - SurfaceControl.openTransaction(); - } - mDimSurface.setPosition(xPos, yPos); - mDimSurface.setSize(dw, dh); - mDimSurface.setLayer(layer); - } catch (RuntimeException e) { - Slog.w(TAG, "Failure setting size or layer", e); - } finally { - if (!inTransaction) { - SurfaceControl.closeTransaction(); - } - } + mDimSurface.setPosition(xPos, yPos); + mDimSurface.setSize(dw, dh); + mLastBounds.set(mBounds); - mLayer = layer; } - // Assumes that surface transactions are currently closed. - void setBounds(Rect bounds) { + /** + * @param bounds The new bounds to set + * @param inTransaction Whether the call is made within a surface transaction. + */ + void setBounds(Rect bounds, boolean inTransaction) { mBounds.set(bounds); if (isDimming() && !mLastBounds.equals(bounds)) { - adjustSurface(mLayer, false); + try { + if (!inTransaction) { + SurfaceControl.openTransaction(); + } + adjustBounds(); + } catch (RuntimeException e) { + Slog.w(TAG, "Failure setting size", e); + } finally { + if (!inTransaction) { + SurfaceControl.closeTransaction(); + } + } } } @@ -224,9 +225,10 @@ public class DimLayer { return; } - if (!mLastBounds.equals(mBounds) || mLayer != layer) { - adjustSurface(layer, true); + if (!mLastBounds.equals(mBounds)) { + adjustBounds(); } + setLayer(layer); long curTime = SystemClock.uptimeMillis(); final boolean animating = isAnimating(); diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java index 802cf4b..d313e3f 100644 --- a/services/core/java/com/android/server/wm/TaskStack.java +++ b/services/core/java/com/android/server/wm/TaskStack.java @@ -126,8 +126,8 @@ public class TaskStack { return false; } - mDimLayer.setBounds(bounds); - mAnimationBackgroundSurface.setBounds(bounds); + mDimLayer.setBounds(bounds, false); + mAnimationBackgroundSurface.setBounds(bounds, false); mBounds.set(bounds); return true; diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 7b389f5..109785c 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -508,6 +508,7 @@ public class WindowManagerService extends IWindowManager.Stub boolean mClientFreezingScreen = false; int mAppsFreezingScreen = 0; int mLastWindowForcedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; + int mLastKeyguardForcedOrientation = ActivityInfo.SCREEN_ORIENTATION_NOSENSOR; int mLayoutSeq = 0; @@ -3126,6 +3127,10 @@ public class WindowManagerService extends IWindowManager.Stub } winAnimator.mEnteringAnimation = true; if (toBeDisplayed) { + if ((win.mAttrs.softInputMode & SOFT_INPUT_MASK_ADJUST) + == SOFT_INPUT_ADJUST_RESIZE) { + win.mLayoutNeeded = true; + } if (win.isDrawnLw() && okToDisplay()) { winAnimator.applyEnterAnimationLocked(); } @@ -3714,43 +3719,70 @@ public class WindowManagerService extends IWindowManager.Stub } } - public int getOrientationFromWindowsLocked() { - if (mDisplayFrozen || mOpeningApps.size() > 0 || mClosingApps.size() > 0) { - // If the display is frozen, some activities may be in the middle - // of restarting, and thus have removed their old window. If the - // window has the flag to hide the lock screen, then the lock screen - // can re-appear and inflict its own orientation on us. Keep the - // orientation stable until this all settles down. - return mLastWindowForcedOrientation; - } - - // TODO(multidisplay): Change to the correct display. - final WindowList windows = getDefaultWindowListLocked(); - int pos = windows.size() - 1; - while (pos >= 0) { - WindowState win = windows.get(pos); - pos--; - if (win.mAppToken != null) { - // We hit an application window. so the orientation will be determined by the - // app window. No point in continuing further. - return (mLastWindowForcedOrientation=ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED); - } - if (!win.isVisibleLw() || !win.mPolicyVisibilityAfterAnim) { - continue; + public int getOrientationLocked() { + if (mDisplayFrozen) { + if (mLastWindowForcedOrientation != ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) { + if (DEBUG_ORIENTATION) Slog.v(TAG, "Display is frozen, return " + + mLastWindowForcedOrientation); + // If the display is frozen, some activities may be in the middle + // of restarting, and thus have removed their old window. If the + // window has the flag to hide the lock screen, then the lock screen + // can re-appear and inflict its own orientation on us. Keep the + // orientation stable until this all settles down. + return mLastWindowForcedOrientation; } - int req = win.mAttrs.screenOrientation; - if((req == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) || - (req == ActivityInfo.SCREEN_ORIENTATION_BEHIND)){ - continue; + } else { + // TODO(multidisplay): Change to the correct display. + final WindowList windows = getDefaultWindowListLocked(); + int pos = windows.size() - 1; + while (pos >= 0) { + WindowState win = windows.get(pos); + pos--; + if (win.mAppToken != null) { + // We hit an application window. so the orientation will be determined by the + // app window. No point in continuing further. + break; + } + if (!win.isVisibleLw() || !win.mPolicyVisibilityAfterAnim) { + continue; + } + int req = win.mAttrs.screenOrientation; + if((req == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) || + (req == ActivityInfo.SCREEN_ORIENTATION_BEHIND)){ + continue; + } + + if (DEBUG_ORIENTATION) Slog.v(TAG, win + " forcing orientation to " + req); + if (mPolicy.isKeyguardHostWindow(win.mAttrs)) { + mLastKeyguardForcedOrientation = req; + } + return (mLastWindowForcedOrientation = req); } + mLastWindowForcedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; - if (DEBUG_ORIENTATION) Slog.v(TAG, win + " forcing orientation to " + req); - return (mLastWindowForcedOrientation=req); + if (mPolicy.isKeyguardLocked()) { + // The screen is locked and no top system window is requesting an orientation. + // Return either the orientation of the show-when-locked app (if there is any) or + // the orientation of the keyguard. No point in searching from the rest of apps. + WindowState winShowWhenLocked = (WindowState) mPolicy.getWinShowWhenLockedLw(); + AppWindowToken appShowWhenLocked = winShowWhenLocked == null ? + null : winShowWhenLocked.mAppToken; + if (appShowWhenLocked != null) { + int req = appShowWhenLocked.requestedOrientation; + if (req == ActivityInfo.SCREEN_ORIENTATION_BEHIND) { + req = mLastKeyguardForcedOrientation; + } + if (DEBUG_ORIENTATION) Slog.v(TAG, "Done at " + appShowWhenLocked + + " -- show when locked, return " + req); + return req; + } + if (DEBUG_ORIENTATION) Slog.v(TAG, + "No one is requesting an orientation when the screen is locked"); + return mLastKeyguardForcedOrientation; + } } - return (mLastWindowForcedOrientation=ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED); - } - public int getOrientationFromAppTokensLocked() { + // Top system windows are not requesting an orientation. Start searching from apps. int lastOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; boolean findingBehind = false; boolean lastFullscreen = false; @@ -3822,8 +3854,11 @@ public class WindowManagerService extends IWindowManager.Stub findingBehind |= (or == ActivityInfo.SCREEN_ORIENTATION_BEHIND); } } - if (DEBUG_ORIENTATION) Slog.v(TAG, "No app is requesting an orientation"); - return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; + if (DEBUG_ORIENTATION) Slog.v(TAG, "No app is requesting an orientation, return " + + mForcedAppOrientation); + // The next app has not been requested to be visible, so we keep the current orientation + // to prevent freezing/unfreezing the display too early. + return mForcedAppOrientation; } @Override @@ -3903,11 +3938,7 @@ public class WindowManagerService extends IWindowManager.Stub boolean updateOrientationFromAppTokensLocked(boolean inTransaction) { long ident = Binder.clearCallingIdentity(); try { - int req = getOrientationFromWindowsLocked(); - if (req == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) { - req = getOrientationFromAppTokensLocked(); - } - + int req = getOrientationLocked(); if (req != mForcedAppOrientation) { mForcedAppOrientation = req; //send a message to Policy indicating orientation change to take diff --git a/tools/aapt/printapk.cpp b/tools/aapt/printapk.cpp deleted file mode 100644 index def6e2e..0000000 --- a/tools/aapt/printapk.cpp +++ /dev/null @@ -1,127 +0,0 @@ -#include <utils/ResourceTypes.h> -#include <utils/String8.h> -#include <utils/String16.h> -#include <zipfile/zipfile.h> -#include <stdio.h> -#include <fcntl.h> -#include <unistd.h> -#include <stdlib.h> - -using namespace android; - -static int -usage() -{ - fprintf(stderr, - "usage: apk APKFILE\n" - "\n" - "APKFILE an android packge file produced by aapt.\n" - ); - return 1; -} - - -int -main(int argc, char** argv) -{ - const char* filename; - int fd; - ssize_t amt; - off_t size; - void* buf; - zipfile_t zip; - zipentry_t entry; - void* cookie; - void* resfile; - int bufsize; - int err; - - if (argc != 2) { - return usage(); - } - - filename = argv[1]; - fd = open(filename, O_RDONLY); - if (fd == -1) { - fprintf(stderr, "apk: couldn't open file for read: %s\n", filename); - return 1; - } - - size = lseek(fd, 0, SEEK_END); - amt = lseek(fd, 0, SEEK_SET); - - if (size < 0 || amt < 0) { - fprintf(stderr, "apk: error determining file size: %s\n", filename); - return 1; - } - - buf = malloc(size); - if (buf == NULL) { - fprintf(stderr, "apk: file too big: %s\n", filename); - return 1; - } - - amt = read(fd, buf, size); - if (amt != size) { - fprintf(stderr, "apk: error reading file: %s\n", filename); - return 1; - } - - close(fd); - - zip = init_zipfile(buf, size); - if (zip == NULL) { - fprintf(stderr, "apk: file doesn't seem to be a zip file: %s\n", - filename); - return 1; - } - - printf("files:\n"); - cookie = NULL; - while ((entry = iterate_zipfile(zip, &cookie))) { - char* name = get_zipentry_name(entry); - printf(" %s\n", name); - free(name); - } - - entry = lookup_zipentry(zip, "resources.arsc"); - if (entry != NULL) { - size = get_zipentry_size(entry); - bufsize = size + (size / 1000) + 1; - resfile = malloc(bufsize); - - err = decompress_zipentry(entry, resfile, bufsize); - if (err != 0) { - fprintf(stderr, "apk: error decompressing resources.arsc"); - return 1; - } - - ResTable res(resfile, size, resfile); - res.print(); -#if 0 - size_t tableCount = res.getTableCount(); - printf("Tables: %d\n", (int)tableCount); - for (size_t tableIndex=0; tableIndex<tableCount; tableIndex++) { - const ResStringPool* strings = res.getTableStringBlock(tableIndex); - size_t stringCount = strings->size(); - for (size_t stringIndex=0; stringIndex<stringCount; stringIndex++) { - size_t len; - const char16_t* ch = strings->stringAt(stringIndex, &len); - String8 s(String16(ch, len)); - printf(" [%3d] %s\n", (int)stringIndex, s.string()); - } - } - - size_t basePackageCount = res.getBasePackageCount(); - printf("Base Packages: %d\n", (int)basePackageCount); - for (size_t bpIndex=0; bpIndex<basePackageCount; bpIndex++) { - const String16 ch = res.getBasePackageName(bpIndex); - String8 s = String8(ch); - printf(" [%3d] %s\n", (int)bpIndex, s.string()); - } -#endif - } - - - return 0; -} |
