Merge "Import translations. DO NOT MERGE" into klp-dev
diff --git a/Android.mk b/Android.mk
index 69d1daf..eaafa87 100644
--- a/Android.mk
+++ b/Android.mk
@@ -142,7 +142,7 @@
core/java/android/net/INetworkStatsService.aidl \
core/java/android/net/INetworkStatsSession.aidl \
core/java/android/net/nsd/INsdManager.aidl \
- core/java/android/nfc/INdefPushCallback.aidl \
+ core/java/android/nfc/IAppCallback.aidl \
core/java/android/nfc/INfcAdapter.aidl \
core/java/android/nfc/INfcAdapterExtras.aidl \
core/java/android/nfc/INfcTag.aidl \
diff --git a/api/current.txt b/api/current.txt
index 7fbc484..7d06e1c 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -289,6 +289,7 @@
field public static final deprecated int animationResolution = 16843546; // 0x101031a
field public static final int antialias = 16843034; // 0x101011a
field public static final int anyDensity = 16843372; // 0x101026c
+ field public static final int apduServiceBanner = 16843755; // 0x10103eb
field public static final int apiKey = 16843281; // 0x1010211
field public static final int author = 16843444; // 0x10102b4
field public static final int authorities = 16842776; // 0x1010018
@@ -11947,13 +11948,14 @@
ctor public SettingInjectorService(java.lang.String);
method protected abstract android.location.SettingInjectorService.Status getStatus();
method protected final void onHandleIntent(android.content.Intent);
- field public static final java.lang.String ACTION_INJECTED_SETTING_CHANGED = "com.android.location.InjectedSettingChanged";
+ field public static final java.lang.String ACTION_INJECTED_SETTING_CHANGED = "android.location.InjectedSettingChanged";
+ field public static final java.lang.String ACTION_SERVICE_INTENT = "android.location.SettingInjectorService";
+ field public static final java.lang.String ATTRIBUTES_NAME = "injected-location-setting";
+ field public static final java.lang.String META_DATA_NAME = "android.location.SettingInjectorService";
}
public static final class SettingInjectorService.Status {
ctor public SettingInjectorService.Status(java.lang.String, boolean);
- field public final boolean enabled;
- field public final java.lang.String summary;
}
}
@@ -12890,7 +12892,9 @@
field public static final int MEDIA_INFO_BUFFERING_START = 701; // 0x2bd
field public static final int MEDIA_INFO_METADATA_UPDATE = 802; // 0x322
field public static final int MEDIA_INFO_NOT_SEEKABLE = 801; // 0x321
+ field public static final int MEDIA_INFO_SUBTITLE_TIMED_OUT = 902; // 0x386
field public static final int MEDIA_INFO_UNKNOWN = 1; // 0x1
+ field public static final int MEDIA_INFO_UNSUPPORTED_SUBTITLE = 901; // 0x385
field public static final int MEDIA_INFO_VIDEO_RENDERING_START = 3; // 0x3
field public static final int MEDIA_INFO_VIDEO_TRACK_LAGGING = 700; // 0x2bc
field public static final java.lang.String MEDIA_MIMETYPE_TEXT_SUBRIP = "application/x-subrip";
@@ -13187,6 +13191,7 @@
ctor public RemoteControlClient(android.app.PendingIntent);
ctor public RemoteControlClient(android.app.PendingIntent, android.os.Looper);
method public android.media.RemoteControlClient.MetadataEditor editMetadata(boolean);
+ method public void setMetadataUpdateListener(android.media.RemoteControlClient.OnMetadataUpdateListener);
method public void setOnGetPlaybackPositionListener(android.media.RemoteControlClient.OnGetPlaybackPositionListener);
method public void setPlaybackPositionUpdateListener(android.media.RemoteControlClient.OnPlaybackPositionUpdateListener);
method public void setPlaybackState(int);
@@ -13199,6 +13204,7 @@
field public static final int FLAG_KEY_MEDIA_PLAY_PAUSE = 8; // 0x8
field public static final int FLAG_KEY_MEDIA_POSITION_UPDATE = 256; // 0x100
field public static final int FLAG_KEY_MEDIA_PREVIOUS = 1; // 0x1
+ field public static final int FLAG_KEY_MEDIA_RATING = 512; // 0x200
field public static final int FLAG_KEY_MEDIA_REWIND = 2; // 0x2
field public static final int FLAG_KEY_MEDIA_STOP = 32; // 0x20
field public static final int PLAYSTATE_BUFFERING = 8; // 0x8
@@ -13213,18 +13219,32 @@
}
public class RemoteControlClient.MetadataEditor {
+ method public synchronized void addEditableKey(int);
method public synchronized void apply();
method public synchronized void clear();
+ method public synchronized void clearEditableKeys();
method public synchronized android.media.RemoteControlClient.MetadataEditor putBitmap(int, android.graphics.Bitmap) throws java.lang.IllegalArgumentException;
method public synchronized android.media.RemoteControlClient.MetadataEditor putLong(int, long) throws java.lang.IllegalArgumentException;
method public synchronized android.media.RemoteControlClient.MetadataEditor putString(int, java.lang.String) throws java.lang.IllegalArgumentException;
field public static final int BITMAP_KEY_ARTWORK = 100; // 0x64
+ field public static final int LONG_KEY_RATING_BY_OTHERS = 102; // 0x66
+ field public static final int LONG_KEY_RATING_BY_USER = 268435457; // 0x10000001
+ field public static final int LONG_KEY_RATING_TYPE = 101; // 0x65
+ field public static final long RATING_HEART = -1L; // 0xffffffffffffffffL
+ field public static final long RATING_NOT_RATED = -101L; // 0xffffffffffffff9bL
+ field public static final long RATING_THUMB_UP_DOWN = -2L; // 0xfffffffffffffffeL
}
public static abstract interface RemoteControlClient.OnGetPlaybackPositionListener {
method public abstract long onGetPlaybackPosition();
}
+ public static abstract interface RemoteControlClient.OnMetadataUpdateListener {
+ method public abstract void onMetadataUpdateBitmap(int, android.graphics.Bitmap);
+ method public abstract void onMetadataUpdateLong(int, long);
+ method public abstract void onMetadataUpdateString(int, java.lang.String);
+ }
+
public static abstract interface RemoteControlClient.OnPlaybackPositionUpdateListener {
method public abstract void onPlaybackPositionUpdate(long);
}
@@ -15182,7 +15202,7 @@
method public void disableReaderMode(android.app.Activity);
method public void enableForegroundDispatch(android.app.Activity, android.app.PendingIntent, android.content.IntentFilter[], java.lang.String[][]);
method public deprecated void enableForegroundNdefPush(android.app.Activity, android.nfc.NdefMessage);
- method public void enableReaderMode(android.app.Activity, int);
+ method public void enableReaderMode(android.app.Activity, android.nfc.NfcAdapter.ReaderCallback, int, android.os.Bundle);
method public static android.nfc.NfcAdapter getDefaultAdapter(android.content.Context);
method public boolean isEnabled();
method public boolean isNdefPushEnabled();
@@ -15198,12 +15218,14 @@
field public static final java.lang.String EXTRA_ADAPTER_STATE = "android.nfc.extra.ADAPTER_STATE";
field public static final java.lang.String EXTRA_ID = "android.nfc.extra.ID";
field public static final java.lang.String EXTRA_NDEF_MESSAGES = "android.nfc.extra.NDEF_MESSAGES";
+ field public static final java.lang.String EXTRA_READER_PRESENCE_CHECK_DELAY = "presence";
field public static final java.lang.String EXTRA_TAG = "android.nfc.extra.TAG";
field public static final int FLAG_READER_KOVIO = 16; // 0x10
field public static final int FLAG_READER_NFC_A = 1; // 0x1
field public static final int FLAG_READER_NFC_B = 2; // 0x2
field public static final int FLAG_READER_NFC_F = 4; // 0x4
field public static final int FLAG_READER_NFC_V = 8; // 0x8
+ field public static final int FLAG_READER_NO_PLATFORM_SOUNDS = 256; // 0x100
field public static final int FLAG_READER_SKIP_NDEF_CHECK = 128; // 0x80
field public static final int STATE_OFF = 1; // 0x1
field public static final int STATE_ON = 3; // 0x3
@@ -15223,6 +15245,10 @@
method public abstract void onNdefPushComplete(android.nfc.NfcEvent);
}
+ public static abstract interface NfcAdapter.ReaderCallback {
+ method public abstract void onTagDiscovered(android.nfc.Tag);
+ }
+
public final class NfcEvent {
field public final android.nfc.NfcAdapter nfcAdapter;
}
@@ -15248,12 +15274,12 @@
package android.nfc.cardemulation {
- public final class CardEmulationManager {
- method public static synchronized android.nfc.cardemulation.CardEmulationManager getInstance(android.nfc.NfcAdapter);
+ public final class CardEmulation {
+ method public static synchronized android.nfc.cardemulation.CardEmulation getInstance(android.nfc.NfcAdapter);
method public int getSelectionModeForCategory(java.lang.String);
method public boolean isDefaultServiceForAid(android.content.ComponentName, java.lang.String);
method public boolean isDefaultServiceForCategory(android.content.ComponentName, java.lang.String);
- field public static final java.lang.String ACTION_CHANGE_DEFAULT = "android.nfc.cardemulation.ACTION_CHANGE_DEFAULT";
+ field public static final java.lang.String ACTION_CHANGE_DEFAULT = "android.nfc.cardemulation.action.ACTION_CHANGE_DEFAULT";
field public static final java.lang.String CATEGORY_OTHER = "other";
field public static final java.lang.String CATEGORY_PAYMENT = "payment";
field public static final java.lang.String EXTRA_CATEGORY = "category";
@@ -15269,19 +15295,18 @@
method public final android.os.IBinder onBind(android.content.Intent);
method public abstract void onDeactivated(int);
method public byte[] processCommandApdu(byte[], android.os.Bundle);
- method public abstract deprecated byte[] processCommandApdu(byte[], int);
method public final void sendResponseApdu(byte[]);
field public static final int DEACTIVATION_DESELECTED = 1; // 0x1
field public static final int DEACTIVATION_LINK_LOSS = 0; // 0x0
- field public static final java.lang.String SERVICE_INTERFACE = "android.nfc.HostApduService";
- field public static final java.lang.String SERVICE_META_DATA = "android.nfc.HostApduService";
+ field public static final java.lang.String SERVICE_INTERFACE = "android.nfc.cardemulation.action.HOST_APDU_SERVICE";
+ field public static final java.lang.String SERVICE_META_DATA = "android.nfc.cardemulation.host_apdu_service";
}
public abstract class OffHostApduService extends android.app.Service {
ctor public OffHostApduService();
method public abstract android.os.IBinder onBind(android.content.Intent);
- field public static final java.lang.String SERVICE_INTERFACE = "android.nfc.OffHostApduService";
- field public static final java.lang.String SERVICE_META_DATA = "android.nfc.OffHostApduService";
+ field public static final java.lang.String SERVICE_INTERFACE = "android.nfc.cardemulation.action.OFF_HOST_APDU_SERVICE";
+ field public static final java.lang.String SERVICE_META_DATA = "android.nfc.cardemulation.off_host_apdu_service";
}
}
@@ -22577,8 +22602,11 @@
}
public final class KeyPairGeneratorSpec implements java.security.spec.AlgorithmParameterSpec {
+ method public java.security.spec.AlgorithmParameterSpec getAlgorithmParameterSpec();
method public android.content.Context getContext();
method public java.util.Date getEndDate();
+ method public int getKeySize();
+ method public java.lang.String getKeyType();
method public java.lang.String getKeystoreAlias();
method public java.math.BigInteger getSerialNumber();
method public java.util.Date getStartDate();
@@ -22589,9 +22617,12 @@
public static final class KeyPairGeneratorSpec.Builder {
ctor public KeyPairGeneratorSpec.Builder(android.content.Context);
method public android.security.KeyPairGeneratorSpec build();
+ method public android.security.KeyPairGeneratorSpec.Builder setAlgorithmParameterSpec(java.security.spec.AlgorithmParameterSpec);
method public android.security.KeyPairGeneratorSpec.Builder setAlias(java.lang.String);
method public android.security.KeyPairGeneratorSpec.Builder setEncryptionRequired();
method public android.security.KeyPairGeneratorSpec.Builder setEndDate(java.util.Date);
+ method public android.security.KeyPairGeneratorSpec.Builder setKeySize(int);
+ method public android.security.KeyPairGeneratorSpec.Builder setKeyType(java.lang.String) throws java.security.NoSuchAlgorithmException;
method public android.security.KeyPairGeneratorSpec.Builder setSerialNumber(java.math.BigInteger);
method public android.security.KeyPairGeneratorSpec.Builder setStartDate(java.util.Date);
method public android.security.KeyPairGeneratorSpec.Builder setSubject(javax.security.auth.x500.X500Principal);
@@ -27140,6 +27171,7 @@
public class ScaleGestureDetector {
ctor public ScaleGestureDetector(android.content.Context, android.view.ScaleGestureDetector.OnScaleGestureListener);
+ ctor public ScaleGestureDetector(android.content.Context, android.view.ScaleGestureDetector.OnScaleGestureListener, android.os.Handler);
method public float getCurrentSpan();
method public float getCurrentSpanX();
method public float getCurrentSpanY();
@@ -27153,6 +27185,7 @@
method public long getTimeDelta();
method public boolean isInProgress();
method public boolean onTouchEvent(android.view.MotionEvent);
+ method public void setQuickScaleEnabled(boolean);
}
public static abstract interface ScaleGestureDetector.OnScaleGestureListener {
@@ -30370,6 +30403,7 @@
ctor public AbsListView(android.content.Context, android.util.AttributeSet, int);
method public void afterTextChanged(android.text.Editable);
method public void beforeTextChanged(java.lang.CharSequence, int, int, int);
+ method public boolean canScrollList(int);
method public void clearChoices();
method public void clearTextFilter();
method public void deferNotifyDataSetChanged();
@@ -30411,7 +30445,7 @@
method public int pointToPosition(int, int);
method public long pointToRowId(int, int);
method public void reclaimViews(java.util.List<android.view.View>);
- method public boolean scrollListBy(int);
+ method public void scrollListBy(int);
method public void setAdapter(android.widget.ListAdapter);
method public void setCacheColorHint(int);
method public void setChoiceMode(int);
@@ -32634,6 +32668,7 @@
ctor public VideoView(android.content.Context);
ctor public VideoView(android.content.Context, android.util.AttributeSet);
ctor public VideoView(android.content.Context, android.util.AttributeSet, int);
+ method public void addSubtitleSource(java.io.InputStream, android.media.MediaFormat);
method public boolean canPause();
method public boolean canSeekBackward();
method public boolean canSeekForward();
diff --git a/core/java/android/bluetooth/BluetoothGatt.java b/core/java/android/bluetooth/BluetoothGatt.java
index b390aa1..a2bb78c 100644
--- a/core/java/android/bluetooth/BluetoothGatt.java
+++ b/core/java/android/bluetooth/BluetoothGatt.java
@@ -702,6 +702,10 @@
* @param start Start or stop advertising
*/
/*package*/ void listen(boolean start) {
+ if (mContext == null || !mContext.getResources().
+ getBoolean(com.android.internal.R.bool.config_bluetooth_le_peripheral_mode_supported)) {
+ throw new UnsupportedOperationException("BluetoothGatt#listen is blocked");
+ }
if (DBG) Log.d(TAG, "listen() - start: " + start);
if (mService == null || mClientIf == 0) return;
@@ -728,6 +732,10 @@
/*package*/ void setAdvData(boolean advData, boolean includeName, boolean includeTxPower,
Integer minInterval, Integer maxInterval,
Integer appearance, Byte[] manufacturerData) {
+ if (mContext == null || !mContext.getResources().
+ getBoolean(com.android.internal.R.bool.config_bluetooth_le_peripheral_mode_supported)) {
+ throw new UnsupportedOperationException("BluetoothGatt#setAdvData is blocked");
+ }
if (DBG) Log.d(TAG, "setAdvData()");
if (mService == null || mClientIf == 0) return;
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index d7ca9153..dfc0412 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -3300,8 +3300,10 @@
/**
* Optional extra for {@link #ACTION_SHUTDOWN} that allows the sender to qualify that
* this shutdown is only for the user space of the system, not a complete shutdown.
- * Hardware should not be shut down when this is true. The default if not supplied
- * is false.
+ * When this is true, hardware devices can use this information to determine that
+ * they shouldn't do a complete shutdown of their device since this is not a
+ * complete shutdown down to the kernel, but only user space restarting.
+ * The default if not supplied is false.
*/
public static final String EXTRA_SHUTDOWN_USERSPACE_ONLY
= "android.intent.extra.SHUTDOWN_USERSPACE_ONLY";
diff --git a/core/java/android/hardware/FlushCompleteListener.java b/core/java/android/hardware/FlushCompleteListener.java
new file mode 100644
index 0000000..cb5b9e3
--- /dev/null
+++ b/core/java/android/hardware/FlushCompleteListener.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware;
+
+/**
+ * Used for receiving a notification when a flush() has been successfully completed.
+ * @hide
+ */
+public interface FlushCompleteListener {
+ /**
+ * Called after flush() is completed. This flush() could have been initiated by this application
+ * or some other application. All the events in the batch at the point when the flush was called
+ * have been delivered to the applications registered for those sensor events.
+ * <p>
+ *
+ * @param sensor The {@link android.hardware.Sensor Sensor} on which flush was called.
+ *
+ * @see android.hardware.SensorManager#flush(Sensor)
+ */
+ public void onFlushCompleted(Sensor sensor);
+}
diff --git a/core/java/android/hardware/Sensor.java b/core/java/android/hardware/Sensor.java
index 9bffdbe..bbede57 100644
--- a/core/java/android/hardware/Sensor.java
+++ b/core/java/android/hardware/Sensor.java
@@ -319,6 +319,8 @@
private float mResolution;
private float mPower;
private int mMinDelay;
+ private int mFifoReservedEventCount;
+ private int mFifoMaxEventCount;
Sensor() {
}
@@ -381,6 +383,26 @@
return mMinDelay;
}
+ /**
+ * @return Number of events reserved for this sensor in the batch mode FIFO. This gives a
+ * guarantee on the minimum number of events that can be batched
+ * @hide
+ */
+ public int getFifoReservedEventCount() {
+ return mFifoReservedEventCount;
+ }
+
+ /**
+ * @return Maximum number of events of this sensor that could be batched. If this value is zero
+ * it indicates that batch mode is not supported for this sensor. If other applications
+ * registered to batched sensors, the actual number of events that can be batched might be
+ * smaller because the hardware FiFo will be partially used to batch the other sensors.
+ * @hide
+ */
+ public int getFifoMaxEventCount() {
+ return mFifoMaxEventCount;
+ }
+
/** @hide */
public int getHandle() {
return mHandle;
diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java
index 30118f9..b6ca62a 100644
--- a/core/java/android/hardware/SensorManager.java
+++ b/core/java/android/hardware/SensorManager.java
@@ -608,8 +608,72 @@
}
/**
- * Registers a {@link android.hardware.SensorEventListener
- * SensorEventListener} for the given sensor.
+ * Enables batch mode for a sensor with the given rate and maxBatchReportLatency. If the
+ * underlying hardware does not support batch mode, this defaults to
+ * {@link #registerListener(SensorEventListener, Sensor, int)} and other parameters are are
+ * ignored. In non-batch mode, all sensor events must be reported as soon as they are detected.
+ * While in batch mode, sensor events do not need to be reported as soon as they are detected.
+ * They can be temporarily stored in batches and reported in batches, as long as no event is
+ * delayed by more than "maxBatchReportLatency" microseconds. That is, all events since the
+ * previous batch are recorded and returned all at once. This allows to reduce the amount of
+ * interrupts sent to the SoC, and allows the SoC to switch to a lower power state (Idle) while
+ * the sensor is capturing and batching data.
+ * <p>
+ * Registering to a sensor in batch mode will not prevent the SoC from going to suspend mode. In
+ * this case, the sensor will continue to gather events and store it in a hardware FIFO. If the
+ * FIFO gets full before the AP wakes up again, some events will be lost, as the older events
+ * get overwritten by new events in the hardware FIFO. This can be avoided by holding a wake
+ * lock. If the application holds a wake lock, the SoC will not go to suspend mode, so no events
+ * will be lost, as the events will be reported before the FIFO gets full.
+ * </p>
+ * <p>
+ * Batching is always best effort. If a different application requests updates in continuous
+ * mode, this application will also get events in continuous mode. Batch mode updates can be
+ * unregistered by calling {@link #unregisterListener(SensorEventListener)}.
+ * </p>
+ * <p class="note">
+ * </p>
+ * Note: Don't use this method with a one shot trigger sensor such as
+ * {@link Sensor#TYPE_SIGNIFICANT_MOTION}. Use
+ * {@link #requestTriggerSensor(TriggerEventListener, Sensor)} instead. </p>
+ *
+ * @param listener A {@link android.hardware.SensorEventListener SensorEventListener} object
+ * that will receive the sensor events.
+ * @param sensor The {@link android.hardware.Sensor Sensor} to register to.
+ * @param rate The desired delay between two consecutive events in microseconds. This is only a
+ * hint to the system. Events may be received faster or slower than the specified
+ * rate. Usually events are received faster. Can be one of
+ * {@link #SENSOR_DELAY_NORMAL}, {@link #SENSOR_DELAY_UI},
+ * {@link #SENSOR_DELAY_GAME}, {@link #SENSOR_DELAY_FASTEST} or the delay in
+ * microseconds.
+ * @param maxBatchReportLatency An event in the batch can be delayed by at most
+ * maxBatchReportLatency microseconds. More events can be batched if this value is
+ * large. If this is set to zero, batch mode is disabled and events are delivered in
+ * continuous mode as soon as they are available which is equivalent to calling
+ * {@link #registerListener(SensorEventListener, Sensor, int)}.
+ * @param reservedFlags Always set to Zero.
+ * @param flushCompleteListener A {@link android.hardware.FlushCompleteListener
+ * FlushCompleteListener} object which is called when any application calls flush()
+ * on this sensor and all the events in the batch at the time of calling flush() are
+ * successfully delivered to the listeners.
+ * @return true if batch mode is successfully enabled for this sensor, false otherwise.
+ * @see #registerListener(SensorEventListener, Sensor, int)
+ * @see #unregisterListener(SensorEventListener)
+ * @see #flush(Sensor)
+ * @throws IllegalArgumentException when sensor or listener is null or a trigger sensor.
+ * @hide
+ */
+ public boolean registerListener(SensorEventListener listener, Sensor sensor, int rateUs,
+ int maxBatchReportLatencyUs, int reservedFlags,
+ FlushCompleteListener flushCompleteListener) {
+ int delay = getDelay(rateUs);
+ return registerListenerImpl(listener, sensor, delay, null, maxBatchReportLatencyUs,
+ reservedFlags, flushCompleteListener);
+ }
+
+ /**
+ * Registers a {@link android.hardware.SensorEventListener SensorEventListener} for the given
+ * sensor. Events are delivered in continuous mode as soon as they are available.
*
* <p class="note"></p>
* Note: Don't use this method with a one shot trigger sensor such as
@@ -655,31 +719,55 @@
return false;
}
- int delay = -1;
- switch (rate) {
- case SENSOR_DELAY_FASTEST:
- delay = 0;
- break;
- case SENSOR_DELAY_GAME:
- delay = 20000;
- break;
- case SENSOR_DELAY_UI:
- delay = 66667;
- break;
- case SENSOR_DELAY_NORMAL:
- delay = 200000;
- break;
- default:
- delay = rate;
- break;
- }
+ int delay = getDelay(rate);
+ return registerListenerImpl(listener, sensor, delay, handler, 0, 0, null);
+ }
- return registerListenerImpl(listener, sensor, delay, handler);
+ /**
+ * Enables batch mode for a sensor with the given rate and maxBatchReportLatency.
+ * @param handler
+ * The {@link android.os.Handler Handler} the
+ * {@link android.hardware.SensorEvent sensor events} will be
+ * delivered to.
+ *
+ * @see #registerListener(SensorEventListener, Sensor, int, int, int, FlushCompleteListener)
+ * @hide
+ */
+ public boolean registerListener(SensorEventListener listener, Sensor sensor, int rateUs,
+ int maxBatchReportLatencyUs, int reservedFlags, Handler handler,
+ FlushCompleteListener flushCompleteListener) {
+ int delayUs = getDelay(rateUs);
+ return registerListenerImpl(listener, sensor, delayUs, handler, maxBatchReportLatencyUs,
+ reservedFlags, flushCompleteListener);
}
/** @hide */
protected abstract boolean registerListenerImpl(SensorEventListener listener, Sensor sensor,
- int delay, Handler handler);
+ int delayUs, Handler handler, int maxBatchReportLatencyUs, int reservedFlags,
+ FlushCompleteListener flushCompleteListener);
+
+
+ /**
+ * Flushes the batch FIFO of the given sensor. If there are events in the FIFO of this sensor,
+ * they are returned as if the batch timeout has expired. Events are returned in the
+ * usual way through the SensorEventListener. This call doesn't effect the batch timeout for
+ * this sensor. This call is asynchronous and returns immediately. FlushCompleteListener is
+ * called after all the events in the batch at the time of calling this method have been
+ * delivered successfully.
+ * @param sensor
+ * The {@link android.hardware.Sensor Sensor} to flush.
+ * @return true if the flush is initiated successfully. false if the sensor isn't active
+ * i.e no application is registered for updates from this sensor.
+ * @see #registerListener(SensorEventListener, Sensor, int, int, int, FlushCompleteListener)
+ * @throws IllegalArgumentException when sensor is null or a trigger sensor.
+ * @hide
+ */
+ public boolean flush(Sensor sensor) {
+ return flushImpl(sensor);
+ }
+
+ /** @hide */
+ protected abstract boolean flushImpl(Sensor sensor);
/**
* <p>
@@ -1079,15 +1167,15 @@
* <p>
* All three angles above are in <b>radians</b> and <b>positive</b> in the
* <b>counter-clockwise</b> direction.
- *
+ *
* @param R
* rotation matrix see {@link #getRotationMatrix}.
- *
+ *
* @param values
* an array of 3 floats to hold the result.
- *
+ *
* @return The array values passed as argument.
- *
+ *
* @see #getRotationMatrix(float[], float[], float[], float[])
* @see GeomagneticField
*/
@@ -1407,4 +1495,26 @@
return mLegacySensorManager;
}
}
+
+ private static int getDelay(int rate) {
+ int delay = -1;
+ switch (rate) {
+ case SENSOR_DELAY_FASTEST:
+ delay = 0;
+ break;
+ case SENSOR_DELAY_GAME:
+ delay = 20000;
+ break;
+ case SENSOR_DELAY_UI:
+ delay = 66667;
+ break;
+ case SENSOR_DELAY_NORMAL:
+ delay = 200000;
+ break;
+ default:
+ delay = rate;
+ break;
+ }
+ return delay;
+ }
}
diff --git a/core/java/android/hardware/SystemSensorManager.java b/core/java/android/hardware/SystemSensorManager.java
index 852cf4a..9747f0d 100644
--- a/core/java/android/hardware/SystemSensorManager.java
+++ b/core/java/android/hardware/SystemSensorManager.java
@@ -93,30 +93,35 @@
/** @hide */
@Override
protected boolean registerListenerImpl(SensorEventListener listener, Sensor sensor,
- int delay, Handler handler)
- {
+ int delayUs, Handler handler, int maxBatchReportLatencyUs, int reservedFlags,
+ FlushCompleteListener flushCompleteListener) {
+ if (sensor == null) throw new IllegalArgumentException("sensor cannot be null");
+ if (listener == null) throw new IllegalArgumentException("listener cannot be null");
+ if (reservedFlags != 0) throw new IllegalArgumentException("reservedFlags should be zero");
+ if (delayUs < 0) throw new IllegalArgumentException("rateUs should be positive");
+ if (maxBatchReportLatencyUs < 0)
+ throw new IllegalArgumentException("maxBatchReportLatencyUs should be positive");
+ // Trigger Sensors should use the requestTriggerSensor call.
+ if (Sensor.getReportingMode(sensor) == Sensor.REPORTING_MODE_ONE_SHOT)
+ throw new IllegalArgumentException("Trigger Sensors cannot use registerListener");
+
// Invariants to preserve:
// - one Looper per SensorEventListener
// - one Looper per SensorEventQueue
// We map SensorEventListener to a SensorEventQueue, which holds the looper
- if (sensor == null) throw new IllegalArgumentException("sensor cannot be null");
-
- // Trigger Sensors should use the requestTriggerSensor call.
- if (Sensor.getReportingMode(sensor) == Sensor.REPORTING_MODE_ONE_SHOT) return false;
-
synchronized (mSensorListeners) {
SensorEventQueue queue = mSensorListeners.get(listener);
if (queue == null) {
Looper looper = (handler != null) ? handler.getLooper() : mMainLooper;
- queue = new SensorEventQueue(listener, looper, this);
- if (!queue.addSensor(sensor, delay)) {
+ queue = new SensorEventQueue(listener, looper, this, flushCompleteListener);
+ if (!queue.addSensor(sensor, delayUs, maxBatchReportLatencyUs, reservedFlags)) {
queue.dispose();
return false;
}
mSensorListeners.put(listener, queue);
return true;
} else {
- return queue.addSensor(sensor, delay);
+ return queue.addSensor(sensor, delayUs, maxBatchReportLatencyUs, reservedFlags);
}
}
}
@@ -157,14 +162,14 @@
TriggerEventQueue queue = mTriggerListeners.get(listener);
if (queue == null) {
queue = new TriggerEventQueue(listener, mMainLooper, this);
- if (!queue.addSensor(sensor, 0)) {
+ if (!queue.addSensor(sensor, 0, 0, 0)) {
queue.dispose();
return false;
}
mTriggerListeners.put(listener, queue);
return true;
} else {
- return queue.addSensor(sensor, 0);
+ return queue.addSensor(sensor, 0, 0, 0);
}
}
}
@@ -195,6 +200,18 @@
}
}
+ protected boolean flushImpl(Sensor sensor) {
+ if (sensor == null) throw new IllegalArgumentException("sensor cannot be null");
+ if(Sensor.getReportingMode(sensor) == Sensor.REPORTING_MODE_ONE_SHOT)
+ throw new IllegalArgumentException("Trigger Sensors cannot call flush");
+
+ FlushEventQueue queue = new FlushEventQueue(mMainLooper, this);
+ if (queue.flushSensor(sensor) != 0) {
+ return false;
+ }
+ return true;
+ }
+
/*
* BaseEventQueue is the communication channel with the sensor service,
* SensorEventQueue, TriggerEventQueue are subclases and there is one-to-one mapping between
@@ -202,11 +219,12 @@
*/
private static abstract class BaseEventQueue {
private native int nativeInitBaseEventQueue(BaseEventQueue eventQ, MessageQueue msgQ,
-
float[] scratch);
- private static native int nativeEnableSensor(int eventQ, int handle, int us);
+ private static native int nativeEnableSensor(int eventQ, int handle, int rateUs,
+ int maxBatchReportLatencyUs, int reservedFlags);
private static native int nativeDisableSensor(int eventQ, int handle);
private static native void nativeDestroySensorEventQueue(int eventQ);
+ private static native int nativeFlushSensor(int eventQ, int handle);
private int nSensorEventQueue;
private final SparseBooleanArray mActiveSensors = new SparseBooleanArray();
protected final SparseIntArray mSensorAccuracies = new SparseIntArray();
@@ -225,7 +243,8 @@
dispose(false);
}
- public boolean addSensor(Sensor sensor, int delay) {
+ public boolean addSensor(
+ Sensor sensor, int delayUs, int maxBatchReportLatencyUs, int reservedFlags) {
// Check if already present.
int handle = sensor.getHandle();
if (mActiveSensors.get(handle)) return false;
@@ -233,9 +252,13 @@
// Get ready to receive events before calling enable.
mActiveSensors.put(handle, true);
addSensorEvent(sensor);
- if (enableSensor(sensor, delay) != 0) {
- removeSensor(sensor, false);
- return false;
+ if (enableSensor(sensor, delayUs, maxBatchReportLatencyUs, reservedFlags) != 0) {
+ // Try continuous mode if batching fails.
+ if (maxBatchReportLatencyUs == 0 ||
+ maxBatchReportLatencyUs > 0 && enableSensor(sensor, delayUs, 0, 0) != 0) {
+ removeSensor(sensor, false);
+ return false;
+ }
}
return true;
}
@@ -268,6 +291,12 @@
return false;
}
+ public int flushSensor(Sensor sensor) {
+ if (nSensorEventQueue == 0) throw new NullPointerException();
+ if (sensor == null) throw new NullPointerException();
+ return nativeFlushSensor(nSensorEventQueue, sensor.getHandle());
+ }
+
public boolean hasSensors() {
// no more sensors are set
return mActiveSensors.indexOfValue(true) >= 0;
@@ -295,11 +324,14 @@
}
}
- private int enableSensor(Sensor sensor, int us) {
+ private int enableSensor(
+ Sensor sensor, int rateUs, int maxBatchReportLatencyUs, int reservedFlags) {
if (nSensorEventQueue == 0) throw new NullPointerException();
if (sensor == null) throw new NullPointerException();
- return nativeEnableSensor(nSensorEventQueue, sensor.getHandle(), us);
+ return nativeEnableSensor(nSensorEventQueue, sensor.getHandle(), rateUs,
+ maxBatchReportLatencyUs, reservedFlags);
}
+
private int disableSensor(Sensor sensor) {
if (nSensorEventQueue == 0) throw new NullPointerException();
if (sensor == null) throw new NullPointerException();
@@ -307,6 +339,7 @@
}
protected abstract void dispatchSensorEvent(int handle, float[] values, int accuracy,
long timestamp);
+ protected abstract void dispatchFlushCompleteEvent(int handle);
protected abstract void addSensorEvent(Sensor sensor);
protected abstract void removeSensorEvent(Sensor sensor);
@@ -314,12 +347,14 @@
static final class SensorEventQueue extends BaseEventQueue {
private final SensorEventListener mListener;
+ private final FlushCompleteListener mFlushCompleteListener;
private final SparseArray<SensorEvent> mSensorsEvents = new SparseArray<SensorEvent>();
public SensorEventQueue(SensorEventListener listener, Looper looper,
- SystemSensorManager manager) {
+ SystemSensorManager manager, FlushCompleteListener flushCompleteListener) {
super(looper, manager);
mListener = listener;
+ mFlushCompleteListener = flushCompleteListener;
}
public void addSensorEvent(Sensor sensor) {
@@ -370,6 +405,15 @@
}
mListener.onSensorChanged(t);
}
+
+ @SuppressWarnings("unused")
+ protected void dispatchFlushCompleteEvent(int handle) {
+ final Sensor sensor = sHandleToSensor.get(handle);
+ if (mFlushCompleteListener != null) {
+ mFlushCompleteListener.onFlushCompleted(sensor);
+ }
+ return;
+ }
}
static final class TriggerEventQueue extends BaseEventQueue {
@@ -415,5 +459,35 @@
mListener.onTrigger(t);
}
+
+ @SuppressWarnings("unused")
+ protected void dispatchFlushCompleteEvent(int handle) {
+ }
+ }
+
+ static final class FlushEventQueue extends BaseEventQueue {
+ public FlushEventQueue(Looper looper, SystemSensorManager manager) {
+ super(looper, manager);
+ }
+
+ @SuppressWarnings("unused")
+ @Override
+ protected void dispatchSensorEvent(int handle, float[] values, int accuracy,
+ long timestamp) {
+ }
+
+ @Override
+ @SuppressWarnings("unused")
+ protected void addSensorEvent(Sensor sensor) {
+ }
+
+ @Override
+ @SuppressWarnings("unused")
+ protected void removeSensorEvent(Sensor sensor) {
+ }
+
+ @SuppressWarnings("unused")
+ protected void dispatchFlushCompleteEvent(int handle) {
+ }
}
}
diff --git a/core/java/android/hardware/camera2/impl/CameraDevice.java b/core/java/android/hardware/camera2/impl/CameraDevice.java
index 64e4dc9..86a073f 100644
--- a/core/java/android/hardware/camera2/impl/CameraDevice.java
+++ b/core/java/android/hardware/camera2/impl/CameraDevice.java
@@ -301,7 +301,9 @@
synchronized (mLock) {
try {
- mRemoteDevice.disconnect();
+ if (mRemoteDevice != null) {
+ mRemoteDevice.disconnect();
+ }
} catch (CameraRuntimeException e) {
throw e.asChecked();
} catch (RemoteException e) {
diff --git a/core/java/android/nfc/INdefPushCallback.aidl b/core/java/android/nfc/IAppCallback.aidl
similarity index 89%
rename from core/java/android/nfc/INdefPushCallback.aidl
rename to core/java/android/nfc/IAppCallback.aidl
index 16771dc..9599308 100644
--- a/core/java/android/nfc/INdefPushCallback.aidl
+++ b/core/java/android/nfc/IAppCallback.aidl
@@ -17,12 +17,14 @@
package android.nfc;
import android.nfc.BeamShareData;
+import android.nfc.Tag;
/**
* @hide
*/
-interface INdefPushCallback
+interface IAppCallback
{
BeamShareData createBeamShareData();
void onNdefPushComplete();
+ void onTagDiscovered(in Tag tag);
}
diff --git a/core/java/android/nfc/INfcAdapter.aidl b/core/java/android/nfc/INfcAdapter.aidl
index 15d0475..8414738 100644
--- a/core/java/android/nfc/INfcAdapter.aidl
+++ b/core/java/android/nfc/INfcAdapter.aidl
@@ -21,10 +21,11 @@
import android.nfc.NdefMessage;
import android.nfc.Tag;
import android.nfc.TechListParcel;
-import android.nfc.INdefPushCallback;
+import android.nfc.IAppCallback;
import android.nfc.INfcAdapterExtras;
import android.nfc.INfcTag;
import android.nfc.INfcCardEmulation;
+import android.os.Bundle;
/**
* @hide
@@ -44,10 +45,10 @@
void setForegroundDispatch(in PendingIntent intent,
in IntentFilter[] filters, in TechListParcel techLists);
- void setNdefPushCallback(in INdefPushCallback callback);
+ void setAppCallback(in IAppCallback callback);
void dispatch(in Tag tag);
- void setReaderMode (IBinder b, int flags);
+ void setReaderMode (IBinder b, IAppCallback callback, int flags, in Bundle extras);
void setP2pModes(int initatorModes, int targetModes);
}
diff --git a/core/java/android/nfc/NfcActivityManager.java b/core/java/android/nfc/NfcActivityManager.java
index d0d943c..77c0234 100644
--- a/core/java/android/nfc/NfcActivityManager.java
+++ b/core/java/android/nfc/NfcActivityManager.java
@@ -19,6 +19,7 @@
import android.app.Activity;
import android.app.Application;
import android.net.Uri;
+import android.nfc.NfcAdapter.ReaderCallback;
import android.os.Binder;
import android.os.Bundle;
import android.os.RemoteException;
@@ -36,7 +37,7 @@
*
* @hide
*/
-public final class NfcActivityManager extends INdefPushCallback.Stub
+public final class NfcActivityManager extends IAppCallback.Stub
implements Application.ActivityLifecycleCallbacks {
static final String TAG = NfcAdapter.TAG;
static final Boolean DBG = false;
@@ -113,6 +114,8 @@
Uri[] uris = null;
int flags = 0;
int readerModeFlags = 0;
+ NfcAdapter.ReaderCallback readerCallback = null;
+ Bundle readerModeExtras = null;
Binder token;
public NfcActivityState(Activity activity) {
@@ -197,17 +200,20 @@
mDefaultEvent = new NfcEvent(mAdapter);
}
- public void enableReaderMode(Activity activity, int flags) {
+ public void enableReaderMode(Activity activity, ReaderCallback callback, int flags,
+ Bundle extras) {
boolean isResumed;
Binder token;
synchronized (NfcActivityManager.this) {
NfcActivityState state = getActivityState(activity);
+ state.readerCallback = callback;
state.readerModeFlags = flags;
+ state.readerModeExtras = extras;
token = state.token;
isResumed = state.resumed;
}
if (isResumed) {
- setReaderMode(token, flags);
+ setReaderMode(token, flags, extras);
}
}
@@ -216,20 +222,22 @@
Binder token;
synchronized (NfcActivityManager.this) {
NfcActivityState state = getActivityState(activity);
+ state.readerCallback = null;
state.readerModeFlags = 0;
+ state.readerModeExtras = null;
token = state.token;
isResumed = state.resumed;
}
if (isResumed) {
- setReaderMode(token, 0);
+ setReaderMode(token, 0, null);
}
}
- public void setReaderMode(Binder token, int flags) {
+ public void setReaderMode(Binder token, int flags, Bundle extras) {
if (DBG) Log.d(TAG, "Setting reader mode");
try {
- NfcAdapter.sService.setReaderMode(token, flags);
+ NfcAdapter.sService.setReaderMode(token, this, flags, extras);
} catch (RemoteException e) {
mAdapter.attemptDeadServiceRecovery(e);
}
@@ -302,12 +310,12 @@
}
/**
- * Request or unrequest NFC service callbacks for NDEF push.
+ * Request or unrequest NFC service callbacks.
* Makes IPC call - do not hold lock.
*/
void requestNfcServiceCallback() {
try {
- NfcAdapter.sService.setNdefPushCallback(this);
+ NfcAdapter.sService.setAppCallback(this);
} catch (RemoteException e) {
mAdapter.attemptDeadServiceRecovery(e);
}
@@ -375,6 +383,22 @@
}
}
+ @Override
+ public void onTagDiscovered(Tag tag) throws RemoteException {
+ NfcAdapter.ReaderCallback callback;
+ synchronized (NfcActivityManager.this) {
+ NfcActivityState state = findResumedActivityState();
+ if (state == null) return;
+
+ callback = state.readerCallback;
+ }
+
+ // Make callback without lock
+ if (callback != null) {
+ callback.onTagDiscovered(tag);
+ }
+
+ }
/** Callback from Activity life-cycle, on main thread */
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) { /* NO-OP */ }
@@ -387,6 +411,7 @@
@Override
public void onActivityResumed(Activity activity) {
int readerModeFlags = 0;
+ Bundle readerModeExtras = null;
Binder token;
synchronized (NfcActivityManager.this) {
NfcActivityState state = findActivityState(activity);
@@ -395,9 +420,10 @@
state.resumed = true;
token = state.token;
readerModeFlags = state.readerModeFlags;
+ readerModeExtras = state.readerModeExtras;
}
if (readerModeFlags != 0) {
- setReaderMode(token, readerModeFlags);
+ setReaderMode(token, readerModeFlags, readerModeExtras);
}
requestNfcServiceCallback();
}
@@ -417,7 +443,7 @@
}
if (readerModeFlagsSet) {
// Restore default p2p modes
- setReaderMode(token, 0);
+ setReaderMode(token, 0, null);
}
}
@@ -441,4 +467,5 @@
}
}
}
+
}
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index fa0c1f6..2a18900 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -33,6 +33,7 @@
import android.nfc.tech.Ndef;
import android.nfc.tech.NfcA;
import android.nfc.tech.NfcF;
+import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -196,42 +197,42 @@
public static final int STATE_TURNING_OFF = 4;
/**
- * Flag for use with {@link #enableReaderMode(Activity, int)}.
+ * Flag for use with {@link #enableReaderMode(Activity, ReaderCallback, int, Bundle)}.
* <p>
* Setting this flag enables polling for Nfc-A technology.
*/
public static final int FLAG_READER_NFC_A = 0x1;
/**
- * Flag for use with {@link #enableReaderMode(Activity, int)}.
+ * Flag for use with {@link #enableReaderMode(Activity, ReaderCallback, int, Bundle)}.
* <p>
* Setting this flag enables polling for Nfc-B technology.
*/
public static final int FLAG_READER_NFC_B = 0x2;
/**
- * Flag for use with {@link #enableReaderMode(Activity, int)}.
+ * Flag for use with {@link #enableReaderMode(Activity, ReaderCallback, int, Bundle)}.
* <p>
* Setting this flag enables polling for Nfc-F technology.
*/
public static final int FLAG_READER_NFC_F = 0x4;
/**
- * Flag for use with {@link #enableReaderMode(Activity, int)}.
+ * Flag for use with {@link #enableReaderMode(Activity, ReaderCallback, int, Bundle)}.
* <p>
* Setting this flag enables polling for Nfc-V (ISO15693) technology.
*/
public static final int FLAG_READER_NFC_V = 0x8;
/**
- * Flag for use with {@link #enableReaderMode(Activity, int)}.
+ * Flag for use with {@link #enableReaderMode(Activity, ReaderCallback, int, Bundle)}.
* <p>
* Setting this flag enables polling for Kovio technology.
*/
public static final int FLAG_READER_KOVIO = 0x10;
/**
- * Flag for use with {@link #enableReaderMode(Activity, int)}.
+ * Flag for use with {@link #enableReaderMode(Activity, ReaderCallback, int, Bundle)}.
* <p>
* Setting this flag allows the caller to prevent the
* platform from performing an NDEF check on the tags it
@@ -239,6 +240,23 @@
*/
public static final int FLAG_READER_SKIP_NDEF_CHECK = 0x80;
+ /**
+ * Flag for use with {@link #enableReaderMode(Activity, ReaderCallback, int, Bundle)}.
+ * <p>
+ * Setting this flag allows the caller to prevent the
+ * platform from playing sounds when it discovers a tag.
+ */
+ public static final int FLAG_READER_NO_PLATFORM_SOUNDS = 0x100;
+
+ /**
+ * Int Extra for use with {@link #enableReaderMode(Activity, ReaderCallback, int, Bundle)}.
+ * <p>
+ * Setting this integer extra allows the calling application to specify
+ * the delay that the platform will use for performing presence checks
+ * on any discovered tag.
+ */
+ public static final String EXTRA_READER_PRESENCE_CHECK_DELAY = "presence";
+
/** @hide */
public static final int FLAG_NDEF_PUSH_NO_CONFIRM = 0x1;
@@ -291,6 +309,14 @@
final Context mContext;
/**
+ * A callback to be invoked when the system has found a tag in
+ * reader mode.
+ */
+ public interface ReaderCallback {
+ public void onTagDiscovered(Tag tag);
+ }
+
+ /**
* A callback to be invoked when the system successfully delivers your {@link NdefMessage}
* to another device.
* @see #setOnNdefPushCompleteCallback
@@ -1167,19 +1193,18 @@
* {@link Ndef} tag technology from being enumerated on the tag, and that
* NDEF-based tag dispatch will not be functional.
*
- * <p>It is recommended to combine this method with
- * {@link #enableForegroundDispatch(Activity, PendingIntent, IntentFilter[], String[][])
- * to ensure that tags are delivered to this activity.
- *
* <p>For interacting with tags that are emulated on another Android device
* using Android's host-based card-emulation, the recommended flags are
* {@link #FLAG_READER_NFC_A} and {@link #FLAG_READER_SKIP_NDEF_CHECK}.
*
* @param activity the Activity that requests the adapter to be in reader mode
+ * @param callback the callback to be called when a tag is discovered
* @param flags Flags indicating poll technologies and other optional parameters
+ * @param extras Additional extras for configuring reader mode.
*/
- public void enableReaderMode(Activity activity, int flags) {
- mNfcActivityManager.enableReaderMode(activity, flags);
+ public void enableReaderMode(Activity activity, ReaderCallback callback, int flags,
+ Bundle extras) {
+ mNfcActivityManager.enableReaderMode(activity, callback, flags, extras);
}
/**
diff --git a/core/java/android/nfc/cardemulation/ApduServiceInfo.java b/core/java/android/nfc/cardemulation/ApduServiceInfo.java
index b83911a..41c6603 100644
--- a/core/java/android/nfc/cardemulation/ApduServiceInfo.java
+++ b/core/java/android/nfc/cardemulation/ApduServiceInfo.java
@@ -105,8 +105,12 @@
if (onHost) {
parser = si.loadXmlMetaData(pm, HostApduService.SERVICE_META_DATA);
if (parser == null) {
- throw new XmlPullParserException("No " + HostApduService.SERVICE_META_DATA +
- " meta-data");
+ Log.d(TAG, "Didn't find service meta-data, trying legacy.");
+ parser = si.loadXmlMetaData(pm, HostApduService.OLD_SERVICE_META_DATA);
+ if (parser == null) {
+ throw new XmlPullParserException("No " + HostApduService.SERVICE_META_DATA +
+ " meta-data");
+ }
}
} else {
parser = si.loadXmlMetaData(pm, OffHostApduService.SERVICE_META_DATA);
@@ -170,12 +174,12 @@
com.android.internal.R.styleable.AidGroup_description);
String groupCategory = groupAttrs.getString(
com.android.internal.R.styleable.AidGroup_category);
- if (!CardEmulationManager.CATEGORY_PAYMENT.equals(groupCategory)) {
- groupCategory = CardEmulationManager.CATEGORY_OTHER;
+ if (!CardEmulation.CATEGORY_PAYMENT.equals(groupCategory)) {
+ groupCategory = CardEmulation.CATEGORY_OTHER;
}
currentGroup = mCategoryToGroup.get(groupCategory);
if (currentGroup != null) {
- if (!CardEmulationManager.CATEGORY_OTHER.equals(groupCategory)) {
+ if (!CardEmulation.CATEGORY_OTHER.equals(groupCategory)) {
Log.e(TAG, "Not allowing multiple aid-groups in the " +
groupCategory + " category");
currentGroup = null;
diff --git a/core/java/android/nfc/cardemulation/CardEmulation.java b/core/java/android/nfc/cardemulation/CardEmulation.java
new file mode 100644
index 0000000..3cd7863
--- /dev/null
+++ b/core/java/android/nfc/cardemulation/CardEmulation.java
@@ -0,0 +1,343 @@
+/*
+ * 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.nfc.cardemulation;
+
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
+import android.app.ActivityThread;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager;
+import android.nfc.INfcCardEmulation;
+import android.nfc.NfcAdapter;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.Log;
+
+import java.util.HashMap;
+import java.util.List;
+
+public final class CardEmulation {
+ static final String TAG = "CardEmulation";
+
+ /**
+ * Activity action: ask the user to change the default
+ * card emulation service for a certain category. This will
+ * show a dialog that asks the user whether he wants to
+ * replace the current default service with the service
+ * identified with the ComponentName specified in
+ * {@link #EXTRA_SERVICE_COMPONENT}, for the category
+ * specified in {@link #EXTRA_CATEGORY}
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_CHANGE_DEFAULT =
+ "android.nfc.cardemulation.action.ACTION_CHANGE_DEFAULT";
+
+ /**
+ * The category extra for {@link #ACTION_CHANGE_DEFAULT}
+ *
+ * @see #ACTION_CHANGE_DEFAULT
+ */
+ public static final String EXTRA_CATEGORY = "category";
+
+ /**
+ * The ComponentName object passed in as a parcelable
+ * extra for {@link #ACTION_CHANGE_DEFAULT}
+ *
+ * @see #ACTION_CHANGE_DEFAULT
+ */
+ public static final String EXTRA_SERVICE_COMPONENT = "component";
+
+ /**
+ * The payment category can be used to indicate that an AID
+ * represents a payment application.
+ */
+ public static final String CATEGORY_PAYMENT = "payment";
+
+ /**
+ * If an AID group does not contain a category, or the
+ * specified category is not defined by the platform version
+ * that is parsing the AID group, all AIDs in the group will
+ * automatically be categorized under the {@link #CATEGORY_OTHER}
+ * category.
+ */
+ public static final String CATEGORY_OTHER = "other";
+
+ /**
+ * Return value for {@link #getSelectionModeForCategory(String)}.
+ *
+ * <p>In this mode, the user has set a default service for this
+ * AID category. If a remote reader selects any of the AIDs
+ * that the default service has registered in this category,
+ * that service will automatically be bound to to handle
+ * the transaction.
+ *
+ * <p>There are still cases where a service that is
+ * not the default for a category can selected:
+ * <p>
+ * If a remote reader selects an AID in this category
+ * that is not handled by the default service, and there is a set
+ * of other services {S} that do handle this AID, the
+ * user is asked if he wants to use any of the services in
+ * {S} instead.
+ * <p>
+ * As a special case, if the size of {S} is one, containing a single service X,
+ * and all AIDs X has registered in this category are not
+ * registered by any other service, then X will be
+ * selected automatically without asking the user.
+ * <p>Example:
+ * <ul>
+ * <li>Service A registers AIDs "1", "2" and "3" in the category
+ * <li>Service B registers AIDs "3" and "4" in the category
+ * <li>Service C registers AIDs "5" and "6" in the category
+ * </ul>
+ * In this case, the following will happen when service A
+ * is the default:
+ * <ul>
+ * <li>Reader selects AID "1", "2" or "3": service A is invoked automatically
+ * <li>Reader selects AID "4": the user is asked to confirm he
+ * wants to use service B, because its AIDs overlap with service A.
+ * <li>Reader selects AID "5" or "6": service C is invoked automatically,
+ * because all AIDs it has asked for are only registered by C,
+ * and there is no overlap.
+ * </ul>
+ *
+ */
+ public static final int SELECTION_MODE_PREFER_DEFAULT = 0;
+
+ /**
+ * Return value for {@link #getSelectionModeForCategory(String)}.
+ *
+ * <p>In this mode, whenever an AID of this category is selected,
+ * the user is asked which service he wants to use to handle
+ * the transaction, even if there is only one matching service.
+ */
+ public static final int SELECTION_MODE_ALWAYS_ASK = 1;
+
+ /**
+ * Return value for {@link #getSelectionModeForCategory(String)}.
+ *
+ * <p>In this mode, the user will only be asked to select a service
+ * if the selected AID has been registered by multiple applications.
+ */
+ public static final int SELECTION_MODE_ASK_IF_CONFLICT = 2;
+
+ static boolean sIsInitialized = false;
+ static HashMap<Context, CardEmulation> sCardEmus = new HashMap();
+ static INfcCardEmulation sService;
+
+ final Context mContext;
+
+ private CardEmulation(Context context, INfcCardEmulation service) {
+ mContext = context.getApplicationContext();
+ sService = service;
+ }
+
+ public static synchronized CardEmulation getInstance(NfcAdapter adapter) {
+ if (adapter == null) throw new NullPointerException("NfcAdapter is null");
+ Context context = adapter.getContext();
+ if (context == null) {
+ Log.e(TAG, "NfcAdapter context is null.");
+ throw new UnsupportedOperationException();
+ }
+ if (!sIsInitialized) {
+ IPackageManager pm = ActivityThread.getPackageManager();
+ if (pm == null) {
+ Log.e(TAG, "Cannot get PackageManager");
+ throw new UnsupportedOperationException();
+ }
+ try {
+ if (!pm.hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION)) {
+ Log.e(TAG, "This device does not support card emulation");
+ throw new UnsupportedOperationException();
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "PackageManager query failed.");
+ throw new UnsupportedOperationException();
+ }
+ sIsInitialized = true;
+ }
+ CardEmulation manager = sCardEmus.get(context);
+ if (manager == null) {
+ // Get card emu service
+ INfcCardEmulation service = adapter.getCardEmulationService();
+ manager = new CardEmulation(context, service);
+ sCardEmus.put(context, manager);
+ }
+ return manager;
+ }
+
+ /**
+ * Allows an application to query whether a service is currently
+ * the default service to handle a card emulation category.
+ *
+ * <p>Note that if {@link #getSelectionModeForCategory(String)}
+ * returns {@link #SELECTION_MODE_ALWAYS_ASK}, this method will always
+ * return false.
+ *
+ * @param service The ComponentName of the service
+ * @param category The category
+ * @return whether service is currently the default service for the category.
+ */
+ public boolean isDefaultServiceForCategory(ComponentName service, String category) {
+ try {
+ return sService.isDefaultServiceForCategory(UserHandle.myUserId(), service, category);
+ } catch (RemoteException e) {
+ // Try one more time
+ recoverService();
+ if (sService == null) {
+ Log.e(TAG, "Failed to recover CardEmulationService.");
+ return false;
+ }
+ try {
+ return sService.isDefaultServiceForCategory(UserHandle.myUserId(), service,
+ category);
+ } catch (RemoteException ee) {
+ Log.e(TAG, "Failed to recover CardEmulationService.");
+ return false;
+ }
+ }
+ }
+
+ /**
+ *
+ * Allows an application to query whether a service is currently
+ * the default handler for a specified ISO7816-4 Application ID.
+ *
+ * @param service The ComponentName of the service
+ * @param aid The ISO7816-4 Application ID
+ * @return
+ */
+ public boolean isDefaultServiceForAid(ComponentName service, String aid) {
+ try {
+ return sService.isDefaultServiceForAid(UserHandle.myUserId(), service, aid);
+ } catch (RemoteException e) {
+ // Try one more time
+ recoverService();
+ if (sService == null) {
+ Log.e(TAG, "Failed to recover CardEmulationService.");
+ return false;
+ }
+ try {
+ return sService.isDefaultServiceForAid(UserHandle.myUserId(), service, aid);
+ } catch (RemoteException ee) {
+ Log.e(TAG, "Failed to reach CardEmulationService.");
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Returns the application selection mode for the passed in category.
+ * Valid return values are:
+ * <p>{@link #SELECTION_MODE_PREFER_DEFAULT} the user has requested a default
+ * application for this category, which will be preferred.
+ * <p>{@link #SELECTION_MODE_ALWAYS_ASK} the user has requested to be asked
+ * every time what app he would like to use in this category.
+ * <p>{@link #SELECTION_MODE_ASK_IF_CONFLICT} the user will only be asked
+ * to pick a service if there is a conflict.
+ * @param category The category, for example {@link #CATEGORY_PAYMENT}
+ * @return
+ */
+ public int getSelectionModeForCategory(String category) {
+ if (CATEGORY_PAYMENT.equals(category)) {
+ String defaultComponent = Settings.Secure.getString(mContext.getContentResolver(),
+ Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT);
+ if (defaultComponent != null) {
+ return SELECTION_MODE_PREFER_DEFAULT;
+ } else {
+ return SELECTION_MODE_ALWAYS_ASK;
+ }
+ } else {
+ // All other categories are in "only ask if conflict" mode
+ return SELECTION_MODE_ASK_IF_CONFLICT;
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public boolean setDefaultServiceForCategory(ComponentName service, String category) {
+ try {
+ return sService.setDefaultServiceForCategory(UserHandle.myUserId(), service, category);
+ } catch (RemoteException e) {
+ // Try one more time
+ recoverService();
+ if (sService == null) {
+ Log.e(TAG, "Failed to recover CardEmulationService.");
+ return false;
+ }
+ try {
+ return sService.setDefaultServiceForCategory(UserHandle.myUserId(), service,
+ category);
+ } catch (RemoteException ee) {
+ Log.e(TAG, "Failed to reach CardEmulationService.");
+ return false;
+ }
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public boolean setDefaultForNextTap(ComponentName service) {
+ try {
+ return sService.setDefaultForNextTap(UserHandle.myUserId(), service);
+ } catch (RemoteException e) {
+ // Try one more time
+ recoverService();
+ if (sService == null) {
+ Log.e(TAG, "Failed to recover CardEmulationService.");
+ return false;
+ }
+ try {
+ return sService.setDefaultForNextTap(UserHandle.myUserId(), service);
+ } catch (RemoteException ee) {
+ Log.e(TAG, "Failed to reach CardEmulationService.");
+ return false;
+ }
+ }
+ }
+ /**
+ * @hide
+ */
+ public List<ApduServiceInfo> getServices(String category) {
+ try {
+ return sService.getServices(UserHandle.myUserId(), category);
+ } catch (RemoteException e) {
+ // Try one more time
+ recoverService();
+ if (sService == null) {
+ Log.e(TAG, "Failed to recover CardEmulationService.");
+ return null;
+ }
+ try {
+ return sService.getServices(UserHandle.myUserId(), category);
+ } catch (RemoteException ee) {
+ Log.e(TAG, "Failed to reach CardEmulationService.");
+ return null;
+ }
+ }
+ }
+
+ void recoverService() {
+ NfcAdapter adapter = NfcAdapter.getDefaultAdapter(mContext);
+ sService = adapter.getCardEmulationService();
+ }
+}
diff --git a/core/java/android/nfc/cardemulation/CardEmulationManager.java b/core/java/android/nfc/cardemulation/CardEmulationManager.java
index 9d60c73..124ea1c 100644
--- a/core/java/android/nfc/cardemulation/CardEmulationManager.java
+++ b/core/java/android/nfc/cardemulation/CardEmulationManager.java
@@ -33,6 +33,10 @@
import java.util.HashMap;
import java.util.List;
+/**
+ * TODO Remove when calling .apks are upgraded
+ * @hide
+ */
public final class CardEmulationManager {
static final String TAG = "CardEmulationManager";
diff --git a/core/java/android/nfc/cardemulation/HostApduService.java b/core/java/android/nfc/cardemulation/HostApduService.java
index ae94b2f..174acc0 100644
--- a/core/java/android/nfc/cardemulation/HostApduService.java
+++ b/core/java/android/nfc/cardemulation/HostApduService.java
@@ -40,13 +40,31 @@
*/
@SdkConstant(SdkConstantType.SERVICE_ACTION)
public static final String SERVICE_INTERFACE =
- "android.nfc.HostApduService";
+ "android.nfc.cardemulation.action.HOST_APDU_SERVICE";
/**
* The name of the meta-data element that contains
* more information about this service.
*/
- public static final String SERVICE_META_DATA = "android.nfc.HostApduService";
+ public static final String SERVICE_META_DATA =
+ "android.nfc.cardemulation.host_apdu_service";
+
+ /**
+ * The {@link Intent} that must be declared as handled by the service.
+ * TODO Remove
+ * @hide
+ */
+ public static final String OLD_SERVICE_INTERFACE =
+ "android.nfc.HostApduService";
+
+ /**
+ * The name of the meta-data element that contains
+ * more information about this service.
+ *
+ * TODO Remove
+ * @hide
+ */
+ public static final String OLD_SERVICE_META_DATA = "android.nfc.HostApduService";
/**
* Reason for {@link #onDeactivated(int)}.
@@ -63,7 +81,7 @@
* currently active on the logical channel).
*
* <p>Note that this next AID may still be resolved to this
- * service, in which case {@link #processCommandApdu(byte[], int)}
+ * service, in which case {@link #processCommandApdu(byte[], Bundle)}
* will be called again.
*/
public static final int DEACTIVATION_DESELECTED = 1;
@@ -131,7 +149,7 @@
byte[] apdu = dataBundle.getByteArray(KEY_DATA);
if (apdu != null) {
- byte[] responseApdu = processCommandApdu(apdu, 0);
+ byte[] responseApdu = processCommandApdu(apdu, null);
if (responseApdu != null) {
if (mNfcService == null) {
Log.e(TAG, "Response not sent; service was deactivated.");
@@ -230,7 +248,7 @@
* transaction.
*
* <p>Note: this method may be called anywhere between
- * the first {@link #processCommandApdu(byte[], int)}
+ * the first {@link #processCommandApdu(byte[], Bundle)}
* call and a {@link #onDeactivated(int)} call.
*/
public final void notifyUnhandled() {
@@ -290,8 +308,11 @@
* @param flags
* @return a byte-array containing the response APDU, or null if no
* response APDU can be sent at this point.
+ * @hide
*/
- public abstract byte[] processCommandApdu(byte[] commandApdu, int flags);
+ public byte[] processCommandApdu(byte[] commandApdu, int flags) {
+ return null;
+ }
/**
* This method will be called in two possible scenarios:
diff --git a/core/java/android/nfc/cardemulation/OffHostApduService.java b/core/java/android/nfc/cardemulation/OffHostApduService.java
index 79599db..15f63f9 100644
--- a/core/java/android/nfc/cardemulation/OffHostApduService.java
+++ b/core/java/android/nfc/cardemulation/OffHostApduService.java
@@ -42,13 +42,14 @@
*/
@SdkConstant(SdkConstantType.SERVICE_ACTION)
public static final String SERVICE_INTERFACE =
- "android.nfc.OffHostApduService";
+ "android.nfc.cardemulation.action.OFF_HOST_APDU_SERVICE";
/**
* The name of the meta-data element that contains
* more information about this service.
*/
- public static final String SERVICE_META_DATA = "android.nfc.OffHostApduService";
+ public static final String SERVICE_META_DATA =
+ "android.nfc.cardemulation.off_host_apdu_service";
/**
* The Android platform itself will not bind to this service,
diff --git a/core/java/android/printservice/PrinterDiscoverySession.java b/core/java/android/printservice/PrinterDiscoverySession.java
index 1f86ecc..6464cc1 100644
--- a/core/java/android/printservice/PrinterDiscoverySession.java
+++ b/core/java/android/printservice/PrinterDiscoverySession.java
@@ -80,7 +80,7 @@
public abstract class PrinterDiscoverySession {
private static final String LOG_TAG = "PrinterDiscoverySession";
- private static final int MAX_ITEMS_PER_CALLBACK = 100;
+ private static final int MAX_ITEMS_PER_CALLBACK = 50;
private static int sIdCounter = 0;
diff --git a/core/java/android/security/IKeystoreService.java b/core/java/android/security/IKeystoreService.java
index 3d75dc8..bf8d4e5 100644
--- a/core/java/android/security/IKeystoreService.java
+++ b/core/java/android/security/IKeystoreService.java
@@ -244,7 +244,8 @@
return _result;
}
- public int generate(String name, int uid, int flags) throws RemoteException {
+ public int generate(String name, int uid, int keyType, int keySize, int flags,
+ byte[][] args) throws RemoteException {
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
int _result;
@@ -252,7 +253,17 @@
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeString(name);
_data.writeInt(uid);
+ _data.writeInt(keyType);
+ _data.writeInt(keySize);
_data.writeInt(flags);
+ if (args == null) {
+ _data.writeInt(0);
+ } else {
+ _data.writeInt(args.length);
+ for (int i = 0; i < args.length; i++) {
+ _data.writeByteArray(args[i]);
+ }
+ }
mRemote.transact(Stub.TRANSACTION_generate, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
@@ -560,7 +571,8 @@
public int zero() throws RemoteException;
- public int generate(String name, int uid, int flags) throws RemoteException;
+ public int generate(String name, int uid, int keyType, int keySize, int flags, byte[][] args)
+ throws RemoteException;
public int import_key(String name, byte[] data, int uid, int flags) throws RemoteException;
diff --git a/core/java/android/view/ScaleGestureDetector.java b/core/java/android/view/ScaleGestureDetector.java
index 51c5c7b..0bebc04 100644
--- a/core/java/android/view/ScaleGestureDetector.java
+++ b/core/java/android/view/ScaleGestureDetector.java
@@ -18,6 +18,8 @@
import android.content.Context;
import android.content.res.Resources;
+import android.os.Build;
+import android.os.Handler;
import android.os.SystemClock;
import android.util.FloatMath;
@@ -128,6 +130,8 @@
private float mFocusX;
private float mFocusY;
+ private boolean mDoubleTapScales;
+
private float mCurrSpan;
private float mPrevSpan;
private float mInitialSpan;
@@ -148,9 +152,14 @@
private int mTouchHistoryDirection;
private long mTouchHistoryLastAcceptedTime;
private int mTouchMinMajor;
+ private MotionEvent mDoubleTapEvent;
+ private int mDoubleTapMode = DOUBLE_TAP_MODE_NONE;
+ private final Handler mHandler;
private static final long TOUCH_STABILIZE_TIME = 128; // ms
- private static final int TOUCH_MIN_MAJOR = 48; // dp
+ private static final int DOUBLE_TAP_MODE_NONE = 0;
+ private static final int DOUBLE_TAP_MODE_IN_PROGRESS = 1;
+
/**
* Consistency verifier for debugging purposes.
@@ -158,8 +167,37 @@
private final InputEventConsistencyVerifier mInputEventConsistencyVerifier =
InputEventConsistencyVerifier.isInstrumentationEnabled() ?
new InputEventConsistencyVerifier(this, 0) : null;
+ private GestureDetector mGestureDetector;
+ private boolean mEventBeforeOrAboveStartingGestureEvent;
+
+ /**
+ * Creates a ScaleGestureDetector with the supplied listener.
+ * You may only use this constructor from a {@link android.os.Looper Looper} thread.
+ *
+ * @param context the application's context
+ * @param listener the listener invoked for all the callbacks, this must
+ * not be null.
+ *
+ * @throws NullPointerException if {@code listener} is null.
+ */
public ScaleGestureDetector(Context context, OnScaleGestureListener listener) {
+ this(context, listener, null);
+ }
+
+ /**
+ * Creates a ScaleGestureDetector with the supplied listener.
+ * @see android.os.Handler#Handler()
+ *
+ * @param context the application's context
+ * @param listener the listener invoked for all the callbacks, this must
+ * not be null.
+ * @param handler the handler to use for running deferred listener events.
+ *
+ * @throws NullPointerException if {@code listener} is null.
+ */
+ public ScaleGestureDetector(Context context, OnScaleGestureListener listener,
+ Handler handler) {
mContext = context;
mListener = listener;
mSpanSlop = ViewConfiguration.get(context).getScaledTouchSlop() * 2;
@@ -167,8 +205,12 @@
final Resources res = context.getResources();
mTouchMinMajor = res.getDimensionPixelSize(
com.android.internal.R.dimen.config_minScalingTouchMajor);
- mMinSpan = res.getDimensionPixelSize(
- com.android.internal.R.dimen.config_minScalingSpan);
+ mMinSpan = res.getDimensionPixelSize(com.android.internal.R.dimen.config_minScalingSpan);
+ mHandler = handler;
+ // Quick scale is enabled by default after JB_MR2
+ if (context.getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.JELLY_BEAN_MR2) {
+ setQuickScaleEnabled(true);
+ }
}
/**
@@ -263,8 +305,14 @@
final int action = event.getActionMasked();
+ // Forward the event to check for double tap gesture
+ if (mDoubleTapScales) {
+ mGestureDetector.onTouchEvent(event);
+ }
+
final boolean streamComplete = action == MotionEvent.ACTION_UP ||
action == MotionEvent.ACTION_CANCEL;
+
if (action == MotionEvent.ACTION_DOWN || streamComplete) {
// Reset any scale in progress with the listener.
// If it's an ACTION_DOWN we're beginning a new event stream.
@@ -273,6 +321,7 @@
mListener.onScaleEnd(this);
mInProgress = false;
mInitialSpan = 0;
+ mDoubleTapMode = DOUBLE_TAP_MODE_NONE;
}
if (streamComplete) {
@@ -284,21 +333,37 @@
final boolean configChanged = action == MotionEvent.ACTION_DOWN ||
action == MotionEvent.ACTION_POINTER_UP ||
action == MotionEvent.ACTION_POINTER_DOWN;
+
+
final boolean pointerUp = action == MotionEvent.ACTION_POINTER_UP;
final int skipIndex = pointerUp ? event.getActionIndex() : -1;
// Determine focal point
float sumX = 0, sumY = 0;
final int count = event.getPointerCount();
- for (int i = 0; i < count; i++) {
- if (skipIndex == i) continue;
- sumX += event.getX(i);
- sumY += event.getY(i);
- }
final int div = pointerUp ? count - 1 : count;
- final float focusX = sumX / div;
- final float focusY = sumY / div;
+ final float focusX;
+ final float focusY;
+ if (mDoubleTapMode == DOUBLE_TAP_MODE_IN_PROGRESS) {
+ // In double tap mode, the focal pt is always where the double tap
+ // gesture started
+ focusX = mDoubleTapEvent.getX();
+ focusY = mDoubleTapEvent.getY();
+ if (event.getY() < focusY) {
+ mEventBeforeOrAboveStartingGestureEvent = true;
+ } else {
+ mEventBeforeOrAboveStartingGestureEvent = false;
+ }
+ } else {
+ for (int i = 0; i < count; i++) {
+ if (skipIndex == i) continue;
+ sumX += event.getX(i);
+ sumY += event.getY(i);
+ }
+ focusX = sumX / div;
+ focusY = sumY / div;
+ }
addTouchHistory(event);
@@ -320,7 +385,12 @@
// the focal point.
final float spanX = devX * 2;
final float spanY = devY * 2;
- final float span = FloatMath.sqrt(spanX * spanX + spanY * spanY);
+ final float span;
+ if (inDoubleTapMode()) {
+ span = spanY;
+ } else {
+ span = FloatMath.sqrt(spanX * spanX + spanY * spanY);
+ }
// Dispatch begin/end events as needed.
// If the configuration changes, notify the app to reset its current state by beginning
@@ -328,10 +398,11 @@
final boolean wasInProgress = mInProgress;
mFocusX = focusX;
mFocusY = focusY;
- if (mInProgress && (span < mMinSpan || configChanged)) {
+ if (!inDoubleTapMode() && mInProgress && (span < mMinSpan || configChanged)) {
mListener.onScaleEnd(this);
mInProgress = false;
mInitialSpan = span;
+ mDoubleTapMode = DOUBLE_TAP_MODE_NONE;
}
if (configChanged) {
mPrevSpanX = mCurrSpanX = spanX;
@@ -354,6 +425,7 @@
mCurrSpan = span;
boolean updatePrev = true;
+
if (mInProgress) {
updatePrev = mListener.onScale(this);
}
@@ -369,6 +441,34 @@
return true;
}
+
+ private boolean inDoubleTapMode() {
+ return mDoubleTapMode == DOUBLE_TAP_MODE_IN_PROGRESS;
+ }
+
+ /**
+ * Set whether the associated {@link OnScaleGestureListener} should receive onScale callbacks
+ * when the user performs a doubleTap followed by a swipe. Note that this is enabled by default
+ * if the app targets API 19 and newer.
+ * @param scales true to enable quick scaling, false to disable
+ */
+ public void setQuickScaleEnabled(boolean scales) {
+ mDoubleTapScales = scales;
+ if (mDoubleTapScales && mGestureDetector == null) {
+ GestureDetector.SimpleOnGestureListener gestureListener =
+ new GestureDetector.SimpleOnGestureListener() {
+ @Override
+ public boolean onDoubleTap(MotionEvent e) {
+ // Double tap: start watching for a swipe
+ mDoubleTapEvent = e;
+ mDoubleTapMode = DOUBLE_TAP_MODE_IN_PROGRESS;
+ return true;
+ }
+ };
+ mGestureDetector = new GestureDetector(mContext, gestureListener, mHandler);
+ }
+ }
+
/**
* Returns {@code true} if a scale gesture is in progress.
*/
@@ -472,6 +572,12 @@
* @return The current scaling factor.
*/
public float getScaleFactor() {
+ if (inDoubleTapMode() && mEventBeforeOrAboveStartingGestureEvent) {
+ // Drag is moving up; the further away from the gesture
+ // start, the smaller the span should be, the closer,
+ // the larger the span, and therefore the larger the scale
+ return (1 / mCurrSpan) / (1 / mPrevSpan);
+ }
return mPrevSpan > 0 ? mCurrSpan / mPrevSpan : 1;
}
@@ -493,4 +599,4 @@
public long getEventTime() {
return mCurrTime;
}
-}
+}
\ No newline at end of file
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 5b279ec..907290b 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -13256,14 +13256,8 @@
// Keep the DRAWING_CACHE_QUALITY_LOW flag just in case
switch (mViewFlags & DRAWING_CACHE_QUALITY_MASK) {
case DRAWING_CACHE_QUALITY_AUTO:
- quality = Bitmap.Config.ARGB_8888;
- break;
case DRAWING_CACHE_QUALITY_LOW:
- quality = Bitmap.Config.ARGB_8888;
- break;
case DRAWING_CACHE_QUALITY_HIGH:
- quality = Bitmap.Config.ARGB_8888;
- break;
default:
quality = Bitmap.Config.ARGB_8888;
break;
diff --git a/core/java/android/webkit/CallbackProxy.java b/core/java/android/webkit/CallbackProxy.java
index fea6be6..7707392 100644
--- a/core/java/android/webkit/CallbackProxy.java
+++ b/core/java/android/webkit/CallbackProxy.java
@@ -50,7 +50,9 @@
*/
class CallbackProxy extends Handler {
// Logging tag
- private static final String LOGTAG = "CallbackProxy";
+ static final String LOGTAG = "WebViewCallback";
+ // Enables API callback tracing
+ private static final boolean TRACE = DebugFlags.TRACE_CALLBACK;
// Instance of WebViewClient that is the client callback.
private volatile WebViewClient mWebViewClient;
// Instance of WebChromeClient for handling all chrome functions.
@@ -258,6 +260,7 @@
}
boolean override = false;
if (mWebViewClient != null) {
+ if (TRACE) Log.d(LOGTAG, "shouldOverrideUrlLoading=" + overrideUrl);
override = mWebViewClient.shouldOverrideUrlLoading(mWebView.getWebView(),
overrideUrl);
} else {
@@ -307,6 +310,7 @@
String startedUrl = msg.getData().getString("url");
mWebView.onPageStarted(startedUrl);
if (mWebViewClient != null) {
+ if (TRACE) Log.d(LOGTAG, "onPageStarted=" + startedUrl);
mWebViewClient.onPageStarted(mWebView.getWebView(), startedUrl,
(Bitmap) msg.obj);
}
@@ -316,18 +320,21 @@
String finishedUrl = (String) msg.obj;
mWebView.onPageFinished(finishedUrl);
if (mWebViewClient != null) {
+ if (TRACE) Log.d(LOGTAG, "onPageFinished=" + finishedUrl);
mWebViewClient.onPageFinished(mWebView.getWebView(), finishedUrl);
}
break;
case RECEIVED_ICON:
if (mWebChromeClient != null) {
+ if (TRACE) Log.d(LOGTAG, "onReceivedIcon");
mWebChromeClient.onReceivedIcon(mWebView.getWebView(), (Bitmap) msg.obj);
}
break;
case RECEIVED_TOUCH_ICON_URL:
if (mWebChromeClient != null) {
+ if (TRACE) Log.d(LOGTAG, "onReceivedTouchIconUrl");
mWebChromeClient.onReceivedTouchIconUrl(mWebView.getWebView(),
(String) msg.obj, msg.arg1 == 1);
}
@@ -335,6 +342,7 @@
case RECEIVED_TITLE:
if (mWebChromeClient != null) {
+ if (TRACE) Log.d(LOGTAG, "onReceivedTitle");
mWebChromeClient.onReceivedTitle(mWebView.getWebView(),
(String) msg.obj);
}
@@ -345,6 +353,7 @@
int reasonCode = msg.arg1;
final String description = msg.getData().getString("description");
final String failUrl = msg.getData().getString("failingUrl");
+ if (TRACE) Log.d(LOGTAG, "onReceivedError=" + failUrl);
mWebViewClient.onReceivedError(mWebView.getWebView(), reasonCode,
description, failUrl);
}
@@ -356,6 +365,7 @@
Message dontResend =
(Message) msg.getData().getParcelable("dontResend");
if (mWebViewClient != null) {
+ if (TRACE) Log.d(LOGTAG, "onFormResubmission");
mWebViewClient.onFormResubmission(mWebView.getWebView(), dontResend,
resend);
} else {
@@ -379,6 +389,7 @@
HttpAuthHandler handler = (HttpAuthHandler) msg.obj;
String host = msg.getData().getString("host");
String realm = msg.getData().getString("realm");
+ if (TRACE) Log.d(LOGTAG, "onReceivedHttpAuthRequest");
mWebViewClient.onReceivedHttpAuthRequest(mWebView.getWebView(), handler,
host, realm);
}
@@ -388,6 +399,7 @@
if (mWebViewClient != null) {
HashMap<String, Object> map =
(HashMap<String, Object>) msg.obj;
+ if (TRACE) Log.d(LOGTAG, "onReceivedSslError");
mWebViewClient.onReceivedSslError(mWebView.getWebView(),
(SslErrorHandler) map.get("handler"),
(SslError) map.get("error"));
@@ -396,6 +408,7 @@
case PROCEEDED_AFTER_SSL_ERROR:
if (mWebViewClient != null && mWebViewClient instanceof WebViewClientClassicExt) {
+ if (TRACE) Log.d(LOGTAG, "onProceededAfterSslError");
((WebViewClientClassicExt) mWebViewClient).onProceededAfterSslError(
mWebView.getWebView(),
(SslError) msg.obj);
@@ -404,6 +417,7 @@
case CLIENT_CERT_REQUEST:
if (mWebViewClient != null && mWebViewClient instanceof WebViewClientClassicExt) {
+ if (TRACE) Log.d(LOGTAG, "onReceivedClientCertRequest");
HashMap<String, Object> map = (HashMap<String, Object>) msg.obj;
((WebViewClientClassicExt) mWebViewClient).onReceivedClientCertRequest(
mWebView.getWebView(),
@@ -418,6 +432,7 @@
// changed.
synchronized (this) {
if (mWebChromeClient != null) {
+ if (TRACE) Log.d(LOGTAG, "onProgressChanged=" + mLatestProgress);
mWebChromeClient.onProgressChanged(mWebView.getWebView(),
mLatestProgress);
}
@@ -427,14 +442,18 @@
case UPDATE_VISITED:
if (mWebViewClient != null) {
+ String url = (String) msg.obj;
+ if (TRACE) Log.d(LOGTAG, "doUpdateVisitedHistory=" + url);
mWebViewClient.doUpdateVisitedHistory(mWebView.getWebView(),
- (String) msg.obj, msg.arg1 != 0);
+ url, msg.arg1 != 0);
}
break;
case LOAD_RESOURCE:
if (mWebViewClient != null) {
- mWebViewClient.onLoadResource(mWebView.getWebView(), (String) msg.obj);
+ String url = (String) msg.obj;
+ if (TRACE) Log.d(LOGTAG, "onLoadResource=" + url);
+ mWebViewClient.onLoadResource(mWebView.getWebView(), url);
}
break;
@@ -448,6 +467,7 @@
String referer = msg.getData().getString("referer");
Long contentLength = msg.getData().getLong("contentLength");
+ if (TRACE) Log.d(LOGTAG, "onDownloadStart");
if (mDownloadListener instanceof BrowserDownloadListener) {
((BrowserDownloadListener) mDownloadListener).onDownloadStart(url,
userAgent, contentDisposition, mimetype, referer, contentLength);
@@ -460,6 +480,7 @@
case CREATE_WINDOW:
if (mWebChromeClient != null) {
+ if (TRACE) Log.d(LOGTAG, "onCreateWindow");
if (!mWebChromeClient.onCreateWindow(mWebView.getWebView(),
msg.arg1 == 1, msg.arg2 == 1,
(Message) msg.obj)) {
@@ -473,12 +494,14 @@
case REQUEST_FOCUS:
if (mWebChromeClient != null) {
+ if (TRACE) Log.d(LOGTAG, "onRequestFocus");
mWebChromeClient.onRequestFocus(mWebView.getWebView());
}
break;
case CLOSE_WINDOW:
if (mWebChromeClient != null) {
+ if (TRACE) Log.d(LOGTAG, "onCloseWindow");
mWebChromeClient.onCloseWindow(((WebViewClassic) msg.obj).getWebView());
}
break;
@@ -500,6 +523,7 @@
case ASYNC_KEYEVENTS:
if (mWebViewClient != null) {
+ if (TRACE) Log.d(LOGTAG, "onUnhandledKeyEvent");
mWebViewClient.onUnhandledKeyEvent(mWebView.getWebView(),
(KeyEvent) msg.obj);
}
@@ -521,6 +545,7 @@
WebStorage.QuotaUpdater quotaUpdater =
(WebStorage.QuotaUpdater) map.get("quotaUpdater");
+ if (TRACE) Log.d(LOGTAG, "onExceededDatabaseQuota");
mWebChromeClient.onExceededDatabaseQuota(url,
databaseIdentifier, quota, estimatedDatabaseSize,
totalQuota, quotaUpdater);
@@ -538,6 +563,7 @@
WebStorage.QuotaUpdater quotaUpdater =
(WebStorage.QuotaUpdater) map.get("quotaUpdater");
+ if (TRACE) Log.d(LOGTAG, "onReachedMaxAppCacheSize");
mWebChromeClient.onReachedMaxAppCacheSize(requiredStorage,
quota, quotaUpdater);
}
@@ -551,6 +577,7 @@
GeolocationPermissions.Callback callback =
(GeolocationPermissions.Callback)
map.get("callback");
+ if (TRACE) Log.d(LOGTAG, "onGeolocationPermissionsShowPrompt");
mWebChromeClient.onGeolocationPermissionsShowPrompt(origin,
callback);
}
@@ -558,6 +585,7 @@
case GEOLOCATION_PERMISSIONS_HIDE_PROMPT:
if (mWebChromeClient != null) {
+ if (TRACE) Log.d(LOGTAG, "onGeolocationPermissionsHidePrompt");
mWebChromeClient.onGeolocationPermissionsHidePrompt();
}
break;
@@ -566,6 +594,7 @@
if (mWebChromeClient != null) {
final JsResultReceiver receiver = (JsResultReceiver) msg.obj;
JsDialogHelper helper = new JsDialogHelper(receiver.mJsResult, msg);
+ if (TRACE) Log.d(LOGTAG, "onJsAlert");
if (!helper.invokeCallback(mWebChromeClient, mWebView.getWebView())) {
helper.showDialog(mContext);
}
@@ -577,6 +606,7 @@
if(mWebChromeClient != null) {
final JsResultReceiver receiver = (JsResultReceiver) msg.obj;
final JsResult res = receiver.mJsResult;
+ if (TRACE) Log.d(LOGTAG, "onJsTimeout");
if (mWebChromeClient.onJsTimeout()) {
res.confirm();
} else {
@@ -598,6 +628,7 @@
case SCALE_CHANGED:
if (mWebViewClient != null) {
+ if (TRACE) Log.d(LOGTAG, "onScaleChanged");
mWebViewClient.onScaleChanged(mWebView.getWebView(), msg.getData()
.getFloat("old"), msg.getData().getFloat("new"));
}
@@ -624,6 +655,7 @@
ConsoleMessage.MessageLevel messageLevel =
ConsoleMessage.MessageLevel.values()[msgLevel];
+ if (TRACE) Log.d(LOGTAG, "onConsoleMessage");
if (!mWebChromeClient.onConsoleMessage(new ConsoleMessage(message, sourceID,
lineNumber, messageLevel))) {
// If false was returned the user did not provide their own console function so
@@ -654,12 +686,14 @@
case GET_VISITED_HISTORY:
if (mWebChromeClient != null) {
+ if (TRACE) Log.d(LOGTAG, "getVisitedHistory");
mWebChromeClient.getVisitedHistory((ValueCallback<String[]>)msg.obj);
}
break;
case OPEN_FILE_CHOOSER:
if (mWebChromeClient != null) {
+ if (TRACE) Log.d(LOGTAG, "openFileChooser");
UploadFileMessageData data = (UploadFileMessageData)msg.obj;
mWebChromeClient.openFileChooser(data.getUploadFile(), data.getAcceptType(),
data.getCapture());
@@ -668,6 +702,7 @@
case ADD_HISTORY_ITEM:
if (mWebBackForwardListClient != null) {
+ if (TRACE) Log.d(LOGTAG, "onNewHistoryItem");
mWebBackForwardListClient.onNewHistoryItem(
(WebHistoryItem) msg.obj);
}
@@ -693,6 +728,7 @@
String realm = msg.getData().getString("realm");
String account = msg.getData().getString("account");
String args = msg.getData().getString("args");
+ if (TRACE) Log.d(LOGTAG, "onReceivedLoginRequest");
mWebViewClient.onReceivedLoginRequest(mWebView.getWebView(), realm,
account, args);
}
@@ -910,6 +946,7 @@
return null;
}
// Note: This method does _not_ send a message.
+ if (TRACE) Log.d(LOGTAG, "shouldInterceptRequest=" + url);
WebResourceResponse r =
mWebViewClient.shouldInterceptRequest(mWebView.getWebView(), url);
if (r == null) {
diff --git a/core/java/android/webkit/DebugFlags.java b/core/java/android/webkit/DebugFlags.java
index 349113e..524f610 100644
--- a/core/java/android/webkit/DebugFlags.java
+++ b/core/java/android/webkit/DebugFlags.java
@@ -24,25 +24,33 @@
* The name of each flags maps directly to the name of the class in which that
* flag is used.
*
+ * @hide Only used by WebView implementations.
*/
-class DebugFlags {
+public class DebugFlags {
+ public static final boolean COOKIE_SYNC_MANAGER = false;
+ public static final boolean TRACE_API = false;
+ public static final boolean TRACE_CALLBACK = false;
+ public static final boolean TRACE_JAVASCRIPT_BRIDGE = false;
+ public static final boolean URL_UTIL = false;
+ public static final boolean WEB_SYNC_MANAGER = false;
+
+ // TODO: Delete these when WebViewClassic is moved
public static final boolean BROWSER_FRAME = false;
public static final boolean CACHE_MANAGER = false;
public static final boolean CALLBACK_PROXY = false;
public static final boolean COOKIE_MANAGER = false;
- public static final boolean COOKIE_SYNC_MANAGER = false;
public static final boolean FRAME_LOADER = false;
public static final boolean J_WEB_CORE_JAVA_BRIDGE = false;// HIGHLY VERBOSE
public static final boolean LOAD_LISTENER = false;
+ public static final boolean MEASURE_PAGE_SWAP_FPS = false;
public static final boolean NETWORK = false;
public static final boolean SSL_ERROR_HANDLER = false;
public static final boolean STREAM_LOADER = false;
- public static final boolean URL_UTIL = false;
public static final boolean WEB_BACK_FORWARD_LIST = false;
public static final boolean WEB_SETTINGS = false;
- public static final boolean WEB_SYNC_MANAGER = false;
public static final boolean WEB_VIEW = false;
public static final boolean WEB_VIEW_CORE = false;
- public static final boolean MEASURE_PAGE_SWAP_FPS = false;
+
+
}
diff --git a/core/java/android/webkit/HTML5VideoFullScreen.java b/core/java/android/webkit/HTML5VideoFullScreen.java
index b52218d..6fb32c8 100644
--- a/core/java/android/webkit/HTML5VideoFullScreen.java
+++ b/core/java/android/webkit/HTML5VideoFullScreen.java
@@ -19,6 +19,7 @@
import android.content.Context;
import android.media.MediaPlayer;
import android.media.Metadata;
+import android.util.Log;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
@@ -293,12 +294,16 @@
mLayout.setVisibility(View.VISIBLE);
WebChromeClient client = webView.getWebChromeClient();
if (client != null) {
+ if (DebugFlags.TRACE_CALLBACK) Log.d(CallbackProxy.LOGTAG, "onShowCustomView");
client.onShowCustomView(mLayout, mCallback);
// Plugins like Flash will draw over the video so hide
// them while we're playing.
if (webView.getViewManager() != null)
webView.getViewManager().hideAll();
+ if (DebugFlags.TRACE_CALLBACK) {
+ Log.d(CallbackProxy.LOGTAG, "getVideoLoadingProgressView");
+ }
mProgressView = client.getVideoLoadingProgressView();
if (mProgressView != null) {
mLayout.addView(mProgressView, layoutParams);
diff --git a/core/java/android/webkit/HTML5VideoViewProxy.java b/core/java/android/webkit/HTML5VideoViewProxy.java
index a3d62ae..e8538f6 100644
--- a/core/java/android/webkit/HTML5VideoViewProxy.java
+++ b/core/java/android/webkit/HTML5VideoViewProxy.java
@@ -180,6 +180,7 @@
if (!mHTML5VideoView.fullScreenExited() && mHTML5VideoView.isFullScreenMode()) {
WebChromeClient client = webView.getWebChromeClient();
if (client != null) {
+ if (DebugFlags.TRACE_CALLBACK) Log.d(CallbackProxy.LOGTAG, "onHideCustomView");
client.onHideCustomView();
}
}
@@ -405,6 +406,7 @@
case ERROR: {
WebChromeClient client = mWebView.getWebChromeClient();
if (client != null) {
+ if (DebugFlags.TRACE_CALLBACK) Log.d(CallbackProxy.LOGTAG, "onHideCustomView");
client.onHideCustomView();
}
break;
@@ -412,6 +414,9 @@
case LOAD_DEFAULT_POSTER: {
WebChromeClient client = mWebView.getWebChromeClient();
if (client != null) {
+ if (DebugFlags.TRACE_CALLBACK) {
+ Log.d(CallbackProxy.LOGTAG, "getDefaultVideoPoster");
+ }
doSetPoster(client.getDefaultVideoPoster());
}
break;
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index eded438..f0e8c4f 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -244,7 +244,7 @@
implements ViewTreeObserver.OnGlobalFocusChangeListener,
ViewGroup.OnHierarchyChangeListener, ViewDebug.HierarchyHandler {
- private static final String LOGTAG = "webview_proxy";
+ private static final String LOGTAG = "WebView";
// Throwing an exception for incorrect thread usage if the
// build target is JB MR2 or newer. Defaults to false, and is
@@ -496,6 +496,7 @@
sEnforceThreadChecking = context.getApplicationInfo().targetSdkVersion >=
Build.VERSION_CODES.JELLY_BEAN_MR2;
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "WebView<init>");
ensureProviderCreated();
mProvider.init(javaScriptInterfaces, privateBrowsing);
@@ -510,6 +511,7 @@
*/
public void setHorizontalScrollbarOverlay(boolean overlay) {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "setHorizontalScrollbarOverlay=" + overlay);
mProvider.setHorizontalScrollbarOverlay(overlay);
}
@@ -520,6 +522,7 @@
*/
public void setVerticalScrollbarOverlay(boolean overlay) {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "setVerticalScrollbarOverlay=" + overlay);
mProvider.setVerticalScrollbarOverlay(overlay);
}
@@ -574,6 +577,7 @@
@Deprecated
public void setCertificate(SslCertificate certificate) {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "setCertificate=" + certificate);
mProvider.setCertificate(certificate);
}
@@ -597,6 +601,7 @@
@Deprecated
public void savePassword(String host, String username, String password) {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "savePassword=" + host);
mProvider.savePassword(host, username, password);
}
@@ -616,6 +621,7 @@
public void setHttpAuthUsernamePassword(String host, String realm,
String username, String password) {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "setHttpAuthUsernamePassword=" + host);
mProvider.setHttpAuthUsernamePassword(host, realm, username, password);
}
@@ -645,6 +651,7 @@
*/
public void destroy() {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "destroy");
mProvider.destroy();
}
@@ -683,6 +690,7 @@
*/
public void setNetworkAvailable(boolean networkUp) {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "setNetworkAvailable=" + networkUp);
mProvider.setNetworkAvailable(networkUp);
}
@@ -699,6 +707,7 @@
*/
public WebBackForwardList saveState(Bundle outState) {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "saveState");
return mProvider.saveState(outState);
}
@@ -715,6 +724,7 @@
@Deprecated
public boolean savePicture(Bundle b, final File dest) {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "savePicture=" + dest.getName());
return mProvider.savePicture(b, dest);
}
@@ -732,6 +742,7 @@
@Deprecated
public boolean restorePicture(Bundle b, File src) {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "restorePicture=" + src.getName());
return mProvider.restorePicture(b, src);
}
@@ -749,6 +760,7 @@
*/
public WebBackForwardList restoreState(Bundle inState) {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "restoreState");
return mProvider.restoreState(inState);
}
@@ -765,6 +777,7 @@
*/
public void loadUrl(String url, Map<String, String> additionalHttpHeaders) {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "loadUrl(extra headers)=" + url);
mProvider.loadUrl(url, additionalHttpHeaders);
}
@@ -775,6 +788,7 @@
*/
public void loadUrl(String url) {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "loadUrl=" + url);
mProvider.loadUrl(url);
}
@@ -789,6 +803,7 @@
*/
public void postUrl(String url, byte[] postData) {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "postUrl=" + url);
mProvider.postUrl(url, postData);
}
@@ -823,6 +838,7 @@
*/
public void loadData(String data, String mimeType, String encoding) {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "loadData");
mProvider.loadData(data, mimeType, encoding);
}
@@ -855,6 +871,7 @@
public void loadDataWithBaseURL(String baseUrl, String data,
String mimeType, String encoding, String historyUrl) {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "loadDataWithBaseURL=" + baseUrl);
mProvider.loadDataWithBaseURL(baseUrl, data, mimeType, encoding, historyUrl);
}
@@ -871,6 +888,7 @@
*/
public void evaluateJavascript(String script, ValueCallback<String> resultCallback) {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "evaluateJavascript=" + script);
mProvider.evaluateJavaScript(script, resultCallback);
}
@@ -881,6 +899,7 @@
*/
public void saveWebArchive(String filename) {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "saveWebArchive=" + filename);
mProvider.saveWebArchive(filename);
}
@@ -898,6 +917,7 @@
*/
public void saveWebArchive(String basename, boolean autoname, ValueCallback<String> callback) {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "saveWebArchive(auto)=" + basename);
mProvider.saveWebArchive(basename, autoname, callback);
}
@@ -906,6 +926,7 @@
*/
public void stopLoading() {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "stopLoading");
mProvider.stopLoading();
}
@@ -914,6 +935,7 @@
*/
public void reload() {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "reload");
mProvider.reload();
}
@@ -932,6 +954,7 @@
*/
public void goBack() {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "goBack");
mProvider.goBack();
}
@@ -950,6 +973,7 @@
*/
public void goForward() {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "goForward");
mProvider.goForward();
}
@@ -975,6 +999,7 @@
*/
public void goBackOrForward(int steps) {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "goBackOrForwad=" + steps);
mProvider.goBackOrForward(steps);
}
@@ -994,6 +1019,7 @@
*/
public boolean pageUp(boolean top) {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "pageUp");
return mProvider.pageUp(top);
}
@@ -1005,6 +1031,7 @@
*/
public boolean pageDown(boolean bottom) {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "pageDown");
return mProvider.pageDown(bottom);
}
@@ -1017,6 +1044,7 @@
@Deprecated
public void clearView() {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "clearView");
mProvider.clearView();
}
@@ -1036,6 +1064,7 @@
*/
public Picture capturePicture() {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "capturePicture");
return mProvider.capturePicture();
}
@@ -1073,6 +1102,7 @@
ValueCallback<Boolean> resultCallback, CancellationSignal cancellationSignal)
throws java.io.IOException {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "exportToPdf");
mProvider.exportToPdf(fd, attributes, resultCallback, cancellationSignal);
}
@@ -1104,6 +1134,7 @@
*/
public void setInitialScale(int scaleInPercent) {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "setInitialScale=" + scaleInPercent);
mProvider.setInitialScale(scaleInPercent);
}
@@ -1114,6 +1145,7 @@
*/
public void invokeZoomPicker() {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "invokeZoomPicker");
mProvider.invokeZoomPicker();
}
@@ -1137,6 +1169,7 @@
*/
public HitTestResult getHitTestResult() {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "getHitTestResult");
return mProvider.getHitTestResult();
}
@@ -1155,6 +1188,7 @@
*/
public void requestFocusNodeHref(Message hrefMsg) {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "requestFocusNodeHref");
mProvider.requestFocusNodeHref(hrefMsg);
}
@@ -1167,6 +1201,7 @@
*/
public void requestImageRef(Message msg) {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "requestImageRef");
mProvider.requestImageRef(msg);
}
@@ -1271,6 +1306,7 @@
*/
public void pauseTimers() {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "pauseTimers");
mProvider.pauseTimers();
}
@@ -1280,6 +1316,7 @@
*/
public void resumeTimers() {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "resumeTimers");
mProvider.resumeTimers();
}
@@ -1292,6 +1329,7 @@
*/
public void onPause() {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "onPause");
mProvider.onPause();
}
@@ -1300,6 +1338,7 @@
*/
public void onResume() {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "onResume");
mProvider.onResume();
}
@@ -1319,6 +1358,7 @@
*/
public void freeMemory() {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "freeMemory");
mProvider.freeMemory();
}
@@ -1330,6 +1370,7 @@
*/
public void clearCache(boolean includeDiskFiles) {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "clearCache");
mProvider.clearCache(includeDiskFiles);
}
@@ -1341,6 +1382,7 @@
*/
public void clearFormData() {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "clearFormData");
mProvider.clearFormData();
}
@@ -1349,6 +1391,7 @@
*/
public void clearHistory() {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "clearHistory");
mProvider.clearHistory();
}
@@ -1358,6 +1401,7 @@
*/
public void clearSslPreferences() {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "clearSslPreferences");
mProvider.clearSslPreferences();
}
@@ -1399,6 +1443,7 @@
*/
public void findNext(boolean forward) {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "findNext");
mProvider.findNext(forward);
}
@@ -1414,6 +1459,7 @@
@Deprecated
public int findAll(String find) {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "findAll");
StrictMode.noteSlowCall("findAll blocks UI: prefer findAllAsync");
return mProvider.findAll(find);
}
@@ -1428,6 +1474,7 @@
*/
public void findAllAsync(String find) {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "findAllAsync");
mProvider.findAllAsync(find);
}
@@ -1448,6 +1495,7 @@
@Deprecated
public boolean showFindDialog(String text, boolean showIme) {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "showFindDialog");
return mProvider.showFindDialog(text, showIme);
}
@@ -1483,6 +1531,7 @@
*/
public void clearMatches() {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "clearMatches");
mProvider.clearMatches();
}
@@ -1543,6 +1592,7 @@
@Deprecated
public void setPictureListener(PictureListener listener) {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "setPictureListener=" + listener);
mProvider.setPictureListener(listener);
}
@@ -1592,6 +1642,7 @@
*/
public void addJavascriptInterface(Object object, String name) {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "addJavascriptInterface=" + name);
mProvider.addJavascriptInterface(object, name);
}
@@ -1604,6 +1655,7 @@
*/
public void removeJavascriptInterface(String name) {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "removeJavascriptInterface=" + name);
mProvider.removeJavascriptInterface(name);
}
@@ -1693,6 +1745,7 @@
public void flingScroll(int vx, int vy) {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "flingScroll");
mProvider.flingScroll(vx, vy);
}
diff --git a/core/java/android/webkit/WebViewClassic.java b/core/java/android/webkit/WebViewClassic.java
index b1a7878..3f22d53 100644
--- a/core/java/android/webkit/WebViewClassic.java
+++ b/core/java/android/webkit/WebViewClassic.java
@@ -7951,6 +7951,7 @@
// triggered in setNewPicture
Picture picture = mContext.getApplicationInfo().targetSdkVersion <
Build.VERSION_CODES.JELLY_BEAN_MR2 ? capturePicture() : null;
+ if (DebugFlags.TRACE_CALLBACK) Log.d(CallbackProxy.LOGTAG, "onNewPicture");
mPictureListener.onNewPicture(getWebView(), picture);
}
}
@@ -8038,6 +8039,7 @@
// triggered in pageSwapCallback
Picture picture = mContext.getApplicationInfo().targetSdkVersion <
Build.VERSION_CODES.JELLY_BEAN_MR2 ? capturePicture() : null;
+ if (DebugFlags.TRACE_CALLBACK) Log.d(CallbackProxy.LOGTAG, "onNewPicture");
mPictureListener.onNewPicture(getWebView(), picture);
}
}
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index be47bf0..c308024 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -4924,11 +4924,37 @@
* Scrolls the list items within the view by a specified number of pixels.
*
* @param y the amount of pixels to scroll by vertically
- * @return true if the list is able to scroll, or false if the list is
- * already at the beginning/end and unable to scroll any more.
+ * @see #canScrollList(int)
*/
- public boolean scrollListBy(int y) {
- return !trackMotionScroll(-y, -y);
+ public void scrollListBy(int y) {
+ trackMotionScroll(-y, -y);
+ }
+
+ /**
+ * Check if the items in the list can be scrolled in a certain direction.
+ *
+ * @param direction Negative to check scrolling up, positive to check
+ * scrolling down.
+ * @return true if the list can be scrolled in the specified direction,
+ * false otherwise.
+ * @see #scrollListBy(int)
+ */
+ public boolean canScrollList(int direction) {
+ final int childCount = getChildCount();
+ if (childCount == 0) {
+ return false;
+ }
+
+ final int firstPosition = mFirstPosition;
+ final Rect listPadding = mListPadding;
+ if (direction > 0) {
+ final int lastBottom = getChildAt(childCount - 1).getBottom();
+ final int lastPosition = firstPosition + childCount;
+ return lastPosition < mItemCount || lastBottom > getHeight() - listPadding.bottom;
+ } else {
+ final int firstTop = getChildAt(0).getTop();
+ return firstPosition > 0 || firstTop < listPadding.top;
+ }
}
/**
diff --git a/core/java/android/widget/ListPopupWindow.java b/core/java/android/widget/ListPopupWindow.java
index f2da765..b7e1fdd 100644
--- a/core/java/android/widget/ListPopupWindow.java
+++ b/core/java/android/widget/ListPopupWindow.java
@@ -974,10 +974,12 @@
* currently touched list item.
* <p>
* Example usage:
- * <pre>ListPopupWindow myPopup = new ListPopupWindow(context);
+ * <pre>
+ * ListPopupWindow myPopup = new ListPopupWindow(context);
* myPopup.setAnchor(myAnchor);
* OnTouchListener dragListener = myPopup.createDragToOpenListener(myAnchor);
- * myAnchor.setOnTouchListener(dragListener);</pre>
+ * myAnchor.setOnTouchListener(dragListener);
+ * </pre>
*
* @param src the view on which the resulting listener will be set
* @return a touch listener that controls drag-to-open behavior
diff --git a/core/java/android/widget/PopupMenu.java b/core/java/android/widget/PopupMenu.java
index e5344c6..603db70 100644
--- a/core/java/android/widget/PopupMenu.java
+++ b/core/java/android/widget/PopupMenu.java
@@ -82,8 +82,10 @@
* currently touched list item.
* <p>
* Example usage:
- * <pre>PopupMenu myPopup = new PopupMenu(context, myAnchor);
- * myAnchor.setOnTouchListener(myPopup.getDragToOpenListener());</pre>
+ * <pre>
+ * PopupMenu myPopup = new PopupMenu(context, myAnchor);
+ * myAnchor.setOnTouchListener(myPopup.getDragToOpenListener());
+ * </pre>
*
* @return a touch listener that controls drag-to-open behavior
*/
diff --git a/core/java/android/widget/VideoView.java b/core/java/android/widget/VideoView.java
index ebf9fe0..0ddc131 100644
--- a/core/java/android/widget/VideoView.java
+++ b/core/java/android/widget/VideoView.java
@@ -21,12 +21,14 @@
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.Resources;
+import android.graphics.Canvas;
import android.media.AudioManager;
+import android.media.MediaFormat;
import android.media.MediaPlayer;
-import android.media.Metadata;
import android.media.MediaPlayer.OnCompletionListener;
import android.media.MediaPlayer.OnErrorListener;
import android.media.MediaPlayer.OnInfoListener;
+import android.media.Metadata;
import android.net.Uri;
import android.util.AttributeSet;
import android.util.Log;
@@ -40,7 +42,10 @@
import android.widget.MediaController.MediaPlayerControl;
import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
import java.util.Map;
+import java.util.Vector;
/**
* Displays a video file. The VideoView class
@@ -91,6 +96,15 @@
private boolean mCanSeekBack;
private boolean mCanSeekForward;
+ /** List of views overlaid on top of the video. */
+ private ArrayList<View> mOverlays;
+
+ /**
+ * Listener for overlay layout changes. Invalidates the video view to ensure
+ * that captions are redrawn whenever their layout changes.
+ */
+ private OnLayoutChangeListener mOverlayLayoutListener;
+
public VideoView(Context context) {
super(context);
initVideoView();
@@ -194,6 +208,7 @@
setFocusable(true);
setFocusableInTouchMode(true);
requestFocus();
+ mPendingSubtitleTracks = 0;
mCurrentState = STATE_IDLE;
mTargetState = STATE_IDLE;
}
@@ -218,6 +233,47 @@
invalidate();
}
+ /**
+ * Adds an external subtitle source file (from the provided input stream.)
+ *
+ * Note that a single external subtitle source may contain multiple or no
+ * supported tracks in it. If the source contained at least one track in
+ * it, one will receive an {@link MediaPlayer#MEDIA_INFO_METADATA_UPDATE}
+ * info message. Otherwise, if reading the source takes excessive time,
+ * one will receive a {@link MediaPlayer#MEDIA_INFO_SUBTITLE_TIMED_OUT}
+ * message. If the source contained no supported track (including an empty
+ * source file or null input stream), one will receive a {@link
+ * MediaPlayer#MEDIA_INFO_UNSUPPORTED_SUBTITLE} message. One can find the
+ * total number of available tracks using {@link MediaPlayer#getTrackInfo()}
+ * to see what additional tracks become available after this method call.
+ *
+ * @param is input stream containing the subtitle data. It will be
+ * closed by the media framework.
+ * @param format the format of the subtitle track(s). Must contain at least
+ * the mime type ({@link MediaFormat#KEY_MIME}) and the
+ * language ({@link MediaFormat#KEY_LANGUAGE}) of the file.
+ * If the file itself contains the language information,
+ * specify "und" for the language.
+ */
+ public void addSubtitleSource(InputStream is, MediaFormat format) {
+ // always signal unsupported message for now
+ try {
+ if (is != null) {
+ is.close();
+ }
+ } catch (IOException e) {
+ }
+
+ if (mMediaPlayer == null) {
+ ++mPendingSubtitleTracks;
+ } else {
+ mInfoListener.onInfo(
+ mMediaPlayer, MediaPlayer.MEDIA_INFO_UNSUPPORTED_SUBTITLE, 0);
+ }
+ }
+
+ private int mPendingSubtitleTracks;
+
public void stopPlayback() {
if (mMediaPlayer != null) {
mMediaPlayer.stop();
@@ -253,7 +309,7 @@
mMediaPlayer.setOnVideoSizeChangedListener(mSizeChangedListener);
mMediaPlayer.setOnCompletionListener(mCompletionListener);
mMediaPlayer.setOnErrorListener(mErrorListener);
- mMediaPlayer.setOnInfoListener(mOnInfoListener);
+ mMediaPlayer.setOnInfoListener(mInfoListener);
mMediaPlayer.setOnBufferingUpdateListener(mBufferingUpdateListener);
mCurrentBufferPercentage = 0;
mMediaPlayer.setDataSource(mContext, mUri, mHeaders);
@@ -261,6 +317,12 @@
mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mMediaPlayer.setScreenOnWhilePlaying(true);
mMediaPlayer.prepareAsync();
+
+ for (int ix = 0; ix < mPendingSubtitleTracks; ix++) {
+ mInfoListener.onInfo(
+ mMediaPlayer, MediaPlayer.MEDIA_INFO_UNSUPPORTED_SUBTITLE, 0);
+ }
+
// we don't set the target state here either, but preserve the
// target state that was there before.
mCurrentState = STATE_PREPARING;
@@ -277,6 +339,8 @@
mTargetState = STATE_ERROR;
mErrorListener.onError(mMediaPlayer, MediaPlayer.MEDIA_ERROR_UNKNOWN, 0);
return;
+ } finally {
+ mPendingSubtitleTracks = 0;
}
}
@@ -386,6 +450,16 @@
}
};
+ private MediaPlayer.OnInfoListener mInfoListener =
+ new MediaPlayer.OnInfoListener() {
+ public boolean onInfo(MediaPlayer mp, int arg1, int arg2) {
+ if (mOnInfoListener != null) {
+ mOnInfoListener.onInfo(mp, arg1, arg2);
+ }
+ return true;
+ }
+ };
+
private MediaPlayer.OnErrorListener mErrorListener =
new MediaPlayer.OnErrorListener() {
public boolean onError(MediaPlayer mp, int framework_err, int impl_err) {
@@ -530,6 +604,7 @@
mMediaPlayer.reset();
mMediaPlayer.release();
mMediaPlayer = null;
+ mPendingSubtitleTracks = 0;
mCurrentState = STATE_IDLE;
if (cleartargetstate) {
mTargetState = STATE_IDLE;
@@ -702,4 +777,101 @@
}
return mAudioSession;
}
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+
+ // Layout overlay views, if necessary.
+ if (changed && mOverlays != null && !mOverlays.isEmpty()) {
+ measureAndLayoutOverlays();
+ }
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ super.draw(canvas);
+
+ final int count = mOverlays.size();
+ for (int i = 0; i < count; i++) {
+ final View overlay = mOverlays.get(i);
+ overlay.draw(canvas);
+ }
+ }
+
+ /**
+ * Adds a view to be overlaid on top of this video view. During layout, the
+ * view will be forced to match the bounds, less padding, of the video view.
+ * <p>
+ * Overlays are drawn in the order they are added. The last added overlay
+ * will be drawn on top.
+ *
+ * @param overlay the view to overlay
+ * @see #removeOverlay(View)
+ */
+ private void addOverlay(View overlay) {
+ if (mOverlays == null) {
+ mOverlays = new ArrayList<View>(1);
+ }
+
+ if (mOverlayLayoutListener == null) {
+ mOverlayLayoutListener = new OnLayoutChangeListener() {
+ @Override
+ public void onLayoutChange(View v, int left, int top, int right, int bottom,
+ int oldLeft, int oldTop, int oldRight, int oldBottom) {
+ invalidate();
+ }
+ };
+ }
+
+ if (mOverlays.isEmpty()) {
+ setWillNotDraw(false);
+ }
+
+ mOverlays.add(overlay);
+ overlay.addOnLayoutChangeListener(mOverlayLayoutListener);
+ measureAndLayoutOverlays();
+ }
+
+ /**
+ * Removes a view previously added using {@link #addOverlay}.
+ *
+ * @param overlay the view to remove
+ * @see #addOverlay(View)
+ */
+ private void removeOverlay(View overlay) {
+ if (mOverlays == null) {
+ return;
+ }
+
+ overlay.removeOnLayoutChangeListener(mOverlayLayoutListener);
+ mOverlays.remove(overlay);
+
+ if (mOverlays.isEmpty()) {
+ setWillNotDraw(true);
+ }
+
+ invalidate();
+ }
+
+ /**
+ * Forces a measurement and layout pass for all overlaid views.
+ *
+ * @see #addOverlay(View)
+ */
+ private void measureAndLayoutOverlays() {
+ final int left = getPaddingLeft();
+ final int top = getPaddingTop();
+ final int right = getWidth() - left - getPaddingRight();
+ final int bottom = getHeight() - top - getPaddingBottom();
+ final int widthSpec = MeasureSpec.makeMeasureSpec(right - left, MeasureSpec.EXACTLY);
+ final int heightSpec = MeasureSpec.makeMeasureSpec(bottom - top, MeasureSpec.EXACTLY);
+
+ final int count = mOverlays.size();
+ for (int i = 0; i < count; i++) {
+ final View overlay = mOverlays.get(i);
+ overlay.measure(widthSpec, heightSpec);
+ overlay.layout(left, top, right, bottom);
+ }
+ }
}
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index ab81a37..8819237 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -287,102 +287,104 @@
}
protected void onIntentSelected(ResolveInfo ri, Intent intent, boolean alwaysCheck) {
- // Build a reasonable intent filter, based on what matched.
- IntentFilter filter = new IntentFilter();
+ if (mAlwaysUseOption) {
+ // Build a reasonable intent filter, based on what matched.
+ IntentFilter filter = new IntentFilter();
- if (intent.getAction() != null) {
- filter.addAction(intent.getAction());
- }
- Set<String> categories = intent.getCategories();
- if (categories != null) {
- for (String cat : categories) {
- filter.addCategory(cat);
+ if (intent.getAction() != null) {
+ filter.addAction(intent.getAction());
}
- }
- filter.addCategory(Intent.CATEGORY_DEFAULT);
-
- int cat = ri.match&IntentFilter.MATCH_CATEGORY_MASK;
- Uri data = intent.getData();
- if (cat == IntentFilter.MATCH_CATEGORY_TYPE) {
- String mimeType = intent.resolveType(this);
- if (mimeType != null) {
- try {
- filter.addDataType(mimeType);
- } catch (IntentFilter.MalformedMimeTypeException e) {
- Log.w("ResolverActivity", e);
- filter = null;
+ Set<String> categories = intent.getCategories();
+ if (categories != null) {
+ for (String cat : categories) {
+ filter.addCategory(cat);
}
}
- }
- if (data != null && data.getScheme() != null) {
- // We need the data specification if there was no type,
- // OR if the scheme is not one of our magical "file:"
- // or "content:" schemes (see IntentFilter for the reason).
- if (cat != IntentFilter.MATCH_CATEGORY_TYPE
- || (!"file".equals(data.getScheme())
- && !"content".equals(data.getScheme()))) {
- filter.addDataScheme(data.getScheme());
+ filter.addCategory(Intent.CATEGORY_DEFAULT);
- // Look through the resolved filter to determine which part
- // of it matched the original Intent.
- Iterator<PatternMatcher> pIt = ri.filter.schemeSpecificPartsIterator();
- if (pIt != null) {
- String ssp = data.getSchemeSpecificPart();
- while (ssp != null && pIt.hasNext()) {
- PatternMatcher p = pIt.next();
- if (p.match(ssp)) {
- filter.addDataSchemeSpecificPart(p.getPath(), p.getType());
- break;
- }
+ int cat = ri.match&IntentFilter.MATCH_CATEGORY_MASK;
+ Uri data = intent.getData();
+ if (cat == IntentFilter.MATCH_CATEGORY_TYPE) {
+ String mimeType = intent.resolveType(this);
+ if (mimeType != null) {
+ try {
+ filter.addDataType(mimeType);
+ } catch (IntentFilter.MalformedMimeTypeException e) {
+ Log.w("ResolverActivity", e);
+ filter = null;
}
}
- Iterator<IntentFilter.AuthorityEntry> aIt = ri.filter.authoritiesIterator();
- if (aIt != null) {
- while (aIt.hasNext()) {
- IntentFilter.AuthorityEntry a = aIt.next();
- if (a.match(data) >= 0) {
- int port = a.getPort();
- filter.addDataAuthority(a.getHost(),
- port >= 0 ? Integer.toString(port) : null);
- break;
+ }
+ if (data != null && data.getScheme() != null) {
+ // We need the data specification if there was no type,
+ // OR if the scheme is not one of our magical "file:"
+ // or "content:" schemes (see IntentFilter for the reason).
+ if (cat != IntentFilter.MATCH_CATEGORY_TYPE
+ || (!"file".equals(data.getScheme())
+ && !"content".equals(data.getScheme()))) {
+ filter.addDataScheme(data.getScheme());
+
+ // Look through the resolved filter to determine which part
+ // of it matched the original Intent.
+ Iterator<PatternMatcher> pIt = ri.filter.schemeSpecificPartsIterator();
+ if (pIt != null) {
+ String ssp = data.getSchemeSpecificPart();
+ while (ssp != null && pIt.hasNext()) {
+ PatternMatcher p = pIt.next();
+ if (p.match(ssp)) {
+ filter.addDataSchemeSpecificPart(p.getPath(), p.getType());
+ break;
+ }
}
}
- }
- pIt = ri.filter.pathsIterator();
- if (pIt != null) {
- String path = data.getPath();
- while (path != null && pIt.hasNext()) {
- PatternMatcher p = pIt.next();
- if (p.match(path)) {
- filter.addDataPath(p.getPath(), p.getType());
- break;
+ Iterator<IntentFilter.AuthorityEntry> aIt = ri.filter.authoritiesIterator();
+ if (aIt != null) {
+ while (aIt.hasNext()) {
+ IntentFilter.AuthorityEntry a = aIt.next();
+ if (a.match(data) >= 0) {
+ int port = a.getPort();
+ filter.addDataAuthority(a.getHost(),
+ port >= 0 ? Integer.toString(port) : null);
+ break;
+ }
+ }
+ }
+ pIt = ri.filter.pathsIterator();
+ if (pIt != null) {
+ String path = data.getPath();
+ while (path != null && pIt.hasNext()) {
+ PatternMatcher p = pIt.next();
+ if (p.match(path)) {
+ filter.addDataPath(p.getPath(), p.getType());
+ break;
+ }
}
}
}
}
- }
- if (filter != null) {
- final int N = mAdapter.mList.size();
- ComponentName[] set = new ComponentName[N];
- int bestMatch = 0;
- for (int i=0; i<N; i++) {
- ResolveInfo r = mAdapter.mList.get(i).ri;
- set[i] = new ComponentName(r.activityInfo.packageName,
- r.activityInfo.name);
- if (r.match > bestMatch) bestMatch = r.match;
- }
- if (alwaysCheck) {
- getPackageManager().addPreferredActivity(filter, bestMatch, set,
- intent.getComponent());
- } else {
- try {
- AppGlobals.getPackageManager().setLastChosenActivity(intent,
- intent.resolveTypeIfNeeded(getContentResolver()),
- PackageManager.MATCH_DEFAULT_ONLY,
- filter, bestMatch, intent.getComponent());
- } catch (RemoteException re) {
- Log.d(TAG, "Error calling setLastChosenActivity\n" + re);
+ if (filter != null) {
+ final int N = mAdapter.mList.size();
+ ComponentName[] set = new ComponentName[N];
+ int bestMatch = 0;
+ for (int i=0; i<N; i++) {
+ ResolveInfo r = mAdapter.mList.get(i).ri;
+ set[i] = new ComponentName(r.activityInfo.packageName,
+ r.activityInfo.name);
+ if (r.match > bestMatch) bestMatch = r.match;
+ }
+ if (alwaysCheck) {
+ getPackageManager().addPreferredActivity(filter, bestMatch, set,
+ intent.getComponent());
+ } else {
+ try {
+ AppGlobals.getPackageManager().setLastChosenActivity(intent,
+ intent.resolveTypeIfNeeded(getContentResolver()),
+ PackageManager.MATCH_DEFAULT_ONLY,
+ filter, bestMatch, intent.getComponent());
+ } catch (RemoteException re) {
+ Log.d(TAG, "Error calling setLastChosenActivity\n" + re);
+ }
}
}
}
diff --git a/core/java/com/android/internal/app/RestrictionsPinActivity.java b/core/java/com/android/internal/app/RestrictionsPinActivity.java
index f8ce108..2112474 100644
--- a/core/java/com/android/internal/app/RestrictionsPinActivity.java
+++ b/core/java/com/android/internal/app/RestrictionsPinActivity.java
@@ -16,9 +16,7 @@
package com.android.internal.app;
-import android.app.AlertDialog;
import android.content.Context;
-import android.content.DialogInterface;
import android.os.Bundle;
import android.os.UserManager;
import android.text.Editable;
@@ -26,7 +24,8 @@
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
-import android.view.WindowManager;
+import android.view.View.OnClickListener;
+import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener;
@@ -38,14 +37,15 @@
* challenge for an existing PIN. The PIN is maintained by UserManager.
*/
public class RestrictionsPinActivity extends AlertActivity
- implements DialogInterface.OnClickListener, TextWatcher, OnEditorActionListener {
+ implements OnClickListener, TextWatcher, OnEditorActionListener {
protected UserManager mUserManager;
protected boolean mHasRestrictionsPin;
protected EditText mPinText;
protected TextView mPinErrorMessage;
- protected TextView mPinMessage;
+ private Button mOkButton;
+ private Button mCancelButton;
@Override
public void onCreate(Bundle icicle) {
@@ -59,19 +59,20 @@
protected void initUi() {
AlertController.AlertParams ap = mAlertParams;
- ap.mTitle = getString(R.string.restr_pin_enter_pin);
- ap.mPositiveButtonText = getString(R.string.ok);
- ap.mNegativeButtonText = getString(R.string.cancel);
- ap.mPositiveButtonListener = this;
- ap.mNegativeButtonListener = this;
+ ap.mTitle = getString(R.string.restr_pin_enter_admin_pin);
LayoutInflater inflater =
(LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
ap.mView = inflater.inflate(R.layout.restrictions_pin_challenge, null);
- mPinMessage = (TextView) ap.mView.findViewById(R.id.pin_message);
- mPinText = (EditText) ap.mView.findViewById(R.id.pin_text);
mPinErrorMessage = (TextView) ap.mView.findViewById(R.id.pin_error_message);
+ mPinText = (EditText) ap.mView.findViewById(R.id.pin_text);
+ mOkButton = (Button) ap.mView.findViewById(R.id.pin_ok_button);
+ mCancelButton = (Button) ap.mView.findViewById(R.id.pin_cancel_button);
+
mPinText.addTextChangedListener(this);
+
+ mOkButton.setOnClickListener(this);
+ mCancelButton.setOnClickListener(this);
}
protected boolean verifyingPin() {
@@ -84,8 +85,7 @@
setPositiveButtonState(false);
boolean hasPin = mUserManager.hasRestrictionsPin();
if (hasPin) {
- mPinMessage.setVisibility(View.GONE);
- mPinErrorMessage.setVisibility(View.GONE);
+ mPinErrorMessage.setVisibility(View.INVISIBLE);
mPinText.setOnEditorActionListener(this);
updatePinTimer(-1);
} else if (verifyingPin()) {
@@ -94,39 +94,37 @@
}
}
- private void setPositiveButtonState(boolean enabled) {
- mAlert.getButton(DialogInterface.BUTTON_POSITIVE).setEnabled(enabled);
+ protected void setPositiveButtonState(boolean enabled) {
+ mOkButton.setEnabled(enabled);
}
- private void updatePinTimer(int pinTimerMs) {
+ private boolean updatePinTimer(int pinTimerMs) {
if (pinTimerMs < 0) {
pinTimerMs = mUserManager.checkRestrictionsPin(null);
}
+ boolean enableInput;
if (pinTimerMs >= 200) {
- final int seconds = (pinTimerMs + 200) / 1000;
- final String formatString = getResources().getQuantityString(
- R.plurals.restr_pin_countdown,
- seconds);
- mPinErrorMessage.setText(String.format(formatString, seconds));
+ // Do the count down timer for less than a minute, otherwise just say try again later.
+ if (pinTimerMs <= 60000) {
+ final int seconds = (pinTimerMs + 200) / 1000;
+ final String formatString = getResources().getQuantityString(
+ R.plurals.restr_pin_countdown,
+ seconds);
+ mPinErrorMessage.setText(String.format(formatString, seconds));
+ } else {
+ mPinErrorMessage.setText(R.string.restr_pin_try_later);
+ }
+ enableInput = false;
mPinErrorMessage.setVisibility(View.VISIBLE);
- mPinText.setEnabled(false);
mPinText.setText("");
- setPositiveButtonState(false);
mPinText.postDelayed(mCountdownRunnable, Math.min(1000, pinTimerMs));
} else {
- mPinErrorMessage.setVisibility(View.INVISIBLE);
- mPinText.setEnabled(true);
- mPinText.setText("");
+ enableInput = true;
+ mPinErrorMessage.setText(R.string.restr_pin_incorrect);
}
- }
-
- public void onClick(DialogInterface dialog, int which) {
- setResult(RESULT_CANCELED);
- if (which == AlertDialog.BUTTON_POSITIVE) {
- performPositiveButtonAction();
- } else if (which == AlertDialog.BUTTON_NEGATIVE) {
- finish();
- }
+ mPinText.setEnabled(enableInput);
+ setPositiveButtonState(enableInput);
+ return enableInput;
}
protected void performPositiveButtonAction() {
@@ -135,7 +133,10 @@
setResult(RESULT_OK);
finish();
} else if (result >= 0) {
+ mPinErrorMessage.setText(R.string.restr_pin_incorrect);
+ mPinErrorMessage.setVisibility(View.VISIBLE);
updatePinTimer(result);
+ mPinText.setText("");
}
}
@@ -161,7 +162,20 @@
private Runnable mCountdownRunnable = new Runnable() {
public void run() {
- updatePinTimer(-1);
+ if (updatePinTimer(-1)) {
+ // If we are no longer counting down, clear the message.
+ mPinErrorMessage.setVisibility(View.INVISIBLE);
+ }
}
};
+
+ @Override
+ public void onClick(View v) {
+ if (v == mOkButton) {
+ performPositiveButtonAction();
+ } else if (v == mCancelButton) {
+ setResult(RESULT_CANCELED);
+ finish();
+ }
+ }
}
diff --git a/core/java/com/android/internal/app/RestrictionsPinSetupActivity.java b/core/java/com/android/internal/app/RestrictionsPinSetupActivity.java
index 1d09292..f7fc6c6 100644
--- a/core/java/com/android/internal/app/RestrictionsPinSetupActivity.java
+++ b/core/java/com/android/internal/app/RestrictionsPinSetupActivity.java
@@ -16,9 +16,7 @@
package com.android.internal.app;
-import android.app.AlertDialog;
import android.content.Context;
-import android.content.DialogInterface;
import android.os.UserManager;
import android.text.Editable;
import android.text.TextUtils;
@@ -44,17 +42,13 @@
ap.mTitle = getString(R.string.restr_pin_enter_pin);
ap.mPositiveButtonText = getString(R.string.ok);
ap.mNegativeButtonText = getString(R.string.cancel);
- ap.mPositiveButtonListener = this;
- ap.mNegativeButtonListener = this;
LayoutInflater inflater =
(LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
ap.mView = inflater.inflate(R.layout.restrictions_pin_setup, null);
mPinText = (EditText) ap.mView.findViewById(R.id.pin_text);
- mPinMessage = (TextView) ap.mView.findViewById(R.id.pin_message);
mNewPinText = (EditText) ap.mView.findViewById(R.id.pin_new_text);
mConfirmPinText = (EditText) ap.mView.findViewById(R.id.pin_confirm_text);
- mPinErrorMessage = (TextView) ap.mView.findViewById(R.id.pin_error_message);
mNewPinText.addTextChangedListener(this);
mConfirmPinText.addTextChangedListener(this);
@@ -72,19 +66,7 @@
return false;
}
- private void setPositiveButtonState(boolean enabled) {
- mAlert.getButton(DialogInterface.BUTTON_POSITIVE).setEnabled(enabled);
- }
-
- public void onClick(DialogInterface dialog, int which) {
- setResult(RESULT_CANCELED);
- if (which == AlertDialog.BUTTON_POSITIVE) {
- performPositiveButtonAction();
- } else if (which == AlertDialog.BUTTON_NEGATIVE) {
- finish();
- }
- }
-
+ @Override
protected void performPositiveButtonAction() {
if (mHasRestrictionsPin) {
int result = mUserManager.checkRestrictionsPin(mPinText.getText().toString());
@@ -115,7 +97,6 @@
boolean showError = !TextUtils.isEmpty(pin1) && !TextUtils.isEmpty(pin2);
// TODO: Check recovery email address as well
setPositiveButtonState(match);
- mPinErrorMessage.setVisibility((match || !showError) ? View.INVISIBLE : View.VISIBLE);
}
@Override
diff --git a/core/jni/android/graphics/BitmapRegionDecoder.cpp b/core/jni/android/graphics/BitmapRegionDecoder.cpp
index 6646579..ee47ac4 100644
--- a/core/jni/android/graphics/BitmapRegionDecoder.cpp
+++ b/core/jni/android/graphics/BitmapRegionDecoder.cpp
@@ -141,7 +141,7 @@
jboolean isShareable) {
jobject brd = NULL;
// for now we don't allow shareable with java inputstreams
- SkStream* stream = CopyJavaInputStream(env, is, storage);
+ SkStreamRewindable* stream = CopyJavaInputStream(env, is, storage);
if (stream) {
brd = createBitmapRegionDecoder(env, stream);
diff --git a/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp b/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp
index 797d155..d264392 100644
--- a/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp
+++ b/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp
@@ -159,8 +159,8 @@
friend class RewindableJavaStream;
};
-SkStream* WrapJavaInputStream(JNIEnv* env, jobject stream,
- jbyteArray storage) {
+SkStream* CreateJavaInputStreamAdaptor(JNIEnv* env, jobject stream,
+ jbyteArray storage) {
static bool gInited;
if (!gInited) {
@@ -190,6 +190,7 @@
return new JavaInputStreamAdaptor(env, stream, storage);
}
+
static SkMemoryStream* adaptor_to_mem_stream(SkStream* adaptor) {
SkASSERT(adaptor != NULL);
SkDynamicMemoryWStream wStream;
@@ -203,9 +204,9 @@
return new SkMemoryStream(data.get());
}
-SkMemoryStream* CopyJavaInputStream(JNIEnv* env, jobject stream,
- jbyteArray storage) {
- SkAutoTUnref<SkStream> adaptor(WrapJavaInputStream(env, stream, storage));
+SkStreamRewindable* CopyJavaInputStream(JNIEnv* env, jobject stream,
+ jbyteArray storage) {
+ SkAutoTUnref<SkStream> adaptor(CreateJavaInputStreamAdaptor(env, stream, storage));
if (NULL == adaptor.get()) {
return NULL;
}
@@ -302,7 +303,7 @@
SkStreamRewindable* GetRewindableStream(JNIEnv* env, jobject stream,
jbyteArray storage) {
- SkAutoTUnref<SkStream> adaptor(WrapJavaInputStream(env, stream, storage));
+ SkAutoTUnref<SkStream> adaptor(CreateJavaInputStreamAdaptor(env, stream, storage));
if (NULL == adaptor.get()) {
return NULL;
}
diff --git a/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.h b/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.h
index 5218dc5..fcc0c9a 100644
--- a/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.h
+++ b/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.h
@@ -24,8 +24,8 @@
* function returns, since the Java InputStream is not managed
* by the SkStream.
*/
-SkStream* WrapJavaInputStream(JNIEnv* env, jobject stream,
- jbyteArray storage);
+SkStream* CreateJavaInputStreamAdaptor(JNIEnv* env, jobject stream,
+ jbyteArray storage);
/**
* Copy a Java InputStream.
@@ -33,13 +33,11 @@
* @param stream Pointer to Java InputStream.
* @param storage Java byte array for retrieving data from the
* Java InputStream.
- * @return SkMemoryStream The data in stream will be copied to a new
- * SkMemoryStream.
- * FIXME: Could return a more generic return type if ViewStateSerializer
- * did not require an SkMemoryStream.
+ * @return SkStreamRewindable The data in stream will be copied
+ * to a new SkStreamRewindable.
*/
-SkMemoryStream* CopyJavaInputStream(JNIEnv* env, jobject stream,
- jbyteArray storage);
+SkStreamRewindable* CopyJavaInputStream(JNIEnv* env, jobject stream,
+ jbyteArray storage);
/**
* Get a rewindable stream from a Java InputStream.
@@ -50,7 +48,7 @@
* @return SkStreamRewindable Either a wrapper around the Java
* InputStream, if possible, or a copy which is rewindable.
* Since it may be a wrapper, must not be used after the
- * caller returns, like the result of WrapJavaInputStream.
+ * caller returns, like the result of CreateJavaInputStreamAdaptor.
*/
SkStreamRewindable* GetRewindableStream(JNIEnv* env, jobject stream,
jbyteArray storage);
@@ -69,5 +67,4 @@
SkWStream* CreateJavaOutputStreamAdaptor(JNIEnv* env, jobject stream,
jbyteArray storage);
-
#endif
diff --git a/core/jni/android/graphics/Picture.cpp b/core/jni/android/graphics/Picture.cpp
index dff2b18..fcf22b8 100644
--- a/core/jni/android/graphics/Picture.cpp
+++ b/core/jni/android/graphics/Picture.cpp
@@ -39,9 +39,10 @@
static SkPicture* deserialize(JNIEnv* env, jobject, jobject jstream,
jbyteArray jstorage) {
SkPicture* picture = NULL;
- SkAutoTUnref<SkStream> strm(WrapJavaInputStream(env, jstream, jstorage));
- if (strm.get()) {
- picture = SkPicture::CreateFromStream(strm.get());
+ SkStream* strm = CreateJavaInputStreamAdaptor(env, jstream, jstorage);
+ if (strm) {
+ picture = SkPicture::CreateFromStream(strm);
+ delete strm;
}
return picture;
}
diff --git a/core/jni/android_hardware_SensorManager.cpp b/core/jni/android_hardware_SensorManager.cpp
index ae0113b..4290a6e 100644
--- a/core/jni/android_hardware_SensorManager.cpp
+++ b/core/jni/android_hardware_SensorManager.cpp
@@ -31,6 +31,7 @@
static struct {
jclass clazz;
jmethodID dispatchSensorEvent;
+ jmethodID dispatchFlushCompleteEvent;
} gBaseEventQueueClassInfo;
namespace android {
@@ -46,6 +47,8 @@
jfieldID resolution;
jfieldID power;
jfieldID minDelay;
+ jfieldID fifoReservedEventCount;
+ jfieldID fifoMaxEventCount;
} gSensorOffsets;
@@ -67,6 +70,9 @@
sensorOffsets.resolution = _env->GetFieldID(sensorClass, "mResolution","F");
sensorOffsets.power = _env->GetFieldID(sensorClass, "mPower", "F");
sensorOffsets.minDelay = _env->GetFieldID(sensorClass, "mMinDelay", "I");
+ sensorOffsets.fifoReservedEventCount =
+ _env->GetFieldID(sensorClass, "mFifoReservedEventCount", "I");
+ sensorOffsets.fifoMaxEventCount = _env->GetFieldID(sensorClass, "mFifoMaxEventCount", "I");
}
static jint
@@ -78,7 +84,7 @@
size_t count = mgr.getSensorList(&sensorList);
if (size_t(next) >= count)
return -1;
-
+
Sensor const* const list = sensorList[next];
const SensorOffsets& sensorOffsets(gSensorOffsets);
jstring name = env->NewStringUTF(list->getName().string());
@@ -92,7 +98,9 @@
env->SetFloatField(sensor, sensorOffsets.resolution, list->getResolution());
env->SetFloatField(sensor, sensorOffsets.power, list->getPowerUsage());
env->SetIntField(sensor, sensorOffsets.minDelay, list->getMinDelay());
-
+ env->SetIntField(sensor, sensorOffsets.fifoReservedEventCount,
+ list->getFifoReservedEventCount());
+ env->SetIntField(sensor, sensorOffsets.fifoMaxEventCount, list->getFifoMaxEventCount());
next++;
return size_t(next) < count ? next : 0;
}
@@ -150,12 +158,20 @@
env->SetFloatArrayRegion(mScratch, 0, 16, buffer[i].data);
}
- env->CallVoidMethod(mReceiverObject,
- gBaseEventQueueClassInfo.dispatchSensorEvent,
- buffer[i].sensor,
- mScratch,
- buffer[i].vector.status,
- buffer[i].timestamp);
+ if (buffer[i].type == SENSOR_TYPE_META_DATA) {
+ // This is a flush complete sensor event. Call dispatchFlushCompleteEvent
+ // method.
+ env->CallVoidMethod(mReceiverObject,
+ gBaseEventQueueClassInfo.dispatchFlushCompleteEvent,
+ buffer[i].meta_data.sensor);
+ } else {
+ env->CallVoidMethod(mReceiverObject,
+ gBaseEventQueueClassInfo.dispatchSensorEvent,
+ buffer[i].sensor,
+ mScratch,
+ buffer[i].vector.status,
+ buffer[i].timestamp);
+ }
if (env->ExceptionCheck()) {
ALOGE("Exception dispatching input event.");
@@ -186,9 +202,11 @@
return jint(receiver.get());
}
-static jint nativeEnableSensor(JNIEnv *env, jclass clazz, jint eventQ, jint handle, jint us) {
+static jint nativeEnableSensor(JNIEnv *env, jclass clazz, jint eventQ, jint handle, jint rate_us,
+ jint maxBatchReportLatency, jint reservedFlags) {
sp<Receiver> receiver(reinterpret_cast<Receiver *>(eventQ));
- return receiver->getSensorEventQueue()->enableSensor(handle, us);
+ return receiver->getSensorEventQueue()->enableSensor(handle, rate_us, maxBatchReportLatency,
+ reservedFlags);
}
static jint nativeDisableSensor(JNIEnv *env, jclass clazz, jint eventQ, jint handle) {
@@ -202,6 +220,10 @@
receiver->decStrong((void*)nativeInitSensorEventQueue);
}
+static jint nativeFlushSensor(JNIEnv *env, jclass clazz, jint eventQ, jint handle) {
+ sp<Receiver> receiver(reinterpret_cast<Receiver *>(eventQ));
+ return receiver->getSensorEventQueue()->flushSensor(handle);
+}
//----------------------------------------------------------------------------
@@ -221,7 +243,7 @@
(void*)nativeInitSensorEventQueue },
{"nativeEnableSensor",
- "(III)I",
+ "(IIIII)I",
(void*)nativeEnableSensor },
{"nativeDisableSensor",
@@ -231,6 +253,10 @@
{"nativeDestroySensorEventQueue",
"(I)V",
(void*)nativeDestroySensorEventQueue },
+
+ {"nativeFlushSensor",
+ "(II)I",
+ (void*)nativeFlushSensor },
};
}; // namespace android
@@ -260,5 +286,9 @@
gBaseEventQueueClassInfo.clazz,
"dispatchSensorEvent", "(I[FIJ)V");
+ GET_METHOD_ID(gBaseEventQueueClassInfo.dispatchFlushCompleteEvent,
+ gBaseEventQueueClassInfo.clazz,
+ "dispatchFlushCompleteEvent", "(I)V");
+
return 0;
}
diff --git a/core/res/res/layout/restrictions_pin_challenge.xml b/core/res/res/layout/restrictions_pin_challenge.xml
index 954af92..f41924c 100644
--- a/core/res/res/layout/restrictions_pin_challenge.xml
+++ b/core/res/res/layout/restrictions_pin_challenge.xml
@@ -18,42 +18,73 @@
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:layout_marginTop="48dp"
- android:layout_marginBottom="48dp"
android:overScrollMode="ifContentScrolls">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:padding="8dip"
android:orientation="vertical">
-
- <TextView android:id="@+id/pin_message"
- style="?android:attr/textAppearanceMedium"
- android:layout_marginTop="16dp"
- android:layout_marginBottom="16dp"
+ <LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:text="@string/restr_pin_create_pin"
- android:textColor="?android:attr/textColorSecondary" />
+ android:padding="8dip"
+ android:orientation="vertical">
- <EditText android:id="@+id/pin_text"
- style="?android:attr/textAppearanceMedium"
- android:layout_marginBottom="16dp"
+ <EditText android:id="@+id/pin_text"
+ android:layout_marginLeft="8dip"
+ android:layout_marginStart="8dip"
+ android:layout_marginRight="8dip"
+ android:layout_marginEnd="8dip"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:inputType="numberPassword"
+ android:textColor="?android:attr/textColorPrimary" />
+
+ <TextView android:id="@+id/pin_error_message"
+ android:layout_marginTop="8dp"
+ android:layout_marginBottom="8dp"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/restr_pin_incorrect"
+ android:gravity="center"/>
+ </LinearLayout>
+
+ <LinearLayout android:id="@+id/buttonPanel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:hint="@string/restr_pin_enter_pin"
- android:inputType="numberPassword"
- android:textColor="?android:attr/textColorPrimary" />
-
- <TextView android:id="@+id/pin_error_message"
- style="?android:attr/textAppearanceSmall"
- android:layout_marginBottom="16dp"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/restr_pin_error_doesnt_match"
- android:textColor="#FFFF0000" />
-
+ android:minHeight="@dimen/alert_dialog_button_bar_height"
+ android:orientation="vertical"
+ android:divider="?android:attr/dividerHorizontal"
+ android:showDividers="beginning"
+ android:dividerPadding="0dip">
+ <LinearLayout
+ style="?android:attr/buttonBarStyle"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:layoutDirection="locale"
+ android:measureWithLargestChild="true">
+ <Button android:id="@+id/pin_cancel_button"
+ android:layout_width="wrap_content"
+ android:layout_gravity="start"
+ android:layout_weight="1"
+ android:maxLines="2"
+ android:minHeight="@dimen/alert_dialog_button_bar_height"
+ style="?android:attr/buttonBarButtonStyle"
+ android:textSize="14sp"
+ android:layout_height="wrap_content"
+ android:text="@string/cancel" />
+ <Button android:id="@+id/pin_ok_button"
+ android:layout_width="wrap_content"
+ android:layout_gravity="end"
+ android:layout_weight="1"
+ android:maxLines="2"
+ style="?android:attr/buttonBarButtonStyle"
+ android:textSize="14sp"
+ android:minHeight="@dimen/alert_dialog_button_bar_height"
+ android:layout_height="wrap_content"
+ android:text="@string/ok" />
+ </LinearLayout>
+ </LinearLayout>
</LinearLayout>
-
</ScrollView>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index f8cffa7..b6a4250 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -2611,6 +2611,9 @@
<!-- Whether the device must be unlocked before routing data to this service.
The default is false.-->
<attr name="requireDeviceUnlock" format="boolean"/>
+ <!-- A drawable that can be rendered in Android's system UI for representing
+ the service. -->
+ <attr name="apduServiceBanner" format="reference"/>
</declare-styleable>
<!-- Use <code>offhost-apdu-service</code> as the root tag of the XML resource that
@@ -2621,6 +2624,9 @@
<!-- Short description of the functionality the service implements. This attribute
is mandatory.-->
<attr name="description" />
+ <!-- A drawable that can be rendered in Android's system UI for representing
+ the service. -->
+ <attr name="apduServiceBanner"/>
</declare-styleable>
<!-- Specify one or more <code>aid-group</code> elements inside a
@@ -5749,11 +5755,11 @@
describes an injected "Location services" setting. Note that the status value (subtitle)
for the setting is specified dynamically by a subclass of SettingInjectorService.
-->
- <declare-styleable name="InjectedLocationSetting">
- <!-- The user-visible name (title) of the setting. -->
- <attr name="label"/>
- <!-- The icon for the apps covered by the setting. Typically a generic icon for the
- developer. -->
+ <declare-styleable name="SettingInjectorService">
+ <!-- The title for the preference. -->
+ <attr name="title"/>
+ <!-- The icon for the preference, should refer to all apps covered by the setting. Typically
+ a generic icon for the developer. -->
<attr name="icon"/>
<!-- The activity to launch when the setting is clicked on. -->
<attr name="settingsActivity"/>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index d4a408d..dd233c5 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -774,6 +774,9 @@
re-validation -->
<bool name="config_bluetooth_address_validation">false</bool>
+ <!-- Boolean indicating if current platform supports BLE peripheral mode -->
+ <bool name="config_bluetooth_le_peripheral_mode_supported">false</bool>
+
<!-- The default data-use polling period. -->
<integer name="config_datause_polling_period_sec">600</integer>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 696e782..dce2db0 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2074,5 +2074,6 @@
<public type="attr" name="autoMirrored" />
<public type="attr" name="supportsSwitchingToNextInputMethod" />
<public type="attr" name="requireDeviceUnlock" />
+ <public type="attr" name="apduServiceBanner" />
</resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 4b32e2b..f5f9bf6 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -4296,8 +4296,12 @@
<!-- Print fail reason: unknown. [CHAR LIMIT=25] -->
<string name="reason_unknown">unknown</string>
+ <!-- PIN entry dialog title for entering the administrator PIN [CHAR LIMIT=none] -->
+ <string name="restr_pin_enter_admin_pin">Enter administrator PIN</string>
<!-- PIN entry dialog label/hint for PIN [CHAR LIMIT=none] -->
<string name="restr_pin_enter_pin">Enter PIN</string>
+ <!-- PIN entry dialog label/hint for incorrect PIN entry [CHAR LIMIT=none] -->
+ <string name="restr_pin_incorrect">Incorrect</string>
<!-- PIN entry dialog label/hint for old PIN [CHAR LIMIT=none] -->
<string name="restr_pin_enter_old_pin">Current PIN</string>
<!-- PIN entry dialog label for new PIN [CHAR LIMIT=none] -->
@@ -4313,9 +4317,11 @@
<!-- PIN entry dialog countdown message for next chance to enter the PIN [CHAR LIMIT=none] -->
<!-- Phrase describing a time duration using seconds [CHAR LIMIT=16] -->
<plurals name="restr_pin_countdown">
- <item quantity="one">Incorrect PIN. Try again in 1 second.</item>
- <item quantity="other">Incorrect PIN. Try again in <xliff:g id="count">%d</xliff:g> seconds.</item>
+ <item quantity="one">Try again in 1 second</item>
+ <item quantity="other">Try again in <xliff:g id="count">%d</xliff:g> seconds</item>
</plurals>
+ <!-- PIN entry dialog tells the user to not enter a PIN for a while. [CHAR LIMIT=none] -->
+ <string name="restr_pin_try_later">Try again later</string>
<!-- Toast bar message when hiding the transient navigation bar [CHAR LIMIT=35] -->
<string name="transient_navigation_confirmation">Swipe edge of screen to reveal bar</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index f008b10..2b3c287 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -212,7 +212,8 @@
<java-symbol type="id" name="sms_short_code_remember_undo_instruction" />
<java-symbol type="id" name="breadcrumb_section" />
<java-symbol type="id" name="action_bar_spinner" />
- <java-symbol type="id" name="pin_message" />
+ <java-symbol type="id" name="pin_cancel_button" />
+ <java-symbol type="id" name="pin_ok_button" />
<java-symbol type="id" name="pin_text" />
<java-symbol type="id" name="pin_new_text" />
<java-symbol type="id" name="pin_confirm_text" />
@@ -248,6 +249,7 @@
<java-symbol type="bool" name="config_allowActionMenuItemTextWithIcon" />
<java-symbol type="bool" name="config_bluetooth_address_validation" />
<java-symbol type="bool" name="config_bluetooth_sco_off_call" />
+ <java-symbol type="bool" name="config_bluetooth_le_peripheral_mode_supported" />
<java-symbol type="bool" name="config_cellBroadcastAppLinks" />
<java-symbol type="bool" name="config_duplicate_port_omadm_wappush" />
<java-symbol type="bool" name="config_enable_emergency_call_while_sim_locked" />
@@ -870,7 +872,10 @@
<java-symbol type="string" name="mediaSize_na_ledger" />
<java-symbol type="string" name="mediaSize_na_tabloid" />
<java-symbol type="string" name="reason_unknown" />
+ <java-symbol type="string" name="restr_pin_enter_admin_pin" />
<java-symbol type="string" name="restr_pin_enter_pin" />
+ <java-symbol type="string" name="restr_pin_incorrect" />
+ <java-symbol type="string" name="restr_pin_try_later" />
<java-symbol type="string" name="write_fail_reason_cancelled" />
<java-symbol type="string" name="write_fail_reason_cannot_write" />
<java-symbol type="string" name="transient_navigation_confirmation" />
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index 1f38ddb..89d102d 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -126,7 +126,6 @@
interact with the system. -->
<assign-permission name="android.permission.MODIFY_AUDIO_SETTINGS" uid="media" />
- <assign-permission name="android.permission.ACCESS_DRM" uid="media" />
<assign-permission name="android.permission.ACCESS_SURFACE_FLINGER" uid="media" />
<assign-permission name="android.permission.WAKE_LOCK" uid="media" />
<assign-permission name="android.permission.UPDATE_APP_OPS_STATS" uid="media" />
diff --git a/docs/html/distribute/googleplay/promote/brand.jd b/docs/html/distribute/googleplay/promote/brand.jd
index 265584f..0bda561 100644
--- a/docs/html/distribute/googleplay/promote/brand.jd
+++ b/docs/html/distribute/googleplay/promote/brand.jd
@@ -21,8 +21,8 @@
<ul>
<li>Android™ should have a trademark symbol the first time it appears in a creative.</li>
<li>Android should always be capitalized and is never plural or possessive.</li>
- <li>"Android" by itself cannot be used in the name of an application name or accessory product.
-Instead use "for Android."
+ <li>"Android" cannot be used in names of applications or accessory products,
+ including phones, tablets, TVs, speakers, headphones, watches, and other devices. Instead use "for Android".
<ul>
<li><span style="color:red">Incorrect</span>: "Android MediaPlayer"</li>
<li><span style="color:green">Correct</span>: "MediaPlayer for Android"</li>
diff --git a/drm/java/android/drm/DrmManagerClient.java b/drm/java/android/drm/DrmManagerClient.java
index e2606d6..10cdab0 100644
--- a/drm/java/android/drm/DrmManagerClient.java
+++ b/drm/java/android/drm/DrmManagerClient.java
@@ -63,8 +63,6 @@
private final CloseGuard mCloseGuard = CloseGuard.get();
- private static final String EXTENDED_INFO_DATA = "extended_info_data";
-
static {
// Load the respective library
System.loadLibrary("drmframework_jni");
@@ -186,22 +184,8 @@
DrmManagerClient instance = (DrmManagerClient)((WeakReference)thisReference).get();
if (null != instance && null != instance.mInfoHandler) {
- DrmInfoEvent event = new DrmInfoEvent(uniqueId, infoType, message);
Message m = instance.mInfoHandler.obtainMessage(
- InfoHandler.INFO_EVENT_TYPE, event);
- instance.mInfoHandler.sendMessage(m);
- }
- }
-
- private static void notify(
- Object thisReference, int uniqueId, int infoType, String message,
- HashMap<String, Object> attributes) {
- DrmManagerClient instance = (DrmManagerClient)((WeakReference)thisReference).get();
-
- if (null != instance && null != instance.mInfoHandler) {
- DrmInfoEvent event = new DrmInfoEvent(uniqueId, infoType, message, attributes);
- Message m = instance.mInfoHandler.obtainMessage(
- InfoHandler.INFO_EVENT_TYPE, event);
+ InfoHandler.INFO_EVENT_TYPE, uniqueId, infoType, message);
instance.mInfoHandler.sendMessage(m);
}
}
@@ -214,25 +198,23 @@
}
public void handleMessage(Message msg) {
- DrmInfoEvent info = (DrmInfoEvent) msg.obj;
+ DrmInfoEvent info = null;
DrmErrorEvent error = null;
- int uniqueId;
- int eventType;
- String message;
switch (msg.what) {
case InfoHandler.INFO_EVENT_TYPE:
- uniqueId = info.getUniqueId();
- eventType = info.getType();
- message = info.getMessage();
+ int uniqueId = msg.arg1;
+ int infoType = msg.arg2;
+ String message = msg.obj.toString();
- switch (eventType) {
+ switch (infoType) {
case DrmInfoEvent.TYPE_REMOVE_RIGHTS: {
try {
DrmUtils.removeFile(message);
} catch (IOException e) {
e.printStackTrace();
}
+ info = new DrmInfoEvent(uniqueId, infoType, message);
break;
}
case DrmInfoEvent.TYPE_ALREADY_REGISTERED_BY_ANOTHER_ACCOUNT:
@@ -240,11 +222,11 @@
case DrmInfoEvent.TYPE_WAIT_FOR_RIGHTS:
case DrmInfoEvent.TYPE_ACCOUNT_ALREADY_REGISTERED:
case DrmInfoEvent.TYPE_RIGHTS_REMOVED: {
+ info = new DrmInfoEvent(uniqueId, infoType, message);
break;
}
default:
- info = null;
- error = new DrmErrorEvent(uniqueId, eventType, message);
+ error = new DrmErrorEvent(uniqueId, infoType, message);
break;
}
diff --git a/drm/jni/android_drm_DrmManagerClient.cpp b/drm/jni/android_drm_DrmManagerClient.cpp
index 7fce3d0..baddf62 100644
--- a/drm/jni/android_drm_DrmManagerClient.cpp
+++ b/drm/jni/android_drm_DrmManagerClient.cpp
@@ -169,49 +169,11 @@
JNIEnv *env = AndroidRuntime::getJNIEnv();
jstring message = env->NewStringUTF(event.getMessage().string());
ALOGV("JNIOnInfoListener::onInfo => %d | %d | %s", uniqueId, type, event.getMessage().string());
- const DrmBuffer& drmBuffer = event.getData();
- if (event.getCount() > 0 || drmBuffer.length > 0) {
- jclass hashMapClazz = env->FindClass("java/util/HashMap");
- jmethodID hashMapInitId = env->GetMethodID(hashMapClazz, "<init>", "()V");
- jmethodID hashMapPutId = env->GetMethodID(hashMapClazz, "put",
- "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
- jobject hashMapObject = env->NewObject(hashMapClazz, hashMapInitId);
- env->DeleteLocalRef(hashMapClazz);
- if (0 < drmBuffer.length) {
- jfieldID fid = env->GetStaticFieldID(
- mClass, "EXTENDED_INFO_DATA", "Ljava/lang/String;");
- jstring key = (jstring) env->GetStaticObjectField(mClass, fid);
-
- jbyteArray valueByte = env->NewByteArray(drmBuffer.length);
- env->SetByteArrayRegion(valueByte, 0, drmBuffer.length, (jbyte*) drmBuffer.data);
- env->CallObjectMethod(hashMapObject, hashMapPutId, key, valueByte);
- env->DeleteLocalRef(valueByte);
- env->DeleteLocalRef(key);
- }
- DrmInfoEvent::KeyIterator keyIt = event.keyIterator();
- while (keyIt.hasNext()) {
- String8 mapKey = keyIt.next();
- jstring key = env->NewStringUTF(mapKey.string());
- jstring value = env->NewStringUTF(event.get(mapKey).string());
- env->CallObjectMethod(hashMapObject, hashMapPutId, key, value);
- env->DeleteLocalRef(value);
- env->DeleteLocalRef(key);
- }
- env->CallStaticVoidMethod(
- mClass,
- env->GetStaticMethodID(mClass, "notify",
- "(Ljava/lang/Object;IILjava/lang/String;Ljava/util/HashMap;)V"),
- mObject, uniqueId, type, message, hashMapObject);
- env->DeleteLocalRef(hashMapObject);
- } else {
- env->CallStaticVoidMethod(
- mClass,
- env->GetStaticMethodID(mClass, "notify",
- "(Ljava/lang/Object;IILjava/lang/String;)V"),
- mObject, uniqueId, type, message);
- }
- env->DeleteLocalRef(message);
+ env->CallStaticVoidMethod(
+ mClass,
+ env->GetStaticMethodID(mClass, "notify", "(Ljava/lang/Object;IILjava/lang/String;)V"),
+ mObject, uniqueId, type, message);
}
static Mutex sLock;
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index c971da5..495bbca7 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -419,6 +419,10 @@
*
* It is recommended to use {@link #ARGB_8888} instead of this
* configuration.
+ *
+ * Note: as of {@link android.os.Build.VERSION_CODES#KEY_LIME_PIE},
+ * any bitmap created with this configuration will be created
+ * using {@link #ARGB_8888} instead.
*
* @deprecated Because of the poor quality of this configuration,
* it is advised to use {@link #ARGB_8888} instead.
diff --git a/graphics/java/android/graphics/BitmapFactory.java b/graphics/java/android/graphics/BitmapFactory.java
index 1721bee..8872de0 100644
--- a/graphics/java/android/graphics/BitmapFactory.java
+++ b/graphics/java/android/graphics/BitmapFactory.java
@@ -81,7 +81,7 @@
* function to ensure that you are using the bitmap that was used as the
* decode destination.</p>
*
- * @see Bitmap#reconfigure(int,int,Config)
+ * @see Bitmap#reconfigure(int,int, android.graphics.Bitmap.Config)
*/
public Bitmap inBitmap;
diff --git a/graphics/java/android/graphics/LinearGradient.java b/graphics/java/android/graphics/LinearGradient.java
index 54cdcab..4c88de3 100644
--- a/graphics/java/android/graphics/LinearGradient.java
+++ b/graphics/java/android/graphics/LinearGradient.java
@@ -102,8 +102,8 @@
final LinearGradient copy;
switch (mType) {
case TYPE_COLORS_AND_POSITIONS:
- copy = new LinearGradient(mX0, mY0, mX1, mY1, mColors.clone(), mPositions.clone(),
- mTileMode);
+ copy = new LinearGradient(mX0, mY0, mX1, mY1, mColors.clone(),
+ mPositions != null ? mPositions.clone() : null, mTileMode);
break;
case TYPE_COLOR_START_AND_COLOR_END:
copy = new LinearGradient(mX0, mY0, mX1, mY1, mColor0, mColor1, mTileMode);
diff --git a/graphics/java/android/graphics/RadialGradient.java b/graphics/java/android/graphics/RadialGradient.java
index 23244d8..f011e5c 100644
--- a/graphics/java/android/graphics/RadialGradient.java
+++ b/graphics/java/android/graphics/RadialGradient.java
@@ -103,8 +103,8 @@
final RadialGradient copy;
switch (mType) {
case TYPE_COLORS_AND_POSITIONS:
- copy = new RadialGradient(mX, mY, mRadius, mColors.clone(), mPositions.clone(),
- mTileMode);
+ copy = new RadialGradient(mX, mY, mRadius, mColors.clone(),
+ mPositions != null ? mPositions.clone() : null, mTileMode);
break;
case TYPE_COLOR_CENTER_AND_COLOR_EDGE:
copy = new RadialGradient(mX, mY, mRadius, mColor0, mColor1, mTileMode);
diff --git a/graphics/java/android/graphics/SweepGradient.java b/graphics/java/android/graphics/SweepGradient.java
index 3010927..e9cda39 100644
--- a/graphics/java/android/graphics/SweepGradient.java
+++ b/graphics/java/android/graphics/SweepGradient.java
@@ -92,7 +92,8 @@
final SweepGradient copy;
switch (mType) {
case TYPE_COLORS_AND_POSITIONS:
- copy = new SweepGradient(mCx, mCy, mColors.clone(), mPositions.clone());
+ copy = new SweepGradient(mCx, mCy, mColors.clone(),
+ mPositions != null ? mPositions.clone() : null);
break;
case TYPE_COLOR_START_AND_COLOR_END:
copy = new SweepGradient(mCx, mCy, mColor0, mColor1);
diff --git a/keystore/java/android/security/AndroidKeyPairGenerator.java b/keystore/java/android/security/AndroidKeyPairGenerator.java
index 390e732..1ab0aeb 100644
--- a/keystore/java/android/security/AndroidKeyPairGenerator.java
+++ b/keystore/java/android/security/AndroidKeyPairGenerator.java
@@ -18,6 +18,7 @@
import com.android.org.bouncycastle.x509.X509V3CertificateGenerator;
+import com.android.org.conscrypt.NativeCrypto;
import com.android.org.conscrypt.OpenSSLEngine;
import java.security.InvalidAlgorithmParameterException;
@@ -33,7 +34,10 @@
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.DSAParameterSpec;
+import java.security.spec.ECParameterSpec;
import java.security.spec.InvalidKeySpecException;
+import java.security.spec.RSAKeyGenParameterSpec;
import java.security.spec.X509EncodedKeySpec;
/**
@@ -87,8 +91,12 @@
Credentials.deleteAllTypesForAlias(mKeyStore, alias);
+ final int keyType = KeyStore.getKeyTypeForAlgorithm(mSpec.getKeyType());
+ byte[][] args = getArgsForKeyType(keyType, mSpec.getAlgorithmParameterSpec());
+
final String privateKeyAlias = Credentials.USER_PRIVATE_KEY + alias;
- if (!mKeyStore.generate(privateKeyAlias, KeyStore.UID_SELF, mSpec.getFlags())) {
+ if (!mKeyStore.generate(privateKeyAlias, KeyStore.UID_SELF, keyType,
+ mSpec.getKeySize(), mSpec.getFlags(), args)) {
throw new IllegalStateException("could not generate key in keystore");
}
@@ -104,10 +112,10 @@
final PublicKey pubKey;
try {
- final KeyFactory keyFact = KeyFactory.getInstance("RSA");
+ final KeyFactory keyFact = KeyFactory.getInstance(mSpec.getKeyType());
pubKey = keyFact.generatePublic(new X509EncodedKeySpec(pubKeyBytes));
} catch (NoSuchAlgorithmException e) {
- throw new IllegalStateException("Can't instantiate RSA key generator", e);
+ throw new IllegalStateException("Can't instantiate key generator", e);
} catch (InvalidKeySpecException e) {
throw new IllegalStateException("keystore returned invalid key encoding", e);
}
@@ -119,7 +127,7 @@
certGen.setIssuerDN(mSpec.getSubjectDN());
certGen.setNotBefore(mSpec.getStartDate());
certGen.setNotAfter(mSpec.getEndDate());
- certGen.setSignatureAlgorithm("sha1WithRSA");
+ certGen.setSignatureAlgorithm(getDefaultSignatureAlgorithmForKeyType(mSpec.getKeyType()));
final X509Certificate cert;
try {
@@ -146,6 +154,37 @@
return new KeyPair(pubKey, privKey);
}
+ private static String getDefaultSignatureAlgorithmForKeyType(String keyType) {
+ if ("RSA".equalsIgnoreCase(keyType)) {
+ return "sha256WithRSA";
+ } else if ("DSA".equalsIgnoreCase(keyType)) {
+ return "sha1WithDSA";
+ } else if ("EC".equalsIgnoreCase(keyType)) {
+ return "sha256WithECDSA";
+ } else {
+ throw new IllegalArgumentException("Unsupported key type " + keyType);
+ }
+ }
+
+ private static byte[][] getArgsForKeyType(int keyType, AlgorithmParameterSpec spec) {
+ switch (keyType) {
+ case NativeCrypto.EVP_PKEY_RSA:
+ if (spec instanceof RSAKeyGenParameterSpec) {
+ RSAKeyGenParameterSpec rsaSpec = (RSAKeyGenParameterSpec) spec;
+ return new byte[][] { rsaSpec.getPublicExponent().toByteArray() };
+ }
+ break;
+ case NativeCrypto.EVP_PKEY_DSA:
+ if (spec instanceof DSAParameterSpec) {
+ DSAParameterSpec dsaSpec = (DSAParameterSpec) spec;
+ return new byte[][] { dsaSpec.getG().toByteArray(),
+ dsaSpec.getP().toByteArray(), dsaSpec.getQ().toByteArray() };
+ }
+ break;
+ }
+ return null;
+ }
+
@Override
public void initialize(int keysize, SecureRandom random) {
throw new IllegalArgumentException("cannot specify keysize with AndroidKeyPairGenerator");
diff --git a/keystore/java/android/security/KeyPairGeneratorSpec.java b/keystore/java/android/security/KeyPairGeneratorSpec.java
index 59f89bc..21d6caa 100644
--- a/keystore/java/android/security/KeyPairGeneratorSpec.java
+++ b/keystore/java/android/security/KeyPairGeneratorSpec.java
@@ -16,13 +16,18 @@
package android.security;
+import com.android.org.conscrypt.NativeCrypto;
+
import android.content.Context;
import android.text.TextUtils;
import java.math.BigInteger;
+import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.cert.Certificate;
import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.DSAParameterSpec;
+import java.security.spec.RSAKeyGenParameterSpec;
import java.util.Date;
import javax.security.auth.x500.X500Principal;
@@ -50,10 +55,35 @@
* certificate signed by a real Certificate Authority.
*/
public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec {
- private final String mKeystoreAlias;
+ /*
+ * These must be kept in sync with system/security/keystore/defaults.h
+ */
+
+ /* DSA */
+ private static final int DSA_DEFAULT_KEY_SIZE = 1024;
+ private static final int DSA_MIN_KEY_SIZE = 512;
+ private static final int DSA_MAX_KEY_SIZE = 8192;
+
+ /* EC */
+ private static final int EC_DEFAULT_KEY_SIZE = 256;
+ private static final int EC_MIN_KEY_SIZE = 192;
+ private static final int EC_MAX_KEY_SIZE = 521;
+
+ /* RSA */
+ private static final int RSA_DEFAULT_KEY_SIZE = 2048;
+ private static final int RSA_MIN_KEY_SIZE = 512;
+ private static final int RSA_MAX_KEY_SIZE = 8192;
private final Context mContext;
+ private final String mKeystoreAlias;
+
+ private final String mKeyType;
+
+ private final int mKeySize;
+
+ private final AlgorithmParameterSpec mSpec;
+
private final X500Principal mSubjectDN;
private final BigInteger mSerialNumber;
@@ -84,6 +114,9 @@
* @param context Android context for the activity
* @param keyStoreAlias name to use for the generated key in the Android
* keystore
+ * @param keyType key algorithm to use (RSA, DSA, EC)
+ * @param keySize size of key to generate
+ * @param spec the underlying key type parameters
* @param subjectDN X.509 v3 Subject Distinguished Name
* @param serialNumber X509 v3 certificate serial number
* @param startDate the start of the self-signed certificate validity period
@@ -93,9 +126,9 @@
* {@code endDate} is before {@code startDate}.
* @hide should be built with KeyPairGeneratorSpecBuilder
*/
- public KeyPairGeneratorSpec(Context context, String keyStoreAlias,
- X500Principal subjectDN, BigInteger serialNumber, Date startDate, Date endDate,
- int flags) {
+ public KeyPairGeneratorSpec(Context context, String keyStoreAlias, String keyType, int keySize,
+ AlgorithmParameterSpec spec, X500Principal subjectDN, BigInteger serialNumber,
+ Date startDate, Date endDate, int flags) {
if (context == null) {
throw new IllegalArgumentException("context == null");
} else if (TextUtils.isEmpty(keyStoreAlias)) {
@@ -112,8 +145,18 @@
throw new IllegalArgumentException("endDate < startDate");
}
+ final int keyTypeInt = KeyStore.getKeyTypeForAlgorithm(keyType);
+ if (keySize == -1) {
+ keySize = getDefaultKeySizeForType(keyTypeInt);
+ }
+ checkCorrectParametersSpec(keyTypeInt, keySize, spec);
+ checkValidKeySize(keyTypeInt, keySize);
+
mContext = context;
mKeystoreAlias = keyStoreAlias;
+ mKeyType = keyType;
+ mKeySize = keySize;
+ mSpec = spec;
mSubjectDN = subjectDN;
mSerialNumber = serialNumber;
mStartDate = startDate;
@@ -121,6 +164,64 @@
mFlags = flags;
}
+ private static int getDefaultKeySizeForType(int keyType) {
+ if (keyType == NativeCrypto.EVP_PKEY_DSA) {
+ return DSA_DEFAULT_KEY_SIZE;
+ } else if (keyType == NativeCrypto.EVP_PKEY_EC) {
+ return EC_DEFAULT_KEY_SIZE;
+ } else if (keyType == NativeCrypto.EVP_PKEY_RSA) {
+ return RSA_DEFAULT_KEY_SIZE;
+ }
+ throw new IllegalArgumentException("Invalid key type " + keyType);
+ }
+
+ private static void checkValidKeySize(int keyType, int keySize) {
+ if (keyType == NativeCrypto.EVP_PKEY_DSA) {
+ if (keySize < DSA_MIN_KEY_SIZE || keySize > DSA_MAX_KEY_SIZE) {
+ throw new IllegalArgumentException("DSA keys must be >= " + DSA_MIN_KEY_SIZE
+ + " and <= " + DSA_MAX_KEY_SIZE);
+ }
+ } else if (keyType == NativeCrypto.EVP_PKEY_EC) {
+ if (keySize < EC_MIN_KEY_SIZE || keySize > EC_MAX_KEY_SIZE) {
+ throw new IllegalArgumentException("EC keys must be >= " + EC_MIN_KEY_SIZE
+ + " and <= " + EC_MAX_KEY_SIZE);
+ }
+ } else if (keyType == NativeCrypto.EVP_PKEY_RSA) {
+ if (keySize < RSA_MIN_KEY_SIZE || keySize > RSA_MAX_KEY_SIZE) {
+ throw new IllegalArgumentException("RSA keys must be >= " + RSA_MIN_KEY_SIZE
+ + " and <= " + RSA_MAX_KEY_SIZE);
+ }
+ } else {
+ throw new IllegalArgumentException("Invalid key type " + keyType);
+ }
+ }
+
+ private static void checkCorrectParametersSpec(int keyType, int keySize,
+ AlgorithmParameterSpec spec) {
+ if (keyType == NativeCrypto.EVP_PKEY_DSA && spec != null) {
+ if (!(spec instanceof DSAParameterSpec)) {
+ throw new IllegalArgumentException("DSA keys must have DSAParameterSpec specified");
+ }
+ } else if (keyType == NativeCrypto.EVP_PKEY_RSA && spec != null) {
+ if (spec instanceof RSAKeyGenParameterSpec) {
+ RSAKeyGenParameterSpec rsaSpec = (RSAKeyGenParameterSpec) spec;
+ if (keySize != -1 && keySize != rsaSpec.getKeysize()) {
+ throw new IllegalArgumentException("RSA key size must match: " + keySize
+ + " vs " + rsaSpec.getKeysize());
+ }
+ } else {
+ throw new IllegalArgumentException("RSA may only use RSAKeyGenParameterSpec");
+ }
+ }
+ }
+
+ /**
+ * 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}.
@@ -130,10 +231,28 @@
}
/**
- * Gets the Android context used for operations with this instance.
+ * Returns the key type (e.g., "RSA", "DSA", "EC") specified by this
+ * parameter.
*/
- public Context getContext() {
- return mContext;
+ public String getKeyType() {
+ return mKeyType;
+ }
+
+ /**
+ * Returns the key size specified by this parameter. For instance, for RSA
+ * this will return the modulus size and for EC it will return the field
+ * size.
+ */
+ public int getKeySize() {
+ return mKeySize;
+ }
+
+ /**
+ * Returns the {@link AlgorithmParameterSpec} that will be used for creation
+ * of the key pair.
+ */
+ public AlgorithmParameterSpec getAlgorithmParameterSpec() {
+ return mSpec;
}
/**
@@ -209,6 +328,12 @@
private String mKeystoreAlias;
+ private String mKeyType = "RSA";
+
+ private int mKeySize = -1;
+
+ private AlgorithmParameterSpec mSpec;
+
private X500Principal mSubjectDN;
private BigInteger mSerialNumber;
@@ -246,6 +371,49 @@
}
/**
+ * Sets the key type (e.g., RSA, DSA, EC) of the keypair to be created.
+ */
+ public Builder setKeyType(String keyType) throws NoSuchAlgorithmException {
+ if (keyType == null) {
+ throw new NullPointerException("keyType == null");
+ } else {
+ try {
+ KeyStore.getKeyTypeForAlgorithm(keyType);
+ } catch (IllegalArgumentException e) {
+ throw new NoSuchAlgorithmException("Unsupported key type: " + keyType);
+ }
+ }
+ mKeyType = keyType;
+ return this;
+ }
+
+ /**
+ * Sets the key size for the keypair to be created. For instance, for a
+ * key type of RSA this will set the modulus size and for a key type of
+ * EC it will select a curve with a matching field size.
+ */
+ public Builder setKeySize(int keySize) {
+ if (keySize < 0) {
+ throw new IllegalArgumentException("keySize < 0");
+ }
+ mKeySize = keySize;
+ return this;
+ }
+
+ /**
+ * Sets the underlying key type's parameters. This is required for DSA
+ * where you must set this to an instance of
+ * {@link java.security.spec.DSAParameterSpec}.
+ */
+ public Builder setAlgorithmParameterSpec(AlgorithmParameterSpec spec) {
+ if (spec == null) {
+ throw new NullPointerException("spec == null");
+ }
+ mSpec = spec;
+ return this;
+ }
+
+ /**
* Sets the subject used for the self-signed certificate of the
* generated key pair.
*/
@@ -311,8 +479,8 @@
* @return built instance of {@code KeyPairGeneratorSpec}
*/
public KeyPairGeneratorSpec build() {
- return new KeyPairGeneratorSpec(mContext, mKeystoreAlias, mSubjectDN,
- mSerialNumber, mStartDate, mEndDate, mFlags);
+ return new KeyPairGeneratorSpec(mContext, mKeystoreAlias, mKeyType, mKeySize, mSpec,
+ mSubjectDN, mSerialNumber, mStartDate, mEndDate, mFlags);
}
}
}
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index fb5e039..9babb94 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -16,6 +16,8 @@
package android.security;
+import com.android.org.conscrypt.NativeCrypto;
+
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.Log;
@@ -64,6 +66,18 @@
return new KeyStore(keystore);
}
+ static int getKeyTypeForAlgorithm(String keyType) throws IllegalArgumentException {
+ if ("RSA".equalsIgnoreCase(keyType)) {
+ return NativeCrypto.EVP_PKEY_RSA;
+ } else if ("DSA".equalsIgnoreCase(keyType)) {
+ return NativeCrypto.EVP_PKEY_DSA;
+ } else if ("EC".equalsIgnoreCase(keyType)) {
+ return NativeCrypto.EVP_PKEY_EC;
+ } else {
+ throw new IllegalArgumentException("Unsupported key type: " + keyType);
+ }
+ }
+
public State state() {
final int ret;
try {
@@ -188,9 +202,10 @@
}
}
- public boolean generate(String key, int uid, int flags) {
+ public boolean generate(String key, int uid, int keyType, int keySize, int flags,
+ byte[][] args) {
try {
- return mBinder.generate(key, uid, flags) == NO_ERROR;
+ return mBinder.generate(key, uid, keyType, keySize, flags, args) == NO_ERROR;
} catch (RemoteException e) {
Log.w(TAG, "Cannot connect to keystore", e);
return false;
diff --git a/keystore/tests/src/android/security/AndroidKeyPairGeneratorTest.java b/keystore/tests/src/android/security/AndroidKeyPairGeneratorTest.java
index 1582f74..ea6c43d 100644
--- a/keystore/tests/src/android/security/AndroidKeyPairGeneratorTest.java
+++ b/keystore/tests/src/android/security/AndroidKeyPairGeneratorTest.java
@@ -27,6 +27,13 @@
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
+import java.security.interfaces.DSAParams;
+import java.security.interfaces.DSAPublicKey;
+import java.security.interfaces.ECPublicKey;
+import java.security.interfaces.RSAPublicKey;
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.DSAParameterSpec;
+import java.security.spec.RSAKeyGenParameterSpec;
import java.text.SimpleDateFormat;
import java.util.Date;
@@ -118,6 +125,8 @@
mGenerator.initialize(
new KeyPairGeneratorSpec.Builder(getContext())
.setAlias(TEST_ALIAS_1)
+ .setKeyType("RSA")
+ .setKeySize(1024)
.setSubject(TEST_DN_1)
.setSerialNumber(TEST_SERIAL_1)
.setStartDate(NOW)
@@ -142,10 +151,207 @@
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);
+ assertKeyPairCorrect(pair, TEST_ALIAS_1, "RSA", 2048, null, TEST_DN_1, TEST_SERIAL_1, NOW,
+ NOW_PLUS_10_YEARS);
}
- public void testKeyPairGenerator_GenerateKeyPair_Unencrypted_Success() throws Exception {
+ public void testKeyPairGenerator_GenerateKeyPair_DSA_Unencrypted_Success() throws Exception {
+ mGenerator.initialize(new KeyPairGeneratorSpec.Builder(getContext())
+ .setAlias(TEST_ALIAS_1)
+ .setKeyType("DSA")
+ .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);
+
+ assertKeyPairCorrect(pair, TEST_ALIAS_1, "DSA", 1024, null, TEST_DN_1, TEST_SERIAL_1, NOW,
+ NOW_PLUS_10_YEARS);
+ }
+
+ public void testKeyPairGenerator_GenerateKeyPair_DSA_2048_Unencrypted_Success()
+ throws Exception {
+ mGenerator.initialize(new KeyPairGeneratorSpec.Builder(getContext())
+ .setAlias(TEST_ALIAS_1)
+ .setKeyType("DSA")
+ .setKeySize(2048)
+ .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);
+
+ assertKeyPairCorrect(pair, TEST_ALIAS_1, "DSA", 2048, null, TEST_DN_1, TEST_SERIAL_1, NOW,
+ NOW_PLUS_10_YEARS);
+ }
+
+ public void testKeyPairGenerator_GenerateKeyPair_DSA_SpecifiedParams_Unencrypted_Success()
+ throws Exception {
+ /*
+ * generated using: openssl dsaparam -C 2048
+ */
+ BigInteger p = new BigInteger(1, new byte[] {
+ (byte) 0xC0, (byte) 0x3D, (byte) 0x86, (byte) 0x09, (byte) 0xCA, (byte) 0x8C,
+ (byte) 0x37, (byte) 0xCA, (byte) 0xCC, (byte) 0x4A, (byte) 0x81, (byte) 0xBD,
+ (byte) 0xD8, (byte) 0x50, (byte) 0x77, (byte) 0xCD, (byte) 0xDD, (byte) 0x32,
+ (byte) 0x0B, (byte) 0x43, (byte) 0xBF, (byte) 0x42, (byte) 0x06, (byte) 0x5A,
+ (byte) 0x3D, (byte) 0x18, (byte) 0x50, (byte) 0x47, (byte) 0x79, (byte) 0xE1,
+ (byte) 0x5B, (byte) 0x86, (byte) 0x03, (byte) 0xB9, (byte) 0x28, (byte) 0x9C,
+ (byte) 0x18, (byte) 0xA9, (byte) 0xF5, (byte) 0xD6, (byte) 0xF4, (byte) 0x94,
+ (byte) 0x5B, (byte) 0x87, (byte) 0x58, (byte) 0xCA, (byte) 0xB2, (byte) 0x1E,
+ (byte) 0xFC, (byte) 0xED, (byte) 0x37, (byte) 0xC3, (byte) 0x49, (byte) 0xAC,
+ (byte) 0xFA, (byte) 0x46, (byte) 0xDB, (byte) 0x7A, (byte) 0x50, (byte) 0x96,
+ (byte) 0xCF, (byte) 0x52, (byte) 0xD7, (byte) 0x4E, (byte) 0xEB, (byte) 0x26,
+ (byte) 0x41, (byte) 0xA2, (byte) 0x6F, (byte) 0x99, (byte) 0x80, (byte) 0x9F,
+ (byte) 0x0F, (byte) 0x0A, (byte) 0xA8, (byte) 0x0D, (byte) 0xAC, (byte) 0xAB,
+ (byte) 0xEF, (byte) 0x7D, (byte) 0xE7, (byte) 0x4C, (byte) 0xF1, (byte) 0x88,
+ (byte) 0x44, (byte) 0xC9, (byte) 0x17, (byte) 0xD0, (byte) 0xBB, (byte) 0xE2,
+ (byte) 0x01, (byte) 0x8C, (byte) 0xC1, (byte) 0x02, (byte) 0x1D, (byte) 0x3C,
+ (byte) 0x15, (byte) 0xB7, (byte) 0x41, (byte) 0x30, (byte) 0xD8, (byte) 0x11,
+ (byte) 0xBD, (byte) 0x6A, (byte) 0x2A, (byte) 0x0D, (byte) 0x36, (byte) 0x44,
+ (byte) 0x9C, (byte) 0x3F, (byte) 0x32, (byte) 0xE2, (byte) 0x1C, (byte) 0xFB,
+ (byte) 0xE3, (byte) 0xFF, (byte) 0xCC, (byte) 0x1A, (byte) 0x72, (byte) 0x38,
+ (byte) 0x37, (byte) 0x69, (byte) 0x5E, (byte) 0x35, (byte) 0x73, (byte) 0xE1,
+ (byte) 0x1E, (byte) 0x74, (byte) 0x35, (byte) 0x44, (byte) 0x07, (byte) 0xB5,
+ (byte) 0x2F, (byte) 0x0B, (byte) 0x60, (byte) 0xF4, (byte) 0xA9, (byte) 0xE0,
+ (byte) 0x81, (byte) 0xB2, (byte) 0xCD, (byte) 0x8B, (byte) 0x82, (byte) 0x76,
+ (byte) 0x7F, (byte) 0xD4, (byte) 0x17, (byte) 0x32, (byte) 0x86, (byte) 0x98,
+ (byte) 0x7C, (byte) 0x85, (byte) 0x66, (byte) 0xF6, (byte) 0x77, (byte) 0xED,
+ (byte) 0x8B, (byte) 0x1A, (byte) 0x52, (byte) 0x16, (byte) 0xDA, (byte) 0x1C,
+ (byte) 0xA7, (byte) 0x16, (byte) 0x79, (byte) 0x20, (byte) 0x1C, (byte) 0x99,
+ (byte) 0x5F, (byte) 0x12, (byte) 0x66, (byte) 0x15, (byte) 0x9F, (byte) 0xE5,
+ (byte) 0x73, (byte) 0xA9, (byte) 0x61, (byte) 0xBA, (byte) 0xA7, (byte) 0x23,
+ (byte) 0x93, (byte) 0x77, (byte) 0xB5, (byte) 0xF6, (byte) 0xEC, (byte) 0x13,
+ (byte) 0xBF, (byte) 0x95, (byte) 0x60, (byte) 0x78, (byte) 0x84, (byte) 0xE3,
+ (byte) 0x44, (byte) 0xEC, (byte) 0x74, (byte) 0xC2, (byte) 0xCB, (byte) 0xD4,
+ (byte) 0x70, (byte) 0xC5, (byte) 0x7B, (byte) 0xF8, (byte) 0x07, (byte) 0x3B,
+ (byte) 0xEB, (byte) 0x9F, (byte) 0xC9, (byte) 0x7D, (byte) 0xE0, (byte) 0xA5,
+ (byte) 0xBA, (byte) 0x68, (byte) 0x7B, (byte) 0xF4, (byte) 0x70, (byte) 0x40,
+ (byte) 0xAE, (byte) 0xE9, (byte) 0x65, (byte) 0xEE, (byte) 0x5B, (byte) 0x71,
+ (byte) 0x36, (byte) 0x0B, (byte) 0xB0, (byte) 0xA2, (byte) 0x98, (byte) 0x7D,
+ (byte) 0xE3, (byte) 0x24, (byte) 0x95, (byte) 0x2B, (byte) 0xC2, (byte) 0x0A,
+ (byte) 0x78, (byte) 0x3D, (byte) 0xCC, (byte) 0x3A, (byte) 0xEE, (byte) 0xED,
+ (byte) 0x48, (byte) 0xEB, (byte) 0xA3, (byte) 0x78, (byte) 0xA8, (byte) 0x9D,
+ (byte) 0x0A, (byte) 0x8F, (byte) 0x9E, (byte) 0x59, (byte) 0x2C, (byte) 0x44,
+ (byte) 0xB5, (byte) 0xF9, (byte) 0x53, (byte) 0x43,
+ });
+
+ BigInteger q = new BigInteger(1, new byte[] {
+ (byte) 0xA1, (byte) 0x9B, (byte) 0x1D, (byte) 0xC0, (byte) 0xE3, (byte) 0xF6,
+ (byte) 0x4A, (byte) 0x35, (byte) 0xE1, (byte) 0x8A, (byte) 0x43, (byte) 0xC2,
+ (byte) 0x9C, (byte) 0xF9, (byte) 0x52, (byte) 0x8F, (byte) 0x94, (byte) 0xA1,
+ (byte) 0x12, (byte) 0x11, (byte) 0xDB, (byte) 0x9A, (byte) 0xB6, (byte) 0x35,
+ (byte) 0x56, (byte) 0x26, (byte) 0x60, (byte) 0x89, (byte) 0x11, (byte) 0xAC,
+ (byte) 0xA8, (byte) 0xE5,
+ });
+
+ BigInteger g = new BigInteger(1, new byte[] {
+ (byte) 0xA1, (byte) 0x5C, (byte) 0x57, (byte) 0x15, (byte) 0xC3, (byte) 0xD9,
+ (byte) 0xD7, (byte) 0x41, (byte) 0x89, (byte) 0xD6, (byte) 0xB8, (byte) 0x7B,
+ (byte) 0xF3, (byte) 0xE0, (byte) 0xB3, (byte) 0xC5, (byte) 0xD1, (byte) 0xAA,
+ (byte) 0xF9, (byte) 0x55, (byte) 0x48, (byte) 0xF1, (byte) 0xDA, (byte) 0xE8,
+ (byte) 0x6F, (byte) 0x51, (byte) 0x05, (byte) 0xB2, (byte) 0xC9, (byte) 0x64,
+ (byte) 0xDA, (byte) 0x5F, (byte) 0xD4, (byte) 0xAA, (byte) 0xFD, (byte) 0x67,
+ (byte) 0xE0, (byte) 0x10, (byte) 0x2C, (byte) 0x1F, (byte) 0x03, (byte) 0x10,
+ (byte) 0xD4, (byte) 0x4B, (byte) 0x20, (byte) 0x82, (byte) 0x2B, (byte) 0x04,
+ (byte) 0xF9, (byte) 0x09, (byte) 0xAE, (byte) 0x28, (byte) 0x3D, (byte) 0x9B,
+ (byte) 0xFF, (byte) 0x87, (byte) 0x76, (byte) 0xCD, (byte) 0xF0, (byte) 0x11,
+ (byte) 0xB7, (byte) 0xEA, (byte) 0xE6, (byte) 0xCD, (byte) 0x60, (byte) 0xD3,
+ (byte) 0x8C, (byte) 0x74, (byte) 0xD3, (byte) 0x45, (byte) 0x63, (byte) 0x69,
+ (byte) 0x3F, (byte) 0x1D, (byte) 0x31, (byte) 0x25, (byte) 0x49, (byte) 0x97,
+ (byte) 0x4B, (byte) 0x73, (byte) 0x34, (byte) 0x12, (byte) 0x73, (byte) 0x27,
+ (byte) 0x4C, (byte) 0xDA, (byte) 0xF3, (byte) 0x08, (byte) 0xA8, (byte) 0xA9,
+ (byte) 0x27, (byte) 0xE4, (byte) 0xB8, (byte) 0xD6, (byte) 0xB5, (byte) 0xC4,
+ (byte) 0x18, (byte) 0xED, (byte) 0xBD, (byte) 0x6F, (byte) 0xA2, (byte) 0x36,
+ (byte) 0xA2, (byte) 0x9C, (byte) 0x27, (byte) 0x62, (byte) 0x7F, (byte) 0x93,
+ (byte) 0xD7, (byte) 0x52, (byte) 0xA9, (byte) 0x76, (byte) 0x55, (byte) 0x99,
+ (byte) 0x00, (byte) 0x5B, (byte) 0xC2, (byte) 0xB9, (byte) 0x18, (byte) 0xAC,
+ (byte) 0x6B, (byte) 0x83, (byte) 0x0D, (byte) 0xA1, (byte) 0xC5, (byte) 0x01,
+ (byte) 0x1A, (byte) 0xE5, (byte) 0x4D, (byte) 0x2F, (byte) 0xCF, (byte) 0x5D,
+ (byte) 0xB2, (byte) 0xE7, (byte) 0xC7, (byte) 0xCB, (byte) 0x2C, (byte) 0xFF,
+ (byte) 0x51, (byte) 0x1B, (byte) 0x9D, (byte) 0xA4, (byte) 0x05, (byte) 0xEB,
+ (byte) 0x17, (byte) 0xD8, (byte) 0x97, (byte) 0x9D, (byte) 0x0C, (byte) 0x59,
+ (byte) 0x92, (byte) 0x8A, (byte) 0x03, (byte) 0x34, (byte) 0xFD, (byte) 0x16,
+ (byte) 0x0F, (byte) 0x2A, (byte) 0xF9, (byte) 0x7D, (byte) 0xC3, (byte) 0x41,
+ (byte) 0x0D, (byte) 0x06, (byte) 0x5A, (byte) 0x4B, (byte) 0x34, (byte) 0xD5,
+ (byte) 0xF5, (byte) 0x09, (byte) 0x1C, (byte) 0xCE, (byte) 0xA7, (byte) 0x19,
+ (byte) 0x6D, (byte) 0x04, (byte) 0x53, (byte) 0x71, (byte) 0xCC, (byte) 0x84,
+ (byte) 0xA0, (byte) 0xB2, (byte) 0xA0, (byte) 0x68, (byte) 0xA3, (byte) 0x40,
+ (byte) 0xC0, (byte) 0x67, (byte) 0x38, (byte) 0x96, (byte) 0x73, (byte) 0x2E,
+ (byte) 0x8E, (byte) 0x2A, (byte) 0x9D, (byte) 0x56, (byte) 0xE9, (byte) 0xAC,
+ (byte) 0xC7, (byte) 0xEC, (byte) 0x84, (byte) 0x7F, (byte) 0xFC, (byte) 0xE0,
+ (byte) 0x69, (byte) 0x03, (byte) 0x8B, (byte) 0x48, (byte) 0x64, (byte) 0x76,
+ (byte) 0x85, (byte) 0xA5, (byte) 0x10, (byte) 0xD9, (byte) 0x31, (byte) 0xC3,
+ (byte) 0x8B, (byte) 0x07, (byte) 0x48, (byte) 0x62, (byte) 0xF6, (byte) 0x68,
+ (byte) 0xF2, (byte) 0x96, (byte) 0xB2, (byte) 0x18, (byte) 0x5B, (byte) 0xFF,
+ (byte) 0x6D, (byte) 0xD1, (byte) 0x6B, (byte) 0xF5, (byte) 0xFD, (byte) 0x81,
+ (byte) 0xF1, (byte) 0xFD, (byte) 0x04, (byte) 0xF0, (byte) 0x9F, (byte) 0xB7,
+ (byte) 0x08, (byte) 0x95, (byte) 0x57, (byte) 0x48, (byte) 0x07, (byte) 0x00,
+ (byte) 0x52, (byte) 0xEC, (byte) 0x75, (byte) 0x91, (byte) 0x02, (byte) 0x11,
+ (byte) 0xA3, (byte) 0x64, (byte) 0x26, (byte) 0xCA,
+ });
+
+ AlgorithmParameterSpec spec = new DSAParameterSpec(p, q, g);
+ mGenerator.initialize(new KeyPairGeneratorSpec.Builder(getContext())
+ .setAlias(TEST_ALIAS_1)
+ .setKeyType("DSA")
+ .setKeySize(2048)
+ .setAlgorithmParameterSpec(spec)
+ .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);
+
+ assertKeyPairCorrect(pair, TEST_ALIAS_1, "DSA", 2048, spec, TEST_DN_1, TEST_SERIAL_1, NOW,
+ NOW_PLUS_10_YEARS);
+ }
+
+ public void testKeyPairGenerator_GenerateKeyPair_EC_Unencrypted_Success() throws Exception {
+ mGenerator.initialize(new KeyPairGeneratorSpec.Builder(getContext())
+ .setAlias(TEST_ALIAS_1)
+ .setKeyType("EC")
+ .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);
+
+ assertKeyPairCorrect(pair, TEST_ALIAS_1, "EC", 256, null, TEST_DN_1, TEST_SERIAL_1, NOW,
+ NOW_PLUS_10_YEARS);
+ }
+
+ public void testKeyPairGenerator_GenerateKeyPair_EC_P521_Unencrypted_Success() throws Exception {
+ mGenerator.initialize(new KeyPairGeneratorSpec.Builder(getContext())
+ .setAlias(TEST_ALIAS_1)
+ .setKeyType("EC")
+ .setKeySize(521)
+ .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);
+
+ assertKeyPairCorrect(pair, TEST_ALIAS_1, "EC", 521, null, TEST_DN_1, TEST_SERIAL_1, NOW,
+ NOW_PLUS_10_YEARS);
+ }
+
+ public void testKeyPairGenerator_GenerateKeyPair_RSA_Unencrypted_Success() throws Exception {
mGenerator.initialize(new KeyPairGeneratorSpec.Builder(getContext())
.setAlias(TEST_ALIAS_1)
.setSubject(TEST_DN_1)
@@ -157,7 +363,28 @@
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);
+ assertKeyPairCorrect(pair, TEST_ALIAS_1, "RSA", 2048, null, TEST_DN_1, TEST_SERIAL_1, NOW,
+ NOW_PLUS_10_YEARS);
+ }
+
+ public void testKeyPairGenerator_GenerateKeyPair_RSA_WithParams_Unencrypted_Success()
+ throws Exception {
+ AlgorithmParameterSpec spec = new RSAKeyGenParameterSpec(1024, BigInteger.valueOf(3L));
+ mGenerator.initialize(new KeyPairGeneratorSpec.Builder(getContext())
+ .setAlias(TEST_ALIAS_1)
+ .setKeySize(1024)
+ .setAlgorithmParameterSpec(spec)
+ .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);
+
+ assertKeyPairCorrect(pair, TEST_ALIAS_1, "RSA", 1024, spec, TEST_DN_1, TEST_SERIAL_1, NOW,
+ NOW_PLUS_10_YEARS);
}
public void testKeyPairGenerator_GenerateKeyPair_Replaced_Success() throws Exception {
@@ -172,8 +399,8 @@
.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);
+ assertKeyPairCorrect(pair1, TEST_ALIAS_1, "RSA", 2048, null, TEST_DN_1, TEST_SERIAL_1,
+ NOW, NOW_PLUS_10_YEARS);
}
// Replace the original key
@@ -187,8 +414,8 @@
.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,
- NOW_PLUS_10_YEARS);
+ assertKeyPairCorrect(pair2, TEST_ALIAS_2, "RSA", 2048, null, TEST_DN_2, TEST_SERIAL_2,
+ NOW, NOW_PLUS_10_YEARS);
}
}
@@ -205,8 +432,8 @@
.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);
+ assertKeyPairCorrect(pair1, TEST_ALIAS_1, "RSA", 2048, null, TEST_DN_1, TEST_SERIAL_1,
+ NOW, NOW_PLUS_10_YEARS);
}
// Attempt to replace previous key
@@ -230,18 +457,45 @@
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);
+ assertKeyPairCorrect(pair2, TEST_ALIAS_1, "RSA", 2048, null, 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 {
+ private void assertKeyPairCorrect(KeyPair pair, String alias, String keyType, int keySize,
+ AlgorithmParameterSpec spec, X500Principal dn, BigInteger serial, Date start, Date end)
+ throws Exception {
final PublicKey pubKey = pair.getPublic();
assertNotNull("The PublicKey for the KeyPair should be not null", pubKey);
+ assertEquals(keyType, pubKey.getAlgorithm());
+
+ if ("DSA".equalsIgnoreCase(keyType)) {
+ DSAPublicKey dsaPubKey = (DSAPublicKey) pubKey;
+ DSAParams actualParams = dsaPubKey.getParams();
+ assertEquals(keySize, (actualParams.getP().bitLength() + 7) & ~7);
+ if (spec != null) {
+ DSAParameterSpec expectedParams = (DSAParameterSpec) spec;
+ assertEquals(expectedParams.getP(), actualParams.getP());
+ assertEquals(expectedParams.getQ(), actualParams.getQ());
+ assertEquals(expectedParams.getG(), actualParams.getG());
+ }
+ } else if ("EC".equalsIgnoreCase(keyType)) {
+ assertEquals("Curve should be what was specified during initialization", keySize,
+ ((ECPublicKey) pubKey).getParams().getCurve().getField().getFieldSize());
+ } else if ("RSA".equalsIgnoreCase(keyType)) {
+ RSAPublicKey rsaPubKey = (RSAPublicKey) pubKey;
+ assertEquals("Modulus size should be what is specified during initialization",
+ (keySize + 7) & ~7, (rsaPubKey.getModulus().bitLength() + 7) & ~7);
+ if (spec != null) {
+ RSAKeyGenParameterSpec params = (RSAKeyGenParameterSpec) spec;
+ assertEquals((keySize + 7) & ~7, (params.getKeysize() + 7) & ~7);
+ assertEquals(params.getPublicExponent(), rsaPubKey.getPublicExponent());
+ }
+ }
final PrivateKey privKey = pair.getPrivate();
assertNotNull("The PrivateKey for the KeyPair should be not null", privKey);
+ assertEquals(keyType, privKey.getAlgorithm());
final byte[] userCertBytes = mAndroidKeyStore.get(Credentials.USER_CERTIFICATE + alias);
assertNotNull("The user certificate should exist for the generated entry", userCertBytes);
diff --git a/keystore/tests/src/android/security/AndroidKeyStoreTest.java b/keystore/tests/src/android/security/AndroidKeyStoreTest.java
index b7129db..6597d3f 100644
--- a/keystore/tests/src/android/security/AndroidKeyStoreTest.java
+++ b/keystore/tests/src/android/security/AndroidKeyStoreTest.java
@@ -18,7 +18,9 @@
import com.android.org.bouncycastle.x509.X509V3CertificateGenerator;
+import com.android.org.conscrypt.NativeCrypto;
import com.android.org.conscrypt.OpenSSLEngine;
+import com.android.org.conscrypt.OpenSSLKeyHolder;
import android.test.AndroidTestCase;
@@ -39,6 +41,10 @@
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
+import java.security.interfaces.DSAPrivateKey;
+import java.security.interfaces.DSAPublicKey;
+import java.security.interfaces.ECPrivateKey;
+import java.security.interfaces.ECPublicKey;
import java.security.interfaces.RSAPrivateKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
@@ -99,7 +105,7 @@
*
* openssl x509 -outform d -in cacert.pem | xxd -i | sed 's/0x/(byte) 0x/g'
*/
- private static final byte[] FAKE_CA_1 = {
+ private static final byte[] FAKE_RSA_CA_1 = {
(byte) 0x30, (byte) 0x82, (byte) 0x02, (byte) 0xce, (byte) 0x30, (byte) 0x82,
(byte) 0x02, (byte) 0x37, (byte) 0xa0, (byte) 0x03, (byte) 0x02, (byte) 0x01,
(byte) 0x02, (byte) 0x02, (byte) 0x09, (byte) 0x00, (byte) 0xe1, (byte) 0x6a,
@@ -228,7 +234,7 @@
*
* openssl pkcs8 -topk8 -outform d -in userkey.pem -nocrypt | xxd -i | sed 's/0x/(byte) 0x/g'
*/
- private static final byte[] FAKE_KEY_1 = new byte[] {
+ private static final byte[] FAKE_RSA_KEY_1 = new byte[] {
(byte) 0x30, (byte) 0x82, (byte) 0x02, (byte) 0x78, (byte) 0x02, (byte) 0x01,
(byte) 0x00, (byte) 0x30, (byte) 0x0d, (byte) 0x06, (byte) 0x09, (byte) 0x2a,
(byte) 0x86, (byte) 0x48, (byte) 0x86, (byte) 0xf7, (byte) 0x0d, (byte) 0x01,
@@ -342,7 +348,7 @@
*
* openssl x509 -outform d -in usercert.pem | xxd -i | sed 's/0x/(byte) 0x/g'
*/
- private static final byte[] FAKE_USER_1 = new byte[] {
+ private static final byte[] FAKE_RSA_USER_1 = new byte[] {
(byte) 0x30, (byte) 0x82, (byte) 0x02, (byte) 0x95, (byte) 0x30, (byte) 0x82,
(byte) 0x01, (byte) 0xfe, (byte) 0xa0, (byte) 0x03, (byte) 0x02, (byte) 0x01,
(byte) 0x02, (byte) 0x02, (byte) 0x01, (byte) 0x01, (byte) 0x30, (byte) 0x0d,
@@ -456,6 +462,628 @@
(byte) 0x08, (byte) 0x41, (byte) 0x0a, (byte) 0xf3, (byte) 0x72
};
+ /*
+ * The keys and certificates below are generated with:
+ *
+ * openssl req -new -x509 -days 3650 -extensions v3_ca -keyout cakey.pem -out cacert.pem
+ * openssl ecparam -name prime256v1 -out ecparam.pem
+ * openssl req -newkey ec:ecparam.pem -keyout userkey.pem -nodes -days 3650 -out userkey.req
+ * mkdir -p demoCA/newcerts
+ * touch demoCA/index.txt
+ * echo "01" > demoCA/serial
+ * openssl ca -out usercert.pem -in userkey.req -cert cacert.pem -keyfile cakey.pem -days 3650
+ */
+
+ /**
+ * Generated from above and converted with:
+ *
+ * openssl x509 -outform d -in cacert.pem | xxd -i | sed 's/0x/(byte) 0x/g'
+ */
+ private static final byte[] FAKE_EC_CA_1 = {
+ (byte) 0x30, (byte) 0x82, (byte) 0x02, (byte) 0x58, (byte) 0x30, (byte) 0x82,
+ (byte) 0x01, (byte) 0xc1, (byte) 0xa0, (byte) 0x03, (byte) 0x02, (byte) 0x01,
+ (byte) 0x02, (byte) 0x02, (byte) 0x09, (byte) 0x00, (byte) 0xe1, (byte) 0xb2,
+ (byte) 0x8c, (byte) 0x04, (byte) 0x95, (byte) 0xeb, (byte) 0x10, (byte) 0xcb,
+ (byte) 0x30, (byte) 0x0d, (byte) 0x06, (byte) 0x09, (byte) 0x2a, (byte) 0x86,
+ (byte) 0x48, (byte) 0x86, (byte) 0xf7, (byte) 0x0d, (byte) 0x01, (byte) 0x01,
+ (byte) 0x05, (byte) 0x05, (byte) 0x00, (byte) 0x30, (byte) 0x45, (byte) 0x31,
+ (byte) 0x0b, (byte) 0x30, (byte) 0x09, (byte) 0x06, (byte) 0x03, (byte) 0x55,
+ (byte) 0x04, (byte) 0x06, (byte) 0x13, (byte) 0x02, (byte) 0x41, (byte) 0x55,
+ (byte) 0x31, (byte) 0x13, (byte) 0x30, (byte) 0x11, (byte) 0x06, (byte) 0x03,
+ (byte) 0x55, (byte) 0x04, (byte) 0x08, (byte) 0x0c, (byte) 0x0a, (byte) 0x53,
+ (byte) 0x6f, (byte) 0x6d, (byte) 0x65, (byte) 0x2d, (byte) 0x53, (byte) 0x74,
+ (byte) 0x61, (byte) 0x74, (byte) 0x65, (byte) 0x31, (byte) 0x21, (byte) 0x30,
+ (byte) 0x1f, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x0a,
+ (byte) 0x0c, (byte) 0x18, (byte) 0x49, (byte) 0x6e, (byte) 0x74, (byte) 0x65,
+ (byte) 0x72, (byte) 0x6e, (byte) 0x65, (byte) 0x74, (byte) 0x20, (byte) 0x57,
+ (byte) 0x69, (byte) 0x64, (byte) 0x67, (byte) 0x69, (byte) 0x74, (byte) 0x73,
+ (byte) 0x20, (byte) 0x50, (byte) 0x74, (byte) 0x79, (byte) 0x20, (byte) 0x4c,
+ (byte) 0x74, (byte) 0x64, (byte) 0x30, (byte) 0x1e, (byte) 0x17, (byte) 0x0d,
+ (byte) 0x31, (byte) 0x33, (byte) 0x30, (byte) 0x38, (byte) 0x32, (byte) 0x37,
+ (byte) 0x31, (byte) 0x36, (byte) 0x32, (byte) 0x38, (byte) 0x32, (byte) 0x38,
+ (byte) 0x5a, (byte) 0x17, (byte) 0x0d, (byte) 0x32, (byte) 0x33, (byte) 0x30,
+ (byte) 0x38, (byte) 0x32, (byte) 0x35, (byte) 0x31, (byte) 0x36, (byte) 0x32,
+ (byte) 0x38, (byte) 0x32, (byte) 0x38, (byte) 0x5a, (byte) 0x30, (byte) 0x45,
+ (byte) 0x31, (byte) 0x0b, (byte) 0x30, (byte) 0x09, (byte) 0x06, (byte) 0x03,
+ (byte) 0x55, (byte) 0x04, (byte) 0x06, (byte) 0x13, (byte) 0x02, (byte) 0x41,
+ (byte) 0x55, (byte) 0x31, (byte) 0x13, (byte) 0x30, (byte) 0x11, (byte) 0x06,
+ (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x08, (byte) 0x0c, (byte) 0x0a,
+ (byte) 0x53, (byte) 0x6f, (byte) 0x6d, (byte) 0x65, (byte) 0x2d, (byte) 0x53,
+ (byte) 0x74, (byte) 0x61, (byte) 0x74, (byte) 0x65, (byte) 0x31, (byte) 0x21,
+ (byte) 0x30, (byte) 0x1f, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04,
+ (byte) 0x0a, (byte) 0x0c, (byte) 0x18, (byte) 0x49, (byte) 0x6e, (byte) 0x74,
+ (byte) 0x65, (byte) 0x72, (byte) 0x6e, (byte) 0x65, (byte) 0x74, (byte) 0x20,
+ (byte) 0x57, (byte) 0x69, (byte) 0x64, (byte) 0x67, (byte) 0x69, (byte) 0x74,
+ (byte) 0x73, (byte) 0x20, (byte) 0x50, (byte) 0x74, (byte) 0x79, (byte) 0x20,
+ (byte) 0x4c, (byte) 0x74, (byte) 0x64, (byte) 0x30, (byte) 0x81, (byte) 0x9f,
+ (byte) 0x30, (byte) 0x0d, (byte) 0x06, (byte) 0x09, (byte) 0x2a, (byte) 0x86,
+ (byte) 0x48, (byte) 0x86, (byte) 0xf7, (byte) 0x0d, (byte) 0x01, (byte) 0x01,
+ (byte) 0x01, (byte) 0x05, (byte) 0x00, (byte) 0x03, (byte) 0x81, (byte) 0x8d,
+ (byte) 0x00, (byte) 0x30, (byte) 0x81, (byte) 0x89, (byte) 0x02, (byte) 0x81,
+ (byte) 0x81, (byte) 0x00, (byte) 0xb5, (byte) 0xf6, (byte) 0x08, (byte) 0x0f,
+ (byte) 0xc4, (byte) 0x4d, (byte) 0xe4, (byte) 0x0d, (byte) 0x34, (byte) 0x1d,
+ (byte) 0xe2, (byte) 0x23, (byte) 0x18, (byte) 0x63, (byte) 0x03, (byte) 0xf7,
+ (byte) 0x14, (byte) 0x0e, (byte) 0x98, (byte) 0xcd, (byte) 0x45, (byte) 0x1f,
+ (byte) 0xfe, (byte) 0xfb, (byte) 0x09, (byte) 0x3f, (byte) 0x5d, (byte) 0x36,
+ (byte) 0x3b, (byte) 0x0f, (byte) 0xf9, (byte) 0x5e, (byte) 0x86, (byte) 0x56,
+ (byte) 0x64, (byte) 0xd7, (byte) 0x3f, (byte) 0xae, (byte) 0x33, (byte) 0x09,
+ (byte) 0xd3, (byte) 0xdd, (byte) 0x06, (byte) 0x17, (byte) 0x26, (byte) 0xdc,
+ (byte) 0xa2, (byte) 0x8c, (byte) 0x3c, (byte) 0x65, (byte) 0xed, (byte) 0x03,
+ (byte) 0x82, (byte) 0x78, (byte) 0x9b, (byte) 0xee, (byte) 0xe3, (byte) 0x98,
+ (byte) 0x58, (byte) 0xe1, (byte) 0xf1, (byte) 0xa0, (byte) 0x85, (byte) 0xae,
+ (byte) 0x63, (byte) 0x84, (byte) 0x41, (byte) 0x46, (byte) 0xa7, (byte) 0x4f,
+ (byte) 0xdc, (byte) 0xbb, (byte) 0x1c, (byte) 0x6e, (byte) 0xec, (byte) 0x7b,
+ (byte) 0xd5, (byte) 0xab, (byte) 0x3d, (byte) 0x6a, (byte) 0x05, (byte) 0x58,
+ (byte) 0x0f, (byte) 0x9b, (byte) 0x6a, (byte) 0x67, (byte) 0x4b, (byte) 0xe9,
+ (byte) 0x2a, (byte) 0x6d, (byte) 0x96, (byte) 0x11, (byte) 0x53, (byte) 0x95,
+ (byte) 0x78, (byte) 0xaa, (byte) 0xd1, (byte) 0x91, (byte) 0x4a, (byte) 0xf8,
+ (byte) 0x54, (byte) 0x52, (byte) 0x6d, (byte) 0xb9, (byte) 0xca, (byte) 0x74,
+ (byte) 0x81, (byte) 0xf8, (byte) 0x99, (byte) 0x64, (byte) 0xd1, (byte) 0x4f,
+ (byte) 0x01, (byte) 0x38, (byte) 0x4f, (byte) 0x08, (byte) 0x5c, (byte) 0x31,
+ (byte) 0xcb, (byte) 0x7c, (byte) 0x5c, (byte) 0x78, (byte) 0x5d, (byte) 0x47,
+ (byte) 0xd9, (byte) 0xf0, (byte) 0x1a, (byte) 0xeb, (byte) 0x02, (byte) 0x03,
+ (byte) 0x01, (byte) 0x00, (byte) 0x01, (byte) 0xa3, (byte) 0x50, (byte) 0x30,
+ (byte) 0x4e, (byte) 0x30, (byte) 0x1d, (byte) 0x06, (byte) 0x03, (byte) 0x55,
+ (byte) 0x1d, (byte) 0x0e, (byte) 0x04, (byte) 0x16, (byte) 0x04, (byte) 0x14,
+ (byte) 0x5f, (byte) 0x5b, (byte) 0x5e, (byte) 0xac, (byte) 0x29, (byte) 0xfa,
+ (byte) 0xa1, (byte) 0x9f, (byte) 0x9e, (byte) 0xad, (byte) 0x46, (byte) 0xe1,
+ (byte) 0xbc, (byte) 0x20, (byte) 0x72, (byte) 0xcf, (byte) 0x4a, (byte) 0xd4,
+ (byte) 0xfa, (byte) 0xe3, (byte) 0x30, (byte) 0x1f, (byte) 0x06, (byte) 0x03,
+ (byte) 0x55, (byte) 0x1d, (byte) 0x23, (byte) 0x04, (byte) 0x18, (byte) 0x30,
+ (byte) 0x16, (byte) 0x80, (byte) 0x14, (byte) 0x5f, (byte) 0x5b, (byte) 0x5e,
+ (byte) 0xac, (byte) 0x29, (byte) 0xfa, (byte) 0xa1, (byte) 0x9f, (byte) 0x9e,
+ (byte) 0xad, (byte) 0x46, (byte) 0xe1, (byte) 0xbc, (byte) 0x20, (byte) 0x72,
+ (byte) 0xcf, (byte) 0x4a, (byte) 0xd4, (byte) 0xfa, (byte) 0xe3, (byte) 0x30,
+ (byte) 0x0c, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x1d, (byte) 0x13,
+ (byte) 0x04, (byte) 0x05, (byte) 0x30, (byte) 0x03, (byte) 0x01, (byte) 0x01,
+ (byte) 0xff, (byte) 0x30, (byte) 0x0d, (byte) 0x06, (byte) 0x09, (byte) 0x2a,
+ (byte) 0x86, (byte) 0x48, (byte) 0x86, (byte) 0xf7, (byte) 0x0d, (byte) 0x01,
+ (byte) 0x01, (byte) 0x05, (byte) 0x05, (byte) 0x00, (byte) 0x03, (byte) 0x81,
+ (byte) 0x81, (byte) 0x00, (byte) 0xa1, (byte) 0x4a, (byte) 0xe6, (byte) 0xfc,
+ (byte) 0x7f, (byte) 0x17, (byte) 0xaa, (byte) 0x65, (byte) 0x4a, (byte) 0x34,
+ (byte) 0xde, (byte) 0x69, (byte) 0x67, (byte) 0x54, (byte) 0x4d, (byte) 0xa2,
+ (byte) 0xc2, (byte) 0x98, (byte) 0x02, (byte) 0x43, (byte) 0x6a, (byte) 0x0e,
+ (byte) 0x0b, (byte) 0x7f, (byte) 0xa4, (byte) 0x46, (byte) 0xaf, (byte) 0xa4,
+ (byte) 0x65, (byte) 0xa0, (byte) 0xdb, (byte) 0xf1, (byte) 0x5b, (byte) 0xd5,
+ (byte) 0x09, (byte) 0xbc, (byte) 0xee, (byte) 0x37, (byte) 0x51, (byte) 0x19,
+ (byte) 0x36, (byte) 0xc0, (byte) 0x90, (byte) 0xd3, (byte) 0x5f, (byte) 0xf3,
+ (byte) 0x4f, (byte) 0xb9, (byte) 0x08, (byte) 0x45, (byte) 0x0e, (byte) 0x01,
+ (byte) 0x8a, (byte) 0x95, (byte) 0xef, (byte) 0x92, (byte) 0x95, (byte) 0x33,
+ (byte) 0x78, (byte) 0xdd, (byte) 0x90, (byte) 0xbb, (byte) 0xf3, (byte) 0x06,
+ (byte) 0x75, (byte) 0xd0, (byte) 0x66, (byte) 0xe6, (byte) 0xd0, (byte) 0x18,
+ (byte) 0x6e, (byte) 0xeb, (byte) 0x1c, (byte) 0x52, (byte) 0xc3, (byte) 0x2e,
+ (byte) 0x57, (byte) 0x7d, (byte) 0xa9, (byte) 0x03, (byte) 0xdb, (byte) 0xf4,
+ (byte) 0x57, (byte) 0x5f, (byte) 0x6c, (byte) 0x7e, (byte) 0x00, (byte) 0x0d,
+ (byte) 0x8f, (byte) 0xe8, (byte) 0x91, (byte) 0xf7, (byte) 0xae, (byte) 0x24,
+ (byte) 0x35, (byte) 0x07, (byte) 0xb5, (byte) 0x48, (byte) 0x2d, (byte) 0x36,
+ (byte) 0x30, (byte) 0x5d, (byte) 0xe9, (byte) 0x49, (byte) 0x2d, (byte) 0xd1,
+ (byte) 0x5d, (byte) 0xc5, (byte) 0xf4, (byte) 0x33, (byte) 0x77, (byte) 0x3c,
+ (byte) 0x71, (byte) 0xad, (byte) 0x90, (byte) 0x65, (byte) 0xa9, (byte) 0xc1,
+ (byte) 0x0b, (byte) 0x5c, (byte) 0x62, (byte) 0x55, (byte) 0x50, (byte) 0x6f,
+ (byte) 0x9b, (byte) 0xc9, (byte) 0x0d, (byte) 0xee
+ };
+
+ /**
+ * Generated from above and converted with:
+ *
+ * openssl pkcs8 -topk8 -outform d -in userkey.pem -nocrypt | xxd -i | sed 's/0x/(byte) 0x/g'
+ */
+ private static final byte[] FAKE_EC_KEY_1 = new byte[] {
+ (byte) 0x30, (byte) 0x81, (byte) 0x87, (byte) 0x02, (byte) 0x01, (byte) 0x00,
+ (byte) 0x30, (byte) 0x13, (byte) 0x06, (byte) 0x07, (byte) 0x2a, (byte) 0x86,
+ (byte) 0x48, (byte) 0xce, (byte) 0x3d, (byte) 0x02, (byte) 0x01, (byte) 0x06,
+ (byte) 0x08, (byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0xce, (byte) 0x3d,
+ (byte) 0x03, (byte) 0x01, (byte) 0x07, (byte) 0x04, (byte) 0x6d, (byte) 0x30,
+ (byte) 0x6b, (byte) 0x02, (byte) 0x01, (byte) 0x01, (byte) 0x04, (byte) 0x20,
+ (byte) 0x3a, (byte) 0x8a, (byte) 0x02, (byte) 0xdc, (byte) 0xde, (byte) 0x70,
+ (byte) 0x84, (byte) 0x45, (byte) 0x34, (byte) 0xaf, (byte) 0xbd, (byte) 0xd5,
+ (byte) 0x02, (byte) 0x17, (byte) 0x69, (byte) 0x90, (byte) 0x65, (byte) 0x1e,
+ (byte) 0x87, (byte) 0xf1, (byte) 0x3d, (byte) 0x17, (byte) 0xb6, (byte) 0xf4,
+ (byte) 0x31, (byte) 0x94, (byte) 0x86, (byte) 0x76, (byte) 0x55, (byte) 0xf7,
+ (byte) 0xcc, (byte) 0xba, (byte) 0xa1, (byte) 0x44, (byte) 0x03, (byte) 0x42,
+ (byte) 0x00, (byte) 0x04, (byte) 0xd9, (byte) 0xcf, (byte) 0xe7, (byte) 0x9b,
+ (byte) 0x23, (byte) 0xc8, (byte) 0xa3, (byte) 0xb8, (byte) 0x33, (byte) 0x14,
+ (byte) 0xa4, (byte) 0x4d, (byte) 0x75, (byte) 0x90, (byte) 0xf3, (byte) 0xcd,
+ (byte) 0x43, (byte) 0xe5, (byte) 0x1b, (byte) 0x05, (byte) 0x1d, (byte) 0xf3,
+ (byte) 0xd0, (byte) 0xa3, (byte) 0xb7, (byte) 0x32, (byte) 0x5f, (byte) 0x79,
+ (byte) 0xdc, (byte) 0x88, (byte) 0xb8, (byte) 0x4d, (byte) 0xb3, (byte) 0xd1,
+ (byte) 0x6d, (byte) 0xf7, (byte) 0x75, (byte) 0xf3, (byte) 0xbf, (byte) 0x50,
+ (byte) 0xa1, (byte) 0xbc, (byte) 0x03, (byte) 0x64, (byte) 0x22, (byte) 0xe6,
+ (byte) 0x1a, (byte) 0xa1, (byte) 0xe1, (byte) 0x06, (byte) 0x68, (byte) 0x3b,
+ (byte) 0xbc, (byte) 0x9f, (byte) 0xd3, (byte) 0xae, (byte) 0x77, (byte) 0x5e,
+ (byte) 0x88, (byte) 0x0c, (byte) 0x5e, (byte) 0x0c, (byte) 0xb2, (byte) 0x38
+ };
+
+ /**
+ * Generated from above and converted with:
+ *
+ * openssl x509 -outform d -in usercert.pem | xxd -i | sed 's/0x/(byte) 0x/g'
+ */
+ private static final byte[] FAKE_EC_USER_1 = new byte[] {
+ (byte) 0x30, (byte) 0x82, (byte) 0x02, (byte) 0x51, (byte) 0x30, (byte) 0x82,
+ (byte) 0x01, (byte) 0xba, (byte) 0xa0, (byte) 0x03, (byte) 0x02, (byte) 0x01,
+ (byte) 0x02, (byte) 0x02, (byte) 0x01, (byte) 0x01, (byte) 0x30, (byte) 0x0d,
+ (byte) 0x06, (byte) 0x09, (byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0x86,
+ (byte) 0xf7, (byte) 0x0d, (byte) 0x01, (byte) 0x01, (byte) 0x05, (byte) 0x05,
+ (byte) 0x00, (byte) 0x30, (byte) 0x45, (byte) 0x31, (byte) 0x0b, (byte) 0x30,
+ (byte) 0x09, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x06,
+ (byte) 0x13, (byte) 0x02, (byte) 0x41, (byte) 0x55, (byte) 0x31, (byte) 0x13,
+ (byte) 0x30, (byte) 0x11, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04,
+ (byte) 0x08, (byte) 0x0c, (byte) 0x0a, (byte) 0x53, (byte) 0x6f, (byte) 0x6d,
+ (byte) 0x65, (byte) 0x2d, (byte) 0x53, (byte) 0x74, (byte) 0x61, (byte) 0x74,
+ (byte) 0x65, (byte) 0x31, (byte) 0x21, (byte) 0x30, (byte) 0x1f, (byte) 0x06,
+ (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x0a, (byte) 0x0c, (byte) 0x18,
+ (byte) 0x49, (byte) 0x6e, (byte) 0x74, (byte) 0x65, (byte) 0x72, (byte) 0x6e,
+ (byte) 0x65, (byte) 0x74, (byte) 0x20, (byte) 0x57, (byte) 0x69, (byte) 0x64,
+ (byte) 0x67, (byte) 0x69, (byte) 0x74, (byte) 0x73, (byte) 0x20, (byte) 0x50,
+ (byte) 0x74, (byte) 0x79, (byte) 0x20, (byte) 0x4c, (byte) 0x74, (byte) 0x64,
+ (byte) 0x30, (byte) 0x1e, (byte) 0x17, (byte) 0x0d, (byte) 0x31, (byte) 0x33,
+ (byte) 0x30, (byte) 0x38, (byte) 0x32, (byte) 0x37, (byte) 0x31, (byte) 0x36,
+ (byte) 0x33, (byte) 0x30, (byte) 0x30, (byte) 0x38, (byte) 0x5a, (byte) 0x17,
+ (byte) 0x0d, (byte) 0x32, (byte) 0x33, (byte) 0x30, (byte) 0x38, (byte) 0x32,
+ (byte) 0x35, (byte) 0x31, (byte) 0x36, (byte) 0x33, (byte) 0x30, (byte) 0x30,
+ (byte) 0x38, (byte) 0x5a, (byte) 0x30, (byte) 0x62, (byte) 0x31, (byte) 0x0b,
+ (byte) 0x30, (byte) 0x09, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04,
+ (byte) 0x06, (byte) 0x13, (byte) 0x02, (byte) 0x41, (byte) 0x55, (byte) 0x31,
+ (byte) 0x13, (byte) 0x30, (byte) 0x11, (byte) 0x06, (byte) 0x03, (byte) 0x55,
+ (byte) 0x04, (byte) 0x08, (byte) 0x0c, (byte) 0x0a, (byte) 0x53, (byte) 0x6f,
+ (byte) 0x6d, (byte) 0x65, (byte) 0x2d, (byte) 0x53, (byte) 0x74, (byte) 0x61,
+ (byte) 0x74, (byte) 0x65, (byte) 0x31, (byte) 0x21, (byte) 0x30, (byte) 0x1f,
+ (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x0a, (byte) 0x0c,
+ (byte) 0x18, (byte) 0x49, (byte) 0x6e, (byte) 0x74, (byte) 0x65, (byte) 0x72,
+ (byte) 0x6e, (byte) 0x65, (byte) 0x74, (byte) 0x20, (byte) 0x57, (byte) 0x69,
+ (byte) 0x64, (byte) 0x67, (byte) 0x69, (byte) 0x74, (byte) 0x73, (byte) 0x20,
+ (byte) 0x50, (byte) 0x74, (byte) 0x79, (byte) 0x20, (byte) 0x4c, (byte) 0x74,
+ (byte) 0x64, (byte) 0x31, (byte) 0x1b, (byte) 0x30, (byte) 0x19, (byte) 0x06,
+ (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x03, (byte) 0x0c, (byte) 0x12,
+ (byte) 0x73, (byte) 0x65, (byte) 0x72, (byte) 0x76, (byte) 0x65, (byte) 0x72,
+ (byte) 0x2e, (byte) 0x65, (byte) 0x78, (byte) 0x61, (byte) 0x6d, (byte) 0x70,
+ (byte) 0x6c, (byte) 0x65, (byte) 0x2e, (byte) 0x63, (byte) 0x6f, (byte) 0x6d,
+ (byte) 0x30, (byte) 0x59, (byte) 0x30, (byte) 0x13, (byte) 0x06, (byte) 0x07,
+ (byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0xce, (byte) 0x3d, (byte) 0x02,
+ (byte) 0x01, (byte) 0x06, (byte) 0x08, (byte) 0x2a, (byte) 0x86, (byte) 0x48,
+ (byte) 0xce, (byte) 0x3d, (byte) 0x03, (byte) 0x01, (byte) 0x07, (byte) 0x03,
+ (byte) 0x42, (byte) 0x00, (byte) 0x04, (byte) 0xd9, (byte) 0xcf, (byte) 0xe7,
+ (byte) 0x9b, (byte) 0x23, (byte) 0xc8, (byte) 0xa3, (byte) 0xb8, (byte) 0x33,
+ (byte) 0x14, (byte) 0xa4, (byte) 0x4d, (byte) 0x75, (byte) 0x90, (byte) 0xf3,
+ (byte) 0xcd, (byte) 0x43, (byte) 0xe5, (byte) 0x1b, (byte) 0x05, (byte) 0x1d,
+ (byte) 0xf3, (byte) 0xd0, (byte) 0xa3, (byte) 0xb7, (byte) 0x32, (byte) 0x5f,
+ (byte) 0x79, (byte) 0xdc, (byte) 0x88, (byte) 0xb8, (byte) 0x4d, (byte) 0xb3,
+ (byte) 0xd1, (byte) 0x6d, (byte) 0xf7, (byte) 0x75, (byte) 0xf3, (byte) 0xbf,
+ (byte) 0x50, (byte) 0xa1, (byte) 0xbc, (byte) 0x03, (byte) 0x64, (byte) 0x22,
+ (byte) 0xe6, (byte) 0x1a, (byte) 0xa1, (byte) 0xe1, (byte) 0x06, (byte) 0x68,
+ (byte) 0x3b, (byte) 0xbc, (byte) 0x9f, (byte) 0xd3, (byte) 0xae, (byte) 0x77,
+ (byte) 0x5e, (byte) 0x88, (byte) 0x0c, (byte) 0x5e, (byte) 0x0c, (byte) 0xb2,
+ (byte) 0x38, (byte) 0xa3, (byte) 0x7b, (byte) 0x30, (byte) 0x79, (byte) 0x30,
+ (byte) 0x09, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x1d, (byte) 0x13,
+ (byte) 0x04, (byte) 0x02, (byte) 0x30, (byte) 0x00, (byte) 0x30, (byte) 0x2c,
+ (byte) 0x06, (byte) 0x09, (byte) 0x60, (byte) 0x86, (byte) 0x48, (byte) 0x01,
+ (byte) 0x86, (byte) 0xf8, (byte) 0x42, (byte) 0x01, (byte) 0x0d, (byte) 0x04,
+ (byte) 0x1f, (byte) 0x16, (byte) 0x1d, (byte) 0x4f, (byte) 0x70, (byte) 0x65,
+ (byte) 0x6e, (byte) 0x53, (byte) 0x53, (byte) 0x4c, (byte) 0x20, (byte) 0x47,
+ (byte) 0x65, (byte) 0x6e, (byte) 0x65, (byte) 0x72, (byte) 0x61, (byte) 0x74,
+ (byte) 0x65, (byte) 0x64, (byte) 0x20, (byte) 0x43, (byte) 0x65, (byte) 0x72,
+ (byte) 0x74, (byte) 0x69, (byte) 0x66, (byte) 0x69, (byte) 0x63, (byte) 0x61,
+ (byte) 0x74, (byte) 0x65, (byte) 0x30, (byte) 0x1d, (byte) 0x06, (byte) 0x03,
+ (byte) 0x55, (byte) 0x1d, (byte) 0x0e, (byte) 0x04, (byte) 0x16, (byte) 0x04,
+ (byte) 0x14, (byte) 0xd5, (byte) 0xc4, (byte) 0x72, (byte) 0xbd, (byte) 0xd2,
+ (byte) 0x4e, (byte) 0x90, (byte) 0x1b, (byte) 0x14, (byte) 0x32, (byte) 0xdb,
+ (byte) 0x03, (byte) 0xae, (byte) 0xfa, (byte) 0x27, (byte) 0x7d, (byte) 0x8d,
+ (byte) 0xe4, (byte) 0x80, (byte) 0x58, (byte) 0x30, (byte) 0x1f, (byte) 0x06,
+ (byte) 0x03, (byte) 0x55, (byte) 0x1d, (byte) 0x23, (byte) 0x04, (byte) 0x18,
+ (byte) 0x30, (byte) 0x16, (byte) 0x80, (byte) 0x14, (byte) 0x5f, (byte) 0x5b,
+ (byte) 0x5e, (byte) 0xac, (byte) 0x29, (byte) 0xfa, (byte) 0xa1, (byte) 0x9f,
+ (byte) 0x9e, (byte) 0xad, (byte) 0x46, (byte) 0xe1, (byte) 0xbc, (byte) 0x20,
+ (byte) 0x72, (byte) 0xcf, (byte) 0x4a, (byte) 0xd4, (byte) 0xfa, (byte) 0xe3,
+ (byte) 0x30, (byte) 0x0d, (byte) 0x06, (byte) 0x09, (byte) 0x2a, (byte) 0x86,
+ (byte) 0x48, (byte) 0x86, (byte) 0xf7, (byte) 0x0d, (byte) 0x01, (byte) 0x01,
+ (byte) 0x05, (byte) 0x05, (byte) 0x00, (byte) 0x03, (byte) 0x81, (byte) 0x81,
+ (byte) 0x00, (byte) 0x43, (byte) 0x99, (byte) 0x9f, (byte) 0x67, (byte) 0x08,
+ (byte) 0x43, (byte) 0xd5, (byte) 0x6b, (byte) 0x6f, (byte) 0xd7, (byte) 0x05,
+ (byte) 0xd6, (byte) 0x75, (byte) 0x34, (byte) 0x30, (byte) 0xca, (byte) 0x20,
+ (byte) 0x47, (byte) 0x61, (byte) 0xa1, (byte) 0x89, (byte) 0xb6, (byte) 0xf1,
+ (byte) 0x49, (byte) 0x7b, (byte) 0xd9, (byte) 0xb9, (byte) 0xe8, (byte) 0x1e,
+ (byte) 0x29, (byte) 0x74, (byte) 0x0a, (byte) 0x67, (byte) 0xc0, (byte) 0x7d,
+ (byte) 0xb8, (byte) 0xe6, (byte) 0x39, (byte) 0xa8, (byte) 0x5e, (byte) 0xc3,
+ (byte) 0xb0, (byte) 0xa1, (byte) 0x30, (byte) 0x6a, (byte) 0x1f, (byte) 0x1d,
+ (byte) 0xfc, (byte) 0x11, (byte) 0x59, (byte) 0x0b, (byte) 0xb9, (byte) 0xad,
+ (byte) 0x3a, (byte) 0x4e, (byte) 0x50, (byte) 0x0a, (byte) 0x61, (byte) 0xdb,
+ (byte) 0x75, (byte) 0x6b, (byte) 0xe5, (byte) 0x3f, (byte) 0x8d, (byte) 0xde,
+ (byte) 0x28, (byte) 0x68, (byte) 0xb1, (byte) 0x29, (byte) 0x9a, (byte) 0x18,
+ (byte) 0x8a, (byte) 0xfc, (byte) 0x3f, (byte) 0x13, (byte) 0x93, (byte) 0x29,
+ (byte) 0xed, (byte) 0x22, (byte) 0x7c, (byte) 0xb4, (byte) 0x50, (byte) 0xd5,
+ (byte) 0x4d, (byte) 0x32, (byte) 0x4d, (byte) 0x42, (byte) 0x2b, (byte) 0x29,
+ (byte) 0x97, (byte) 0x86, (byte) 0xc0, (byte) 0x01, (byte) 0x00, (byte) 0x25,
+ (byte) 0xf6, (byte) 0xd3, (byte) 0x2a, (byte) 0xd8, (byte) 0xda, (byte) 0x13,
+ (byte) 0x94, (byte) 0x12, (byte) 0x78, (byte) 0x14, (byte) 0x0b, (byte) 0x51,
+ (byte) 0xc0, (byte) 0x45, (byte) 0xb4, (byte) 0x02, (byte) 0x37, (byte) 0x98,
+ (byte) 0x42, (byte) 0x3c, (byte) 0xcb, (byte) 0x2e, (byte) 0xe4, (byte) 0x38,
+ (byte) 0x69, (byte) 0x1b, (byte) 0x72, (byte) 0xf0, (byte) 0xaa, (byte) 0x89,
+ (byte) 0x7e, (byte) 0xde, (byte) 0xb2
+ };
+
+ /*
+ * The keys and certificates below are generated with:
+ *
+ * openssl req -new -x509 -days 3650 -extensions v3_ca -keyout cakey.pem -out cacert.pem
+ * openssl dsaparam -out dsaparam.pem 1024
+ * openssl req -newkey dsa:dsaparam.pem -keyout userkey.pem -nodes -days 3650 -out userkey.req
+ * mkdir -p demoCA/newcerts
+ * touch demoCA/index.txt
+ * echo "01" > demoCA/serial
+ * openssl ca -out usercert.pem -in userkey.req -cert cacert.pem -keyfile cakey.pem -days 3650
+ */
+
+ /**
+ * Generated from above and converted with:
+ *
+ * openssl x509 -outform d -in cacert.pem | xxd -i | sed 's/0x/(byte) 0x/g'
+ */
+ private static final byte[] FAKE_DSA_CA_1 = new byte[] {
+ (byte) 0x30, (byte) 0x82, (byte) 0x02, (byte) 0x8a, (byte) 0x30, (byte) 0x82,
+ (byte) 0x01, (byte) 0xf3, (byte) 0xa0, (byte) 0x03, (byte) 0x02, (byte) 0x01,
+ (byte) 0x02, (byte) 0x02, (byte) 0x09, (byte) 0x00, (byte) 0x87, (byte) 0xc0,
+ (byte) 0x68, (byte) 0x7f, (byte) 0x42, (byte) 0x92, (byte) 0x0b, (byte) 0x7a,
+ (byte) 0x30, (byte) 0x0d, (byte) 0x06, (byte) 0x09, (byte) 0x2a, (byte) 0x86,
+ (byte) 0x48, (byte) 0x86, (byte) 0xf7, (byte) 0x0d, (byte) 0x01, (byte) 0x01,
+ (byte) 0x05, (byte) 0x05, (byte) 0x00, (byte) 0x30, (byte) 0x5e, (byte) 0x31,
+ (byte) 0x0b, (byte) 0x30, (byte) 0x09, (byte) 0x06, (byte) 0x03, (byte) 0x55,
+ (byte) 0x04, (byte) 0x06, (byte) 0x13, (byte) 0x02, (byte) 0x41, (byte) 0x55,
+ (byte) 0x31, (byte) 0x13, (byte) 0x30, (byte) 0x11, (byte) 0x06, (byte) 0x03,
+ (byte) 0x55, (byte) 0x04, (byte) 0x08, (byte) 0x0c, (byte) 0x0a, (byte) 0x53,
+ (byte) 0x6f, (byte) 0x6d, (byte) 0x65, (byte) 0x2d, (byte) 0x53, (byte) 0x74,
+ (byte) 0x61, (byte) 0x74, (byte) 0x65, (byte) 0x31, (byte) 0x21, (byte) 0x30,
+ (byte) 0x1f, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x0a,
+ (byte) 0x0c, (byte) 0x18, (byte) 0x49, (byte) 0x6e, (byte) 0x74, (byte) 0x65,
+ (byte) 0x72, (byte) 0x6e, (byte) 0x65, (byte) 0x74, (byte) 0x20, (byte) 0x57,
+ (byte) 0x69, (byte) 0x64, (byte) 0x67, (byte) 0x69, (byte) 0x74, (byte) 0x73,
+ (byte) 0x20, (byte) 0x50, (byte) 0x74, (byte) 0x79, (byte) 0x20, (byte) 0x4c,
+ (byte) 0x74, (byte) 0x64, (byte) 0x31, (byte) 0x17, (byte) 0x30, (byte) 0x15,
+ (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x03, (byte) 0x0c,
+ (byte) 0x0e, (byte) 0x63, (byte) 0x61, (byte) 0x2e, (byte) 0x65, (byte) 0x78,
+ (byte) 0x61, (byte) 0x6d, (byte) 0x70, (byte) 0x6c, (byte) 0x65, (byte) 0x2e,
+ (byte) 0x63, (byte) 0x6f, (byte) 0x6d, (byte) 0x30, (byte) 0x1e, (byte) 0x17,
+ (byte) 0x0d, (byte) 0x31, (byte) 0x33, (byte) 0x30, (byte) 0x38, (byte) 0x32,
+ (byte) 0x37, (byte) 0x32, (byte) 0x33, (byte) 0x33, (byte) 0x31, (byte) 0x32,
+ (byte) 0x39, (byte) 0x5a, (byte) 0x17, (byte) 0x0d, (byte) 0x32, (byte) 0x33,
+ (byte) 0x30, (byte) 0x38, (byte) 0x32, (byte) 0x35, (byte) 0x32, (byte) 0x33,
+ (byte) 0x33, (byte) 0x31, (byte) 0x32, (byte) 0x39, (byte) 0x5a, (byte) 0x30,
+ (byte) 0x5e, (byte) 0x31, (byte) 0x0b, (byte) 0x30, (byte) 0x09, (byte) 0x06,
+ (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x06, (byte) 0x13, (byte) 0x02,
+ (byte) 0x41, (byte) 0x55, (byte) 0x31, (byte) 0x13, (byte) 0x30, (byte) 0x11,
+ (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x08, (byte) 0x0c,
+ (byte) 0x0a, (byte) 0x53, (byte) 0x6f, (byte) 0x6d, (byte) 0x65, (byte) 0x2d,
+ (byte) 0x53, (byte) 0x74, (byte) 0x61, (byte) 0x74, (byte) 0x65, (byte) 0x31,
+ (byte) 0x21, (byte) 0x30, (byte) 0x1f, (byte) 0x06, (byte) 0x03, (byte) 0x55,
+ (byte) 0x04, (byte) 0x0a, (byte) 0x0c, (byte) 0x18, (byte) 0x49, (byte) 0x6e,
+ (byte) 0x74, (byte) 0x65, (byte) 0x72, (byte) 0x6e, (byte) 0x65, (byte) 0x74,
+ (byte) 0x20, (byte) 0x57, (byte) 0x69, (byte) 0x64, (byte) 0x67, (byte) 0x69,
+ (byte) 0x74, (byte) 0x73, (byte) 0x20, (byte) 0x50, (byte) 0x74, (byte) 0x79,
+ (byte) 0x20, (byte) 0x4c, (byte) 0x74, (byte) 0x64, (byte) 0x31, (byte) 0x17,
+ (byte) 0x30, (byte) 0x15, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04,
+ (byte) 0x03, (byte) 0x0c, (byte) 0x0e, (byte) 0x63, (byte) 0x61, (byte) 0x2e,
+ (byte) 0x65, (byte) 0x78, (byte) 0x61, (byte) 0x6d, (byte) 0x70, (byte) 0x6c,
+ (byte) 0x65, (byte) 0x2e, (byte) 0x63, (byte) 0x6f, (byte) 0x6d, (byte) 0x30,
+ (byte) 0x81, (byte) 0x9f, (byte) 0x30, (byte) 0x0d, (byte) 0x06, (byte) 0x09,
+ (byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0x86, (byte) 0xf7, (byte) 0x0d,
+ (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x05, (byte) 0x00, (byte) 0x03,
+ (byte) 0x81, (byte) 0x8d, (byte) 0x00, (byte) 0x30, (byte) 0x81, (byte) 0x89,
+ (byte) 0x02, (byte) 0x81, (byte) 0x81, (byte) 0x00, (byte) 0xa4, (byte) 0xc7,
+ (byte) 0x06, (byte) 0xba, (byte) 0xdf, (byte) 0x2b, (byte) 0xee, (byte) 0xd2,
+ (byte) 0xb9, (byte) 0xe4, (byte) 0x52, (byte) 0x21, (byte) 0x68, (byte) 0x2b,
+ (byte) 0x83, (byte) 0xdf, (byte) 0xe3, (byte) 0x9c, (byte) 0x08, (byte) 0x73,
+ (byte) 0xdd, (byte) 0x90, (byte) 0xea, (byte) 0x97, (byte) 0x0c, (byte) 0x96,
+ (byte) 0x20, (byte) 0xb1, (byte) 0xee, (byte) 0x11, (byte) 0xd5, (byte) 0xd4,
+ (byte) 0x7c, (byte) 0x44, (byte) 0x96, (byte) 0x2e, (byte) 0x6e, (byte) 0xa2,
+ (byte) 0xb2, (byte) 0xa3, (byte) 0x4b, (byte) 0x0f, (byte) 0x32, (byte) 0x90,
+ (byte) 0xaf, (byte) 0x5c, (byte) 0x6f, (byte) 0x00, (byte) 0x88, (byte) 0x45,
+ (byte) 0x4e, (byte) 0x9b, (byte) 0x26, (byte) 0xc1, (byte) 0x94, (byte) 0x3c,
+ (byte) 0xfe, (byte) 0x10, (byte) 0xbd, (byte) 0xda, (byte) 0xf2, (byte) 0x8d,
+ (byte) 0x03, (byte) 0x52, (byte) 0x32, (byte) 0x11, (byte) 0xff, (byte) 0xf6,
+ (byte) 0xf9, (byte) 0x6e, (byte) 0x8f, (byte) 0x0f, (byte) 0xc8, (byte) 0x0a,
+ (byte) 0x48, (byte) 0x39, (byte) 0x33, (byte) 0xb9, (byte) 0x0c, (byte) 0xb3,
+ (byte) 0x2b, (byte) 0xab, (byte) 0x7d, (byte) 0x79, (byte) 0x6f, (byte) 0x57,
+ (byte) 0x5b, (byte) 0xb8, (byte) 0x84, (byte) 0xb6, (byte) 0xcc, (byte) 0xe8,
+ (byte) 0x30, (byte) 0x78, (byte) 0xff, (byte) 0x92, (byte) 0xe5, (byte) 0x43,
+ (byte) 0x2e, (byte) 0xef, (byte) 0x66, (byte) 0x98, (byte) 0xb4, (byte) 0xfe,
+ (byte) 0xa2, (byte) 0x40, (byte) 0xf2, (byte) 0x1f, (byte) 0xd0, (byte) 0x86,
+ (byte) 0x16, (byte) 0xc8, (byte) 0x45, (byte) 0xc4, (byte) 0x52, (byte) 0xcb,
+ (byte) 0x31, (byte) 0x5c, (byte) 0x9f, (byte) 0x32, (byte) 0x3b, (byte) 0xf7,
+ (byte) 0x19, (byte) 0x08, (byte) 0xc7, (byte) 0x00, (byte) 0x21, (byte) 0x7d,
+ (byte) 0x02, (byte) 0x03, (byte) 0x01, (byte) 0x00, (byte) 0x01, (byte) 0xa3,
+ (byte) 0x50, (byte) 0x30, (byte) 0x4e, (byte) 0x30, (byte) 0x1d, (byte) 0x06,
+ (byte) 0x03, (byte) 0x55, (byte) 0x1d, (byte) 0x0e, (byte) 0x04, (byte) 0x16,
+ (byte) 0x04, (byte) 0x14, (byte) 0x47, (byte) 0x82, (byte) 0xa3, (byte) 0xf1,
+ (byte) 0xc2, (byte) 0x7e, (byte) 0x3a, (byte) 0xde, (byte) 0x4f, (byte) 0x30,
+ (byte) 0x4c, (byte) 0x7f, (byte) 0x72, (byte) 0x81, (byte) 0x15, (byte) 0x32,
+ (byte) 0xda, (byte) 0x7f, (byte) 0x58, (byte) 0x18, (byte) 0x30, (byte) 0x1f,
+ (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x1d, (byte) 0x23, (byte) 0x04,
+ (byte) 0x18, (byte) 0x30, (byte) 0x16, (byte) 0x80, (byte) 0x14, (byte) 0x47,
+ (byte) 0x82, (byte) 0xa3, (byte) 0xf1, (byte) 0xc2, (byte) 0x7e, (byte) 0x3a,
+ (byte) 0xde, (byte) 0x4f, (byte) 0x30, (byte) 0x4c, (byte) 0x7f, (byte) 0x72,
+ (byte) 0x81, (byte) 0x15, (byte) 0x32, (byte) 0xda, (byte) 0x7f, (byte) 0x58,
+ (byte) 0x18, (byte) 0x30, (byte) 0x0c, (byte) 0x06, (byte) 0x03, (byte) 0x55,
+ (byte) 0x1d, (byte) 0x13, (byte) 0x04, (byte) 0x05, (byte) 0x30, (byte) 0x03,
+ (byte) 0x01, (byte) 0x01, (byte) 0xff, (byte) 0x30, (byte) 0x0d, (byte) 0x06,
+ (byte) 0x09, (byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0x86, (byte) 0xf7,
+ (byte) 0x0d, (byte) 0x01, (byte) 0x01, (byte) 0x05, (byte) 0x05, (byte) 0x00,
+ (byte) 0x03, (byte) 0x81, (byte) 0x81, (byte) 0x00, (byte) 0x08, (byte) 0x7f,
+ (byte) 0x6a, (byte) 0x48, (byte) 0x90, (byte) 0x7b, (byte) 0x9b, (byte) 0x72,
+ (byte) 0x13, (byte) 0xa7, (byte) 0xef, (byte) 0x6b, (byte) 0x0b, (byte) 0x59,
+ (byte) 0xe5, (byte) 0x49, (byte) 0x72, (byte) 0x3a, (byte) 0xc8, (byte) 0x84,
+ (byte) 0xcc, (byte) 0x23, (byte) 0x18, (byte) 0x4c, (byte) 0xec, (byte) 0xc7,
+ (byte) 0xef, (byte) 0xcb, (byte) 0xa7, (byte) 0xbe, (byte) 0xe4, (byte) 0xef,
+ (byte) 0x8f, (byte) 0xc6, (byte) 0x06, (byte) 0x8c, (byte) 0xc0, (byte) 0xe4,
+ (byte) 0x2f, (byte) 0x2a, (byte) 0xc0, (byte) 0x35, (byte) 0x7d, (byte) 0x5e,
+ (byte) 0x19, (byte) 0x29, (byte) 0x8c, (byte) 0xb9, (byte) 0xf1, (byte) 0x1e,
+ (byte) 0xaf, (byte) 0x82, (byte) 0xd8, (byte) 0xe3, (byte) 0x88, (byte) 0xe1,
+ (byte) 0x31, (byte) 0xc8, (byte) 0x82, (byte) 0x1f, (byte) 0x83, (byte) 0xa9,
+ (byte) 0xde, (byte) 0xfe, (byte) 0x4b, (byte) 0xe2, (byte) 0x78, (byte) 0x64,
+ (byte) 0xed, (byte) 0xa4, (byte) 0x7b, (byte) 0xee, (byte) 0x8d, (byte) 0x71,
+ (byte) 0x1b, (byte) 0x44, (byte) 0xe6, (byte) 0xb7, (byte) 0xe8, (byte) 0xc5,
+ (byte) 0x9a, (byte) 0x93, (byte) 0x92, (byte) 0x6f, (byte) 0x6f, (byte) 0xdb,
+ (byte) 0xbd, (byte) 0xd7, (byte) 0x03, (byte) 0x85, (byte) 0xa9, (byte) 0x5f,
+ (byte) 0x53, (byte) 0x5f, (byte) 0x5d, (byte) 0x30, (byte) 0xc6, (byte) 0xd9,
+ (byte) 0xce, (byte) 0x34, (byte) 0xa8, (byte) 0xbe, (byte) 0x31, (byte) 0x47,
+ (byte) 0x1c, (byte) 0xa4, (byte) 0x7f, (byte) 0xc0, (byte) 0x2c, (byte) 0xbc,
+ (byte) 0xfe, (byte) 0x1a, (byte) 0x31, (byte) 0xd8, (byte) 0x77, (byte) 0x4d,
+ (byte) 0xfc, (byte) 0x45, (byte) 0x84, (byte) 0xfc, (byte) 0x45, (byte) 0x12,
+ (byte) 0xab, (byte) 0x50, (byte) 0xe4, (byte) 0x45, (byte) 0xe5, (byte) 0x11
+ };
+
+ /**
+ * Generated from above and converted with: openssl pkcs8 -topk8 -outform d
+ * -in userkey.pem -nocrypt | xxd -i | sed 's/0x/(byte) 0x/g'
+ */
+ private static final byte[] FAKE_DSA_KEY_1 = new byte[] {
+ (byte) 0x30, (byte) 0x82, (byte) 0x01, (byte) 0x4c, (byte) 0x02, (byte) 0x01,
+ (byte) 0x00, (byte) 0x30, (byte) 0x82, (byte) 0x01, (byte) 0x2c, (byte) 0x06,
+ (byte) 0x07, (byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0xce, (byte) 0x38,
+ (byte) 0x04, (byte) 0x01, (byte) 0x30, (byte) 0x82, (byte) 0x01, (byte) 0x1f,
+ (byte) 0x02, (byte) 0x81, (byte) 0x81, (byte) 0x00, (byte) 0xb3, (byte) 0x23,
+ (byte) 0xf7, (byte) 0x86, (byte) 0xbd, (byte) 0x3b, (byte) 0x86, (byte) 0xcc,
+ (byte) 0xc3, (byte) 0x91, (byte) 0xc0, (byte) 0x30, (byte) 0x32, (byte) 0x02,
+ (byte) 0x47, (byte) 0x35, (byte) 0x01, (byte) 0xef, (byte) 0xee, (byte) 0x98,
+ (byte) 0x13, (byte) 0x56, (byte) 0x49, (byte) 0x47, (byte) 0xb5, (byte) 0x20,
+ (byte) 0xa8, (byte) 0x60, (byte) 0xcb, (byte) 0xc0, (byte) 0xd5, (byte) 0x77,
+ (byte) 0xc1, (byte) 0x69, (byte) 0xcd, (byte) 0x18, (byte) 0x34, (byte) 0x92,
+ (byte) 0xf2, (byte) 0x6a, (byte) 0x2a, (byte) 0x10, (byte) 0x59, (byte) 0x1c,
+ (byte) 0x91, (byte) 0x20, (byte) 0x51, (byte) 0xca, (byte) 0x37, (byte) 0xb2,
+ (byte) 0x87, (byte) 0xa6, (byte) 0x8a, (byte) 0x02, (byte) 0xfd, (byte) 0x45,
+ (byte) 0x46, (byte) 0xf9, (byte) 0x76, (byte) 0xb1, (byte) 0x35, (byte) 0x38,
+ (byte) 0x8d, (byte) 0xff, (byte) 0x4c, (byte) 0x5d, (byte) 0x75, (byte) 0x8f,
+ (byte) 0x66, (byte) 0x15, (byte) 0x7d, (byte) 0x7b, (byte) 0xda, (byte) 0xdb,
+ (byte) 0x57, (byte) 0x39, (byte) 0xff, (byte) 0x91, (byte) 0x3f, (byte) 0xdd,
+ (byte) 0xe2, (byte) 0xb4, (byte) 0x22, (byte) 0x60, (byte) 0x4c, (byte) 0x32,
+ (byte) 0x3b, (byte) 0x9d, (byte) 0x34, (byte) 0x9f, (byte) 0xb9, (byte) 0x5d,
+ (byte) 0x75, (byte) 0xb9, (byte) 0xd3, (byte) 0x7f, (byte) 0x11, (byte) 0xba,
+ (byte) 0xb7, (byte) 0xc8, (byte) 0x32, (byte) 0xc6, (byte) 0xce, (byte) 0x71,
+ (byte) 0x91, (byte) 0xd3, (byte) 0x32, (byte) 0xaf, (byte) 0x4d, (byte) 0x7e,
+ (byte) 0x7c, (byte) 0x15, (byte) 0xf7, (byte) 0x71, (byte) 0x2c, (byte) 0x52,
+ (byte) 0x65, (byte) 0x4d, (byte) 0xa9, (byte) 0x81, (byte) 0x25, (byte) 0x35,
+ (byte) 0xce, (byte) 0x0b, (byte) 0x5b, (byte) 0x56, (byte) 0xfe, (byte) 0xf1,
+ (byte) 0x02, (byte) 0x15, (byte) 0x00, (byte) 0xeb, (byte) 0x4e, (byte) 0x7f,
+ (byte) 0x7a, (byte) 0x31, (byte) 0xb3, (byte) 0x7d, (byte) 0x8d, (byte) 0xb2,
+ (byte) 0xf7, (byte) 0xaf, (byte) 0xad, (byte) 0xb1, (byte) 0x42, (byte) 0x92,
+ (byte) 0xf3, (byte) 0x6c, (byte) 0xe4, (byte) 0xed, (byte) 0x8b, (byte) 0x02,
+ (byte) 0x81, (byte) 0x81, (byte) 0x00, (byte) 0x81, (byte) 0xc8, (byte) 0x36,
+ (byte) 0x48, (byte) 0xdb, (byte) 0x71, (byte) 0x2b, (byte) 0x91, (byte) 0xce,
+ (byte) 0x6d, (byte) 0xbc, (byte) 0xb8, (byte) 0xf9, (byte) 0xcb, (byte) 0x50,
+ (byte) 0x91, (byte) 0x10, (byte) 0x8a, (byte) 0xf8, (byte) 0x37, (byte) 0x50,
+ (byte) 0xda, (byte) 0x4f, (byte) 0xc8, (byte) 0x4d, (byte) 0x73, (byte) 0xcb,
+ (byte) 0x4d, (byte) 0xb0, (byte) 0x19, (byte) 0x54, (byte) 0x5a, (byte) 0xf3,
+ (byte) 0x6c, (byte) 0xc9, (byte) 0xd8, (byte) 0x96, (byte) 0xd9, (byte) 0xb0,
+ (byte) 0x54, (byte) 0x7e, (byte) 0x7d, (byte) 0xe2, (byte) 0x58, (byte) 0x0e,
+ (byte) 0x5f, (byte) 0xc0, (byte) 0xce, (byte) 0xb9, (byte) 0x5c, (byte) 0xe3,
+ (byte) 0xd3, (byte) 0xdf, (byte) 0xcf, (byte) 0x45, (byte) 0x74, (byte) 0xfb,
+ (byte) 0xe6, (byte) 0x20, (byte) 0xe7, (byte) 0xfc, (byte) 0x0f, (byte) 0xca,
+ (byte) 0xdb, (byte) 0xc0, (byte) 0x0b, (byte) 0xe1, (byte) 0x5a, (byte) 0x16,
+ (byte) 0x1d, (byte) 0xb3, (byte) 0x2e, (byte) 0xe5, (byte) 0x5f, (byte) 0x89,
+ (byte) 0x17, (byte) 0x73, (byte) 0x50, (byte) 0xd1, (byte) 0x4a, (byte) 0x60,
+ (byte) 0xb7, (byte) 0xaa, (byte) 0xf0, (byte) 0xc7, (byte) 0xc5, (byte) 0x03,
+ (byte) 0x4e, (byte) 0x36, (byte) 0x51, (byte) 0x9e, (byte) 0x2f, (byte) 0xfa,
+ (byte) 0xf3, (byte) 0xd6, (byte) 0x58, (byte) 0x14, (byte) 0x02, (byte) 0xb4,
+ (byte) 0x41, (byte) 0xd6, (byte) 0x72, (byte) 0x6f, (byte) 0x58, (byte) 0x5b,
+ (byte) 0x2d, (byte) 0x23, (byte) 0xc0, (byte) 0x75, (byte) 0x4f, (byte) 0x39,
+ (byte) 0xa8, (byte) 0x6a, (byte) 0xdf, (byte) 0x79, (byte) 0x21, (byte) 0xf2,
+ (byte) 0x77, (byte) 0x91, (byte) 0x3f, (byte) 0x1c, (byte) 0x4d, (byte) 0x48,
+ (byte) 0x78, (byte) 0xcd, (byte) 0xed, (byte) 0x79, (byte) 0x23, (byte) 0x04,
+ (byte) 0x17, (byte) 0x02, (byte) 0x15, (byte) 0x00, (byte) 0xc7, (byte) 0xe7,
+ (byte) 0xe2, (byte) 0x6b, (byte) 0x14, (byte) 0xe6, (byte) 0x31, (byte) 0x12,
+ (byte) 0xb2, (byte) 0x1e, (byte) 0xd4, (byte) 0xf2, (byte) 0x9b, (byte) 0x2c,
+ (byte) 0xf6, (byte) 0x54, (byte) 0x4c, (byte) 0x12, (byte) 0xe8, (byte) 0x22
+ };
+
+ /**
+ * Generated from above and converted with: openssl x509 -outform d -in
+ * usercert.pem | xxd -i | sed 's/0x/(byte) 0x/g'
+ */
+ private static final byte[] FAKE_DSA_USER_1 = new byte[] {
+ (byte) 0x30, (byte) 0x82, (byte) 0x03, (byte) 0xca, (byte) 0x30, (byte) 0x82,
+ (byte) 0x03, (byte) 0x33, (byte) 0xa0, (byte) 0x03, (byte) 0x02, (byte) 0x01,
+ (byte) 0x02, (byte) 0x02, (byte) 0x01, (byte) 0x01, (byte) 0x30, (byte) 0x0d,
+ (byte) 0x06, (byte) 0x09, (byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0x86,
+ (byte) 0xf7, (byte) 0x0d, (byte) 0x01, (byte) 0x01, (byte) 0x05, (byte) 0x05,
+ (byte) 0x00, (byte) 0x30, (byte) 0x5e, (byte) 0x31, (byte) 0x0b, (byte) 0x30,
+ (byte) 0x09, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x06,
+ (byte) 0x13, (byte) 0x02, (byte) 0x41, (byte) 0x55, (byte) 0x31, (byte) 0x13,
+ (byte) 0x30, (byte) 0x11, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04,
+ (byte) 0x08, (byte) 0x0c, (byte) 0x0a, (byte) 0x53, (byte) 0x6f, (byte) 0x6d,
+ (byte) 0x65, (byte) 0x2d, (byte) 0x53, (byte) 0x74, (byte) 0x61, (byte) 0x74,
+ (byte) 0x65, (byte) 0x31, (byte) 0x21, (byte) 0x30, (byte) 0x1f, (byte) 0x06,
+ (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x0a, (byte) 0x0c, (byte) 0x18,
+ (byte) 0x49, (byte) 0x6e, (byte) 0x74, (byte) 0x65, (byte) 0x72, (byte) 0x6e,
+ (byte) 0x65, (byte) 0x74, (byte) 0x20, (byte) 0x57, (byte) 0x69, (byte) 0x64,
+ (byte) 0x67, (byte) 0x69, (byte) 0x74, (byte) 0x73, (byte) 0x20, (byte) 0x50,
+ (byte) 0x74, (byte) 0x79, (byte) 0x20, (byte) 0x4c, (byte) 0x74, (byte) 0x64,
+ (byte) 0x31, (byte) 0x17, (byte) 0x30, (byte) 0x15, (byte) 0x06, (byte) 0x03,
+ (byte) 0x55, (byte) 0x04, (byte) 0x03, (byte) 0x0c, (byte) 0x0e, (byte) 0x63,
+ (byte) 0x61, (byte) 0x2e, (byte) 0x65, (byte) 0x78, (byte) 0x61, (byte) 0x6d,
+ (byte) 0x70, (byte) 0x6c, (byte) 0x65, (byte) 0x2e, (byte) 0x63, (byte) 0x6f,
+ (byte) 0x6d, (byte) 0x30, (byte) 0x1e, (byte) 0x17, (byte) 0x0d, (byte) 0x31,
+ (byte) 0x33, (byte) 0x30, (byte) 0x38, (byte) 0x32, (byte) 0x37, (byte) 0x32,
+ (byte) 0x33, (byte) 0x33, (byte) 0x34, (byte) 0x32, (byte) 0x32, (byte) 0x5a,
+ (byte) 0x17, (byte) 0x0d, (byte) 0x32, (byte) 0x33, (byte) 0x30, (byte) 0x38,
+ (byte) 0x32, (byte) 0x35, (byte) 0x32, (byte) 0x33, (byte) 0x33, (byte) 0x34,
+ (byte) 0x32, (byte) 0x32, (byte) 0x5a, (byte) 0x30, (byte) 0x62, (byte) 0x31,
+ (byte) 0x0b, (byte) 0x30, (byte) 0x09, (byte) 0x06, (byte) 0x03, (byte) 0x55,
+ (byte) 0x04, (byte) 0x06, (byte) 0x13, (byte) 0x02, (byte) 0x41, (byte) 0x55,
+ (byte) 0x31, (byte) 0x13, (byte) 0x30, (byte) 0x11, (byte) 0x06, (byte) 0x03,
+ (byte) 0x55, (byte) 0x04, (byte) 0x08, (byte) 0x0c, (byte) 0x0a, (byte) 0x53,
+ (byte) 0x6f, (byte) 0x6d, (byte) 0x65, (byte) 0x2d, (byte) 0x53, (byte) 0x74,
+ (byte) 0x61, (byte) 0x74, (byte) 0x65, (byte) 0x31, (byte) 0x21, (byte) 0x30,
+ (byte) 0x1f, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x0a,
+ (byte) 0x0c, (byte) 0x18, (byte) 0x49, (byte) 0x6e, (byte) 0x74, (byte) 0x65,
+ (byte) 0x72, (byte) 0x6e, (byte) 0x65, (byte) 0x74, (byte) 0x20, (byte) 0x57,
+ (byte) 0x69, (byte) 0x64, (byte) 0x67, (byte) 0x69, (byte) 0x74, (byte) 0x73,
+ (byte) 0x20, (byte) 0x50, (byte) 0x74, (byte) 0x79, (byte) 0x20, (byte) 0x4c,
+ (byte) 0x74, (byte) 0x64, (byte) 0x31, (byte) 0x1b, (byte) 0x30, (byte) 0x19,
+ (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x03, (byte) 0x0c,
+ (byte) 0x12, (byte) 0x73, (byte) 0x65, (byte) 0x72, (byte) 0x76, (byte) 0x65,
+ (byte) 0x72, (byte) 0x2e, (byte) 0x65, (byte) 0x78, (byte) 0x61, (byte) 0x6d,
+ (byte) 0x70, (byte) 0x6c, (byte) 0x65, (byte) 0x2e, (byte) 0x63, (byte) 0x6f,
+ (byte) 0x6d, (byte) 0x30, (byte) 0x82, (byte) 0x01, (byte) 0xb7, (byte) 0x30,
+ (byte) 0x82, (byte) 0x01, (byte) 0x2c, (byte) 0x06, (byte) 0x07, (byte) 0x2a,
+ (byte) 0x86, (byte) 0x48, (byte) 0xce, (byte) 0x38, (byte) 0x04, (byte) 0x01,
+ (byte) 0x30, (byte) 0x82, (byte) 0x01, (byte) 0x1f, (byte) 0x02, (byte) 0x81,
+ (byte) 0x81, (byte) 0x00, (byte) 0xb3, (byte) 0x23, (byte) 0xf7, (byte) 0x86,
+ (byte) 0xbd, (byte) 0x3b, (byte) 0x86, (byte) 0xcc, (byte) 0xc3, (byte) 0x91,
+ (byte) 0xc0, (byte) 0x30, (byte) 0x32, (byte) 0x02, (byte) 0x47, (byte) 0x35,
+ (byte) 0x01, (byte) 0xef, (byte) 0xee, (byte) 0x98, (byte) 0x13, (byte) 0x56,
+ (byte) 0x49, (byte) 0x47, (byte) 0xb5, (byte) 0x20, (byte) 0xa8, (byte) 0x60,
+ (byte) 0xcb, (byte) 0xc0, (byte) 0xd5, (byte) 0x77, (byte) 0xc1, (byte) 0x69,
+ (byte) 0xcd, (byte) 0x18, (byte) 0x34, (byte) 0x92, (byte) 0xf2, (byte) 0x6a,
+ (byte) 0x2a, (byte) 0x10, (byte) 0x59, (byte) 0x1c, (byte) 0x91, (byte) 0x20,
+ (byte) 0x51, (byte) 0xca, (byte) 0x37, (byte) 0xb2, (byte) 0x87, (byte) 0xa6,
+ (byte) 0x8a, (byte) 0x02, (byte) 0xfd, (byte) 0x45, (byte) 0x46, (byte) 0xf9,
+ (byte) 0x76, (byte) 0xb1, (byte) 0x35, (byte) 0x38, (byte) 0x8d, (byte) 0xff,
+ (byte) 0x4c, (byte) 0x5d, (byte) 0x75, (byte) 0x8f, (byte) 0x66, (byte) 0x15,
+ (byte) 0x7d, (byte) 0x7b, (byte) 0xda, (byte) 0xdb, (byte) 0x57, (byte) 0x39,
+ (byte) 0xff, (byte) 0x91, (byte) 0x3f, (byte) 0xdd, (byte) 0xe2, (byte) 0xb4,
+ (byte) 0x22, (byte) 0x60, (byte) 0x4c, (byte) 0x32, (byte) 0x3b, (byte) 0x9d,
+ (byte) 0x34, (byte) 0x9f, (byte) 0xb9, (byte) 0x5d, (byte) 0x75, (byte) 0xb9,
+ (byte) 0xd3, (byte) 0x7f, (byte) 0x11, (byte) 0xba, (byte) 0xb7, (byte) 0xc8,
+ (byte) 0x32, (byte) 0xc6, (byte) 0xce, (byte) 0x71, (byte) 0x91, (byte) 0xd3,
+ (byte) 0x32, (byte) 0xaf, (byte) 0x4d, (byte) 0x7e, (byte) 0x7c, (byte) 0x15,
+ (byte) 0xf7, (byte) 0x71, (byte) 0x2c, (byte) 0x52, (byte) 0x65, (byte) 0x4d,
+ (byte) 0xa9, (byte) 0x81, (byte) 0x25, (byte) 0x35, (byte) 0xce, (byte) 0x0b,
+ (byte) 0x5b, (byte) 0x56, (byte) 0xfe, (byte) 0xf1, (byte) 0x02, (byte) 0x15,
+ (byte) 0x00, (byte) 0xeb, (byte) 0x4e, (byte) 0x7f, (byte) 0x7a, (byte) 0x31,
+ (byte) 0xb3, (byte) 0x7d, (byte) 0x8d, (byte) 0xb2, (byte) 0xf7, (byte) 0xaf,
+ (byte) 0xad, (byte) 0xb1, (byte) 0x42, (byte) 0x92, (byte) 0xf3, (byte) 0x6c,
+ (byte) 0xe4, (byte) 0xed, (byte) 0x8b, (byte) 0x02, (byte) 0x81, (byte) 0x81,
+ (byte) 0x00, (byte) 0x81, (byte) 0xc8, (byte) 0x36, (byte) 0x48, (byte) 0xdb,
+ (byte) 0x71, (byte) 0x2b, (byte) 0x91, (byte) 0xce, (byte) 0x6d, (byte) 0xbc,
+ (byte) 0xb8, (byte) 0xf9, (byte) 0xcb, (byte) 0x50, (byte) 0x91, (byte) 0x10,
+ (byte) 0x8a, (byte) 0xf8, (byte) 0x37, (byte) 0x50, (byte) 0xda, (byte) 0x4f,
+ (byte) 0xc8, (byte) 0x4d, (byte) 0x73, (byte) 0xcb, (byte) 0x4d, (byte) 0xb0,
+ (byte) 0x19, (byte) 0x54, (byte) 0x5a, (byte) 0xf3, (byte) 0x6c, (byte) 0xc9,
+ (byte) 0xd8, (byte) 0x96, (byte) 0xd9, (byte) 0xb0, (byte) 0x54, (byte) 0x7e,
+ (byte) 0x7d, (byte) 0xe2, (byte) 0x58, (byte) 0x0e, (byte) 0x5f, (byte) 0xc0,
+ (byte) 0xce, (byte) 0xb9, (byte) 0x5c, (byte) 0xe3, (byte) 0xd3, (byte) 0xdf,
+ (byte) 0xcf, (byte) 0x45, (byte) 0x74, (byte) 0xfb, (byte) 0xe6, (byte) 0x20,
+ (byte) 0xe7, (byte) 0xfc, (byte) 0x0f, (byte) 0xca, (byte) 0xdb, (byte) 0xc0,
+ (byte) 0x0b, (byte) 0xe1, (byte) 0x5a, (byte) 0x16, (byte) 0x1d, (byte) 0xb3,
+ (byte) 0x2e, (byte) 0xe5, (byte) 0x5f, (byte) 0x89, (byte) 0x17, (byte) 0x73,
+ (byte) 0x50, (byte) 0xd1, (byte) 0x4a, (byte) 0x60, (byte) 0xb7, (byte) 0xaa,
+ (byte) 0xf0, (byte) 0xc7, (byte) 0xc5, (byte) 0x03, (byte) 0x4e, (byte) 0x36,
+ (byte) 0x51, (byte) 0x9e, (byte) 0x2f, (byte) 0xfa, (byte) 0xf3, (byte) 0xd6,
+ (byte) 0x58, (byte) 0x14, (byte) 0x02, (byte) 0xb4, (byte) 0x41, (byte) 0xd6,
+ (byte) 0x72, (byte) 0x6f, (byte) 0x58, (byte) 0x5b, (byte) 0x2d, (byte) 0x23,
+ (byte) 0xc0, (byte) 0x75, (byte) 0x4f, (byte) 0x39, (byte) 0xa8, (byte) 0x6a,
+ (byte) 0xdf, (byte) 0x79, (byte) 0x21, (byte) 0xf2, (byte) 0x77, (byte) 0x91,
+ (byte) 0x3f, (byte) 0x1c, (byte) 0x4d, (byte) 0x48, (byte) 0x78, (byte) 0xcd,
+ (byte) 0xed, (byte) 0x79, (byte) 0x23, (byte) 0x03, (byte) 0x81, (byte) 0x84,
+ (byte) 0x00, (byte) 0x02, (byte) 0x81, (byte) 0x80, (byte) 0x1a, (byte) 0x50,
+ (byte) 0x9d, (byte) 0x3e, (byte) 0xa1, (byte) 0x6c, (byte) 0x99, (byte) 0x35,
+ (byte) 0x36, (byte) 0x26, (byte) 0x22, (byte) 0x6b, (byte) 0x47, (byte) 0x45,
+ (byte) 0x80, (byte) 0x5b, (byte) 0xd5, (byte) 0xc1, (byte) 0xc5, (byte) 0x70,
+ (byte) 0x75, (byte) 0x55, (byte) 0x66, (byte) 0x33, (byte) 0x1d, (byte) 0xae,
+ (byte) 0xd0, (byte) 0x01, (byte) 0x64, (byte) 0x8b, (byte) 0xae, (byte) 0x9d,
+ (byte) 0x66, (byte) 0x58, (byte) 0xf9, (byte) 0x42, (byte) 0x74, (byte) 0x3a,
+ (byte) 0x32, (byte) 0xc7, (byte) 0x7f, (byte) 0x25, (byte) 0x64, (byte) 0x7d,
+ (byte) 0x08, (byte) 0x26, (byte) 0xbf, (byte) 0x21, (byte) 0x3a, (byte) 0x84,
+ (byte) 0xcc, (byte) 0x2c, (byte) 0x66, (byte) 0x7d, (byte) 0xc7, (byte) 0xd6,
+ (byte) 0xb1, (byte) 0x69, (byte) 0x57, (byte) 0x67, (byte) 0x52, (byte) 0x73,
+ (byte) 0x3f, (byte) 0x79, (byte) 0x60, (byte) 0xaa, (byte) 0xf4, (byte) 0x8a,
+ (byte) 0x48, (byte) 0x42, (byte) 0x46, (byte) 0x41, (byte) 0xd0, (byte) 0x50,
+ (byte) 0x9b, (byte) 0xa2, (byte) 0x4e, (byte) 0xa5, (byte) 0x88, (byte) 0x10,
+ (byte) 0xf7, (byte) 0x61, (byte) 0xa2, (byte) 0xfa, (byte) 0x8d, (byte) 0xa6,
+ (byte) 0x13, (byte) 0x9e, (byte) 0x36, (byte) 0x86, (byte) 0x62, (byte) 0xf0,
+ (byte) 0x97, (byte) 0xef, (byte) 0x11, (byte) 0xc6, (byte) 0x35, (byte) 0xd3,
+ (byte) 0x79, (byte) 0x30, (byte) 0xde, (byte) 0xf2, (byte) 0x7f, (byte) 0x7a,
+ (byte) 0x3c, (byte) 0x03, (byte) 0xa3, (byte) 0xc5, (byte) 0xbc, (byte) 0xb1,
+ (byte) 0xbc, (byte) 0x2f, (byte) 0x10, (byte) 0xf4, (byte) 0x51, (byte) 0x89,
+ (byte) 0xe2, (byte) 0xaf, (byte) 0xf7, (byte) 0x61, (byte) 0x1a, (byte) 0xf0,
+ (byte) 0x87, (byte) 0x5e, (byte) 0xa5, (byte) 0x02, (byte) 0xd2, (byte) 0xe4,
+ (byte) 0xa3, (byte) 0x7b, (byte) 0x30, (byte) 0x79, (byte) 0x30, (byte) 0x09,
+ (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x1d, (byte) 0x13, (byte) 0x04,
+ (byte) 0x02, (byte) 0x30, (byte) 0x00, (byte) 0x30, (byte) 0x2c, (byte) 0x06,
+ (byte) 0x09, (byte) 0x60, (byte) 0x86, (byte) 0x48, (byte) 0x01, (byte) 0x86,
+ (byte) 0xf8, (byte) 0x42, (byte) 0x01, (byte) 0x0d, (byte) 0x04, (byte) 0x1f,
+ (byte) 0x16, (byte) 0x1d, (byte) 0x4f, (byte) 0x70, (byte) 0x65, (byte) 0x6e,
+ (byte) 0x53, (byte) 0x53, (byte) 0x4c, (byte) 0x20, (byte) 0x47, (byte) 0x65,
+ (byte) 0x6e, (byte) 0x65, (byte) 0x72, (byte) 0x61, (byte) 0x74, (byte) 0x65,
+ (byte) 0x64, (byte) 0x20, (byte) 0x43, (byte) 0x65, (byte) 0x72, (byte) 0x74,
+ (byte) 0x69, (byte) 0x66, (byte) 0x69, (byte) 0x63, (byte) 0x61, (byte) 0x74,
+ (byte) 0x65, (byte) 0x30, (byte) 0x1d, (byte) 0x06, (byte) 0x03, (byte) 0x55,
+ (byte) 0x1d, (byte) 0x0e, (byte) 0x04, (byte) 0x16, (byte) 0x04, (byte) 0x14,
+ (byte) 0xd1, (byte) 0x6c, (byte) 0x36, (byte) 0x36, (byte) 0x61, (byte) 0x6c,
+ (byte) 0xf6, (byte) 0x90, (byte) 0x82, (byte) 0x82, (byte) 0x87, (byte) 0x93,
+ (byte) 0xbe, (byte) 0x99, (byte) 0x60, (byte) 0x1b, (byte) 0x03, (byte) 0x58,
+ (byte) 0x36, (byte) 0x63, (byte) 0x30, (byte) 0x1f, (byte) 0x06, (byte) 0x03,
+ (byte) 0x55, (byte) 0x1d, (byte) 0x23, (byte) 0x04, (byte) 0x18, (byte) 0x30,
+ (byte) 0x16, (byte) 0x80, (byte) 0x14, (byte) 0x47, (byte) 0x82, (byte) 0xa3,
+ (byte) 0xf1, (byte) 0xc2, (byte) 0x7e, (byte) 0x3a, (byte) 0xde, (byte) 0x4f,
+ (byte) 0x30, (byte) 0x4c, (byte) 0x7f, (byte) 0x72, (byte) 0x81, (byte) 0x15,
+ (byte) 0x32, (byte) 0xda, (byte) 0x7f, (byte) 0x58, (byte) 0x18, (byte) 0x30,
+ (byte) 0x0d, (byte) 0x06, (byte) 0x09, (byte) 0x2a, (byte) 0x86, (byte) 0x48,
+ (byte) 0x86, (byte) 0xf7, (byte) 0x0d, (byte) 0x01, (byte) 0x01, (byte) 0x05,
+ (byte) 0x05, (byte) 0x00, (byte) 0x03, (byte) 0x81, (byte) 0x81, (byte) 0x00,
+ (byte) 0x81, (byte) 0xde, (byte) 0x20, (byte) 0xa1, (byte) 0xb2, (byte) 0x50,
+ (byte) 0x03, (byte) 0xcd, (byte) 0x90, (byte) 0x4f, (byte) 0x2b, (byte) 0x47,
+ (byte) 0x1d, (byte) 0xac, (byte) 0x6e, (byte) 0xb4, (byte) 0xc7, (byte) 0x14,
+ (byte) 0xc6, (byte) 0x4f, (byte) 0x45, (byte) 0xaf, (byte) 0x81, (byte) 0x5d,
+ (byte) 0x5a, (byte) 0x31, (byte) 0xff, (byte) 0x9c, (byte) 0x4d, (byte) 0xdc,
+ (byte) 0x9e, (byte) 0x36, (byte) 0x9f, (byte) 0x9b, (byte) 0xb1, (byte) 0xc9,
+ (byte) 0x50, (byte) 0xa3, (byte) 0xf6, (byte) 0x9c, (byte) 0x68, (byte) 0x6f,
+ (byte) 0x68, (byte) 0xd9, (byte) 0x56, (byte) 0x1b, (byte) 0xe5, (byte) 0x1b,
+ (byte) 0x41, (byte) 0xd4, (byte) 0xcc, (byte) 0xb6, (byte) 0x37, (byte) 0xd5,
+ (byte) 0x69, (byte) 0x6b, (byte) 0x39, (byte) 0xaf, (byte) 0xc6, (byte) 0xb8,
+ (byte) 0x39, (byte) 0x76, (byte) 0xe3, (byte) 0xf7, (byte) 0x97, (byte) 0x74,
+ (byte) 0x31, (byte) 0xc4, (byte) 0x2d, (byte) 0xb7, (byte) 0x9a, (byte) 0xa4,
+ (byte) 0xfa, (byte) 0x9f, (byte) 0xa8, (byte) 0xe3, (byte) 0x41, (byte) 0xda,
+ (byte) 0x2f, (byte) 0x0c, (byte) 0x9d, (byte) 0x83, (byte) 0xdc, (byte) 0x86,
+ (byte) 0x1f, (byte) 0x5c, (byte) 0x0f, (byte) 0x87, (byte) 0x05, (byte) 0xc9,
+ (byte) 0xb0, (byte) 0x63, (byte) 0xca, (byte) 0x9b, (byte) 0xdb, (byte) 0xe6,
+ (byte) 0x3c, (byte) 0xe9, (byte) 0x23, (byte) 0x9e, (byte) 0x23, (byte) 0x44,
+ (byte) 0x1d, (byte) 0x5b, (byte) 0x60, (byte) 0x66, (byte) 0xb6, (byte) 0x72,
+ (byte) 0x8c, (byte) 0x87, (byte) 0x86, (byte) 0xe8, (byte) 0xdb, (byte) 0x29,
+ (byte) 0x67, (byte) 0x9c, (byte) 0x33, (byte) 0x5c, (byte) 0x39, (byte) 0xf1,
+ (byte) 0xb5, (byte) 0x9b, (byte) 0xb8, (byte) 0xe1, (byte) 0x42, (byte) 0x51,
+ (byte) 0xed, (byte) 0x2c
+ };
+
/**
* The amount of time to allow before and after expected time for variance
* in timing tests.
@@ -505,11 +1133,12 @@
assertAliases(new String[] {});
assertTrue(mAndroidKeyStore.generate(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1,
- KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+ KeyStore.UID_SELF, NativeCrypto.EVP_PKEY_RSA, 1024, KeyStore.FLAG_ENCRYPTED,
+ null));
assertAliases(new String[] { TEST_ALIAS_1 });
- assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_2, FAKE_CA_1,
+ assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_2, FAKE_RSA_CA_1,
KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
assertAliases(new String[] { TEST_ALIAS_1, TEST_ALIAS_2 });
@@ -533,11 +1162,12 @@
assertAliases(new String[] {});
assertTrue(mAndroidKeyStore.generate(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1,
- KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+ KeyStore.UID_SELF, NativeCrypto.EVP_PKEY_RSA, 1024, KeyStore.FLAG_ENCRYPTED,
+ null));
assertTrue("Should contain generated private key", mKeyStore.containsAlias(TEST_ALIAS_1));
- assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_2, FAKE_CA_1,
+ assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_2, FAKE_RSA_CA_1,
KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
assertTrue("Should contain added CA certificate", mKeyStore.containsAlias(TEST_ALIAS_2));
@@ -551,7 +1181,7 @@
mKeyStore.load(null, null);
- assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_2, FAKE_CA_1,
+ assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_2, FAKE_RSA_CA_1,
KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
assertTrue("Should contain added CA certificate", mKeyStore.containsAlias(TEST_ALIAS_2));
@@ -572,18 +1202,18 @@
// TEST_ALIAS_1
assertTrue(mAndroidKeyStore.importKey(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1,
- FAKE_KEY_1, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
- assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_1, FAKE_USER_1,
+ FAKE_RSA_KEY_1, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+ assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_1, FAKE_RSA_USER_1,
KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
- assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1,
+ assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_RSA_CA_1,
KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
// TEST_ALIAS_2
- assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_2, FAKE_CA_1,
+ assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_2, FAKE_RSA_CA_1,
KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
// TEST_ALIAS_3
- assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_3, FAKE_CA_1,
+ assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_3, FAKE_RSA_CA_1,
KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
assertAliases(new String[] { TEST_ALIAS_1, TEST_ALIAS_2, TEST_ALIAS_3 });
@@ -617,10 +1247,10 @@
// TEST_ALIAS_1
assertTrue(mAndroidKeyStore.importKey(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1,
- FAKE_KEY_1, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
- assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_1, FAKE_USER_1,
+ FAKE_RSA_KEY_1, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+ assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_1, FAKE_RSA_USER_1,
KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
- assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1,
+ assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_RSA_CA_1,
KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
// Should not throw when a non-existent entry is requested for delete.
@@ -632,7 +1262,7 @@
mKeyStore.load(null, null);
- assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1,
+ assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_RSA_CA_1,
KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
assertAliases(new String[] { TEST_ALIAS_1 });
@@ -645,7 +1275,7 @@
assertNotNull("Retrieved certificate should not be null", retrieved);
CertificateFactory f = CertificateFactory.getInstance("X.509");
- Certificate actual = f.generateCertificate(new ByteArrayInputStream(FAKE_CA_1));
+ Certificate actual = f.generateCertificate(new ByteArrayInputStream(FAKE_RSA_CA_1));
assertEquals("Actual and retrieved certificates should be the same", actual, retrieved);
}
@@ -664,11 +1294,11 @@
mKeyStore.load(null, null);
- assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1,
+ assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_RSA_CA_1,
KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
CertificateFactory f = CertificateFactory.getInstance("X.509");
- Certificate actual = f.generateCertificate(new ByteArrayInputStream(FAKE_CA_1));
+ Certificate actual = f.generateCertificate(new ByteArrayInputStream(FAKE_RSA_CA_1));
assertEquals("Stored certificate alias should be found", TEST_ALIAS_1,
mKeyStore.getCertificateAlias(actual));
@@ -681,14 +1311,14 @@
mKeyStore.load(null, null);
assertTrue(mAndroidKeyStore.importKey(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1,
- FAKE_KEY_1, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
- assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_1, FAKE_USER_1,
+ FAKE_RSA_KEY_1, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+ assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_1, FAKE_RSA_USER_1,
KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
- assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1,
+ assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_RSA_CA_1,
KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
CertificateFactory f = CertificateFactory.getInstance("X.509");
- Certificate actual = f.generateCertificate(new ByteArrayInputStream(FAKE_USER_1));
+ Certificate actual = f.generateCertificate(new ByteArrayInputStream(FAKE_RSA_USER_1));
assertEquals("Stored certificate alias should be found", TEST_ALIAS_1,
mKeyStore.getCertificateAlias(actual));
@@ -701,19 +1331,19 @@
mKeyStore.load(null, null);
// Insert TrustedCertificateEntry with CA name
- assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_2, FAKE_CA_1,
+ assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_2, FAKE_RSA_CA_1,
KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
// Insert PrivateKeyEntry that uses the same CA
assertTrue(mAndroidKeyStore.importKey(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1,
- FAKE_KEY_1, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
- assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_1, FAKE_USER_1,
+ FAKE_RSA_KEY_1, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+ assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_1, FAKE_RSA_USER_1,
KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
- assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1,
+ assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_RSA_CA_1,
KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
CertificateFactory f = CertificateFactory.getInstance("X.509");
- Certificate actual = f.generateCertificate(new ByteArrayInputStream(FAKE_CA_1));
+ Certificate actual = f.generateCertificate(new ByteArrayInputStream(FAKE_RSA_CA_1));
assertEquals("Stored certificate alias should be found", TEST_ALIAS_2,
mKeyStore.getCertificateAlias(actual));
@@ -726,7 +1356,7 @@
mKeyStore.load(null, null);
CertificateFactory f = CertificateFactory.getInstance("X.509");
- Certificate actual = f.generateCertificate(new ByteArrayInputStream(FAKE_CA_1));
+ Certificate actual = f.generateCertificate(new ByteArrayInputStream(FAKE_RSA_CA_1));
assertNull("Stored certificate alias should not be found",
mKeyStore.getCertificateAlias(actual));
@@ -737,11 +1367,11 @@
mKeyStore.load(null, null);
- assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1,
+ assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_RSA_CA_1,
KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
CertificateFactory f = CertificateFactory.getInstance("X.509");
- Certificate userCert = f.generateCertificate(new ByteArrayInputStream(FAKE_USER_1));
+ Certificate userCert = f.generateCertificate(new ByteArrayInputStream(FAKE_RSA_USER_1));
assertNull("Stored certificate alias should be found",
mKeyStore.getCertificateAlias(userCert));
@@ -753,16 +1383,16 @@
mKeyStore.load(null, null);
assertTrue(mAndroidKeyStore.importKey(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1,
- FAKE_KEY_1, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
- assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_1, FAKE_USER_1,
+ FAKE_RSA_KEY_1, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+ assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_1, FAKE_RSA_USER_1,
KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
- assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1,
+ assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_RSA_CA_1,
KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
CertificateFactory cf = CertificateFactory.getInstance("X.509");
Certificate[] expected = new Certificate[2];
- expected[0] = cf.generateCertificate(new ByteArrayInputStream(FAKE_USER_1));
- expected[1] = cf.generateCertificate(new ByteArrayInputStream(FAKE_CA_1));
+ expected[0] = cf.generateCertificate(new ByteArrayInputStream(FAKE_RSA_USER_1));
+ expected[1] = cf.generateCertificate(new ByteArrayInputStream(FAKE_RSA_CA_1));
Certificate[] actual = mKeyStore.getCertificateChain(TEST_ALIAS_1);
@@ -792,10 +1422,10 @@
mKeyStore.load(null, null);
assertTrue(mAndroidKeyStore.importKey(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1,
- FAKE_KEY_1, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
- assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_1, FAKE_USER_1,
+ FAKE_RSA_KEY_1, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+ assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_1, FAKE_RSA_USER_1,
KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
- assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1,
+ assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_RSA_CA_1,
KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
Date now = new Date();
@@ -812,10 +1442,10 @@
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,
+ FAKE_RSA_KEY_1, KeyStore.UID_SELF, KeyStore.FLAG_NONE));
+ assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_1, FAKE_RSA_USER_1,
KeyStore.UID_SELF, KeyStore.FLAG_NONE));
- assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1,
+ assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_RSA_CA_1,
KeyStore.UID_SELF, KeyStore.FLAG_NONE));
Date now = new Date();
@@ -833,7 +1463,7 @@
mKeyStore.load(null, null);
- assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1,
+ assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_RSA_CA_1,
KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
Date now = new Date();
@@ -853,10 +1483,10 @@
mKeyStore.load(null, null);
assertTrue(mAndroidKeyStore.importKey(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1,
- FAKE_KEY_1, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
- assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_1, FAKE_USER_1,
+ FAKE_RSA_KEY_1, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+ assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_1, FAKE_RSA_USER_1,
KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
- assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1,
+ assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_RSA_CA_1,
KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
Entry entry = mKeyStore.getEntry(TEST_ALIAS_1, null);
@@ -866,17 +1496,18 @@
PrivateKeyEntry keyEntry = (PrivateKeyEntry) entry;
- assertPrivateKeyEntryEquals(keyEntry, FAKE_KEY_1, FAKE_USER_1, FAKE_CA_1);
+ assertPrivateKeyEntryEquals(keyEntry, "RSA", FAKE_RSA_KEY_1, FAKE_RSA_USER_1,
+ FAKE_RSA_CA_1);
}
- public void testKeyStore_GetEntry_NullParams_Unencrypted_Success() throws Exception {
+ public void testKeyStore_GetEntry_DSA_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,
+ FAKE_DSA_KEY_1, KeyStore.UID_SELF, KeyStore.FLAG_NONE));
+ assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_1,
+ FAKE_DSA_USER_1, KeyStore.UID_SELF, KeyStore.FLAG_NONE));
+ assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_DSA_CA_1,
KeyStore.UID_SELF, KeyStore.FLAG_NONE));
Entry entry = mKeyStore.getEntry(TEST_ALIAS_1, null);
@@ -886,13 +1517,54 @@
PrivateKeyEntry keyEntry = (PrivateKeyEntry) entry;
- assertPrivateKeyEntryEquals(keyEntry, FAKE_KEY_1, FAKE_USER_1, FAKE_CA_1);
+ assertPrivateKeyEntryEquals(keyEntry, "DSA", FAKE_DSA_KEY_1, FAKE_DSA_USER_1, FAKE_DSA_CA_1);
+ }
+
+ public void testKeyStore_GetEntry_EC_NullParams_Unencrypted_Success() throws Exception {
+ mKeyStore.load(null, null);
+
+ assertTrue(mAndroidKeyStore.importKey(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1,
+ FAKE_EC_KEY_1, KeyStore.UID_SELF, KeyStore.FLAG_NONE));
+ assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_1,
+ FAKE_EC_USER_1, KeyStore.UID_SELF, KeyStore.FLAG_NONE));
+ assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_EC_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, "EC", FAKE_EC_KEY_1, FAKE_EC_USER_1, FAKE_EC_CA_1);
+ }
+
+ public void testKeyStore_GetEntry_RSA_NullParams_Unencrypted_Success() throws Exception {
+ mKeyStore.load(null, null);
+
+ assertTrue(mAndroidKeyStore.importKey(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1,
+ FAKE_RSA_KEY_1, KeyStore.UID_SELF, KeyStore.FLAG_NONE));
+ assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_1,
+ FAKE_RSA_USER_1, KeyStore.UID_SELF, KeyStore.FLAG_NONE));
+ assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_RSA_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, "RSA", FAKE_RSA_KEY_1, FAKE_RSA_USER_1,
+ FAKE_RSA_CA_1);
}
@SuppressWarnings("unchecked")
- private void assertPrivateKeyEntryEquals(PrivateKeyEntry keyEntry, byte[] key, byte[] cert,
- byte[] ca) throws Exception {
- KeyFactory keyFact = KeyFactory.getInstance("RSA");
+ private void assertPrivateKeyEntryEquals(PrivateKeyEntry keyEntry, String keyType, byte[] key,
+ byte[] cert, byte[] ca) throws Exception {
+ KeyFactory keyFact = KeyFactory.getInstance(keyType);
PrivateKey expectedKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(key));
CertificateFactory certFact = CertificateFactory.getInstance("X.509");
@@ -911,9 +1583,19 @@
private void assertPrivateKeyEntryEquals(PrivateKeyEntry keyEntry, PrivateKey expectedKey,
Certificate expectedCert, Collection<Certificate> expectedChain) throws Exception {
- assertEquals("Returned PrivateKey should be what we inserted",
- ((RSAPrivateKey) expectedKey).getModulus(),
- ((RSAPrivateKey) keyEntry.getPrivateKey()).getModulus());
+ if (expectedKey instanceof DSAPrivateKey) {
+ assertEquals("Returned PrivateKey should be what we inserted",
+ ((DSAPrivateKey) expectedKey).getParams(),
+ ((DSAPublicKey) keyEntry.getCertificate().getPublicKey()).getParams());
+ } else if (expectedKey instanceof ECPrivateKey) {
+ assertEquals("Returned PrivateKey should be what we inserted",
+ ((ECPrivateKey) expectedKey).getParams().getCurve(),
+ ((ECPublicKey) keyEntry.getCertificate().getPublicKey()).getParams().getCurve());
+ } else if (expectedKey instanceof RSAPrivateKey) {
+ 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());
@@ -956,10 +1638,10 @@
mKeyStore.load(null, null);
assertTrue(mAndroidKeyStore.importKey(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1,
- FAKE_KEY_1, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
- assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_1, FAKE_USER_1,
+ FAKE_RSA_KEY_1, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+ assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_1, FAKE_RSA_USER_1,
KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
- assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1,
+ assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_RSA_CA_1,
KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
Key key = mKeyStore.getKey(TEST_ALIAS_1, null);
@@ -970,7 +1652,7 @@
RSAPrivateKey actualKey = (RSAPrivateKey) key;
KeyFactory keyFact = KeyFactory.getInstance("RSA");
- PrivateKey expectedKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(FAKE_KEY_1));
+ PrivateKey expectedKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(FAKE_RSA_KEY_1));
assertEquals("Inserted key should be same as retrieved key",
((RSAPrivateKey) expectedKey).getModulus(), actualKey.getModulus());
@@ -980,10 +1662,10 @@
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,
+ FAKE_RSA_KEY_1, KeyStore.UID_SELF, KeyStore.FLAG_NONE));
+ assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_1, FAKE_RSA_USER_1,
KeyStore.UID_SELF, KeyStore.FLAG_NONE));
- assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1,
+ assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_RSA_CA_1,
KeyStore.UID_SELF, KeyStore.FLAG_NONE));
Key key = mKeyStore.getKey(TEST_ALIAS_1, null);
@@ -994,7 +1676,7 @@
RSAPrivateKey actualKey = (RSAPrivateKey) key;
KeyFactory keyFact = KeyFactory.getInstance("RSA");
- PrivateKey expectedKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(FAKE_KEY_1));
+ PrivateKey expectedKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(FAKE_RSA_KEY_1));
assertEquals("Inserted key should be same as retrieved key",
((RSAPrivateKey) expectedKey).getModulus(), actualKey.getModulus());
@@ -1005,7 +1687,7 @@
mKeyStore.load(null, null);
- assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1,
+ assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_RSA_CA_1,
KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
assertNull("Certificate entries should return null", mKeyStore.getKey(TEST_ALIAS_1, null));
@@ -1035,7 +1717,7 @@
setupPassword();
mKeyStore.load(null, null);
- assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1,
+ assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_RSA_CA_1,
KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
assertTrue("Should return true for CA certificate",
@@ -1047,10 +1729,10 @@
mKeyStore.load(null, null);
assertTrue(mAndroidKeyStore.importKey(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1,
- FAKE_KEY_1, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
- assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_1, FAKE_USER_1,
+ FAKE_RSA_KEY_1, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+ assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_1, FAKE_RSA_USER_1,
KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
- assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1,
+ assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_RSA_CA_1,
KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
assertFalse("Should return false for PrivateKeyEntry",
@@ -1077,10 +1759,10 @@
mKeyStore.load(null, null);
assertTrue(mAndroidKeyStore.importKey(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1,
- FAKE_KEY_1, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
- assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_1, FAKE_USER_1,
+ FAKE_RSA_KEY_1, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+ assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_1, FAKE_RSA_USER_1,
KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
- assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1,
+ assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_RSA_CA_1,
KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
assertTrue("Should return true for PrivateKeyEntry", mKeyStore.isKeyEntry(TEST_ALIAS_1));
@@ -1090,7 +1772,7 @@
setupPassword();
mKeyStore.load(null, null);
- assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1,
+ assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_RSA_CA_1,
KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
assertFalse("Should return false for CA certificate", mKeyStore.isKeyEntry(TEST_ALIAS_1));
@@ -1106,7 +1788,7 @@
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));
+ final Certificate actual = f.generateCertificate(new ByteArrayInputStream(FAKE_RSA_CA_1));
setupPassword();
mKeyStore.load(null, null);
@@ -1124,13 +1806,13 @@
setupPassword();
mKeyStore.load(null, null);
- assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1,
+ assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_RSA_CA_1,
KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
assertAliases(new String[] { TEST_ALIAS_1 });
final CertificateFactory f = CertificateFactory.getInstance("X.509");
- final Certificate cert = f.generateCertificate(new ByteArrayInputStream(FAKE_CA_1));
+ final Certificate cert = f.generateCertificate(new ByteArrayInputStream(FAKE_RSA_CA_1));
// TODO have separate FAKE_CA for second test
mKeyStore.setCertificateEntry(TEST_ALIAS_1, cert);
@@ -1143,16 +1825,16 @@
mKeyStore.load(null, null);
assertTrue(mAndroidKeyStore.importKey(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1,
- FAKE_KEY_1, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
- assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_1, FAKE_USER_1,
+ FAKE_RSA_KEY_1, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+ assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_1, FAKE_RSA_USER_1,
KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
- assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1,
+ assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_RSA_CA_1,
KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
assertAliases(new String[] { TEST_ALIAS_1 });
final CertificateFactory f = CertificateFactory.getInstance("X.509");
- final Certificate cert = f.generateCertificate(new ByteArrayInputStream(FAKE_CA_1));
+ final Certificate cert = f.generateCertificate(new ByteArrayInputStream(FAKE_RSA_CA_1));
try {
mKeyStore.setCertificateEntry(TEST_ALIAS_1, cert);
@@ -1166,13 +1848,13 @@
mKeyStore.load(null, null);
KeyFactory keyFact = KeyFactory.getInstance("RSA");
- PrivateKey expectedKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(FAKE_KEY_1));
+ PrivateKey expectedKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(FAKE_RSA_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));
+ expectedChain[0] = f.generateCertificate(new ByteArrayInputStream(FAKE_RSA_USER_1));
+ expectedChain[1] = f.generateCertificate(new ByteArrayInputStream(FAKE_RSA_CA_1));
PrivateKeyEntry expected = new PrivateKeyEntry(expectedKey, expectedChain);
@@ -1186,20 +1868,74 @@
PrivateKeyEntry actual = (PrivateKeyEntry) actualEntry;
- assertPrivateKeyEntryEquals(actual, FAKE_KEY_1, FAKE_USER_1, FAKE_CA_1);
+ assertPrivateKeyEntryEquals(actual, "RSA", FAKE_RSA_KEY_1, FAKE_RSA_USER_1, FAKE_RSA_CA_1);
}
- public void testKeyStore_SetEntry_PrivateKeyEntry_Unencrypted_Success() throws Exception {
+ public void testKeyStore_SetEntry_PrivateKeyEntry_DSA_Unencrypted_Success() throws Exception {
+ mKeyStore.load(null, null);
+
+ KeyFactory keyFact = KeyFactory.getInstance("DSA");
+ PrivateKey expectedKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(FAKE_DSA_KEY_1));
+
+ final CertificateFactory f = CertificateFactory.getInstance("X.509");
+
+ final Certificate[] expectedChain = new Certificate[2];
+ expectedChain[0] = f.generateCertificate(new ByteArrayInputStream(FAKE_DSA_USER_1));
+ expectedChain[1] = f.generateCertificate(new ByteArrayInputStream(FAKE_DSA_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, "DSA", FAKE_DSA_KEY_1, FAKE_DSA_USER_1, FAKE_DSA_CA_1);
+ }
+
+ public void testKeyStore_SetEntry_PrivateKeyEntry_EC_Unencrypted_Success() throws Exception {
+ mKeyStore.load(null, null);
+
+ KeyFactory keyFact = KeyFactory.getInstance("EC");
+ PrivateKey expectedKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(FAKE_EC_KEY_1));
+
+ final CertificateFactory f = CertificateFactory.getInstance("X.509");
+
+ final Certificate[] expectedChain = new Certificate[2];
+ expectedChain[0] = f.generateCertificate(new ByteArrayInputStream(FAKE_EC_USER_1));
+ expectedChain[1] = f.generateCertificate(new ByteArrayInputStream(FAKE_EC_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, "EC", FAKE_EC_KEY_1, FAKE_EC_USER_1, FAKE_EC_CA_1);
+ }
+
+ public void testKeyStore_SetEntry_PrivateKeyEntry_RSA_Unencrypted_Success() throws Exception {
mKeyStore.load(null, null);
KeyFactory keyFact = KeyFactory.getInstance("RSA");
- PrivateKey expectedKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(FAKE_KEY_1));
+ PrivateKey expectedKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(FAKE_RSA_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));
+ expectedChain[0] = f.generateCertificate(new ByteArrayInputStream(FAKE_RSA_USER_1));
+ expectedChain[1] = f.generateCertificate(new ByteArrayInputStream(FAKE_RSA_CA_1));
PrivateKeyEntry expected = new PrivateKeyEntry(expectedKey, expectedChain);
@@ -1213,20 +1949,20 @@
PrivateKeyEntry actual = (PrivateKeyEntry) actualEntry;
- assertPrivateKeyEntryEquals(actual, FAKE_KEY_1, FAKE_USER_1, FAKE_CA_1);
+ assertPrivateKeyEntryEquals(actual, "RSA", FAKE_RSA_KEY_1, FAKE_RSA_USER_1, FAKE_RSA_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));
+ PrivateKey expectedKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(FAKE_RSA_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));
+ expectedChain[0] = f.generateCertificate(new ByteArrayInputStream(FAKE_RSA_USER_1));
+ expectedChain[1] = f.generateCertificate(new ByteArrayInputStream(FAKE_RSA_CA_1));
PrivateKeyEntry entry = new PrivateKeyEntry(expectedKey, expectedChain);
@@ -1253,11 +1989,11 @@
// Start with PrivateKeyEntry
{
- PrivateKey expectedKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(FAKE_KEY_1));
+ PrivateKey expectedKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(FAKE_RSA_KEY_1));
final Certificate[] expectedChain = new Certificate[2];
- expectedChain[0] = f.generateCertificate(new ByteArrayInputStream(FAKE_USER_1));
- expectedChain[1] = f.generateCertificate(new ByteArrayInputStream(FAKE_CA_1));
+ expectedChain[0] = f.generateCertificate(new ByteArrayInputStream(FAKE_RSA_USER_1));
+ expectedChain[1] = f.generateCertificate(new ByteArrayInputStream(FAKE_RSA_CA_1));
PrivateKeyEntry expected = new PrivateKeyEntry(expectedKey, expectedChain);
@@ -1271,17 +2007,18 @@
PrivateKeyEntry actual = (PrivateKeyEntry) actualEntry;
- assertPrivateKeyEntryEquals(actual, FAKE_KEY_1, FAKE_USER_1, FAKE_CA_1);
+ assertPrivateKeyEntryEquals(actual, "RSA", FAKE_RSA_KEY_1, FAKE_RSA_USER_1,
+ FAKE_RSA_CA_1);
}
// TODO make entirely new test vector for the overwrite
// Replace with PrivateKeyEntry
{
- PrivateKey expectedKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(FAKE_KEY_1));
+ PrivateKey expectedKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(FAKE_RSA_KEY_1));
final Certificate[] expectedChain = new Certificate[2];
- expectedChain[0] = f.generateCertificate(new ByteArrayInputStream(FAKE_USER_1));
- expectedChain[1] = f.generateCertificate(new ByteArrayInputStream(FAKE_CA_1));
+ expectedChain[0] = f.generateCertificate(new ByteArrayInputStream(FAKE_RSA_USER_1));
+ expectedChain[1] = f.generateCertificate(new ByteArrayInputStream(FAKE_RSA_CA_1));
PrivateKeyEntry expected = new PrivateKeyEntry(expectedKey, expectedChain);
@@ -1295,7 +2032,8 @@
PrivateKeyEntry actual = (PrivateKeyEntry) actualEntry;
- assertPrivateKeyEntryEquals(actual, FAKE_KEY_1, FAKE_USER_1, FAKE_CA_1);
+ assertPrivateKeyEntryEquals(actual, "RSA", FAKE_RSA_KEY_1, FAKE_RSA_USER_1,
+ FAKE_RSA_CA_1);
}
}
@@ -1308,7 +2046,7 @@
// Start with TrustedCertificateEntry
{
- final Certificate caCert = f.generateCertificate(new ByteArrayInputStream(FAKE_CA_1));
+ final Certificate caCert = f.generateCertificate(new ByteArrayInputStream(FAKE_RSA_CA_1));
TrustedCertificateEntry expectedCertEntry = new TrustedCertificateEntry(caCert);
mKeyStore.setEntry(TEST_ALIAS_1, expectedCertEntry, null);
@@ -1326,10 +2064,10 @@
// Replace with PrivateKeyEntry
{
KeyFactory keyFact = KeyFactory.getInstance("RSA");
- PrivateKey expectedKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(FAKE_KEY_1));
+ PrivateKey expectedKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(FAKE_RSA_KEY_1));
final Certificate[] expectedChain = new Certificate[2];
- expectedChain[0] = f.generateCertificate(new ByteArrayInputStream(FAKE_USER_1));
- expectedChain[1] = f.generateCertificate(new ByteArrayInputStream(FAKE_CA_1));
+ expectedChain[0] = f.generateCertificate(new ByteArrayInputStream(FAKE_RSA_USER_1));
+ expectedChain[1] = f.generateCertificate(new ByteArrayInputStream(FAKE_RSA_CA_1));
PrivateKeyEntry expectedPrivEntry = new PrivateKeyEntry(expectedKey, expectedChain);
@@ -1341,7 +2079,8 @@
actualEntry instanceof PrivateKeyEntry);
PrivateKeyEntry actualPrivEntry = (PrivateKeyEntry) actualEntry;
- assertPrivateKeyEntryEquals(actualPrivEntry, FAKE_KEY_1, FAKE_USER_1, FAKE_CA_1);
+ assertPrivateKeyEntryEquals(actualPrivEntry, "RSA", FAKE_RSA_KEY_1, FAKE_RSA_USER_1,
+ FAKE_RSA_CA_1);
}
}
@@ -1352,14 +2091,14 @@
final CertificateFactory f = CertificateFactory.getInstance("X.509");
- final Certificate caCert = f.generateCertificate(new ByteArrayInputStream(FAKE_CA_1));
+ final Certificate caCert = f.generateCertificate(new ByteArrayInputStream(FAKE_RSA_CA_1));
// Start with PrivateKeyEntry
{
KeyFactory keyFact = KeyFactory.getInstance("RSA");
- PrivateKey expectedKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(FAKE_KEY_1));
+ PrivateKey expectedKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(FAKE_RSA_KEY_1));
final Certificate[] expectedChain = new Certificate[2];
- expectedChain[0] = f.generateCertificate(new ByteArrayInputStream(FAKE_USER_1));
+ expectedChain[0] = f.generateCertificate(new ByteArrayInputStream(FAKE_RSA_USER_1));
expectedChain[1] = caCert;
PrivateKeyEntry expectedPrivEntry = new PrivateKeyEntry(expectedKey, expectedChain);
@@ -1372,7 +2111,8 @@
actualEntry instanceof PrivateKeyEntry);
PrivateKeyEntry actualPrivEntry = (PrivateKeyEntry) actualEntry;
- assertPrivateKeyEntryEquals(actualPrivEntry, FAKE_KEY_1, FAKE_USER_1, FAKE_CA_1);
+ assertPrivateKeyEntryEquals(actualPrivEntry, "RSA", FAKE_RSA_KEY_1, FAKE_RSA_USER_1,
+ FAKE_RSA_CA_1);
}
// Replace with TrustedCertificateEntry
@@ -1400,14 +2140,14 @@
final CertificateFactory f = CertificateFactory.getInstance("X.509");
- final Certificate caCert = f.generateCertificate(new ByteArrayInputStream(FAKE_CA_1));
+ final Certificate caCert = f.generateCertificate(new ByteArrayInputStream(FAKE_RSA_CA_1));
// Start with PrivateKeyEntry
{
KeyFactory keyFact = KeyFactory.getInstance("RSA");
- PrivateKey expectedKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(FAKE_KEY_1));
+ PrivateKey expectedKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(FAKE_RSA_KEY_1));
final Certificate[] expectedChain = new Certificate[2];
- expectedChain[0] = f.generateCertificate(new ByteArrayInputStream(FAKE_USER_1));
+ expectedChain[0] = f.generateCertificate(new ByteArrayInputStream(FAKE_RSA_USER_1));
expectedChain[1] = caCert;
PrivateKeyEntry expectedPrivEntry = new PrivateKeyEntry(expectedKey, expectedChain);
@@ -1420,15 +2160,16 @@
actualEntry instanceof PrivateKeyEntry);
PrivateKeyEntry actualPrivEntry = (PrivateKeyEntry) actualEntry;
- assertPrivateKeyEntryEquals(actualPrivEntry, FAKE_KEY_1, FAKE_USER_1, FAKE_CA_1);
+ assertPrivateKeyEntryEquals(actualPrivEntry, "RSA", FAKE_RSA_KEY_1, FAKE_RSA_USER_1,
+ FAKE_RSA_CA_1);
}
// Replace with PrivateKeyEntry that has no chain
{
KeyFactory keyFact = KeyFactory.getInstance("RSA");
- PrivateKey expectedKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(FAKE_KEY_1));
+ PrivateKey expectedKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(FAKE_RSA_KEY_1));
final Certificate[] expectedChain = new Certificate[1];
- expectedChain[0] = f.generateCertificate(new ByteArrayInputStream(FAKE_USER_1));
+ expectedChain[0] = f.generateCertificate(new ByteArrayInputStream(FAKE_RSA_USER_1));
PrivateKeyEntry expectedPrivEntry = new PrivateKeyEntry(expectedKey, expectedChain);
@@ -1440,7 +2181,8 @@
actualEntry instanceof PrivateKeyEntry);
PrivateKeyEntry actualPrivEntry = (PrivateKeyEntry) actualEntry;
- assertPrivateKeyEntryEquals(actualPrivEntry, FAKE_KEY_1, FAKE_USER_1, null);
+ assertPrivateKeyEntryEquals(actualPrivEntry, "RSA", FAKE_RSA_KEY_1, FAKE_RSA_USER_1,
+ null);
}
}
@@ -1453,7 +2195,7 @@
// Insert TrustedCertificateEntry
{
- final Certificate caCert = f.generateCertificate(new ByteArrayInputStream(FAKE_CA_1));
+ final Certificate caCert = f.generateCertificate(new ByteArrayInputStream(FAKE_RSA_CA_1));
TrustedCertificateEntry expectedCertEntry = new TrustedCertificateEntry(caCert);
mKeyStore.setEntry(TEST_ALIAS_1, expectedCertEntry, null);
@@ -1471,7 +2213,7 @@
// Replace with TrustedCertificateEntry of USER
{
final Certificate userCert = f
- .generateCertificate(new ByteArrayInputStream(FAKE_USER_1));
+ .generateCertificate(new ByteArrayInputStream(FAKE_RSA_USER_1));
TrustedCertificateEntry expectedUserEntry = new TrustedCertificateEntry(userCert);
mKeyStore.setEntry(TEST_ALIAS_1, expectedUserEntry, null);
@@ -1493,12 +2235,12 @@
final CertificateFactory f = CertificateFactory.getInstance("X.509");
- final Certificate caCert = f.generateCertificate(new ByteArrayInputStream(FAKE_CA_1));
+ final Certificate caCert = f.generateCertificate(new ByteArrayInputStream(FAKE_RSA_CA_1));
KeyFactory keyFact = KeyFactory.getInstance("RSA");
- PrivateKey privKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(FAKE_KEY_1));
+ PrivateKey privKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(FAKE_RSA_KEY_1));
final Certificate[] chain = new Certificate[2];
- chain[0] = f.generateCertificate(new ByteArrayInputStream(FAKE_USER_1));
+ chain[0] = f.generateCertificate(new ByteArrayInputStream(FAKE_RSA_USER_1));
chain[1] = caCert;
try {
@@ -1514,12 +2256,12 @@
final CertificateFactory f = CertificateFactory.getInstance("X.509");
- final Certificate caCert = f.generateCertificate(new ByteArrayInputStream(FAKE_CA_1));
+ final Certificate caCert = f.generateCertificate(new ByteArrayInputStream(FAKE_RSA_CA_1));
KeyFactory keyFact = KeyFactory.getInstance("RSA");
- PrivateKey privKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(FAKE_KEY_1));
+ PrivateKey privKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(FAKE_RSA_KEY_1));
final Certificate[] chain = new Certificate[2];
- chain[0] = f.generateCertificate(new ByteArrayInputStream(FAKE_USER_1));
+ chain[0] = f.generateCertificate(new ByteArrayInputStream(FAKE_RSA_USER_1));
chain[1] = caCert;
mKeyStore.setKeyEntry(TEST_ALIAS_1, privKey, null, chain);
@@ -1532,7 +2274,7 @@
PrivateKeyEntry actual = (PrivateKeyEntry) actualEntry;
- assertPrivateKeyEntryEquals(actual, FAKE_KEY_1, FAKE_USER_1, FAKE_CA_1);
+ assertPrivateKeyEntryEquals(actual, "RSA", FAKE_RSA_KEY_1, FAKE_RSA_USER_1, FAKE_RSA_CA_1);
}
public void testKeyStore_SetKeyEntry_Replaced_Encrypted_Success() throws Exception {
@@ -1541,14 +2283,14 @@
final CertificateFactory f = CertificateFactory.getInstance("X.509");
- final Certificate caCert = f.generateCertificate(new ByteArrayInputStream(FAKE_CA_1));
+ final Certificate caCert = f.generateCertificate(new ByteArrayInputStream(FAKE_RSA_CA_1));
// Insert initial key
{
KeyFactory keyFact = KeyFactory.getInstance("RSA");
- PrivateKey privKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(FAKE_KEY_1));
+ PrivateKey privKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(FAKE_RSA_KEY_1));
final Certificate[] chain = new Certificate[2];
- chain[0] = f.generateCertificate(new ByteArrayInputStream(FAKE_USER_1));
+ chain[0] = f.generateCertificate(new ByteArrayInputStream(FAKE_RSA_USER_1));
chain[1] = caCert;
mKeyStore.setKeyEntry(TEST_ALIAS_1, privKey, null, chain);
@@ -1561,16 +2303,17 @@
PrivateKeyEntry actual = (PrivateKeyEntry) actualEntry;
- assertPrivateKeyEntryEquals(actual, FAKE_KEY_1, FAKE_USER_1, FAKE_CA_1);
+ assertPrivateKeyEntryEquals(actual, "RSA", FAKE_RSA_KEY_1, FAKE_RSA_USER_1,
+ FAKE_RSA_CA_1);
}
// TODO make a separate key
// Replace key
{
KeyFactory keyFact = KeyFactory.getInstance("RSA");
- PrivateKey privKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(FAKE_KEY_1));
+ PrivateKey privKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(FAKE_RSA_KEY_1));
final Certificate[] chain = new Certificate[2];
- chain[0] = f.generateCertificate(new ByteArrayInputStream(FAKE_USER_1));
+ chain[0] = f.generateCertificate(new ByteArrayInputStream(FAKE_RSA_USER_1));
chain[1] = caCert;
mKeyStore.setKeyEntry(TEST_ALIAS_1, privKey, null, chain);
@@ -1583,7 +2326,8 @@
PrivateKeyEntry actual = (PrivateKeyEntry) actualEntry;
- assertPrivateKeyEntryEquals(actual, FAKE_KEY_1, FAKE_USER_1, FAKE_CA_1);
+ assertPrivateKeyEntryEquals(actual, "RSA", FAKE_RSA_KEY_1, FAKE_RSA_USER_1,
+ FAKE_RSA_CA_1);
}
}
@@ -1635,7 +2379,7 @@
{
final String privateKeyAlias = Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1;
assertTrue(mAndroidKeyStore.generate(privateKeyAlias, KeyStore.UID_SELF,
- KeyStore.FLAG_ENCRYPTED));
+ NativeCrypto.EVP_PKEY_RSA, 1024, KeyStore.FLAG_ENCRYPTED, null));
Key key = mKeyStore.getKey(TEST_ALIAS_1, null);
@@ -1691,7 +2435,7 @@
{
final String privateKeyAlias = Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1;
assertTrue(mAndroidKeyStore.generate(privateKeyAlias, KeyStore.UID_SELF,
- KeyStore.FLAG_ENCRYPTED));
+ NativeCrypto.EVP_PKEY_RSA, 1024, KeyStore.FLAG_ENCRYPTED, null));
X509Certificate cert = generateCertificate(mAndroidKeyStore, TEST_ALIAS_1,
TEST_SERIAL_1, TEST_DN_1, NOW, NOW_PLUS_10_YEARS);
@@ -1704,7 +2448,7 @@
{
final String privateKeyAlias = Credentials.USER_PRIVATE_KEY + TEST_ALIAS_2;
assertTrue(mAndroidKeyStore.generate(privateKeyAlias, KeyStore.UID_SELF,
- KeyStore.FLAG_ENCRYPTED));
+ NativeCrypto.EVP_PKEY_RSA, 1024, KeyStore.FLAG_ENCRYPTED, null));
X509Certificate cert = generateCertificate(mAndroidKeyStore, TEST_ALIAS_2,
TEST_SERIAL_2, TEST_DN_2, NOW, NOW_PLUS_10_YEARS);
@@ -1736,7 +2480,8 @@
{
final String privateKeyAlias = Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1;
assertTrue(mAndroidKeyStore.generate(privateKeyAlias,
- android.security.KeyStore.UID_SELF, android.security.KeyStore.FLAG_NONE));
+ android.security.KeyStore.UID_SELF, NativeCrypto.EVP_PKEY_RSA, 1024,
+ android.security.KeyStore.FLAG_NONE, null));
X509Certificate cert =
generateCertificate(mAndroidKeyStore, TEST_ALIAS_1, TEST_SERIAL_1, TEST_DN_1,
@@ -1774,20 +2519,21 @@
setupPassword();
mKeyStore.load(null, null);
- assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1,
+ assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_RSA_CA_1,
KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
assertEquals("The keystore size should match expected", 1, mKeyStore.size());
assertAliases(new String[] { TEST_ALIAS_1 });
- assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_2, FAKE_CA_1,
+ assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_2, FAKE_RSA_CA_1,
KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
assertEquals("The keystore size should match expected", 2, mKeyStore.size());
assertAliases(new String[] { TEST_ALIAS_1, TEST_ALIAS_2 });
assertTrue(mAndroidKeyStore.generate(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_3,
- KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+ KeyStore.UID_SELF, NativeCrypto.EVP_PKEY_RSA, 1024, KeyStore.FLAG_ENCRYPTED,
+ null));
assertEquals("The keystore size should match expected", 3, mKeyStore.size());
assertAliases(new String[] { TEST_ALIAS_1, TEST_ALIAS_2, TEST_ALIAS_3 });
@@ -1854,7 +2600,8 @@
private void setupKey() throws Exception {
final String privateKeyAlias = Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1;
assertTrue(mAndroidKeyStore
- .generate(privateKeyAlias, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+ .generate(privateKeyAlias, KeyStore.UID_SELF, NativeCrypto.EVP_PKEY_RSA, 1024,
+ KeyStore.FLAG_ENCRYPTED, null));
X509Certificate cert = generateCertificate(mAndroidKeyStore, TEST_ALIAS_1, TEST_SERIAL_1,
TEST_DN_1, NOW, NOW_PLUS_10_YEARS);
diff --git a/keystore/tests/src/android/security/KeyPairGeneratorSpecTest.java b/keystore/tests/src/android/security/KeyPairGeneratorSpecTest.java
index 113d730..bc8dd13 100644
--- a/keystore/tests/src/android/security/KeyPairGeneratorSpecTest.java
+++ b/keystore/tests/src/android/security/KeyPairGeneratorSpecTest.java
@@ -40,13 +40,17 @@
public void testConstructor_Success() throws Exception {
KeyPairGeneratorSpec spec =
- new KeyPairGeneratorSpec(getContext(), TEST_ALIAS_1, TEST_DN_1, SERIAL_1,
- NOW, NOW_PLUS_10_YEARS, 0);
+ new KeyPairGeneratorSpec(getContext(), TEST_ALIAS_1, "RSA", 1024, null, TEST_DN_1,
+ SERIAL_1, NOW, NOW_PLUS_10_YEARS, 0);
assertEquals("Context should be the one specified", getContext(), spec.getContext());
assertEquals("Alias should be the one specified", TEST_ALIAS_1, spec.getKeystoreAlias());
+ assertEquals("Key algorithm should be the one specified", "RSA", spec.getKeyType());
+
+ assertEquals("Key size should be the one specified", 1024, spec.getKeySize());
+
assertEquals("subjectDN should be the one specified", TEST_DN_1, spec.getSubjectDN());
assertEquals("startDate should be the one specified", NOW, spec.getStartDate());
@@ -57,6 +61,8 @@
public void testBuilder_Success() throws Exception {
KeyPairGeneratorSpec spec = new KeyPairGeneratorSpec.Builder(getContext())
.setAlias(TEST_ALIAS_1)
+ .setKeyType("RSA")
+ .setKeySize(1024)
.setSubject(TEST_DN_1)
.setSerialNumber(SERIAL_1)
.setStartDate(NOW)
@@ -68,6 +74,10 @@
assertEquals("Alias should be the one specified", TEST_ALIAS_1, spec.getKeystoreAlias());
+ assertEquals("Key algorithm should be the one specified", "RSA", spec.getKeyType());
+
+ assertEquals("Key size should be the one specified", 1024, spec.getKeySize());
+
assertEquals("subjectDN should be the one specified", TEST_DN_1, spec.getSubjectDN());
assertEquals("startDate should be the one specified", NOW, spec.getStartDate());
@@ -79,7 +89,7 @@
public void testConstructor_NullContext_Failure() throws Exception {
try {
- new KeyPairGeneratorSpec(null, TEST_ALIAS_1, TEST_DN_1, SERIAL_1, NOW,
+ new KeyPairGeneratorSpec(null, TEST_ALIAS_1, "RSA", 1024, null, TEST_DN_1, SERIAL_1, NOW,
NOW_PLUS_10_YEARS, 0);
fail("Should throw IllegalArgumentException when context is null");
} catch (IllegalArgumentException success) {
@@ -88,7 +98,7 @@
public void testConstructor_NullKeystoreAlias_Failure() throws Exception {
try {
- new KeyPairGeneratorSpec(getContext(), null, TEST_DN_1, SERIAL_1, NOW,
+ new KeyPairGeneratorSpec(getContext(), null, "RSA", 1024, null, TEST_DN_1, SERIAL_1, NOW,
NOW_PLUS_10_YEARS, 0);
fail("Should throw IllegalArgumentException when keystoreAlias is null");
} catch (IllegalArgumentException success) {
@@ -97,7 +107,7 @@
public void testConstructor_NullSubjectDN_Failure() throws Exception {
try {
- new KeyPairGeneratorSpec(getContext(), TEST_ALIAS_1, null, SERIAL_1, NOW,
+ new KeyPairGeneratorSpec(getContext(), TEST_ALIAS_1, "RSA", 1024, null, null, SERIAL_1, NOW,
NOW_PLUS_10_YEARS, 0);
fail("Should throw IllegalArgumentException when subjectDN is null");
} catch (IllegalArgumentException success) {
@@ -106,7 +116,7 @@
public void testConstructor_NullSerial_Failure() throws Exception {
try {
- new KeyPairGeneratorSpec(getContext(), TEST_ALIAS_1, TEST_DN_1, null, NOW,
+ new KeyPairGeneratorSpec(getContext(), TEST_ALIAS_1, "RSA", 1024, null, TEST_DN_1, null, NOW,
NOW_PLUS_10_YEARS, 0);
fail("Should throw IllegalArgumentException when startDate is null");
} catch (IllegalArgumentException success) {
@@ -115,8 +125,8 @@
public void testConstructor_NullStartDate_Failure() throws Exception {
try {
- new KeyPairGeneratorSpec(getContext(), TEST_ALIAS_1, TEST_DN_1, SERIAL_1, null,
- NOW_PLUS_10_YEARS, 0);
+ new KeyPairGeneratorSpec(getContext(), TEST_ALIAS_1, "RSA", 1024, null, TEST_DN_1, SERIAL_1,
+ null, NOW_PLUS_10_YEARS, 0);
fail("Should throw IllegalArgumentException when startDate is null");
} catch (IllegalArgumentException success) {
}
@@ -124,8 +134,8 @@
public void testConstructor_NullEndDate_Failure() throws Exception {
try {
- new KeyPairGeneratorSpec(getContext(), TEST_ALIAS_1, TEST_DN_1, SERIAL_1, NOW,
- null, 0);
+ new KeyPairGeneratorSpec(getContext(), TEST_ALIAS_1, "RSA", 1024, null, TEST_DN_1, SERIAL_1,
+ NOW, null, 0);
fail("Should throw IllegalArgumentException when keystoreAlias is null");
} catch (IllegalArgumentException success) {
}
@@ -133,7 +143,7 @@
public void testConstructor_EndBeforeStart_Failure() throws Exception {
try {
- new KeyPairGeneratorSpec(getContext(), TEST_ALIAS_1, TEST_DN_1, SERIAL_1,
+ new KeyPairGeneratorSpec(getContext(), TEST_ALIAS_1, "RSA", 1024, null, TEST_DN_1, SERIAL_1,
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/KeyStoreTest.java b/keystore/tests/src/android/security/KeyStoreTest.java
index 9bf88d3..7a142cc 100644
--- a/keystore/tests/src/android/security/KeyStoreTest.java
+++ b/keystore/tests/src/android/security/KeyStoreTest.java
@@ -22,6 +22,7 @@
import android.test.ActivityUnitTestCase;
import android.test.AssertionFailedError;
import android.test.suitebuilder.annotation.MediumTest;
+import com.android.org.conscrypt.NativeCrypto;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Date;
@@ -347,21 +348,24 @@
public void testGenerate_NotInitialized_Fail() throws Exception {
assertFalse("Should fail when keystore is not initialized",
- mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+ mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, NativeCrypto.EVP_PKEY_RSA,
+ 1024, KeyStore.FLAG_ENCRYPTED, null));
}
public void testGenerate_Locked_Fail() throws Exception {
mKeyStore.password(TEST_PASSWD);
mKeyStore.lock();
assertFalse("Should fail when keystore is locked",
- mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+ mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, NativeCrypto.EVP_PKEY_RSA,
+ 1024, KeyStore.FLAG_ENCRYPTED, null));
}
public void testGenerate_Success() throws Exception {
assertTrue(mKeyStore.password(TEST_PASSWD));
assertTrue("Should be able to generate key when unlocked",
- mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+ mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, NativeCrypto.EVP_PKEY_RSA,
+ 1024, KeyStore.FLAG_ENCRYPTED, null));
assertTrue(mKeyStore.contains(TEST_KEYNAME));
assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.WIFI_UID));
}
@@ -370,7 +374,8 @@
assertTrue(mKeyStore.password(TEST_PASSWD));
assertTrue("Should be able to generate key when unlocked",
- mKeyStore.generate(TEST_KEYNAME, Process.WIFI_UID, KeyStore.FLAG_ENCRYPTED));
+ mKeyStore.generate(TEST_KEYNAME, Process.WIFI_UID, NativeCrypto.EVP_PKEY_RSA,
+ 1024, KeyStore.FLAG_ENCRYPTED, null));
assertTrue(mKeyStore.contains(TEST_KEYNAME, Process.WIFI_UID));
assertFalse(mKeyStore.contains(TEST_KEYNAME));
}
@@ -378,7 +383,8 @@
public void testGenerate_ungrantedUid_Bluetooth_Failure() throws Exception {
assertTrue(mKeyStore.password(TEST_PASSWD));
- assertFalse(mKeyStore.generate(TEST_KEYNAME, Process.BLUETOOTH_UID, KeyStore.FLAG_ENCRYPTED));
+ assertFalse(mKeyStore.generate(TEST_KEYNAME, Process.BLUETOOTH_UID,
+ NativeCrypto.EVP_PKEY_RSA, 1024, KeyStore.FLAG_ENCRYPTED, null));
assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.BLUETOOTH_UID));
assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.WIFI_UID));
assertFalse(mKeyStore.contains(TEST_KEYNAME));
@@ -424,7 +430,8 @@
public void testSign_Success() throws Exception {
mKeyStore.password(TEST_PASSWD);
- assertTrue(mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+ assertTrue(mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, NativeCrypto.EVP_PKEY_RSA,
+ 1024, KeyStore.FLAG_ENCRYPTED, null));
assertTrue(mKeyStore.contains(TEST_KEYNAME));
final byte[] signature = mKeyStore.sign(TEST_KEYNAME, TEST_DATA);
@@ -434,7 +441,8 @@
public void testVerify_Success() throws Exception {
mKeyStore.password(TEST_PASSWD);
- assertTrue(mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+ assertTrue(mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, NativeCrypto.EVP_PKEY_RSA,
+ 1024, KeyStore.FLAG_ENCRYPTED, null));
assertTrue(mKeyStore.contains(TEST_KEYNAME));
final byte[] signature = mKeyStore.sign(TEST_KEYNAME, TEST_DATA);
@@ -461,7 +469,8 @@
mKeyStore.password(TEST_PASSWD));
assertTrue("Should be able to generate key for testcase",
- mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+ mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, NativeCrypto.EVP_PKEY_RSA,
+ 1024, KeyStore.FLAG_ENCRYPTED, null));
assertTrue("Should be able to grant key to other user",
mKeyStore.grant(TEST_KEYNAME, 0));
@@ -494,7 +503,8 @@
mKeyStore.password(TEST_PASSWD));
assertTrue("Should be able to generate key for testcase",
- mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+ mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, NativeCrypto.EVP_PKEY_RSA,
+ 1024, KeyStore.FLAG_ENCRYPTED, null));
assertTrue("Should be able to grant key to other user",
mKeyStore.grant(TEST_KEYNAME, 0));
@@ -527,7 +537,8 @@
mKeyStore.password(TEST_PASSWD));
assertTrue("Should be able to generate key for testcase",
- mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+ mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, NativeCrypto.EVP_PKEY_RSA,
+ 1024, KeyStore.FLAG_ENCRYPTED, null));
assertFalse("Should not be able to revoke not existent grant",
mKeyStore.ungrant(TEST_KEYNAME, 0));
@@ -538,7 +549,8 @@
mKeyStore.password(TEST_PASSWD));
assertTrue("Should be able to generate key for testcase",
- mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+ mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, NativeCrypto.EVP_PKEY_RSA,
+ 1024, KeyStore.FLAG_ENCRYPTED, null));
assertTrue("Should be able to grant key to other user",
mKeyStore.grant(TEST_KEYNAME, 0));
@@ -555,7 +567,8 @@
mKeyStore.password(TEST_PASSWD));
assertTrue("Should be able to generate key for testcase",
- mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+ mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, NativeCrypto.EVP_PKEY_RSA,
+ 1024, KeyStore.FLAG_ENCRYPTED, null));
assertTrue("Should be able to grant key to other user",
mKeyStore.grant(TEST_KEYNAME, 0));
@@ -575,7 +588,8 @@
assertFalse(mKeyStore.contains(TEST_KEYNAME));
- assertTrue(mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+ assertTrue(mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, NativeCrypto.EVP_PKEY_RSA,
+ 1024, KeyStore.FLAG_ENCRYPTED, null));
assertTrue(mKeyStore.contains(TEST_KEYNAME));
assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.WIFI_UID));
@@ -613,7 +627,8 @@
assertFalse(mKeyStore.contains(TEST_KEYNAME));
- assertTrue(mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+ assertTrue(mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, NativeCrypto.EVP_PKEY_RSA,
+ 1024, KeyStore.FLAG_ENCRYPTED, null));
assertTrue(mKeyStore.contains(TEST_KEYNAME));
assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.BLUETOOTH_UID));
diff --git a/location/java/android/location/SettingInjectorService.java b/location/java/android/location/SettingInjectorService.java
index 0b0f05d..8181f4e 100644
--- a/location/java/android/location/SettingInjectorService.java
+++ b/location/java/android/location/SettingInjectorService.java
@@ -22,12 +22,11 @@
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
-import android.preference.Preference;
import android.util.Log;
/**
- * Dynamically specifies the summary (subtile) and enabled status of a preference injected into
- * the "Settings > Location > Location services" list.
+ * Dynamically specifies the summary (subtitle) and enabled status of a preference injected into
+ * the list of location services displayed by the system settings app.
*
* The location services list is intended for use only by preferences that affect multiple apps from
* the same developer. Location settings that apply only to one app should be shown within that app,
@@ -39,52 +38,47 @@
* <pre>
* <service android:name="com.example.android.injector.MyInjectorService" >
* <intent-filter>
- * <action android:name="com.android.settings.InjectedLocationSetting" />
+ * <action android:name="android.location.SettingInjectorService" />
* </intent-filter>
*
* <meta-data
- * android:name="com.android.settings.InjectedLocationSetting"
+ * android:name="android.location.SettingInjectorService"
* android:resource="@xml/my_injected_location_setting" />
* </service>
* </pre>
* The resource file specifies the static data for the setting:
* <pre>
* <injected-location-setting xmlns:android="http://schemas.android.com/apk/res/android"
- * android:label="@string/injected_setting_label"
- * android:icon="@drawable/ic_launcher"
+ * android:title="@string/injected_setting_title"
+ * android:icon="@drawable/ic_acme_corp"
* android:settingsActivity="com.example.android.injector.MySettingActivity"
* />
* </pre>
* Here:
* <ul>
- * <li>label: The {@link Preference#getTitle()} value. The title should make it clear which apps
- * are affected by the setting, typically by including the name of the developer. For example,
- * "Acme Corp. ads preferences." </li>
+ * <li>title: The {@link android.preference.Preference#getTitle()} value. The title should make
+ * it clear which apps are affected by the setting, typically by including the name of the
+ * developer. For example, "Acme Corp. ads preferences." </li>
*
- * <li>icon: The {@link Preference#getIcon()} value. Typically this will be a generic icon for
- * the developer rather than the icon for an individual app.</li>
+ * <li>icon: The {@link android.preference.Preference#getIcon()} value. Typically this will be a
+ * generic icon for the developer rather than the icon for an individual app.</li>
*
* <li>settingsActivity: the activity which is launched to allow the user to modify the setting
- * value The activity must be in the same package as the subclass of
+ * value. The activity must be in the same package as the subclass of
* {@link SettingInjectorService}. The activity should use your own branding to help emphasize
* to the user that it is not part of the system settings.</li>
* </ul>
*
- * To ensure a good user experience, your {@link #onHandleIntent(Intent)} must complete within
- * 200 msec even if your app is not already running. This means that both
- * {@link android.app.Application#onCreate()} and {@link #getStatus()} must be fast. If you exceed
- * this time, then this can delay the retrieval of settings status for other apps as well.
- *
- * For consistency, the label and {@link #getStatus()} values should be provided in all of the
- * locales supported by the system settings app. The text should not contain offensive language.
+ * To ensure a good user experience, the average time from the start of
+ * {@link #startService(Intent)} to the end of {@link #onHandleIntent(Intent)} should be less
+ * than 300 msec even if your app is not already in memory. This means that both your
+ * {@link android.app.Application#onCreate()} and your {@link #getStatus()} must be fast. If
+ * either is slow, it can delay the display of settings values for other apps as well.
*
* For compactness, only one copy of a given setting should be injected. If each account has a
* distinct value for the setting, then the {@link #getStatus()} value should represent a summary of
* the state across all of the accounts and {@code settingsActivity} should display the value for
* each account.
- *
- * Apps that violate these guidelines will be taken down from the Google Play Store and/or flagged
- * as malware.
*/
// TODO: is there a public list of supported locales?
// TODO: is there a public list of guidelines for settings text?
@@ -94,6 +88,30 @@
private static final String TAG = "SettingInjectorService";
/**
+ * Intent action that must be declared in the manifest for the subclass. Used to start the
+ * service to read the dynamic status for the setting.
+ */
+ public static final String ACTION_SERVICE_INTENT = "android.location.SettingInjectorService";
+
+ /**
+ * Name of the meta-data tag used to specify the resource file that includes the settings
+ * attributes.
+ */
+ public static final String META_DATA_NAME = "android.location.SettingInjectorService";
+
+ /**
+ * Name of the XML tag that includes the attributes for the setting.
+ */
+ public static final String ATTRIBUTES_NAME = "injected-location-setting";
+
+ /**
+ * Intent action a client should broadcast when the value of one of its injected settings has
+ * changed, so that the setting can be updated in the UI.
+ */
+ public static final String ACTION_INJECTED_SETTING_CHANGED =
+ "android.location.InjectedSettingChanged";
+
+ /**
* Name of the bundle key for the string specifying the summary for the setting (e.g., "ON" or
* "OFF").
*
@@ -115,13 +133,6 @@
*/
public static final String MESSENGER_KEY = "messenger";
- /**
- * Intent action a client should broadcast when the value of one of its injected settings has
- * changed, so that the setting can be updated in the UI.
- */
- public static final String ACTION_INJECTED_SETTING_CHANGED =
- "com.android.location.InjectedSettingChanged";
-
private final String mName;
/**
@@ -169,7 +180,10 @@
}
/**
- * Reads the status of the setting.
+ * Reads the status of the setting. Should not perform unpredictably-long operations such as
+ * network access--see the running-time comments in the class-level javadoc.
+ *
+ * @return the status of the setting value
*/
protected abstract Status getStatus();
@@ -179,12 +193,16 @@
public static final class Status {
/**
- * The {@link Preference#getSummary()} value
+ * The {@link android.preference.Preference#getSummary()} value.
+ *
+ * @hide
*/
public final String summary;
/**
- * The {@link Preference#isEnabled()} value
+ * The {@link android.preference.Preference#isEnabled()} value.
+ *
+ * @hide
*/
public final boolean enabled;
@@ -193,9 +211,8 @@
* <p/>
* Note that to prevent churn in the settings list, there is no support for dynamically
* choosing to hide a setting. Instead you should provide a {@code enabled} value of false,
- * which will gray the setting out and disable the link from "Settings > Location"
- * to your setting activity. One reason why you might choose to do this is if
- * {@link android.provider.Settings.Secure#LOCATION_MODE}
+ * which will disable the setting and its link to your setting activity. One reason why you
+ * might choose to do this is if {@link android.provider.Settings.Secure#LOCATION_MODE}
* is {@link android.provider.Settings.Secure#LOCATION_MODE_OFF}.
*
* It is possible that the user may click on the setting before you return a false value for
@@ -203,8 +220,9 @@
* though the setting is disabled. The simplest approach may be to simply call
* {@link android.app.Activity#finish()} when disabled.
*
- * @param summary the {@link Preference#getSummary()} value (allowed to be null or empty)
- * @param enabled the {@link Preference#isEnabled()} value
+ * @param summary the {@link android.preference.Preference#getSummary()} value (allowed to
+ * be null or empty)
+ * @param enabled the {@link android.preference.Preference#isEnabled()} value
*/
public Status(String summary, boolean enabled) {
this.summary = summary;
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 946dd71..1b9bdaf 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -2382,6 +2382,16 @@
*/
public static final int MEDIA_INFO_TIMED_TEXT_ERROR = 900;
+ /** Subtitle track was not supported by the media framework.
+ * @see android.media.MediaPlayer.OnInfoListener
+ */
+ public static final int MEDIA_INFO_UNSUPPORTED_SUBTITLE = 901;
+
+ /** Reading the subtitle track takes too long.
+ * @see android.media.MediaPlayer.OnInfoListener
+ */
+ public static final int MEDIA_INFO_SUBTITLE_TIMED_OUT = 902;
+
/**
* Interface definition of a callback to be invoked to communicate some
* info and/or warning about the media or its playback.
@@ -2402,6 +2412,8 @@
* <li>{@link #MEDIA_INFO_BAD_INTERLEAVING}
* <li>{@link #MEDIA_INFO_NOT_SEEKABLE}
* <li>{@link #MEDIA_INFO_METADATA_UPDATE}
+ * <li>{@link #MEDIA_INFO_UNSUPPORTED_SUBTITLE}
+ * <li>{@link #MEDIA_INFO_SUBTITLE_TIMED_OUT}
* </ul>
* @param extra an extra code, specific to the info. Typically
* implementation dependent.
diff --git a/media/java/android/media/MediaTimeProvider.java b/media/java/android/media/MediaTimeProvider.java
new file mode 100644
index 0000000..c1fda81
--- /dev/null
+++ b/media/java/android/media/MediaTimeProvider.java
@@ -0,0 +1,90 @@
+/*
+ * 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.media;
+
+/** @hide */
+public interface MediaTimeProvider {
+ // we do not allow negative media time
+ /**
+ * Presentation time value if no timed event notification is requested.
+ */
+ public final static long NO_TIME = -1;
+
+ /**
+ * Cancels all previous notification request from this listener if any. It
+ * registers the listener to get seek and stop notifications. If timeUs is
+ * not negative, it also registers the listener for a timed event
+ * notification when the presentation time reaches (becomes greater) than
+ * the value specified. This happens immediately if the current media time
+ * is larger than or equal to timeUs.
+ *
+ * @param timeUs presentation time to get timed event callback at (or
+ * {@link #NO_TIME})
+ */
+ public void notifyAt(long timeUs, OnMediaTimeListener listener);
+
+ /**
+ * Cancels all previous notification request from this listener if any. It
+ * registers the listener to get seek and stop notifications. If the media
+ * is stopped, the listener will immediately receive a stop notification.
+ * Otherwise, it will receive a timed event notificaton.
+ */
+ public void scheduleUpdate(OnMediaTimeListener listener);
+
+ /**
+ * Cancels all previous notification request from this listener if any.
+ */
+ public void cancelNotifications(OnMediaTimeListener listener);
+
+ /**
+ * Get the current presentation time.
+ *
+ * @param precise Whether getting a precise time is important. This is
+ * more costly.
+ * @param monotonic Whether returned time should be monotonic: that is,
+ * greater than or equal to the last returned time. Don't
+ * always set this to true. E.g. this has undesired
+ * consequences if the media is seeked between calls.
+ * @throw IllegalStateException if the media is not initialized
+ */
+ public long getCurrentTimeUs(boolean precise, boolean monotonic)
+ throws IllegalStateException;
+
+ /** @hide */
+ public static interface OnMediaTimeListener {
+ /**
+ * Called when the registered time was reached naturally.
+ *
+ * @param timeUs current media time
+ */
+ void onTimedEvent(long timeUs);
+
+ /**
+ * Called when the media time changed due to seeking.
+ *
+ * @param timeUs current media time
+ */
+ void onSeek(long timeUs);
+
+ /**
+ * Called when the playback stopped. This is not called on pause, only
+ * on full stop, at which point there is no further current media time.
+ */
+ void onStop();
+ }
+}
+
diff --git a/media/java/android/media/RemoteControlClient.java b/media/java/android/media/RemoteControlClient.java
index 4dcac31..58f5d55 100644
--- a/media/java/android/media/RemoteControlClient.java
+++ b/media/java/android/media/RemoteControlClient.java
@@ -294,14 +294,13 @@
*/
public final static int FLAG_KEY_MEDIA_POSITION_UPDATE = 1 << 8;
/**
- * @hide
- * CANDIDATE FOR PUBLIC API
* Flag indicating a RemoteControlClient supports ratings.
* This flag must be set in order for components that display the RemoteControlClient
* information, to display ratings information, and, if ratings are declared editable
* (by calling {@link MetadataEditor#addEditableKey(int)} with the
* {@link MetadataEditor#LONG_KEY_RATING_BY_USER} key), it will enable the user to rate
- * the media.
+ * the media, with values being received through the interface set with
+ * {@link #setMetadataUpdateListener(OnMetadataUpdateListener)}.
* @see #setTransportControlFlags(int)
*/
public final static int FLAG_KEY_MEDIA_RATING = 1 << 9;
@@ -452,8 +451,6 @@
*/
public final static int BITMAP_KEY_ARTWORK = 100;
/**
- * @hide
- * CANDIDATE FOR PUBLIC API
* The metadata key qualifying the content rating.
* The value associated with this key may be: {@link #RATING_HEART},
* {@link #RATING_THUMB_UP_DOWN}, or a non-null positive integer expressing a maximum
@@ -461,8 +458,6 @@
*/
public final static int LONG_KEY_RATING_TYPE = 101;
/**
- * @hide
- * CANDIDATE FOR PUBLIC API
* The metadata key for the content's average rating, not the user's rating.
* The value associated with this key may be: an integer value between 0 and 100,
* or {@link #RATING_NOT_RATED} to express that no average rating is available.
@@ -472,11 +467,12 @@
* <p></p>
* When the rating type is:
* <ul>
- * <li>{@link #RATING_HEART}, a rating of 50 to 100 means "heart selected",</li>
- * <li>{@link #RATING_THUMB_UP_DOWN}, a rating of 0 to 49 means "thumb down", 50 means
- * both "thumb up" and "thumb down" selected, 51 to 100 means "thumb up"</li>
+ * <li>{@link #RATING_HEART}, a rating of 51 to 100 means "heart selected",</li>
+ * <li>{@link #RATING_THUMB_UP_DOWN}, a rating of 0 to 50 means "thumb down",
+ * 51 to 100 means "thumb up"</li>
* <li>a non-null positive integer, the rating value is mapped to the number of stars, e.g.
- * with a maximum of 5 stars, a rating of 21 to 40 maps to 2 stars.</li>
+ * with a maximum of 5 stars, a rating of 0 maps to 0 stars, 1 to 20 maps to 1 star,
+ * 21 to 40 maps to 2 stars, etc.</li>
* </ul>
* @see #LONG_KEY_RATING_BY_USER
*/
@@ -489,34 +485,29 @@
*/
public final static int KEY_EDITABLE_MASK = 0x1FFFFFFF;
/**
- * @hide
- * CANDIDATE FOR PUBLIC API
- * The metadata key for the content's rating by the user.
+ * The metadata key for the content's user rating.
* The value associated with this key may be: an integer value between 0 and 100,
* or {@link #RATING_NOT_RATED} to express that the user hasn't rated this content.
* Rules for the interpretation of the rating value according to the rating style are
- * the same as for {@link #LONG_KEY_RATING_BY_OTHERS}
+ * the same as for {@link #LONG_KEY_RATING_BY_OTHERS}.
+ * This key can be flagged as "editable" (with {@link #addEditableKey(int)}) to enable
+ * receiving user rating values through the
+ * {@link android.media.RemoteControlClient.OnMetadataUpdateListener} interface.
*/
public final static int LONG_KEY_RATING_BY_USER = 0x10000001;
/**
- * @hide
- * CANDIDATE FOR PUBLIC API
* A rating style with a single degree of rating, "heart" vs "no heart". Can be used to
* indicate the content referred to is a favorite (or not).
* @see #LONG_KEY_RATING_TYPE
*/
public final static long RATING_HEART = -1;
/**
- * @hide
- * CANDIDATE FOR PUBLIC API
* A rating style for "thumb up" vs "thumb down".
* @see #LONG_KEY_RATING_TYPE
*/
public final static long RATING_THUMB_UP_DOWN = -2;
/**
- * @hide
- * CANDIDATE FOR PUBLIC API
* A rating value indicating no rating is available.
* @see #LONG_KEY_RATING_BY_OTHERS
* @see #LONG_KEY_RATING_BY_USER
@@ -573,7 +564,9 @@
* {@link android.media.MediaMetadataRetriever#METADATA_KEY_DISC_NUMBER},
* {@link android.media.MediaMetadataRetriever#METADATA_KEY_DURATION} (with a value
* expressed in milliseconds),
- * {@link android.media.MediaMetadataRetriever#METADATA_KEY_YEAR}.
+ * {@link android.media.MediaMetadataRetriever#METADATA_KEY_YEAR},
+ * {@link #LONG_KEY_RATING_BY_OTHERS}, {@link #LONG_KEY_RATING_BY_USER},
+ * {@link #LONG_KEY_RATING_TYPE}.
* @param value The long value for the given key
* @return Returns a reference to the same MetadataEditor object, so you can chain put
* calls together.
@@ -620,8 +613,9 @@
/**
* Clears all the metadata that has been set since the MetadataEditor instance was
* created with {@link RemoteControlClient#editMetadata(boolean)}.
+ * Note that clearing the metadata doesn't reset the editable keys
+ * (use {@link #clearEditableKeys()} instead).
*/
- // TODO add in javadoc that this doesn't call clearEditableKeys()
public synchronized void clear() {
if (mApplied) {
Log.e(TAG, "Can't clear a previously applied MetadataEditor");
@@ -632,8 +626,11 @@
}
/**
- * @hide
- * CANDIDATE FOR PUBLIC API
+ * Flag the given key as being editable.
+ * This will declare the metadata field as eligible to be updated, with new values
+ * received through the {@link RemoteControlClient.OnMetadataUpdateListener} interface.
+ * @param key the type of metadata that can be edited. The supported key is
+ * {@link #LONG_KEY_RATING_BY_USER}.
*/
public synchronized void addEditableKey(int key) {
if (mApplied) {
@@ -651,8 +648,7 @@
}
/**
- * @hide
- * CANDIDATE FOR PUBLIC API
+ * Causes all metadata fields to be read-only.
*/
public synchronized void clearEditableKeys() {
if (mApplied) {
@@ -869,7 +865,8 @@
* {@link #FLAG_KEY_MEDIA_STOP},
* {@link #FLAG_KEY_MEDIA_FAST_FORWARD},
* {@link #FLAG_KEY_MEDIA_NEXT},
- * {@link #FLAG_KEY_MEDIA_POSITION_UPDATE}
+ * {@link #FLAG_KEY_MEDIA_POSITION_UPDATE},
+ * {@link #FLAG_KEY_MEDIA_RATING}.
*/
public void setTransportControlFlags(int transportControlFlags) {
synchronized(mCacheLock) {
@@ -882,36 +879,39 @@
}
/**
- * @hide
- * CANDIDATE FOR PUBLIC API
- * TODO ADD DESCRIPTION
+ * Interface definition for a callback to be invoked when one of the metadata values has
+ * been updated.
*/
public interface OnMetadataUpdateListener {
/**
- * TODO ADD DESCRIPTION
- * @param key
- * @param newValue
+ * Called on the implementer to notify that the metadata field for the given key has
+ * been updated to the new value of type <code>long</long>.
+ * @param key the identifier of the updated metadata field of type <code>long</long>.
+ * @param newValue the new <code>long</long> value for the key
*/
void onMetadataUpdateLong(int key, long newValue);
/**
- * TODO ADD DESCRIPTION
- * @param key
- * @param newValue
+ * Called on the implementer to notify that the metadata field for the given key has
+ * been updated to the new <code>String</long>.
+ * @param key the identifier of the updated metadata field of type <code>String</long>.
+ * @param newValue the new <code>String</long> value for the key
*/
void onMetadataUpdateString(int key, String newValue);
/**
- * TODO ADD DESCRIPTION
- * @param key
- * @param newValue
+ * Called on the implementer to notify that the metadata field for the given key has
+ * been updated to the new {@link android.graphics.Bitmap}.
+ * @param key the identifier of the updated metadata field of type
+ * {@link android.graphics.Bitmap}.
+ * @param newValue the new {@link android.graphics.Bitmap} for the key
*/
void onMetadataUpdateBitmap(int key, Bitmap newValue);
}
/**
- * @hide
- * CANDIDATE FOR PUBLIC API
- * TODO ADD DESCRIPTION
- * @param l
+ * Sets the listener to be called whenever the metadata is updated.
+ * New metadata values will be received in the same thread as the one in which
+ * RemoteControlClient was created.
+ * @param l the metadata update listener
*/
public void setMetadataUpdateListener(OnMetadataUpdateListener l) {
synchronized(mCacheLock) {
diff --git a/media/java/android/media/SubtitleController.java b/media/java/android/media/SubtitleController.java
new file mode 100644
index 0000000..edd569c
--- /dev/null
+++ b/media/java/android/media/SubtitleController.java
@@ -0,0 +1,309 @@
+/*
+ * 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.media;
+
+import java.util.Locale;
+import java.util.Vector;
+
+import android.content.Context;
+import android.media.MediaPlayer.OnSubtitleDataListener;
+import android.view.View;
+import android.view.accessibility.CaptioningManager;
+
+/**
+ * The subtitle controller provides the architecture to display subtitles for a
+ * media source. It allows specifying which tracks to display, on which anchor
+ * to display them, and also allows adding external, out-of-band subtitle tracks.
+ *
+ * @hide
+ */
+public class SubtitleController {
+ private Context mContext;
+ private MediaTimeProvider mTimeProvider;
+ private Vector<Renderer> mRenderers;
+ private Vector<SubtitleTrack> mTracks;
+ private SubtitleTrack mSelectedTrack;
+ private boolean mShowing;
+
+ /**
+ * Creates a subtitle controller for a media playback object that implements
+ * the MediaTimeProvider interface.
+ *
+ * @param timeProvider
+ */
+ public SubtitleController(
+ Context context,
+ MediaTimeProvider timeProvider,
+ Listener listener) {
+ mContext = context;
+ mTimeProvider = timeProvider;
+ mListener = listener;
+
+ mRenderers = new Vector<Renderer>();
+ mShowing = false;
+ mTracks = new Vector<SubtitleTrack>();
+ }
+
+ /**
+ * @return the available subtitle tracks for this media. These include
+ * the tracks found by {@link MediaPlayer} as well as any tracks added
+ * manually via {@link #addTrack}.
+ */
+ public SubtitleTrack[] getTracks() {
+ SubtitleTrack[] tracks = new SubtitleTrack[mTracks.size()];
+ mTracks.toArray(tracks);
+ return tracks;
+ }
+
+ /**
+ * @return the currently selected subtitle track
+ */
+ public SubtitleTrack getSelectedTrack() {
+ return mSelectedTrack;
+ }
+
+ private View getSubtitleView() {
+ if (mSelectedTrack == null) {
+ return null;
+ }
+ return mSelectedTrack.getView();
+ }
+
+ /**
+ * Selects a subtitle track. As a result, this track will receive
+ * in-band data from the {@link MediaPlayer}. However, this does
+ * not change the subtitle visibility.
+ *
+ * @param track The subtitle track to select. This must be one of the
+ * tracks in {@link #getTracks}.
+ * @return true if the track was successfully selected.
+ */
+ public boolean selectTrack(SubtitleTrack track) {
+ if (track != null && !mTracks.contains(track)) {
+ return false;
+ }
+ mTrackIsExplicit = true;
+ if (mSelectedTrack == track) {
+ return true;
+ }
+
+ if (mSelectedTrack != null) {
+ mSelectedTrack.hide();
+ mSelectedTrack.setTimeProvider(null);
+ }
+
+ mSelectedTrack = track;
+ mAnchor.setSubtitleView(getSubtitleView());
+
+ if (mSelectedTrack != null) {
+ mSelectedTrack.setTimeProvider(mTimeProvider);
+ mSelectedTrack.show();
+ }
+
+ if (mListener != null) {
+ mListener.onSubtitleTrackSelected(track);
+ }
+ return true;
+ }
+
+ /**
+ * @return the default subtitle track based on system preferences, or null,
+ * if no such track exists in this manager.
+ */
+ public SubtitleTrack getDefaultTrack() {
+ Locale locale = CaptioningManager.getLocale(mContext.getContentResolver());
+
+ for (SubtitleTrack track: mTracks) {
+ MediaFormat format = track.getFormat();
+ String language = format.getString(MediaFormat.KEY_LANGUAGE);
+ // TODO: select track with best renderer. For now, we select first
+ // track with local's language or first track if locale has none
+ if (locale == null ||
+ locale.getLanguage().equals("") ||
+ locale.getISO3Language().equals(language) ||
+ locale.getLanguage().equals(language)) {
+ return track;
+ }
+ }
+ return null;
+ }
+
+ private boolean mTrackIsExplicit = false;
+ private boolean mVisibilityIsExplicit = false;
+
+ /** @hide */
+ public void selectDefaultTrack() {
+ if (mTrackIsExplicit) {
+ return;
+ }
+
+ SubtitleTrack track = getDefaultTrack();
+ if (track != null) {
+ selectTrack(track);
+ mTrackIsExplicit = false;
+ if (!mVisibilityIsExplicit) {
+ if (CaptioningManager.isEnabled(mContext.getContentResolver())) {
+ show();
+ } else {
+ hide();
+ }
+ mVisibilityIsExplicit = false;
+ }
+ }
+ }
+
+ /** @hide */
+ public void reset() {
+ hide();
+ selectTrack(null);
+ mTracks.clear();
+ mTrackIsExplicit = false;
+ mVisibilityIsExplicit = false;
+ }
+
+ /**
+ * Adds a new, external subtitle track to the manager.
+ *
+ * @param format the format of the track that will include at least
+ * the MIME type {@link MediaFormat@KEY_MIME}.
+ * @return the created {@link SubtitleTrack} object
+ */
+ public SubtitleTrack addTrack(MediaFormat format) {
+ for (Renderer renderer: mRenderers) {
+ if (renderer.supports(format)) {
+ SubtitleTrack track = renderer.createTrack(format);
+ if (track != null) {
+ mTracks.add(track);
+ return track;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Show the selected (or default) subtitle track.
+ */
+ public void show() {
+ mShowing = true;
+ mVisibilityIsExplicit = true;
+ if (mSelectedTrack != null) {
+ mSelectedTrack.show();
+ }
+ }
+
+ /**
+ * Hide the selected (or default) subtitle track.
+ */
+ public void hide() {
+ mVisibilityIsExplicit = true;
+ if (mSelectedTrack != null) {
+ mSelectedTrack.hide();
+ }
+ mShowing = false;
+ }
+
+ /**
+ * Interface for supporting a single or multiple subtitle types in {@link
+ * MediaPlayer}.
+ */
+ public abstract static class Renderer {
+ /**
+ * Called by {@link MediaPlayer}'s {@link SubtitleController} when a new
+ * subtitle track is detected, to see if it should use this object to
+ * parse and display this subtitle track.
+ *
+ * @param format the format of the track that will include at least
+ * the MIME type {@link MediaFormat@KEY_MIME}.
+ *
+ * @return true if and only if the track format is supported by this
+ * renderer
+ */
+ public abstract boolean supports(MediaFormat format);
+
+ /**
+ * Called by {@link MediaPlayer}'s {@link SubtitleController} for each
+ * subtitle track that was detected and is supported by this object to
+ * create a {@link SubtitleTrack} object. This object will be created
+ * for each track that was found. If the track is selected for display,
+ * this object will be used to parse and display the track data.
+ *
+ * @param format the format of the track that will include at least
+ * the MIME type {@link MediaFormat@KEY_MIME}.
+ * @return a {@link SubtitleTrack} object that will be used to parse
+ * and render the subtitle track.
+ */
+ public abstract SubtitleTrack createTrack(MediaFormat format);
+ }
+
+ /**
+ * Add support for a subtitle format in {@link MediaPlayer}.
+ *
+ * @param renderer a {@link SubtitleController.Renderer} object that adds
+ * support for a subtitle format.
+ */
+ public void registerRenderer(Renderer renderer) {
+ // TODO how to get available renderers in the system
+ if (!mRenderers.contains(renderer)) {
+ // TODO should added renderers override existing ones (to allow replacing?)
+ mRenderers.add(renderer);
+ }
+ }
+
+ /**
+ * Subtitle anchor, an object that is able to display a subtitle view,
+ * e.g. a VideoView.
+ */
+ public interface Anchor {
+ /**
+ * Anchor should set the subtitle view to the supplied view,
+ * or none, if the supplied view is null.
+ *
+ * @param view subtitle view, or null
+ */
+ public void setSubtitleView(View view);
+ }
+
+ private Anchor mAnchor;
+
+ /** @hide */
+ public void setAnchor(Anchor anchor) {
+ if (mAnchor == anchor) {
+ return;
+ }
+
+ if (mAnchor != null) {
+ mAnchor.setSubtitleView(null);
+ }
+ mAnchor = anchor;
+ if (mAnchor != null) {
+ mAnchor.setSubtitleView(getSubtitleView());
+ }
+ }
+
+ public interface Listener {
+ /**
+ * Called when a subtitle track has been selected.
+ *
+ * @param track selected subtitle track or null
+ * @hide
+ */
+ public void onSubtitleTrackSelected(SubtitleTrack track);
+ }
+
+ private Listener mListener;
+}
diff --git a/media/java/android/media/SubtitleTrack.java b/media/java/android/media/SubtitleTrack.java
new file mode 100644
index 0000000..09fb3f2
--- /dev/null
+++ b/media/java/android/media/SubtitleTrack.java
@@ -0,0 +1,648 @@
+/*
+ * 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.media;
+
+import android.os.Handler;
+import android.util.Log;
+import android.util.LongSparseArray;
+import android.util.Pair;
+import android.view.View;
+
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.SortedMap;
+import java.util.TreeMap;
+import java.util.Vector;
+
+/**
+ * A subtitle track abstract base class that is responsible for parsing and displaying
+ * an instance of a particular type of subtitle.
+ *
+ * @hide
+ */
+public abstract class SubtitleTrack implements MediaTimeProvider.OnMediaTimeListener {
+ private static final String TAG = "SubtitleTrack";
+ private long mLastUpdateTimeMs;
+ private long mLastTimeMs;
+
+ private Runnable mRunnable;
+
+ /** @hide TODO private */
+ final protected LongSparseArray<Run> mRunsByEndTime = new LongSparseArray<Run>();
+ /** @hide TODO private */
+ final protected LongSparseArray<Run> mRunsByID = new LongSparseArray<Run>();
+
+ /** @hide TODO private */
+ protected CueList mCues;
+ /** @hide TODO private */
+ final protected Vector<Cue> mActiveCues = new Vector<Cue>();
+ /** @hide */
+ protected boolean mVisible;
+
+ /** @hide */
+ public boolean DEBUG = false;
+
+ /** @hide */
+ protected Handler mHandler = new Handler();
+
+ private MediaFormat mFormat;
+
+ public SubtitleTrack(MediaFormat format) {
+ mFormat = format;
+ mCues = new CueList();
+ clearActiveCues();
+ mLastTimeMs = -1;
+ }
+
+ /** @hide */
+ public MediaFormat getFormat() {
+ return mFormat;
+ }
+
+ private long mNextScheduledTimeMs = -1;
+
+ /**
+ * Called when there is input data for the subtitle track. The
+ * complete subtitle for a track can include multiple whole units
+ * (runs). Each of these units can have multiple sections. The
+ * contents of a run are submitted in sequential order, with eos
+ * indicating the last section of the run. Calls from different
+ * runs must not be intermixed.
+ *
+ * @param data
+ * @param eos true if this is the last section of the run.
+ * @param runID mostly-unique ID for this run of data. Subtitle cues
+ * with runID of 0 are discarded immediately after
+ * display. Cues with runID of ~0 are discarded
+ * only at the deletion of the track object. Cues
+ * with other runID-s are discarded at the end of the
+ * run, which defaults to the latest timestamp of
+ * any of its cues (with this runID).
+ *
+ * TODO use ByteBuffer
+ */
+ public abstract void onData(String data, boolean eos, long runID);
+
+ /**
+ * Called when adding the subtitle rendering view to the view hierarchy, as
+ * well as when showing or hiding the subtitle track, or when the video
+ * surface position has changed.
+ *
+ * @return the view object that displays this subtitle track. For most
+ * renderers there should be a single shared view instance that is used
+ * for all tracks supported by that renderer, as at most one subtitle
+ * track is visible at one time.
+ */
+ public abstract View getView();
+
+ /**
+ * Called when the active cues have changed, and the contents of the subtitle
+ * view should be updated.
+ *
+ * @hide
+ */
+ public abstract void updateView(Vector<Cue> activeCues);
+
+ /** @hide */
+ protected synchronized void updateActiveCues(boolean rebuild, long timeMs) {
+ // out-of-order times mean seeking or new active cues being added
+ // (during their own timespan)
+ if (rebuild || mLastUpdateTimeMs > timeMs) {
+ clearActiveCues();
+ }
+
+ for(Iterator<Pair<Long, Cue> > it =
+ mCues.entriesBetween(mLastUpdateTimeMs, timeMs).iterator(); it.hasNext(); ) {
+ Pair<Long, Cue> event = it.next();
+ Cue cue = event.second;
+
+ if (cue.mEndTimeMs == event.first) {
+ // remove past cues
+ if (DEBUG) Log.v(TAG, "Removing " + cue);
+ mActiveCues.remove(cue);
+ if (cue.mRunID == 0) {
+ it.remove();
+ }
+ } else if (cue.mStartTimeMs == event.first) {
+ // add new cues
+ // TRICKY: this will happen in start order
+ if (DEBUG) Log.v(TAG, "Adding " + cue);
+ if (cue.mInnerTimesMs != null) {
+ cue.onTime(timeMs);
+ }
+ mActiveCues.add(cue);
+ } else if (cue.mInnerTimesMs != null) {
+ // cue is modified
+ cue.onTime(timeMs);
+ }
+ }
+
+ /* complete any runs */
+ while (mRunsByEndTime.size() > 0 &&
+ mRunsByEndTime.keyAt(0) <= timeMs) {
+ removeRunsByEndTimeIndex(0); // removes element
+ }
+ mLastUpdateTimeMs = timeMs;
+ }
+
+ private void removeRunsByEndTimeIndex(int ix) {
+ Run run = mRunsByEndTime.valueAt(ix);
+ while (run != null) {
+ Cue cue = run.mFirstCue;
+ while (cue != null) {
+ mCues.remove(cue);
+ Cue nextCue = cue.mNextInRun;
+ cue.mNextInRun = null;
+ cue = nextCue;
+ }
+ mRunsByID.remove(run.mRunID);
+ Run nextRun = run.mNextRunAtEndTimeMs;
+ run.mPrevRunAtEndTimeMs = null;
+ run.mNextRunAtEndTimeMs = null;
+ run = nextRun;
+ }
+ mRunsByEndTime.removeAt(ix);
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ /* remove all cues (untangle all cross-links) */
+ int size = mRunsByEndTime.size();
+ for(int ix = size - 1; ix >= 0; ix--) {
+ removeRunsByEndTimeIndex(ix);
+ }
+
+ super.finalize();
+ }
+
+ private synchronized void takeTime(long timeMs) {
+ mLastTimeMs = timeMs;
+ }
+
+ /** @hide */
+ protected synchronized void clearActiveCues() {
+ if (DEBUG) Log.v(TAG, "Clearing " + mActiveCues.size() + " active cues");
+ mActiveCues.clear();
+ mLastUpdateTimeMs = -1;
+ }
+
+ /** @hide */
+ public void scheduleTimedEvents() {
+ /* get times for the next event */
+ if (mTimeProvider != null) {
+ mNextScheduledTimeMs = mCues.nextTimeAfter(mLastTimeMs);
+ if (DEBUG) Log.d(TAG, "sched @" + mNextScheduledTimeMs + " after " + mLastTimeMs);
+ mTimeProvider.notifyAt(
+ mNextScheduledTimeMs >= 0 ?
+ (mNextScheduledTimeMs * 1000) : MediaTimeProvider.NO_TIME,
+ this);
+ }
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ public void onTimedEvent(long timeUs) {
+ if (DEBUG) Log.d(TAG, "onTimedEvent " + timeUs);
+ synchronized (this) {
+ long timeMs = timeUs / 1000;
+ updateActiveCues(false, timeMs);
+ takeTime(timeMs);
+ }
+ updateView(mActiveCues);
+ scheduleTimedEvents();
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ public void onSeek(long timeUs) {
+ if (DEBUG) Log.d(TAG, "onSeek " + timeUs);
+ synchronized (this) {
+ long timeMs = timeUs / 1000;
+ updateActiveCues(true, timeMs);
+ takeTime(timeMs);
+ }
+ updateView(mActiveCues);
+ scheduleTimedEvents();
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ public void onStop() {
+ synchronized (this) {
+ if (DEBUG) Log.d(TAG, "onStop");
+ clearActiveCues();
+ mLastTimeMs = -1;
+ }
+ updateView(mActiveCues);
+ mNextScheduledTimeMs = -1;
+ mTimeProvider.notifyAt(MediaTimeProvider.NO_TIME, this);
+ }
+
+ /** @hide */
+ protected MediaTimeProvider mTimeProvider;
+
+ /** @hide */
+ public void show() {
+ if (mVisible) {
+ return;
+ }
+
+ mVisible = true;
+ getView().setVisibility(View.VISIBLE);
+ if (mTimeProvider != null) {
+ mTimeProvider.scheduleUpdate(this);
+ }
+ }
+
+ /** @hide */
+ public void hide() {
+ if (!mVisible) {
+ return;
+ }
+
+ if (mTimeProvider != null) {
+ mTimeProvider.cancelNotifications(this);
+ }
+ getView().setVisibility(View.INVISIBLE);
+ mVisible = false;
+ }
+
+ /** @hide */
+ protected synchronized boolean addCue(Cue cue) {
+ mCues.add(cue);
+
+ if (cue.mRunID != 0) {
+ Run run = mRunsByID.get(cue.mRunID);
+ if (run == null) {
+ run = new Run();
+ mRunsByID.put(cue.mRunID, run);
+ run.mEndTimeMs = cue.mEndTimeMs;
+ } else if (run.mEndTimeMs < cue.mEndTimeMs) {
+ run.mEndTimeMs = cue.mEndTimeMs;
+ }
+
+ // link-up cues in the same run
+ cue.mNextInRun = run.mFirstCue;
+ run.mFirstCue = cue;
+ }
+
+ // if a cue is added that should be visible, need to refresh view
+ long nowMs = -1;
+ if (mTimeProvider != null) {
+ try {
+ nowMs = mTimeProvider.getCurrentTimeUs(
+ false /* precise */, true /* monotonic */) / 1000;
+ } catch (IllegalStateException e) {
+ // handle as it we are not playing
+ }
+ }
+
+ if (DEBUG) Log.v(TAG, "mVisible=" + mVisible + ", " +
+ cue.mStartTimeMs + " <= " + nowMs + ", " +
+ cue.mEndTimeMs + " >= " + mLastTimeMs);
+
+ if (mVisible &&
+ cue.mStartTimeMs <= nowMs &&
+ // we don't trust nowMs, so check any cue since last callback
+ cue.mEndTimeMs >= mLastTimeMs) {
+ if (mRunnable != null) {
+ mHandler.removeCallbacks(mRunnable);
+ }
+ final SubtitleTrack track = this;
+ final long thenMs = nowMs;
+ mRunnable = new Runnable() {
+ @Override
+ public void run() {
+ // even with synchronized, it is possible that we are going
+ // to do multiple updates as the runnable could be already
+ // running.
+ synchronized (track) {
+ mRunnable = null;
+ updateActiveCues(true, thenMs);
+ updateView(mActiveCues);
+ }
+ }
+ };
+ // delay update so we don't update view on every cue. TODO why 10?
+ if (mHandler.postDelayed(mRunnable, 10 /* delay */)) {
+ if (DEBUG) Log.v(TAG, "scheduling update");
+ } else {
+ if (DEBUG) Log.w(TAG, "failed to schedule subtitle view update");
+ }
+ return true;
+ }
+
+ if (mVisible &&
+ cue.mEndTimeMs >= mLastTimeMs &&
+ (cue.mStartTimeMs < mNextScheduledTimeMs ||
+ mNextScheduledTimeMs < 0)) {
+ scheduleTimedEvents();
+ }
+
+ return false;
+ }
+
+ /** @hide */
+ public void setTimeProvider(MediaTimeProvider timeProvider) {
+ if (mTimeProvider == timeProvider) {
+ return;
+ }
+ if (mTimeProvider != null) {
+ mTimeProvider.cancelNotifications(this);
+ }
+ mTimeProvider = timeProvider;
+ if (mTimeProvider != null) {
+ mTimeProvider.scheduleUpdate(this);
+ }
+ }
+
+
+ /** @hide */
+ static class CueList {
+ private static final String TAG = "CueList";
+ // simplistic, inefficient implementation
+ private SortedMap<Long, Vector<Cue> > mCues;
+ public boolean DEBUG = false;
+
+ private boolean addEvent(Cue cue, long timeMs) {
+ Vector<Cue> cues = mCues.get(timeMs);
+ if (cues == null) {
+ cues = new Vector<Cue>(2);
+ mCues.put(timeMs, cues);
+ } else if (cues.contains(cue)) {
+ // do not duplicate cues
+ return false;
+ }
+
+ cues.add(cue);
+ return true;
+ }
+
+ private void removeEvent(Cue cue, long timeMs) {
+ Vector<Cue> cues = mCues.get(timeMs);
+ if (cues != null) {
+ cues.remove(cue);
+ if (cues.size() == 0) {
+ mCues.remove(timeMs);
+ }
+ }
+ }
+
+ public void add(Cue cue) {
+ // ignore non-positive-duration cues
+ if (cue.mStartTimeMs >= cue.mEndTimeMs)
+ return;
+
+ if (!addEvent(cue, cue.mStartTimeMs)) {
+ return;
+ }
+
+ long lastTimeMs = cue.mStartTimeMs;
+ if (cue.mInnerTimesMs != null) {
+ for (long timeMs: cue.mInnerTimesMs) {
+ if (timeMs > lastTimeMs && timeMs < cue.mEndTimeMs) {
+ addEvent(cue, timeMs);
+ lastTimeMs = timeMs;
+ }
+ }
+ }
+
+ addEvent(cue, cue.mEndTimeMs);
+ }
+
+ public void remove(Cue cue) {
+ removeEvent(cue, cue.mStartTimeMs);
+ if (cue.mInnerTimesMs != null) {
+ for (long timeMs: cue.mInnerTimesMs) {
+ removeEvent(cue, timeMs);
+ }
+ }
+ removeEvent(cue, cue.mEndTimeMs);
+ }
+
+ public Iterable<Pair<Long, Cue>> entriesBetween(
+ final long lastTimeMs, final long timeMs) {
+ return new Iterable<Pair<Long, Cue> >() {
+ @Override
+ public Iterator<Pair<Long, Cue> > iterator() {
+ if (DEBUG) Log.d(TAG, "slice (" + lastTimeMs + ", " + timeMs + "]=");
+ try {
+ return new EntryIterator(
+ mCues.subMap(lastTimeMs + 1, timeMs + 1));
+ } catch(IllegalArgumentException e) {
+ return new EntryIterator(null);
+ }
+ }
+ };
+ }
+
+ public long nextTimeAfter(long timeMs) {
+ SortedMap<Long, Vector<Cue>> tail = null;
+ try {
+ tail = mCues.tailMap(timeMs + 1);
+ if (tail != null) {
+ return tail.firstKey();
+ } else {
+ return -1;
+ }
+ } catch(IllegalArgumentException e) {
+ return -1;
+ } catch(NoSuchElementException e) {
+ return -1;
+ }
+ }
+
+ class EntryIterator implements Iterator<Pair<Long, Cue> > {
+ @Override
+ public boolean hasNext() {
+ return !mDone;
+ }
+
+ @Override
+ public Pair<Long, Cue> next() {
+ if (mDone) {
+ throw new NoSuchElementException("");
+ }
+ mLastEntry = new Pair<Long, Cue>(
+ mCurrentTimeMs, mListIterator.next());
+ mLastListIterator = mListIterator;
+ if (!mListIterator.hasNext()) {
+ nextKey();
+ }
+ return mLastEntry;
+ }
+
+ @Override
+ public void remove() {
+ // only allow removing end tags
+ if (mLastListIterator == null ||
+ mLastEntry.second.mEndTimeMs != mLastEntry.first) {
+ throw new IllegalStateException("");
+ }
+
+ // remove end-cue
+ mLastListIterator.remove();
+ mLastListIterator = null;
+ if (mCues.get(mLastEntry.first).size() == 0) {
+ mCues.remove(mLastEntry.first);
+ }
+
+ // remove rest of the cues
+ Cue cue = mLastEntry.second;
+ removeEvent(cue, cue.mStartTimeMs);
+ if (cue.mInnerTimesMs != null) {
+ for (long timeMs: cue.mInnerTimesMs) {
+ removeEvent(cue, timeMs);
+ }
+ }
+ }
+
+ public EntryIterator(SortedMap<Long, Vector<Cue> > cues) {
+ if (DEBUG) Log.v(TAG, cues + "");
+ mRemainingCues = cues;
+ mLastListIterator = null;
+ nextKey();
+ }
+
+ private void nextKey() {
+ do {
+ try {
+ if (mRemainingCues == null) {
+ throw new NoSuchElementException("");
+ }
+ mCurrentTimeMs = mRemainingCues.firstKey();
+ mListIterator =
+ mRemainingCues.get(mCurrentTimeMs).iterator();
+ try {
+ mRemainingCues =
+ mRemainingCues.tailMap(mCurrentTimeMs + 1);
+ } catch (IllegalArgumentException e) {
+ mRemainingCues = null;
+ }
+ mDone = false;
+ } catch (NoSuchElementException e) {
+ mDone = true;
+ mRemainingCues = null;
+ mListIterator = null;
+ return;
+ }
+ } while (!mListIterator.hasNext());
+ }
+
+ private long mCurrentTimeMs;
+ private Iterator<Cue> mListIterator;
+ private boolean mDone;
+ private SortedMap<Long, Vector<Cue> > mRemainingCues;
+ private Iterator<Cue> mLastListIterator;
+ private Pair<Long,Cue> mLastEntry;
+ }
+
+ CueList() {
+ mCues = new TreeMap<Long, Vector<Cue>>();
+ }
+ }
+
+ /** @hide */
+ public static class Cue {
+ public long mStartTimeMs;
+ public long mEndTimeMs;
+ public long[] mInnerTimesMs;
+ public long mRunID;
+
+ /** @hide */
+ public Cue mNextInRun;
+
+ public void onTime(long timeMs) { }
+ }
+
+ /** @hide update mRunsByEndTime (with default end time) */
+ protected void finishedRun(long runID) {
+ if (runID != 0 && runID != ~0) {
+ Run run = mRunsByID.get(runID);
+ if (run != null) {
+ run.storeByEndTimeMs(mRunsByEndTime);
+ }
+ }
+ }
+
+ /** @hide update mRunsByEndTime with given end time */
+ public void setRunDiscardTimeMs(long runID, long timeMs) {
+ if (runID != 0 && runID != ~0) {
+ Run run = mRunsByID.get(runID);
+ if (run != null) {
+ run.mEndTimeMs = timeMs;
+ run.storeByEndTimeMs(mRunsByEndTime);
+ }
+ }
+ }
+
+ /** @hide */
+ private static class Run {
+ public Cue mFirstCue;
+ public Run mNextRunAtEndTimeMs;
+ public Run mPrevRunAtEndTimeMs;
+ public long mEndTimeMs = -1;
+ public long mRunID = 0;
+ private long mStoredEndTimeMs = -1;
+
+ public void storeByEndTimeMs(LongSparseArray<Run> runsByEndTime) {
+ // remove old value if any
+ int ix = runsByEndTime.indexOfKey(mStoredEndTimeMs);
+ if (ix >= 0) {
+ if (mPrevRunAtEndTimeMs == null) {
+ assert(this == runsByEndTime.valueAt(ix));
+ if (mNextRunAtEndTimeMs == null) {
+ runsByEndTime.removeAt(ix);
+ } else {
+ runsByEndTime.setValueAt(ix, mNextRunAtEndTimeMs);
+ }
+ }
+ removeAtEndTimeMs();
+ }
+
+ // add new value
+ if (mEndTimeMs >= 0) {
+ mPrevRunAtEndTimeMs = null;
+ mNextRunAtEndTimeMs = runsByEndTime.get(mEndTimeMs);
+ if (mNextRunAtEndTimeMs != null) {
+ mNextRunAtEndTimeMs.mPrevRunAtEndTimeMs = this;
+ }
+ runsByEndTime.put(mEndTimeMs, this);
+ mStoredEndTimeMs = mEndTimeMs;
+ }
+ }
+
+ public void removeAtEndTimeMs() {
+ Run prev = mPrevRunAtEndTimeMs;
+
+ if (mPrevRunAtEndTimeMs != null) {
+ mPrevRunAtEndTimeMs.mNextRunAtEndTimeMs = mNextRunAtEndTimeMs;
+ mPrevRunAtEndTimeMs = null;
+ }
+ if (mNextRunAtEndTimeMs != null) {
+ mNextRunAtEndTimeMs.mPrevRunAtEndTimeMs = prev;
+ mNextRunAtEndTimeMs = null;
+ }
+ }
+ }
+}
diff --git a/packages/DocumentsUI/res/layout/item_loading.xml b/packages/DocumentsUI/res/layout/item_loading.xml
new file mode 100644
index 0000000..7da71e3
--- /dev/null
+++ b/packages/DocumentsUI/res/layout/item_loading.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:paddingTop="8dip"
+ android:paddingBottom="8dip"
+ android:orientation="horizontal">
+
+ <ProgressBar
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:indeterminate="true"
+ style="?android:attr/progressBarStyle" />
+
+</FrameLayout>
diff --git a/packages/DocumentsUI/res/layout/item_message_grid.xml b/packages/DocumentsUI/res/layout/item_message_grid.xml
new file mode 100644
index 0000000..941340e
--- /dev/null
+++ b/packages/DocumentsUI/res/layout/item_message_grid.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="180dip"
+ android:paddingBottom="?android:attr/listPreferredItemPaddingEnd"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@color/chip"
+ android:foreground="@drawable/item_background"
+ android:duplicateParentState="true">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:paddingBottom="6dp"
+ android:orientation="vertical"
+ android:gravity="center">
+
+ <ImageView
+ android:id="@android:id/icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:contentDescription="@null" />
+
+ <TextView
+ android:id="@android:id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ android:paddingTop="6dp"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textAlignment="viewStart" />
+
+ </LinearLayout>
+
+ </FrameLayout>
+
+</FrameLayout>
diff --git a/packages/DocumentsUI/res/layout/item_message_list.xml b/packages/DocumentsUI/res/layout/item_message_list.xml
new file mode 100644
index 0000000..dda3c80
--- /dev/null
+++ b/packages/DocumentsUI/res/layout/item_message_list.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="@drawable/item_background"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:paddingTop="8dip"
+ android:paddingBottom="8dip"
+ android:orientation="horizontal">
+
+ <ImageView
+ android:id="@android:id/icon"
+ android:layout_width="@android:dimen/app_icon_size"
+ android:layout_height="@android:dimen/app_icon_size"
+ android:layout_marginEnd="8dip"
+ android:layout_gravity="center_vertical"
+ android:scaleType="centerInside"
+ android:contentDescription="@null" />
+
+ <TextView
+ android:id="@android:id/title"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textAlignment="viewStart" />
+
+</LinearLayout>
diff --git a/packages/DocumentsUI/res/layout/item_title.xml b/packages/DocumentsUI/res/layout/item_title.xml
index fe6c14d..eab3839 100644
--- a/packages/DocumentsUI/res/layout/item_title.xml
+++ b/packages/DocumentsUI/res/layout/item_title.xml
@@ -29,13 +29,4 @@
android:textAppearance="?android:attr/textAppearanceMedium"
android:textAlignment="viewStart" />
- <TextView
- android:id="@android:id/summary"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:singleLine="true"
- android:ellipsize="marquee"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:textAlignment="viewStart" />
-
</LinearLayout>
diff --git a/packages/DocumentsUI/res/menu/activity.xml b/packages/DocumentsUI/res/menu/activity.xml
index 575336c..e182159 100644
--- a/packages/DocumentsUI/res/menu/activity.xml
+++ b/packages/DocumentsUI/res/menu/activity.xml
@@ -28,6 +28,23 @@
android:actionViewClass="android.widget.SearchView"
android:imeOptions="actionSearch" />
<item
+ android:id="@+id/menu_sort"
+ android:title="@string/menu_sort"
+ android:icon="@drawable/ic_menu_sort"
+ android:showAsAction="always">
+ <menu>
+ <item
+ android:id="@+id/menu_sort_name"
+ android:title="@string/sort_name" />
+ <item
+ android:id="@+id/menu_sort_date"
+ android:title="@string/sort_date" />
+ <item
+ android:id="@+id/menu_sort_size"
+ android:title="@string/sort_size" />
+ </menu>
+ </item>
+ <item
android:id="@+id/menu_grid"
android:title="@string/menu_grid"
android:icon="@drawable/ic_menu_grid"
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
index 5b23ca5..33d7d6af 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
@@ -397,13 +397,8 @@
continue;
}
- try {
- if (resolver.delete(doc.uri, null, null) != 1) {
- Log.w(TAG, "Failed to delete " + doc);
- hadTrouble = true;
- }
- } catch (Exception e) {
- Log.w(TAG, "Failed to delete " + doc + ": " + e);
+ if (!DocumentsContract.deleteDocument(resolver, doc.uri)) {
+ Log.w(TAG, "Failed to delete " + doc);
hadTrouble = true;
}
}
@@ -417,11 +412,83 @@
return ((DocumentsActivity) fragment.getActivity()).getDisplayState();
}
+ private interface Footer {
+ public View getView(View convertView, ViewGroup parent);
+ }
+
+ private static class LoadingFooter implements Footer {
+ @Override
+ public View getView(View convertView, ViewGroup parent) {
+ final Context context = parent.getContext();
+ if (convertView == null) {
+ final LayoutInflater inflater = LayoutInflater.from(context);
+ convertView = inflater.inflate(R.layout.item_loading, parent, false);
+ }
+ return convertView;
+ }
+ }
+
+ private class MessageFooter implements Footer {
+ private final int mIcon;
+ private final String mMessage;
+
+ public MessageFooter(int icon, String message) {
+ mIcon = icon;
+ mMessage = message;
+ }
+
+ @Override
+ public View getView(View convertView, ViewGroup parent) {
+ final Context context = parent.getContext();
+ final State state = getDisplayState(DirectoryFragment.this);
+
+ if (convertView == null) {
+ final LayoutInflater inflater = LayoutInflater.from(context);
+ if (state.mode == MODE_LIST) {
+ convertView = inflater.inflate(R.layout.item_message_list, parent, false);
+ } else if (state.mode == MODE_GRID) {
+ convertView = inflater.inflate(R.layout.item_message_grid, parent, false);
+ } else {
+ throw new IllegalStateException();
+ }
+ }
+
+ final ImageView icon = (ImageView) convertView.findViewById(android.R.id.icon);
+ final TextView title = (TextView) convertView.findViewById(android.R.id.title);
+ icon.setImageResource(mIcon);
+ title.setText(mMessage);
+ return convertView;
+ }
+ }
+
private class DocumentsAdapter extends BaseAdapter {
private Cursor mCursor;
+ private int mCursorCount;
+
+ private List<Footer> mFooters = Lists.newArrayList();
public void swapCursor(Cursor cursor) {
mCursor = cursor;
+ mCursorCount = cursor != null ? cursor.getCount() : 0;
+
+ mFooters.clear();
+
+ final Bundle extras = cursor != null ? cursor.getExtras() : null;
+ if (extras != null) {
+ final String info = extras.getString(DocumentsContract.EXTRA_INFO);
+ if (info != null) {
+ mFooters.add(new MessageFooter(
+ com.android.internal.R.drawable.ic_menu_info_details, info));
+ }
+ final String error = extras.getString(DocumentsContract.EXTRA_ERROR);
+ if (error != null) {
+ mFooters.add(new MessageFooter(
+ com.android.internal.R.drawable.ic_dialog_alert, error));
+ }
+ if (extras.getBoolean(DocumentsContract.EXTRA_LOADING, false)) {
+ mFooters.add(new LoadingFooter());
+ }
+ }
if (isEmpty()) {
mEmptyView.setVisibility(View.VISIBLE);
@@ -434,6 +501,15 @@
@Override
public View getView(int position, View convertView, ViewGroup parent) {
+ if (position < mCursorCount) {
+ return getDocumentView(position, convertView, parent);
+ } else {
+ position -= mCursorCount;
+ return mFooters.get(position).getView(convertView, parent);
+ }
+ }
+
+ private View getDocumentView(int position, View convertView, ViewGroup parent) {
final Context context = parent.getContext();
final State state = getDisplayState(DirectoryFragment.this);
@@ -540,21 +616,42 @@
@Override
public int getCount() {
- return mCursor != null ? mCursor.getCount() : 0;
+ return mCursorCount + mFooters.size();
}
@Override
public Cursor getItem(int position) {
- if (mCursor != null) {
+ if (position < mCursorCount) {
mCursor.moveToPosition(position);
+ return mCursor;
+ } else {
+ return null;
}
- return mCursor;
}
@Override
public long getItemId(int position) {
return position;
}
+
+ @Override
+ public int getItemViewType(int position) {
+ if (position < mCursorCount) {
+ return 0;
+ } else {
+ return IGNORE_ITEM_VIEW_TYPE;
+ }
+ }
+
+ @Override
+ public boolean areAllItemsEnabled() {
+ return false;
+ }
+
+ @Override
+ public boolean isEnabled(int position) {
+ return position < mCursorCount;
+ }
}
private static class ThumbnailAsyncTask extends AsyncTask<Uri, Void, Bitmap> {
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java
index 3f016b5..6ea57d7 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java
@@ -77,11 +77,12 @@
.getContentResolver().acquireUnstableContentProviderClient(authority);
final Cursor cursor = result.client.query(
mUri, null, null, null, getQuerySortOrder(mSortOrder), mSignal);
+ cursor.registerContentObserver(mObserver);
+
final Cursor withRoot = new RootCursorWrapper(mUri.getAuthority(), mRootId, cursor, -1);
final Cursor sorted = new SortingCursorWrapper(withRoot, mSortOrder);
result.cursor = sorted;
- result.cursor.registerContentObserver(mObserver);
} catch (Exception e) {
result.exception = e;
ContentProviderClient.closeQuietly(result.client);
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
index f569f5a..4da6567 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
@@ -32,7 +32,6 @@
import android.content.ActivityNotFoundException;
import android.content.ClipData;
import android.content.ComponentName;
-import android.content.ContentProviderClient;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Intent;
@@ -65,6 +64,8 @@
import com.android.documentsui.model.DurableUtils;
import com.android.documentsui.model.RootInfo;
+import libcore.io.IoUtils;
+
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Arrays;
@@ -81,6 +82,8 @@
private static final String EXTRA_STATE = "state";
+ private boolean mIgnoreNextNavigation;
+
private RootsCache mRoots;
private State mState;
@@ -192,10 +195,20 @@
} catch (IOException e) {
Log.w(TAG, "Failed to resume", e);
} finally {
- cursor.close();
+ IoUtils.closeQuietly(cursor);
}
- mDrawerLayout.openDrawer(mRootsContainer);
+ // If restored root isn't valid, fall back to recents
+ final RootInfo root = getCurrentRoot();
+ final List<RootInfo> matchingRoots = mRoots.getMatchingRoots(mState);
+ if (!matchingRoots.contains(root)) {
+ mState.stack.clear();
+ }
+
+ // Only open drawer when showing recents
+ if (mState.stack.isRecents()) {
+ mDrawerLayout.openDrawer(mRootsContainer);
+ }
}
}
@@ -245,6 +258,14 @@
actionBar.setDisplayShowHomeEnabled(true);
+ if (mState.action == ACTION_MANAGE) {
+ actionBar.setDisplayHomeAsUpEnabled(false);
+ mDrawerToggle.setDrawerIndicatorEnabled(false);
+ } else {
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ mDrawerToggle.setDrawerIndicatorEnabled(true);
+ }
+
if (mDrawerLayout.isDrawerOpen(mRootsContainer)) {
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
actionBar.setIcon(new ColorDrawable());
@@ -254,33 +275,19 @@
} else if (mState.action == ACTION_CREATE) {
actionBar.setTitle(R.string.title_save);
}
-
- actionBar.setDisplayHomeAsUpEnabled(true);
- mDrawerToggle.setDrawerIndicatorEnabled(true);
-
} else {
final RootInfo root = getCurrentRoot();
actionBar.setIcon(root != null ? root.loadIcon(this) : null);
- if (mRoots.isRecentsRoot(root)) {
+ if (mState.stack.size() <= 1) {
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
actionBar.setTitle(root.title);
} else {
+ mIgnoreNextNavigation = true;
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
actionBar.setTitle(null);
- actionBar.setListNavigationCallbacks(mSortAdapter, mSortListener);
- actionBar.setSelectedNavigationItem(mState.sortOrder);
- }
-
- if (mState.stack.size() > 1) {
- actionBar.setDisplayHomeAsUpEnabled(true);
- mDrawerToggle.setDrawerIndicatorEnabled(false);
- } else if (mState.action == ACTION_MANAGE) {
- actionBar.setDisplayHomeAsUpEnabled(false);
- mDrawerToggle.setDrawerIndicatorEnabled(false);
- } else {
- actionBar.setDisplayHomeAsUpEnabled(true);
- mDrawerToggle.setDrawerIndicatorEnabled(true);
+ actionBar.setListNavigationCallbacks(mStackAdapter, mStackListener);
+ actionBar.setSelectedNavigationItem(mStackAdapter.getCount() - 1);
}
}
}
@@ -328,13 +335,20 @@
final MenuItem createDir = menu.findItem(R.id.menu_create_dir);
final MenuItem search = menu.findItem(R.id.menu_search);
- final MenuItem grid = menu.findItem(R.id.menu_grid);
+ final MenuItem sort = menu.findItem(R.id.menu_sort);
+ final MenuItem sortSize = menu.findItem(R.id.menu_sort_size);
+ final MenuItem grid = menu.findItem(R.id.menu_grid);
final MenuItem list = menu.findItem(R.id.menu_list);
final MenuItem settings = menu.findItem(R.id.menu_settings);
grid.setVisible(mState.mode != MODE_GRID);
list.setVisible(mState.mode != MODE_LIST);
+ // No sorting in recents
+ sort.setVisible(cwd != null);
+ // Only sort by size when visible
+ sortSize.setVisible(mState.showSize);
+
final boolean searchVisible;
if (mState.action == ACTION_CREATE) {
createDir.setVisible(cwd != null && cwd.isCreateSupported());
@@ -375,6 +389,18 @@
return true;
} else if (id == R.id.menu_search) {
return false;
+ } else if (id == R.id.menu_sort_name) {
+ mState.sortOrder = State.SORT_ORDER_DISPLAY_NAME;
+ updateDisplayState();
+ return true;
+ } else if (id == R.id.menu_sort_date) {
+ mState.sortOrder = State.SORT_ORDER_LAST_MODIFIED;
+ updateDisplayState();
+ return true;
+ } else if (id == R.id.menu_sort_size) {
+ mState.sortOrder = State.SORT_ORDER_SIZE;
+ updateDisplayState();
+ return true;
} else if (id == R.id.menu_grid) {
// TODO: persist explicit user mode for cwd
mState.mode = MODE_GRID;
@@ -421,25 +447,15 @@
updateActionBar();
}
- // TODO: support additional sort orders
- private BaseAdapter mSortAdapter = new BaseAdapter() {
+ private BaseAdapter mStackAdapter = new BaseAdapter() {
@Override
public int getCount() {
- return mState.showSize ? 3 : 2;
+ return mState.stack.size();
}
@Override
- public Object getItem(int position) {
- switch (position) {
- case 0:
- return getText(R.string.sort_name);
- case 1:
- return getText(R.string.sort_date);
- case 2:
- return getText(R.string.sort_size);
- default:
- return null;
- }
+ public DocumentInfo getItem(int position) {
+ return mState.stack.get(mState.stack.size() - position - 1);
}
@Override
@@ -455,17 +471,15 @@
}
final TextView title = (TextView) convertView.findViewById(android.R.id.title);
- final TextView summary = (TextView) convertView.findViewById(android.R.id.summary);
+ final DocumentInfo doc = getItem(position);
- if (mState.stack.size() > 0) {
- title.setText(mState.stack.getTitle(mRoots));
+ if (position == 0) {
+ final RootInfo root = getCurrentRoot();
+ title.setText(root.title);
} else {
- // No directory means recents
- title.setText(R.string.root_recent);
+ title.setText(doc.displayName);
}
- summary.setText((String) getItem(position));
-
return convertView;
}
@@ -477,17 +491,31 @@
}
final TextView text1 = (TextView) convertView.findViewById(android.R.id.text1);
- text1.setText((String) getItem(position));
+ final DocumentInfo doc = getItem(position);
+
+ if (position == 0) {
+ final RootInfo root = getCurrentRoot();
+ text1.setText(root.title);
+ } else {
+ text1.setText(doc.displayName);
+ }
return convertView;
}
};
- private OnNavigationListener mSortListener = new OnNavigationListener() {
+ private OnNavigationListener mStackListener = new OnNavigationListener() {
@Override
public boolean onNavigationItemSelected(int itemPosition, long itemId) {
- mState.sortOrder = itemPosition;
- updateDisplayState();
+ if (mIgnoreNextNavigation) {
+ mIgnoreNextNavigation = false;
+ return false;
+ }
+
+ while (mState.stack.size() > itemPosition + 1) {
+ mState.stack.pop();
+ }
+ onCurrentDirectoryChanged();
return true;
}
};
@@ -629,16 +657,12 @@
final DocumentInfo cwd = getCurrentDirectory();
final String authority = cwd.uri.getAuthority();
- final ContentProviderClient client = getContentResolver()
- .acquireUnstableContentProviderClient(authority);
- try {
- final Uri childUri = DocumentsContract.createDocument(
- getContentResolver(), cwd.uri, mimeType, displayName);
+ final Uri childUri = DocumentsContract.createDocument(
+ getContentResolver(), cwd.uri, mimeType, displayName);
+ if (childUri != null) {
onFinished(childUri);
- } catch (Exception e) {
+ } else {
Toast.makeText(this, R.string.save_error, Toast.LENGTH_SHORT).show();
- } finally {
- ContentProviderClient.closeQuietly(client);
}
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RootCursorWrapper.java b/packages/DocumentsUI/src/com/android/documentsui/RootCursorWrapper.java
index d0e5ff6..0b58218 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RootCursorWrapper.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RootCursorWrapper.java
@@ -18,6 +18,7 @@
import android.database.AbstractCursor;
import android.database.Cursor;
+import android.os.Bundle;
/**
* Cursor wrapper that adds columns to identify which root a document came from.
@@ -63,6 +64,11 @@
}
@Override
+ public Bundle getExtras() {
+ return mCursor.getExtras();
+ }
+
+ @Override
public void close() {
super.close();
mCursor.close();
@@ -128,5 +134,4 @@
public boolean isNull(int column) {
return mCursor.isNull(column);
}
-
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/SortingCursorWrapper.java b/packages/DocumentsUI/src/com/android/documentsui/SortingCursorWrapper.java
index b434a35..19ad2e2 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/SortingCursorWrapper.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/SortingCursorWrapper.java
@@ -22,6 +22,7 @@
import android.database.AbstractCursor;
import android.database.Cursor;
+import android.os.Bundle;
import android.provider.DocumentsContract.Document;
/**
@@ -96,6 +97,11 @@
}
@Override
+ public Bundle getExtras() {
+ return mCursor.getExtras();
+ }
+
+ @Override
public void close() {
super.close();
mCursor.close();
diff --git a/packages/DocumentsUI/src/com/android/documentsui/model/DocumentInfo.java b/packages/DocumentsUI/src/com/android/documentsui/model/DocumentInfo.java
index 7721bcc..9874265 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/model/DocumentInfo.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/model/DocumentInfo.java
@@ -16,6 +16,7 @@
package com.android.documentsui.model;
+import android.content.ContentProviderClient;
import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.PackageManager;
@@ -26,7 +27,6 @@
import android.provider.DocumentsContract;
import android.provider.DocumentsContract.Document;
-import com.android.documentsui.RecentsProvider;
import com.android.documentsui.RootCursorWrapper;
import libcore.io.IoUtils;
@@ -117,41 +117,12 @@
return doc;
}
- @Deprecated
- public static DocumentInfo fromRecentOpenCursor(ContentResolver resolver, Cursor recentCursor)
- throws FileNotFoundException {
- final Uri uri = Uri.parse(getCursorString(recentCursor, RecentsProvider.COL_URI));
- final long lastModified = getCursorLong(recentCursor, RecentsProvider.COL_TIMESTAMP);
-
- Cursor cursor = null;
- try {
- cursor = resolver.query(uri, null, null, null, null);
- if (!cursor.moveToFirst()) {
- throw new FileNotFoundException("Missing details for " + uri);
- }
-
- final DocumentInfo doc = new DocumentInfo();
- doc.uri = uri;
- doc.mimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE);
- doc.displayName = getCursorString(cursor, Document.COLUMN_DISPLAY_NAME);
- doc.lastModified = lastModified;
- doc.flags = getCursorInt(cursor, Document.COLUMN_FLAGS)
- & Document.FLAG_SUPPORTS_THUMBNAIL;
- doc.summary = getCursorString(cursor, Document.COLUMN_SUMMARY);
- doc.size = getCursorLong(cursor, Document.COLUMN_SIZE);
- doc.icon = getCursorInt(cursor, Document.COLUMN_ICON);
- return doc;
- } catch (Throwable t) {
- throw asFileNotFoundException(t);
- } finally {
- IoUtils.closeQuietly(cursor);
- }
- }
-
public static DocumentInfo fromUri(ContentResolver resolver, Uri uri) throws FileNotFoundException {
+ final ContentProviderClient client = resolver.acquireUnstableContentProviderClient(
+ uri.getAuthority());
Cursor cursor = null;
try {
- cursor = resolver.query(uri, null, null, null, null);
+ cursor = client.query(uri, null, null, null, null);
if (!cursor.moveToFirst()) {
throw new FileNotFoundException("Missing details for " + uri);
}
@@ -169,6 +140,7 @@
throw asFileNotFoundException(t);
} finally {
IoUtils.closeQuietly(cursor);
+ ContentProviderClient.closeQuietly(client);
}
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/model/DocumentStack.java b/packages/DocumentsUI/src/com/android/documentsui/model/DocumentStack.java
index 64631ab..33a1376 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/model/DocumentStack.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/model/DocumentStack.java
@@ -45,6 +45,10 @@
}
}
+ public boolean isRecents() {
+ return size() == 0;
+ }
+
@Override
public void reset() {
clear();
diff --git a/packages/ExternalStorageProvider/AndroidManifest.xml b/packages/ExternalStorageProvider/AndroidManifest.xml
index 5272166..7094efc 100644
--- a/packages/ExternalStorageProvider/AndroidManifest.xml
+++ b/packages/ExternalStorageProvider/AndroidManifest.xml
@@ -13,7 +13,20 @@
android:permission="android.permission.MANAGE_DOCUMENTS">
<meta-data
android:name="android.content.DOCUMENT_PROVIDER"
- android:resource="@xml/document_provider" />
+ android:value="true" />
+ </provider>
+
+ <!-- TODO: find a better place for tests to live -->
+ <provider
+ android:name=".TestDocumentsProvider"
+ android:authorities="com.example.documents"
+ android:grantUriPermissions="true"
+ android:exported="true"
+ android:permission="android.permission.MANAGE_DOCUMENTS"
+ android:enabled="false">
+ <meta-data
+ android:name="android.content.DOCUMENT_PROVIDER"
+ android:value="true" />
</provider>
</application>
</manifest>
diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/TestDocumentsProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/TestDocumentsProvider.java
new file mode 100644
index 0000000..872974f
--- /dev/null
+++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/TestDocumentsProvider.java
@@ -0,0 +1,244 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.externalstorage;
+
+import android.content.ContentResolver;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.database.MatrixCursor.RowBuilder;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.os.ParcelFileDescriptor;
+import android.os.SystemClock;
+import android.provider.DocumentsContract;
+import android.provider.DocumentsContract.Document;
+import android.provider.DocumentsContract.Root;
+import android.provider.DocumentsProvider;
+import android.util.Log;
+
+import java.io.FileNotFoundException;
+import java.lang.ref.WeakReference;
+
+public class TestDocumentsProvider extends DocumentsProvider {
+ private static final String TAG = "TestDocuments";
+
+ private static final boolean CRASH_ROOTS = false;
+ private static final boolean CRASH_DOCUMENT = false;
+
+ private static final String MY_ROOT_ID = "myRoot";
+ private static final String MY_DOC_ID = "myDoc";
+ private static final String MY_DOC_NULL = "myNull";
+
+ private static final String[] DEFAULT_ROOT_PROJECTION = new String[] {
+ Root.COLUMN_ROOT_ID, Root.COLUMN_ROOT_TYPE, Root.COLUMN_FLAGS, Root.COLUMN_ICON,
+ Root.COLUMN_TITLE, Root.COLUMN_SUMMARY, Root.COLUMN_DOCUMENT_ID,
+ Root.COLUMN_AVAILABLE_BYTES,
+ };
+
+ private static final String[] DEFAULT_DOCUMENT_PROJECTION = new String[] {
+ Document.COLUMN_DOCUMENT_ID, Document.COLUMN_MIME_TYPE, Document.COLUMN_DISPLAY_NAME,
+ Document.COLUMN_LAST_MODIFIED, Document.COLUMN_FLAGS, Document.COLUMN_SIZE,
+ };
+
+ private static String[] resolveRootProjection(String[] projection) {
+ return projection != null ? projection : DEFAULT_ROOT_PROJECTION;
+ }
+
+ private static String[] resolveDocumentProjection(String[] projection) {
+ return projection != null ? projection : DEFAULT_DOCUMENT_PROJECTION;
+ }
+
+ @Override
+ public Cursor queryRoots(String[] projection) throws FileNotFoundException {
+ if (CRASH_ROOTS) System.exit(12);
+
+ final MatrixCursor result = new MatrixCursor(resolveRootProjection(projection));
+ final RowBuilder row = result.newRow();
+ row.offer(Root.COLUMN_ROOT_ID, MY_ROOT_ID);
+ row.offer(Root.COLUMN_ROOT_TYPE, Root.ROOT_TYPE_SERVICE);
+ row.offer(Root.COLUMN_FLAGS, Root.FLAG_SUPPORTS_RECENTS);
+ row.offer(Root.COLUMN_TITLE, "_Test title which is really long");
+ row.offer(Root.COLUMN_SUMMARY, "_Summary which is also super long text");
+ row.offer(Root.COLUMN_DOCUMENT_ID, MY_DOC_ID);
+ row.offer(Root.COLUMN_AVAILABLE_BYTES, 1024);
+ return result;
+ }
+
+ @Override
+ public Cursor queryDocument(String documentId, String[] projection)
+ throws FileNotFoundException {
+ if (CRASH_DOCUMENT) System.exit(12);
+
+ final MatrixCursor result = new MatrixCursor(resolveDocumentProjection(projection));
+ includeFile(result, documentId);
+ return result;
+ }
+
+ /**
+ * Holds any outstanding or finished "network" fetching.
+ */
+ private WeakReference<CloudTask> mTask;
+
+ private static class CloudTask implements Runnable {
+
+ private final ContentResolver mResolver;
+ private final Uri mNotifyUri;
+
+ private volatile boolean mFinished;
+
+ public CloudTask(ContentResolver resolver, Uri notifyUri) {
+ mResolver = resolver;
+ mNotifyUri = notifyUri;
+ }
+
+ @Override
+ public void run() {
+ // Pretend to do some network
+ Log.d(TAG, hashCode() + ": pretending to do some network!");
+ SystemClock.sleep(2000);
+ Log.d(TAG, hashCode() + ": network done!");
+
+ mFinished = true;
+
+ // Tell anyone remotely they should requery
+ mResolver.notifyChange(mNotifyUri, null, false);
+ }
+
+ public boolean includeIfFinished(MatrixCursor result) {
+ Log.d(TAG, hashCode() + ": includeIfFinished() found " + mFinished);
+ if (mFinished) {
+ includeFile(result, "_networkfile1");
+ includeFile(result, "_networkfile2");
+ includeFile(result, "_networkfile3");
+ return true;
+ } else {
+ return false;
+ }
+ }
+ }
+
+ private static class CloudCursor extends MatrixCursor {
+ public Object keepAlive;
+ public final Bundle extras = new Bundle();
+
+ public CloudCursor(String[] columnNames) {
+ super(columnNames);
+ }
+
+ @Override
+ public Bundle getExtras() {
+ return extras;
+ }
+ }
+
+ @Override
+ public Cursor queryChildDocuments(
+ String parentDocumentId, String[] projection, String sortOrder)
+ throws FileNotFoundException {
+
+ final ContentResolver resolver = getContext().getContentResolver();
+ final Uri notifyUri = DocumentsContract.buildDocumentUri(
+ "com.example.documents", parentDocumentId);
+
+ CloudCursor result = new CloudCursor(resolveDocumentProjection(projection));
+ result.setNotificationUri(resolver, notifyUri);
+
+ // Always include local results
+ includeFile(result, MY_DOC_NULL);
+ includeFile(result, "localfile1");
+ includeFile(result, "localfile2");
+
+ synchronized (this) {
+ // Try picking up an existing network fetch
+ CloudTask task = mTask != null ? mTask.get() : null;
+ if (task == null) {
+ Log.d(TAG, "No network task found; starting!");
+ task = new CloudTask(resolver, notifyUri);
+ mTask = new WeakReference<CloudTask>(task);
+ new Thread(task).start();
+
+ // Aggressively try freeing weak reference above
+ new Thread() {
+ @Override
+ public void run() {
+ while (mTask.get() != null) {
+ SystemClock.sleep(200);
+ System.gc();
+ System.runFinalization();
+ }
+ Log.d(TAG, "AHA! THE CLOUD TASK WAS GC'ED!");
+ }
+ }.start();
+ }
+
+ // Blend in cloud results if ready
+ if (task.includeIfFinished(result)) {
+ result.extras.putString(DocumentsContract.EXTRA_INFO,
+ "Everything Went Better Than Expected and this message is quite "
+ + "long and verbose and maybe even too long");
+ result.extras.putString(DocumentsContract.EXTRA_ERROR,
+ "But then again, maybe our server ran into an error, which means "
+ + "we're going to have a bad time");
+ } else {
+ result.extras.putBoolean(DocumentsContract.EXTRA_LOADING, true);
+ }
+
+ // Tie the network fetch to the cursor GC lifetime
+ result.keepAlive = task;
+
+ return result;
+ }
+ }
+
+ @Override
+ public Cursor queryRecentDocuments(String rootId, String[] projection)
+ throws FileNotFoundException {
+ // Pretend to take a super long time to respond
+ SystemClock.sleep(3000);
+
+ final MatrixCursor result = new MatrixCursor(resolveDocumentProjection(projection));
+ includeFile(result, "It was /worth/ the_wait for?the file:with the&incredibly long name");
+ return result;
+ }
+
+ @Override
+ public ParcelFileDescriptor openDocument(String docId, String mode, CancellationSignal signal)
+ throws FileNotFoundException {
+ throw new FileNotFoundException();
+ }
+
+ @Override
+ public boolean onCreate() {
+ return true;
+ }
+
+ private static void includeFile(MatrixCursor result, String docId) {
+ final RowBuilder row = result.newRow();
+ row.offer(Document.COLUMN_DOCUMENT_ID, docId);
+ row.offer(Document.COLUMN_DISPLAY_NAME, docId);
+ row.offer(Document.COLUMN_LAST_MODIFIED, System.currentTimeMillis());
+
+ if (MY_DOC_ID.equals(docId)) {
+ row.offer(Document.COLUMN_MIME_TYPE, Document.MIME_TYPE_DIR);
+ } else if (MY_DOC_NULL.equals(docId)) {
+ // No MIME type
+ } else {
+ row.offer(Document.COLUMN_MIME_TYPE, "application/octet-stream");
+ }
+ }
+}
diff --git a/packages/PrintSpooler/res/drawable-hdpi/ic_menu_add.png b/packages/PrintSpooler/res/drawable-hdpi/ic_menu_add.png
deleted file mode 100644
index 4b68f52..0000000
--- a/packages/PrintSpooler/res/drawable-hdpi/ic_menu_add.png
+++ /dev/null
Binary files differ
diff --git a/packages/PrintSpooler/res/drawable-mdpi/ic_menu_add.png b/packages/PrintSpooler/res/drawable-mdpi/ic_menu_add.png
deleted file mode 100644
index 15ffadd..0000000
--- a/packages/PrintSpooler/res/drawable-mdpi/ic_menu_add.png
+++ /dev/null
Binary files differ
diff --git a/packages/PrintSpooler/res/drawable-xhdpi/ic_menu_add.png b/packages/PrintSpooler/res/drawable-xhdpi/ic_menu_add.png
deleted file mode 100644
index 420510e..0000000
--- a/packages/PrintSpooler/res/drawable-xhdpi/ic_menu_add.png
+++ /dev/null
Binary files differ
diff --git a/packages/PrintSpooler/res/menu/select_printer_activity.xml b/packages/PrintSpooler/res/menu/select_printer_activity.xml
index 28fbd35..d4ce1cf 100644
--- a/packages/PrintSpooler/res/menu/select_printer_activity.xml
+++ b/packages/PrintSpooler/res/menu/select_printer_activity.xml
@@ -29,7 +29,7 @@
<item
android:id="@+id/action_add_printer"
android:title="@null"
- android:icon="@drawable/ic_menu_add"
+ android:icon="@*android:drawable/create_contact"
android:showAsAction="ifRoom"
android:alphabeticShortcut="a">
</item>
diff --git a/packages/PrintSpooler/src/com/android/printspooler/FusedPrintersProvider.java b/packages/PrintSpooler/src/com/android/printspooler/FusedPrintersProvider.java
index ad8d95a..88da21f 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/FusedPrintersProvider.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/FusedPrintersProvider.java
@@ -47,7 +47,6 @@
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
-import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@@ -66,15 +65,16 @@
private static final int MAX_FAVORITE_PRINTER_COUNT = 4;
- private final Map<PrinterId, PrinterInfo> mPrinters =
- new LinkedHashMap<PrinterId, PrinterInfo>();
+ private final List<PrinterInfo> mPrinters =
+ new ArrayList<PrinterInfo>();
+
+ private final List<PrinterInfo> mFavoritePrinters =
+ new ArrayList<PrinterInfo>();
private final PersistenceManager mPersistenceManager;
private PrinterDiscoverySession mDiscoverySession;
- private List<PrinterInfo> mFavoritePrinters;
-
private PrinterId mTrackedPrinter;
public FusedPrintersProvider(Context context) {
@@ -86,15 +86,40 @@
mPersistenceManager.addPrinterAndWritePrinterHistory(printer);
}
- public List<PrinterInfo> getPrinters() {
- return new ArrayList<PrinterInfo>(mPrinters.values());
- }
-
- @Override
- public void deliverResult(List<PrinterInfo> printers) {
- if (isStarted()) {
- super.deliverResult(printers);
+ private void computeAndDeliverResult() {
+ if (!isStarted()) {
+ return;
}
+
+ List<PrinterInfo> printers = new ArrayList<PrinterInfo>();
+
+ // We want the first few favorite printers on top of the list.
+ final int favoriteCount = Math.min(mFavoritePrinters.size(),
+ MAX_FAVORITE_PRINTER_COUNT);
+ for (int i = 0; i < favoriteCount; i++) {
+ printers.add(mFavoritePrinters.get(i));
+ }
+
+ // Add discovered printers updating favorites if needed.
+ final int printerCount = mPrinters.size();
+ for (int i = 0; i < printerCount; i++) {
+ PrinterInfo discoveredPrinter = mPrinters.get(i);
+ boolean printerHandled = false;
+ for (int j = 0; j< favoriteCount; j++) {
+ PrinterInfo favoritePrinter = printers.get(j);
+ if (favoritePrinter.getId().equals(discoveredPrinter.getId())) {
+ printers.set(j, discoveredPrinter);
+ printerHandled = true;
+ break;
+ }
+ }
+ if (!printerHandled) {
+ printers.add(discoveredPrinter);
+ }
+ }
+
+ // Deliver the printers.
+ deliverResult(printers);
}
@Override
@@ -104,14 +129,12 @@
}
// The contract is that if we already have a valid,
// result the we have to deliver it immediately.
- if (!mPrinters.isEmpty()) {
- deliverResult(new ArrayList<PrinterInfo>(mPrinters.values()));
+ if (!mPrinters.isEmpty() || !mFavoritePrinters.isEmpty()) {
+ computeAndDeliverResult();
}
- // If the data has changed since the last load
- // or is not available, start a load.
- if (takeContentChanged() || mPrinters.isEmpty()) {
- onForceLoad();
- }
+ // Always load the data to ensure discovery period is
+ // started and to make sure obsolete printers are updated.
+ onForceLoad();
}
@Override
@@ -127,7 +150,6 @@
if (DEBUG) {
Log.i(LOG_TAG, "onForceLoad()" + FusedPrintersProvider.this.hashCode());
}
- onCancelLoad();
loadInternal();
}
@@ -139,8 +161,9 @@
mDiscoverySession.setOnPrintersChangeListener(new OnPrintersChangeListener() {
@Override
public void onPrintersChanged() {
- deliverResult(new ArrayList<PrinterInfo>(
- mDiscoverySession.getPrinters()));
+ mPrinters.clear();
+ mPrinters.addAll(mDiscoverySession.getPrinters());
+ computeAndDeliverResult();
}
});
mPersistenceManager.readPrinterHistory();
@@ -244,27 +267,20 @@
@Override
protected void onPostExecute(List<PrinterInfo> printers) {
if (DEBUG) {
- Log.i(LOG_TAG, "read history completed" + FusedPrintersProvider.this.hashCode());
+ Log.i(LOG_TAG, "read history completed"
+ + FusedPrintersProvider.this.hashCode());
}
mHistoricalPrinters = printers;
// Compute the favorite printers.
- mFavoritePrinters = computeFavoritePrinters(printers);
-
- // We want the first few favorite printers on top of the list.
- final int favoriteCount = Math.min(mFavoritePrinters.size(),
- MAX_FAVORITE_PRINTER_COUNT);
- for (int i = 0; i < favoriteCount; i++) {
- PrinterInfo favoritePrinter = mFavoritePrinters.get(i);
- mPrinters.put(favoritePrinter.getId(), favoritePrinter);
- }
+ mFavoritePrinters.addAll(computeFavoritePrinters(printers));
mReadHistoryInProgress = false;
mReadHistoryCompleted = true;
// Deliver the favorites.
- deliverResult(mFavoritePrinters);
+ computeAndDeliverResult();
// Start loading the available printers.
loadInternal();
@@ -420,8 +436,9 @@
serializer.startTag(null, TAG_PRINTER);
serializer.attribute(null, ATTR_NAME, printer.getName());
+ // Historical printers are always stored as unavailable.
serializer.attribute(null, ATTR_STATUS, String.valueOf(
- printer.getStatus()));
+ PrinterInfo.STATUS_UNAVAILABLE));
String description = printer.getDescription();
if (description != null) {
serializer.attribute(null, ATTR_DESCRIPTION, description);
@@ -488,7 +505,8 @@
mHistoricalPrinters.remove(0);
}
mHistoricalPrinters.add(printer);
- mWriteTask.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, mHistoricalPrinters);
+ mWriteTask.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR,
+ new ArrayList<PrinterInfo>(mHistoricalPrinters));
}
private List<PrinterInfo> computeFavoritePrinters(List<PrinterInfo> printers) {
diff --git a/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java b/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java
index 520331cb..5361a1e 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java
@@ -817,6 +817,10 @@
}
if (id == DEST_ADAPTER_ITEM_ID_ALL_PRINTERS) {
+ // The selection changed to the all printers item. We
+ // want to select back the last selected printer.
+ mIgnoreNextDestinationChange = true;
+ mEditor.selectPrinter(mCurrentPrinter.getId());
startSelectPrinterActivity();
return;
}
@@ -1024,7 +1028,7 @@
mDestinationSpinnerAdapter.registerDataSetObserver(new DataSetObserver() {
@Override
public void onChanged() {
- // Initially, we have only sage to PDF as a printer but after some
+ // Initially, we have only safe to PDF as a printer but after some
// printers are loaded we want to select the user's favorite one
// which is the first.
if (!mFavoritePrinterSelected && mDestinationSpinnerAdapter.getCount() > 2) {
@@ -1044,6 +1048,15 @@
continue;
}
+ // If the current printer became available and has no
+ // capabilities, we refresh it.
+ if (mCurrentPrinter.getStatus() == PrinterInfo.STATUS_UNAVAILABLE
+ && printer.getStatus() != PrinterInfo.STATUS_UNAVAILABLE
+ && printer.getCapabilities() == null) {
+ refreshCurrentPrinter();
+ return;
+ }
+
// Update the UI if capabilities changed.
boolean capabilitiesChanged = false;
@@ -1127,6 +1140,18 @@
}
}
+ public void addCurrentPrinterToHistory() {
+ PrinterInfo printer = (PrinterInfo) mDestinationSpinner.getSelectedItem();
+ if (printer != null) {
+ FusedPrintersProvider printersLoader = (FusedPrintersProvider)
+ (Loader<?>) getLoaderManager().getLoader(
+ LOADER_ID_PRINTERS_LOADER);
+ if (printersLoader != null) {
+ printersLoader.addHistoricalPrinter(printer);
+ }
+ }
+ }
+
public void selectPrinter(PrinterId printerId) {
mDestinationSpinnerAdapter.ensurePrinterShownPrinterShown(printerId);
final int position = mDestinationSpinnerAdapter.getPrinterIndex(printerId);
@@ -1348,6 +1373,7 @@
}
public void confirmPrint() {
+ addCurrentPrinterToHistory();
mEditorState = EDITOR_STATE_CONFIRMED_PRINT;
showUi(UI_GENERATING_PRINT_JOB, null);
}
@@ -1772,7 +1798,6 @@
mPageRangeEditText.setVisibility(View.INVISIBLE);
mPageRangeTitle.setVisibility(View.INVISIBLE);
}
- mRangeOptionsSpinner.setEnabled(true);
// Print/Print preview
if (mDestinationSpinner.getSelectedItemId()
@@ -1871,8 +1896,6 @@
}
private void startSelectPrinterActivity() {
- mIgnoreNextDestinationChange = true;
- mDestinationSpinner.setSelection(0);
Intent intent = new Intent(PrintJobConfigActivity.this,
SelectPrinterActivity.class);
startActivityForResult(intent, ACTIVITY_REQUEST_SELECT_PRINTER);
@@ -1959,6 +1982,9 @@
if (position == 0) {
return DEST_ADAPTER_ITEM_ID_SAVE_AS_PDF;
}
+ if (position == 1) {
+ return DEST_ADAPTER_ITEM_ID_ALL_PRINTERS;
+ }
} else {
if (position == 1) {
return DEST_ADAPTER_ITEM_ID_SAVE_AS_PDF;
@@ -2059,7 +2085,7 @@
PrinterInfo printer = (PrinterInfo) mPrinters.get(i);
if (printer.getId().equals(mLastShownPrinterId)) {
// If already in the list - do nothing.
- if (i < getCount() - 1) {
+ if (i < getCount() - 2) {
return;
}
// Else replace the last one.
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 6753922..a5dab33 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -33,6 +33,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
import android.content.res.AssetFileDescriptor;
import android.database.AbstractCursor;
import android.database.Cursor;
@@ -477,6 +478,13 @@
try {
final String value = c.moveToNext() ? c.getString(0) : null;
if (value == null) {
+ // sanity-check the user before touching the db
+ final UserInfo user = mUserManager.getUserInfo(userHandle);
+ if (user == null) {
+ // can happen due to races when deleting users; treat as benign
+ return false;
+ }
+
final SecureRandom random = new SecureRandom();
final String newAndroidIdValue = Long.toHexString(random.nextLong());
final ContentValues values = new ContentValues();
@@ -490,7 +498,7 @@
Slog.d(TAG, "Generated and saved new ANDROID_ID [" + newAndroidIdValue
+ "] for user " + userHandle);
// Write a dropbox entry if it's a restricted profile
- if (mUserManager.getUserInfo(userHandle).isRestricted()) {
+ if (user.isRestricted()) {
DropBoxManager dbm = (DropBoxManager)
getContext().getSystemService(Context.DROPBOX_SERVICE);
if (dbm != null && dbm.isTagEnabled(DROPBOX_TAG_USERLOG)) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index e32d7f2..ff85cb4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -70,7 +70,6 @@
import com.android.systemui.RecentsComponent;
import com.android.systemui.SearchPanelView;
import com.android.systemui.SystemUI;
-import com.android.systemui.statusbar.phone.BarTransitions;
import com.android.systemui.statusbar.policy.NotificationRowLayout;
import java.util.ArrayList;
@@ -828,7 +827,7 @@
// Construct the icon.
final StatusBarIconView iconView = new StatusBarIconView(mContext,
notification.getPackageName() + "/0x" + Integer.toHexString(notification.getId()),
- notification.getNotification(), getStatusBarMode());
+ notification.getNotification());
iconView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
final StatusBarIcon ic = new StatusBarIcon(notification.getPackageName(),
@@ -851,10 +850,6 @@
return entry;
}
- protected int getStatusBarMode() {
- return BarTransitions.MODE_OPAQUE;
- }
-
protected void addNotificationViews(NotificationData.Entry entry) {
// Add the expanded view and icon.
int pos = mNotificationData.add(entry);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
index 5689bfa..9f9524b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
@@ -16,11 +16,6 @@
package com.android.systemui.statusbar;
-import static com.android.systemui.statusbar.phone.BarTransitions.MODE_OPAQUE;
-import static com.android.systemui.statusbar.phone.BarTransitions.MODE_SEMI_TRANSPARENT;
-import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSPARENT;
-
-import android.animation.ObjectAnimator;
import android.app.Notification;
import android.content.Context;
import android.content.pm.PackageManager;
@@ -53,13 +48,10 @@
private int mNumberY;
private String mNumberText;
private Notification mNotification;
- private final float mAlphaWhenOpaque;
- private final float mAlphaWhenTransparent = 1;
- public StatusBarIconView(Context context, String slot, Notification notification, int mode) {
+ public StatusBarIconView(Context context, String slot, Notification notification) {
super(context);
final Resources res = context.getResources();
- mAlphaWhenOpaque = res.getFraction(R.dimen.status_bar_icon_drawing_alpha, 1, 1);
mSlot = slot;
mNumberPain = new Paint();
mNumberPain.setTextAlign(Paint.Align.CENTER);
@@ -76,7 +68,6 @@
final float scale = (float)imageBounds / (float)outerBounds;
setScaleX(scale);
setScaleY(scale);
- setAlpha(getAlphaFor(mode));
}
setScaleType(ImageView.ScaleType.CENTER);
@@ -85,22 +76,11 @@
public StatusBarIconView(Context context, AttributeSet attrs) {
super(context, attrs);
final Resources res = context.getResources();
- mAlphaWhenOpaque = res.getFraction(R.dimen.status_bar_icon_drawing_alpha, 1, 1);
final int outerBounds = res.getDimensionPixelSize(R.dimen.status_bar_icon_size);
final int imageBounds = res.getDimensionPixelSize(R.dimen.status_bar_icon_drawing_size);
final float scale = (float)imageBounds / (float)outerBounds;
setScaleX(scale);
setScaleY(scale);
- setAlpha(getAlphaFor(MODE_OPAQUE));
- }
-
- public ObjectAnimator animateTransitionTo(int mode) {
- return ObjectAnimator.ofFloat(this, "alpha", getAlpha(), getAlphaFor(mode));
- }
-
- public float getAlphaFor(int mode) {
- final boolean isTransparent = mode == MODE_SEMI_TRANSPARENT || mode == MODE_TRANSPARENT;
- return isTransparent ? mAlphaWhenTransparent : mAlphaWhenOpaque;
}
private static boolean streq(String a, String b) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 870202a..bde3795 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -839,7 +839,7 @@
public void addIcon(String slot, int index, int viewIndex, StatusBarIcon icon) {
if (SPEW) Log.d(TAG, "addIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex
+ " icon=" + icon);
- StatusBarIconView view = new StatusBarIconView(mContext, slot, null, getStatusBarMode());
+ StatusBarIconView view = new StatusBarIconView(mContext, slot, null);
view.set(icon);
mStatusIcons.addView(view, viewIndex, new LinearLayout.LayoutParams(mIconSize, mIconSize));
}
@@ -1741,7 +1741,9 @@
mGestureRec.add(event);
}
- setInteracting(true);
+ if (mStatusBarWindowState == WINDOW_STATE_SHOWING) {
+ setInteracting(true);
+ }
return false;
}
@@ -1769,7 +1771,6 @@
&& mStatusBarWindowState != state) {
mStatusBarWindowState = state;
if (DEBUG_WINDOW_STATE) Log.d(TAG, "Status bar " + windowStateToString(state));
- mStatusBarWindow.setEnabled(showing);
if (!showing) {
mStatusBarView.collapseAllPanels(false);
}
@@ -1779,7 +1780,6 @@
&& mNavigationBarWindowState != state) {
mNavigationBarWindowState = state;
if (DEBUG_WINDOW_STATE) Log.d(TAG, "Navigation bar " + windowStateToString(state));
- mNavigationBarView.setEnabled(showing);
}
}
@@ -1857,11 +1857,6 @@
}
}
- @Override
- protected int getStatusBarMode() {
- return mStatusBarView.getBarTransitions().getMode();
- }
-
private int computeBarMode(int oldVis, int newVis, BarTransitions transitions,
int transientFlag, int transparentFlag) {
final int oldMode = barMode(oldVis, transientFlag, transparentFlag);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index 2e9ee87..fa494c2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -16,8 +16,12 @@
package com.android.systemui.statusbar.phone;
+import static com.android.systemui.statusbar.phone.BarTransitions.MODE_SEMI_TRANSPARENT;
+import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSPARENT;
+
import android.animation.Animator;
import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
import android.app.ActivityManager;
import android.content.Context;
import android.content.res.Resources;
@@ -27,15 +31,10 @@
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
-import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
import com.android.systemui.EventLogTags;
import com.android.systemui.R;
-import com.android.systemui.statusbar.StatusBarIconView;
-
-import java.util.ArrayList;
-import java.util.List;
public class PhoneStatusBarView extends PanelBar {
private static final String TAG = "PhoneStatusBarView";
@@ -56,11 +55,22 @@
private final class StatusBarTransitions extends BarTransitions {
private final int mTransparent;
+ private final float mAlphaWhenOpaque;
+ private final float mAlphaWhenTransparent = 1;
+ private View mLeftSide;
+ private View mRightSide;
public StatusBarTransitions(Context context) {
super(context, PhoneStatusBarView.this);
final Resources res = context.getResources();
mTransparent = res.getColor(R.color.status_bar_background_transparent);
+ mAlphaWhenOpaque = res.getFraction(R.dimen.status_bar_icon_drawing_alpha, 1, 1);
+ }
+
+ public void init() {
+ mLeftSide = findViewById(R.id.notification_icon_area);
+ mRightSide = findViewById(R.id.system_icon_area);
+ applyMode(getMode(), false /*animate*/);
}
@Override
@@ -69,41 +79,32 @@
return super.getBackgroundColor(mode);
}
+ public ObjectAnimator animateTransitionTo(View v, float toAlpha) {
+ return ObjectAnimator.ofFloat(v, "alpha", v.getAlpha(), toAlpha);
+ }
+
+ public float getAlphaFor(int mode) {
+ final boolean isTransparent = mode == MODE_SEMI_TRANSPARENT || mode == MODE_TRANSPARENT;
+ return isTransparent ? mAlphaWhenTransparent : mAlphaWhenOpaque;
+ }
+
@Override
protected void onTransition(int oldMode, int newMode, boolean animate) {
super.onTransition(oldMode, newMode, animate);
+ applyMode(newMode, animate);
+ }
+
+ private void applyMode(int mode, boolean animate) {
+ float newAlpha = getAlphaFor(mode);
if (animate) {
- List<Animator> animators = new ArrayList<Animator>();
- for(StatusBarIconView icon : findStatusBarIcons()) {
- animators.add(icon.animateTransitionTo(newMode));
- }
+ ObjectAnimator lhs = animateTransitionTo(mLeftSide, newAlpha);
+ ObjectAnimator rhs = animateTransitionTo(mRightSide, newAlpha);
AnimatorSet set = new AnimatorSet();
- set.playTogether(animators);
+ set.playTogether(lhs, rhs);
set.start();
} else {
- for(StatusBarIconView icon : findStatusBarIcons()) {
- icon.setAlpha(icon.getAlphaFor(newMode));
- }
- }
- }
-
- private List<StatusBarIconView> findStatusBarIcons() {
- List<StatusBarIconView> icons = new ArrayList<StatusBarIconView>();
- findStatusBarIcons(icons, findViewById(R.id.moreIcon));
- findStatusBarIcons(icons, findViewById(R.id.statusIcons));
- findStatusBarIcons(icons, findViewById(R.id.notificationIcons));
- return icons;
- }
-
- private void findStatusBarIcons(List<StatusBarIconView> icons, View v) {
- if (v instanceof StatusBarIconView) {
- icons.add((StatusBarIconView) v);
- } else if (v instanceof ViewGroup) {
- ViewGroup group = (ViewGroup) v;
- final int N = group.getChildCount();
- for (int i = 0; i < N; i++) {
- findStatusBarIcons(icons, group.getChildAt(i));
- }
+ mLeftSide.setAlpha(newAlpha);
+ mRightSide.setAlpha(newAlpha);
}
}
}
@@ -140,6 +141,7 @@
for (PanelView pv : mPanels) {
pv.setRubberbandingEnabled(!mFullWidthNotifications);
}
+ mBarTransitions.init();
}
@Override
diff --git a/services/java/com/android/server/accessibility/TouchExplorer.java b/services/java/com/android/server/accessibility/TouchExplorer.java
index 8fb3998..1b8876d 100644
--- a/services/java/com/android/server/accessibility/TouchExplorer.java
+++ b/services/java/com/android/server/accessibility/TouchExplorer.java
@@ -417,9 +417,6 @@
mSendTouchInteractionEndDelayed.forceSendAndRemove();
}
- // Cache the event until we discern exploration from gesturing.
- mSendHoverEnterAndMoveDelayed.addEvent(event);
-
// If we have the first tap, schedule a long press and break
// since we do not want to schedule hover enter because
// the delayed callback will kick in before the long click.
@@ -432,11 +429,16 @@
break;
}
if (!mTouchExplorationInProgress) {
- // Deliver hover enter with a delay to have a chance
- // to detect what the user is trying to do.
- final int pointerId = receivedTracker.getPrimaryPointerId();
- final int pointerIdBits = (1 << pointerId);
- mSendHoverEnterAndMoveDelayed.post(event, true, pointerIdBits, policyFlags);
+ if (!mSendHoverEnterAndMoveDelayed.isPending()) {
+ // Deliver hover enter with a delay to have a chance
+ // to detect what the user is trying to do.
+ final int pointerId = receivedTracker.getPrimaryPointerId();
+ final int pointerIdBits = (1 << pointerId);
+ mSendHoverEnterAndMoveDelayed.post(event, true, pointerIdBits,
+ policyFlags);
+ }
+ // Cache the event until we discern exploration from gesturing.
+ mSendHoverEnterAndMoveDelayed.addEvent(event);
}
} break;
case MotionEvent.ACTION_POINTER_DOWN: {
@@ -1719,7 +1721,7 @@
} break;
}
if (DEBUG) {
- Slog.i(LOG_TAG_RECEIVED_POINTER_TRACKER, "Received pointer: " + toString());
+ Slog.i(LOG_TAG_RECEIVED_POINTER_TRACKER, "Received pointer:\n" + toString());
}
}
@@ -1777,7 +1779,7 @@
*/
public int getPrimaryPointerId() {
if (mPrimaryPointerId == INVALID_POINTER_ID) {
- mPrimaryPointerId = findPrimaryPointer();
+ mPrimaryPointerId = findPrimaryPointerId();
}
return mPrimaryPointerId;
}
@@ -1861,17 +1863,21 @@
}
/**
- * @return The primary pointer.
+ * @return The primary pointer id.
*/
- private int findPrimaryPointer() {
+ private int findPrimaryPointerId() {
int primaryPointerId = INVALID_POINTER_ID;
long minDownTime = Long.MAX_VALUE;
+
// Find the pointer that went down first.
- for (int i = 0, count = mReceivedPointerDownTime.length; i < count; i++) {
- final long downPointerTime = mReceivedPointerDownTime[i];
+ int pointerIdBits = mReceivedPointersDown;
+ while (pointerIdBits > 0) {
+ final int pointerId = Integer.numberOfTrailingZeros(pointerIdBits);
+ pointerIdBits &= ~(1 << pointerId);
+ final long downPointerTime = mReceivedPointerDownTime[pointerId];
if (downPointerTime < minDownTime) {
minDownTime = downPointerTime;
- primaryPointerId = i;
+ primaryPointerId = pointerId;
}
}
return primaryPointerId;
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 4678b85..1813593 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -6517,8 +6517,11 @@
}
}
final long origId = Binder.clearCallingIdentity();
- stack.moveTaskToBackLocked(taskId, null);
- Binder.restoreCallingIdentity(origId);
+ try {
+ stack.moveTaskToBackLocked(taskId, null);
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
}
}
}
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index 2fefadf..8d27c5c 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -255,7 +255,7 @@
//public Handler() {
// if (localLOGV) Slog.v(TAG, "Handler started!");
//}
- public ActivityStackHandler(Looper looper) {
+ ActivityStackHandler(Looper looper) {
super(looper);
}
@@ -331,7 +331,6 @@
mWindowManager = service.mWindowManager;
mStackSupervisor = service.mStackSupervisor;
mContext = context;
- PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
mStackId = stackId;
mCurrentUser = service.mCurrentUserId;
}
@@ -837,7 +836,7 @@
}
}
- private final void completePauseLocked() {
+ private void completePauseLocked() {
ActivityRecord prev = mPausingActivity;
if (DEBUG_PAUSE) Slog.v(TAG, "Complete pause: " + prev);
@@ -1765,7 +1764,7 @@
for (int taskNdx = 0; taskNdx < numTasks; ++taskNdx) {
TaskRecord task = mTaskHistory.get(taskNdx);
final ArrayList<ActivityRecord> activities = task.mActivities;
- if (activities.size() == 0) {
+ if (activities.isEmpty()) {
continue;
}
TaskGroup group = new TaskGroup();
@@ -2335,7 +2334,7 @@
finishActivityResultsLocked(r, resultCode, resultData);
- if (mService.mPendingThumbnails.size() > 0) {
+ if (!mService.mPendingThumbnails.isEmpty()) {
// There are clients waiting to receive thumbnails so, in case
// this is an activity that someone is waiting for, add it
// to the pending list so we can correctly update the clients.
@@ -2561,7 +2560,7 @@
cleanUpActivityServicesLocked(r);
}
- if (mService.mPendingThumbnails.size() > 0) {
+ if (!mService.mPendingThumbnails.isEmpty()) {
// There are clients waiting to receive thumbnails so, in case
// this is an activity that someone is waiting for, add it
// to the pending list so we can correctly update the clients.
@@ -2698,7 +2697,7 @@
mService.mHandler.sendEmptyMessage(
ActivityManagerService.CANCEL_HEAVY_NOTIFICATION_MSG);
}
- if (r.app.activities.size() == 0) {
+ if (r.app.activities.isEmpty()) {
// No longer have activities, so update oom adj.
mService.updateOomAdjLocked();
}
@@ -3009,9 +3008,38 @@
if (tr == null) {
return false;
}
+
mTaskHistory.remove(tr);
mTaskHistory.add(0, tr);
+ // There is an assumption that moving a task to the back moves it behind the home activity.
+ // We make sure here that some activity in the stack will launch home.
+ ActivityRecord lastActivity = null;
+ int numTasks = mTaskHistory.size();
+ int taskNdx;
+ for (taskNdx = numTasks - 1; taskNdx >= 1; --taskNdx) {
+ final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
+ int activityNdx;
+ for (activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
+ final ActivityRecord r = activities.get(activityNdx);
+ if (r.mLaunchHomeTaskNext) {
+ break;
+ }
+ if (taskNdx == 1 && activityNdx == 0) {
+ // Final activity before tr task.
+ lastActivity = r;
+ }
+ }
+ if (activityNdx >= 0) {
+ // Early exit, we found an activity that will launchHomeTaskNext.
+ break;
+ }
+ }
+ if (lastActivity != null) {
+ // No early exit, we did not find an activity that will launchHomeTaskNext, set one.
+ lastActivity.mLaunchHomeTaskNext = true;
+ }
+
if (reason != null &&
(reason.intent.getFlags() & Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) {
mWindowManager.prepareAppTransition(AppTransition.TRANSIT_NONE, false);
@@ -3020,8 +3048,7 @@
mNoAnimActivities.add(r);
}
} else {
- mWindowManager.prepareAppTransition(
- AppTransition.TRANSIT_TASK_TO_BACK, false);
+ mWindowManager.prepareAppTransition(AppTransition.TRANSIT_TASK_TO_BACK, false);
}
mWindowManager.moveTaskToBottom(task);
@@ -3029,9 +3056,8 @@
validateAppTokensLocked();
}
- if (mResumedActivity != null && mResumedActivity.task == tr &&
- mResumedActivity.mLaunchHomeTaskNext) {
- // TODO: Can we skip the next line and just pass mResumedAct. to resumeHomeAct.()?
+ if (numTasks <= 1 || (mResumedActivity != null && mResumedActivity.task == tr &&
+ mResumedActivity.mLaunchHomeTaskNext)) {
mResumedActivity.mLaunchHomeTaskNext = false;
return mStackSupervisor.resumeHomeActivity(null);
}
@@ -3175,7 +3201,7 @@
return true;
}
- private final boolean relaunchActivityLocked(ActivityRecord r,
+ private boolean relaunchActivityLocked(ActivityRecord r,
int changes, boolean andResume) {
List<ResultInfo> results = null;
List<Intent> newIntents = null;
@@ -3487,7 +3513,7 @@
boolean removeTask(TaskRecord task) {
mTaskHistory.remove(task);
- return mTaskHistory.size() == 0;
+ return mTaskHistory.isEmpty();
}
TaskRecord createTaskRecord(int taskId, ActivityInfo info, Intent intent, boolean toTop) {
diff --git a/services/java/com/android/server/pm/Settings.java b/services/java/com/android/server/pm/Settings.java
index ff1128d..415cda1 100644
--- a/services/java/com/android/server/pm/Settings.java
+++ b/services/java/com/android/server/pm/Settings.java
@@ -1385,6 +1385,11 @@
StringBuilder sb = new StringBuilder();
for (final PackageSetting pkg : mPackages.values()) {
+ if (pkg.pkg == null || pkg.pkg.applicationInfo == null) {
+ Slog.w(TAG, "Skipping " + pkg + " due to missing metadata");
+ continue;
+ }
+
final ApplicationInfo ai = pkg.pkg.applicationInfo;
final String dataPath = ai.dataDir;
final boolean isDebug = (ai.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
diff --git a/services/java/com/android/server/print/RemotePrintService.java b/services/java/com/android/server/print/RemotePrintService.java
index 14af9d8..3c67aa9 100644
--- a/services/java/com/android/server/print/RemotePrintService.java
+++ b/services/java/com/android/server/print/RemotePrintService.java
@@ -163,7 +163,7 @@
if (isBound()) {
if (DEBUG) {
- Slog.i(LOG_TAG, "[user: " + mUserId + "] handleOnAllPrintJobsHandled()");
+ Slog.i(LOG_TAG, "[user: " + mUserId + "] onAllPrintJobsHandled()");
}
// If the service has a printer discovery session
@@ -185,7 +185,7 @@
// which means that there are no print jobs to be cancelled.
if (isBound()) {
if (DEBUG) {
- Slog.i(LOG_TAG, "[user: " + mUserId + "] handleRequestCancelPrintJob()");
+ Slog.i(LOG_TAG, "[user: " + mUserId + "] requestCancelPrintJob()");
}
try {
mPrintService.requestCancelPrintJob(printJob);
@@ -215,7 +215,7 @@
});
} else {
if (DEBUG) {
- Slog.i(LOG_TAG, "[user: " + mUserId + "] handleOnPrintJobQueued()");
+ Slog.i(LOG_TAG, "[user: " + mUserId + "] onPrintJobQueued()");
}
try {
mPrintService.onPrintJobQueued(printJob);
@@ -358,7 +358,7 @@
});
} else {
if (DEBUG) {
- Slog.i(LOG_TAG, "[user: " + mUserId + "] handleValidatePrinters()");
+ Slog.i(LOG_TAG, "[user: " + mUserId + "] validatePrinters()");
}
try {
mPrintService.validatePrinters(printerIds);
@@ -385,7 +385,7 @@
});
} else {
if (DEBUG) {
- Slog.i(LOG_TAG, "[user: " + mUserId + "] handleStartPrinterTracking()");
+ Slog.i(LOG_TAG, "[user: " + mUserId + "] startPrinterTracking()");
}
try {
mPrintService.startPrinterStateTracking(printerId);
@@ -412,7 +412,7 @@
});
} else {
if (DEBUG) {
- Slog.i(LOG_TAG, "[user: " + mUserId + "] handleStopPrinterTracking()");
+ Slog.i(LOG_TAG, "[user: " + mUserId + "] stopPrinterTracking()");
}
try {
mPrintService.stopPrinterStateTracking(printerId);
diff --git a/services/java/com/android/server/print/UserState.java b/services/java/com/android/server/print/UserState.java
index 7d94a42..b9c676d 100644
--- a/services/java/com/android/server/print/UserState.java
+++ b/services/java/com/android/server/print/UserState.java
@@ -62,7 +62,7 @@
private static final boolean DEBUG = true && Build.IS_DEBUGGABLE;
- private static final int MAX_ITEMS_PER_CALLBACK = 100;
+ private static final int MAX_ITEMS_PER_CALLBACK = 50;
private static final char COMPONENT_NAME_SEPARATOR = ':';
@@ -576,10 +576,12 @@
// Remember we got a start request to match with an end.
mStartedPrinterDiscoveryTokens.add(observer.asBinder());
+
// The service are already performing discovery - nothing to do.
if (mStartedPrinterDiscoveryTokens.size() > 1) {
return;
}
+
List<RemotePrintService> services = new ArrayList<RemotePrintService>(
mActiveServices.values());
SomeArgs args = SomeArgs.obtain();
@@ -858,11 +860,7 @@
final int observerCount = mDiscoveryObservers.beginBroadcast();
for (int i = 0; i < observerCount; i++) {
IPrinterDiscoveryObserver observer = mDiscoveryObservers.getBroadcastItem(i);
- try {
- observer.onPrintersAdded(addedPrinters);
- } catch (RemoteException re) {
- Log.i(LOG_TAG, "Error dispatching added printers", re);
- }
+ handlePrintersAdded(observer, addedPrinters);
}
mDiscoveryObservers.finishBroadcast();
}
@@ -871,11 +869,7 @@
final int observerCount = mDiscoveryObservers.beginBroadcast();
for (int i = 0; i < observerCount; i++) {
IPrinterDiscoveryObserver observer = mDiscoveryObservers.getBroadcastItem(i);
- try {
- observer.onPrintersRemoved(removedPrinterIds);
- } catch (RemoteException re) {
- Log.i(LOG_TAG, "Error dispatching removed printers", re);
- }
+ handlePrintersRemoved(observer, removedPrinterIds);
}
mDiscoveryObservers.finishBroadcast();
}
@@ -884,11 +878,7 @@
final int observerCount = mDiscoveryObservers.beginBroadcast();
for (int i = 0; i < observerCount; i++) {
IPrinterDiscoveryObserver observer = mDiscoveryObservers.getBroadcastItem(i);
- try {
- observer.onPrintersUpdated(updatedPrinters);
- } catch (RemoteException re) {
- Log.i(LOG_TAG, "Error dispatching updated printers", re);
- }
+ handlePrintersUpdated(observer, updatedPrinters);
}
mDiscoveryObservers.finishBroadcast();
}
@@ -957,7 +947,7 @@
final int start = i * MAX_ITEMS_PER_CALLBACK;
final int end = Math.min(start + MAX_ITEMS_PER_CALLBACK, printerCount);
List<PrinterInfo> subPrinters = printers.subList(start, end);
- observer.onPrintersAdded(subPrinters);
+ observer.onPrintersAdded(subPrinters);
}
}
} catch (RemoteException re) {
@@ -986,6 +976,27 @@
}
}
+ private void handlePrintersUpdated(IPrinterDiscoveryObserver observer,
+ List<PrinterInfo> updatedPrinters) {
+ try {
+ final int printerCount = updatedPrinters.size();
+ if (printerCount <= MAX_ITEMS_PER_CALLBACK) {
+ observer.onPrintersUpdated(updatedPrinters);
+ } else {
+ // Send the added printers in chunks avoiding the binder transaction limit.
+ final int transactionCount = (printerCount / MAX_ITEMS_PER_CALLBACK) + 1;
+ for (int i = 0; i < transactionCount; i++) {
+ final int start = i * MAX_ITEMS_PER_CALLBACK;
+ final int end = Math.min(start + MAX_ITEMS_PER_CALLBACK, printerCount);
+ List<PrinterInfo> subPrinters = updatedPrinters.subList(start, end);
+ observer.onPrintersUpdated(subPrinters);
+ }
+ }
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error sending updated printers", re);
+ }
+ }
+
private final class SessionHandler extends Handler {
public static final int MSG_PRINTERS_ADDED = 1;
public static final int MSG_PRINTERS_REMOVED = 2;