Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Android UI freezing on launch #314

Open
jenskuhrjorgensen opened this issue Mar 15, 2020 · 63 comments
Open

Android UI freezing on launch #314

jenskuhrjorgensen opened this issue Mar 15, 2020 · 63 comments

Comments

@jenskuhrjorgensen
Copy link
Contributor

jenskuhrjorgensen commented Mar 15, 2020

Hi

I'm experiencing problems with react-native-keychain on Android which unfortunately forces me to disable the library for Android (on iOS it works flawlessly). Sometimes the UI freezes for several seconds shortly after launching the app. This happens when simply importing the library without even using it, as in the following code:

// App.js
import React, {Component} from 'react';
import {TextInput,} from 'react-native';
import 'react-native-keychain';

export default class KeychainExample extends Component {
  render() {
    return (
      <TextInput
        style={{flex: 1}}
      />
    );
  }
}

As you can see in the screen recording below, the UI freezes and ignores any input for several seconds shortly after reloading the app.

freeze

Logcat shows a couple of errors, but I don't know if they are related:

2020-03-15 18:15:01.464 19347-19347/com.keychainexample E/SpannableStringBuilder: SPAN_EXCLUSIVE_EXCLUSIVE spans cannot have a zero length
2020-03-15 18:15:01.464 19347-19347/com.keychainexample E/SpannableStringBuilder: SPAN_EXCLUSIVE_EXCLUSIVE spans cannot have a zero length
2020-03-15 18:15:02.585 19347-19397/com.keychainexample D/OpenGLRenderer: endAllActiveAnimators on 0x7e77934600 (AlertController$RecycleListView) with handle 0x7e885a9f60
2020-03-15 18:15:02.603 19347-20435/com.keychainexample D/DecorView: onWindowFocusChangedFromViewRoot hasFocus: true, DecorView@7311aa4[MainActivity]
2020-03-15 18:15:02.757 19347-19347/com.keychainexample D/ReactNative: ReactInstanceManager.onJSBundleLoadedFromServer()
2020-03-15 18:15:02.758 19347-19347/com.keychainexample D/ReactNative: ReactInstanceManager.recreateReactContextInBackground()
2020-03-15 18:15:02.758 19347-19347/com.keychainexample D/ReactNative: ReactInstanceManager.runCreateReactContextOnNewThread()
2020-03-15 18:15:02.758 19347-19347/com.keychainexample D/ReactNative: ReactInstanceManager.tearDownReactContext()
2020-03-15 18:15:02.761 19347-19347/com.keychainexample D/ReactNative: CatalystInstanceImpl.destroy() start
2020-03-15 18:15:02.762 19347-19347/com.keychainexample W/unknown:ReactNative: Packager connection already open, nooping.
2020-03-15 18:15:02.766 19347-32636/com.keychainexample D/ReactNative: ReactInstanceManager.createReactContext()
2020-03-15 18:15:02.766 19347-32574/com.keychainexample W/unknown:ReactNative: Calling JS function after bridge has been destroyed: RCTEventEmitter.receiveEvent([3,"topBlur",{"target":3}])
2020-03-15 18:15:02.766 19347-32574/com.keychainexample W/unknown:ReactNative: Calling JS function after bridge has been destroyed: RCTEventEmitter.receiveEvent([3,"topEndEditing",{"text":"","target":3}])
2020-03-15 18:15:02.771 19347-32637/com.keychainexample V/RNKeychainManager: warming up started at 229678618570351
2020-03-15 18:15:02.771 19347-32636/com.keychainexample D/ReactNative: Initializing React Xplat Bridge.
2020-03-15 18:15:02.772 19347-32636/com.keychainexample D/ReactNative: Initializing React Xplat Bridge before initializeBridge
    
    --------- beginning of system
