react-native-security-suite 0.9.22 → 1.0.0-rc.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (189) hide show
  1. package/README.md +291 -69
  2. package/android/build.gradle +11 -0
  3. package/android/gradle.properties +1 -1
  4. package/android/src/main/java/com/securitysuite/CryptoConfig.java +106 -0
  5. package/android/src/main/java/com/securitysuite/CryptoUtils.java +155 -0
  6. package/android/src/main/java/com/securitysuite/EcdhKeyStore.java +60 -0
  7. package/android/src/main/java/com/securitysuite/HeaderSanitizer.java +75 -0
  8. package/android/src/main/java/com/securitysuite/JWSGenerator.java +237 -32
  9. package/android/src/main/java/com/securitysuite/JwsFetchPayload.java +81 -0
  10. package/android/src/main/java/com/securitysuite/Obfuscation.java +57 -0
  11. package/android/src/main/java/com/securitysuite/SecureStorageNative.java +211 -0
  12. package/android/src/main/java/com/securitysuite/SecureView.java +2 -10
  13. package/android/src/main/java/com/securitysuite/SecureWindowHelper.java +30 -0
  14. package/android/src/main/java/com/securitysuite/SecuritySuiteModule.java +317 -102
  15. package/android/src/main/java/com/securitysuite/Sslpinning.java +219 -106
  16. package/android/src/main/java/com/securitysuite/security/AppIntegrityChecker.java +133 -0
  17. package/android/src/main/java/com/securitysuite/security/EmulatorDetector.java +145 -0
  18. package/android/src/main/java/com/securitysuite/security/RuntimeDetector.java +234 -0
  19. package/android/src/test/java/com/securitysuite/JWSGeneratorTest.java +153 -0
  20. package/android/src/test/java/com/securitysuite/SecureStorageNativeTest.java +37 -0
  21. package/ios/CryptoConfig.swift +73 -0
  22. package/ios/JWSGenerator.swift +288 -0
  23. package/ios/JWSGeneratorTests.swift +168 -0
  24. package/ios/KeychainHelper.swift +104 -0
  25. package/ios/Obfuscation.swift +42 -0
  26. package/ios/SecureStorageNative.swift +84 -0
  27. package/ios/Security/AppIntegrityChecker.swift +85 -0
  28. package/ios/Security/EmulatorDetector.swift +45 -0
  29. package/ios/Security/RuntimeDetector.swift +107 -0
  30. package/ios/SecuritySuite.mm +28 -4
  31. package/ios/SecuritySuite.swift +427 -134
  32. package/ios/SslPinning.swift +242 -263
  33. package/lib/commonjs/clipboard/index.js +3 -0
  34. package/lib/commonjs/clipboard/index.js.map +1 -0
  35. package/lib/commonjs/crypto/index.js +29 -0
  36. package/lib/commonjs/crypto/index.js.map +1 -0
  37. package/lib/commonjs/device/index.js +40 -0
  38. package/lib/commonjs/device/index.js.map +1 -0
  39. package/lib/commonjs/errors.js +62 -0
  40. package/lib/commonjs/errors.js.map +1 -0
  41. package/lib/commonjs/index.js +220 -151
  42. package/lib/commonjs/index.js.map +1 -1
  43. package/lib/commonjs/integrity/index.js +40 -0
  44. package/lib/commonjs/integrity/index.js.map +1 -0
  45. package/lib/commonjs/jws.js +141 -0
  46. package/lib/commonjs/jws.js.map +1 -0
  47. package/lib/commonjs/legacy/cryptoOptions.js +29 -0
  48. package/lib/commonjs/legacy/cryptoOptions.js.map +1 -0
  49. package/lib/commonjs/native/bridge.js +23 -0
  50. package/lib/commonjs/native/bridge.js.map +1 -0
  51. package/lib/commonjs/network/index.js +3 -0
  52. package/lib/commonjs/network/index.js.map +1 -0
  53. package/lib/commonjs/risk/score.js +36 -0
  54. package/lib/commonjs/risk/score.js.map +1 -0
  55. package/lib/commonjs/runtime/index.js +31 -0
  56. package/lib/commonjs/runtime/index.js.map +1 -0
  57. package/lib/commonjs/screen/index.js +13 -0
  58. package/lib/commonjs/screen/index.js.map +1 -0
  59. package/lib/commonjs/securitySuite/index.js +42 -0
  60. package/lib/commonjs/securitySuite/index.js.map +1 -0
  61. package/lib/commonjs/storage/index.js +3 -0
  62. package/lib/commonjs/storage/index.js.map +1 -0
  63. package/lib/commonjs/types/detection.js +2 -0
  64. package/lib/commonjs/types/detection.js.map +1 -0
  65. package/lib/module/clipboard/index.js +3 -0
  66. package/lib/module/clipboard/index.js.map +1 -0
  67. package/lib/module/crypto/index.js +25 -0
  68. package/lib/module/crypto/index.js.map +1 -0
  69. package/lib/module/device/index.js +36 -0
  70. package/lib/module/device/index.js.map +1 -0
  71. package/lib/module/errors.js +55 -0
  72. package/lib/module/errors.js.map +1 -0
  73. package/lib/module/index.js +147 -148
  74. package/lib/module/index.js.map +1 -1
  75. package/lib/module/integrity/index.js +36 -0
  76. package/lib/module/integrity/index.js.map +1 -0
  77. package/lib/module/jws.js +127 -0
  78. package/lib/module/jws.js.map +1 -0
  79. package/lib/module/legacy/cryptoOptions.js +25 -0
  80. package/lib/module/legacy/cryptoOptions.js.map +1 -0
  81. package/lib/module/native/bridge.js +19 -0
  82. package/lib/module/native/bridge.js.map +1 -0
  83. package/lib/module/network/index.js +3 -0
  84. package/lib/module/network/index.js.map +1 -0
  85. package/lib/module/risk/score.js +32 -0
  86. package/lib/module/risk/score.js.map +1 -0
  87. package/lib/module/runtime/index.js +27 -0
  88. package/lib/module/runtime/index.js.map +1 -0
  89. package/lib/module/screen/index.js +5 -0
  90. package/lib/module/screen/index.js.map +1 -0
  91. package/lib/module/securitySuite/index.js +38 -0
  92. package/lib/module/securitySuite/index.js.map +1 -0
  93. package/lib/module/storage/index.js +3 -0
  94. package/lib/module/storage/index.js.map +1 -0
  95. package/lib/module/types/detection.js +2 -0
  96. package/lib/module/types/detection.js.map +1 -0
  97. package/lib/typescript/commonjs/docs/api-v1-proposal.d.ts +215 -0
  98. package/lib/typescript/commonjs/docs/api-v1-proposal.d.ts.map +1 -0
  99. package/lib/typescript/commonjs/src/SecureView.d.ts +1 -1
  100. package/lib/typescript/commonjs/src/SecureView.d.ts.map +1 -1
  101. package/lib/typescript/commonjs/src/clipboard/index.d.ts +2 -0
  102. package/lib/typescript/commonjs/src/clipboard/index.d.ts.map +1 -0
  103. package/lib/typescript/commonjs/src/crypto/index.d.ts +15 -0
  104. package/lib/typescript/commonjs/src/crypto/index.d.ts.map +1 -0
  105. package/lib/typescript/commonjs/src/device/index.d.ts +11 -0
  106. package/lib/typescript/commonjs/src/device/index.d.ts.map +1 -0
  107. package/lib/typescript/commonjs/src/errors.d.ts +17 -0
  108. package/lib/typescript/commonjs/src/errors.d.ts.map +1 -0
  109. package/lib/typescript/commonjs/src/helpers.d.ts.map +1 -1
  110. package/lib/typescript/commonjs/src/index.d.ts +77 -24
  111. package/lib/typescript/commonjs/src/index.d.ts.map +1 -1
  112. package/lib/typescript/commonjs/src/integrity/index.d.ts +6 -0
  113. package/lib/typescript/commonjs/src/integrity/index.d.ts.map +1 -0
  114. package/lib/typescript/commonjs/src/jws.d.ts +44 -0
  115. package/lib/typescript/commonjs/src/jws.d.ts.map +1 -0
  116. package/lib/typescript/commonjs/src/legacy/cryptoOptions.d.ts +35 -0
  117. package/lib/typescript/commonjs/src/legacy/cryptoOptions.d.ts.map +1 -0
  118. package/lib/typescript/commonjs/src/native/bridge.d.ts +12 -0
  119. package/lib/typescript/commonjs/src/native/bridge.d.ts.map +1 -0
  120. package/lib/typescript/commonjs/src/network/index.d.ts +2 -0
  121. package/lib/typescript/commonjs/src/network/index.d.ts.map +1 -0
  122. package/lib/typescript/commonjs/src/risk/score.d.ts +12 -0
  123. package/lib/typescript/commonjs/src/risk/score.d.ts.map +1 -0
  124. package/lib/typescript/commonjs/src/runtime/index.d.ts +6 -0
  125. package/lib/typescript/commonjs/src/runtime/index.d.ts.map +1 -0
  126. package/lib/typescript/commonjs/src/screen/index.d.ts +3 -0
  127. package/lib/typescript/commonjs/src/screen/index.d.ts.map +1 -0
  128. package/lib/typescript/commonjs/src/securitySuite/index.d.ts +6 -0
  129. package/lib/typescript/commonjs/src/securitySuite/index.d.ts.map +1 -0
  130. package/lib/typescript/commonjs/src/storage/index.d.ts +2 -0
  131. package/lib/typescript/commonjs/src/storage/index.d.ts.map +1 -0
  132. package/lib/typescript/commonjs/src/types/detection.d.ts +41 -0
  133. package/lib/typescript/commonjs/src/types/detection.d.ts.map +1 -0
  134. package/lib/typescript/module/docs/api-v1-proposal.d.ts +215 -0
  135. package/lib/typescript/module/docs/api-v1-proposal.d.ts.map +1 -0
  136. package/lib/typescript/module/src/SecureView.d.ts +1 -1
  137. package/lib/typescript/module/src/SecureView.d.ts.map +1 -1
  138. package/lib/typescript/module/src/clipboard/index.d.ts +2 -0
  139. package/lib/typescript/module/src/clipboard/index.d.ts.map +1 -0
  140. package/lib/typescript/module/src/crypto/index.d.ts +15 -0
  141. package/lib/typescript/module/src/crypto/index.d.ts.map +1 -0
  142. package/lib/typescript/module/src/device/index.d.ts +11 -0
  143. package/lib/typescript/module/src/device/index.d.ts.map +1 -0
  144. package/lib/typescript/module/src/errors.d.ts +17 -0
  145. package/lib/typescript/module/src/errors.d.ts.map +1 -0
  146. package/lib/typescript/module/src/helpers.d.ts.map +1 -1
  147. package/lib/typescript/module/src/index.d.ts +77 -24
  148. package/lib/typescript/module/src/index.d.ts.map +1 -1
  149. package/lib/typescript/module/src/integrity/index.d.ts +6 -0
  150. package/lib/typescript/module/src/integrity/index.d.ts.map +1 -0
  151. package/lib/typescript/module/src/jws.d.ts +44 -0
  152. package/lib/typescript/module/src/jws.d.ts.map +1 -0
  153. package/lib/typescript/module/src/legacy/cryptoOptions.d.ts +35 -0
  154. package/lib/typescript/module/src/legacy/cryptoOptions.d.ts.map +1 -0
  155. package/lib/typescript/module/src/native/bridge.d.ts +12 -0
  156. package/lib/typescript/module/src/native/bridge.d.ts.map +1 -0
  157. package/lib/typescript/module/src/network/index.d.ts +2 -0
  158. package/lib/typescript/module/src/network/index.d.ts.map +1 -0
  159. package/lib/typescript/module/src/risk/score.d.ts +12 -0
  160. package/lib/typescript/module/src/risk/score.d.ts.map +1 -0
  161. package/lib/typescript/module/src/runtime/index.d.ts +6 -0
  162. package/lib/typescript/module/src/runtime/index.d.ts.map +1 -0
  163. package/lib/typescript/module/src/screen/index.d.ts +3 -0
  164. package/lib/typescript/module/src/screen/index.d.ts.map +1 -0
  165. package/lib/typescript/module/src/securitySuite/index.d.ts +6 -0
  166. package/lib/typescript/module/src/securitySuite/index.d.ts.map +1 -0
  167. package/lib/typescript/module/src/storage/index.d.ts +2 -0
  168. package/lib/typescript/module/src/storage/index.d.ts.map +1 -0
  169. package/lib/typescript/module/src/types/detection.d.ts +41 -0
  170. package/lib/typescript/module/src/types/detection.d.ts.map +1 -0
  171. package/package.json +2 -10
  172. package/src/clipboard/index.ts +1 -0
  173. package/src/crypto/index.ts +40 -0
  174. package/src/device/index.ts +47 -0
  175. package/src/errors.ts +84 -0
  176. package/src/index.tsx +293 -195
  177. package/src/integrity/index.ts +46 -0
  178. package/src/jws.ts +213 -0
  179. package/src/legacy/cryptoOptions.ts +84 -0
  180. package/src/native/bridge.ts +37 -0
  181. package/src/network/index.ts +1 -0
  182. package/src/risk/score.ts +49 -0
  183. package/src/runtime/index.ts +43 -0
  184. package/src/screen/index.ts +2 -0
  185. package/src/securitySuite/index.ts +45 -0
  186. package/src/storage/index.ts +1 -0
  187. package/src/types/detection.ts +46 -0
  188. package/android/src/main/java/com/securitysuite/StorageEncryption.java +0 -52
  189. package/ios/StorageEncryption.swift +0 -89
