Fix for #924 - Concurrent Modification Exception (#1091)

Co-authored-by: 8bhsolutions <48874658+8bhsolutions@users.noreply.github.com>
This commit is contained in:
ebhsgit 2021-03-28 02:17:39 +11:00 committed by GitHub
parent 11364918b2
commit 9dcf3eb68b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -19,7 +19,9 @@
package org.apache.cordova;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import org.json.JSONException;
@ -40,8 +42,8 @@ public class PluginManager {
private static final int SLOW_EXEC_WARNING_THRESHOLD = Debug.isDebuggerConnected() ? 60 : 16;
// List of service entries
private final LinkedHashMap<String, CordovaPlugin> pluginMap = new LinkedHashMap<String, CordovaPlugin>();
private final LinkedHashMap<String, PluginEntry> entryMap = new LinkedHashMap<String, PluginEntry>();
private final Map<String, CordovaPlugin> pluginMap = Collections.synchronizedMap(new LinkedHashMap<String, CordovaPlugin>());
private final Map<String, PluginEntry> entryMap = Collections.synchronizedMap(new LinkedHashMap<String, PluginEntry>());
private final CordovaInterface ctx;
private final CordovaWebView app;
@ -90,16 +92,20 @@ public class PluginManager {
* Create plugins objects that have onload set.
*/
private void startupPlugins() {
synchronized (entryMap) {
for (PluginEntry entry : entryMap.values()) {
// Add a null entry to for each non-startup plugin to avoid ConcurrentModificationException
// When iterating plugins.
if (entry.onload) {
getPlugin(entry.service);
} else {
}
else {
LOG.d(TAG, "startupPlugins: put - " + entry.service);
pluginMap.put(entry.service, null);
}
}
}
}
/**
* Receives a request for execution and fulfills it by finding the appropriate
@ -169,6 +175,7 @@ public class PluginManager {
ret = instantiatePlugin(pe.pluginClass);
}
ret.privateInitialize(service, ctx, app, app.getPreferences());
LOG.d(TAG, "getPlugin - put: " + service);
pluginMap.put(service, ret);
}
return ret;
@ -196,6 +203,7 @@ public class PluginManager {
this.entryMap.put(entry.service, entry);
if (entry.plugin != null) {
entry.plugin.privateInitialize(entry.service, ctx, app, app.getPreferences());
LOG.d(TAG, "addService: put - " + entry.service);
pluginMap.put(entry.service, entry.plugin);
}
}
@ -206,12 +214,14 @@ public class PluginManager {
* @param multitasking Flag indicating if multitasking is turned on for app
*/
public void onPause(boolean multitasking) {
synchronized (this.pluginMap) {
for (CordovaPlugin plugin : this.pluginMap.values()) {
if (plugin != null) {
plugin.onPause(multitasking);
}
}
}
}
/**
* Called when the system received an HTTP authentication request. Plugins can use
@ -226,11 +236,13 @@ public class PluginManager {
*
*/
public boolean onReceivedHttpAuthRequest(CordovaWebView view, ICordovaHttpAuthHandler handler, String host, String realm) {
synchronized (this.pluginMap) {
for (CordovaPlugin plugin : this.pluginMap.values()) {
if (plugin != null && plugin.onReceivedHttpAuthRequest(app, handler, host, realm)) {
return true;
}
}
}
return false;
}
@ -245,11 +257,13 @@ public class PluginManager {
*
*/
public boolean onReceivedClientCertRequest(CordovaWebView view, ICordovaClientCertRequest request) {
synchronized (this.pluginMap) {
for (CordovaPlugin plugin : this.pluginMap.values()) {
if (plugin != null && plugin.onReceivedClientCertRequest(app, request)) {
return true;
}
}
}
return false;
}
@ -259,45 +273,53 @@ public class PluginManager {
* @param multitasking Flag indicating if multitasking is turned on for app
*/
public void onResume(boolean multitasking) {
synchronized (this.pluginMap) {
for (CordovaPlugin plugin : this.pluginMap.values()) {
if (plugin != null) {
plugin.onResume(multitasking);
}
}
}
}
/**
* Called when the activity is becoming visible to the user.
*/
public void onStart() {
synchronized (this.pluginMap) {
for (CordovaPlugin plugin : this.pluginMap.values()) {
if (plugin != null) {
plugin.onStart();
}
}
}
}
/**
* Called when the activity is no longer visible to the user.
*/
public void onStop() {
synchronized (this.pluginMap) {
for (CordovaPlugin plugin : this.pluginMap.values()) {
if (plugin != null) {
plugin.onStop();
}
}
}
}
/**
* The final call you receive before your activity is destroyed.
*/
public void onDestroy() {
synchronized (this.pluginMap) {
for (CordovaPlugin plugin : this.pluginMap.values()) {
if (plugin != null) {
plugin.onDestroy();
}
}
}
}
/**
* Send a message to all plugins.
@ -307,6 +329,8 @@ public class PluginManager {
* @return Object to stop propagation or null
*/
public Object postMessage(String id, Object data) {
LOG.d(TAG, "postMessage: " + id);
synchronized (this.pluginMap) {
for (CordovaPlugin plugin : this.pluginMap.values()) {
if (plugin != null) {
Object obj = plugin.onMessage(id, data);
@ -315,6 +339,7 @@ public class PluginManager {
}
}
}
}
return ctx.onMessage(id, data);
}
@ -322,12 +347,14 @@ public class PluginManager {
* Called when the activity receives a new intent.
*/
public void onNewIntent(Intent intent) {
synchronized (this.pluginMap) {
for (CordovaPlugin plugin : this.pluginMap.values()) {
if (plugin != null) {
plugin.onNewIntent(intent);
}
}
}
}
/**
* Called when the webview is going to request an external resource.
@ -341,6 +368,7 @@ public class PluginManager {
* false to block the resource.
*/
public boolean shouldAllowRequest(String url) {
synchronized (this.entryMap) {
for (PluginEntry entry : this.entryMap.values()) {
CordovaPlugin plugin = pluginMap.get(entry.service);
if (plugin != null) {
@ -350,6 +378,7 @@ public class PluginManager {
}
}
}
}
// Default policy:
if (url.startsWith("blob:") || url.startsWith("data:") || url.startsWith("about:blank")) {
@ -379,6 +408,7 @@ public class PluginManager {
* false to block the navigation.
*/
public boolean shouldAllowNavigation(String url) {
synchronized (this.entryMap) {
for (PluginEntry entry : this.entryMap.values()) {
CordovaPlugin plugin = pluginMap.get(entry.service);
if (plugin != null) {
@ -388,6 +418,7 @@ public class PluginManager {
}
}
}
}
// Default policy:
return url.startsWith("file://") || url.startsWith("about:blank");
@ -398,6 +429,7 @@ public class PluginManager {
* Called when the webview is requesting the exec() bridge be enabled.
*/
public boolean shouldAllowBridgeAccess(String url) {
synchronized (this.entryMap) {
for (PluginEntry entry : this.entryMap.values()) {
CordovaPlugin plugin = pluginMap.get(entry.service);
if (plugin != null) {
@ -407,6 +439,7 @@ public class PluginManager {
}
}
}
}
// Default policy:
return url.startsWith("file://");
@ -425,6 +458,7 @@ public class PluginManager {
* false to block the intent.
*/
public Boolean shouldOpenExternalUrl(String url) {
synchronized (this.entryMap) {
for (PluginEntry entry : this.entryMap.values()) {
CordovaPlugin plugin = pluginMap.get(entry.service);
if (plugin != null) {
@ -434,6 +468,7 @@ public class PluginManager {
}
}
}
}
// Default policy:
// External URLs are not allowed
return false;
@ -446,6 +481,7 @@ public class PluginManager {
* @return Return false to allow the URL to load, return true to prevent the URL from loading.
*/
public boolean onOverrideUrlLoading(String url) {
synchronized (this.entryMap) {
for (PluginEntry entry : this.entryMap.values()) {
CordovaPlugin plugin = pluginMap.get(entry.service);
if (plugin != null && plugin.onOverrideUrlLoading(url)) {
@ -454,19 +490,23 @@ public class PluginManager {
}
return false;
}
}
/**
* Called when the app navigates or refreshes.
*/
public void onReset() {
synchronized (this.pluginMap) {
for (CordovaPlugin plugin : this.pluginMap.values()) {
if (plugin != null) {
plugin.onReset();
}
}
}
}
Uri remapUri(Uri uri) {
synchronized (this.pluginMap) {
for (CordovaPlugin plugin : this.pluginMap.values()) {
if (plugin != null) {
Uri ret = plugin.remapUri(uri);
@ -475,6 +515,7 @@ public class PluginManager {
}
}
}
}
return null;
}
@ -504,15 +545,18 @@ public class PluginManager {
* @param newConfig The new device configuration
*/
public void onConfigurationChanged(Configuration newConfig) {
synchronized (this.pluginMap) {
for (CordovaPlugin plugin : this.pluginMap.values()) {
if (plugin != null) {
plugin.onConfigurationChanged(newConfig);
}
}
}
}
public Bundle onSaveInstanceState() {
Bundle state = new Bundle();
synchronized (this.pluginMap) {
for (CordovaPlugin plugin : this.pluginMap.values()) {
if (plugin != null) {
Bundle pluginState = plugin.onSaveInstanceState();
@ -521,6 +565,7 @@ public class PluginManager {
}
}
}
}
return state;
}
}