2020-03-15 18:15:02.773 19347-32637/com.keychainexample D/RNKeychainManager: Probe cipher storage: CipherStorageFacebookConceal
2020-03-15 18:15:02.773 19347-32637/com.keychainexample D/RNKeychainManager: Probe cipher storage: CipherStorageKeystoreAesCbc
2020-03-15 18:15:02.775 19347-32636/com.keychainexample D/ReactNative: Initializing React Xplat Bridge after initializeBridge
2020-03-15 18:15:02.776 19347-32636/com.keychainexample D/ReactNative: CatalystInstanceImpl.runJSBundle()
2020-03-15 18:15:02.776 19347-32639/com.keychainexample D/ReactNative: ReactInstanceManager.setupReactContext()
2020-03-15 18:15:02.776 19347-32639/com.keychainexample D/ReactNative: CatalystInstanceImpl.initialize()
2020-03-15 18:15:02.778 19347-32639/com.keychainexample D/ReactNative: ReactInstanceManager.attachRootViewToInstance()
2020-03-15 18:15:02.788 19347-32639/com.keychainexample W/unknown:ViewManagerPropertyUpdater: Could not find generated setter for class com.facebook.react.views.art.ARTGroupViewManager
2020-03-15 18:15:02.788 19347-32639/com.keychainexample W/unknown:ViewManagerPropertyUpdater: Could not find generated setter for class com.facebook.react.views.art.ARTGroupShadowNode
2020-03-15 18:15:02.789 19347-32639/com.keychainexample W/unknown:ViewManagerPropertyUpdater: Could not find generated setter for class com.facebook.react.views.art.ARTShapeViewManager
2020-03-15 18:15:02.789 19347-32639/com.keychainexample W/unknown:ViewManagerPropertyUpdater: Could not find generated setter for class com.facebook.react.views.art.ARTShapeShadowNode
2020-03-15 18:15:02.790 19347-32639/com.keychainexample W/unknown:ViewManagerPropertyUpdater: Could not find generated setter for class com.facebook.react.views.art.ARTTextViewManager
2020-03-15 18:15:02.790 19347-32639/com.keychainexample W/unknown:ViewManagerPropertyUpdater: Could not find generated setter for class com.facebook.react.views.art.ARTTextShadowNode
2020-03-15 18:15:02.791 19347-32639/com.keychainexample W/unknown:ViewManagerPropertyUpdater: Could not find generated setter for class com.facebook.react.views.checkbox.ReactCheckBoxManager
2020-03-15 18:15:02.792 19347-32639/com.keychainexample W/unknown:ViewManagerPropertyUpdater: Could not find generated setter for class com.facebook.react.uimanager.LayoutShadowNode
2020-03-15 18:15:02.793 19347-32639/com.keychainexample W/unknown:ViewManagerPropertyUpdater: Could not find generated setter for class com.facebook.react.views.picker.ReactDialogPickerManager
2020-03-15 18:15:02.795 19347-32639/com.keychainexample W/unknown:ViewManagerPropertyUpdater: Could not find generated setter for class com.facebook.react.views.drawer.ReactDrawerLayoutManager
2020-03-15 18:15:02.795 19347-32639/com.keychainexample W/unknown:ViewManagerPropertyUpdater: Could not find generated setter for class com.facebook.react.views.picker.ReactDropdownPickerManager
2020-03-15 18:15:02.796 19347-32639/com.keychainexample W/unknown:ViewManagerPropertyUpdater: Could not find generated setter for class com.facebook.react.views.scroll.ReactHorizontalScrollViewManager
2020-03-15 18:15:02.797 19347-32639/com.keychainexample W/unknown:ViewManagerPropertyUpdater: Could not find generated setter for class com.facebook.react.views.scroll.ReactHorizontalScrollContainerViewManager
2020-03-15 18:15:02.797 19347-32639/com.keychainexample W/unknown:ViewManagerPropertyUpdater: Could not find generated setter for class com.facebook.react.views.progressbar.ReactProgressBarViewManager
2020-03-15 18:15:02.799 19347-32639/com.keychainexample W/unknown:ViewManagerPropertyUpdater: Could not find generated setter for class com.facebook.react.views.progressbar.ProgressBarShadowNode
2020-03-15 18:15:02.799 19347-32639/com.keychainexample W/unknown:ViewManagerPropertyUpdater: Could not find generated setter for class com.facebook.react.views.scroll.ReactScrollViewManager
2020-03-15 18:15:02.801 19347-32639/com.keychainexample W/unknown:ViewManagerPropertyUpdater: Could not find generated setter for class com.facebook.react.views.slider.ReactSliderManager
2020-03-15 18:15:02.801 19347-32639/com.keychainexample W/unknown:ViewManagerPropertyUpdater: Could not find generated setter for class com.facebook.react.views.slider.ReactSliderManager$ReactSliderShadowNode
2020-03-15 18:15:02.802 19347-32639/com.keychainexample W/unknown:ViewManagerPropertyUpdater: Could not find generated setter for class com.facebook.react.views.switchview.ReactSwitchManager
2020-03-15 18:15:02.803 19347-32639/com.keychainexample W/unknown:ViewManagerPropertyUpdater: Could not find generated setter for class com.facebook.react.views.switchview.ReactSwitchManager$ReactSwitchShadowNode
2020-03-15 18:15:02.803 19347-32639/com.keychainexample W/unknown:ViewManagerPropertyUpdater: Could not find generated setter for class com.facebook.react.views.swiperefresh.SwipeRefreshLayoutManager
2020-03-15 18:15:02.804 19347-32639/com.keychainexample W/unknown:ViewManagerPropertyUpdater: Could not find generated setter for class com.facebook.react.views.art.ARTSurfaceViewManager
2020-03-15 18:15:02.804 19347-32639/com.keychainexample W/unknown:ViewManagerPropertyUpdater: Could not find generated setter for class com.facebook.react.views.art.ARTSurfaceViewShadowNode
2020-03-15 18:15:02.804 19347-32639/com.keychainexample W/unknown:ViewManagerPropertyUpdater: Could not find generated setter for class com.facebook.react.views.text.frescosupport.FrescoBasedReactTextInlineImageViewManager
2020-03-15 18:15:02.805 19347-32639/com.keychainexample W/unknown:ViewManagerPropertyUpdater: Could not find generated setter for class com.facebook.react.views.text.frescosupport.FrescoBasedReactTextInlineImageShadowNode
2020-03-15 18:15:02.805 19347-32639/com.keychainexample W/unknown:ViewManagerPropertyUpdater: Could not find generated setter for class com.facebook.react.views.image.ReactImageManager
2020-03-15 18:15:02.806 19347-32639/com.keychainexample W/unknown:ViewManagerPropertyUpdater: Could not find generated setter for class com.facebook.react.views.modal.ReactModalHostManager
2020-03-15 18:15:02.807 19347-32639/com.keychainexample W/unknown:ViewManagerPropertyUpdater: Could not find generated setter for class com.facebook.react.views.modal.ModalHostShadowNode
2020-03-15 18:15:02.807 19347-32639/com.keychainexample W/unknown:ViewManagerPropertyUpdater: Could not find generated setter for class com.facebook.react.views.text.ReactRawTextManager
2020-03-15 18:15:02.807 19347-32639/com.keychainexample W/unknown:ViewManagerPropertyUpdater: Could not find generated setter for class com.facebook.react.views.text.ReactRawTextShadowNode
2020-03-15 18:15:02.808 19347-32639/com.keychainexample W/unknown:ViewManagerPropertyUpdater: Could not find generated setter for class com.facebook.react.views.textinput.ReactTextInputManager
2020-03-15 18:15:02.810 19347-32639/com.keychainexample W/unknown:ViewManagerPropertyUpdater: Could not find generated setter for class com.facebook.react.views.textinput.ReactTextInputShadowNode
2020-03-15 18:15:02.812 19347-32639/com.keychainexample W/unknown:ViewManagerPropertyUpdater: Could not find generated setter for class com.facebook.react.views.text.ReactTextViewManager
2020-03-15 18:15:02.813 19347-32639/com.keychainexample W/unknown:ViewManagerPropertyUpdater: Could not find generated setter for class com.facebook.react.views.text.ReactTextShadowNode
2020-03-15 18:15:02.813 19347-32639/com.keychainexample W/unknown:ViewManagerPropertyUpdater: Could not find generated setter for class com.facebook.react.views.view.ReactViewManager
2020-03-15 18:15:02.813 19347-19452/com.keychainexample D/ReactNative: CatalystInstanceImpl.destroy() end
2020-03-15 18:15:02.815 19347-32639/com.keychainexample W/unknown:ViewManagerPropertyUpdater: Could not find generated setter for class com.facebook.react.views.viewpager.ReactViewPagerManager
2020-03-15 18:15:02.815 19347-32639/com.keychainexample W/unknown:ViewManagerPropertyUpdater: Could not find generated setter for class com.facebook.react.views.text.ReactVirtualTextViewManager
2020-03-15 18:15:02.815 19347-32639/com.keychainexample W/unknown:ViewManagerPropertyUpdater: Could not find generated setter for class com.facebook.react.views.text.ReactVirtualTextShadowNode
2020-03-15 18:15:02.841 19347-19347/com.keychainexample W/unknown:ReactNative: Packager connection already open, nooping.
2020-03-15 18:15:02.889 19347-32637/com.keychainexample D/RNKeychainManager: Probe cipher storage: CipherStorageKeystoreRsaEcb
2020-03-15 18:15:03.148 19347-32638/com.keychainexample I/ReactNativeJS: Running "KeychainExample" with {"rootTag":311}
2020-03-15 18:15:03.247 19347-19672/com.keychainexample W/unknown:ReactNative: Calling JS function after bridge has been destroyed: RCTDeviceEventEmitter.emit(["websocketMessage",{"data":"{\"type\":\"update-start\",\"body\":{\"isInitialUpdate\":true}}","type":"text","id":1}])
2020-03-15 18:15:03.247 19347-19449/com.keychainexample W/unknown:ReactNative: Calling JS function after bridge has been destroyed: RCTDeviceEventEmitter.emit(["websocketMessage",{"data":"{\"type\":\"update-start\",\"body\":{\"isInitialUpdate\":true}}","type":"text","id":1}])
2020-03-15 18:15:03.248 19347-19864/com.keychainexample W/unknown:ReactNative: Calling JS function after bridge has been destroyed: RCTDeviceEventEmitter.emit(["websocketMessage",{"data":"{\"type\":\"update-start\",\"body\":{\"isInitialUpdate\":true}}","type":"text","id":1}])
2020-03-15 18:15:03.248 19347-19828/com.keychainexample W/unknown:ReactNative: Calling JS function after bridge has been destroyed: RCTDeviceEventEmitter.emit(["websocketMessage",{"data":"{\"type\":\"update-start\",\"body\":{\"isInitialUpdate\":true}}","type":"text","id":1}])
2020-03-15 18:15:03.249 19347-20528/com.keychainexample W/unknown:ReactNative: Calling JS function after bridge has been destroyed: RCTDeviceEventEmitter.emit(["websocketMessage",{"data":"{\"type\":\"update-start\",\"body\":{\"isInitialUpdate\":true}}","type":"text","id":1}])
2020-03-15 18:15:03.249 19347-20458/com.keychainexample W/unknown:ReactNative: Calling JS function after bridge has been destroyed: RCTDeviceEventEmitter.emit(["websocketMessage",{"data":"{\"type\":\"update-start\",\"body\":{\"isInitialUpdate\":true}}","type":"text","id":1}])
2020-03-15 18:15:03.249 19347-20415/com.keychainexample W/unknown:ReactNative: Calling JS function after bridge has been destroyed: RCTDeviceEventEmitter.emit(["websocketMessage",{"data":"{\"type\":\"update-start\",\"body\":{\"isInitialUpdate\":true}}","type":"text","id":1}])
2020-03-15 18:15:03.249 19347-19958/com.keychainexample W/unknown:ReactNative: Calling JS function after bridge has been destroyed: RCTDeviceEventEmitter.emit(["websocketMessage",{"data":"{\"type\":\"update-start\",\"body\":{\"isInitialUpdate\":true}}","type":"text","id":1}])
2020-03-15 18:15:03.250 19347-20621/com.keychainexample W/unknown:ReactNative: Calling JS function after bridge has been destroyed: RCTDeviceEventEmitter.emit(["websocketMessage",{"data":"{\"type\":\"update-start\",\"body\":{\"isInitialUpdate\":true}}","type":"text","id":1}])
2020-03-15 18:15:03.250 19347-20591/com.keychainexample W/unknown:ReactNative: Calling JS function after bridge has been destroyed: RCTDeviceEventEmitter.emit(["websocketMessage",{"data":"{\"type\":\"update-start\",\"body\":{\"isInitialUpdate\":true}}","type":"text","id":1}])
2020-03-15 18:15:03.250 19347-20561/com.keychainexample W/unknown:ReactNative: Calling JS function after bridge has been destroyed: RCTDeviceEventEmitter.emit(["websocketMessage",{"data":"{\"type\":\"update-start\",\"body\":{\"isInitialUpdate\":true}}","type":"text","id":1}])
2020-03-15 18:15:03.251 19347-27463/com.keychainexample W/unknown:ReactNative: Calling JS function after bridge has been destroyed: RCTDeviceEventEmitter.emit(["websocketMessage",{"data":"{\"type\":\"update-start\",\"body\":{\"isInitialUpdate\":true}}","type":"text","id":1}])
2020-03-15 18:15:03.252 19347-20687/com.keychainexample W/unknown:ReactNative: Calling JS function after bridge has been destroyed: RCTDeviceEventEmitter.emit(["websocketMessage",{"data":"{\"type\":\"update-start\",\"body\":{\"isInitialUpdate\":true}}","type":"text","id":1}])
2020-03-15 18:15:03.253 19347-27632/com.keychainexample W/unknown:ReactNative: Calling JS function after bridge has been destroyed: RCTDeviceEventEmitter.emit(["websocketMessage",{"data":"{\"type\":\"update-start\",\"body\":{\"isInitialUpdate\":true}}","type":"text","id":1}])
2020-03-15 18:15:03.253 19347-27591/com.keychainexample W/unknown:ReactNative: Calling JS function after bridge has been destroyed: RCTDeviceEventEmitter.emit(["websocketMessage",{"data":"{\"type\":\"update-start\",\"body\":{\"isInitialUpdate\":true}}","type":"text","id":1}])
2020-03-15 18:15:03.254 19347-27549/com.keychainexample W/unknown:ReactNative: Calling JS function after bridge has been destroyed: RCTDeviceEventEmitter.emit(["websocketMessage",{"data":"{\"type\":\"update-start\",\"body\":{\"isInitialUpdate\":true}}","type":"text","id":1}])
2020-03-15 18:15:03.254 19347-27510/com.keychainexample W/unknown:ReactNative: Calling JS function after bridge has been destroyed: RCTDeviceEventEmitter.emit(["websocketMessage",{"data":"{\"type\":\"update-start\",\"body\":{\"isInitialUpdate\":true}}","type":"text","id":1}])
2020-03-15 18:15:03.254 19347-27864/com.keychainexample W/unknown:ReactNative: Calling JS function after bridge has been destroyed: RCTDeviceEventEmitter.emit(["websocketMessage",{"data":"{\"type\":\"update-start\",\"body\":{\"isInitialUpdate\":true}}","type":"text","id":1}])
2020-03-15 18:15:03.254 19347-27667/com.keychainexample W/unknown:ReactNative: Calling JS function after bridge has been destroyed: RCTDeviceEventEmitter.emit(["websocketMessage",{"data":"{\"type\":\"update-start\",\"body\":{\"isInitialUpdate\":true}}","type":"text","id":1}])
2020-03-15 18:15:03.255 19347-27646/com.keychainexample W/unknown:ReactNative: Calling JS function after bridge has been destroyed: RCTDeviceEventEmitter.emit(["websocketMessage",{"data":"{\"type\":\"update-start\",\"body\":{\"isInitialUpdate\":true}}","type":"text","id":1}])
2020-03-15 18:15:03.255 19347-28207/com.keychainexample W/unknown:ReactNative: Calling JS function after bridge has been destroyed: RCTDeviceEventEmitter.emit(["websocketMessage",{"data":"{\"type\":\"update-start\",\"body\":{\"isInitialUpdate\":true}}","type":"text","id":1}])
2020-03-15 18:15:03.255 19347-28057/com.keychainexample W/unknown:ReactNative: Calling JS function after bridge has been destroyed: RCTDeviceEventEmitter.emit(["websocketMessage",{"data":"{\"type\":\"update-start\",\"body\":{\"isInitialUpdate\":true}}","type":"text","id":1}])
2020-03-15 18:15:03.256 19347-27935/com.keychainexample W/unknown:ReactNative: Calling JS function after bridge has been destroyed: RCTDeviceEventEmitter.emit(["websocketMessage",{"data":"{\"type\":\"update-start\",\"body\":{\"isInitialUpdate\":true}}","type":"text","id":1}])
2020-03-15 18:15:03.256 19347-27900/com.keychainexample W/unknown:ReactNative: Calling JS function after bridge has been destroyed: RCTDeviceEventEmitter.emit(["websocketMessage",{"data":"{\"type\":\"update-start\",\"body\":{\"isInitialUpdate\":true}}","type":"text","id":1}])
2020-03-15 18:15:03.256 19347-28771/com.keychainexample W/unknown:ReactNative: Calling JS function after bridge has been destroyed: RCTDeviceEventEmitter.emit(["websocketMessage",{"data":"{\"type\":\"update-start\",\"body\":{\"isInitialUpdate\":true}}","type":"text","id":1}])
2020-03-15 18:15:03.257 19347-28615/com.keychainexample W/unknown:ReactNative: Calling JS function after bridge has been destroyed: RCTDeviceEventEmitter.emit(["websocketMessage",{"data":"{\"type\":\"update-start\",\"body\":{\"isInitialUpdate\":true}}","type":"text","id":1}])
2020-03-15 18:15:03.257 19347-28571/com.keychainexample W/unknown:ReactNative: Calling JS function after bridge has been destroyed: RCTDeviceEventEmitter.emit(["websocketMessage",{"data":"{\"type\":\"update-start\",\"body\":{\"isInitialUpdate\":true}}","type":"text","id":1}])
2020-03-15 18:15:03.257 19347-28799/com.keychainexample W/unknown:ReactNative: Calling JS function after bridge has been destroyed: RCTDeviceEventEmitter.emit(["websocketMessage",{"data":"{\"type\":\"update-start\",\"body\":{\"isInitialUpdate\":true}}","type":"text","id":1}])
2020-03-15 18:15:03.257 19347-28872/com.keychainexample W/unknown:ReactNative: Calling JS function after bridge has been destroyed: RCTDeviceEventEmitter.emit(["websocketMessage",{"data":"{\"type\":\"update-start\",\"body\":{\"isInitialUpdate\":true}}","type":"text","id":1}])
2020-03-15 18:15:03.258 19347-28966/com.keychainexample W/unknown:ReactNative: Calling JS function after bridge has been destroyed: RCTDeviceEventEmitter.emit(["websocketMessage",{"data":"{\"type\":\"update-start\",\"body\":{\"isInitialUpdate\":true}}","type":"text","id":1}])
2020-03-15 18:15:03.258 19347-32597/com.keychainexample W/unknown:ReactNative: Calling JS function after bridge has been destroyed: RCTDeviceEventEmitter.emit(["websocketMessage",{"data":"{\"type\":\"update-start\",\"body\":{\"isInitialUpdate\":true}}","type":"text","id":1}])
2020-03-15 18:15:03.259 19347-19672/com.keychainexample W/unknown:ReactNative: Calling JS function after bridge has been destroyed: RCTDeviceEventEmitter.emit(["websocketMessage",{"data":"{\"type\":\"update\",\"body\":{\"revisionId\":\"e18a6388909dd3d1\",\"isInitialUpdate\":true,\"added\":[],\"modified\":[],\"deleted\":[]}}","type":"text","id":1}])
2020-03-15 18:15:03.259 19347-19449/com.keychainexample W/unknown:ReactNative: Calling JS function after bridge has been destroyed: RCTDeviceEventEmitter.emit(["websocketMessage",{"data":"{\"type\":\"update\",\"body\":{\"revisionId\":\"e18a6388909dd3d1\",\"isInitialUpdate\":true,\"added\":[],\"modified\":[],\"deleted\":[]}}","type":"text","id":1}])
2020-03-15 18:15:03.259 19347-19864/com.keychainexample W/unknown:ReactNative: Calling JS function after bridge has been destroyed: RCTDeviceEventEmitter.emit(["websocketMessage",{"data":"{\"type\":\"update\",\"body\":{\"revisionId\":\"e18a6388909dd3d1\",\"isInitialUpdate\":true,\"added\":[],\"modified\":[],\"deleted\":[]}}","type":"text","id":1}])
2020-03-15 18:15:03.259 19347-19864/com.keychainexample W/unknown:ReactNative: Calling JS function after bridge has been destroyed: RCTDeviceEventEmitter.emit(["websocketMessage",{"data":"{\"type\":\"update-done\"}","type":"text","id":1}])
2020-03-15 18:15:03.260 19347-19828/com.keychainexample W/unknown:ReactNative: Calling JS function after bridge has been destroyed: RCTDeviceEventEmitter.emit(["websocketMessage",{"data":"{\"type\":\"update\",\"body\":{\"revisionId\":\"e18a6388909dd3d1\",\"isInitialUpdate\":true,\"added\":[],\"modified\":[],\"deleted\":[]}}","type":"text","id":1}])
2020-03-15 18:15:03.260 19347-19828/com.keychainexample W/unknown:ReactNative: Calling JS function after bridge has been destroyed: RCTDeviceEventEmitter.emit(["websocketMessage",{"data":"{\"type\":\"update-done\"}","type":"text","id":1}])
2020-03-15 18:15:03.261 19347-20458/com.keychainexample W/unknown:ReactNative: Calling JS function after bridge has been destroyed: RCTDeviceEventEmitter.emit(["websocketMessage",{"data":"{\"type\":\"update\",\"body\":{\"revisionId\":\"e18a6388909dd3d1\",\"isInitialUpdate\":true,\"added\":[],\"modified\":[],\"deleted\":[]}}","type":"text","id":1}])
2020-03-15 18:15:03.261 19347-20458/com.keychainexample W/unknown:ReactNative: Calling JS function after bridge has been destroyed: RCTDeviceEventEmitter.emit(["websocketMessage",{"data":"{\"type\":\"update-done\"}","type":"text","id":1}])
2020-03-15 18:15:03.261 19347-20528/com.keychainexample W/unknown:ReactNative: Calling JS function after bridge has been destroyed: RCTDeviceEventEmitter.emit(["websocketMessage",{"data":"{\"type\":\"update\",\"body\":{\"revisionId\":\"e18a6388909dd3d1\",\"isInitialUpdate\":true,\"added\":[],\"modified\":[],\"deleted\":[]}}","type":"text","id":1}])
2020-03-15 18:15:03.261 19347-20528/com.keychainexample W/unknown:ReactNative: Calling JS function after bridge has been destroyed: RCTDeviceEventEmitter.emit(["websocketMessage",{"data":"{\"type\":\"update-done\"}","type":"text","id":1}])
2020-03-15 18:15:03.261 19347-19958/com.keychainexample W/unknown:ReactNative: Calling JS function after bridge has been destroyed: RCTDeviceEventEmitter.emit(["websocketMessage",{"data":"{\"type\":\"update\",\"body\":{\"revisionId\":\"e18a6388909dd3d1\",\"isInitialUpdate\":true,\"added\":[],\"modified\":[],\"deleted\":[]}}","type":"text","id":1}])
2020-03-15 18:15:03.261 19347-19958/com.keychainexample W/unknown:ReactNative: Calling JS function after bridge has been destroyed: RCTDeviceEventEmitter.emit(["websocketMessage",{"data":"{\"type\":\"update-done\"}","type":"text","id":1}])
2020-03-15 18:15:03.262 19347-20621/com.keychainexample W/unknown:ReactNative: Calling JS function after bridge has been destroyed: RCTDeviceEventEmitter.emit(["websocketMessage",{"data":"{\"type\":\"update\",\"body\":{\"revisionId\":\"e18a6388909dd3d1\",\"isInitialUpdate\":true,\"added\":[],\"modified\":[],\"deleted\":[]}}","type":"text","id":1}])
2020-03-15 18:15:03.262 19347-20621/com.keychainexample W/unknown:ReactNative: Calling JS function after bridge has been destroyed: RCTDeviceEventEmitter.emit(["websocketMessage",{"data":"{\"type\":\"update-done\"}","type":"text","id":1}])
2020-03-15 18:15:03.262 19347-20415/com.keychainexample W/unknown:ReactNative: Calling JS function after bridge has been destroyed: RCTDeviceEventEmitter.emit(["websocketMessage",{"data":"{\"type\":\"update\",\"body\":{\"revisionId\":\"e18a6388909dd3d1\",\"isInitialUpdate\":true,\"added\":[],\"modified\":[],\"deleted\":[]}}","type":"text","id":1}])
2020-03-15 18:15:03.262 19347-20415/com.keychainexample W/unknown:ReactNative: Calling JS function after bridge has been destroyed: RCTDeviceEventEmitter.emit(["websocketMessage",{"data":"{\"type\":\"update-done\"}","type":"text","id":1}])
2020-03-15 18:15:03.262 19347-20591/com.keychainexample W/unknown:ReactNative: Calling JS function after bridge has been destroyed: RCTDeviceEventEmitter.emit(["websocketMessage",{"data":"{\"type\":\"update\",\"body\":{\"revisionId\":\"e18a6388909dd3d1\",\"isInitialUpdate\":true,\"added\":[],\"modified\":[],\"deleted\":[]}}","type":"text","id":1}])
2020-03-15 18:15:03.262 19347-20591/com.keychainexample W/unknown:ReactNative: Calling JS function after bridge has been destroyed: RCTDeviceEventEmitter.emit(["websocketMessage",{"data":"{\"type\":\"update-done\"}","type":"text","id":1}])
2020-03-15 18:15:03.263 19347-20561/com.keychainexample W/unknown:ReactNative: Calling JS function after bridge has been destroyed: RCTDeviceEventEmitter.emit(["websocketMessage",{"data":"{\"type\":\"update\",\"body\":{\"revisionId\":\"e18a6388909dd3d1\",\"isInitialUpdate\":true,\"added\":[],\"modified\":[],\"deleted\":[]}}","type":"text","id":1}])
2020-03-15 18:15:03.263 19347-20561/com.keychainexample W/unknown:ReactNative: Calling JS function after bridge has been destroyed: RCTDeviceEventEmitter.emit(["websocketMessage",{"data":"{\"type\":\"update-done\"}","type":"text","id":1}])
2020-03-15 18:15:03.263 19347-27463/com.keychainexample W/unknown:ReactNative: Calling JS function after bridge has been destroyed: RCTDeviceEventEmitter.emit(["websocketMessage",{"data":"{\"type\":\"update\",\"body\":{\"revisionId\":\"e18a6388909dd3d1\",\"isInitialUpdate\":true,\"added\":[],\"modified\":[],\"deleted\":[]}}","type":"text","id":1}])
2020-03-15 18:15:03.263 19347-27463/com.keychainexample W/unknown:ReactNative: Calling JS function after bridge has been destroyed: RCTDeviceEventEmitter.emit(["websocketMessage",{"data":"{\"type\":\"update-done\"}","type":"text","id":1}])
2020-03-15 18:15:03.263 19347-20687/com.keychainexample W/unknown:ReactNative: Calling JS function after bridge has been destroyed: RCTDeviceEventEmitter.emit(["websocketMessage",{"data":"{\"type\":\"update\",\"body\":{\"revisionId\":\"e18a6388909dd3d1\",\"isInitialUpdate\":true,\"added\":[],\"modified\":[],\"deleted\":[]}}","type":"text","id":1}])
2020-03-15 18:15:03.263 19347-20687/com.keychainexample W/unknown:ReactNative: Calling JS function after bridge has been destroyed: RCTDeviceEventEmitter.emit(["websocketMessage",{"data":"{\"type\":\"update-done\"}","type":"text","id":1}])
2020-03-15 18:15:03.264 19347-27632/com.keychainexample W/unknown:ReactNative: Calling JS function after bridge has been destroyed: RCTDeviceEventEmitter.emit(["websocketMessage",{"data":"{\"type\":\"update\",\"body\":{\"revisionId\":\"e18a6388909dd3d1\",\"isInitialUpdate\":true,\"added\":[],\"modified\":[],\"deleted\":[]}}","type":"text","id":1}])
2020-03-15 18:15:03.264 19347-27632/com.keychainexample W/unknown:ReactNative: Calling JS function after bridge has been destroyed: RCTDeviceEventEmitter.emit(["websocketMessage",{"data":"{\"type\":\"update-done\"}","type":"text","id":1}])
2020-03-15 18:15:03.264 19347-27591/com.keychainexample W/unknown:ReactNative: Calling JS function after bridge has been destroyed: RCTDeviceEventEmitter.emit(["websocketMessage",{"data":"{\"type\":\"update\",\"body\":{\"revisionId\":\"e18a6388909dd3d1\",\"isInitialUpdate\":true,\"added\":[],\"modified\":[],\"deleted\":[]}}","type":"text","id":1}])
2020-03-15 18:15:03.264 19347-27591/com.keychainexample W/unknown:ReactNative: Calling JS function after bridge has been destroyed: RCTDeviceEventEmitter.emit(["websocketMessage",{"data":"{\"type\":\"update-done\"}","type":"text","id":1}])
2020-03-15 18:15:03.264 19347-27549/com.keychainexample W/unknown:ReactNative: Calling JS function after bridge has been destroyed: RCTDeviceEventEmitter.emit(["websocketMessage",{"data":"{\"type\":\"update\",\"body\":{\"revisionId\":\"e18a6388909dd3d1\",\"isInitialUpdate\":true,\"added\":[],\"modified\":[],\"deleted\":[]}}","type":"text","id":1}])
2020-03-15 18:15:03.265 19347-27549/com.keychainexample W/unknown:ReactNative: Calling JS function after bridge has been destroyed: RCTDeviceEventEmitter.emit(["websocketMessage",{"data":"{\"type\":\"update-done\"}","type":"text","id":1}])
2020-03-15 18:15:03.265 19347-27510/com.keychainexample W/unknown:ReactNative: Calling JS function after bridge has been destroyed: RCTDeviceEventEmitter.emit(["websocketMessage",{"data":"{\"type\":\"update\",\"body\":{\"revisionId\":\"e18a6388909dd3d1\",\"isInitialUpdate\":true,\"added\":[],\"modified\":[],\"deleted\":[]}}","type":"text","id":1}])
2020-03-15 18:15:03.265 19347-27510/com.keychainexample W/unknown:ReactNative: Calling JS function after bridge has been destroyed: RCTDeviceEventEmitter.emit(["websocketMessage",{"data":"{\"type\":\"update-done\"}","type":"text","id":1}])
2020-03-15 18:15:03.265 19347-27864/com.keychainexample W/unknown:ReactNative: Calling JS function after bridge has been destroyed: RCTDeviceEventEmitter.emit(["websocketMessage",{"data":"{\"type\":\"update\",\"body\":{\"revisionId\":\"e18a6388909dd3d1\",\"isInitialUpdate\":true,\"added\":[],\"modified\":[],\"deleted\":[]}}","type":"text","id":1}])
2020-03-15 18:15:03.265 19347-27864/com.keychainexample W/unknown:ReactNative: Calling JS function after bridge has been destroyed: RCTDeviceEventEmitter.emit(["websocketMessage",{"data":"{\"type\":\"update-done\"}","type":"text","id":1}])
2020-03-15 18:15:03.265 19347-27667/com.keychainexample W/unknown:ReactNative: Calling JS function after bridge has been destroyed: RCTDeviceEventEmitter.emit(["websocketMessage",{"data":"{\"type\":\"update\",\"body\":{\"revisionId\":\"e18a6388909dd3d1\",\"isInitialUpdate\":true,\"added\":[],\"modified\":[],\"deleted\":[]}}","type":"text","id":1}])
2020-03-15 18:15:03.266 19347-27667/com.keychainexample W/unknown:ReactNative: Calling JS function after bridge has been destroyed: RCTDeviceEventEmitter.emit(["websocketMessage",{"data":"{\"type\":\"update-done\"}","type":"text","id":1}])
2020-03-15 18:15:03.266 19347-27646/com.keychainexample W/unknown:ReactNative: Calling JS function after bridge has been destroyed: RCTDeviceEventEmitter.emit(["websocketMessage",{"data":"{\"type\":\"update\",\"body\":{\"revisionId\":\"e18a6388909dd3d1\",\"isInitialUpdate\":true,\"added\":[],\"modified\":[],\"deleted\":[]}}","type":"text","id":1}])
2020-03-15 18:15:03.266 19347-27646/com.keychainexample W/unknown:ReactNative: Calling JS function after bridge has been destroyed: RCTDeviceEventEmitter.emit(["websocketMessage",{"data":"{\"type\":\"update-done\"}","type":"text","id":1}])
2020-03-15 18:15:03.266 19347-28207/com.keychainexample W/unknown:ReactNative: Calling JS function after bridge has been destroyed: RCTDeviceEventEmitter.emit(["websocketMessage",{"data":"{\"type\":\"update\",\"body\":{\"revisionId\":\"e18a6388909dd3d1\",\"isInitialUpdate\":true,\"added\":[],\"modified\":[],\"deleted\":[]}}","type":"text","id":1}])
2020-03-15 18:15:03.266 19347-28207/com.keychainexample W/unknown:ReactNative: Calling JS function after bridge has been destroyed: RCTDeviceEventEmitter.emit(["websocketMessage",{"data":"{\"type\":\"update-done\"}","type":"text","id":1}])
2020-03-15 18:15:03.266 19347-28057/com.keychainexample W/unknown:ReactNative: Calling JS function after bridge has been destroyed: RCTDeviceEventEmitter.emit(["websocketMessage",{"data":"{\"type\":\"update\",\"body\":{\"revisionId\":\"e18a6388909dd3d1\",\"isInitialUpdate\":true,\"added\":[],\"modified\":[],\"deleted\":[]}}","type":"text","id":1}])
2020-03-15 18:15:03.267 19347-28057/com.keychainexample W/unknown:ReactNative: Calling JS function after bridge has been destroyed: RCTDeviceEventEmitter.emit(["websocketMessage",{"data":"{\"type\":\"update-done\"}","type":"text","id":1}])
2020-03-15 18:15:03.267 19347-27935/com.keychainexample W/unknown:ReactNative: Calling JS function after bridge has been destroyed: RCTDeviceEventEmitter.emit(["websocketMessage",{"data":"{\"type\":\"update\",\"body\":{\"revisionId\":\"e18a6388909dd3d1\",\"isInitialUpdate\":true,\"added\":[],\"modified\":[],\"deleted\":[]}}","type":"text","id":1}])
2020-03-15 18:15:03.267 19347-27935/com.keychainexample W/unknown:ReactNative: Calling JS function after bridge has been destroyed: RCTDeviceEventEmitter.emit(["websocketMessage",{"data":"{\"type\":\"update-done\"}","type":"text","id":1}])
2020-03-15 18:15:03.267 19347-27900/com.keychainexample W/unknown:ReactNative: Calling JS function after bridge has been destroyed: RCTDeviceEventEmitter.emit(["websocketMessage",{"data":"{\"type\":\"update\",\"body\":{\"revisionId\":\"e18a6388909dd3d1\",\"isInitialUpdate\":true,\"added\":[],\"modified\":[],\"deleted\":[]}}","type":"text","id":1}])
2020-03-15 18:15:03.267 19347-27900/com.keychainexample W/unknown:ReactNative: Calling JS function after bridge has been destroyed: RCTDeviceEventEmitter.emit(["websocketMessage",{"data":"{\"type\":\"update-done\"}","type":"text","id":1}])
2020-03-15 18:15:03.267 19347-28771/com.keychainexample W/unknown:ReactNative: Calling JS function after bridge has been destroyed: RCTDeviceEventEmitter.emit(["websocketMessage",{"data":"{\"type\":\"update\",\"body\":{\"revisionId\":\"e18a6388909dd3d1\",\"isInitialUpdate\":true,\"added\":[],\"modified\":[],\"deleted\":[]}}","type":"text","id":1}])
2020-03-15 18:15:03.268 19347-28771/com.keychainexample W/unknown:ReactNative: Calling JS function after bridge has been destroyed: RCTDeviceEventEmitter.emit(["websocketMessage",{"data":"{\"type\":\"update-done\"}","type":"text","id":1}])
2020-03-15 18:15:03.268 19347-28615/com.keychainexample W/unknown:ReactNative: Calling JS function after bridge has been destroyed: RCTDeviceEventEmitter.emit(["websocketMessage",{"data":"{\"type\":\"update\",\"body\":{\"revisionId\":\"e18a6388909dd3d1\",\"isInitialUpdate\":true,\"added\":[],\"modified\":[],\"deleted\":[]}}","type":"text","id":1}])
2020-03-15 18:15:03.268 19347-28615/com.keychainexample W/unknown:ReactNative: Calling JS function after bridge has been destroyed: RCTDeviceEventEmitter.emit(["websocketMessage",{"data":"{\"type\":\"update-done\"}","type":"text","id":1}])
2020-03-15 18:15:03.269 19347-28571/com.keychainexample W/unknown:ReactNative: Calling JS function after bridge has been destroyed: RCTDeviceEventEmitter.emit(["websocketMessage",{"data":"{\"type\":\"update\",\"body\":{\"revisionId\":\"e18a6388909dd3d1\",\"isInitialUpdate\":true,\"added\":[],\"modified\":[],\"deleted\":[]}}","type":"text","id":1}])
2020-03-15 18:15:03.269 19347-28571/com.keychainexample W/unknown:ReactNative: Calling JS function after bridge has been destroyed: RCTDeviceEventEmitter.emit(["websocketMessage",{"data":"{\"type\":\"update-done\"}","type":"text","id":1}])
2020-03-15 18:15:03.269 19347-28799/com.keychainexample W/unknown:ReactNative: Calling JS function after bridge has been destroyed: RCTDeviceEventEmitter.emit(["websocketMessage",{"data":"{\"type\":\"update\",\"body\":{\"revisionId\":\"e18a6388909dd3d1\",\"isInitialUpdate\":true,\"added\":[],\"modified\":[],\"deleted\":[]}}","type":"text","id":1}])
2020-03-15 18:15:03.269 19347-28799/com.keychainexample W/unknown:ReactNative: Calling JS function after bridge has been destroyed: RCTDeviceEventEmitter.emit(["websocketMessage",{"data":"{\"type\":\"update-done\"}","type":"text","id":1}])
2020-03-15 18:15:03.270 19347-28872/com.keychainexample W/unknown:ReactNative: Calling JS function after bridge has been destroyed: RCTDeviceEventEmitter.emit(["websocketMessage",{"data":"{\"type\":\"update\",\"body\":{\"revisionId\":\"e18a6388909dd3d1\",\"isInitialUpdate\":true,\"added\":[],\"modified\":[],\"deleted\":[]}}","type":"text","id":1}])
2020-03-15 18:15:03.270 19347-28872/com.keychainexample W/unknown:ReactNative: Calling JS function after bridge has been destroyed: RCTDeviceEventEmitter.emit(["websocketMessage",{"data":"{\"type\":\"update-done\"}","type":"text","id":1}])
2020-03-15 18:15:03.271 19347-28966/com.keychainexample W/unknown:ReactNative: Calling JS function after bridge has been destroyed: RCTDeviceEventEmitter.emit(["websocketMessage",{"data":"{\"type\":\"update\",\"body\":{\"revisionId\":\"e18a6388909dd3d1\",\"isInitialUpdate\":true,\"added\":[],\"modified\":[],\"deleted\":[]}}","type":"text","id":1}])
2020-03-15 18:15:03.271 19347-28966/com.keychainexample W/unknown:ReactNative: Calling JS function after bridge has been destroyed: RCTDeviceEventEmitter.emit(["websocketMessage",{"data":"{\"type\":\"update-done\"}","type":"text","id":1}])
2020-03-15 18:15:03.271 19347-32597/com.keychainexample W/unknown:ReactNative: Calling JS function after bridge has been destroyed: RCTDeviceEventEmitter.emit(["websocketMessage",{"data":"{\"type\":\"update\",\"body\":{\"revisionId\":\"e18a6388909dd3d1\",\"isInitialUpdate\":true,\"added\":[],\"modified\":[],\"deleted\":[]}}","type":"text","id":1}])
2020-03-15 18:15:03.271 19347-32597/com.keychainexample W/unknown:ReactNative: Calling JS function after bridge has been destroyed: RCTDeviceEventEmitter.emit(["websocketMessage",{"data":"{\"type\":\"update-done\"}","type":"text","id":1}])
2020-03-15 18:15:03.271 19347-19672/com.keychainexample W/unknown:ReactNative: Calling JS function after bridge has been destroyed: RCTDeviceEventEmitter.emit(["websocketMessage",{"data":"{\"type\":\"update-done\"}","type":"text","id":1}])
2020-03-15 18:15:03.272 19347-19449/com.keychainexample W/unknown:ReactNative: Calling JS function after bridge has been destroyed: RCTDeviceEventEmitter.emit(["websocketMessage",{"data":"{\"type\":\"update-done\"}","type":"text","id":1}])
2020-03-15 18:15:03.677 19347-19347/com.keychainexample E/SpannableStringBuilder: SPAN_EXCLUSIVE_EXCLUSIVE spans cannot have a zero length
2020-03-15 18:15:03.682 19347-19347/com.keychainexample I/chatty: uid=10440(com.keychainexample) identical 2 lines
2020-03-15 18:15:03.682 19347-19347/com.keychainexample E/SpannableStringBuilder: SPAN_EXCLUSIVE_EXCLUSIVE spans cannot have a zero length
2020-03-15 18:15:16.947 19347-32637/com.keychainexample D/RNKeychainManager: Selected storage: CipherStorageKeystoreRsaEcb
2020-03-15 18:15:16.966 19347-32637/com.keychainexample W/CipherStorageBase: StrongBox security storage is not available.
    android.security.keystore.StrongBoxUnavailableException: Failed to generate key pair
        at android.security.keystore.AndroidKeyStoreKeyPairGeneratorSpi.generateKeystoreKeyPair(AndroidKeyStoreKeyPairGeneratorSpi.java:511)
        at android.security.keystore.AndroidKeyStoreKeyPairGeneratorSpi.generateKeyPair(AndroidKeyStoreKeyPairGeneratorSpi.java:470)
        at java.security.KeyPairGenerator$Delegate.generateKeyPair(KeyPairGenerator.java:727)
        at com.oblador.keychain.cipherStorage.CipherStorageKeystoreRsaEcb.generateKey(CipherStorageKeystoreRsaEcb.java:256)
        at com.oblador.keychain.cipherStorage.CipherStorageBase.tryGenerateStrongBoxSecurityKey(CipherStorageBase.java:444)
        at com.oblador.keychain.cipherStorage.CipherStorageBase.generateKeyAndStoreUnderAlias(CipherStorageBase.java:391)
        at com.oblador.keychain.KeychainModule.internalWarmingBestCipher(KeychainModule.java:167)
        at com.oblador.keychain.KeychainModule.lambda$NuQDyTTfZc67dTNiVeEDbYNRCJw(Unknown Source:0)
        at com.oblador.keychain.-$$Lambda$KeychainModule$NuQDyTTfZc67dTNiVeEDbYNRCJw.run(Unknown Source:2)
        at java.lang.Thread.run(Thread.java:764)
     Caused by: android.security.KeyStoreException: No StrongBox available
        at android.security.keystore.AndroidKeyStoreKeyPairGeneratorSpi.generateKeystoreKeyPair(AndroidKeyStoreKeyPairGeneratorSpi.java:511) 
        at android.security.keystore.AndroidKeyStoreKeyPairGeneratorSpi.generateKeyPair(AndroidKeyStoreKeyPairGeneratorSpi.java:470) 
        at java.security.KeyPairGenerator$Delegate.generateKeyPair(KeyPairGenerator.java:727) 
        at com.oblador.keychain.cipherStorage.CipherStorageKeystoreRsaEcb.generateKey(CipherStorageKeystoreRsaEcb.java:256) 
        at com.oblador.keychain.cipherStorage.CipherStorageBase.tryGenerateStrongBoxSecurityKey(CipherStorageBase.java:444) 
        at com.oblador.keychain.cipherStorage.CipherStorageBase.generateKeyAndStoreUnderAlias(CipherStorageBase.java:391) 
        at com.oblador.keychain.KeychainModule.internalWarmingBestCipher(KeychainModule.java:167) 
        at com.oblador.keychain.KeychainModule.lambda$NuQDyTTfZc67dTNiVeEDbYNRCJw(Unknown Source:0) 
        at com.oblador.keychain.-$$Lambda$KeychainModule$NuQDyTTfZc67dTNiVeEDbYNRCJw.run(Unknown Source:2) 
        at java.lang.Thread.run(Thread.java:764) 
