Secure random number generation for all Android versions - android

I am currently playing around with the idea of an application for android which involves encryption. I am planning to use aes in ctr mode and PBKDF2 with whirlpool for key stretching.
I am going to implement a new bouncy castle implementation instead of androids built in old implementation. To make sure it works as intended on any android version.
But I am having some problem figuring out a solid way to generate random numbers for salt and key. I have read somewhere that the built in secure random in android is insecure in some old android versions and I have also heard that most android phones have a hard time keeping a high entropy in dev/random and blocks often. Shouldn’t that have a huge impact on the security of dev/urandom?
I am therefore looking for good ways to use the sensors on the phone to gather more entropy.

The following classes should help you alleviate issues with the Android SecureRandom class. This code was created instead of a text because otherwise the small details.
/**
* A strengthener that can be used to generate and re-seed random number
* generators that do not seed themselves appropriately.
*
* #author owlstead
*/
public class SecureRandomStrengthener {
private static final String DEFAULT_PSEUDO_RANDOM_NUMBER_GENERATOR = "SHA1PRNG";
private static final EntropySource TIME_ENTROPY_SOURCE = new EntropySource() {
final ByteBuffer timeBuffer = ByteBuffer.allocate(Long.SIZE / Byte.SIZE
* 2);
#Override
public ByteBuffer provideEntropy() {
this.timeBuffer.clear();
this.timeBuffer.putLong(System.currentTimeMillis());
this.timeBuffer.putLong(System.nanoTime());
this.timeBuffer.flip();
return this.timeBuffer;
}
};
private final String algorithm;
private final List<EntropySource> entropySources = new LinkedList<EntropySource>();
private final MessageDigest digest;
private final ByteBuffer seedBuffer;
/**
* Generates an instance of a {#link SecureRandomStrengthener} that
* generates and re-seeds instances of {#code "SHA1PRNG"}.
*
* #return the strengthener, never null
*/
public static SecureRandomStrengthener getInstance() {
return new SecureRandomStrengthener(
DEFAULT_PSEUDO_RANDOM_NUMBER_GENERATOR);
}
/**
* Generates an instance of a {#link SecureRandomStrengthener} that
* generates instances of the given argument. Note that the availability of
* the given algorithm arguments in not tested until generation.
*
* #param algorithm
* the algorithm indicating the {#link SecureRandom} instance to
* use
* #return the strengthener, never null
*/
public static SecureRandomStrengthener getInstance(final String algorithm) {
return new SecureRandomStrengthener(algorithm);
}
private SecureRandomStrengthener(final String algorithm) {
if (algorithm == null || algorithm.length() == 0) {
throw new IllegalArgumentException(
"Please provide a PRNG algorithm string such as SHA1PRNG");
}
this.algorithm = algorithm;
try {
this.digest = MessageDigest.getInstance("SHA1");
} catch (final NoSuchAlgorithmException e) {
throw new IllegalStateException(
"MessageDigest to create seed not available", e);
}
this.seedBuffer = ByteBuffer.allocate(this.digest.getDigestLength());
}
/**
* Add an entropy source, which will be called for each generation and
* re-seeding of the given random number generator.
*
* #param source
* the source of entropy
*/
public void addEntropySource(final EntropySource source) {
if (source == null) {
throw new IllegalArgumentException(
"EntropySource should not be null");
}
this.entropySources.add(source);
}
/**
* Generates and seeds a random number generator of the configured
* algorithm. Calls the {#link EntropySource#provideEntropy()} method of all
* added sources of entropy.
*
* #return the random number generator
*/
public SecureRandom generateAndSeedRandomNumberGenerator() {
final SecureRandom secureRandom;
try {
secureRandom = SecureRandom.getInstance(this.algorithm);
} catch (final NoSuchAlgorithmException e) {
throw new IllegalStateException("PRNG is not available", e);
}
reseed(secureRandom);
return secureRandom;
}
/**
* Re-seeds the random number generator. Calls the
* {#link EntropySource#provideEntropy()} method of all added sources of
* entropy.
*
* #param secureRandom
* the random number generator to re-seed
*/
public void reseed(final SecureRandom secureRandom) {
this.seedBuffer.clear();
secureRandom.nextBytes(this.seedBuffer.array());
for (final EntropySource source : this.entropySources) {
final ByteBuffer entropy = source.provideEntropy();
if (entropy == null) {
continue;
}
final ByteBuffer wipeBuffer = entropy.duplicate();
this.digest.update(entropy);
wipe(wipeBuffer);
}
this.digest.update(TIME_ENTROPY_SOURCE.provideEntropy());
this.digest.update(this.seedBuffer);
this.seedBuffer.clear();
// remove data from seedBuffer so it won't be retrievable
// reuse
try {
this.digest.digest(this.seedBuffer.array(), 0,
this.seedBuffer.capacity());
} catch (final DigestException e) {
throw new IllegalStateException(
"DigestException should not be thrown", e);
}
secureRandom.setSeed(this.seedBuffer.array());
wipe(this.seedBuffer);
}
private void wipe(final ByteBuffer buf) {
while (buf.hasRemaining()) {
buf.put((byte) 0);
}
}
}
And this is the small interface that is EntropySource:
/**
* A simple interface that can be used to retrieve entropy from any source.
*
* #author owlstead
*/
public interface EntropySource {
/**
* Retrieves the entropy.
* The position of the ByteBuffer must be advanced to the limit by any users calling this method.
* The values of the bytes between the position and limit should be set to zero by any users calling this method.
*
* #return entropy within the position and limit of the given buffer
*/
ByteBuffer provideEntropy();
}
Note that the output of the classes has not been tested for randomness (but this relies mainly on the returned SecureRandom class and should therefore be fine).
Finally, as I don't have the Android 1.6 runtime ready, somebody should test it against this or a lower version for compatibility (!).

