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 17package android.net; 18 19import android.content.BroadcastReceiver; 20import android.content.ComponentName; 21import android.content.Context; 22import android.content.Intent; 23import android.content.IntentFilter; 24import android.content.ServiceConnection; 25import android.os.Bundle; 26import android.os.Handler; 27import android.os.IBinder; 28import android.os.Message; 29import android.os.Messenger; 30import android.os.RemoteException; 31import android.os.UserHandle; 32import android.util.Log; 33 34import java.net.InetAddress; 35import java.net.UnknownHostException; 36import java.util.concurrent.atomic.AtomicBoolean; 37import 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 */ 44public 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