2020-03-15 18:15:17.065 19347-19347/com.keychainexample W/InputMethodManager: IME died: com.touchtype.swiftkey/com.touchtype.KeyboardService
    android.os.DeadObjectException
        at android.os.BinderProxy.transactNative(Native Method)
        at android.os.BinderProxy.transact(Binder.java:1179)
        at com.android.internal.view.IInputMethodSession$Stub$Proxy.updateSelection(IInputMethodSession.java:224)
        at android.view.inputmethod.InputMethodManager.updateSelection(InputMethodManager.java:1672)
        at android.widget.Editor.sendUpdateSelection(Editor.java:1749)
        at android.widget.Editor.finishBatchEdit(Editor.java:1589)
        at android.widget.Editor.endBatchEdit(Editor.java:1563)
        at android.widget.TextView.endBatchEdit(TextView.java:8068)
        at com.android.internal.widget.EditableInputConnection.endBatchEdit(EditableInputConnection.java:78)
        at com.android.internal.widget.EditableInputConnection.closeConnection(EditableInputConnection.java:91)
        at com.android.internal.view.IInputConnectionWrapper.executeMessage(IInputConnectionWrapper.java:541)
        at com.android.internal.view.IInputConnectionWrapper.dispatchMessage(IInputConnectionWrapper.java:225)
        at com.android.internal.view.IInputConnectionWrapper.closeConnection(IInputConnectionWrapper.java:211)
        at android.view.inputmethod.InputMethodManager$ControlledInputConnectionWrapper.deactivate(InputMethodManager.java:600)
        at android.view.inputmethod.InputMethodManager.startInputInner(InputMethodManager.java:1334)
        at android.view.inputmethod.InputMethodManager$H.handleMessage(InputMethodManager.java:538)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loop(Looper.java:193)
        at android.app.ActivityThread.main(ActivityThread.java:6898)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:537)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
