Home | History | Annotate | Download | only in net
      1 /*
      2  * Copyright (C) 2014 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package android.net;
     18 
     19 import android.content.BroadcastReceiver;
     20 import android.content.ComponentName;
     21 import android.content.Context;
     22 import android.content.Intent;
     23 import android.content.IntentFilter;
     24 import android.content.ServiceConnection;
     25 import android.os.Bundle;
     26 import android.os.Handler;
     27 import android.os.IBinder;
     28 import android.os.Message;
     29 import android.os.Messenger;
     30 import android.os.RemoteException;
     31 import android.os.UserHandle;
     32 import android.util.Log;
     33 
     34 import java.net.InetAddress;
     35 import java.net.UnknownHostException;
     36 import java.util.concurrent.atomic.AtomicBoolean;
     37 import java.util.concurrent.atomic.AtomicInteger;
     38 
     39 /**
     40  * A data tracker responsible for bringing up and tearing down the system proxy server.
     41  *
     42  * {@hide}
     43  */
     44 public class ProxyDataTracker extends BaseNetworkStateTracker {
     45     private static final String TAG = "ProxyDataTracker";
     46     private static final String NETWORK_TYPE = "PROXY";
     47 
     48     // TODO: investigate how to get these DNS addresses from the system.
     49     private static final String DNS1 = "8.8.8.8";
     50     private static final String DNS2 = "8.8.4.4";
     51     private static final String INTERFACE_NAME = "ifb0";
     52     private static final String REASON_ENABLED = "enabled";
     53     private static final String REASON_DISABLED = "disabled";
     54     private static final String REASON_PROXY_DOWN = "proxy_down";
     55 
     56     private static final int MSG_TEAR_DOWN_REQUEST = 1;
     57     private static final int MSG_SETUP_REQUEST = 2;
     58 
     59     private static final String PERMISSION_PROXY_STATUS_SENDER =
     60             "android.permission.ACCESS_NETWORK_CONDITIONS";
     61     private static final String ACTION_PROXY_STATUS_CHANGE =
     62             "com.android.net.PROXY_STATUS_CHANGE";
     63     private static final String KEY_IS_PROXY_AVAILABLE = "is_proxy_available";
     64     private static final String KEY_REPLY_TO_MESSENGER_BINDER = "reply_to_messenger_binder";
     65     private static final String KEY_REPLY_TO_MESSENGER_BINDER_BUNDLE =
     66             "reply_to_messenger_binder_bundle";
     67 
     68     private Handler mTarget;
     69     private Messenger mProxyStatusService;
     70     private AtomicBoolean mReconnectRequested = new AtomicBoolean(false);
     71     private AtomicBoolean mIsProxyAvailable = new AtomicBoolean(false);
     72     private final AtomicInteger mDefaultGatewayAddr = new AtomicInteger(0);
     73 
     74     private final BroadcastReceiver mProxyStatusServiceListener = new BroadcastReceiver() {
     75         @Override
     76         public void onReceive(Context context, Intent intent) {
     77             if (intent.getAction().equals(ACTION_PROXY_STATUS_CHANGE)) {
     78                 mIsProxyAvailable.set(intent.getBooleanExtra(KEY_IS_PROXY_AVAILABLE, false));
     79                 if (mIsProxyAvailable.get()) {
     80                     Bundle bundle = intent.getBundleExtra(KEY_REPLY_TO_MESSENGER_BINDER_BUNDLE);
     81                     if (bundle == null || bundle.getBinder(KEY_REPLY_TO_MESSENGER_BINDER) == null) {
     82                         Log.e(TAG, "no messenger binder in the intent to send future requests");
     83                         mIsProxyAvailable.set(false);
     84                         return;
     85                     }
     86                     mProxyStatusService =
     87                             new Messenger(bundle.getBinder(KEY_REPLY_TO_MESSENGER_BINDER));
     88                     // If there is a pending reconnect request, do it now.
     89                     if (mReconnectRequested.get()) {
     90                         reconnect();
     91                     }
     92                 } else {
     93                     setDetailedState(NetworkInfo.DetailedState.DISCONNECTED,
     94                             REASON_PROXY_DOWN, null);
     95                 }
     96             } else {
     97                 Log.d(TAG, "Unrecognized broadcast intent");
     98             }
     99         }
    100     };
    101 
    102     /**
    103      * Create a new ProxyDataTracker
    104      */
    105     public ProxyDataTracker() {
    106         mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_PROXY, 0, NETWORK_TYPE, "");
    107         mLinkProperties = new LinkProperties();
    108         mNetworkCapabilities = new NetworkCapabilities();
    109         mNetworkInfo.setIsAvailable(true);
    110         try {
    111             mLinkProperties.addDnsServer(InetAddress.getByName(DNS1));
    112             mLinkProperties.addDnsServer(InetAddress.getByName(DNS2));
    113             mLinkProperties.setInterfaceName(INTERFACE_NAME);
    114         } catch (UnknownHostException e) {
    115             Log.e(TAG, "Could not add DNS address", e);
    116         }
    117     }
    118 
    119     @Override
    120     public Object clone() throws CloneNotSupportedException {
    121         throw new CloneNotSupportedException();
    122     }
    123 
    124     @Override
    125     public void startMonitoring(Context context, Handler target) {
    126         mContext = context;
    127         mTarget = target;
    128         mContext.registerReceiver(mProxyStatusServiceListener,
    129                 new IntentFilter(ACTION_PROXY_STATUS_CHANGE),
    130                 PERMISSION_PROXY_STATUS_SENDER,
    131                 null);
    132     }
    133 
    134     /**
    135      * Disable connectivity to the network.
    136      */
    137     public boolean teardown() {
    138         setTeardownRequested(true);
    139         mReconnectRequested.set(false);
    140         try {
    141             if (mIsProxyAvailable.get() && mProxyStatusService != null) {
    142                 mProxyStatusService.send(Message.obtain(null, MSG_TEAR_DOWN_REQUEST));
    143             }
    144         } catch (RemoteException e) {
    145             Log.e(TAG, "Unable to connect to proxy status service", e);
    146             return false;
    147         }
    148         setDetailedState(NetworkInfo.DetailedState.DISCONNECTED, REASON_DISABLED, null);
    149         return true;
    150     }
    151 
    152     /**
    153      * Re-enable proxy data connectivity after a {@link #teardown()}.
    154      */
    155     public boolean reconnect() {
    156         mReconnectRequested.set(true);
    157         setTeardownRequested(false);
    158         if (!mIsProxyAvailable.get()) {
    159             Log.w(TAG, "Reconnect requested even though proxy service is not up. Bailing.");
    160             return false;
    161         }
    162         setDetailedState(NetworkInfo.DetailedState.CONNECTING, REASON_ENABLED, null);
    163 
    164         try {
    165             mProxyStatusService.send(Message.obtain(null, MSG_SETUP_REQUEST));
    166         } catch (RemoteException e) {
    167             Log.e(TAG, "Unable to connect to proxy status service", e);
    168             setDetailedState(NetworkInfo.DetailedState.DISCONNECTED, REASON_PROXY_DOWN, null);
    169             return false;
    170         }
    171         // We'll assume proxy is set up successfully. If not, a status change broadcast will be
    172         // received afterwards to indicate any failure.
    173         setDetailedState(NetworkInfo.DetailedState.CONNECTED, REASON_ENABLED, null);
    174         return true;
    175     }
    176 
    177     /**
    178      * Fetch default gateway address for the network
    179      */
    180     public int getDefaultGatewayAddr() {
    181         return mDefaultGatewayAddr.get();
    182     }
    183 
    184     /**
    185      * Return the system properties name associated with the tcp buffer sizes
    186      * for this network.
    187      */
    188     public String getTcpBufferSizesPropName() {
    189         return "net.tcp.buffersize.wifi";
    190     }
    191 
    192     /**
    193      * Record the detailed state of a network, and if it is a
    194      * change from the previous state, send a notification to
    195      * any listeners.
    196      * @param state the new @{code DetailedState}
    197      * @param reason a {@code String} indicating a reason for the state change,
    198      * if one was supplied. May be {@code null}.
    199      * @param extraInfo optional {@code String} providing extra information about the state change
    200      */
    201     private void setDetailedState(NetworkInfo.DetailedState state, String reason,
    202             String extraInfo) {
    203         mNetworkInfo.setDetailedState(state, reason, extraInfo);
    204         Message msg = mTarget.obtainMessage(EVENT_STATE_CHANGED, mNetworkInfo);
    205         msg.sendToTarget();
    206     }
    207 }
    208