Merge "Add a little more resource load tracing." into jb-mr2-dev
diff --git a/api/current.txt b/api/current.txt
index ceccfb7..1e9dc29 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -4195,6 +4195,7 @@
method public final boolean performGlobalAction(int);
method public void setOnAccessibilityEventListener(android.app.UiAutomation.OnAccessibilityEventListener);
method public boolean setRotation(int);
+ method public void setRunAsMonkey(boolean);
method public final void setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo);
method public android.graphics.Bitmap takeScreenshot();
method public void waitForIdle(long, long) throws java.util.concurrent.TimeoutException;
@@ -20555,19 +20556,37 @@
package android.security {
- public class AndroidKeyPairGeneratorSpec implements java.security.spec.AlgorithmParameterSpec {
+ public final class AndroidKeyPairGeneratorSpec implements java.security.spec.AlgorithmParameterSpec {
+ method public android.content.Context getContext();
+ method public java.util.Date getEndDate();
+ method public java.lang.String getKeystoreAlias();
+ method public java.math.BigInteger getSerialNumber();
+ method public java.util.Date getStartDate();
+ method public javax.security.auth.x500.X500Principal getSubjectDN();
+ method public boolean isEncryptionRequired();
}
- public static class AndroidKeyPairGeneratorSpec.Builder {
+ public static final class AndroidKeyPairGeneratorSpec.Builder {
ctor public AndroidKeyPairGeneratorSpec.Builder(android.content.Context);
method public android.security.AndroidKeyPairGeneratorSpec build();
method public android.security.AndroidKeyPairGeneratorSpec.Builder setAlias(java.lang.String);
+ method public android.security.AndroidKeyPairGeneratorSpec.Builder setEncryptionRequired();
method public android.security.AndroidKeyPairGeneratorSpec.Builder setEndDate(java.util.Date);
method public android.security.AndroidKeyPairGeneratorSpec.Builder setSerialNumber(java.math.BigInteger);
method public android.security.AndroidKeyPairGeneratorSpec.Builder setStartDate(java.util.Date);
method public android.security.AndroidKeyPairGeneratorSpec.Builder setSubject(javax.security.auth.x500.X500Principal);
}
+ public final class AndroidKeyStoreParameter implements java.security.KeyStore.ProtectionParameter {
+ method public boolean isEncryptionRequired();
+ }
+
+ public static final class AndroidKeyStoreParameter.Builder {
+ ctor public AndroidKeyStoreParameter.Builder(android.content.Context);
+ method public android.security.AndroidKeyStoreParameter build();
+ method public android.security.AndroidKeyStoreParameter.Builder setEncryptionRequired();
+ }
+
public final class KeyChain {
ctor public KeyChain();
method public static void choosePrivateKeyAlias(android.app.Activity, android.security.KeyChainAliasCallback, java.lang.String[], java.security.Principal[], java.lang.String, int, java.lang.String);
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index c99051b..98baa0e 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -1413,6 +1413,14 @@
return true;
}
+ case SET_USER_IS_MONKEY_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ final boolean monkey = (data.readInt() == 1);
+ setUserIsMonkey(monkey);
+ reply.writeNoException();
+ return true;
+ }
+
case FINISH_HEAVY_WEIGHT_APP_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
finishHeavyWeightApp();
@@ -3633,7 +3641,18 @@
reply.recycle();
return res;
}
-
+
+ public void setUserIsMonkey(boolean monkey) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeInt(monkey ? 1 : 0);
+ mRemote.transact(SET_USER_IS_MONKEY_TRANSACTION, data, reply, 0);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
+
public void finishHeavyWeightApp() throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index fa8839a..33a2770 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -285,7 +285,9 @@
int enterAnim, int exitAnim) throws RemoteException;
public boolean isUserAMonkey() throws RemoteException;
-
+
+ public void setUserIsMonkey(boolean monkey) throws RemoteException;
+
public void finishHeavyWeightApp() throws RemoteException;
public void setImmersive(IBinder token, boolean immersive) throws RemoteException;
@@ -635,4 +637,5 @@
int REPORT_TOP_ACTIVITY_EXTRAS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+162;
int GET_LAUNCHED_FROM_PACKAGE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+163;
int KILL_UID_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+164;
+ int SET_USER_IS_MONKEY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+165;
}
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index e0dfb25..a307a73 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -189,6 +189,10 @@
if (mPerfMetrics != null) {
results.putAll(mPerfMetrics);
}
+ if (mUiAutomation != null) {
+ mUiAutomation.disconnect();
+ mUiAutomation = null;
+ }
mThread.finishInstrumentation(resultCode, results);
}
@@ -1695,10 +1699,6 @@
startPerformanceSnapshot();
}
onStart();
- if (mUiAutomation != null) {
- mUiAutomation.disconnect();
- mUiAutomation = null;
- }
}
}
diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java
index d9799b6..05b79c1 100644
--- a/core/java/android/app/UiAutomation.java
+++ b/core/java/android/app/UiAutomation.java
@@ -619,6 +619,25 @@
return screenShot;
}
+ /**
+ * Sets whether this UiAutomation to run in a "monkey" mode. Applications can query whether
+ * they are executed in a "monkey" mode, i.e. run by a test framework, and avoid doing
+ * potentially undesirable actions such as calling 911 or posting on public forums etc.
+ *
+ * @param enable whether to run in a "monkey" mode or not. Default is not.
+ * @see {@link ActivityManager#isUserAMonkey()}
+ */
+ public void setRunAsMonkey(boolean enable) {
+ synchronized (mLock) {
+ throwIfNotConnectedLocked();
+ }
+ try {
+ ActivityManagerNative.getDefault().setUserIsMonkey(enable);
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error while setting run as monkey!", re);
+ }
+ }
+
private static float getDegreesForRotation(int value) {
switch (value) {
case Surface.ROTATION_90: {
diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java
index 37cbe04..30118f9 100644
--- a/core/java/android/hardware/SensorManager.java
+++ b/core/java/android/hardware/SensorManager.java
@@ -1387,14 +1387,14 @@
* @throws IllegalArgumentException when sensor is a trigger sensor.
*/
public boolean cancelTriggerSensor(TriggerEventListener listener, Sensor sensor) {
- return cancelTriggerSensorImpl(listener, sensor);
+ return cancelTriggerSensorImpl(listener, sensor, true);
}
/**
* @hide
*/
protected abstract boolean cancelTriggerSensorImpl(TriggerEventListener listener,
- Sensor sensor);
+ Sensor sensor, boolean disable);
private LegacySensorManager getLegacySensorManager() {
diff --git a/core/java/android/hardware/SystemSensorManager.java b/core/java/android/hardware/SystemSensorManager.java
index c6c999b..852cf4a 100644
--- a/core/java/android/hardware/SystemSensorManager.java
+++ b/core/java/android/hardware/SystemSensorManager.java
@@ -20,6 +20,7 @@
import android.os.Handler;
import android.os.Looper;
import android.os.MessageQueue;
+import android.util.Log;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
@@ -135,7 +136,7 @@
if (sensor == null) {
result = queue.removeAllSensors();
} else {
- result = queue.removeSensor(sensor);
+ result = queue.removeSensor(sensor, true);
}
if (result && !queue.hasSensors()) {
mSensorListeners.remove(listener);
@@ -170,7 +171,8 @@
/** @hide */
@Override
- protected boolean cancelTriggerSensorImpl(TriggerEventListener listener, Sensor sensor) {
+ protected boolean cancelTriggerSensorImpl(TriggerEventListener listener, Sensor sensor,
+ boolean disable) {
if (sensor != null && Sensor.getReportingMode(sensor) != Sensor.REPORTING_MODE_ONE_SHOT) {
return false;
}
@@ -181,7 +183,7 @@
if (sensor == null) {
result = queue.removeAllSensors();
} else {
- result = queue.removeSensor(sensor);
+ result = queue.removeSensor(sensor, disable);
}
if (result && !queue.hasSensors()) {
mTriggerListeners.remove(listener);
@@ -225,14 +227,17 @@
public boolean addSensor(Sensor sensor, int delay) {
// Check if already present.
- if (mActiveSensors.get(sensor.getHandle())) return false;
+ int handle = sensor.getHandle();
+ if (mActiveSensors.get(handle)) return false;
- if (enableSensor(sensor, delay) == 0) {
- mActiveSensors.put(sensor.getHandle(), true);
- addSensorEvent(sensor);
- return true;
+ // Get ready to receive events before calling enable.
+ mActiveSensors.put(handle, true);
+ addSensorEvent(sensor);
+ if (enableSensor(sensor, delay) != 0) {
+ removeSensor(sensor, false);
+ return false;
}
- return false;
+ return true;
}
public boolean removeAllSensors() {
@@ -252,10 +257,10 @@
return true;
}
- public boolean removeSensor(Sensor sensor) {
+ public boolean removeSensor(Sensor sensor, boolean disable) {
final int handle = sensor.getHandle();
if (mActiveSensors.get(handle)) {
- disableSensor(sensor);
+ if (disable) disableSensor(sensor);
mActiveSensors.put(sensor.getHandle(), false);
removeSensorEvent(sensor);
return true;
@@ -334,6 +339,10 @@
long timestamp) {
final Sensor sensor = sHandleToSensor.get(handle);
SensorEvent t = mSensorsEvents.get(handle);
+ if (t == null) {
+ Log.e(TAG, "Error: Sensor Event is null for Sensor: " + sensor);
+ return;
+ }
// Copy from the values array.
System.arraycopy(values, 0, t.values, 0, t.values.length);
t.timestamp = timestamp;
@@ -390,14 +399,19 @@
long timestamp) {
final Sensor sensor = sHandleToSensor.get(handle);
TriggerEvent t = mTriggerEvents.get(handle);
+ if (t == null) {
+ Log.e(TAG, "Error: Trigger Event is null for Sensor: " + sensor);
+ return;
+ }
// Copy from the values array.
System.arraycopy(values, 0, t.values, 0, t.values.length);
t.timestamp = timestamp;
t.sensor = sensor;
- // A trigger sensor should be auto disabled.
- mManager.cancelTriggerSensorImpl(mListener, sensor);
+ // A trigger sensor is auto disabled. So just clean up and don't call native
+ // disable.
+ mManager.cancelTriggerSensorImpl(mListener, sensor, false);
mListener.onTrigger(t);
}
diff --git a/core/java/android/security/IKeystoreService.java b/core/java/android/security/IKeystoreService.java
index e1cc90e..3d75dc8 100644
--- a/core/java/android/security/IKeystoreService.java
+++ b/core/java/android/security/IKeystoreService.java
@@ -78,7 +78,7 @@
return _result;
}
- public int insert(String name, byte[] item, int uid) throws RemoteException {
+ public int insert(String name, byte[] item, int uid, int flags) throws RemoteException {
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
int _result;
@@ -87,6 +87,7 @@
_data.writeString(name);
_data.writeByteArray(item);
_data.writeInt(uid);
+ _data.writeInt(flags);
mRemote.transact(Stub.TRANSACTION_insert, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
@@ -243,7 +244,7 @@
return _result;
}
- public int generate(String name, int uid) throws RemoteException {
+ public int generate(String name, int uid, int flags) throws RemoteException {
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
int _result;
@@ -251,6 +252,7 @@
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeString(name);
_data.writeInt(uid);
+ _data.writeInt(flags);
mRemote.transact(Stub.TRANSACTION_generate, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
@@ -261,7 +263,8 @@
return _result;
}
- public int import_key(String name, byte[] data, int uid) throws RemoteException {
+ public int import_key(String name, byte[] data, int uid, int flags)
+ throws RemoteException {
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
int _result;
@@ -270,6 +273,7 @@
_data.writeString(name);
_data.writeByteArray(data);
_data.writeInt(uid);
+ _data.writeInt(flags);
mRemote.transact(Stub.TRANSACTION_import, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
@@ -538,7 +542,7 @@
public byte[] get(String name) throws RemoteException;
- public int insert(String name, byte[] item, int uid) throws RemoteException;
+ public int insert(String name, byte[] item, int uid, int flags) throws RemoteException;
public int del(String name, int uid) throws RemoteException;
@@ -556,9 +560,9 @@
public int zero() throws RemoteException;
- public int generate(String name, int uid) throws RemoteException;
+ public int generate(String name, int uid, int flags) throws RemoteException;
- public int import_key(String name, byte[] data, int uid) throws RemoteException;
+ public int import_key(String name, byte[] data, int uid, int flags) throws RemoteException;
public byte[] sign(String name, byte[] data) throws RemoteException;
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 6da4b62..bf502dd 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -1464,10 +1464,13 @@
final float y = event.getY();
final int childrenCount = mChildrenCount;
if (childrenCount != 0) {
+ final boolean customChildOrder = isChildrenDrawingOrderEnabled();
final View[] children = mChildren;
HoverTarget lastHoverTarget = null;
for (int i = childrenCount - 1; i >= 0; i--) {
- final View child = children[i];
+ final int childIndex = customChildOrder
+ ? getChildDrawingOrder(childrenCount, i) : i;
+ final View child = children[childIndex];
if (!canViewReceivePointerEvents(child)
|| !isTransformedTouchPointInView(x, y, child, null)) {
continue;
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 094c901..f03c077 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1727,7 +1727,7 @@
if (didLayout) {
performLayout(lp, desiredWindowWidth, desiredWindowHeight);
- // By this point all views have been sized and positionned
+ // By this point all views have been sized and positioned
// We can compute the transparent area
if ((host.mPrivateFlags & View.PFLAG_REQUEST_TRANSPARENT_REGIONS) != 0) {
@@ -1745,6 +1745,7 @@
if (!mTransparentRegion.equals(mPreviousTransparentRegion)) {
mPreviousTransparentRegion.set(mTransparentRegion);
+ mFullRedrawNeeded = true;
// reconfigure window manager
try {
mWindowSession.setTransparentRegion(mWindow, mTransparentRegion);
@@ -4922,7 +4923,7 @@
public void handleDispatchDoneAnimating() {
if (mWindowsAnimating) {
mWindowsAnimating = false;
- if (!mDirty.isEmpty() || mIsAnimating) {
+ if (!mDirty.isEmpty() || mIsAnimating || mFullRedrawNeeded) {
scheduleTraversals();
}
}
diff --git a/keystore/java/android/security/AndroidKeyPairGenerator.java b/keystore/java/android/security/AndroidKeyPairGenerator.java
index c42001b..6975583 100644
--- a/keystore/java/android/security/AndroidKeyPairGenerator.java
+++ b/keystore/java/android/security/AndroidKeyPairGenerator.java
@@ -49,10 +49,7 @@
*
* {@hide}
*/
-@SuppressWarnings("deprecation")
public class AndroidKeyPairGenerator extends KeyPairGeneratorSpi {
- public static final String NAME = "AndroidKeyPairGenerator";
-
private android.security.KeyStore mKeyStore;
private AndroidKeyPairGeneratorSpec mSpec;
@@ -79,12 +76,21 @@
"Must call initialize with an AndroidKeyPairGeneratorSpec first");
}
+ if (((mSpec.getFlags() & KeyStore.FLAG_ENCRYPTED) != 0)
+ && (mKeyStore.state() != KeyStore.State.UNLOCKED)) {
+ throw new IllegalStateException(
+ "Android keystore must be in initialized and unlocked state "
+ + "if encryption is required");
+ }
+
final String alias = mSpec.getKeystoreAlias();
Credentials.deleteAllTypesForAlias(mKeyStore, alias);
final String privateKeyAlias = Credentials.USER_PRIVATE_KEY + alias;
- mKeyStore.generate(privateKeyAlias);
+ if (!mKeyStore.generate(privateKeyAlias, KeyStore.UID_SELF, mSpec.getFlags())) {
+ throw new IllegalStateException("could not generate key in keystore");
+ }
final PrivateKey privKey;
final OpenSSLEngine engine = OpenSSLEngine.getInstance("keystore");
@@ -131,7 +137,8 @@
throw new IllegalStateException("Can't get encoding of certificate", e);
}
- if (!mKeyStore.put(Credentials.USER_CERTIFICATE + alias, certBytes)) {
+ if (!mKeyStore.put(Credentials.USER_CERTIFICATE + alias, certBytes, KeyStore.UID_SELF,
+ mSpec.getFlags())) {
Credentials.deleteAllTypesForAlias(mKeyStore, alias);
throw new IllegalStateException("Can't store certificate in AndroidKeyStore");
}
diff --git a/keystore/java/android/security/AndroidKeyPairGeneratorSpec.java b/keystore/java/android/security/AndroidKeyPairGeneratorSpec.java
index 18225a5..b126f03 100644
--- a/keystore/java/android/security/AndroidKeyPairGeneratorSpec.java
+++ b/keystore/java/android/security/AndroidKeyPairGeneratorSpec.java
@@ -32,10 +32,9 @@
* {@code KeyPairGenerator} that works with <a href="{@docRoot}
* guide/topics/security/keystore.html">Android KeyStore facility</a>. The
* Android KeyStore facility is accessed through a
- * {@link java.security.KeyPairGenerator} API using the
- * {@code AndroidKeyPairGenerator} 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.
+ * {@link java.security.KeyPairGenerator} 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)}
@@ -47,10 +46,10 @@
* Distinguished Name along with the other parameters specified with the
* {@link Builder}.
* <p>
- * The self-signed certificate may be replaced at a later time by a certificate
- * signed by a real Certificate Authority.
+ * The self-signed X.509 certificate may be replaced at a later time by a
+ * certificate signed by a real Certificate Authority.
*/
-public class AndroidKeyPairGeneratorSpec implements AlgorithmParameterSpec {
+public final class AndroidKeyPairGeneratorSpec implements AlgorithmParameterSpec {
private final String mKeystoreAlias;
private final Context mContext;
@@ -63,6 +62,8 @@
private final Date mEndDate;
+ private final int mFlags;
+
/**
* Parameter specification for the "{@code AndroidKeyPairGenerator}"
* instance of the {@link java.security.KeyPairGenerator} API. The
@@ -93,7 +94,8 @@
* @hide should be built with AndroidKeyPairGeneratorSpecBuilder
*/
public AndroidKeyPairGeneratorSpec(Context context, String keyStoreAlias,
- X500Principal subjectDN, BigInteger serialNumber, Date startDate, Date endDate) {
+ X500Principal subjectDN, BigInteger serialNumber, Date startDate, Date endDate,
+ int flags) {
if (context == null) {
throw new IllegalArgumentException("context == null");
} else if (TextUtils.isEmpty(keyStoreAlias)) {
@@ -116,48 +118,69 @@
mSerialNumber = serialNumber;
mStartDate = startDate;
mEndDate = endDate;
+ mFlags = flags;
}
/**
- * @hide
+ * Returns the alias that will be used in the {@code java.security.KeyStore}
+ * in conjunction with the {@code AndroidKeyStore}.
*/
- String getKeystoreAlias() {
+ public String getKeystoreAlias() {
return mKeystoreAlias;
}
/**
- * @hide
+ * Gets the Android context used for operations with this instance.
*/
- Context getContext() {
+ public Context getContext() {
return mContext;
}
/**
- * @hide
+ * Gets the subject distinguished name to be used on the X.509 certificate
+ * that will be put in the {@link java.security.KeyStore}.
*/
- X500Principal getSubjectDN() {
+ public X500Principal getSubjectDN() {
return mSubjectDN;
}
/**
- * @hide
+ * Gets the serial number to be used on the X.509 certificate that will be
+ * put in the {@link java.security.KeyStore}.
*/
- BigInteger getSerialNumber() {
+ public BigInteger getSerialNumber() {
return mSerialNumber;
}
/**
- * @hide
+ * Gets the start date to be used on the X.509 certificate that will be put
+ * in the {@link java.security.KeyStore}.
*/
- Date getStartDate() {
+ public Date getStartDate() {
return mStartDate;
}
/**
+ * Gets the end date to be used on the X.509 certificate that will be put in
+ * the {@link java.security.KeyStore}.
+ */
+ public Date getEndDate() {
+ return mEndDate;
+ }
+
+ /**
* @hide
*/
- Date getEndDate() {
- return mEndDate;
+ int getFlags() {
+ return mFlags;
+ }
+
+ /**
+ * Returns {@code true} if this parameter will require generated keys to be
+ * encrypted in the {@link java.security.KeyStore}.
+ */
+ public boolean isEncryptionRequired() {
+ return (mFlags & KeyStore.FLAG_ENCRYPTED) != 0;
}
/**
@@ -175,16 +198,17 @@
* Calendar end = new Calendar();
* end.add(1, Calendar.YEAR);
*
- * AndroidKeyPairGeneratorSpec spec = new AndroidKeyPairGeneratorSpec.Builder(mContext)
- * .setAlias("myKey")
- * .setSubject(new X500Principal("CN=myKey"))
- * .setSerial(BigInteger.valueOf(1337))
- * .setStartDate(start.getTime())
- * .setEndDate(end.getTime())
- * .build();
+ * AndroidKeyPairGeneratorSpec spec =
+ * new AndroidKeyPairGeneratorSpec.Builder(mContext)
+ * .setAlias("myKey")
+ * .setSubject(new X500Principal("CN=myKey"))
+ * .setSerial(BigInteger.valueOf(1337))
+ * .setStartDate(start.getTime())
+ * .setEndDate(end.getTime())
+ * .build();
* </pre>
*/
- public static class Builder {
+ public final static class Builder {
private final Context mContext;
private String mKeystoreAlias;
@@ -197,6 +221,14 @@
private Date mEndDate;
+ private int mFlags;
+
+ /**
+ * 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");
@@ -266,6 +298,17 @@
}
/**
+ * 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() {
+ mFlags |= KeyStore.FLAG_ENCRYPTED;
+ return this;
+ }
+
+ /**
* Builds the instance of the {@code AndroidKeyPairGeneratorSpec}.
*
* @throws IllegalArgumentException if a required field is missing
@@ -273,7 +316,7 @@
*/
public AndroidKeyPairGeneratorSpec build() {
return new AndroidKeyPairGeneratorSpec(mContext, mKeystoreAlias, mSubjectDN,
- mSerialNumber, mStartDate, mEndDate);
+ mSerialNumber, mStartDate, mEndDate, mFlags);
}
}
}
diff --git a/keystore/java/android/security/AndroidKeyStore.java b/keystore/java/android/security/AndroidKeyStore.java
index 8a9826b..dcc9516 100644
--- a/keystore/java/android/security/AndroidKeyStore.java
+++ b/keystore/java/android/security/AndroidKeyStore.java
@@ -27,6 +27,10 @@
import java.io.OutputStream;
import java.security.InvalidKeyException;
import java.security.Key;
+import java.security.KeyStore.Entry;
+import java.security.KeyStore.PrivateKeyEntry;
+import java.security.KeyStore.ProtectionParameter;
+import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.KeyStoreSpi;
import java.security.NoSuchAlgorithmException;
@@ -198,14 +202,14 @@
}
if (key instanceof PrivateKey) {
- setPrivateKeyEntry(alias, (PrivateKey) key, chain);
+ setPrivateKeyEntry(alias, (PrivateKey) key, chain, null);
} else {
throw new KeyStoreException("Only PrivateKeys are supported");
}
}
- private void setPrivateKeyEntry(String alias, PrivateKey key, Certificate[] chain)
- throws KeyStoreException {
+ private void setPrivateKeyEntry(String alias, PrivateKey key, Certificate[] chain,
+ AndroidKeyStoreParameter params) throws KeyStoreException {
byte[] keyBytes = null;
final String pkeyAlias;
@@ -317,15 +321,20 @@
Credentials.deleteCertificateTypesForAlias(mKeyStore, alias);
}
+ final int flags = (params == null) ? 0 : params.getFlags();
+
if (shouldReplacePrivateKey
- && !mKeyStore.importKey(Credentials.USER_PRIVATE_KEY + alias, keyBytes)) {
+ && !mKeyStore.importKey(Credentials.USER_PRIVATE_KEY + alias, keyBytes,
+ android.security.KeyStore.UID_SELF, flags)) {
Credentials.deleteAllTypesForAlias(mKeyStore, alias);
throw new KeyStoreException("Couldn't put private key in keystore");
- } else if (!mKeyStore.put(Credentials.USER_CERTIFICATE + alias, userCertBytes)) {
+ } else if (!mKeyStore.put(Credentials.USER_CERTIFICATE + alias, userCertBytes,
+ android.security.KeyStore.UID_SELF, flags)) {
Credentials.deleteAllTypesForAlias(mKeyStore, alias);
throw new KeyStoreException("Couldn't put certificate #1 in keystore");
} else if (chainBytes != null
- && !mKeyStore.put(Credentials.CA_CERTIFICATE + alias, chainBytes)) {
+ && !mKeyStore.put(Credentials.CA_CERTIFICATE + alias, chainBytes,
+ android.security.KeyStore.UID_SELF, flags)) {
Credentials.deleteAllTypesForAlias(mKeyStore, alias);
throw new KeyStoreException("Couldn't put certificate chain in keystore");
}
@@ -355,7 +364,8 @@
throw new KeyStoreException(e);
}
- if (!mKeyStore.put(Credentials.CA_CERTIFICATE + alias, encoded)) {
+ if (!mKeyStore.put(Credentials.CA_CERTIFICATE + alias, encoded,
+ android.security.KeyStore.UID_SELF, android.security.KeyStore.FLAG_NONE)) {
throw new KeyStoreException("Couldn't insert certificate; is KeyStore initialized?");
}
}
@@ -517,4 +527,37 @@
mKeyStore = android.security.KeyStore.getInstance();
}
+ @Override
+ public void engineSetEntry(String alias, Entry entry, ProtectionParameter param)
+ throws KeyStoreException {
+ if (entry == null) {
+ throw new KeyStoreException("entry == null");
+ }
+
+ if (engineContainsAlias(alias)) {
+ engineDeleteEntry(alias);
+ }
+
+ if (entry instanceof KeyStore.TrustedCertificateEntry) {
+ KeyStore.TrustedCertificateEntry trE = (KeyStore.TrustedCertificateEntry) entry;
+ engineSetCertificateEntry(alias, trE.getTrustedCertificate());
+ return;
+ }
+
+ if (param != null && !(param instanceof AndroidKeyStoreParameter)) {
+ throw new KeyStoreException("protParam should be AndroidKeyStoreParameter; was: "
+ + param.getClass().getName());
+ }
+
+ if (entry instanceof PrivateKeyEntry) {
+ PrivateKeyEntry prE = (PrivateKeyEntry) entry;
+ setPrivateKeyEntry(alias, prE.getPrivateKey(), prE.getCertificateChain(),
+ (AndroidKeyStoreParameter) param);
+ return;
+ }
+
+ throw new KeyStoreException(
+ "Entry must be a PrivateKeyEntry or TrustedCertificateEntry; was " + entry);
+ }
+
}
diff --git a/keystore/java/android/security/AndroidKeyStoreParameter.java b/keystore/java/android/security/AndroidKeyStoreParameter.java
new file mode 100644
index 0000000..44f57c4
--- /dev/null
+++ b/keystore/java/android/security/AndroidKeyStoreParameter.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security;
+
+import android.content.Context;
+import android.security.AndroidKeyPairGeneratorSpec.Builder;
+
+import java.security.KeyPairGenerator;
+import java.security.PrivateKey;
+import java.security.KeyStore.ProtectionParameter;
+import java.security.cert.Certificate;
+
+/**
+ * This provides the optional parameters that can be specified for
+ * {@code KeyStore} entries that work with <a href="{@docRoot}
+ * guide/topics/security/keystore.html">Android KeyStore facility</a>. The
+ * Android KeyStore facility is accessed through a
+ * {@link java.security.KeyStore} 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>
+ * Any entries placed in the {@code KeyStore} may be retrieved later. Note that
+ * there is only one logical instance of the {@code KeyStore} per application
+ * UID so apps using the {@code sharedUid} facility will also share a
+ * {@code KeyStore}.
+ * <p>
+ * Keys may be generated using the {@link KeyPairGenerator} facility with a
+ * {@link AndroidKeyPairGeneratorSpec} to specify the entry's {@code alias}. A
+ * self-signed X.509 certificate will be attached to generated entries, but that
+ * may be replaced at a later time by a certificate signed by a real Certificate
+ * Authority.
+ */
+public final class AndroidKeyStoreParameter implements ProtectionParameter {
+ private int mFlags;
+
+ private AndroidKeyStoreParameter(int flags) {
+ mFlags = flags;
+ }
+
+ /**
+ * @hide
+ */
+ public int getFlags() {
+ return mFlags;
+ }
+
+ /**
+ * Returns {@code true} if this parameter requires entries to be encrypted
+ * on the disk.
+ */
+ public boolean isEncryptionRequired() {
+ return (mFlags & KeyStore.FLAG_ENCRYPTED) != 0;
+ }
+
+ /**
+ * Builder class for {@link AndroidKeyStoreParameter} objects.
+ * <p>
+ * This will build protection parameters for use with the <a
+ * href="{@docRoot} guide/topics/security/keystore.html">Android KeyStore
+ * facility</a>.
+ * <p>
+ * This can be used to require that KeyStore entries be stored encrypted.
+ * <p>
+ * Example:
+ *
+ * <pre class="prettyprint">
+ * AndroidKeyStoreParameter params =
+ * new AndroidKeyStoreParameter.Builder(mContext).setEncryptionRequired().build();
+ * </pre>
+ */
+ public final static class Builder {
+ private int mFlags;
+
+ /**
+ * 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");
+ }
+
+ // Context is currently not used, but will be in the future.
+ }
+
+ /**
+ * 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() {
+ mFlags |= KeyStore.FLAG_ENCRYPTED;
+ return this;
+ }
+
+ /**
+ * Builds the instance of the {@code AndroidKeyPairGeneratorSpec}.
+ *
+ * @throws IllegalArgumentException if a required field is missing
+ * @return built instance of {@code AndroidKeyPairGeneratorSpec}
+ */
+ public AndroidKeyStoreParameter build() {
+ return new AndroidKeyStoreParameter(mFlags);
+ }
+ }
+}
diff --git a/keystore/java/android/security/AndroidKeyStoreProvider.java b/keystore/java/android/security/AndroidKeyStoreProvider.java
index 40d7e1a..8ca301e 100644
--- a/keystore/java/android/security/AndroidKeyStoreProvider.java
+++ b/keystore/java/android/security/AndroidKeyStoreProvider.java
@@ -33,7 +33,6 @@
put("KeyStore." + AndroidKeyStore.NAME, AndroidKeyStore.class.getName());
// java.security.KeyPairGenerator
- put("KeyPairGenerator." + AndroidKeyPairGenerator.NAME,
- AndroidKeyPairGenerator.class.getName());
+ put("KeyPairGenerator." + AndroidKeyStore.NAME, AndroidKeyPairGenerator.class.getName());
}
}
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index 852f0bb..45385ee 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -40,6 +40,13 @@
public static final int UNDEFINED_ACTION = 9;
public static final int WRONG_PASSWORD = 10;
+ // Used for UID field to indicate the calling UID.
+ public static final int UID_SELF = -1;
+
+ // Flags for "put" "import" and "generate"
+ public static final int FLAG_NONE = 0;
+ public static final int FLAG_ENCRYPTED = 1;
+
// States
public enum State { UNLOCKED, LOCKED, UNINITIALIZED };
@@ -87,17 +94,21 @@
}
}
- public boolean put(String key, byte[] value, int uid) {
+ public boolean put(String key, byte[] value, int uid, int flags) {
try {
- return mBinder.insert(key, value, uid) == NO_ERROR;
+ return mBinder.insert(key, value, uid, flags) == NO_ERROR;
} catch (RemoteException e) {
Log.w(TAG, "Cannot connect to keystore", e);
return false;
}
}
+ public boolean put(String key, byte[] value, int uid) {
+ return put(key, value, uid, FLAG_ENCRYPTED);
+ }
+
public boolean put(String key, byte[] value) {
- return put(key, value, -1);
+ return put(key, value, UID_SELF);
}
public boolean delete(String key, int uid) {
@@ -110,7 +121,7 @@
}
public boolean delete(String key) {
- return delete(key, -1);
+ return delete(key, UID_SELF);
}
public boolean contains(String key, int uid) {
@@ -123,7 +134,7 @@
}
public boolean contains(String key) {
- return contains(key, -1);
+ return contains(key, UID_SELF);
}
public String[] saw(String prefix, int uid) {
@@ -136,7 +147,7 @@
}
public String[] saw(String prefix) {
- return saw(prefix, -1);
+ return saw(prefix, UID_SELF);
}
public boolean reset() {
@@ -185,30 +196,38 @@
}
}
- public boolean generate(String key, int uid) {
+ public boolean generate(String key, int uid, int flags) {
try {
- return mBinder.generate(key, uid) == NO_ERROR;
+ return mBinder.generate(key, uid, flags) == NO_ERROR;
} catch (RemoteException e) {
Log.w(TAG, "Cannot connect to keystore", e);
return false;
}
}
+ public boolean generate(String key, int uid) {
+ return generate(key, uid, FLAG_ENCRYPTED);
+ }
+
public boolean generate(String key) {
- return generate(key, -1);
+ return generate(key, UID_SELF);
}
- public boolean importKey(String keyName, byte[] key, int uid) {
+ public boolean importKey(String keyName, byte[] key, int uid, int flags) {
try {
- return mBinder.import_key(keyName, key, uid) == NO_ERROR;
+ return mBinder.import_key(keyName, key, uid, flags) == NO_ERROR;
} catch (RemoteException e) {
Log.w(TAG, "Cannot connect to keystore", e);
return false;
}
}
+ public boolean importKey(String keyName, byte[] key, int uid) {
+ return importKey(keyName, key, uid, FLAG_ENCRYPTED);
+ }
+
public boolean importKey(String keyName, byte[] key) {
- return importKey(keyName, key, -1);
+ return importKey(keyName, key, UID_SELF);
}
public byte[] getPubkey(String key) {
@@ -230,7 +249,7 @@
}
public boolean delKey(String key) {
- return delKey(key, -1);
+ return delKey(key, UID_SELF);
}
public byte[] sign(String key, byte[] data) {
diff --git a/keystore/tests/src/android/security/AndroidKeyPairGeneratorSpecTest.java b/keystore/tests/src/android/security/AndroidKeyPairGeneratorSpecTest.java
index 3d275cd..5d4ab9c 100644
--- a/keystore/tests/src/android/security/AndroidKeyPairGeneratorSpecTest.java
+++ b/keystore/tests/src/android/security/AndroidKeyPairGeneratorSpecTest.java
@@ -39,8 +39,9 @@
private static final Date NOW_PLUS_10_YEARS = new Date(NOW.getYear() + 10, 0, 1);
public void testConstructor_Success() throws Exception {
- AndroidKeyPairGeneratorSpec spec = new AndroidKeyPairGeneratorSpec(getContext(),
- TEST_ALIAS_1, TEST_DN_1, SERIAL_1, NOW, NOW_PLUS_10_YEARS);
+ AndroidKeyPairGeneratorSpec spec =
+ new AndroidKeyPairGeneratorSpec(getContext(), TEST_ALIAS_1, TEST_DN_1, SERIAL_1,
+ NOW, NOW_PLUS_10_YEARS, 0);
assertEquals("Context should be the one specified", getContext(), spec.getContext());
@@ -60,6 +61,7 @@
.setSerialNumber(SERIAL_1)
.setStartDate(NOW)
.setEndDate(NOW_PLUS_10_YEARS)
+ .setEncryptionRequired()
.build();
assertEquals("Context should be the one specified", getContext(), spec.getContext());
@@ -71,12 +73,14 @@
assertEquals("startDate should be the one specified", NOW, spec.getStartDate());
assertEquals("endDate should be the one specified", NOW_PLUS_10_YEARS, spec.getEndDate());
+
+ assertEquals("encryption flag should be on", KeyStore.FLAG_ENCRYPTED, spec.getFlags());
}
public void testConstructor_NullContext_Failure() throws Exception {
try {
new AndroidKeyPairGeneratorSpec(null, TEST_ALIAS_1, TEST_DN_1, SERIAL_1, NOW,
- NOW_PLUS_10_YEARS);
+ NOW_PLUS_10_YEARS, 0);
fail("Should throw IllegalArgumentException when context is null");
} catch (IllegalArgumentException success) {
}
@@ -85,7 +89,7 @@
public void testConstructor_NullKeystoreAlias_Failure() throws Exception {
try {
new AndroidKeyPairGeneratorSpec(getContext(), null, TEST_DN_1, SERIAL_1, NOW,
- NOW_PLUS_10_YEARS);
+ NOW_PLUS_10_YEARS, 0);
fail("Should throw IllegalArgumentException when keystoreAlias is null");
} catch (IllegalArgumentException success) {
}
@@ -94,7 +98,7 @@
public void testConstructor_NullSubjectDN_Failure() throws Exception {
try {
new AndroidKeyPairGeneratorSpec(getContext(), TEST_ALIAS_1, null, SERIAL_1, NOW,
- NOW_PLUS_10_YEARS);
+ NOW_PLUS_10_YEARS, 0);
fail("Should throw IllegalArgumentException when subjectDN is null");
} catch (IllegalArgumentException success) {
}
@@ -103,7 +107,7 @@
public void testConstructor_NullSerial_Failure() throws Exception {
try {
new AndroidKeyPairGeneratorSpec(getContext(), TEST_ALIAS_1, TEST_DN_1, null, NOW,
- NOW_PLUS_10_YEARS);
+ NOW_PLUS_10_YEARS, 0);
fail("Should throw IllegalArgumentException when startDate is null");
} catch (IllegalArgumentException success) {
}
@@ -112,7 +116,7 @@
public void testConstructor_NullStartDate_Failure() throws Exception {
try {
new AndroidKeyPairGeneratorSpec(getContext(), TEST_ALIAS_1, TEST_DN_1, SERIAL_1, null,
- NOW_PLUS_10_YEARS);
+ NOW_PLUS_10_YEARS, 0);
fail("Should throw IllegalArgumentException when startDate is null");
} catch (IllegalArgumentException success) {
}
@@ -121,7 +125,7 @@
public void testConstructor_NullEndDate_Failure() throws Exception {
try {
new AndroidKeyPairGeneratorSpec(getContext(), TEST_ALIAS_1, TEST_DN_1, SERIAL_1, NOW,
- null);
+ null, 0);
fail("Should throw IllegalArgumentException when keystoreAlias is null");
} catch (IllegalArgumentException success) {
}
@@ -130,7 +134,7 @@
public void testConstructor_EndBeforeStart_Failure() throws Exception {
try {
new AndroidKeyPairGeneratorSpec(getContext(), TEST_ALIAS_1, TEST_DN_1, SERIAL_1,
- NOW_PLUS_10_YEARS, NOW);
+ NOW_PLUS_10_YEARS, NOW, 0);
fail("Should throw IllegalArgumentException when end is before start");
} catch (IllegalArgumentException success) {
}
diff --git a/keystore/tests/src/android/security/AndroidKeyPairGeneratorTest.java b/keystore/tests/src/android/security/AndroidKeyPairGeneratorTest.java
index 69007c4..c5cf514 100644
--- a/keystore/tests/src/android/security/AndroidKeyPairGeneratorTest.java
+++ b/keystore/tests/src/android/security/AndroidKeyPairGeneratorTest.java
@@ -27,6 +27,7 @@
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
+import java.text.SimpleDateFormat;
import java.util.Date;
import javax.security.auth.x500.X500Principal;
@@ -64,22 +65,34 @@
assertFalse(mAndroidKeyStore.isUnlocked());
+ mGenerator = java.security.KeyPairGenerator.getInstance("AndroidKeyStore");
+ }
+
+ private void setupPassword() {
assertTrue(mAndroidKeyStore.password("1111"));
assertTrue(mAndroidKeyStore.isUnlocked());
String[] aliases = mAndroidKeyStore.saw("");
assertNotNull(aliases);
assertEquals(0, aliases.length);
-
- mGenerator = java.security.KeyPairGenerator.getInstance(AndroidKeyPairGenerator.NAME);
}
- public void testKeyPairGenerator_Initialize_Params_Success() throws Exception {
- mGenerator.initialize(new AndroidKeyPairGeneratorSpec(getContext(), TEST_ALIAS_1,
- TEST_DN_1, TEST_SERIAL_1, NOW, NOW_PLUS_10_YEARS));
+ public void testKeyPairGenerator_Initialize_Params_Encrypted_Success() throws Exception {
+ setupPassword();
+
+ mGenerator.initialize(new AndroidKeyPairGeneratorSpec.Builder(getContext())
+ .setAlias(TEST_ALIAS_1)
+ .setSubject(TEST_DN_1)
+ .setSerialNumber(TEST_SERIAL_1)
+ .setStartDate(NOW)
+ .setEndDate(NOW_PLUS_10_YEARS)
+ .setEncryptionRequired()
+ .build());
}
- public void testKeyPairGenerator_Initialize_KeySize_Failure() throws Exception {
+ public void testKeyPairGenerator_Initialize_KeySize_Encrypted_Failure() throws Exception {
+ setupPassword();
+
try {
mGenerator.initialize(1024);
fail("KeyPairGenerator should not support setting the key size");
@@ -87,7 +100,10 @@
}
}
- public void testKeyPairGenerator_Initialize_KeySizeAndSecureRandom_Failure() throws Exception {
+ public void testKeyPairGenerator_Initialize_KeySizeAndSecureRandom_Encrypted_Failure()
+ throws Exception {
+ setupPassword();
+
try {
mGenerator.initialize(1024, new SecureRandom());
fail("KeyPairGenerator should not support setting the key size");
@@ -95,14 +111,48 @@
}
}
- public void testKeyPairGenerator_Initialize_ParamsAndSecureRandom_Failure() throws Exception {
- mGenerator.initialize(new AndroidKeyPairGeneratorSpec(getContext(), TEST_ALIAS_1,
- TEST_DN_1, TEST_SERIAL_1, NOW, NOW_PLUS_10_YEARS), new SecureRandom());
+ public void testKeyPairGenerator_Initialize_ParamsAndSecureRandom_Encrypted_Failure()
+ throws Exception {
+ setupPassword();
+
+ mGenerator.initialize(
+ new AndroidKeyPairGeneratorSpec.Builder(getContext())
+ .setAlias(TEST_ALIAS_1)
+ .setSubject(TEST_DN_1)
+ .setSerialNumber(TEST_SERIAL_1)
+ .setStartDate(NOW)
+ .setEndDate(NOW_PLUS_10_YEARS)
+ .setEncryptionRequired()
+ .build(),
+ new SecureRandom());
}
- public void testKeyPairGenerator_GenerateKeyPair_Success() throws Exception {
- mGenerator.initialize(new AndroidKeyPairGeneratorSpec(getContext(), TEST_ALIAS_1,
- TEST_DN_1, TEST_SERIAL_1, NOW, NOW_PLUS_10_YEARS));
+ public void testKeyPairGenerator_GenerateKeyPair_Encrypted_Success() throws Exception {
+ setupPassword();
+
+ mGenerator.initialize(new AndroidKeyPairGeneratorSpec.Builder(getContext())
+ .setAlias(TEST_ALIAS_1)
+ .setSubject(TEST_DN_1)
+ .setSerialNumber(TEST_SERIAL_1)
+ .setStartDate(NOW)
+ .setEndDate(NOW_PLUS_10_YEARS)
+ .setEncryptionRequired()
+ .build());
+
+ final KeyPair pair = mGenerator.generateKeyPair();
+ assertNotNull("The KeyPair returned should not be null", pair);
+
+ assertKeyPairCorrect(pair, TEST_ALIAS_1, TEST_DN_1, TEST_SERIAL_1, NOW, NOW_PLUS_10_YEARS);
+ }
+
+ public void testKeyPairGenerator_GenerateKeyPair_Unencrypted_Success() throws Exception {
+ mGenerator.initialize(new AndroidKeyPairGeneratorSpec.Builder(getContext())
+ .setAlias(TEST_ALIAS_1)
+ .setSubject(TEST_DN_1)
+ .setSerialNumber(TEST_SERIAL_1)
+ .setStartDate(NOW)
+ .setEndDate(NOW_PLUS_10_YEARS)
+ .build());
final KeyPair pair = mGenerator.generateKeyPair();
assertNotNull("The KeyPair returned should not be null", pair);
@@ -113,8 +163,13 @@
public void testKeyPairGenerator_GenerateKeyPair_Replaced_Success() throws Exception {
// Generate the first key
{
- mGenerator.initialize(new AndroidKeyPairGeneratorSpec(getContext(), TEST_ALIAS_1,
- TEST_DN_1, TEST_SERIAL_1, NOW, NOW_PLUS_10_YEARS));
+ mGenerator.initialize(new AndroidKeyPairGeneratorSpec.Builder(getContext())
+ .setAlias(TEST_ALIAS_1)
+ .setSubject(TEST_DN_1)
+ .setSerialNumber(TEST_SERIAL_1)
+ .setStartDate(NOW)
+ .setEndDate(NOW_PLUS_10_YEARS)
+ .build());
final KeyPair pair1 = mGenerator.generateKeyPair();
assertNotNull("The KeyPair returned should not be null", pair1);
assertKeyPairCorrect(pair1, TEST_ALIAS_1, TEST_DN_1, TEST_SERIAL_1, NOW,
@@ -123,8 +178,13 @@
// Replace the original key
{
- mGenerator.initialize(new AndroidKeyPairGeneratorSpec(getContext(), TEST_ALIAS_2,
- TEST_DN_2, TEST_SERIAL_2, NOW, NOW_PLUS_10_YEARS));
+ mGenerator.initialize(new AndroidKeyPairGeneratorSpec.Builder(getContext())
+ .setAlias(TEST_ALIAS_2)
+ .setSubject(TEST_DN_2)
+ .setSerialNumber(TEST_SERIAL_2)
+ .setStartDate(NOW)
+ .setEndDate(NOW_PLUS_10_YEARS)
+ .build());
final KeyPair pair2 = mGenerator.generateKeyPair();
assertNotNull("The KeyPair returned should not be null", pair2);
assertKeyPairCorrect(pair2, TEST_ALIAS_2, TEST_DN_2, TEST_SERIAL_2, NOW,
@@ -132,6 +192,49 @@
}
}
+ public void testKeyPairGenerator_GenerateKeyPair_Replaced_UnencryptedToEncrypted_Success()
+ throws Exception {
+ // Generate the first key
+ {
+ mGenerator.initialize(new AndroidKeyPairGeneratorSpec.Builder(getContext())
+ .setAlias(TEST_ALIAS_1)
+ .setSubject(TEST_DN_1)
+ .setSerialNumber(TEST_SERIAL_1)
+ .setStartDate(NOW)
+ .setEndDate(NOW_PLUS_10_YEARS)
+ .build());
+ final KeyPair pair1 = mGenerator.generateKeyPair();
+ assertNotNull("The KeyPair returned should not be null", pair1);
+ assertKeyPairCorrect(pair1, TEST_ALIAS_1, TEST_DN_1, TEST_SERIAL_1, NOW,
+ NOW_PLUS_10_YEARS);
+ }
+
+ // Attempt to replace previous key
+ {
+ mGenerator.initialize(new AndroidKeyPairGeneratorSpec.Builder(getContext())
+ .setAlias(TEST_ALIAS_1)
+ .setSubject(TEST_DN_2)
+ .setSerialNumber(TEST_SERIAL_2)
+ .setStartDate(NOW)
+ .setEndDate(NOW_PLUS_10_YEARS)
+ .setEncryptionRequired()
+ .build());
+ try {
+ mGenerator.generateKeyPair();
+ fail("Should not be able to generate encrypted key while not initialized");
+ } catch (IllegalStateException expected) {
+ }
+
+ assertTrue(mAndroidKeyStore.password("1111"));
+ assertTrue(mAndroidKeyStore.isUnlocked());
+
+ final KeyPair pair2 = mGenerator.generateKeyPair();
+ assertNotNull("The KeyPair returned should not be null", pair2);
+ assertKeyPairCorrect(pair2, TEST_ALIAS_1, TEST_DN_2, TEST_SERIAL_2, NOW,
+ NOW_PLUS_10_YEARS);
+ }
+ }
+
private void assertKeyPairCorrect(KeyPair pair, String alias, X500Principal dn,
BigInteger serial, Date start, Date end) throws Exception {
final PublicKey pubKey = pair.getPublic();
@@ -163,10 +266,10 @@
assertEquals("The Serial should be the one passed into the params", serial,
x509userCert.getSerialNumber());
- assertEquals("The notBefore date should be the one passed into the params", start,
+ assertDateEquals("The notBefore date should be the one passed into the params", start,
x509userCert.getNotBefore());
- assertEquals("The notAfter date should be the one passed into the params", end,
+ assertDateEquals("The notAfter date should be the one passed into the params", end,
x509userCert.getNotAfter());
x509userCert.verify(pubKey);
@@ -178,4 +281,13 @@
assertNotNull("The keystore should return the public key for the generated key",
pubKeyBytes);
}
+
+ private static void assertDateEquals(String message, Date date1, Date date2) throws Exception {
+ SimpleDateFormat formatter = new SimpleDateFormat("dd MMM yyyy HH:mm:ss");
+
+ String result1 = formatter.format(date1);
+ String result2 = formatter.format(date2);
+
+ assertEquals(message, result1, result2);
+ }
}
diff --git a/keystore/tests/src/android/security/AndroidKeyStoreTest.java b/keystore/tests/src/android/security/AndroidKeyStoreTest.java
index 8928e06..05ffe109 100644
--- a/keystore/tests/src/android/security/AndroidKeyStoreTest.java
+++ b/keystore/tests/src/android/security/AndroidKeyStoreTest.java
@@ -469,12 +469,14 @@
assertTrue(mAndroidKeyStore.reset());
assertFalse(mAndroidKeyStore.isUnlocked());
+ mKeyStore = java.security.KeyStore.getInstance("AndroidKeyStore");
+ }
+
+ private void setupPassword() {
assertTrue(mAndroidKeyStore.password("1111"));
assertTrue(mAndroidKeyStore.isUnlocked());
assertEquals(0, mAndroidKeyStore.saw("").length);
-
- mKeyStore = java.security.KeyStore.getInstance(AndroidKeyStore.NAME);
}
private void assertAliases(final String[] expectedAliases) throws KeyStoreException {
@@ -495,7 +497,9 @@
expectedAliases.length, count);
}
- public void testKeyStore_Aliases_Success() throws Exception {
+ public void testKeyStore_Aliases_Encrypted_Success() throws Exception {
+ setupPassword();
+
mKeyStore.load(null, null);
assertAliases(new String[] {});
@@ -509,7 +513,9 @@
assertAliases(new String[] { TEST_ALIAS_1, TEST_ALIAS_2 });
}
- public void testKeyStore_Aliases_NotInitialized_Failure() throws Exception {
+ public void testKeyStore_Aliases_NotInitialized_Encrypted_Failure() throws Exception {
+ setupPassword();
+
try {
mKeyStore.aliases();
fail("KeyStore should throw exception when not initialized");
@@ -517,7 +523,9 @@
}
}
- public void testKeyStore_ContainsAliases_PrivateAndCA_Success() throws Exception {
+ public void testKeyStore_ContainsAliases_PrivateAndCA_Encrypted_Success() throws Exception {
+ setupPassword();
+
mKeyStore.load(null, null);
assertAliases(new String[] {});
@@ -534,7 +542,9 @@
mKeyStore.containsAlias(TEST_ALIAS_3));
}
- public void testKeyStore_ContainsAliases_CAOnly_Success() throws Exception {
+ public void testKeyStore_ContainsAliases_CAOnly_Encrypted_Success() throws Exception {
+ setupPassword();
+
mKeyStore.load(null, null);
assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_2, FAKE_CA_1));
@@ -542,13 +552,17 @@
assertTrue("Should contain added CA certificate", mKeyStore.containsAlias(TEST_ALIAS_2));
}
- public void testKeyStore_ContainsAliases_NonExistent_Failure() throws Exception {
+ public void testKeyStore_ContainsAliases_NonExistent_Encrypted_Failure() throws Exception {
+ setupPassword();
+
mKeyStore.load(null, null);
assertFalse("Should contain added CA certificate", mKeyStore.containsAlias(TEST_ALIAS_1));
}
- public void testKeyStore_DeleteEntry_Success() throws Exception {
+ public void testKeyStore_DeleteEntry_Encrypted_Success() throws Exception {
+ setupPassword();
+
mKeyStore.load(null, null);
// TEST_ALIAS_1
@@ -578,14 +592,18 @@
assertAliases(new String[] { });
}
- public void testKeyStore_DeleteEntry_EmptyStore_Success() throws Exception {
+ public void testKeyStore_DeleteEntry_EmptyStore_Encrypted_Success() throws Exception {
+ setupPassword();
+
mKeyStore.load(null, null);
// Should not throw when a non-existent entry is requested for delete.
mKeyStore.deleteEntry(TEST_ALIAS_1);
}
- public void testKeyStore_DeleteEntry_NonExistent_Success() throws Exception {
+ public void testKeyStore_DeleteEntry_NonExistent_Encrypted_Success() throws Exception {
+ setupPassword();
+
mKeyStore.load(null, null);
// TEST_ALIAS_1
@@ -598,7 +616,9 @@
mKeyStore.deleteEntry(TEST_ALIAS_2);
}
- public void testKeyStore_GetCertificate_Single_Success() throws Exception {
+ public void testKeyStore_GetCertificate_Single_Encrypted_Success() throws Exception {
+ setupPassword();
+
mKeyStore.load(null, null);
assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1));
@@ -618,14 +638,18 @@
assertEquals("Actual and retrieved certificates should be the same", actual, retrieved);
}
- public void testKeyStore_GetCertificate_NonExist_Failure() throws Exception {
+ public void testKeyStore_GetCertificate_NonExist_Encrypted_Failure() throws Exception {
+ setupPassword();
+
mKeyStore.load(null, null);
assertNull("Certificate should not exist in keystore",
mKeyStore.getCertificate(TEST_ALIAS_1));
}
- public void testKeyStore_GetCertificateAlias_CAEntry_Success() throws Exception {
+ public void testKeyStore_GetCertificateAlias_CAEntry_Encrypted_Success() throws Exception {
+ setupPassword();
+
mKeyStore.load(null, null);
assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1));
@@ -637,7 +661,10 @@
mKeyStore.getCertificateAlias(actual));
}
- public void testKeyStore_GetCertificateAlias_PrivateKeyEntry_Success() throws Exception {
+ public void testKeyStore_GetCertificateAlias_PrivateKeyEntry_Encrypted_Success()
+ throws Exception {
+ setupPassword();
+
mKeyStore.load(null, null);
assertTrue(mAndroidKeyStore.importKey(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1,
@@ -652,8 +679,10 @@
mKeyStore.getCertificateAlias(actual));
}
- public void testKeyStore_GetCertificateAlias_CAEntry_WithPrivateKeyUsingCA_Success()
+ public void testKeyStore_GetCertificateAlias_CAEntry_WithPrivateKeyUsingCA_Encrypted_Success()
throws Exception {
+ setupPassword();
+
mKeyStore.load(null, null);
// Insert TrustedCertificateEntry with CA name
@@ -672,7 +701,10 @@
mKeyStore.getCertificateAlias(actual));
}
- public void testKeyStore_GetCertificateAlias_NonExist_Empty_Failure() throws Exception {
+ public void testKeyStore_GetCertificateAlias_NonExist_Empty_Encrypted_Failure()
+ throws Exception {
+ setupPassword();
+
mKeyStore.load(null, null);
CertificateFactory f = CertificateFactory.getInstance("X.509");
@@ -682,7 +714,9 @@
mKeyStore.getCertificateAlias(actual));
}
- public void testKeyStore_GetCertificateAlias_NonExist_Failure() throws Exception {
+ public void testKeyStore_GetCertificateAlias_NonExist_Encrypted_Failure() throws Exception {
+ setupPassword();
+
mKeyStore.load(null, null);
assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1));
@@ -694,7 +728,9 @@
mKeyStore.getCertificateAlias(userCert));
}
- public void testKeyStore_GetCertificateChain_SingleLength_Success() throws Exception {
+ public void testKeyStore_GetCertificateChain_SingleLength_Encrypted_Success() throws Exception {
+ setupPassword();
+
mKeyStore.load(null, null);
assertTrue(mAndroidKeyStore.importKey(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1,
@@ -720,14 +756,18 @@
mKeyStore.getCertificateChain(TEST_ALIAS_2));
}
- public void testKeyStore_GetCertificateChain_NonExist_Failure() throws Exception {
+ public void testKeyStore_GetCertificateChain_NonExist_Encrypted_Failure() throws Exception {
+ setupPassword();
+
mKeyStore.load(null, null);
assertNull("Stored certificate alias should not be found",
mKeyStore.getCertificateChain(TEST_ALIAS_1));
}
- public void testKeyStore_GetCreationDate_PrivateKeyEntry_Success() throws Exception {
+ public void testKeyStore_GetCreationDate_PrivateKeyEntry_Encrypted_Success() throws Exception {
+ setupPassword();
+
mKeyStore.load(null, null);
assertTrue(mAndroidKeyStore.importKey(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1,
@@ -745,7 +785,29 @@
assertTrue("Time should be close to current time", actual.after(expectedAfter));
}
- public void testKeyStore_GetCreationDate_CAEntry_Success() throws Exception {
+ public void testKeyStore_GetCreationDate_PrivateKeyEntry_Unencrypted_Success() throws Exception {
+ mKeyStore.load(null, null);
+
+ assertTrue(mAndroidKeyStore.importKey(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1,
+ FAKE_KEY_1, KeyStore.UID_SELF, KeyStore.FLAG_NONE));
+ assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_1, FAKE_USER_1,
+ KeyStore.UID_SELF, KeyStore.FLAG_NONE));
+ assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1,
+ KeyStore.UID_SELF, KeyStore.FLAG_NONE));
+
+ Date now = new Date();
+ Date actual = mKeyStore.getCreationDate(TEST_ALIAS_1);
+
+ Date expectedAfter = new Date(now.getTime() - SLOP_TIME_MILLIS);
+ Date expectedBefore = new Date(now.getTime() + SLOP_TIME_MILLIS);
+
+ assertTrue("Time should be close to current time", actual.before(expectedBefore));
+ assertTrue("Time should be close to current time", actual.after(expectedAfter));
+ }
+
+ public void testKeyStore_GetCreationDate_CAEntry_Encrypted_Success() throws Exception {
+ setupPassword();
+
mKeyStore.load(null, null);
assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1));
@@ -761,7 +823,9 @@
assertTrue("Time should be close to current time", actual.after(expectedAfter));
}
- public void testKeyStore_GetEntry_NullParams_Success() throws Exception {
+ public void testKeyStore_GetEntry_NullParams_Encrypted_Success() throws Exception {
+ setupPassword();
+
mKeyStore.load(null, null);
assertTrue(mAndroidKeyStore.importKey(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1,
@@ -779,6 +843,26 @@
assertPrivateKeyEntryEquals(keyEntry, FAKE_KEY_1, FAKE_USER_1, FAKE_CA_1);
}
+ public void testKeyStore_GetEntry_NullParams_Unencrypted_Success() throws Exception {
+ mKeyStore.load(null, null);
+
+ assertTrue(mAndroidKeyStore.importKey(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1,
+ FAKE_KEY_1, KeyStore.UID_SELF, KeyStore.FLAG_NONE));
+ assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_1, FAKE_USER_1,
+ KeyStore.UID_SELF, KeyStore.FLAG_NONE));
+ assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1,
+ KeyStore.UID_SELF, KeyStore.FLAG_NONE));
+
+ Entry entry = mKeyStore.getEntry(TEST_ALIAS_1, null);
+ assertNotNull("Entry should exist", entry);
+
+ assertTrue("Should be a PrivateKeyEntry", entry instanceof PrivateKeyEntry);
+
+ PrivateKeyEntry keyEntry = (PrivateKeyEntry) entry;
+
+ assertPrivateKeyEntryEquals(keyEntry, FAKE_KEY_1, FAKE_USER_1, FAKE_CA_1);
+ }
+
@SuppressWarnings("unchecked")
private void assertPrivateKeyEntryEquals(PrivateKeyEntry keyEntry, byte[] key, byte[] cert,
byte[] ca) throws Exception {
@@ -801,8 +885,9 @@
private void assertPrivateKeyEntryEquals(PrivateKeyEntry keyEntry, PrivateKey expectedKey,
Certificate expectedCert, Collection<Certificate> expectedChain) throws Exception {
- assertEquals("Returned PrivateKey should be what we inserted", expectedKey,
- keyEntry.getPrivateKey());
+ assertEquals("Returned PrivateKey should be what we inserted",
+ ((RSAPrivateKey) expectedKey).getModulus(),
+ ((RSAPrivateKey) keyEntry.getPrivateKey()).getModulus());
assertEquals("Returned Certificate should be what we inserted", expectedCert,
keyEntry.getCertificate());
@@ -823,14 +908,25 @@
}
}
- public void testKeyStore_GetEntry_Nonexistent_NullParams_Failure() throws Exception {
+ public void testKeyStore_GetEntry_Nonexistent_NullParams_Encrypted_Failure() throws Exception {
+ setupPassword();
+
mKeyStore.load(null, null);
assertNull("A non-existent entry should return null",
mKeyStore.getEntry(TEST_ALIAS_1, null));
}
- public void testKeyStore_GetKey_NoPassword_Success() throws Exception {
+ public void testKeyStore_GetEntry_Nonexistent_NullParams_Unencrypted_Failure() throws Exception {
+ mKeyStore.load(null, null);
+
+ assertNull("A non-existent entry should return null",
+ mKeyStore.getEntry(TEST_ALIAS_1, null));
+ }
+
+ public void testKeyStore_GetKey_NoPassword_Encrypted_Success() throws Exception {
+ setupPassword();
+
mKeyStore.load(null, null);
assertTrue(mAndroidKeyStore.importKey(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1,
@@ -848,10 +944,37 @@
KeyFactory keyFact = KeyFactory.getInstance("RSA");
PrivateKey expectedKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(FAKE_KEY_1));
- assertEquals("Inserted key should be same as retrieved key", actualKey, expectedKey);
+ assertEquals("Inserted key should be same as retrieved key",
+ ((RSAPrivateKey) expectedKey).getModulus(), actualKey.getModulus());
}
- public void testKeyStore_GetKey_Certificate_Failure() throws Exception {
+ public void testKeyStore_GetKey_NoPassword_Unencrypted_Success() throws Exception {
+ mKeyStore.load(null, null);
+
+ assertTrue(mAndroidKeyStore.importKey(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1,
+ FAKE_KEY_1, KeyStore.UID_SELF, KeyStore.FLAG_NONE));
+ assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_1, FAKE_USER_1,
+ KeyStore.UID_SELF, KeyStore.FLAG_NONE));
+ assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1,
+ KeyStore.UID_SELF, KeyStore.FLAG_NONE));
+
+ Key key = mKeyStore.getKey(TEST_ALIAS_1, null);
+ assertNotNull("Key should exist", key);
+
+ assertTrue("Should be a RSAPrivateKey", key instanceof RSAPrivateKey);
+
+ RSAPrivateKey actualKey = (RSAPrivateKey) key;
+
+ KeyFactory keyFact = KeyFactory.getInstance("RSA");
+ PrivateKey expectedKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(FAKE_KEY_1));
+
+ assertEquals("Inserted key should be same as retrieved key",
+ ((RSAPrivateKey) expectedKey).getModulus(), actualKey.getModulus());
+ }
+
+ public void testKeyStore_GetKey_Certificate_Encrypted_Failure() throws Exception {
+ setupPassword();
+
mKeyStore.load(null, null);
assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1));
@@ -859,21 +982,28 @@
assertNull("Certificate entries should return null", mKeyStore.getKey(TEST_ALIAS_1, null));
}
- public void testKeyStore_GetKey_NonExistent_Failure() throws Exception {
+ public void testKeyStore_GetKey_NonExistent_Encrypted_Failure() throws Exception {
+ setupPassword();
+
mKeyStore.load(null, null);
assertNull("A non-existent entry should return null", mKeyStore.getKey(TEST_ALIAS_1, null));
}
- public void testKeyStore_GetProvider_Success() throws Exception {
+ public void testKeyStore_GetProvider_Encrypted_Success() throws Exception {
+ assertEquals(AndroidKeyStoreProvider.PROVIDER_NAME, mKeyStore.getProvider().getName());
+ setupPassword();
assertEquals(AndroidKeyStoreProvider.PROVIDER_NAME, mKeyStore.getProvider().getName());
}
- public void testKeyStore_GetType_Success() throws Exception {
+ public void testKeyStore_GetType_Encrypted_Success() throws Exception {
+ assertEquals(AndroidKeyStore.NAME, mKeyStore.getType());
+ setupPassword();
assertEquals(AndroidKeyStore.NAME, mKeyStore.getType());
}
- public void testKeyStore_IsCertificateEntry_CA_Success() throws Exception {
+ public void testKeyStore_IsCertificateEntry_CA_Encrypted_Success() throws Exception {
+ setupPassword();
mKeyStore.load(null, null);
assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1));
@@ -882,7 +1012,8 @@
mKeyStore.isCertificateEntry(TEST_ALIAS_1));
}
- public void testKeyStore_IsCertificateEntry_PrivateKey_Failure() throws Exception {
+ public void testKeyStore_IsCertificateEntry_PrivateKey_Encrypted_Failure() throws Exception {
+ setupPassword();
mKeyStore.load(null, null);
assertTrue(mAndroidKeyStore.importKey(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1,
@@ -894,14 +1025,23 @@
mKeyStore.isCertificateEntry(TEST_ALIAS_1));
}
- public void testKeyStore_IsCertificateEntry_NonExist_Failure() throws Exception {
+ public void testKeyStore_IsCertificateEntry_NonExist_Encrypted_Failure() throws Exception {
+ setupPassword();
mKeyStore.load(null, null);
assertFalse("Should return false for non-existent entry",
mKeyStore.isCertificateEntry(TEST_ALIAS_1));
}
- public void testKeyStore_IsKeyEntry_PrivateKey_Success() throws Exception {
+ public void testKeyStore_IsCertificateEntry_NonExist_Unencrypted_Failure() throws Exception {
+ mKeyStore.load(null, null);
+
+ assertFalse("Should return false for non-existent entry",
+ mKeyStore.isCertificateEntry(TEST_ALIAS_1));
+ }
+
+ public void testKeyStore_IsKeyEntry_PrivateKey_Encrypted_Success() throws Exception {
+ setupPassword();
mKeyStore.load(null, null);
assertTrue(mAndroidKeyStore.importKey(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1,
@@ -912,7 +1052,8 @@
assertTrue("Should return true for PrivateKeyEntry", mKeyStore.isKeyEntry(TEST_ALIAS_1));
}
- public void testKeyStore_IsKeyEntry_CA_Failure() throws Exception {
+ public void testKeyStore_IsKeyEntry_CA_Encrypted_Failure() throws Exception {
+ setupPassword();
mKeyStore.load(null, null);
assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1));
@@ -920,17 +1061,19 @@
assertFalse("Should return false for CA certificate", mKeyStore.isKeyEntry(TEST_ALIAS_1));
}
- public void testKeyStore_IsKeyEntry_NonExist_Failure() throws Exception {
+ public void testKeyStore_IsKeyEntry_NonExist_Encrypted_Failure() throws Exception {
+ setupPassword();
mKeyStore.load(null, null);
assertFalse("Should return false for non-existent entry",
mKeyStore.isKeyEntry(TEST_ALIAS_1));
}
- public void testKeyStore_SetCertificate_CA_Success() throws Exception {
+ public void testKeyStore_SetCertificate_CA_Encrypted_Success() throws Exception {
final CertificateFactory f = CertificateFactory.getInstance("X.509");
final Certificate actual = f.generateCertificate(new ByteArrayInputStream(FAKE_CA_1));
+ setupPassword();
mKeyStore.load(null, null);
mKeyStore.setCertificateEntry(TEST_ALIAS_1, actual);
@@ -942,7 +1085,8 @@
retrieved);
}
- public void testKeyStore_SetCertificate_CAExists_Overwrite_Success() throws Exception {
+ public void testKeyStore_SetCertificate_CAExists_Overwrite_Encrypted_Success() throws Exception {
+ setupPassword();
mKeyStore.load(null, null);
assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1));
@@ -958,7 +1102,8 @@
assertAliases(new String[] { TEST_ALIAS_1 });
}
- public void testKeyStore_SetCertificate_PrivateKeyExists_Failure() throws Exception {
+ public void testKeyStore_SetCertificate_PrivateKeyExists_Encrypted_Failure() throws Exception {
+ setupPassword();
mKeyStore.load(null, null);
assertTrue(mAndroidKeyStore.importKey(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1,
@@ -978,7 +1123,8 @@
}
}
- public void testKeyStore_SetEntry_PrivateKeyEntry_Success() throws Exception {
+ public void testKeyStore_SetEntry_PrivateKeyEntry_Encrypted_Success() throws Exception {
+ setupPassword();
mKeyStore.load(null, null);
KeyFactory keyFact = KeyFactory.getInstance("RSA");
@@ -1005,8 +1151,63 @@
assertPrivateKeyEntryEquals(actual, FAKE_KEY_1, FAKE_USER_1, FAKE_CA_1);
}
- public void testKeyStore_SetEntry_PrivateKeyEntry_Overwrites_PrivateKeyEntry_Success()
+ public void testKeyStore_SetEntry_PrivateKeyEntry_Unencrypted_Success() throws Exception {
+ mKeyStore.load(null, null);
+
+ KeyFactory keyFact = KeyFactory.getInstance("RSA");
+ PrivateKey expectedKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(FAKE_KEY_1));
+
+ final CertificateFactory f = CertificateFactory.getInstance("X.509");
+
+ final Certificate[] expectedChain = new Certificate[2];
+ expectedChain[0] = f.generateCertificate(new ByteArrayInputStream(FAKE_USER_1));
+ expectedChain[1] = f.generateCertificate(new ByteArrayInputStream(FAKE_CA_1));
+
+ PrivateKeyEntry expected = new PrivateKeyEntry(expectedKey, expectedChain);
+
+ mKeyStore.setEntry(TEST_ALIAS_1, expected, null);
+
+ Entry actualEntry = mKeyStore.getEntry(TEST_ALIAS_1, null);
+ assertNotNull("Retrieved entry should exist", actualEntry);
+
+ assertTrue("Retrieved entry should be of type PrivateKeyEntry",
+ actualEntry instanceof PrivateKeyEntry);
+
+ PrivateKeyEntry actual = (PrivateKeyEntry) actualEntry;
+
+ assertPrivateKeyEntryEquals(actual, FAKE_KEY_1, FAKE_USER_1, FAKE_CA_1);
+ }
+
+ public void testKeyStore_SetEntry_PrivateKeyEntry_Params_Unencrypted_Failure() throws Exception {
+ mKeyStore.load(null, null);
+
+ KeyFactory keyFact = KeyFactory.getInstance("RSA");
+ PrivateKey expectedKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(FAKE_KEY_1));
+
+ final CertificateFactory f = CertificateFactory.getInstance("X.509");
+
+ final Certificate[] expectedChain = new Certificate[2];
+ expectedChain[0] = f.generateCertificate(new ByteArrayInputStream(FAKE_USER_1));
+ expectedChain[1] = f.generateCertificate(new ByteArrayInputStream(FAKE_CA_1));
+
+ PrivateKeyEntry entry = new PrivateKeyEntry(expectedKey, expectedChain);
+
+ try {
+ mKeyStore.setEntry(TEST_ALIAS_1, entry,
+ new AndroidKeyStoreParameter.Builder(getContext())
+ .setEncryptionRequired()
+ .build());
+ fail("Shouldn't be able to insert encrypted entry when KeyStore uninitialized");
+ } catch (KeyStoreException expected) {
+ }
+
+ assertNull(mKeyStore.getEntry(TEST_ALIAS_1, null));
+ }
+
+ public void
+ testKeyStore_SetEntry_PrivateKeyEntry_Overwrites_PrivateKeyEntry_Encrypted_Success()
throws Exception {
+ setupPassword();
mKeyStore.load(null, null);
final KeyFactory keyFact = KeyFactory.getInstance("RSA");
@@ -1060,7 +1261,9 @@
}
}
- public void testKeyStore_SetEntry_CAEntry_Overwrites_PrivateKeyEntry_Success() throws Exception {
+ public void testKeyStore_SetEntry_CAEntry_Overwrites_PrivateKeyEntry_Encrypted_Success()
+ throws Exception {
+ setupPassword();
mKeyStore.load(null, null);
final CertificateFactory f = CertificateFactory.getInstance("X.509");
@@ -1104,7 +1307,9 @@
}
}
- public void testKeyStore_SetEntry_PrivateKeyEntry_Overwrites_CAEntry_Success() throws Exception {
+ public void testKeyStore_SetEntry_PrivateKeyEntry_Overwrites_CAEntry_Encrypted_Success()
+ throws Exception {
+ setupPassword();
mKeyStore.load(null, null);
final CertificateFactory f = CertificateFactory.getInstance("X.509");
@@ -1148,8 +1353,11 @@
}
}
- public void testKeyStore_SetEntry_PrivateKeyEntry_Overwrites_ShortPrivateKeyEntry_Success()
+ public
+ void
+ testKeyStore_SetEntry_PrivateKeyEntry_Overwrites_ShortPrivateKeyEntry_Encrypted_Success()
throws Exception {
+ setupPassword();
mKeyStore.load(null, null);
final CertificateFactory f = CertificateFactory.getInstance("X.509");
@@ -1198,7 +1406,9 @@
}
}
- public void testKeyStore_SetEntry_CAEntry_Overwrites_CAEntry_Success() throws Exception {
+ public void testKeyStore_SetEntry_CAEntry_Overwrites_CAEntry_Encrypted_Success()
+ throws Exception {
+ setupPassword();
mKeyStore.load(null, null);
final CertificateFactory f = CertificateFactory.getInstance("X.509");
@@ -1239,7 +1449,8 @@
}
}
- public void testKeyStore_SetKeyEntry_ProtectedKey_Failure() throws Exception {
+ public void testKeyStore_SetKeyEntry_ProtectedKey_Encrypted_Failure() throws Exception {
+ setupPassword();
mKeyStore.load(null, null);
final CertificateFactory f = CertificateFactory.getInstance("X.509");
@@ -1259,7 +1470,8 @@
}
}
- public void testKeyStore_SetKeyEntry_Success() throws Exception {
+ public void testKeyStore_SetKeyEntry_Encrypted_Success() throws Exception {
+ setupPassword();
mKeyStore.load(null, null);
final CertificateFactory f = CertificateFactory.getInstance("X.509");
@@ -1285,7 +1497,8 @@
assertPrivateKeyEntryEquals(actual, FAKE_KEY_1, FAKE_USER_1, FAKE_CA_1);
}
- public void testKeyStore_SetKeyEntry_Replaced_Success() throws Exception {
+ public void testKeyStore_SetKeyEntry_Replaced_Encrypted_Success() throws Exception {
+ setupPassword();
mKeyStore.load(null, null);
final CertificateFactory f = CertificateFactory.getInstance("X.509");
@@ -1376,7 +1589,8 @@
return cert;
}
- public void testKeyStore_SetKeyEntry_ReplacedChain_Success() throws Exception {
+ public void testKeyStore_SetKeyEntry_ReplacedChain_Encrypted_Success() throws Exception {
+ setupPassword();
mKeyStore.load(null, null);
// Create key #1
@@ -1429,8 +1643,9 @@
}
}
- public void testKeyStore_SetKeyEntry_ReplacedChain_DifferentPrivateKey_Failure()
+ public void testKeyStore_SetKeyEntry_ReplacedChain_DifferentPrivateKey_Encrypted_Failure()
throws Exception {
+ setupPassword();
mKeyStore.load(null, null);
// Create key #1
@@ -1472,7 +1687,48 @@
}
}
- public void testKeyStore_Size_Success() throws Exception {
+ public void testKeyStore_SetKeyEntry_ReplacedChain_UnencryptedToEncrypted_Failure()
+ throws Exception {
+ mKeyStore.load(null, null);
+
+ // Create key #1
+ {
+ final String privateKeyAlias = Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1;
+ assertTrue(mAndroidKeyStore.generate(privateKeyAlias,
+ android.security.KeyStore.UID_SELF, android.security.KeyStore.FLAG_NONE));
+
+ X509Certificate cert =
+ generateCertificate(mAndroidKeyStore, TEST_ALIAS_1, TEST_SERIAL_1, TEST_DN_1,
+ NOW, NOW_PLUS_10_YEARS);
+
+ assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_1,
+ cert.getEncoded(), android.security.KeyStore.UID_SELF,
+ android.security.KeyStore.FLAG_NONE));
+ }
+
+ // Replace with one that requires encryption
+ {
+ Entry entry = mKeyStore.getEntry(TEST_ALIAS_1, null);
+
+ try {
+ mKeyStore.setEntry(TEST_ALIAS_1, entry, new AndroidKeyStoreParameter.Builder(
+ getContext()).setEncryptionRequired().build());
+ fail("Should not allow setting of Entry without unlocked keystore");
+ } catch (KeyStoreException success) {
+ }
+
+ assertTrue(mAndroidKeyStore.password("1111"));
+ assertTrue(mAndroidKeyStore.isUnlocked());
+
+ mKeyStore.setEntry(TEST_ALIAS_1, entry,
+ new AndroidKeyStoreParameter.Builder(getContext())
+ .setEncryptionRequired()
+ .build());
+ }
+ }
+
+ public void testKeyStore_Size_Encrypted_Success() throws Exception {
+ setupPassword();
mKeyStore.load(null, null);
assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1));
@@ -1501,7 +1757,8 @@
assertAliases(new String[] { TEST_ALIAS_2 });
}
- public void testKeyStore_Store_LoadStoreParam_Failure() throws Exception {
+ public void testKeyStore_Store_LoadStoreParam_Encrypted_Failure() throws Exception {
+ setupPassword();
mKeyStore.load(null, null);
try {
@@ -1511,7 +1768,7 @@
}
}
- public void testKeyStore_Load_InputStreamSupplied_Failure() throws Exception {
+ public void testKeyStore_Load_InputStreamSupplied_Encrypted_Failure() throws Exception {
byte[] buf = "FAKE KEYSTORE".getBytes();
ByteArrayInputStream is = new ByteArrayInputStream(buf);
@@ -1522,7 +1779,7 @@
}
}
- public void testKeyStore_Load_PasswordSupplied_Failure() throws Exception {
+ public void testKeyStore_Load_PasswordSupplied_Encrypted_Failure() throws Exception {
try {
mKeyStore.load(null, "password".toCharArray());
fail("Should throw IllegalArgumentException when password is supplied");
@@ -1530,7 +1787,8 @@
}
}
- public void testKeyStore_Store_OutputStream_Failure() throws Exception {
+ public void testKeyStore_Store_OutputStream_Encrypted_Failure() throws Exception {
+ setupPassword();
mKeyStore.load(null, null);
OutputStream sink = new ByteArrayOutputStream();
@@ -1558,7 +1816,8 @@
cert.getEncoded()));
}
- public void testKeyStore_KeyOperations_Wrap_Success() throws Exception {
+ public void testKeyStore_KeyOperations_Wrap_Encrypted_Success() throws Exception {
+ setupPassword();
mKeyStore.load(null, null);
setupKey();
diff --git a/libs/hwui/Dither.cpp b/libs/hwui/Dither.cpp
index 51f1e39..19b3849 100644
--- a/libs/hwui/Dither.cpp
+++ b/libs/hwui/Dither.cpp
@@ -75,6 +75,7 @@
void Dither::clear() {
if (mInitialized) {
glDeleteTextures(1, &mDitherTexture);
+ mInitialized = false;
}
}
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 2417cff..bc1df85 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -936,6 +936,12 @@
CompatModeDialog mCompatModeDialog;
long mLastMemUsageReportTime = 0;
+ /**
+ * Flag whether the current user is a "monkey", i.e. whether
+ * the UI is driven by a UI automation tool.
+ */
+ private boolean mUserIsMonkey;
+
final Handler mHandler = new Handler() {
//public Handler() {
// if (localLOGV) Slog.v(TAG, "Handler started!");
@@ -7434,11 +7440,27 @@
}
}
- public boolean isUserAMonkey() {
- // For now the fact that there is a controller implies
- // we have a monkey.
+ public void setUserIsMonkey(boolean userIsMonkey) {
synchronized (this) {
- return mController != null;
+ synchronized (mPidsSelfLocked) {
+ final int callingPid = Binder.getCallingPid();
+ ProcessRecord precessRecord = mPidsSelfLocked.get(callingPid);
+ if (precessRecord == null) {
+ throw new SecurityException("Unknown process: " + callingPid);
+ }
+ if (precessRecord.instrumentationUiAutomationConnection == null) {
+ throw new SecurityException("Only an instrumentation process "
+ + "with a UiAutomation can call setUserIsMonkey");
+ }
+ }
+ mUserIsMonkey = userIsMonkey;
+ }
+ }
+
+ public boolean isUserAMonkey() {
+ synchronized (this) {
+ // If there is a controller also implies the user is a monkey.
+ return (mUserIsMonkey || mController != null);
}
}
@@ -12435,6 +12457,9 @@
} catch (RemoteException re) {
/* ignore */
}
+ // Only a UiAutomation can set this flag and now that
+ // it is finished we make sure it is reset to its default.
+ mUserIsMonkey = false;
}
app.instrumentationWatcher = null;
app.instrumentationUiAutomationConnection = null;
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index 77168f9..44aaa43 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -1597,7 +1597,7 @@
static status_t writeLayoutClasses(
FILE* fp, const sp<AaptAssets>& assets,
- const sp<AaptSymbols>& symbols, int indent, bool includePrivate)
+ const sp<AaptSymbols>& symbols, int indent, bool includePrivate, bool nonConstantId)
{
const char* indentStr = getIndentSpace(indent);
if (!includePrivate) {
@@ -1612,6 +1612,14 @@
indentStr = getIndentSpace(indent);
bool hasErrors = false;
+ const char * id_array_format = nonConstantId ?
+ "%spublic static int[] %s = {\n%s" :
+ "%spublic static final int[] %s = {\n%s";
+
+ const char * id_array_index_format = nonConstantId ?
+ "%spublic static int %s_%s = %d;\n" :
+ "%spublic static final int %s_%s = %d;\n";
+
size_t i;
size_t N = symbols->getNestedSymbols().size();
for (i=0; i<N; i++) {
@@ -1745,8 +1753,7 @@
}
fprintf(fp,
- "%spublic static final int[] %s = {\n"
- "%s",
+ id_array_format,
indentStr, nclassName.string(),
getIndentSpace(indent+1));
@@ -1840,7 +1847,7 @@
fprintf(fp, "%s@Deprecated\n", indentStr);
}
fprintf(fp,
- "%spublic static final int %s_%s = %d;\n",
+ id_array_index_format,
indentStr, nclassName.string(),
String8(name).string(), (int)pos);
}
@@ -2083,7 +2090,7 @@
}
if (styleableSymbols != NULL) {
- err = writeLayoutClasses(fp, assets, styleableSymbols, indent, includePrivate);
+ err = writeLayoutClasses(fp, assets, styleableSymbols, indent, includePrivate, nonConstantId);
if (err != NO_ERROR) {
return err;
}