2020-03-15 18:15:17.104 19347-19347/com.keychainexample E/unknown:ReactNative: Got DOWN touch before receiving UP or CANCEL from last gesture
2020-03-15 18:15:20.067 19347-32637/com.keychainexample V/RNKeychainManager: warming up takes: 17296 ms

Tested on OnePlus 5T (A5010) running OxygenOS 9.0.10.

react-native info:
System:
    OS: macOS 10.15.3
    CPU: (12) x64 Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz
    Memory: 3.50 GB / 32.00 GB
    Shell: 3.2.57 - /bin/bash
  Binaries:
    Node: 10.16.0 - /usr/local/bin/node
    Yarn: 1.19.2 - ~/.yarn/bin/yarn
    npm: 6.13.4 - /usr/local/bin/npm
    Watchman: 4.9.0 - /usr/local/bin/watchman
  SDKs:
    iOS SDK:
      Platforms: iOS 13.2, DriverKit 19.0, macOS 10.15, tvOS 13.2, watchOS 6.1
    Android SDK:
      API Levels: 19, 23, 24, 25, 26, 27, 28, 29
      Build Tools: 23.0.1, 23.0.3, 25.0.1, 25.0.2, 25.0.3, 26.0.0, 26.0.1, 26.0.3, 27.0.3, 28.0.0, 28.0.0, 28.0.2, 28.0.3, 29.0.2
      System Images: android-19 | Google APIs ARM EABI v7a, android-19 | Google APIs Intel x86 Atom, android-25 | Google APIs Intel x86 Atom, android-27 | Google APIs Intel x86 Atom, android-28 | Google APIs Intel x86 Atom, android-29 | Google APIs Intel x86 Atom
  IDEs:
    Android Studio: 3.6 AI-192.7142.36.36.6241897
    Xcode: 11.3.1/11C504 - /usr/bin/xcodebuild
  npmPackages:
    react: 16.9.0 => 16.9.0
    react-native: 0.61.2 => 0.61.2
  npmGlobalPackages:
    react-native-cli: 2.0.1
    react-native-create-library: 3.1.2
    react-native-nemid: 1.13.0