Related

Decryption much slower compared to encryption on Android

I have an implementation of 'AES' encryption and decryption with 'CBC' mode and 'PKCS5Padding' padding in Kotlin. I noticed that while decrypting cipherInputStream.read(buffer) reads only 512 bytes at a time instead of the full buffer size which is 8192 bytes. Why is that? While encrypting it uses whole buffer.
These are the constants that I am using,
private val TRANSFORMATION = "AES/CBC/PKCS5Padding"
private var SECRET_KEY_FAC_ALGORITHM = "PBKDF2WithHmacSHA1"
private val SECRET_KEY_SPEC_ALGORITHM = "AES"
private val cipher = Cipher.getInstance(TRANSFORMATION)
private val random = SecureRandom()
private val KEY_BITS_LENGTH = 256
private val IV_BYTES_LENGTH = cipher.blockSize
private val SALT_BYTES_LENGTH = KEY_BITS_LENGTH / 8
private val ITERATIONS = 10000
Decryption code
cis = CipherInputStream(input, cipher)
val buffer = ByteArray(8192)
var read = cis.read(buffer)
while (read > -1) {
fos.write(buffer, 0, read)
read = cis.read(buffer)
}
Encryption code
fos.write(iv)
fos.write(salt)
cos = CipherOutputStream(fos, cipher)
val buffer = ByteArray(8192)
var read = input.read(buffer)
while (read > -1) {
cos.write(buffer, 0, read)
read = input.read(buffer)
}
Recently I had a similar issue.
The problem was internal buffer of CipherInputStream class which is defined as follows
private byte[] ibuffer = new byte[512];
What significantly improved decryption speed was increasing this buffer size to 8192. So I've just copy pasted original CipherInputStream class to my own class and modified buffer size.
What is funny is the comment above this ibuffer field.
the size 512 bytes is somewhat randomly chosen */
Hope it helped
I just implemented the class by changing the size of the length of ibuffer. (Copy paste with the changed value only)
import java.io.IOException;
import java.io.InputStream;
import javax.crypto.AEADBadTagException;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NullCipher;
import javax.crypto.ShortBufferException;
public class FasterCipherInputStream extends CipherInputStream {
private static final String TAG = "FasterCipherInputStream";
private static final int BUFFER_SIZE = 20971520;
// the cipher engine to use to process stream data
private final Cipher cipher;
// the underlying input stream
private final InputStream input;
/* the buffer holding data that have been read in from the
underlying stream, but have not been processed by the cipher
engine. the size 512 bytes is somewhat randomly chosen */
private final byte[] ibuffer = new byte[BUFFER_SIZE];
// having reached the end of the underlying input stream
private boolean done = false;
/* the buffer holding data that have been processed by the cipher
engine, but have not been read out */
private byte[] obuffer;
// the offset pointing to the next "new" byte
private int ostart = 0;
// the offset pointing to the last "new" byte
private int ofinish = 0;
// stream status
private boolean closed = false;
/**
* private convenience function.
*
* Entry condition: ostart = ofinish
*
* Exit condition: ostart <= ofinish
*
* return (ofinish-ostart) (we have this many bytes for you)
* return 0 (no data now, but could have more later)
* return -1 (absolutely no more data)
*
* Note: Exceptions are only thrown after the stream is completely read.
* For AEAD ciphers a read() of any length will internally cause the
* whole stream to be read fully and verify the authentication tag before
* returning decrypted data or exceptions.
*/
private int getMoreData() throws IOException {
// Android-changed: The method was creating a new object every time update(byte[], int, int)
// or doFinal() was called resulting in the old object being GCed. With do(byte[], int) and
// update(byte[], int, int, byte[], int), we use already initialized obuffer.
if (done) return -1;
ofinish = 0;
ostart = 0;
int expectedOutputSize = cipher.getOutputSize(ibuffer.length);
if (obuffer == null || expectedOutputSize > obuffer.length) {
obuffer = new byte[expectedOutputSize];
}
int readin = input.read(ibuffer);
if (readin == -1) {
done = true;
try {
// doFinal resets the cipher and it is the final call that is made. If there isn't
// any more byte available, it returns 0. In case of any exception is raised,
// obuffer will get reset and therefore, it is equivalent to no bytes returned.
ofinish = cipher.doFinal(obuffer, 0);
} catch (IllegalBlockSizeException | BadPaddingException e) {
obuffer = null;
throw new IOException(e);
} catch (ShortBufferException e) {
obuffer = null;
throw new IllegalStateException("ShortBufferException is not expected", e);
}
} else {
// update returns number of bytes stored in obuffer.
try {
ofinish = cipher.update(ibuffer, 0, readin, obuffer, 0);
} catch (IllegalStateException e) {
obuffer = null;
throw e;
} catch (ShortBufferException e) {
// Should not reset the value of ofinish as the cipher is still not invalidated.
obuffer = null;
throw new IllegalStateException("ShortBufferException is not expected", e);
}
}
return ofinish;
}
/**
* Constructs a CipherInputStream from an InputStream and a
* Cipher.
* <br>Note: if the specified input stream or cipher is
* null, a NullPointerException may be thrown later when
* they are used.
* #param is the to-be-processed input stream
* #param c an initialized Cipher object
*/
public FasterCipherInputStream(InputStream is, Cipher c) {
super(is);
input = is;
cipher = c;
}
/**
* Constructs a CipherInputStream from an InputStream without
* specifying a Cipher. This has the effect of constructing a
* CipherInputStream using a NullCipher.
* <br>Note: if the specified input stream is null, a
* NullPointerException may be thrown later when it is used.
* #param is the to-be-processed input stream
*/
protected FasterCipherInputStream(InputStream is) {
super(is);
input = is;
cipher = new NullCipher();
}
/**
* Reads the next byte of data from this input stream. The value
* byte is returned as an <code>int</code> in the range
* <code>0</code> to <code>255</code>. If no byte is available
* because the end of the stream has been reached, the value
* <code>-1</code> is returned. This method blocks until input data
* is available, the end of the stream is detected, or an exception
* is thrown.
* <p>
*
* #return the next byte of data, or <code>-1</code> if the end of the
* stream is reached.
* #exception IOException if an I/O error occurs.
* #since JCE1.2
*/
public int read() throws IOException {
if (ostart >= ofinish) {
// we loop for new data as the spec says we are blocking
int i = 0;
while (i == 0) i = getMoreData();
if (i == -1) return -1;
}
return ((int) obuffer[ostart++] & 0xff);
};
/**
* Reads up to <code>b.length</code> bytes of data from this input
* stream into an array of bytes.
* <p>
* The <code>read</code> method of <code>InputStream</code> calls
* the <code>read</code> method of three arguments with the arguments
* <code>b</code>, <code>0</code>, and <code>b.length</code>.
*
* #param b the buffer into which the data is read.
* #return the total number of bytes read into the buffer, or
* <code>-1</code> is there is no more data because the end of
* the stream has been reached.
* #exception IOException if an I/O error occurs.
* #see java.io.InputStream#read(byte[], int, int)
* #since JCE1.2
*/
public int read(byte b[]) throws IOException {
return read(b, 0, b.length);
}
/**
* Reads up to <code>len</code> bytes of data from this input stream
* into an array of bytes. This method blocks until some input is
* available. If the first argument is <code>null,</code> up to
* <code>len</code> bytes are read and discarded.
*
* #param b the buffer into which the data is read.
* #param off the start offset in the destination array
* <code>buf</code>
* #param len the maximum number of bytes read.
* #return the total number of bytes read into the buffer, or
* <code>-1</code> if there is no more data because the end of
* the stream has been reached.
* #exception IOException if an I/O error occurs.
* #see java.io.InputStream#read()
* #since JCE1.2
*/
public int read(byte b[], int off, int len) throws IOException {
if (ostart >= ofinish) {
// we loop for new data as the spec says we are blocking
int i = 0;
while (i == 0) i = getMoreData();
if (i == -1) return -1;
}
if (len <= 0) {
return 0;
}
int available = ofinish - ostart;
if (len < available) available = len;
if (b != null) {
System.arraycopy(obuffer, ostart, b, off, available);
}
ostart = ostart + available;
return available;
}
/**
* Skips <code>n</code> bytes of input from the bytes that can be read
* from this input stream without blocking.
*
* <p>Fewer bytes than requested might be skipped.
* The actual number of bytes skipped is equal to <code>n</code> or
* the result of a call to
* {#link #available() available},
* whichever is smaller.
* If <code>n</code> is less than zero, no bytes are skipped.
*
* <p>The actual number of bytes skipped is returned.
*
* #param n the number of bytes to be skipped.
* #return the actual number of bytes skipped.
* #exception IOException if an I/O error occurs.
* #since JCE1.2
*/
public long skip(long n) throws IOException {
int available = ofinish - ostart;
if (n > available) {
n = available;
}
if (n < 0) {
return 0;
}
ostart += n;
return n;
}
/**
* Returns the number of bytes that can be read from this input
* stream without blocking. The <code>available</code> method of
* <code>InputStream</code> returns <code>0</code>. This method
* <B>should</B> be overridden by subclasses.
*
* #return the number of bytes that can be read from this input stream
* without blocking.
* #exception IOException if an I/O error occurs.
* #since JCE1.2
*/
public int available() throws IOException {
return (ofinish - ostart);
}
/**
* Closes this input stream and releases any system resources
* associated with the stream.
* <p>
* The <code>close</code> method of <code>CipherInputStream</code>
* calls the <code>close</code> method of its underlying input
* stream.
*
* #exception IOException if an I/O error occurs.
* #since JCE1.2
*/
public void close() throws IOException {
if (closed) {
return;
}
closed = true;
input.close();
// Android-removed: Removed a now-inaccurate comment
if (!done) {
try {
cipher.doFinal();
}
catch (BadPaddingException | IllegalBlockSizeException ex) {
// Android-changed: Added throw if bad tag is seen. See b/31590622.
if (ex instanceof AEADBadTagException) {
throw new IOException(ex);
}
}
}
ostart = 0;
ofinish = 0;
}
/**
* Tests if this input stream supports the <code>mark</code>
* and <code>reset</code> methods, which it does not.
*
* #return <code>false</code>, since this class does not support the
* <code>mark</code> and <code>reset</code> methods.
* #see java.io.InputStream#mark(int)
* #see java.io.InputStream#reset()
* #since JCE1.2
*/
public boolean markSupported() {
return false;
}
}
It worked fine for my case while decrypting a file over 30 MB. Hope someone can find some flaws though worked really well for my case.
Edit: Sorry somehow I missed that the above answer says the same. Keeping it for others in case they just need to copy from somewhere. Thanks.

