Phonegap/Cordova App breaks in Jelly Bean - Access-Control-Allow-Origin and setAllowUniversalAccessFromFileURLs
Asked Answered
E

1

4

I've been developing* with Cordova (aka Phonegap) for Android for well over a year now and am trying to make my apps available to run in Jelly Bean, but I am getting the following error:

XMLHttpRequest cannot load http://127.0.0.1:40582/[somerandomstring]. Origin null is not allowed by Access-Control-Allow-Origin. at null:1

(and similar errors for any subsequent ajax requesting use of localhost or file://) Just to test, I grant access to everything in the config.xml in the section for Access-Control-Allow-Origin

<access origin="*"/>
<access origin="http://127.0.0.1*"/>

In my research I have discovered the error is related to a setting change that Google made as of Android Jelly Bean. Here is what I found: From: https://git-wip-us.apache.org/repos/asf?p=incubator-cordova-android.git;a=commitdiff;h=07439ff9

-- This is from org.apache.cordova.CordovaWebView

// Jellybean rightfully tried to lock this down. Too bad they didn't give us a whitelist
// while we do this
if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1)
    Level16Apis.enableUniversalAccess(settings);

-- This is also from org.apache.cordova.CordovaWebView

// Wrapping these functions in their own class prevents warnings in adb like:
// VFY: unable to resolve virtual method 285: Landroid/webkit/WebSettings;.setAllowUniversalAccessFromFileURLs
@TargetApi(16)
private static class Level16Apis {
    static void enableUniversalAccess(WebSettings settings) {
         settings.setAllowUniversalAccessFromFileURLs(true);
     }
}

It's nice that Cordova tried to work around the change, but unfortunately this does not work...

In these SO threads here and here I found a common solution, to simply change a setting as follows:

if(android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
    super.appView.getSettings().setAllowUniversalAccessFromFileURLs(true);    
}

Now I get the following warning:

Call requires API level 16 (current min is 8) 
android.webkit.WebSettings#setAllowUniversalAccessFromFileURLs

Here is what I have for the api in my AndroidManifest.xml

<uses-sdk android:minSdkVersion="8" android:targetSdkVersion="16" />

Why is it requiring me to change the minSdkVersion to 16 rather than follow my targetSdkVersion which is 16?

Thoughts?

Notes: I'm currently using Cordova 2.0, Eclipse Indigo SR2 (all updates current), Android SDK (all updates current), on Windows 7 Home (all updates current), Java 7 Update 7.

Entrenchment answered 13/9, 2012 at 15:11 Comment(9)
What is the target SDK version in your manifest?Kelt
I think I figured it out. It's not ideal, but at least it works. Thanks for offering your assistance.Entrenchment
Can you give me a reproduction scenario? I know you worked around it but we don't want this bug in the framework.Kelt
Download Phonegap/Cordova 2.0 from phonegap.com, extract the zip. Copy the "example" project from the lib/Android/ directory to your Eclipse Workspace directory. In Eclipse, got to File->New->Other then in the wizard expand Android and select "Android Project from Existing Code", hit next, browse to the example project, click "Finish". Edit "AndroidManifest.xml" in text editor, add android:targetSdkVersion="16" to the uses-sdk tag. Edit "project.properties" and change the target api to 16. Create AVD running API 16. Run project as app in that AVD. Check logcat to see XMLHttpRequest errors...Entrenchment
The example app still appears to load, but is not fully functional. Also, if you add in any Phonegap/Cordova calls to read/write assets to the app it will fail with the same XMLHttpRequest error as mentioned in the OP above. If you want to see some specific examples I would be happy to email them to you or share them via some other method.Entrenchment
Also, changing the access-origin in the config.xml does not help solve the problem, only setting the "setAllowUniversalAccessFromFileURLs" to true seems to help. I realize this was attempted in org.apache.cordova.CordovaWebView.java, but it doesn't seem to work as intended and I'm not sure why.Entrenchment
For more context, my app loads index.html which includes cordova-2.0.0.js and runs some javascript when onDeviceReady fires which will connect to our server and compare javascript and css assets to see if there are updates available. If there are it will download and save the assets to the app. Once complete the app then loads all necessary assets and displays the welcome screen/page which is now functional... With the bug I mentioned in the OP, it hangs when attempting to check for updates -- due to the XMLHttpRequest error also mentioned in the OPEntrenchment
Thanks for looking into this @SimonMacDonald I appreciate your hard work and dedication to the Phonegap/Cordova project.Entrenchment
Thanks for all the comments. Why don't you just email me a index.html that reproduces the problem so I can take a look. I'm at simon[dot]macdonald[at]gmail[dot]comKelt
E
13

OK so after a ton of searching, guessing, and checking, I found a workable solution.

I had to create a separate function for the setAllowUniversalAccessFromFileURLs call... That fixed the TargetApi issue but then presented a different one on JellyBean where it wouldn't connect to the file I had in my loadURL call so I had to override the onReceivedError function. Here is my resulting code:

package com.MyUniqueDomain.MyUniquePackage;

import android.annotation.TargetApi;
import android.os.Bundle;
import org.apache.cordova.*;

public class MainActivity extends DroidGap {

    private int retryCount = 0;

    @Override
    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        super.setStringProperty("loadingDialog", "Please wait -- loading...");
        super.init();
        if(android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
            fixJellyBeanIssues();
        }
        super.loadUrl("file:///android_asset/www/index.html");
    }

    @TargetApi(16)
    protected void fixJellyBeanIssues() {
        System.out.println(super.appView.toString());
        try {
            super.appView.getSettings().setAllowUniversalAccessFromFileURLs(true);
        } catch(NullPointerException e) {
            System.out.println(e.toString());
        }
    }

    // catch an error and if try again 1x or quit
    @Override
    public void onReceivedError( int errorCode, String description, String failingUrl)
    {
        if(retryCount < 3) {
            retryCount++;
            System.out.println("Connection failed, trying again. Retry Count: "+retryCount);
            super.loadUrl("file:///android_asset/www/index.html");
        } else {
            System.out.println("Sorry, it failed three times so I give up.");
            super.loadUrl("file:///android_asset/www/fail.html");
        }
        return;
    }
}
Entrenchment answered 13/9, 2012 at 19:56 Comment(3)
Luckily you will not need to jump through these hoops in 2.1.0 as the bug is already fixed for that release.Kelt
It works perfect! Thanks! The only thing I have to change was @TargetApi(16) for @TargetApi(Build.VERSION_CODES.JELLY_BEAN)Ethaethan
+1 for saving my life thank you soo much :) this answer work perfectly for meSparkman

© 2022 - 2024 — McMap. All rights reserved.