Let me know if you need any more information!

@OleksandrKucherenko I know you have put a lot of effort into this version of the library, especially the Android part. Maybe you have encountered similar issues?

Best regards
Jens

@OleksandrKucherenko
Copy link
Contributor

from the provided information, I can assume a long startup time of the keychain library.

errors are not critical, even expected. I log them for informing developers about device capabilities and what configuration of the cypher storage applied.

@jenskuhrjorgensen
Copy link
Contributor Author

Hi @OleksandrKucherenko

Thank you for your snappy reply!

from the provided information, I can assume a long startup time of the keychain library.
Do you mean that the UI is supposed to freeze during startup? Sorry if I misunderstand you.

errors are not critical, even expected. I log them for informing developers about device capabilities and what configuration of the cypher storage applied.

I though so, but wanted to provide them in case someone could use them for something :)

@OleksandrKucherenko
Copy link
Contributor

from the provided information, I can assume a long startup time of the keychain library.
Do you mean that the UI is supposed to freeze during startup? Sorry if I misunderstand you.

actually it should not happen, the library did not occupy the main thread (UI thread). initialization is done in a background thread and yes it's slow on the first run, but its the result of the crypto API itself.

@OleksandrKucherenko
Copy link
Contributor

OleksandrKucherenko commented Mar 16, 2020

2020-03-15 18:15:03.677 19347-19347/com.keychainexample E/SpannableStringBuilder: SPAN_EXCLUSIVE_EXCLUSIVE spans cannot have a zero length
2020-03-15 18:15:03.682 19347-19347/com.keychainexample I/chatty: uid=10440(com.keychainexample) identical 2 lines
2020-03-15 18:15:03.682 19347-19347/com.keychainexample E/SpannableStringBuilder: SPAN_EXCLUSIVE_EXCLUSIVE spans cannot have a zero length
2020-03-15 18:15:16.947 19347-32637/com.keychainexample D/RNKeychainManager: Selected storage: CipherStorageKeystoreRsaEcb
2020-03-15 18:15:16.966 19347-32637/com.keychainexample W/CipherStorageBase: StrongBox security storage is not available.

W/- warning, I/ - info, E/ - error, D/ - debug

@jenskuhrjorgensen
Copy link
Contributor Author

jenskuhrjorgensen commented Mar 16, 2020

from the provided information, I can assume a long startup time of the keychain library.
Do you mean that the UI is supposed to freeze during startup? Sorry if I misunderstand you.

actually it should not happen, the library did not occupy the main thread (UI thread). initialization is done in a background thread and yes it's slow on the first run, but its the result of the crypto API itself.

Yeah I could see that effort had been put into doing the warming up in a background thread, but it doesn't seem to work as intended.

it's slow on the first run

Meaning that it is slow on every first run/every launch of the app?

@OleksandrKucherenko
Copy link
Contributor

Meaning that it is slow on every first run/every launch of the app?

As I said it should not slowdown the UI. Something else is eating the CPU and freezing the UI thread. So far I don't have any clue why it happens. Initialization often takes up to 10 seconds on slow devices.

what device do you use? or it emulator?

@jenskuhrjorgensen
Copy link
Contributor Author

As stated in the first post (somewhere between all the logs 😄 ) I'm running it on a OnePlus 5T. Are you using the library in some app yourself? And are you not able to reproduce the performance issues that I'm experiencing? What device are you running?

@OleksandrKucherenko
Copy link
Contributor

And are you not able to reproduce the performance issues that I'm experiencing? What device are you running?

during working on PR other devs mention that startup was very slow. and after that report in the PR were added commits with "caching" and "warmup" logic. In test scenario that I use (Example app), startup of the app was never an issue, the first call of the keychain lib was slow, but never a startup of the app!

@OleksandrKucherenko
Copy link
Contributor

OleksandrKucherenko commented Mar 16, 2020

private void internalWarmingBestCipher() {
try {
final long startTime = System.nanoTime();
Log.v(KEYCHAIN_MODULE, "warming up started at " + startTime);
final CipherStorageBase best = (CipherStorageBase) getCipherStorageForCurrentAPILevel();
final Cipher instance = best.getCachedInstance();
final boolean isSecure = best.supportsSecureHardware();
final SecurityLevel requiredLevel = isSecure ? SecurityLevel.SECURE_HARDWARE : SecurityLevel.SECURE_SOFTWARE;
best.generateKeyAndStoreUnderAlias("warmingUp", requiredLevel);
best.getKeyStoreAndLoad();
Log.v(KEYCHAIN_MODULE, "warming up takes: " +
TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime) +
" ms");
} catch (Throwable ex) {
Log.e(KEYCHAIN_MODULE, "warming up failed!", ex);
}
}

check logs for warming up keywords

@OleksandrKucherenko
Copy link
Contributor

public KeychainModule(@NonNull final ReactApplicationContext reactContext) {
super(reactContext);
prefsStorage = new PrefsStorage(reactContext);
addCipherStorageToMap(new CipherStorageFacebookConceal(reactContext));
addCipherStorageToMap(new CipherStorageKeystoreAesCbc());
// we have a references to newer api that will fail load of app classes in old androids OS
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
addCipherStorageToMap(new CipherStorageKeystoreRsaEcb());
}
}

and this is the initialization code itself. Inside constructors of the Cyphers only the facebook cypher may have some side effect due to native library loading.

@jenskuhrjorgensen
Copy link
Contributor Author

private void internalWarmingBestCipher() {
try {
final long startTime = System.nanoTime();
Log.v(KEYCHAIN_MODULE, "warming up started at " + startTime);
final CipherStorageBase best = (CipherStorageBase) getCipherStorageForCurrentAPILevel();
final Cipher instance = best.getCachedInstance();
final boolean isSecure = best.supportsSecureHardware();
final SecurityLevel requiredLevel = isSecure ? SecurityLevel.SECURE_HARDWARE : SecurityLevel.SECURE_SOFTWARE;
best.generateKeyAndStoreUnderAlias("warmingUp", requiredLevel);
best.getKeyStoreAndLoad();
Log.v(KEYCHAIN_MODULE, "warming up takes: " +
TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime) +
" ms");
} catch (Throwable ex) {
Log.e(KEYCHAIN_MODULE, "warming up failed!", ex);
}
}

check logs for warming up keywords

I did, and in particular this looked extreme:
2020-03-15 18:15:20.067 19347-32637/com.keychainexample V/RNKeychainManager: warming up takes: 17296 ms

