Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5b37fa2e authored by Lorenzo Colitti's avatar Lorenzo Colitti
Browse files

Allow using third-party HTTP/... stacks for per-network URLs.

Also switch to double-checked locking for
mNetworkBoundSocketFactory and OkHttpClient.

Change-Id: Ic52776ee760036ad5623b7496156b8909dc282fa
parent 4b54271f
Loading
Loading
Loading
Loading
+5 −0
Original line number Original line Diff line number Diff line
@@ -17474,10 +17474,15 @@ package android.net {
    method public java.net.URL getBoundURL(java.net.URL) throws java.net.MalformedURLException;
    method public java.net.URL getBoundURL(java.net.URL) throws java.net.MalformedURLException;
    method public java.net.InetAddress getByName(java.lang.String) throws java.net.UnknownHostException;
    method public java.net.InetAddress getByName(java.lang.String) throws java.net.UnknownHostException;
    method public javax.net.SocketFactory getSocketFactory();
    method public javax.net.SocketFactory getSocketFactory();
    method public static void setNetworkBoundURLFactory(android.net.NetworkBoundURLFactory);
    method public void writeToParcel(android.os.Parcel, int);
    method public void writeToParcel(android.os.Parcel, int);
    field public static final android.os.Parcelable.Creator CREATOR;
    field public static final android.os.Parcelable.Creator CREATOR;
  }
  }
  public abstract interface NetworkBoundURLFactory {
    method public abstract java.net.URL getBoundURL(android.net.Network, java.net.URL) throws java.net.MalformedURLException;
  }
  public final class NetworkCapabilities implements android.os.Parcelable {
  public final class NetworkCapabilities implements android.os.Parcelable {
    ctor public NetworkCapabilities(android.net.NetworkCapabilities);
    ctor public NetworkCapabilities(android.net.NetworkCapabilities);
    method public int describeContents();
    method public int describeContents();
+71 −21
Original line number Original line Diff line number Diff line
@@ -16,6 +16,7 @@


package android.net;
package android.net;


import android.net.NetworkBoundURLFactory;
import android.net.NetworkUtils;
import android.net.NetworkUtils;
import android.os.Parcelable;
import android.os.Parcelable;
import android.os.Parcel;
import android.os.Parcel;
@@ -29,6 +30,8 @@ import java.net.SocketAddress;
import java.net.SocketException;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.net.UnknownHostException;
import java.net.URL;
import java.net.URL;
import java.net.URLStreamHandler;
import java.util.concurrent.atomic.AtomicReference;
import javax.net.SocketFactory;
import javax.net.SocketFactory;


import com.android.okhttp.HostResolver;
import com.android.okhttp.HostResolver;
@@ -52,8 +55,8 @@ public class Network implements Parcelable {


    // Objects used to perform per-network operations such as getSocketFactory
    // Objects used to perform per-network operations such as getSocketFactory
    // and getBoundURL, and a lock to protect access to them.
    // and getBoundURL, and a lock to protect access to them.
    private NetworkBoundSocketFactory mNetworkBoundSocketFactory = null;
    private volatile NetworkBoundSocketFactory mNetworkBoundSocketFactory = null;
    private OkHttpClient mOkHttpClient = null;
    private volatile OkHttpClient mOkHttpClient = null;
    private Object mLock = new Object();
    private Object mLock = new Object();


    /**
    /**
@@ -174,36 +177,83 @@ public class Network implements Parcelable {
     *         {@code Network}.
     *         {@code Network}.
     */
     */
    public SocketFactory getSocketFactory() {
    public SocketFactory getSocketFactory() {
        if (mNetworkBoundSocketFactory == null) {
            synchronized (mLock) {
            synchronized (mLock) {
                if (mNetworkBoundSocketFactory == null) {
                if (mNetworkBoundSocketFactory == null) {
                    mNetworkBoundSocketFactory = new NetworkBoundSocketFactory(netId);
                    mNetworkBoundSocketFactory = new NetworkBoundSocketFactory(netId);
                }
                }
            }
            }
        }
        return mNetworkBoundSocketFactory;
        return mNetworkBoundSocketFactory;
    }
    }


    /**
    /** The default NetworkBoundURLFactory, used if setNetworkBoundURLFactory is never called. */
     * Returns a {@link URL} based on the given URL but bound to this {@code Network}.
    private static final NetworkBoundURLFactory DEFAULT_URL_FACTORY = new NetworkBoundURLFactory() {
     * Note that if this {@code Network} ever disconnects, this factory and any URL object it
        public URL getBoundURL(final Network network, URL url) throws MalformedURLException {
     * produced in the past or future will cease to work.
            if (network.mOkHttpClient == null) {
     *
                synchronized (network.mLock) {
     * @return a {@link URL} bound to this {@code Network}.
                    if (network.mOkHttpClient == null) {
     */
    public URL getBoundURL(URL url) throws MalformedURLException {
        synchronized (mLock) {
            if (mOkHttpClient == null) {
                        HostResolver hostResolver = new HostResolver() {
                        HostResolver hostResolver = new HostResolver() {
                            @Override
                            @Override
                    public InetAddress[] getAllByName(String host) throws UnknownHostException {
                            public InetAddress[] getAllByName(String host)
                        return Network.this.getAllByName(host);
                                    throws UnknownHostException {
                                return network.getAllByName(host);
                            }
                            }
                        };
                        };
                mOkHttpClient = new OkHttpClient()
                        network.mOkHttpClient = new OkHttpClient()
                        .setSocketFactory(getSocketFactory())
                                .setSocketFactory(network.getSocketFactory())
                                .setHostResolver(hostResolver);
                                .setHostResolver(hostResolver);
                    }
                    }
                }
                }
        return new URL(url, "", mOkHttpClient.createURLStreamHandler(url.getProtocol()));
            }

            String protocol = url.getProtocol();
            URLStreamHandler handler = network.mOkHttpClient.createURLStreamHandler(protocol);
            if (handler == null) {
                // OkHttpClient only supports HTTP and HTTPS and returns a null URLStreamHandler if
                // passed another protocol.
                throw new MalformedURLException("Invalid URL or unrecognized protocol " + protocol);
            }
            return new URL(url, "", handler);
        }
    };

    private static AtomicReference<NetworkBoundURLFactory> sNetworkBoundURLFactory =
            new AtomicReference <NetworkBoundURLFactory>(DEFAULT_URL_FACTORY);

    /**
     * Returns a {@link URL} based on the given URL but bound to this {@code Network},
     * such that opening the URL will send all network traffic on this Network.
     *
     * Note that if this {@code Network} ever disconnects, any URL object generated by this method
     * in the past or future will cease to work.
     *
     * The returned URL may have a {@link URLStreamHandler} explicitly set, which may not be the
     * handler generated by the factory set with {@link java.net.URL.setURLStreamHandlerFactory}. To
     * affect the {@code URLStreamHandler}s of URLs returned by this method, call
     * {@link #setNetworkBoundURLFactory}.
     *
     * Because the returned URLs may have an explicit {@code URLStreamHandler} set, using them as a
     * context when constructing other URLs and explicitly specifying a {@code URLStreamHandler} may
     * result in URLs that are no longer bound to the same {@code Network}.
     *
     * The default implementation only supports {@code HTTP} and {@code HTTPS} URLs.
     *
     * @return a {@link URL} bound to this {@code Network}.
     */
    public URL getBoundURL(URL url) throws MalformedURLException {
        return sNetworkBoundURLFactory.get().getBoundURL(this, url);
    }

    /**
     * Sets the {@link NetworkBoundURLFactory} to be used by future {@link #getBoundURL} calls.
     * If {@code null}, clears any factory that was previously specified.
     */
    public static void setNetworkBoundURLFactory(NetworkBoundURLFactory factory) {
        if (factory == null) {
            factory = DEFAULT_URL_FACTORY;
        }
        sNetworkBoundURLFactory.set(factory);
    }
    }


    // implement the Parcelable interface
    // implement the Parcelable interface
+35 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.net;

import java.net.MalformedURLException;
import java.net.URL;

/**
 * An interface that describes a factory for network-specific {@link URL} objects.
 */
public interface NetworkBoundURLFactory {
    /**
     * Returns a {@link URL} based on the given URL but bound to the specified {@code Network},
     * such that opening the URL will send all network traffic on the specified Network.
     *
     * @return a {@link URL} bound to this {@code Network}.
     * @throws MalformedURLException if the URL was not valid, or this factory cannot handle the
     *         specified URL (e.g., if it does not support the protocol of the URL).
     */
    public URL getBoundURL(Network network, URL url) throws MalformedURLException;
}