@@ -0,0 +1,57 @@
1
+ package com.securitysuite;
2
+
3
+ import android.util.Base64;
4
+
5
+ import java.security.MessageDigest;
6
+ import java.security.SecureRandom;
7
+ import java.util.Arrays;
8
+
9
+ import javax.crypto.Cipher;
10
+ import javax.crypto.spec.IvParameterSpec;
11
+ import javax.crypto.spec.SecretKeySpec;
12
+
13
+ /**
14
+ * Local obfuscation only — NOT secure encryption. Requires an explicit user-provided secret.
15
+ * Never use for credentials, tokens, or PII at rest.
16
+ */
17
+ public class Obfuscation {
18
+ private static byte[] deriveKey(String secret) throws Exception {
19
+ if (secret == null || secret.trim().isEmpty()) {
20
+ throw new IllegalArgumentException("Obfuscation secret is required");
21
+ }
22
+ MessageDigest md = MessageDigest.getInstance("SHA-256");
23
+ return md.digest(secret.getBytes("utf-8"));
24
+ }
25
+
26
+ public static String obfuscate(String input, String secret) throws Exception {
27
+ byte[] iv = CryptoUtils.randomBytes(16);
28
+ Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
29
+ cipher.init(
30
+ Cipher.ENCRYPT_MODE,
31
+ new SecretKeySpec(deriveKey(secret), "AES"),
32
+ new IvParameterSpec(iv)
33
+ );
34
+ byte[] cipherText = cipher.doFinal(input.getBytes("utf-8"));
35
+ byte[] ivAndCipherText = new byte[iv.length + cipherText.length];
36
+ System.arraycopy(iv, 0, ivAndCipherText, 0, iv.length);
37
+ System.arraycopy(cipherText, 0, ivAndCipherText, iv.length, cipherText.length);
38
+ return Base64.encodeToString(ivAndCipherText, Base64.NO_WRAP);
39
+ }
40
+
41
+ public static String deobfuscate(String encoded, String secret) throws Exception {
42
+ byte[] ivAndCipherText = Base64.decode(encoded, Base64.NO_WRAP);
43
+ if (ivAndCipherText.length < 17) {
44
+ throw new IllegalArgumentException("Invalid obfuscated payload");
45
+ }
46
+ byte[] iv = Arrays.copyOfRange(ivAndCipherText, 0, 16);
47
+ byte[] cipherText = Arrays.copyOfRange(ivAndCipherText, 16, ivAndCipherText.length);
48
+
49
+ Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
50
+ cipher.init(
51
+ Cipher.DECRYPT_MODE,
52
+ new SecretKeySpec(deriveKey(secret), "AES"),
53
+ new IvParameterSpec(iv)
54
+ );
55
+ return new String(cipher.doFinal(cipherText), "utf-8");
56
+ }
57
+ }
@@ -0,0 +1,211 @@
1
+ package com.securitysuite;
2
+
3
+ import android.content.Context;
4
+ import android.content.SharedPreferences;
5
+ import android.security.keystore.KeyPermanentlyInvalidatedException;
6
+
7
+ import androidx.security.crypto.EncryptedSharedPreferences;
8
+ import androidx.security.crypto.MasterKey;
9
+
10
+ import com.facebook.react.bridge.Arguments;
11
+ import com.facebook.react.bridge.ReadableArray;
12
+ import com.facebook.react.bridge.WritableArray;
13
+
14
+ import java.io.IOException;
15
+ import java.security.GeneralSecurityException;
16
+ import java.security.KeyStoreException;
17
+ import java.security.ProviderException;
18
+ import java.security.UnrecoverableKeyException;
19
+ import java.util.Map;
20
+
21
+ /**
22
+ * Hardware-backed encrypted storage via Android Keystore + EncryptedSharedPreferences.
23
+ * Keys are managed by the system — no hardcoded secrets or salts.
24
+ */
25
+ public final class SecureStorageNative {
26
+ private static final String PREFS_FILE = "com.securitysuite.secure_storage";
27
+ private static final String KEY_PREFIX = "rss:";
28
+ private static final String SECURE_STORAGE_FAILED = "Secure storage operation failed";
29
+
30
+ private static volatile SharedPreferences cachedPreferences;
31
+
32
+ private SecureStorageNative() {}
33
+
34
+ private static boolean isKeyStoreOrDecryptionFailure(Throwable error) {
35
+ Throwable current = error;
36
+ while (current != null) {
37
+ if (current instanceof GeneralSecurityException
38
+ || current instanceof KeyStoreException
39
+ || current instanceof UnrecoverableKeyException
40
+ || current instanceof KeyPermanentlyInvalidatedException
41
+ || current instanceof ProviderException
42
+ || current instanceof IOException) {
43
+ return true;
44
+ }
45
+ current = current.getCause();
46
+ }
47
+ return false;
48
+ }
49
+
50
+ private static SharedPreferences createEncryptedPreferences(Context context)
51
+ throws GeneralSecurityException, IOException {
52
+ MasterKey masterKey =
53
+ new MasterKey.Builder(context)
54
+ .setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
55
+ .build();
56
+
57
+ return EncryptedSharedPreferences.create(
58
+ context,
59
+ PREFS_FILE,
60
+ masterKey,
61
+ EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
62
+ EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM);
63
+ }
64
+
65
+ private static SharedPreferences getPreferences(Context context) throws Exception {
66
+ SharedPreferences preferences = cachedPreferences;
67
+ if (preferences != null) {
68
+ return preferences;
69
+ }
70
+
71
+ synchronized (SecureStorageNative.class) {
72
+ if (cachedPreferences != null) {
73
+ return cachedPreferences;
74
+ }
75
+
76
+ try {
77
+ cachedPreferences = createEncryptedPreferences(context.getApplicationContext());
78
+ return cachedPreferences;
79
+ } catch (Exception initialError) {
80
+ if (!isKeyStoreOrDecryptionFailure(initialError)) {
81
+ throw new Exception(
82
+ SECURE_STORAGE_FAILED + ": KeyStore initialization failed", initialError);
83
+ }
84
+
85
+ // One-time recovery: drop legacy/plaintext prefs so encrypted storage starts fresh.
86
+ context.getApplicationContext().deleteSharedPreferences(PREFS_FILE);
87
+ cachedPreferences = null;
88
+
89
+ try {
90
+ cachedPreferences = createEncryptedPreferences(context.getApplicationContext());
91
+ return cachedPreferences;
92
+ } catch (Exception retryError) {
93
+ throw new Exception(
94
+ SECURE_STORAGE_FAILED + ": KeyStore initialization failed", retryError);
95
+ }
96
+ }
97
+ }
98
+ }
99
+
100
+ private static String namespacedKey(String key) throws IllegalArgumentException {
101
+ if (key == null || key.trim().isEmpty()) {
102
+ throw new IllegalArgumentException("Storage key is required");
103
+ }
104
+ return KEY_PREFIX + key.trim();
105
+ }
106
+
107
+ private static Exception secureStorageException(String operation, Exception error) {
108
+ String detail = error.getMessage();
109
+ if (detail == null || detail.isEmpty()) {
110
+ detail = error.getClass().getSimpleName();
111
+ }
112
+ return new Exception(SECURE_STORAGE_FAILED + " (" + operation + "): " + detail, error);
113
+ }
114
+
115
+ public static void setItem(Context context, String key, String value) throws Exception {
116
+ try {
117
+ getPreferences(context).edit().putString(namespacedKey(key), value).apply();
118
+ } catch (Exception error) {
119
+ throw secureStorageException("setItem", error);
120
+ }
121
+ }
122
+
123
+ public static String getItem(Context context, String key) throws Exception {
124
+ try {
125
+ return getPreferences(context).getString(namespacedKey(key), null);
126
+ } catch (Exception error) {
127
+ throw secureStorageException("getItem", error);
128
+ }
129
+ }
130
+
131
+ public static void removeItem(Context context, String key) throws Exception {
132
+ try {
133
+ getPreferences(context).edit().remove(namespacedKey(key)).apply();
134
+ } catch (Exception error) {
135
+ throw secureStorageException("removeItem", error);
136
+ }
137
+ }
138
+
139
+ public static void clear(Context context) throws Exception {
140
+ try {
141
+ SharedPreferences prefs = getPreferences(context);
142
+ SharedPreferences.Editor editor = prefs.edit();
143
+ for (Map.Entry<String, ?> entry : prefs.getAll().entrySet()) {
144
+ if (entry.getKey().startsWith(KEY_PREFIX)) {
145
+ editor.remove(entry.getKey());
146
+ }
147
+ }
148
+ editor.apply();
149
+ } catch (Exception error) {
150
+ throw secureStorageException("clear", error);
151
+ }
152
+ }
153
+
154
+ public static WritableArray getAllKeys(Context context) throws Exception {
155
+ try {
156
+ WritableArray keys = Arguments.createArray();
157
+ for (Map.Entry<String, ?> entry : getPreferences(context).getAll().entrySet()) {
158
+ if (entry.getKey().startsWith(KEY_PREFIX)) {
159
+ keys.pushString(entry.getKey().substring(KEY_PREFIX.length()));
160
+ }
161
+ }
162
+ return keys;
163
+ } catch (Exception error) {
164
+ throw secureStorageException("getAllKeys", error);
165
+ }
166
+ }
167
+
168
+ public static void multiSet(Context context, ReadableArray pairs) throws Exception {
169
+ try {
170
+ SharedPreferences.Editor editor = getPreferences(context).edit();
171
+ for (int i = 0; i < pairs.size(); i++) {
172
+ ReadableArray pair = pairs.getArray(i);
173
+ if (pair != null && pair.size() == 2) {
174
+ editor.putString(namespacedKey(pair.getString(0)), pair.getString(1));
175
+ }
176
+ }
177
+ editor.apply();
178
+ } catch (Exception error) {
179
+ throw secureStorageException("multiSet", error);
180
+ }
181
+ }
182
+
183
+ public static WritableArray multiGet(Context context, ReadableArray keys) throws Exception {
184
+ try {
185
+ SharedPreferences prefs = getPreferences(context);
186
+ WritableArray result = Arguments.createArray();
187
+ for (int i = 0; i < keys.size(); i++) {
188
+ String key = keys.getString(i);
189
+ WritableArray pair = Arguments.createArray();
190
+ pair.pushString(key);
191
+ pair.pushString(prefs.getString(namespacedKey(key), null));
192
+ result.pushArray(pair);
193
+ }
194
+ return result;
195
+ } catch (Exception error) {
196
+ throw secureStorageException("multiGet", error);
197
+ }
198
+ }
199
+
200
+ public static void multiRemove(Context context, ReadableArray keys) throws Exception {
201
+ try {
202
+ SharedPreferences.Editor editor = getPreferences(context).edit();
203
+ for (int i = 0; i < keys.size(); i++) {
204
+ editor.remove(namespacedKey(keys.getString(i)));
205
+ }
206
+ editor.apply();
207
+ } catch (Exception error) {
208
+ throw secureStorageException("multiRemove", error);
209
+ }
210
+ }
211
+ }
@@ -3,8 +3,6 @@ package com.securitysuite;
3
3
  import android.app.Activity;