@jenskuhrjorgensen
Copy link
Contributor Author

And are you not able to reproduce the performance issues that I'm experiencing? What device are you running?

during working on PR other devs mention that startup was very slow. and after that report in the PR were added commits with "caching" and "warmup" logic. In test scenario that I use (Example app), startup of the app was never an issue, the first call of the keychain lib was slow, but never a startup of the app!

The app startup time is not that bad, but after using it in my app, I sometimes experienced an unresponsive UI. I could reproduce it in the KeychainExample app by constantly interacting with the UI (e.g. constantly tapping a text input as you can see in the screen recording in the original post), but if I didn't interact with the UI constantly, I wouldn't notice the performance issues. The app renders/launches quick enough.

@OleksandrKucherenko
Copy link
Contributor

OleksandrKucherenko commented Mar 16, 2020

I did, and in particular this looked extreme:
2020-03-15 18:15:20.067 19347-32637/com.keychainexample V/RNKeychainManager: warming up takes: 17296 ms

yep... but that a complete module warmup... it triggers maximum things that's are in the module to force java load classes and initialize them. I think you can investigate deeper with android studio profiler and see which method takes the most of the time.

low level crypto/keystores api itself is very slow... I have no idea why it so

@maherzaidoune
Copy link

maherzaidoune commented Apr 22, 2020

i've faced the same issue, the warming process took long time , that cause UI freezing on low performance phones,
i specified the STORAGE_TYPE , (AES in my case)
Screen Shot 2020-04-23 at 12 33 16 AM
and comment the code as in the image
Screen Shot 2020-04-23 at 12 34 39 AM
Screen Shot 2020-04-23 at 9 46 56 AM

@jenskuhrjorgensen
Copy link
Contributor Author

i've faced the same issue, the warming process took long time , that cause UI freezing on low performance phones,
i specified the STORAGE_TYPE , (AES in my case)
Screen Shot 2020-04-23 at 12 33 16 AM
and comment the code as in the image
Screen Shot 2020-04-23 at 12 34 39 AM

And did that help on performance or what is your conclusion?

@maherzaidoune
Copy link

i've faced the same issue, the warming process took long time , that cause UI freezing on low performance phones,
i specified the STORAGE_TYPE , (AES in my case)
Screen Shot 2020-04-23 at 12 33 16 AM
and comment the code as in the image
Screen Shot 2020-04-23 at 12 34 39 AM

And did that help on performance or what is your conclusion?

Well yes, as i'm using RSA ,i didn't need to add Facebook || AES.
The last screenshot (just edited my answer), i just needed to create an instance of Cipher , that reduce the warming up time from 20s to 2s (Huawei y7 pro)

@pwneth
Copy link

pwneth commented Apr 24, 2020

@OleksandrKucherenko have you found any fix for this issue? Still seems to be a problem on older Android devices.

@cladjules
Copy link

cladjules commented May 18, 2020

@OleksandrKucherenko I believe it does block the UI because all the internal function are synchronized and when the main thread tries to call getGenericPassword it waits for the background thread to finish before resuming and allowing the main thread to proceed.
I assume that loading the Keystore is much faster in the main thread.

@cladjules
Copy link

Personally commenting out the warming makes it much faster,

  /** Allow initialization in chain. */
  public static KeychainModule withWarming(@NonNull final ReactApplicationContext reactContext) {
    final KeychainModule instance = new KeychainModule(reactContext);

    // force initialization of the crypto api in background thread
//    final Thread warmingUp = new Thread(instance::internalWarmingBestCipher, "keychain-warming-up");
//    warmingUp.setDaemon(true);
//    warmingUp.start();

    return instance;
  }

But I need to test on more Android versions.

@jenskuhrjorgensen
Copy link
Contributor Author

Personally commenting out the warming makes it much faster,

@cladjules And are there any notable drawbacks from removing the warming? Does it still work?

@cladjules
Copy link

Personally commenting out the warming makes it much faster,

@cladjules And are there any notable drawbacks from removing the warming? Does it still work?

I have only tried on Android 10, but it's worth testing other versions,
use my Fork in your package.json and see if that helps:
https://github.com/cladjules/react-native-keychain

@cladjules
Copy link

I have tried setting up the Priority to the thread to high,
but that didn't help.

I cannot see anything in the warming code that would require the UI Thread,
but I assume that Android 10 makes the background threads much slower than the main...

@maherzaidoune
Copy link

Personally commenting out the warming makes it much faster,

  /** Allow initialization in chain. */
  public static KeychainModule withWarming(@NonNull final ReactApplicationContext reactContext) {
    final KeychainModule instance = new KeychainModule(reactContext);

    // force initialization of the crypto api in background thread
//    final Thread warmingUp = new Thread(instance::internalWarmingBestCipher, "keychain-warming-up");
//    warmingUp.setDaemon(true);
//    warmingUp.start();

    return instance;
  }

But I need to test on more Android versions.

i tried that before, the UI freezing when i'm requesting keychain for the first time. best solution for me was to edit the warmingUp method and the keyModule method to reduce the code need to be executed.

@cladjules
Copy link

Personally commenting out the warming makes it much faster,

  /** Allow initialization in chain. */
  public static KeychainModule withWarming(@NonNull final ReactApplicationContext reactContext) {
    final KeychainModule instance = new KeychainModule(reactContext);

    // force initialization of the crypto api in background thread
//    final Thread warmingUp = new Thread(instance::internalWarmingBestCipher, "keychain-warming-up");
//    warmingUp.setDaemon(true);
//    warmingUp.start();

    return instance;
  }

But I need to test on more Android versions.

i tried that before, the UI freezing when i'm requesting keychain for the first time. best solution for me was to edit the warmingUp method and the keyModule method to reduce the code need to be executed.

Android 10 looks fine though,
did you have a freeze on lower versions?

Thanks :)

@miikapakarinen
Copy link

We ran into this same issue with several different phone models but it did only occur in Android 10. Android 8 and 9 were not visibly affected.

For example on Samsung A20 Android 10 the warmup time was anything between 5k-40k ms.
Downgrading to 4.0.5 solved the issue.

@john-y-pazekha
Copy link
Contributor

john-y-pazekha commented May 28, 2020

@jenskuhrjorgensen
@Nullabl3
@cladjules
@maherzaidoune
@pwneth

Gentlemen, I need your help to reproduce this issue. Since it happens only on specific combination of phone and Android version, I need to know what exactly you're running.

Could you please use this APK and attach here the report it produced?
get-device-info.apk.zip

Best regards,
John

@john-y-pazekha
Copy link
Contributor

john-y-pazekha commented May 29, 2020

Could you please use this APK and attach here the report it produced?
get-device-info.apk.zip

Since a concern was expressed about safety of this APK, I would like to assure you gentlemen that this APK is not malicious. It requests no permissions so it cannot harm your system.

Alternatively, you can clone the source from this repo and run it in Android Studio.

@jenskuhrjorgensen
@Nullabl3
@cladjules
@maherzaidoune
@pwneth
Please use either way to produce the log. Without this information we won't be able to reproduce the issue.

@jenskuhrjorgensen
Copy link
Contributor Author

jenskuhrjorgensen commented May 29, 2020

Hi @john-y-pazekha
I must admit, that I also found the APK pretty suspicious, so I actually decided not to install. However, as @oblador vouched good for you I gave it a go, but without success. First Play Protect blocked the installation. I tried this and got past the "blocked-by-play-protect" screen, but now it just says "App not installed" with no explanation. Here are my phone specs:
Screenshot_20200529-110633

I also did some more investigation in the performance issues. I tried to disable the warming as suggested by others:

// force initialization of the crypto api in background thread
  //    final Thread warmingUp = new Thread(instance::internalWarmingBestCipher, "keychain-warming-up");
  //    warmingUp.setDaemon(true);
  //    warmingUp.start();

And it helped on the initial freezing, but only delays it for the first time you save credentials. I did some logging and found out that the time consuming task is generator.generateKeyPair().getPrivate(); in CipherStorageKeystoreRsaEcb which takes between 6 and 12 seconds on my OnePlus 5T. The line calls the java crypto API and indicates that the performance issue is in the crypto API itself, which has also been stated by @OleksandrKucherenko .

In CipherStorageKeystoreRsaEcb the library uses java.security.KeyPairGenerator to generate the keys instead of javax.crypto.KeyGenerator which is suggested in Android's biometric auth tutorial, and I'm not sure/smart enough to know why that is, but maybe javax.crypto.KeyGenerator is faster than java.security.KeyPairGenerator.

@maherzaidoune
Copy link

@john-y-pazekha Sorry for the late response - I just became a dad, so I've been afk for three weeks :D

Here are the results:

{
  "BOARD": "msm8998",
  "CPU_ABI2": "",
  "HOST": "rd-build-103",
  "SUPPORTED_64_BIT_ABIS": [
    "arm64-v8a"
  ],
  "CPU_ABI": "arm64-v8a",
  "PERMISSIONS_REVIEW_REQUIRED": true,
  "DISPLAY": "ONEPLUS A5010_43_200601",
  "SUPPORTED_ABIS": [
    "arm64-v8a",
    "armeabi-v7a",
    "armeabi"
  ],
  "FINGERPRINT": "OnePlus/OnePlus5T/OnePlus5T:10/QKQ1.191014.012/2006012146:user/release-keys",
  "PRODUCT": "OnePlus5T",
  "ID": "QKQ1.191014.012",
  "TYPE": "user",
  "SERIAL": "unknown",
  "DEVICE": "OnePlus5T",
  "TIME": 1591020189000,
  "MODEL": "ONEPLUS A5010",
  "MANUFACTURER": "OnePlus",
  "USER": "jenkins",
  "BRAND": "OnePlus",
  "SUPPORTED_32_BIT_ABIS": [
    "armeabi-v7a",
    "armeabi"
  ],
  "HARDWARE": "qcom",
  "IS_DEBUGGABLE": false,
  "BOOTLOADER": "unknown",
  "RADIO": "unknown",
  "UNKNOWN": "unknown",
  "IS_EMULATOR": false,
  "TAGS": "release-keys"
}

Congrats 🥳

@aeirola
Copy link
Collaborator

aeirola commented Aug 4, 2020

Experiencing the same issue on our app as well. Managed to work around the issue by disabling autolinking and adding the non warmed up module to my app.

Would be nice if the library would support loading it without necessarily performing the warmup on start, and then allow developers to explicitly control when to perform the warmup with some warmup() function.

For anyone wanting to do a similar workaround, these are roughly the changes I did.

react-native.config.js: Disable autolinking

module.exports = {
  dependencies: {
    'react-native-keychain': { platforms: { android: null } },
  },
};

settings.gradle: Add the project

include ':react-native-keychain'
project(':react-native-keychain').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-keychain/android')

app/build.gradle: Add the dependency

dependencies {
    implementation project(':react-native-keychain')
}

ColdKeychaingPackage.java: Separate non-warmupping package

package example;

import androidx.annotation.NonNull;

import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.JavaScriptModule;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;

import java.util.Collections;
import java.util.List;

import com.oblador.keychain.KeychainModule;

@SuppressWarnings("unused")
public class ColdKeychainPackage implements ReactPackage {

  public ColdKeychainPackage() {

  }

  @Override
  @NonNull
  public List<NativeModule> createNativeModules(@NonNull final ReactApplicationContext reactContext) {
    return Collections.singletonList(new KeychainModule(reactContext));
  }

  @NonNull
  public List<Class<? extends JavaScriptModule>> createJSModules() {
    return Collections.emptyList();
  }

  @Override
  @NonNull
  public List<ViewManager> createViewManagers(@NonNull final ReactApplicationContext reactContext) {
    return Collections.emptyList();
  }
}

MainApplication.java: Add the cold keychain package

        @Override
        protected List<ReactPackage> getPackages() {
          @SuppressWarnings("UnnecessaryLocalVariable")
          List<ReactPackage> packages = new PackageList(this).getPackages();
          // Packages that cannot be autolinked yet can be added manually here, for example:
          // packages.add(new MyReactNativePackage());
          packages.add(new ColdKeychainPackage());
          return packages;
        }

