SIP call error file JNI android - android

/*
* Recieve data on the connected socket into the specified buffer. The
* packet fields <code>data</code> & <code>length</code> are passed in
* addition to <code>packet</code> to eliminate the JNI field access
* calls.
*
* #param aFD the socket FileDescriptor #param packet the DatagramPacket to
* receive into #param data the data buffer of the packet #param offset the
* offset in the data buffer #param length the length of the data buffer in
* the packet #param receiveTimeout the maximum length of time the socket
* should block, reading #param peek indicates to peek at the data #return
* number of data received #exception IOException upon an read error or
* timeout
*/
static native int recvConnectedDatagramImpl(FileDescriptor aFD,
DatagramPacket packet, byte[] data, int offset, int length,
int receiveTimeout, boolean peek) ;
static native int recvConnectedDatagramDirectImpl(FileDescriptor aFD,
DatagramPacket packet, int address, int offset, int length,
int receiveTimeout, boolean peek) ;
/*
* Send the <code>data</code> to the nominated target <code>address</code>
* and <code>port</code>. These values are derived from the
* DatagramPacket to reduce the field calls within JNI.
*
* #param fd the socket FileDescriptor #param data the data buffer of the
* packet #param offset the offset in the data buffer #param length the
* length of the data buffer in the packet #param port the target host port
* #param bindToDevice if bind to device #param trafficClass the traffic
* class to be used when the datagram is sent #param inetAddress address to
* connect to. #return number of data send
*
* #exception IOException upon an read error or timeout
*/
static native int sendDatagramImpl(FileDescriptor fd,
byte[] data, int offset, int length, int port,
boolean bindToDevice, int trafficClass, InetAddress inetAddress)
;
static native int sendDatagramDirectImpl(FileDescriptor fd,
int address, int offset, int length, int port,
boolean bindToDevice, int trafficClass, InetAddress inetAddress)
;
/*
* Send the <code>data</code> to the address and port to which the was
* connnected and <code>port</code>.
*
* #param fd the socket FileDescriptor #param data the data buffer of the
* packet #param offset the offset in the data buffer #param length the
* length of the data buffer in the packet #param bindToDevice not used,
* current kept in case needed as was the case for sendDatagramImpl #return
* number of data send #exception IOException upon an read error or timeout
*/
static native int sendConnectedDatagramImpl(FileDescriptor fd,
byte[] data, int offset, int length, boolean bindToDevice)
;
static native int sendConnectedDatagramDirectImpl(FileDescriptor fd,
int address, int offset, int length, boolean bindToDevice)
;

Related

How to send 7 bit commands to M1 card by NFC on Android?

I'm having trouble using NFC on Android. I want to send some data to M1 card. I can send commands using the transceive() method. But the transceive() method has a byte[] argument, and a byte is 8 bit. I want to send 0b1000000 (short frame) to my M1 card. How send that to M1 card?
/**
* Send raw NfcA data to a tag and receive the response.
*
* <p>This is equivalent to connecting to this tag via {#link NfcA}
* and calling {#link NfcA#transceive}. Note that all MIFARE Classic
* tags are based on {#link NfcA} technology.
*
* <p>Use {#link #getMaxTransceiveLength} to retrieve the maximum number of bytes
* that can be sent with {#link #transceive}.
*
* <p>This is an I/O operation and will block until complete. It must
* not be called from the main application thread. A blocked call will be canceled with
* {#link IOException} if {#link #close} is called from another thread.
*
* <p class="note">Requires the {#link android.Manifest.permission#NFC} permission.
*
* #see NfcA#transceive
*/
public byte[] transceive(byte[] data) throws IOException {
return transceive(data, true);
}
My code is:
/**
* Write a block of 16 byte data to tag.
* #param sectorIndex The sector to where the data should be written
* #param blockIndex The block to where the data should be written
* #param data 16 byte of data.
* #param key The MIFARE Classic key for the given sector.
* #param useAsKeyB If true, key will be treated as key B
* for authentication.
* #return The return codes are:<br />
* <ul>
* <li>0 - Everything went fine.</li>
* <li>1 - Sector index is out of range.</li>
* <li>2 - Block index is out of range.</li>
* <li>3 - Data are not 16 bytes.</li>
* <li>4 - Authentication went wrong.</li>
* <li>-1 - Error while writing to tag.</li>
* </ul>
* #see #authenticate(int, byte[], boolean)
*/
public int writeBlock(int sectorIndex, int blockIndex, byte[] data,
byte[] key, boolean useAsKeyB) {
if (getSectorCount()-1 < sectorIndex) {
return 1;
}
if (mMFC.getBlockCountInSector(sectorIndex)-1 < blockIndex) {
return 2;
}
if (data.length != 16) {
return 3;
}
int block = mMFC.sectorToBlock(sectorIndex) + blockIndex;
// write chinese card
if (block == 0) {
// Write block.
try {
mMFC.transceive(new byte[]{(byte)0x50, (byte)0x00, (byte)0x57, (byte)0xCD});
mMFC.transceive(new byte[]{(byte)0x40}); // TODO: here need send 0b1000000(7 bit) , not 0b01000000(8 bit)
mMFC.transceive(new byte[]{(byte)0x43});
mMFC.writeBlock(block, data);
} catch (IOException e) {
Log.e(LOG_TAG, "Error while writing block to tag.", e);
return -1;
}
return 0;
}
if (!authenticate(sectorIndex, key, useAsKeyB)) {
return 4;
}
// Write block.
try {
mMFC.writeBlock(block, data);
} catch (IOException e) {
Log.e(LOG_TAG, "Error while writing block to tag.", e);
return -1;
}
return 0;
}
What you are trying to do is simply not possible on Android. The Android NFC API does not provide any method to send short frames (7 bit frame as defined in ISO/IEC 14443-3 Type A) directly.
In fact, the transceive() method of NfcA does not only send the byte array that is passed as its argument. Instead, it will also cause the CRC checksum to be automatically appended to the frame. As a consequence, you can only exchange normal frames (as defined in ISO/IEC 14443-3 Type A) using the transceive() method.
Since you are using MIFARE Classic (or rather some UID-changable clones), you will experience even further limitations on Android devices that support MIFARE Classic (i.e. devices with an NFC chipset manufactured by NXP): If a tag is detected as MIFARE Classic, the NFC controller will be switched into a special mode where it interprets high-level versions of the MIFARE Classic commands and automatically translates them to their low-level (and potentially encrypted) versions. This is necessary since MIFARE Classic does not fully follow the framing rules of ISO/IEC 14443-3 Type A. Unfortunately, this also typically prevents you from sending any raw frames directly to these tags at all.

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.

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;
}

Secure random number generation for all Android versions

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 (!).

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