yandextaxitech/binaryprefs

exception appears during reinitializing Preferences

balamyt opened this issue · 10 comments

in build.gradle:
implementation 'com.github.iamironz:binaryprefs:0.9.9'

сreate the settings as follows

return new BinaryPreferencesBuilder(MainApplication.getInstance())
            .name(BuildConfig.PREFERENCE_FOLDER)
            .encryption(customEncryptor)
            .registerPersistable(MyEntity.KEY, MyEntity.class)
            .build();//crash is here

entity class

public final class MyEntity implements Persistable, Parcelable {

   private int mId;
   private String mSource;
   private long mLastModification;
   private int mRating;


   public MyEntity(int id, String source, long lastModification, int rating) {
      mId = id;
      mSource = source;
      mLastModification = lastModification;
      mRating = rating;
   }

   //...

   @Override
   public void writeExternal(DataOutput out) {
      out.writeInt(mId);
      out.writeString(mSource);
      out.writeLong(mLastModification);
      out.writeInt(mRating);
   }

   @Override
   public void readExternal(DataInput in) {
      mId = in.readInt();
      mSource = in.readString();
      mLastModification = in.readLong();
      mRating = in.readInt();
   }

   @Override
   public Persistable deepClone() {
      return new MyEntity(mId, mSource, mLastModification, mRating);
   }

   //...
}

When first created Preference it's all right. But if called

mPreferences.edit().putPersistable(key, myEntityInstance).commit();

and then app is killed and removed from recents screen then during the next initialization appears exception:

com.ironz.binaryprefs.exception.FileOperationException: java.util.concurrent.ExecutionException: java.lang.ArrayIndexOutOfBoundsException: length=0; index=0
at com.ironz.binaryprefs.task.Completable.completeBlockingUnsafe(Completable.java:44)
at com.ironz.binaryprefs.BinaryPreferences.fetchCache(BinaryPreferences.java:64)
at com.ironz.binaryprefs.BinaryPreferences.(BinaryPreferences.java:43)
at com.ironz.binaryprefs.BinaryPreferencesBuilder.build(BinaryPreferencesBuilder.java:88)
at by.klepcha.app.dagger.ActivityModule.providePreferences(ActivityModule.java:53)
...
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:703)
Caused by: java.util.concurrent.ExecutionException: java.lang.ArrayIndexOutOfBoundsException: length=0; index=0
at java.util.concurrent.FutureTask.report(FutureTask.java:93)
at java.util.concurrent.FutureTask.get(FutureTask.java:163)
at com.ironz.binaryprefs.task.Completable.completeBlockingUnsafe(Completable.java:42)
at com.ironz.binaryprefs.BinaryPreferences.fetchCache(BinaryPreferences.java:64) 
at com.ironz.binaryprefs.BinaryPreferences.(BinaryPreferences.java:43) 
at com.ironz.binaryprefs.BinaryPreferencesBuilder.build(BinaryPreferencesBuilder.java:88) 
at by.klepcha.app.dagger.ActivityModule.providePreferences(ActivityModule.java:53) 
...
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:703) 
Caused by: java.lang.ArrayIndexOutOfBoundsException: length=0; index=0
at com.ironz.binaryprefs.serialization.SerializerFactory.deserialize(SerializerFactory.java:54)
at com.ironz.binaryprefs.BinaryPreferences$1.run(BinaryPreferences.java:59)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:422)
at java.util.concurrent.FutureTask.run(FutureTask.java:237)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
at java.lang.Thread.run(Thread.java:818)

Hi.
Thanks for issue submitting.
I will investigate this as soon as possible.

Regards.

@balamyt I see that you has no have default constructor for MyEntity class. You actually don't?

Also your customEncryptor#encrypt or recrypt field's methods implementation returns correct encrypted result or byte[0] ?

This usually happens if you provide empty byte array into FileAdapter for saving.

I see that you has no have default constructor for MyEntity class. You actually don't?
Yes, I have not default constructor.

my customEncryptor#encrypt:

   @Override
   public byte[] encrypt(byte[] bytes) {
      if (!hasAlias()) {
         throw createExceptionForDevelopment("set aliases to keystore before encrypt");
      }

      try {
         KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry) mKeyStore.getEntry(mAlias, null);
         RSAPublicKey publicKey = (RSAPublicKey) privateKeyEntry.getCertificate().getPublicKey();

         Cipher inCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding", "AndroidOpenSSL");
         inCipher.init(Cipher.ENCRYPT_MODE, publicKey);

         return encrypt(bytes, inCipher);

      } catch (NoSuchAlgorithmException | UnrecoverableEntryException | KeyStoreException | NoSuchProviderException e) {
         throw createExceptionForUser("in encrypt() something wrong at process with encryption", e, e.getLocalizedMessage());
      } catch (NoSuchPaddingException | InvalidKeyException e) {
         throw createExceptionForDevelopment("need check to correct alias before");
      }
   }

   private byte[] encrypt(byte[] bytes, Cipher inCipher) throws AppKeystoreException {
      ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
      CipherOutputStream cipherOutputStream = new CipherOutputStream(outputStream, inCipher);
      try {
         cipherOutputStream.write(bytes);
         return outputStream.toByteArray();
      } catch (IOException e) {
         throw createExceptionForUser("in encrypt() something wrong at process with cipherOutputStream", e, e.getLocalizedMessage());
      } finally {
         CloseableUtils.close(cipherOutputStream);
      }
   }

@balamyt try create an unit test which encrypts and decrypts non-empty byte[] value with your encryption.

Also please note that you should have default constructor because during initialization it will be called.

Thank!!
It's all right!
The problem was my encryption implementation.

Also i've added two stages byte array length checking:

iamironz@bf0ef9d
iamironz@53678f7

This checks will be in 1.0.0-ALPHA1 release.

Regards.