@giregk
Copy link

giregk commented Aug 26, 2020

Hello, I have taken a great inspiration from this module to write my own (because I like to understand stuff). Thank you by the way for your work !

I didn't investigate this issue in particular, but I was surprised when I saw the warming up mechanism in the code. This mechanism generates a "warmingUp" key which has no other purpose.

I don't know if you are aware, but if the chosen algorithm is RSA, it will take a lot of time to generate a key pair. Just try to generate an RSA key pair in your terminal with ssh-keygen, you'll see that takes a noticeable time. Generating an RSA key pair is a random process. The algorithm tests a lot of random numbers until it finds a prime one that's big enough and then derives the private and the public key from it. So the generation can be quick or really long. It both depends on the speed of your processor and on luck.

I believe this slowness issue will be solved once you remove your warmup mechanism as @cladjules commented. And from my understanding of the library, it has no other side effect.

You could also use the AES algorithm instead of the RSA one. It's as secure for this use case. (But generating a key in the Keystore with the option "setUserAuthenticationRequired(true)" and immediately use it to encrypt the user password will fail if the user does not authenticate in between. I guess that's precisely why the RSA algorithm was used in the first place).

I hope this helps.

@SudoPlz
Copy link

SudoPlz commented Oct 1, 2020

Just to make things clear, after reading this issue, it looks like our workarounds for now are:

  1. Use AES somehow instead of RSA
    OR
  2. Use the non-warming-up version instead of auto linking, until this is officially resolved somehow?

Did I understand that correctly?
Thanks

@giregk
Copy link

giregk commented Oct 8, 2020

@SudoPlz
Yes. Patch the package as suggested above to remove the initial freeze, or force the use of AES by using the STORAGE_TYPE option.
The problem is that the AES option is incompatible with the user interaction requirement (biometric or password).

Under the hood

To improve on my previous comment, here is what actually happens. A secure hardware generates the keys: either a secret symmetric key (AES) or a private-public key pair (RSA) in our case. That AES key or RSA public key is used to encrypt your item value and store the encrypted result on a public file. The security lies in the fact that the secure hardware makes sure that only your app can retrieve the key. You can also specify options so that the secure hardware reveals the key only after the user has authenticated with biometrics or password.

The problem on Android is that if you specify that option for an AES key, to be able to encrypt your entry value with the newly created key, you would have to authenticate. So the user experience would be weird: the user types his password, then has to use his fingerprint or device password right after so the app can save the password... That is the reason why, I suppose, the authors of the library decided that in order to enable this option, the best way was to generate an RSA key pair. Indeed, the public key of the RSA key pair is always available... since it is public.

Then came the drawback that generating an RSA key pair is a random process, so it sometimes takes a long time. I suppose the authors of the library thought the problem was the time it took to load the library, and decided to add a warming up mechanism, which is of course not a solution and makes the problem even worse.

There are several alternatives here:
1/ keeping the current RSA implementation and accepting the key generation time randomness (maybe adding a message for the user)
2/ using an AES key without the biometric/password requirement at the hardware level while still requiring it programmatically (perfect UX, but lower theoretical security though still very good)
3/ using an AES key with the biometric/password requirement at the hardware level (highest security) with a UX compromise (requiring authentication right after key generation)

In any case, the warming up mechanism should be removed.

I have implemented 2/ on my project with a simplified api. I can send you the code if you wish.

@SudoPlz
Copy link

SudoPlz commented Oct 8, 2020

@giregk thank you SOOOO much for explaining, this is perfect!!!

Ok so then I guess here's a scenario, for an app with low security needs, a user types in their username and password, and for whatever reason we wish to save those credentials.

We also wish to retrieve that username and password only after they authenticate via biometrics (mostly because it's easier and more convenient than typing a password - because that could happen many times).

Does that mean we can't use solution no2 which seems great for us as well?
Also yes, if you could share with us your no2 configuration that would be awesome.

Here are my 2 configs currently:

public static NO_BIOMETRICS_CONFIG: Keychain.Options = {
  ...Platform.select({
    android: {
      accessControl: Keychain.ACCESS_CONTROL.APPLICATION_PASSWORD,
    },
    ios: {}
  }),
  accessible: Keychain.ACCESSIBLE.ALWAYS,
  securityLevel: Keychain.SECURITY_LEVEL.ANY,
  storage: Keychain.STORAGE_TYPE.FB // using FB storage to prevent huge RSA creation times, we don't need RSA since we won't be using biometrics on this one
};

public static RAW_CREDS_CONFIG: Keychain.Options = {
  accessControl: Keychain.ACCESS_CONTROL.BIOMETRY_ANY,
  accessible: Keychain.ACCESSIBLE.WHEN_UNLOCKED,
  authenticationPrompt: {
    title: 'Unlock to log in',
  },
  // probably uses RSA since we're using BIOMETRY_ANY ?
};

The NO_BIOMETRICS_CONFIG is for saving access tokens or other stuff, so we don't care about biometrics there, since it's not using RSA or AES so it's fast already correct.

The RAW_CREDS_CONFIG is what should be the one that's slowing us down, so that's probably what needs to change to AES.

p.s: I find it super hard to figure out what the differences between FB, RSA and AES are and when to choose each one for this library, are those documented anywhere?

@giregk
Copy link

giregk commented Oct 8, 2020

@SudoPlz

About FB vs RSA vs AES

  • stands for Facebook: this will use Facebook Conceal, a deprecated library that was used in the old times to encrypt the content. This does not use any secure mecanism. I don't know exactly how it works, but basically the secret key will be stored alongside the encrypted content so anyone who has access to the file can uncrypt what you put in the keychain. It's not a security, it's just obfuscation.
  • RSA and AES are two standard cryptographic algorithms
    • RSA is an asymmetric algorithm, meaning the key that is used to encrypt is different than the key that is used to uncrypt. We call those keys a "public-private key pair". The private key has to remain secret while the public does not. They go together. If you encrypt a message with the public key, only the person that knows the private key can uncrypt it.
    • AES is a symmetric algorithm, meaning the same key is used to encrypt and uncrypt the message. That key has to remain secret, so it's called a "secret key".

You can find a lot more details on the web about these two algorithms.

The lib chooses the algorithm for you depending on your parameters if you don't specify it.

About your use case

Your NO_BIOMETRICS_CONFIG should not specify the storage type to let the lib decide for you. I can't remember exactly how the code decides, but I think it won't use RSA unless you specify the biometric requirement. (But I'm not sure)

In the case of your RAW_CREDS_CONFIG, my solution 2/ is fine. Biometrics is not really a security improvement anyways, so let's consider it simply as a a means to improve the UX. Here is the code I use in my project: https://github.com/giregk/react-native-simple-keychain. The api is a bit different though. Do read the native code, it will help you.

@SudoPlz
Copy link

SudoPlz commented Oct 8, 2020

@giregk I really appreciate the explanation.

So it sounds like I should use AES for both my configurations

That being said, I was under the impression that in order to use your 2nd solution with AES, we can't use biometrics (accessControl: Keychain.ACCESS_CONTROL.BIOMETRY_ANY).

So how can I achieve the following scenario:

A) User types credentials
B) Storing credentials to keychain using AES (no biometrics prompts)
C) User attempts to load those credentials (Biometrics prompt here)
D) AES is used to decrypt, and the credentials are returned

is the following config the right way to do that, or will that result in biometrics being asked after the user types their credentials and tries to save them?

const RAW_CREDS_CONFIG: Keychain.Options = {
    accessControl: Keychain.ACCESS_CONTROL.BIOMETRY_ANY,
    accessible: Keychain.ACCESSIBLE.WHEN_UNLOCKED,
    authenticationPrompt: {
      title: 'Unlock to log into Acuity',
    },
    storage: Keychain.STORAGE_TYPE.AES // <-- does aes work with biometrics?
  };

then

await Keychain.setGenericPassword( // <-- NO biometrics requested here
      username,
      password,
      RAW_CREDS_CONFIG,
)
// ...
await Keychain.getGenericPassword(RAW_CREDS_CONFIG); // <-- biometrics requested here

@giregk
Copy link

giregk commented Oct 8, 2020

@SudoPlz Try that, but I don't know if that will work. I think it will not. The only way to achieve the desired scenario without having the random freezing time on android is by changing the native implementation of the lib, like in my repo.

=== EDIT
It does work :)

@SudoPlz
Copy link

SudoPlz commented Oct 8, 2020

@giregk so for now:

  • the ONLY way to use biometrics is RSA, correct?
  • and if we DON'T warmup then it will just take more time to create the encryption password when we first store the credentials.

are those 2 assumptions correct?

@giregk
Copy link

giregk commented Oct 8, 2020

@SudoPlz yes

=== EDIT

...assuming you use the lib as is. If you rewrite the lib from scratch, AES can work.

=== EDIT 2
Wrong! as shown in below comment

@SudoPlz
Copy link

SudoPlz commented Oct 8, 2020

@giregk do you know which line of code stops this library from using AES with biometrics? Or from a high level what needs to change to achieve that?

@giregk
Copy link

giregk commented Oct 8, 2020

@SudoPlz I think you should read the native code. I promise it will help you.

It's the whole logic. But .setUserAuthenticationRequired(true) is the line that creates the secure-hardware requirement to use biometrics or device password. It won't be enough to change juste this line though.

@SudoPlz
Copy link

SudoPlz commented Oct 8, 2020

I don't know if I did anything wrong with my tests, but selecting AES with my current config which now looks like this:

public static RAW_CREDS_CONFIG: Keychain.Options = {
    accessControl: Keychain.ACCESS_CONTROL.BIOMETRY_ANY,
    accessible: Keychain.ACCESSIBLE.WHEN_UNLOCKED,
    authenticationPrompt: {
      title: 'Unlock to log in',
    },
    storage: Keychain.STORAGE_TYPE.AES
  };

seems to have worked without any hustle.
Saving the credentials went down from 1 second to 0.1 second and I can use biometrics just fine.

@giregk
Copy link

giregk commented Oct 8, 2020

@SudoPlz Excellent !! I don't think you did anything wrong, it just means I was wrong when I said it would not work !

@abramovks
Copy link

abramovks commented Nov 18, 2020

Same problem. Samsung M31

example with RAW_CREDS_CONFIG not work

Manual link and build with .withoutWarmUp() not work.
packages.add(new KeychainPackage(new KeychainModuleBuilder().withoutWarmUp()));

logcat loop:

11-18 14:23:17.266 4462 9364 D keystore: [BEGIN::end]
11-18 14:23:17.269 4462 9365 D keystore: [UPDATE::end]
11-18 14:23:17.273 4462 9366 D keystore: [FINISH::end]
11-18 14:23:17.279 4285 4285 W keymaster_tee: [WRN]begin req PARAMS: A32 B2 P64

11-18 14:23:17.282 4462 9368 D keystore: [BEGIN::end]
11-18 14:23:17.285 4462 9369 D keystore: [UPDATE::end]
11-18 14:23:17.289 4462 9370 D keystore: [FINISH::end]
11-18 14:23:17.294 4462 9372 D keystore: [getKeyCharacteristics::end]
11-18 14:23:17.304 4462 9373 D keystore: [getKeyCharacteristics::end]

11-18 14:23:17.310 4285 4285 W keymaster_tee: [WRN]begin req PARAMS: A32 B2 P64
11-18 14:23:17.314 4462 9374 D keystore: [BEGIN::end]
11-18 14:23:17.318 4462 9375 D keystore: [UPDATE::end]
11-18 14:23:17.328 4462 9376 D keystore: [FINISH::end]