4
4
  import android.graphics.Color;
5
5
  import android.view.View;
6
- import android.view.Window;
7
- import android.view.WindowManager;
8
6
  import android.widget.FrameLayout;
9
7
  import android.content.Context;
10
8
 
@@ -34,10 +32,7 @@ public class SecureView extends FrameLayout {
34
32
  super.onAttachedToWindow();
35
33
  final Activity activity = reactContext.getCurrentActivity();
36
34
  if (activity != null) {
37
- activity.runOnUiThread(() -> {
38
- Window window = activity.getWindow();
39
- window.addFlags(WindowManager.LayoutParams.FLAG_SECURE);
40
- });
35
+ activity.runOnUiThread(() -> SecureWindowHelper.acquire(activity.getWindow()));
41
36
  }
42
37
  }
43
38
 
@@ -46,10 +41,7 @@ public class SecureView extends FrameLayout {
46
41
  super.onDetachedFromWindow();
47
42
  final Activity activity = reactContext.getCurrentActivity();
48
43
  if (activity != null) {
49
- activity.runOnUiThread(() -> {
50
- Window window = activity.getWindow();
51
- window.clearFlags(WindowManager.LayoutParams.FLAG_SECURE);
52
- });
44
+ activity.runOnUiThread(() -> SecureWindowHelper.release(activity.getWindow()));
53
45
  }
54
46
  }
55
47
 
@@ -0,0 +1,30 @@
1
+ package com.securitysuite;
2
+
3
+ import android.view.Window;
4
+ import android.view.WindowManager;
5
+
6
+ public final class SecureWindowHelper {
7
+ private static int refCount = 0;
8
+
9
+ private SecureWindowHelper() {}
10
+
11
+ public static synchronized void acquire(Window window) {
12
+ if (window == null) {
13
+ return;
14
+ }
15
+ if (refCount == 0) {
16
+ window.addFlags(WindowManager.LayoutParams.FLAG_SECURE);
17
+ }
18
+ refCount++;
19
+ }
20
+
21
+ public static synchronized void release(Window window) {
22
+ if (window == null) {
23
+ return;
24
+ }
25
+ refCount = Math.max(0, refCount - 1);
26
+ if (refCount == 0) {
27
+ window.clearFlags(WindowManager.LayoutParams.FLAG_SECURE);
28
+ }
29
+ }
30
+ }