Do I have to create a native activity to get a window or I can provide one from window manager on android devices for vulkan applications?

whatever resources that I have found on internet, initialize from creating a native activity and providing android_app->window for creating vkAndroidSurfaceKHR. So, I just want to know can we have a window manager which supplies this window for surface creation.
To create a vkAndroidSurfaceKHR from a simple Java app, you get your instance of android.view.View and perform a native call to ANativeWindow_fromSurface(env, win).
Note View and its subclasses are able to draw 3D content from GPU, as OpenGL and Vulkan.
I did this way in my api around line 9100,
/**
* Get display handles for Android and AWT Canvas
* #param win - a java.awt.Canvas instance or a android.view.Surface
* #param displayHandles - return native surface handle
*
* #return true if all goes Ok.
*/
protected static native boolean getDisplayHandles0(Object win, long[] displayHandles);/*
#ifdef VK_USE_PLATFORM_ANDROID_KHR
ANativeWindow* window;
// Return the ANativeWindow associated with a Java Surface object,
// for interacting with it through native code. This acquires a reference
// on the ANativeWindow that is returned; be sure to use ANativeWindow_release()
// when done with it so that it doesn't leak.
window = ANativeWindow_fromSurface(env, win);
displayHandles[0] = reinterpret_cast<jlong>(window);
return JNI_TRUE;
#else
...
#endif
*/
}
I also implemented this in another way, around line 10370, from same above source:
/**
*
* #see http://www.javaworld.com/article/2075263/core-java/embed-java-code-into-your-native-apps.html
*
* #param instance - Vulkan instance
* #param nativeWindow - instance of android.view.Surface or java.awt.Canvas
* #param pAllocatorHandle - native handle to a VkAllocationCallbacks
* #param pSurface
* #return
*/
protected static native int vkCreateWindowSurface0(long instance,
Object nativeWindow,
long pAllocatorHandle,
long[] pSurface,
long[] awtDrawingSurface);/*
VkAllocationCallbacks* pAllocator = reinterpret_cast<VkAllocationCallbacks*>(pAllocatorHandle);
VkInstance vkInstance = reinterpret_cast<VkInstance>(instance);
VkSurfaceKHR* _pSurface = new VkSurfaceKHR[1];
VkResult res = VkResult::VK_ERROR_NATIVE_WINDOW_IN_USE_KHR;
#ifdef VK_USE_PLATFORM_ANDROID_KHR
ANativeWindow* window = NULL;
window = ANativeWindow_fromSurface(env, nativeWindow);
if (window == NULL)
return VkResult::VK_ERROR_NATIVE_WINDOW_IN_USE_KHR;
VkAndroidSurfaceCreateInfoKHR info;
info.sType = VkStructureType::VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR;
info.pNext = NULL;
info.flags = 0;
info.window = window;
res = vkCreateAndroidSurfaceKHR(vkInstance, &info, pAllocator, _pSurface);
#else
...
#endif
if(res >= 0){
pSurface[0] = reinterpret_cast<jlong>(_pSurface[0]);
}else{
pSurface[0] = (jlong)0;
fprintf(stderr,"Failed to create Vulkan SurfaceKHR.");
}
delete[] _pSurface;
return res;
}

Android Recording Call parameters

Im developing a DTMF decoder. What I need is to record a voice call and then extract the frecuency range. Everything is working ok but there are some android versions in which I get the following error when I set up the audio source
"Invalid capture preset 3 for AudioAttributes"
In order to get the right parameters I have developed an algorithm:
private static final int[] FREQUENCY = {8000, 11025, 16000, 22050, 44100}; // 44100 is guaranteed to work in all devices
private static final int[] CHANNEL_CONFIGURATION = {AudioFormat.CHANNEL_IN_MONO,
AudioFormat.CHANNEL_IN_STEREO};
private static final int[] AUDIO_ENCODING = {AudioFormat.ENCODING_DEFAULT,
AudioFormat.ENCODING_PCM_8BIT,
AudioFormat.ENCODING_PCM_16BIT};
for (int i = 0; i < FREQUENCY.length && !found; i ++) {
for (int j = 0; j < CHANNEL_CONFIGURATION.length && !found; j ++) {
for (int k = 0; k < AUDIO_ENCODING.length && !found; k ++) {
try {
bufferSize = AudioRecord.getMinBufferSize(FREQUENCY[i], CHANNEL_CONFIGURATION[j], AUDIO_ENCODING[k]);
if (bufferSize != AudioRecord.ERROR_BAD_VALUE && bufferSize != AudioRecord.ERROR) {
audioRecord = new AudioRecord(MediaRecorder.AudioSource.VOICE_DOWNLINK, FREQUENCY[i], CHANNEL_CONFIGURATION[j], AUDIO_ENCODING[k], bufferSize);
found = true;
}
} catch (Exception e) {
Log.e(TAG, e.toString());
}
}
}
}
There are no correct parameters found for api 19 or 22 in order to set up an AudioRecord. In every case an exception is raised.
I'm quite locked with this. Im not thinking about to use MediaRecoder class because I can not read a buffer directly from the recoder and this is critical for the dtmf decoding proccess. I have also seen some dtmf open source decoder but all of them have this problem
Conclusion
Android official BUG
first
AudioRecord.java It's constructor public AudioRecord(int audioSource, int sampleRateInHz, int channelConfig, int audioFormat,int bufferSizeInBytes) may NOT recommend for use(I think), an IllegalArgumentException was throwed directly,another constructor metho like below(especially CANDIDATE FOR PUBLIC API):
/**
* #hide
* CANDIDATE FOR PUBLIC API
* Class constructor with {#link AudioAttributes} and {#link AudioFormat}.
* #param attributes a non-null {#link AudioAttributes} instance. Use
* {#link AudioAttributes.Builder#setCapturePreset(int)} for configuring the capture
* preset for this instance.
* #param format a non-null {#link AudioFormat} instance describing the format of the data
* that will be recorded through this AudioRecord. See {#link AudioFormat.Builder} for
* configuring the audio format parameters such as encoding, channel mask and sample rate.
* #param bufferSizeInBytes the total size (in bytes) of the buffer where audio data is written
* to during the recording. New audio data can be read from this buffer in smaller chunks
* than this size. See {#link #getMinBufferSize(int, int, int)} to determine the minimum
* required buffer size for the successful creation of an AudioRecord instance. Using values
* smaller than getMinBufferSize() will result in an initialization failure.
* #param sessionId ID of audio session the AudioRecord must be attached to, or
* {#link AudioManager#AUDIO_SESSION_ID_GENERATE} if the session isn't known at construction
* time. See also {#link AudioManager#generateAudioSessionId()} to obtain a session ID before
* construction.
* #throws IllegalArgumentException
*/
public AudioRecord(AudioAttributes attributes, AudioFormat format, int bufferSizeInBytes,int sessionId) throws IllegalArgumentException {
}
second
you can try
/** Voice call uplink + downlink audio source */
public static final int VOICE_CALL = 4;
third
/**
* #hide
* Sets the capture preset.
* Use this audio attributes configuration method when building an {#link AudioRecord}
* instance with {#link AudioRecord#AudioRecord(AudioAttributes, AudioFormat, int)}.
* #param preset one of {#link MediaRecorder.AudioSource#DEFAULT},
* {#link MediaRecorder.AudioSource#MIC}, {#link MediaRecorder.AudioSource#CAMCORDER},
* {#link MediaRecorder.AudioSource#VOICE_RECOGNITION} or
* {#link MediaRecorder.AudioSource#VOICE_COMMUNICATION}.
* #return the same Builder instance.
*/
#SystemApi
public Builder setCapturePreset(int preset) {
//....
Log.e(TAG, "Invalid capture preset " + preset + " for AudioAttributes");
}
Reference resources
AudioRecord.java
AudioAttributes.java
AudioAttributes.java
#SystemApi #hide
https://code.google.com/p/android/issues/detail?id=2117&q=call%20recorder&colspec=ID%20Type%20Status%20Owner%20Summary%20Stars
https://code.google.com/p/android/issues/detail?id=4075
Some android versions have this feature disabled. If you have the android source code you can make it works. Im currently working with cyanogenmod so I have customized the AudioAttributes.java class in order to not to raise an exception when it happens.
We only have to changhe the setCapturePreset() method in AudioAttributes.java by adding all Audio sources that we want within the switch/case structure.
This is the original:
/**
* #hide
* Sets the capture preset.
* Use this audio attributes configuration method when building an {#link AudioRecord}
* instance with {#link AudioRecord#AudioRecord(AudioAttributes, AudioFormat, int)}.
* #param preset one of {#link MediaRecorder.AudioSource#DEFAULT},
* {#link MediaRecorder.AudioSource#MIC}, {#link MediaRecorder.AudioSource#CAMCORDER},
* {#link MediaRecorder.AudioSource#VOICE_RECOGNITION} or
* {#link MediaRecorder.AudioSource#VOICE_COMMUNICATION}.
* #return the same Builder instance.
*/
#SystemApi
public Builder setCapturePreset(int preset) {
switch (preset) {
case MediaRecorder.AudioSource.DEFAULT:
case MediaRecorder.AudioSource.MIC:
case MediaRecorder.AudioSource.CAMCORDER:
case MediaRecorder.AudioSource.VOICE_RECOGNITION:
case MediaRecorder.AudioSource.VOICE_COMMUNICATION:
mSource = preset;
break;
default:
Log.e(TAG, "Invalid capture preset " + preset + " for AudioAttributes");
}
return this;
}
And I replaced with this:
/**
* #hide
* Sets the capture preset.
* Use this audio attributes configuration method when building an {#link AudioRecord}
* instance with {#link AudioRecord#AudioRecord(AudioAttributes, AudioFormat, int)}.
* #param preset one of {#link MediaRecorder.AudioSource#DEFAULT},
* {#link MediaRecorder.AudioSource#MIC}, {#link MediaRecorder.AudioSource#CAMCORDER},
* {#link MediaRecorder.AudioSource#VOICE_RECOGNITION} or
* {#link MediaRecorder.AudioSource#VOICE_COMMUNICATION}.
* #return the same Builder instance.
*/
#SystemApi
public Builder setCapturePreset(int preset) {
switch (preset) {
case MediaRecorder.AudioSource.DEFAULT:
case MediaRecorder.AudioSource.MIC:
case MediaRecorder.AudioSource.CAMCORDER:
case MediaRecorder.AudioSource.VOICE_RECOGNITION:
case MediaRecorder.AudioSource.VOICE_COMMUNICATION:
case MediaRecorder.AudioSource.VOICE_DOWNLINK:
case MediaRecorder.AudioSource.VOICE_UPLINK:
case MediaRecorder.AudioSource.VOICE_CALL:
mSource = preset;
break;
default:
Log.e(TAG, "Invalid capture preset " + preset + " for AudioAttributes");
}
return this;
}

Public Key Cryptography on Android

I know that there is code like this:
http://www.java2s.com/Code/Android/Security/RSAencryptdecryptfunctionRSAECBPKCS1Padding.htm
/*
Copyright (c) 2010, Sungjin Han <meinside#gmail.com>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of meinside nor the names of its contributors may be
used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
//package org.andlib.helpers;
public static KeyPair generateRsaKeyPair(int keySize, BigInteger publicExponent)
{
KeyPair keys = null;
try
{
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
RSAKeyGenParameterSpec spec = new RSAKeyGenParameterSpec(keySize, publicExponent);
keyGen.initialize(spec);
keys = keyGen.generateKeyPair();
}
catch(Exception e)
{
// Logger.e(e.toString());
}
return keys;
}
/**
* generates a RSA public key with given modulus and public exponent
*
* #param modulus (must be positive? don't know exactly)
* #param publicExponent
* #return
*/
public static PublicKey generateRsaPublicKey(BigInteger modulus, BigInteger publicExponent)
{
try
{
return KeyFactory.getInstance("RSA").generatePublic(new RSAPublicKeySpec(modulus, publicExponent));
}
catch(Exception e)
{
// Logger.e(e.toString());
}
return null;
}
/**
* generates a RSA private key with given modulus and private exponent
*
* #param modulus (must be positive? don't know exactly)
* #param privateExponent
* #return
*/
public static PrivateKey generateRsaPrivateKey(BigInteger modulus, BigInteger privateExponent)
{
try
{
return KeyFactory.getInstance("RSA").generatePrivate(new RSAPrivateKeySpec(modulus, privateExponent));
}
catch(Exception e)
{
// Logger.e(e.toString());
}
return null;
}
/**
* RSA encrypt function (RSA / ECB / PKCS1-Padding)
*
* #param original
* #param key
* #return
*/
public static byte[] rsaEncrypt(byte[] original, PublicKey key)
{
try
{
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, key);
return cipher.doFinal(original);
}
catch(Exception e)
{
// Logger.e(e.toString());
}
return null;
}
/**
* RSA decrypt function (RSA / ECB / PKCS1-Padding)
*
* #param encrypted
* #param key
* #return
*/
public static byte[] rsaDecrypt(byte[] encrypted, PrivateKey key)
{
try
{
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.DECRYPT_MODE, key);
return cipher.doFinal(encrypted);
}
catch(Exception e)
{
// Logger.e(e.toString());
}
return null;
}
Some questions:
Are there side attacks for the code linked above, or is it secure?
If the answer to the first question is no, then is there a module which takes care of side attacks?
I am using Froyo and have tried googling for a while now. All help is appreciated, thanks.
What do you mean by 'side attacks'? Whether it's secure or not, depends on many things, mostly on how you use it, and what are you encrypting/decrypting. Funny thing is, the license is longer than the code, and code basically does nothing special: just swallowing exceptions...
This has little to do with Android, BTW.

Android: Is it possible to display video thumbnails?

I created a video recording application with library dialog. The library dialog displays the list of recorded videos where each item consists of icon, video title, tags and location information the following way:
Does anyone know whether it is possible to replace icons with video thumbnails (single frame preview)?
Thanks!
if you don't or cannot go through cursor and if you have only paths or File objects, you can use since API level 8 (2.2)
public static Bitmap createVideoThumbnail (String filePath, int kind)
Android documentation
The following code runs perfectly:
Bitmap bMap = ThumbnailUtils.createVideoThumbnail(file.getAbsolutePath(), MediaStore.Video.Thumbnails.MICRO_KIND);
If you are using API 2.0 or newer this will work.
int id = **"The Video's ID"**
ImageView iv = (ImageView ) convertView.findViewById(R.id.imagePreview);
ContentResolver crThumb = getContentResolver();
BitmapFactory.Options options=new BitmapFactory.Options();
options.inSampleSize = 1;
Bitmap curThumb = MediaStore.Video.Thumbnails.getThumbnail(crThumb, id, MediaStore.Video.Thumbnails.MICRO_KIND, options);
iv.setImageBitmap(curThumb);
Using the class:
import android.provider.MediaStore.Video.Thumbnails;
We can get two preview thumbnail sizes from the video:
Thumbnails.MICRO_KIND for 96 x 96
Thumbnails.MINI_KIND for 512 x 384 px
This is a code example:
String filePath = "/sdcard/DCIM/Camera/my_video.mp4"; //change the location of your file!
ImageView imageview_mini = (ImageView)findViewById(R.id.thumbnail_mini);
ImageView imageview_micro = (ImageView)findViewById(R.id.thumbnail_micro);
Bitmap bmThumbnail;
//MICRO_KIND, size: 96 x 96 thumbnail
bmThumbnail = ThumbnailUtils.createVideoThumbnail(filePath, Thumbnails.MICRO_KIND);
imageview_micro.setImageBitmap(bmThumbnail);
// MINI_KIND, size: 512 x 384 thumbnail
bmThumbnail = ThumbnailUtils.createVideoThumbnail(filePath, Thumbnails.MINI_KIND);
imageview_mini.setImageBitmap(bmThumbnail);
I really suggest you to use the Glide library. It's among the most efficient way to generate and display a video thumbnail for a local video file.
Just add this line to your gradle file :
compile 'com.github.bumptech.glide:glide:3.7.0'
And it will become as simple as :
String filePath = "/storage/emulated/0/Pictures/example_video.mp4";
Glide
.with( context )
.load( Uri.fromFile( new File( filePath ) ) )
.into( imageViewGifAsBitmap );
You can find more informations here : https://futurestud.io/blog/glide-displaying-gifs-and-videos
Cheers !
Currently I Use following code :
Bitmap bMap = ThumbnailUtils.createVideoThumbnail(file.getAbsolutePath(), MediaStore.Video.Thumbnails.MICRO_KIND);
But I found better solution with Glide library with following code ( It also cache your image and have better performance than previous approach )
Glide.with(context)
.load(uri)
.placeholder(R.drawable.ic_video_place_holder)
.into(imageView);
Try this it's working for me
RequestOptions requestOptions = new RequestOptions();
Glide.with(getContext())
.load("video_url")
.apply(requestOptions)
.thumbnail(Glide.with(getContext()).load("video_url"))
.into("yourimageview");
This solution will work for any version of Android. It has proven to work in 1.5 and 2.2 This is not another "This is for Android 2.0+" solution. I found this through an email message board collection page and cannot find the original link. All credit goes to the original poster.
In your app you would use this by calling:
Bitmap bm = getVideoFrame(VideoStringUri);
Somewhere in it's own function (outside the OnCreate, ect), you would need:
private Bitmap getVideoFrame(String uri) {
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
try {
retriever.setMode(MediaMetadataRetriever.MODE_CAPTURE_FRAME_ONLY);
retriever.setDataSource(uri);
return retriever.captureFrame();
} catch (IllegalArgumentException ex) {
ex.printStackTrace();
} catch (RuntimeException ex) {
ex.printStackTrace();
} finally {
try {
retriever.release();
} catch (RuntimeException ex) {
}
}
return null;
}
In your src folder, you need a new subdirectory android/media which will house the class (copied from the android source itself) which allows you to use this function. This part should not be changed, renamed, or placed anywhere else. MediaMetadataRetriever.java needs to be under android.media in your source folder for this all to work.
/*
* 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.media;
import java.io.FileDescriptor;
import java.io.FileNotFoundException;
import java.io.IOException;
import android.content.ContentResolver;
import android.content.Context;
import android.content.res.AssetFileDescriptor;
import android.graphics.Bitmap;
import android.net.Uri;
/**
* MediaMetadataRetriever class provides a unified interface for retrieving
* frame and meta data from an input media file. {#hide}
*/
public class MediaMetadataRetriever {
static {
System.loadLibrary("media_jni");
native_init();
}
// The field below is accessed by native methods
private int mNativeContext;
public MediaMetadataRetriever() {
native_setup();
}
/**
* Call this method before setDataSource() so that the mode becomes
* effective for subsequent operations. This method can be called only once
* at the beginning if the intended mode of operation for a
* MediaMetadataRetriever object remains the same for its whole lifetime,
* and thus it is unnecessary to call this method each time setDataSource()
* is called. If this is not never called (which is allowed), by default the
* intended mode of operation is to both capture frame and retrieve meta
* data (i.e., MODE_GET_METADATA_ONLY | MODE_CAPTURE_FRAME_ONLY). Often,
* this may not be what one wants, since doing this has negative performance
* impact on execution time of a call to setDataSource(), since both types
* of operations may be time consuming.
*
* #param mode
* The intended mode of operation. Can be any combination of
* MODE_GET_METADATA_ONLY and MODE_CAPTURE_FRAME_ONLY: 1.
* MODE_GET_METADATA_ONLY & MODE_CAPTURE_FRAME_ONLY: For neither
* frame capture nor meta data retrieval 2.
* MODE_GET_METADATA_ONLY: For meta data retrieval only 3.
* MODE_CAPTURE_FRAME_ONLY: For frame capture only 4.
* MODE_GET_METADATA_ONLY | MODE_CAPTURE_FRAME_ONLY: For both
* frame capture and meta data retrieval
*/
public native void setMode(int mode);
/**
* #return the current mode of operation. A negative return value indicates
* some runtime error has occurred.
*/
public native int getMode();
/**
* Sets the data source (file pathname) to use. Call this method before the
* rest of the methods in this class. This method may be time-consuming.
*
* #param path
* The path of the input media file.
* #throws IllegalArgumentException
* If the path is invalid.
*/
public native void setDataSource(String path)
throws IllegalArgumentException;
/**
* Sets the data source (FileDescriptor) to use. It is the caller's
* responsibility to close the file descriptor. It is safe to do so as soon
* as this call returns. Call this method before the rest of the methods in
* this class. This method may be time-consuming.
*
* #param fd
* the FileDescriptor for the file you want to play
* #param offset
* the offset into the file where the data to be played starts,
* in bytes. It must be non-negative
* #param length
* the length in bytes of the data to be played. It must be
* non-negative.
* #throws IllegalArgumentException
* if the arguments are invalid
*/
public native void setDataSource(FileDescriptor fd, long offset, long length)
throws IllegalArgumentException;
/**
* Sets the data source (FileDescriptor) to use. It is the caller's
* responsibility to close the file descriptor. It is safe to do so as soon
* as this call returns. Call this method before the rest of the methods in
* this class. This method may be time-consuming.
*
* #param fd
* the FileDescriptor for the file you want to play
* #throws IllegalArgumentException
* if the FileDescriptor is invalid
*/
public void setDataSource(FileDescriptor fd)
throws IllegalArgumentException {
// intentionally less than LONG_MAX
setDataSource(fd, 0, 0x7ffffffffffffffL);
}
/**
* Sets the data source as a content Uri. Call this method before the rest
* of the methods in this class. This method may be time-consuming.
*
* #param context
* the Context to use when resolving the Uri
* #param uri
* the Content URI of the data you want to play
* #throws IllegalArgumentException
* if the Uri is invalid
* #throws SecurityException
* if the Uri cannot be used due to lack of permission.
*/
public void setDataSource(Context context, Uri uri)
throws IllegalArgumentException, SecurityException {
if (uri == null) {
throw new IllegalArgumentException();
}
String scheme = uri.getScheme();
if (scheme == null || scheme.equals("file")) {
setDataSource(uri.getPath());
return;
}
AssetFileDescriptor fd = null;
try {
ContentResolver resolver = context.getContentResolver();
try {
fd = resolver.openAssetFileDescriptor(uri, "r");
} catch (FileNotFoundException e) {
throw new IllegalArgumentException();
}
if (fd == null) {
throw new IllegalArgumentException();
}
FileDescriptor descriptor = fd.getFileDescriptor();
if (!descriptor.valid()) {
throw new IllegalArgumentException();
}
// Note: using getDeclaredLength so that our behavior is the same
// as previous versions when the content provider is returning
// a full file.
if (fd.getDeclaredLength() < 0) {
setDataSource(descriptor);
} else {
setDataSource(descriptor, fd.getStartOffset(),
fd.getDeclaredLength());
}
return;
} catch (SecurityException ex) {
} finally {
try {
if (fd != null) {
fd.close();
}
} catch (IOException ioEx) {
}
}
setDataSource(uri.toString());
}
/**
* Call this method after setDataSource(). This method retrieves the meta
* data value associated with the keyCode.
*
* The keyCode currently supported is listed below as METADATA_XXX
* constants. With any other value, it returns a null pointer.
*
* #param keyCode
* One of the constants listed below at the end of the class.
* #return The meta data value associate with the given keyCode on success;
* null on failure.
*/
public native String extractMetadata(int keyCode);
/**
* Call this method after setDataSource(). This method finds a
* representative frame if successful and returns it as a bitmap. This is
* useful for generating a thumbnail for an input media source.
*
* #return A Bitmap containing a representative video frame, which can be
* null, if such a frame cannot be retrieved.
*/
public native Bitmap captureFrame();
/**
* Call this method after setDataSource(). This method finds the optional
* graphic or album art associated (embedded or external url linked) the
* related data source.
*
* #return null if no such graphic is found.
*/
public native byte[] extractAlbumArt();
/**
* Call it when one is done with the object. This method releases the memory
* allocated internally.
*/
public native void release();
private native void native_setup();
private static native void native_init();
private native final void native_finalize();
#Override
protected void finalize() throws Throwable {
try {
native_finalize();
} finally {
super.finalize();
}
}
public static final int MODE_GET_METADATA_ONLY = 0x01;
public static final int MODE_CAPTURE_FRAME_ONLY = 0x02;
/*
* Do not change these values without updating their counterparts in
* include/media/mediametadataretriever.h!
*/
public static final int METADATA_KEY_CD_TRACK_NUMBER = 0;
public static final int METADATA_KEY_ALBUM = 1;
public static final int METADATA_KEY_ARTIST = 2;
public static final int METADATA_KEY_AUTHOR = 3;
public static final int METADATA_KEY_COMPOSER = 4;
public static final int METADATA_KEY_DATE = 5;
public static final int METADATA_KEY_GENRE = 6;
public static final int METADATA_KEY_TITLE = 7;
public static final int METADATA_KEY_YEAR = 8;
public static final int METADATA_KEY_DURATION = 9;
public static final int METADATA_KEY_NUM_TRACKS = 10;
public static final int METADATA_KEY_IS_DRM_CRIPPLED = 11;
public static final int METADATA_KEY_CODEC = 12;
public static final int METADATA_KEY_RATING = 13;
public static final int METADATA_KEY_COMMENT = 14;
public static final int METADATA_KEY_COPYRIGHT = 15;
public static final int METADATA_KEY_BIT_RATE = 16;
public static final int METADATA_KEY_FRAME_RATE = 17;
public static final int METADATA_KEY_VIDEO_FORMAT = 18;
public static final int METADATA_KEY_VIDEO_HEIGHT = 19;
public static final int METADATA_KEY_VIDEO_WIDTH = 20;
public static final int METADATA_KEY_WRITER = 21;
public static final int METADATA_KEY_MIMETYPE = 22;
public static final int METADATA_KEY_DISCNUMBER = 23;
public static final int METADATA_KEY_ALBUMARTIST = 24;
// Add more here...
}
Android 1.5 and 1.6 do not offer this thumbnails, but 2.0 does, as seen on the official release notes:
Media
MediaScanner now generates thumbnails for all images when they are inserted into MediaStore.
New Thumbnail API for retrieving image and video thumbnails on demand.
I am answering this question late but hope it will help the other candidate facing same problem.
I have used two methods to load thumbnail for videos list the first was
Bitmap bmThumbnail;
bmThumbnail = ThumbnailUtils.createVideoThumbnail(FILE_PATH
+ videoList.get(position),
MediaStore.Video.Thumbnails.MINI_KIND);
if (bmThumbnail != null) {
Log.d("VideoAdapter","video thumbnail found");
holder.imgVideo.setImageBitmap(bmThumbnail);
} else {
Log.d("VideoAdapter","video thumbnail not found");
}
its look good but there was a problem with this solution because when i scroll video list it will freeze some time due to its large processing.
so after this i found another solution which works perfectly by using Glide Library.
Glide
.with( mContext )
.load( Uri.fromFile( new File( FILE_PATH+videoList.get(position) ) ) )
.into( holder.imgVideo );
I recommended the later solution for showing thumbnail with video list .
thanks
I know this is an old question with an accepted answer but I want to post my answer in case somebody is searching for this:
so when I wanted to only use Uri so I did this:
val mmr = MediaMetadataRetriever()
mmr.setDataSource(videoUri)
val thummbnailBitmap = mmr.frameAtTime
imageView.setImageBitmap(thummbnailBitmap)
This is code for live Video thumbnail.
public class LoadVideoThumbnail extends AsyncTask<Object, Object, Bitmap>{
#Override
protected Bitmap doInBackground(Object... params) {try {
String mMediaPath = "http://commonsware.com/misc/test2.3gp";
Log.e("TEST Chirag","<< thumbnail doInBackground"+ mMediaPath);
FileOutputStream out;
File land=new File(Environment.getExternalStorageDirectory().getAbsoluteFile()
+"/portland.jpg");
Bitmap bitmap = ThumbnailUtils.createVideoThumbnail(mMediaPath, MediaStore.Video.Thumbnails.MICRO_KIND);
ByteArrayOutputStream stream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, stream);
byte[] byteArray = stream.toByteArray();
out=new FileOutputStream(land.getPath());
out.write(byteArray);
out.close();
return bitmap;
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(Bitmap result) {
// TODO Auto-generated method stub
super.onPostExecute(result);
if(result != null){
((ImageView)findViewById(R.id.imageView1)).setImageBitmap(result);
}
Log.e("TEST Chirag","====> End");
}
}

Categories

Resources