11-18 14:23:17.334 4285 4285 W keymaster_tee: [WRN]begin req PARAMS: A32 B2 P64
11-18 14:23:17.338 4462 9377 D keystore: [BEGIN::end]
11-18 14:23:17.341 4462 9379 D keystore: [UPDATE::end]
11-18 14:23:17.345 4462 9380 D keystore: [FINISH::end]
11-18 14:23:17.350 4462 9382 D keystore: [getKeyCharacteristics::end]
11-18 14:23:17.359 4462 9383 D keystore: [getKeyCharacteristics::end]

11-18 14:23:17.364 4285 4285 W keymaster_tee: [WRN]begin req PARAMS: A32 B2 P64

@WilliamAlexander
Copy link

WilliamAlexander commented Dec 1, 2020

Still on 6.1.1 here and was also experiencing UI freeze on devices. It was locking the whole phone up.
I found the culprit.
It was related to the RSA cipher. It was taking a long time to load on devices especially ones with biometrics.
As I was not using biometrics part of the keychain package and supporting API 23+, I decided to patch the build.
The patch commented out the facebook (yuck) and RSA option.
I now only work with the AES cipher and it super fast again. (Phew. I was blaming the size of the android bundle and thought Android was slow at parsing the bundle on load).

NB: This was a solution for my app as I'm only concerned about AES cipher. It will not work for everyone
The patch package.
https://www.npmjs.com/package/patch-package

The patch code:

  diff --git a/node_modules/react-native-keychain/...
  /** Default constructor. */
  public KeychainModule(@NonNull final ReactApplicationContext reactContext) {
    super(reactContext);
    prefsStorage = new PrefsStorage(reactContext);

    -- addCipherStorageToMap(new CipherStorageFacebookConceal(reactContext));
    addCipherStorageToMap(new CipherStorageKeystoreAesCbc());

    // we have a references to newer api that will fail load of app classes in old androids OS
    -- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
    --   addCipherStorageToMap(new CipherStorageKeystoreRsaEcb());
    -- }
  }

The technical part:
pre-warming the RSA cipher (calculating that vast 2048 bit prime number) plus when the exception handler took an eternity to roll up the stack and then the next step was to re-run the pre-warming with the biometrics enabled.

@xesf
Copy link

xesf commented Jan 5, 2021

Still on 6.1.1 here and was also experiencing UI freeze on devices. It was locking the whole phone up.
I found the culprit.
It was related to the RSA cipher. It was taking a long time to load on devices especially ones with biometrics.
As I was not using biometrics part of the keychain package and supporting API 23+, I decided to patch the build.
The patch commented out the facebook (yuck) and RSA option.
I now only work with the AES cipher and it super fast again. (Phew. I was blaming the size of the android bundle and thought Android was slow at parsing the bundle on load).

NB: This was a solution for my app as I'm only concerned about AES cipher. It will not work for everyone
The patch package.
https://www.npmjs.com/package/patch-package

The patch code:

  diff --git a/node_modules/react-native-keychain/...
  /** Default constructor. */
  public KeychainModule(@NonNull final ReactApplicationContext reactContext) {
    super(reactContext);
    prefsStorage = new PrefsStorage(reactContext);

    -- addCipherStorageToMap(new CipherStorageFacebookConceal(reactContext));
    addCipherStorageToMap(new CipherStorageKeystoreAesCbc());

    // we have a references to newer api that will fail load of app classes in old androids OS
    -- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
    --   addCipherStorageToMap(new CipherStorageKeystoreRsaEcb());
    -- }
  }

The technical part:
pre-warming the RSA cipher (calculating that vast 2048 bit prime number) plus when the exception handler took an eternity to roll up the stack and then the next step was to re-run the pre-warming with the biometrics enabled.

Yep, patching this fixed the issue I was having with the device being frozen for few seconds.
Thanks for posting.

@SudoPlz
Copy link

SudoPlz commented Apr 22, 2021

@oblador Are there any plans for an official fix for this on the native side of this lib?

@arpitgarg23
Copy link

With RN 0.64 and keychain 7.0.0 The warning in log is still there for android

2021-05-21 14:28:04.820 24618-24697/com.xx.xx W/CipherStorageBase: StrongBox security storage is not available.
    android.security.keystore.StrongBoxUnavailableException: Failed to generate key pair
        at android.security.keystore.AndroidKeyStoreKeyPairGeneratorSpi.generateKeystoreKeyPair(AndroidKeyStoreKeyPairGeneratorSpi.java:511)
        at android.security.keystore.AndroidKeyStoreKeyPairGeneratorSpi.generateKeyPair(AndroidKeyStoreKeyPairGeneratorSpi.java:470)
        at java.security.KeyPairGenerator$Delegate.generateKeyPair(KeyPairGenerator.java:727)
        at com.oblador.keychain.cipherStorage.CipherStorageKeystoreRsaEcb.generateKey(CipherStorageKeystoreRsaEcb.java:257)
        at com.oblador.keychain.cipherStorage.CipherStorageBase.tryGenerateStrongBoxSecurityKey(CipherStorageBase.java:461)
        at com.oblador.keychain.cipherStorage.CipherStorageBase.generateKeyAndStoreUnderAlias(CipherStorageBase.java:408)
        at com.oblador.keychain.KeychainModule.internalWarmingBestCipher(KeychainModule.java:174)
        at com.oblador.keychain.KeychainModule.lambda$DYujhqpjRgfFQ_gyuwMwyxxqDlk(Unknown Source:0)
        at com.oblador.keychain.-$$Lambda$KeychainModule$DYujhqpjRgfFQ_gyuwMwyxxqDlk.run(Unknown Source:2)
        at java.lang.Thread.run(Thread.java:764)
     Caused by: android.security.KeyStoreException: No StrongBox available
        at android.security.keystore.AndroidKeyStoreKeyPairGeneratorSpi.generateKeystoreKeyPair(AndroidKeyStoreKeyPairGeneratorSpi.java:511) 
        at android.security.keystore.AndroidKeyStoreKeyPairGeneratorSpi.generateKeyPair(AndroidKeyStoreKeyPairGeneratorSpi.java:470) 
        at java.security.KeyPairGenerator$Delegate.generateKeyPair(KeyPairGenerator.java:727) 
        at com.oblador.keychain.cipherStorage.CipherStorageKeystoreRsaEcb.generateKey(CipherStorageKeystoreRsaEcb.java:257) 
        at com.oblador.keychain.cipherStorage.CipherStorageBase.tryGenerateStrongBoxSecurityKey(CipherStorageBase.java:461) 
        at com.oblador.keychain.cipherStorage.CipherStorageBase.generateKeyAndStoreUnderAlias(CipherStorageBase.java:408) 
        at com.oblador.keychain.KeychainModule.internalWarmingBestCipher(KeychainModule.java:174) 
        at com.oblador.keychain.KeychainModule.lambda$DYujhqpjRgfFQ_gyuwMwyxxqDlk(Unknown Source:0) 
        at com.oblador.keychain.-$$Lambda$KeychainModule$DYujhqpjRgfFQ_gyuwMwyxxqDlk.run(Unknown Source:2) 
        at java.lang.Thread.run(Thread.java:764) 

@leolusoli
Copy link

2/ on my project with a simplified a

Hi, can you send an example of the second case implementation? Thanks.

@giregk
Copy link

giregk commented Jul 22, 2021

@leolusoli

2/ on my project with a simplified a

Hi, can you send an example of the second case implementation? Thanks.

Here you go: https://gist.github.com/giregk/4965b2007acbc60b5a39f4bcf4e2f7f6

@oblador
Copy link
Owner

oblador commented Oct 2, 2021

Please try out 8.0.0 which has performance improvements

@sijav
Copy link

sijav commented Nov 10, 2021

Version 8 has still the same problems

@nhannah
Copy link

nhannah commented Jan 6, 2022

I ran into this same issue on Android 12 with a Pixel 5. Android 11 was having no issue and after upgrading to 12 it would occur. I ended up with a hacky fix of waiting for the splash screen to dismiss before calling getGenericPassword as it does not seem 1:1 with the issues listed here as the solutions presented had 0 effect when done. I tracked the issue back to the UI blocking call, it seemed as though the keystore2 function call would silently fail and never unblock the UI thread. This was reproducible in versions 7.0.0 and 8.0.0.

Update: The fix did not work in a production build, running into this error:

01-06 09:56:37.474   586   586 E android.hardware.keymaster@4.1-service.citadel: GenerateKey : device response error code: UNSUPPORTED_KEY_SIZE
01-06 09:56:37.475   618   618 E keystore2: keystore2::error: In generate_key.
01-06 09:56:37.475   618   618 E keystore2:
01-06 09:56:37.475   618   618 E keystore2: Caused by:
01-06 09:56:37.475   618   618 E keystore2:     0: While generating Key without explicit attestation key.
01-06 09:56:37.475   618   618 E keystore2:     1: Error::Km(ErrorCode(-6))
01-06 09:56:37.483 14747 14807 W CipherStorageBase: StrongBox security storage is not available.
01-06 09:56:37.483 14747 14807 W CipherStorageBase: java.security.ProviderException: Failed to generate key pair.
01-06 09:56:37.483 14747 14807 W CipherStorageBase: 	at android.security.keystore2.AndroidKeyStoreKeyPairGeneratorSpi.generateKeyPairHelper(AndroidKeyStoreKeyPairGeneratorSpi.java:620)
01-06 09:56:37.483 14747 14807 W CipherStorageBase: 	at android.security.keystore2.AndroidKeyStoreKeyPairGeneratorSpi.generateKeyPair(AndroidKeyStoreKeyPairGeneratorSpi.java:545)
01-06 09:56:37.483 14747 14807 W CipherStorageBase: 	at java.security.KeyPairGenerator$Delegate.generateKeyPair(KeyPairGenerator.java:727)
01-06 09:56:37.483 14747 14807 W CipherStorageBase: 	at com.oblador.keychain.cipherStorage.CipherStorageKeystoreRsaEcb.generateKey(CipherStorageKeystoreRsaEcb.java:257)
01-06 09:56:37.483 14747 14807 W CipherStorageBase: 	at com.oblador.keychain.cipherStorage.CipherStorageBase.tryGenerateStrongBoxSecurityKey(CipherStorageBase.java:461)
01-06 09:56:37.483 14747 14807 W CipherStorageBase: 	at com.oblador.keychain.cipherStorage.CipherStorageBase.generateKeyAndStoreUnderAlias(CipherStorageBase.java:408)
01-06 09:56:37.483 14747 14807 W CipherStorageBase: 	at com.oblador.keychain.KeychainModule.internalWarmingBestCipher(KeychainModule.java:174)
01-06 09:56:37.483 14747 14807 W CipherStorageBase: 	at com.oblador.keychain.KeychainModule.lambda$DYujhqpjRgfFQ_gyuwMwyxxqDlk(Unknown Source:0)
01-06 09:56:37.483 14747 14807 W CipherStorageBase: 	at com.oblador.keychain.-$$Lambda$KeychainModule$DYujhqpjRgfFQ_gyuwMwyxxqDlk.run(Unknown Source:2)
01-06 09:56:37.483 14747 14807 W CipherStorageBase: 	at java.lang.Thread.run(Thread.java:920)
01-06 09:56:37.483 14747 14807 W CipherStorageBase: Caused by: android.security.KeyStoreException: Unsupported key size
01-06 09:56:37.483 14747 14807 W CipherStorageBase: 	at android.security.KeyStore2.getKeyStoreException(KeyStore2.java:356)
01-06 09:56:37.483 14747 14807 W CipherStorageBase: 	at android.security.KeyStoreSecurityLevel.handleExceptions(KeyStoreSecurityLevel.java:57)
01-06 09:56:37.483 14747 14807 W CipherStorageBase: 	at android.security.KeyStoreSecurityLevel.generateKey(KeyStoreSecurityLevel.java:145)
01-06 09:56:37.483 14747 14807 W CipherStorageBase: 	at android.security.keystore2.AndroidKeyStoreKeyPairGeneratorSpi.generateKeyPairHelper(AndroidKeyStoreKeyPairGeneratorSpi.java:587)```

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests