org.chromium.ChromeProxy.java Source code

Java tutorial

Introduction

Here is the source code for org.chromium.ChromeProxy.java

Source

// Copyright (c) 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package org.chromium;

import android.content.Context;
import android.content.Intent;
import android.net.Proxy;
import android.util.ArrayMap;

import java.lang.System;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

import org.apache.cordova.CallbackContext;
import org.apache.cordova.CordovaArgs;
import org.apache.cordova.CordovaPlugin;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.util.Log;

public class ChromeProxy extends CordovaPlugin {
    private static final String LOG_TAG = "ChromeProxy";

    private JSONObject config = new JSONObject();

    @Override
    public boolean execute(String action, CordovaArgs args, final CallbackContext callbackContext)
            throws JSONException {
        if ("get".equals(action)) {
            get(args, callbackContext);
            return true;
        } else if ("set".equals(action)) {
            set(args, callbackContext);
            return true;
        } else if ("clear".equals(action)) {
            clear(args, callbackContext);
            return true;
        }
        return false;
    }

    private void get(final CordovaArgs args, final CallbackContext callbackContext) {
        cordova.getThreadPool().execute(new Runnable() {
            @Override
            public void run() {
                // Ignoring scope.
                // Arguably, this should use System.getProperty to see if somehow
                // the proxy settings were already set...
                callbackContext.success(config);
            }
        });
    }

    private void setHttpProxy(JSONObject rule, String prefix, String nonProxyHosts) throws JSONException {
        String scheme = rule.getString("scheme");
        if (!"http".equals(scheme)) {
            throw new JSONException("Scheme must be http");
        }
        String host = rule.getString("host");
        int port = rule.has("port") ? rule.getInt("port") : 80;
        String portString = Integer.toString(port);
        System.setProperty(prefix + ".proxyHost", host);
        System.setProperty(prefix + ".proxyPort", portString);
        if (nonProxyHosts != null) {
            System.setProperty(prefix + ".nonProxyHosts", nonProxyHosts);
        }
    }

    private void set(final CordovaArgs args, final CallbackContext callbackContext) {
        cordova.getThreadPool().execute(new Runnable() {
            @Override
            public void run() {
                // Ignoring scope.
                JSONObject details;
                try {
                    details = args.getJSONObject(0);
                } catch (JSONException e) {
                    callbackContext.error("Failed to parse details as JSON");
                    return;
                }
                try {
                    if (details.has("value")) {
                        config = details.getJSONObject("value");
                        String mode = config.getString("mode");
                        if (!"fixed_servers".equals(mode) && !"direct".equals(mode)) {
                            callbackContext.error("Mode must be fixed_servers or direct");
                            return;
                        }
                        if ("direct".equals(mode)) {
                            clear(args, callbackContext);
                            return;
                        }
                    } else {
                        callbackContext.error("Missing value");
                    }
                } catch (JSONException e) {
                    callbackContext.error("Failed to parse value as JSON");
                }

                try {
                    JSONObject rules = config.getJSONObject("rules");
                    String nonProxyHosts = null;
                    if (rules.has("bypassList")) {
                        JSONArray bypassList = rules.getJSONArray("bypassList");
                        nonProxyHosts = bypassList.join("|");
                    }
                    if (rules.has("singleProxy")) {
                        JSONObject rule = rules.getJSONObject("singleProxy");
                        String scheme = rule.getString("scheme");
                        if ("socks5".equals(scheme)) {
                            if (nonProxyHosts != null) {
                                callbackContext.error("nonProxyHosts not supported with socks");
                                return;
                            }
                            String host = rule.getString("host");
                            int port = rule.has("port") ? rule.getInt("port") : 1080;
                            String portString = Integer.toString(port);
                            System.setProperty("socksProxyHost", host);
                            System.setProperty("socksProxyPort", portString);
                        } else if ("http".equals(scheme)) {
                            setHttpProxy(rule, "http", nonProxyHosts);
                            setHttpProxy(rule, "https", nonProxyHosts);
                            setHttpProxy(rule, "ftp", nonProxyHosts);
                        } else {
                            callbackContext.error("Scheme must be socks5 or http");
                            return;
                        }
                    } else {
                        if (rules.has("proxyForHttp")) {
                            JSONObject rule = rules.getJSONObject("httpProxy");
                            setHttpProxy(rule, "http", nonProxyHosts);
                        }
                        if (rules.has("proxyForHttps")) {
                            JSONObject rule = rules.getJSONObject("proxyForHttps");
                            setHttpProxy(rule, "https", nonProxyHosts);
                        }
                        if (rules.has("proxyForFtp")) {
                            JSONObject rule = rules.getJSONObject("proxyForFtp");
                            setHttpProxy(rule, "ftp", nonProxyHosts);
                        }
                    }
                    onSettingsChanged();
                    callbackContext.success();
                } catch (JSONException e) {
                    callbackContext.error("Failed to parse rule(s) as JSON");
                } catch (Exception e) {
                    callbackContext.error("Other error");
                }
            }
        });
    }

    private void clear(final CordovaArgs args, final CallbackContext callbackContext) {
        cordova.getThreadPool().execute(new Runnable() {
            @Override
            public void run() {
                System.clearProperty("socksProxyHost");
                System.clearProperty("socksProxyPort");
                System.clearProperty("http.proxyHost");
                System.clearProperty("http.proxyPort");
                System.clearProperty("http.nonProxyHosts");
                System.clearProperty("https.proxyHost");
                System.clearProperty("https.proxyPort");
                System.clearProperty("https.nonProxyHosts");
                System.clearProperty("ftp.proxyHost");
                System.clearProperty("ftp.proxyPort");
                System.clearProperty("ftp.nonProxyHosts");
                onSettingsChanged();
            }
        });
    }

    private void onSettingsChanged() {
        Context appContext = this.cordova.getActivity().getApplicationContext();
        // See http://stackoverflow.com/questions/32245972/android-webview-non-fqdn-urls-not-routing-through-proxy-on-lollipop
        // and https://crbug.com/525945 for the source of this pattern.
        try {
            Class<?> applicationClass = Class.forName("android.app.Application");
            Field mLoadedApkField = applicationClass.getDeclaredField("mLoadedApk");
            mLoadedApkField.setAccessible(true);
            Object mloadedApk = mLoadedApkField.get(appContext);
            Class<?> loadedApkClass = Class.forName("android.app.LoadedApk");
            Field mReceiversField = loadedApkClass.getDeclaredField("mReceivers");
            mReceiversField.setAccessible(true);
            ArrayMap<?, ?> receivers = (ArrayMap<?, ?>) mReceiversField.get(mloadedApk);
            for (Object receiverMap : receivers.values()) {
                for (Object receiver : ((ArrayMap<?, ?>) receiverMap).keySet()) {
                    Class<?> clazz = receiver.getClass();
                    if (clazz.getName().contains("ProxyChangeListener")) {
                        Method onReceiveMethod = clazz.getDeclaredMethod("onReceive", Context.class, Intent.class);
                        Intent intent = new Intent(Proxy.PROXY_CHANGE_ACTION);
                        onReceiveMethod.invoke(receiver, appContext, intent);
                    }
                }
            }
        } catch (Exception e) {
            // TODO
        }
    }

}