3044 lines
120 KiB
Java
3044 lines
120 KiB
Java
/*
|
|
* Copyright (C) 2006 The Android Open Source Project
|
|
* Copyright (C) 2009-2011, The Linux Foundation. All rights reserved.
|
|
*
|
|
* 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 com.android.server;
|
|
|
|
import com.android.internal.net.IPVersion;
|
|
import com.android.internal.telephony.ITelephony;
|
|
import com.android.internal.telephony.Phone;
|
|
import com.android.internal.telephony.TelephonyIntents;
|
|
import com.android.internal.telephony.QosSpec;
|
|
|
|
import android.content.BroadcastReceiver;
|
|
import android.content.Context;
|
|
import android.content.Intent;
|
|
import android.content.IntentFilter;
|
|
import android.net.ConnectivityManager;
|
|
import android.net.DhcpInfo;
|
|
import android.net.FmcNotifier;
|
|
import android.net.IConSvcEventListener;
|
|
import android.net.IFmcEventListener;
|
|
import android.net.ILinkSocketMessageHandler;
|
|
import android.net.LinkCapabilities;
|
|
import android.net.ExtraLinkCapabilities;
|
|
import android.net.QoSTracker;
|
|
import android.net.LinkInfo;
|
|
import android.net.LinkNotifier;
|
|
import android.net.LinkProperties;
|
|
import android.net.LinkProvider;
|
|
import android.net.LocalSocket;
|
|
import android.net.LocalSocketAddress;
|
|
import android.net.NetworkInfo;
|
|
import android.net.LinkProvider.LinkRequirement;
|
|
import android.net.wifi.ScanResult;
|
|
import android.net.wifi.WifiInfo;
|
|
import android.net.wifi.WifiManager;
|
|
import android.os.BatteryManager;
|
|
import android.os.Handler;
|
|
import android.os.HandlerThread;
|
|
import android.os.IBinder;
|
|
import android.os.Looper;
|
|
import android.os.Message;
|
|
import android.os.Parcel;
|
|
import android.os.RemoteException;
|
|
import android.os.ServiceManager;
|
|
import android.provider.Settings;
|
|
import android.telephony.PhoneStateListener;
|
|
import android.telephony.ServiceState;
|
|
import android.telephony.SignalStrength;
|
|
import android.telephony.TelephonyManager;
|
|
import android.util.Log;
|
|
|
|
import java.io.BufferedReader;
|
|
import java.io.File;
|
|
import java.io.FileReader;
|
|
import java.io.FileWriter;
|
|
import java.io.IOException;
|
|
import java.io.InputStream;
|
|
import java.net.InetAddress;
|
|
import java.net.NetworkInterface;
|
|
import java.net.SocketException;
|
|
import java.net.UnknownHostException;
|
|
import java.sql.Timestamp;
|
|
import java.util.ArrayList;
|
|
import java.util.Enumeration;
|
|
import java.util.HashMap;
|
|
import java.util.Iterator;
|
|
import java.util.List;
|
|
import java.util.Collection;
|
|
import java.util.Collections;
|
|
import java.util.Map;
|
|
import java.util.NoSuchElementException;
|
|
import java.util.StringTokenizer;
|
|
import java.util.Map.Entry;
|
|
import java.util.concurrent.ConcurrentHashMap;
|
|
import java.util.regex.Pattern;
|
|
import android.os.INetworkManagementService;
|
|
|
|
/**
|
|
* {@hide}
|
|
*/
|
|
class CNERequest {
|
|
static final String LOG_TAG = "CNE"; // global logcat tag
|
|
static final String LOCAL_TAG = "CNE_DEBUG"; // local logcat tag
|
|
|
|
// ***** Class Variables
|
|
static int sNextSerial = 0;
|
|
static Object sSerialMonitor = new Object();
|
|
private static Object sPoolSync = new Object();
|
|
private static CNERequest sPool = null;
|
|
private static int sPoolSize = 0;
|
|
private static final int MAX_POOL_SIZE = 4;
|
|
|
|
// ***** Instance Variables
|
|
int mSerial;
|
|
int mRequest;
|
|
Message mResult;
|
|
Parcel mp;
|
|
CNERequest mNext;
|
|
|
|
/**
|
|
* Retrieves a new CNERequest instance from the pool.
|
|
*
|
|
* @param request
|
|
* CNE_REQUEST_*
|
|
* @param result
|
|
* sent when operation completes
|
|
* @return a CNERequest instance from the pool.
|
|
*/
|
|
static CNERequest obtain(int request) {
|
|
CNERequest rr = null;
|
|
|
|
synchronized (sPoolSync) {
|
|
if (sPool != null) {
|
|
rr = sPool;
|
|
sPool = rr.mNext;
|
|
rr.mNext = null;
|
|
sPoolSize--;
|
|
}
|
|
}
|
|
|
|
if (rr == null) {
|
|
rr = new CNERequest();
|
|
}
|
|
|
|
synchronized (sSerialMonitor) {
|
|
rr.mSerial = sNextSerial++;
|
|
}
|
|
rr.mRequest = request;
|
|
rr.mp = Parcel.obtain();
|
|
// first elements in any CNE Parcel
|
|
rr.mp.writeInt(request);
|
|
rr.mp.writeInt(rr.mSerial);
|
|
return rr;
|
|
}
|
|
|
|
/**
|
|
* Returns a CNERequest instance to the pool.
|
|
*
|
|
* Note: This should only be called once per use.
|
|
*/
|
|
void release() {
|
|
synchronized (sPoolSync) {
|
|
if (sPoolSize < MAX_POOL_SIZE) {
|
|
this.mNext = sPool;
|
|
sPool = this;
|
|
sPoolSize++;
|
|
}
|
|
}
|
|
}
|
|
|
|
private CNERequest() {
|
|
return;
|
|
}
|
|
|
|
static void resetSerial() {
|
|
synchronized (sSerialMonitor) {
|
|
sNextSerial = 0;
|
|
}
|
|
}
|
|
|
|
String serialString() {
|
|
// Cheesy way to do %04d
|
|
StringBuilder sb = new StringBuilder(8);
|
|
String sn;
|
|
|
|
sn = Integer.toString(mSerial);
|
|
|
|
// sb.append("J[");
|
|
sb.append('[');
|
|
for (int i = 0, s = sn.length(); i < 4 - s; i++) {
|
|
sb.append('0');
|
|
}
|
|
|
|
sb.append(sn);
|
|
sb.append(']');
|
|
return sb.toString();
|
|
}
|
|
|
|
void onError(int error) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
public final class CNE implements ILinkManager {
|
|
static final String LOG_TAG = "CNE"; // global logcat tag
|
|
static final String LOCAL_TAG = "CNE_DEBUG"; // local logcat tag
|
|
private static final boolean DBG = true; // enable local logging?
|
|
|
|
// ***** Instance Variables
|
|
LocalSocket mSocket;
|
|
HandlerThread mSenderThread;
|
|
CNESender mSender;
|
|
Thread mReceiverThread;
|
|
CNEReceiver mReceiver;
|
|
private Context mContext;
|
|
int mRequestMessagesPending;
|
|
private static int mSocketId = 0;
|
|
|
|
ArrayList<CNERequest> mRequestsList = new ArrayList<CNERequest>();
|
|
//both Capabilities and mQosTrackers must have synchronized access
|
|
Map<Integer, ExtraLinkCapabilities> activeCapabilities;
|
|
Collection<QoSTracker> mQosTrackers;
|
|
|
|
/* to do move all the constants to one file */
|
|
|
|
// ***** Events
|
|
|
|
static final int EVENT_SEND = 1;
|
|
|
|
// ***** Constants
|
|
|
|
/* CNE feature flag */
|
|
static boolean isCndUp = false;
|
|
// match with constant in CNE.cpp
|
|
static final int CNE_MAX_COMMAND_BYTES = (8 * 1024);
|
|
static final int RESPONSE_SOLICITED = 0;
|
|
static final int RESPONSE_UNSOLICITED = 1;
|
|
|
|
static final String SOCKET_NAME_CNE = "cnd";
|
|
|
|
static final int SOCKET_OPEN_RETRY_MILLIS = 4 * 1000;
|
|
|
|
/* Different requests types - corresponding to cnd_commands.h */
|
|
static final int CNE_REQUEST_INIT = 1;
|
|
static final int CNE_REQUEST_REG_ROLE = 2;
|
|
static final int CNE_REQUEST_GET_COMPATIBLE_NWS = 3;
|
|
static final int CNE_REQUEST_CONF_NW = 4;
|
|
static final int CNE_REQUEST_DEREG_ROLE = 5;
|
|
static final int CNE_REQUEST_REG_NOTIFICATIONS = 6;
|
|
static final int CNE_REQUEST_UPDATE_BATTERY_INFO = 7;
|
|
static final int CNE_REQUEST_UPDATE_WLAN_INFO = 8;
|
|
static final int CNE_REQUEST_UPDATE_WWAN_INFO = 9;
|
|
static final int CNE_NOTIFY_RAT_CONNECT_STATUS = 10;
|
|
static final int CNE_NOTIFY_DEFAULT_NW_PREF = 11;
|
|
static final int CNE_REQUEST_UPDATE_WLAN_SCAN_RESULTS = 12;
|
|
static final int CNE_NOTIFY_SENSOR_EVENT_CMD = 13;
|
|
static final int CNE_REQUEST_CONFIG_IPROUTE2_CMD = 14;
|
|
static final int CNE_NOTIFY_TIMER_EXPIRED_CMD = 15;
|
|
static final int CNE_REQUEST_START_FMC_CMD = 16;
|
|
static final int CNE_REQUEST_STOP_FMC_CMD = 17;
|
|
static final int CNE_REQUEST_UPDATE_WWAN_DORMANCY_INFO = 18;
|
|
static final int CNE_REQUEST_UPDATE_DEFAULT_NETWORK_INFO = 19;
|
|
|
|
/* UNSOL Responses - corresponding to cnd_unsol_messages.h */
|
|
static final int CNE_RESPONSE_REG_ROLE = 1;
|
|
static final int CNE_RESPONSE_GET_BEST_NW = 2;
|
|
static final int CNE_RESPONSE_CONFIRM_NW = 3;
|
|
static final int CNE_RESPONSE_DEREG_ROLE = 4;
|
|
/* UNSOL Events */
|
|
static final int CNE_REQUEST_BRING_RAT_DOWN = 5;
|
|
static final int CNE_REQUEST_BRING_RAT_UP = 6;
|
|
static final int CNE_NOTIFY_MORE_PREFERED_RAT_AVAIL = 7;
|
|
static final int CNE_NOTIFY_RAT_LOST = 8;
|
|
static final int CNE_REQUEST_START_SCAN_WLAN = 9;
|
|
static final int CNE_NOTIFY_INFLIGHT_STATUS = 10;
|
|
static final int CNE_NOTIFY_FMC_STATUS = 11;
|
|
static final int CNE_NOTIFY_HOST_ROUTING_IP = 12;
|
|
|
|
/* RAT type - corresponding to CneRatType */
|
|
static final int CNE_RAT_MIN = 0;
|
|
static final int CNE_RAT_WWAN = CNE_RAT_MIN;
|
|
static final int CNE_RAT_WLAN = 1;
|
|
static final int CNE_RAT_ANY = 2;
|
|
static final int CNE_RAT_NONE = 3;
|
|
static final int CNE_RAT_MAX = 4;
|
|
static final int CNE_RAT_INVALID = CNE_RAT_MAX;
|
|
|
|
/* different status codes */
|
|
public static final int STATUS_FAILURE = 0;
|
|
public static final int STATUS_SUCCESS = 1;
|
|
|
|
public static final int STATUS_NOT_INFLIGHT = 0;
|
|
public static final int STATUS_INFLIGHT = 1;
|
|
|
|
private static NetworkInfo.State ipv6NetState = NetworkInfo.State.DISCONNECTED;
|
|
|
|
static final int CNE_REGID_INVALID = -1;
|
|
static final int CNE_ROLE_INVALID = -1;
|
|
static final int CNE_DEFAULT_CON_REGID = 0;
|
|
static final int CNE_INVALID_PID = -1;
|
|
|
|
static final int CNE_LINK_SATISFIED = 1;
|
|
static final int CNE_LINK_NOT_SATISFIED = 0;
|
|
|
|
static final int CNE_MASK_ON_LINK_AVAIL_SENT = 0x0001;
|
|
static final int CNE_MASK_ON_BETTER_LINK_AVAIL_SENT = 0x0002;
|
|
|
|
static final int CNE_NET_SUBTYPE_WLAN_B = 20;
|
|
static final int CNE_NET_SUBTYPE_WLAN_G = 21;
|
|
|
|
//TODO move this down to QCNE, java should have no knowledge of this role.
|
|
/* QoS Role range, 1000 < QoSRoleInt < 2000 */
|
|
private static final int QOS_ROLE_MIN = 1001;
|
|
private static final int QOS_ROLE_MAX = 1999;
|
|
|
|
|
|
private static int mRoleRegId = 0;
|
|
private WifiManager mWifiManager;
|
|
private TelephonyManager mTelephonyManager;
|
|
private ConnectivityService mService;
|
|
private int mNetworkPreference;
|
|
private int mDefaultNetwork = ConnectivityManager.MAX_NETWORK_TYPE;
|
|
|
|
private SignalStrength mSignalStrength = new SignalStrength();
|
|
ServiceState mServiceState;
|
|
private String activeWlanIfName = null;
|
|
private String activeWwanV4IfName = null;
|
|
private String activeWwanV6IfName = null;
|
|
private String activeWlanGatewayAddr = null;
|
|
private String activeWwanV4GatewayAddr = null;
|
|
private String hostRoutingIpAddr = null;
|
|
private static boolean mRemoveHostEntry = false;
|
|
|
|
private static boolean isWwanDormant = false;
|
|
|
|
private class AddressInfo {
|
|
String ipAddr;
|
|
String gatewayAddr;
|
|
String ifName;
|
|
|
|
public AddressInfo() {
|
|
ipAddr = null;
|
|
gatewayAddr = null;
|
|
ifName = null;
|
|
}
|
|
}
|
|
|
|
AddressInfo getWlanAddrInfo(IPVersion version) {
|
|
AddressInfo wlanAddrInfo = new AddressInfo();
|
|
String ipAddr = null;
|
|
String gatewayAddr = null;
|
|
if (version == IPVersion.INET) {
|
|
try {
|
|
DhcpInfo dhcpInfo = mWifiManager.getDhcpInfo();
|
|
int ipAddressInt = dhcpInfo.ipAddress;
|
|
int gatewayInt = dhcpInfo.gateway;
|
|
if (ipAddressInt != 0) {
|
|
ipAddr = ((ipAddressInt) & 0xff) + "."
|
|
+ ((ipAddressInt >> 8) & 0xff) + "."
|
|
+ ((ipAddressInt >> 16) & 0xff) + "."
|
|
+ ((ipAddressInt >> 24) & 0xff);
|
|
}
|
|
if (gatewayInt != 0) {
|
|
gatewayAddr = ((gatewayInt) & 0xff) + "."
|
|
+ ((gatewayInt >> 8) & 0xff) + "."
|
|
+ ((gatewayInt >> 16) & 0xff) + "."
|
|
+ ((gatewayInt >> 24) & 0xff);
|
|
}
|
|
wlanAddrInfo.ipAddr = ipAddr;
|
|
wlanAddrInfo.gatewayAddr = gatewayAddr;
|
|
InetAddress inetAddr = InetAddress.getByName(ipAddr);
|
|
NetworkInterface netIface = NetworkInterface.getByInetAddress(inetAddr);
|
|
wlanAddrInfo.ifName = netIface.getName();
|
|
} catch (SocketException sexp) {
|
|
logw("Could not get the netIface obj");
|
|
} catch (NullPointerException nexp) {
|
|
// WiFi is probably down.
|
|
logw("DhcpInfo obj is NULL. V4 address is not yet available.");
|
|
} catch (UnknownHostException uexp) {
|
|
logw( "Invalid host name" + ipAddr);
|
|
}
|
|
} else if (version == IPVersion.INET6) {
|
|
/*
|
|
* currently wifistate tracker is not supporting V6
|
|
* address, without this if V6 interface is brought up by
|
|
* kernel, we can get the V6 address info if we have a
|
|
* valid v4 address.
|
|
*/
|
|
AddressInfo v4Addr = getWlanAddrInfo(IPVersion.INET);
|
|
try {
|
|
InetAddress inet4Addr = InetAddress.getByName(v4Addr.ipAddr);
|
|
NetworkInterface netIface = NetworkInterface.getByInetAddress(inet4Addr);
|
|
wlanAddrInfo.ifName = netIface.getName();
|
|
Enumeration<InetAddress> e = netIface.getInetAddresses();
|
|
while (e.hasMoreElements()) {
|
|
InetAddress inetAddr = (InetAddress) e.nextElement();
|
|
if (!inetAddr.equals(inet4Addr)) {
|
|
wlanAddrInfo.ipAddr = inetAddr.getHostAddress();
|
|
// TODO: get the gateway addr for v6
|
|
}
|
|
}
|
|
} catch (SocketException sexp) {
|
|
logw("Could not get the netIface obj");
|
|
} catch (NullPointerException nexp) {
|
|
logw("V4Adrr is not yet available");
|
|
} catch (UnknownHostException uexp) {
|
|
logw("Can't get V6Addr without valid V4Addr= " + v4Addr);
|
|
}
|
|
} else {
|
|
loge("Unsupported ipversion." + version);
|
|
}
|
|
return wlanAddrInfo;
|
|
}
|
|
|
|
AddressInfo getWwanAddrInfo(String apnType, IPVersion ipv) {
|
|
AddressInfo wwanAddrInfo = new AddressInfo();
|
|
try {
|
|
wwanAddrInfo.ifName = mTelephonyManager.getActiveInterfaceName(apnType, ipv);
|
|
wwanAddrInfo.ipAddr = mTelephonyManager.getActiveIpAddress(apnType, ipv);
|
|
wwanAddrInfo.gatewayAddr = mTelephonyManager.getActiveGateway(apnType, ipv);
|
|
} catch (NullPointerException nexp) {
|
|
logw("mTelephonyManager is null");
|
|
}
|
|
return wwanAddrInfo;
|
|
}
|
|
|
|
BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
|
|
public void onReceive(Context context, Intent intent) {
|
|
final String action = intent.getAction();
|
|
|
|
if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
|
|
dlogi( "CNE received action: " + action);
|
|
double level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0);
|
|
double scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, 1);
|
|
int pluginType = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0);
|
|
int status = intent.getIntExtra(BatteryManager.EXTRA_STATUS, 0);
|
|
int normalizedLevel = (int)((level/scale) * 100);
|
|
updateBatteryStatus(status, pluginType, normalizedLevel);
|
|
|
|
} else if (action.equals(Intent.ACTION_SHUTDOWN)) {
|
|
dlogi("CNE received action: " + action);
|
|
stopFmc(null);
|
|
|
|
} else if (action.equals(WifiManager.RSSI_CHANGED_ACTION)) {
|
|
dlogi( "CNE received action RSSI Changed events: " + action);
|
|
if (mWifiManager != null) {
|
|
String ipV4Addr = null;
|
|
String iface = null;
|
|
ConnectivityManager cm = (ConnectivityManager) mContext
|
|
.getSystemService(Context.CONNECTIVITY_SERVICE);
|
|
NetworkInfo networkInfo = cm.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
|
|
NetworkInfo.State networkState = (networkInfo
|
|
== null ? NetworkInfo.State.UNKNOWN : networkInfo.getState());
|
|
if (networkState == NetworkInfo.State.CONNECTED) {
|
|
AddressInfo aInfo = getWlanAddrInfo(IPVersion.INET);
|
|
ipV4Addr = aInfo.ipAddr;
|
|
iface = aInfo.ifName;
|
|
}
|
|
WifiInfo wifiInfo = mWifiManager.getConnectionInfo();
|
|
String ssid = wifiInfo.getSSID();
|
|
int rssi = wifiInfo.getRssi();
|
|
Timestamp ts = new Timestamp(System.currentTimeMillis());
|
|
String tsStr = ts.toString();
|
|
logi("CNE received action RSSI Changed events -"
|
|
+ " networkState: " + networkState
|
|
+ " ssid= " + ssid
|
|
+ " rssi=" + rssi
|
|
+ " ipV4Addr=" + ipV4Addr
|
|
+ " timeStamp:" + tsStr
|
|
+ " networkState: " + networkState);
|
|
updateWlanStatus(CNE_NET_SUBTYPE_WLAN_G, NetworkStateToInt(networkState), rssi,
|
|
ssid, ipV4Addr, iface, tsStr);
|
|
} else {
|
|
logw("CNE received action RSSI Changed events, null WifiManager");
|
|
}
|
|
|
|
} else if ((action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) ||
|
|
(action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) ||
|
|
(action.equals(WifiManager.NO_MORE_WIFI_LOCKS))) {
|
|
|
|
dlogi("CNE received action Network/Wifi State Changed: " + action);
|
|
|
|
if (mWifiManager != null) {
|
|
AddressInfo wlanV4Addr = getWlanAddrInfo(IPVersion.INET);
|
|
NetworkInfo.State networkState = NetworkInfo.State.UNKNOWN;
|
|
if(action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION) ||
|
|
action.equals(WifiManager.NO_MORE_WIFI_LOCKS)) {
|
|
NetworkInfo networkInfo = (NetworkInfo) intent
|
|
.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
|
|
networkState = (networkInfo == null ? NetworkInfo.State.UNKNOWN
|
|
: networkInfo.getState());
|
|
}
|
|
if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
|
|
final boolean enabled = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
|
|
WifiManager.WIFI_STATE_UNKNOWN) == WifiManager.WIFI_STATE_ENABLED;
|
|
if (!enabled) {
|
|
networkState = NetworkInfo.State.DISCONNECTED;
|
|
} else {
|
|
networkState = NetworkInfo.State.UNKNOWN;
|
|
}
|
|
}
|
|
dlogi("CNE received action Network/Wifi State Changed"
|
|
+ " networkState: " + networkState);
|
|
if (networkState == NetworkInfo.State.CONNECTED) {
|
|
activeWlanIfName = wlanV4Addr.ifName;
|
|
activeWlanGatewayAddr = wlanV4Addr.gatewayAddr;
|
|
}
|
|
WifiInfo wifiInfo = mWifiManager.getConnectionInfo();
|
|
String ssid = wifiInfo.getSSID();
|
|
int rssi = wifiInfo.getRssi();
|
|
Timestamp ts = new Timestamp(System.currentTimeMillis());
|
|
String tsStr = ts.toString();
|
|
dlogi("CNE received action Network/Wifi State Changed -"
|
|
+ " ssid=" + ssid
|
|
+ " rssi=" + rssi
|
|
+ " networkState=" + networkState
|
|
+ " ifName=" + wlanV4Addr.ifName
|
|
+ " ipV4Addr=" + wlanV4Addr.ipAddr
|
|
+ " gateway=" + wlanV4Addr.gatewayAddr
|
|
+ " timeStamp=" + tsStr);
|
|
updateWlanStatus(CNE_NET_SUBTYPE_WLAN_G, NetworkStateToInt(networkState), rssi,
|
|
ssid, wlanV4Addr.ipAddr, wlanV4Addr.ifName, tsStr);
|
|
notifyRatConnectStatus(CNE_RAT_WLAN, NetworkStateToInt(networkState),
|
|
wlanV4Addr.ipAddr);
|
|
} else {
|
|
logw("CNE received action Network State Changed, null WifiManager");
|
|
}
|
|
} else if (action.equals(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
|
|
dlogi( "CNE received action: " + action);
|
|
if (mWifiManager != null) {
|
|
//List<ScanResult> results = mWifiManager.getScanResults();
|
|
// updateWlanScanResults(results);
|
|
}
|
|
} else if (action.equals(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED)) {
|
|
String apnType = intent.getStringExtra(Phone.DATA_APN_TYPES_KEY);
|
|
if (apnType.equals(Phone.APN_TYPE_DEFAULT)) {
|
|
AddressInfo wwanV4AddrInfo = getWwanAddrInfo(Phone.APN_TYPE_DEFAULT,
|
|
IPVersion.INET);
|
|
IPVersion ipv = IPVersion.INET;
|
|
String str = intent.getStringExtra(Phone.DATA_IPVERSION_KEY);
|
|
if (str != null) {
|
|
ipv = Enum.valueOf(IPVersion.class, str);
|
|
}
|
|
|
|
str = intent.getStringExtra(Phone.DATA_APN_TYPE_STATE);
|
|
NetworkInfo.State netState = NetworkInfo.State.DISCONNECTED;
|
|
if (str != null) {
|
|
netState = convertToNetworkState(Enum.valueOf(Phone.DataState.class, str));
|
|
}
|
|
if (netState == NetworkInfo.State.CONNECTED) {
|
|
if (ipv == IPVersion.INET) {
|
|
activeWwanV4IfName = wwanV4AddrInfo.ifName;
|
|
activeWwanV4GatewayAddr = wwanV4AddrInfo.gatewayAddr;
|
|
} else {
|
|
if (ipv6NetState != netState) {
|
|
ipv6NetState = netState;
|
|
logd("Adding IPV6 default route for mobile");
|
|
mService.setDefaultRoute(ConnectivityManager.TYPE_MOBILE, ipv);
|
|
}
|
|
}
|
|
} else {
|
|
if (ipv == IPVersion.INET6) {
|
|
ipv6NetState = netState;
|
|
}
|
|
}
|
|
|
|
AddressInfo wwanAddrInfo;
|
|
if (ipv == IPVersion.INET) {
|
|
wwanAddrInfo = wwanV4AddrInfo;
|
|
} else {
|
|
Log.w(LOG_TAG, "IPV6 is not supported by CNE");
|
|
return;
|
|
}
|
|
|
|
int roaming = (int) (mTelephonyManager.isNetworkRoaming() ? 1 : 0);
|
|
int networkType = mTelephonyManager.getNetworkType();
|
|
int signalStrength = getSignalStrength(networkType);
|
|
Timestamp ts = new Timestamp(System.currentTimeMillis());
|
|
String tsStr = ts.toString();
|
|
dlogi("onDataConnectionStateChanged -"
|
|
+ " networkType= " + networkType
|
|
+ " networkState=" + netState
|
|
+ " signalStr=" + signalStrength
|
|
+ " roaming=" + roaming
|
|
+ " ifName=" + wwanAddrInfo.ifName
|
|
+ " ipAddr=" + wwanAddrInfo.ipAddr
|
|
+ " gateway=" + wwanAddrInfo.gatewayAddr
|
|
+ " timeStamp=" + tsStr);
|
|
updateWwanStatus(networkType, NetworkStateToInt(netState), signalStrength,
|
|
roaming, wwanV4AddrInfo.ipAddr, wwanAddrInfo.ifName, tsStr);
|
|
notifyRatConnectStatus(CNE_RAT_WWAN, NetworkStateToInt(netState),
|
|
wwanV4AddrInfo.ipAddr);
|
|
} else {
|
|
loge("CNE currently does not support this apnType=" + apnType);
|
|
}
|
|
} else if (action.equals(TelephonyIntents.ACTION_QOS_STATE_IND)) {
|
|
int qosIndState = intent.getIntExtra(QosSpec.QosIntentKeys.QOS_INDICATION_STATE, -1);
|
|
int qosState = intent.getIntExtra(QosSpec.QosIntentKeys.QOS_STATUS, -1);
|
|
int qosId = intent.getIntExtra(QosSpec.QosIntentKeys.QOS_ID, -1);
|
|
int txId = intent.getIntExtra(QosSpec.QosIntentKeys.QOS_USERDATA, -1);
|
|
QosSpec myQos = new QosSpec();
|
|
myQos = (QosSpec) intent.getExtras().getParcelable(QosSpec.QosIntentKeys.QOS_SPEC);
|
|
updateQosStatus(txId, qosId, qosIndState, qosState, myQos);
|
|
} else {
|
|
logw("CNE received unexpected action: " + action);
|
|
}
|
|
}
|
|
};
|
|
|
|
private void updateQosStatus (int txId, int qosId, int qosIndState, int qosState, QosSpec myQos) {
|
|
// lookup using txid only when status is initiated since we dont have a qosid yet.
|
|
QoSTracker qt = (qosIndState == QosSpec.QosIndStates.INITIATED) ?
|
|
findQosBySocketId(txId) : findQosByQosId(qosId);
|
|
dlogi("updateQosStatus got indication: " + qosIndState
|
|
+ " qosState: " + qosState
|
|
+ " txId: " + txId
|
|
+ " qosId: " + qosId);
|
|
if (qt != null) qt.handleQosEvent(qosId, qosIndState, qosState, myQos);
|
|
else logd("updateQosStatus did not find a handle to sid: " + txId + " qid: " + qosId);
|
|
}
|
|
|
|
private static enum RatTriedStatus {
|
|
RAT_STATUS_TRIED, RAT_STATUS_NOT_TRIED
|
|
}
|
|
|
|
private class RatInfo {
|
|
int rat;
|
|
RatTriedStatus status;
|
|
|
|
/* other rat related info passed by the callback */
|
|
public RatInfo() {
|
|
rat = CNE_RAT_INVALID;
|
|
}
|
|
|
|
public boolean equals(Object o) {
|
|
if (o instanceof RatInfo) {
|
|
RatInfo ratInfo = (RatInfo) o;
|
|
if (this.rat == ratInfo.rat) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private class CallbackInfo {
|
|
IConSvcEventListener listener;
|
|
boolean isNotifBetterRat;
|
|
}
|
|
|
|
private class RegInfo implements IBinder.DeathRecipient {
|
|
private int role;
|
|
private int regId;
|
|
private int pid;
|
|
private IBinder mBinder;
|
|
private int notificationsSent;
|
|
/*
|
|
* do we want to have the copy of the Link Reqs?? here? if yes
|
|
* which one
|
|
*/
|
|
ArrayList<RatInfo> compatibleRatsList;
|
|
int activeRat;
|
|
int betterRat;
|
|
/* pid and callbackInfo list */
|
|
private CallbackInfo cbInfo;
|
|
|
|
/* constructor */
|
|
public RegInfo(IBinder binder) {
|
|
role = CNE_ROLE_INVALID;
|
|
regId = mRoleRegId++;
|
|
pid = CNE_INVALID_PID;
|
|
compatibleRatsList = new ArrayList<RatInfo>();
|
|
activeRat = CNE_RAT_INVALID;
|
|
betterRat = CNE_RAT_INVALID;
|
|
cbInfo = new CallbackInfo();
|
|
mBinder = binder;
|
|
if (mBinder != null) {
|
|
try {
|
|
dlogi( "CNE linking binder to death");
|
|
mBinder.linkToDeath(this, 0);
|
|
} catch (RemoteException e) {
|
|
binderDied();
|
|
}
|
|
}
|
|
}
|
|
|
|
public void binderDied() {
|
|
dlogi( "CNE binder died role=" + role + "regId =" + regId);
|
|
releaseLink_LP(role, pid);
|
|
}
|
|
|
|
public void dump() {
|
|
dlogi(" Role: " + role);
|
|
dlogi(" RegId: " + regId);
|
|
dlogi(" Pid: " + pid);
|
|
dlogi("ActiveRat: " + activeRat);
|
|
dlogi("BetterRat: " + betterRat);
|
|
for (int index = 0; index < compatibleRatsList.size(); index++) {
|
|
RatInfo ratInfo = (RatInfo) compatibleRatsList.get(index);
|
|
dlogi("compatibleRat[" + index + "]=" + ratInfo.rat + " ratState = "
|
|
+ ratInfo.status);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* regId, RegInfo map */
|
|
private ConcurrentHashMap<Integer, RegInfo> activeRegsList;
|
|
|
|
/*
|
|
* DefaultConnection Class: this class registers for default role
|
|
* and keeps one connection up always if available ...so that the
|
|
* device is always data connected and all the legacy apps that
|
|
* are not using cne api have connectivity
|
|
*/
|
|
class DefaultConnection {
|
|
Map<LinkRequirement, String> mLinkReqs;
|
|
MyLinkNotifier mLinkNotifier;
|
|
|
|
/* Default constructor initializing linkrequirements to null */
|
|
public DefaultConnection() {
|
|
mLinkReqs = null;
|
|
}
|
|
|
|
/* constructor initializing the linkRequirements */
|
|
public DefaultConnection(Map<LinkRequirement, String> linkReqs) {
|
|
mLinkReqs = linkReqs;
|
|
}
|
|
|
|
/*
|
|
* This function will start the connection with a default
|
|
* role.
|
|
*/
|
|
public void startConnection() {
|
|
dlogi( "DefaultConnection startConnection called");
|
|
mLinkNotifier = new MyLinkNotifier();
|
|
getLink_LP(LinkProvider.ROLE_DEFAULT,
|
|
mLinkReqs,
|
|
mLinkNotifier.getCallingPid(),
|
|
mLinkNotifier);
|
|
|
|
}
|
|
|
|
/* Ends the previously started connection */
|
|
public void endConnection() {
|
|
dlogi( "DefaultConnection endConnection called");
|
|
releaseLink_LP(LinkProvider.ROLE_DEFAULT,mLinkNotifier.getCallingPid());
|
|
}
|
|
|
|
/*
|
|
* Implementation of the LinkNotifier interface to pass to the
|
|
* LinkProvider class used to start connection
|
|
*/
|
|
private class MyLinkNotifier extends IConSvcEventListener.Stub {
|
|
/* default constructor */
|
|
public MyLinkNotifier() {
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* on the linkavailability notification we have to notify
|
|
* it it to the iproute to set the default routing table
|
|
*/
|
|
public void onLinkAvail(LinkInfo info) {
|
|
dlogi( "DefaultConnection onLinkAvail called");
|
|
reportLinkSatisfaction_LP(LinkProvider.ROLE_DEFAULT,
|
|
getCallingPid(),
|
|
info,
|
|
true,
|
|
true);
|
|
/* notify to the default network */
|
|
notifyDefaultNwChange(info.getNwId());
|
|
}
|
|
|
|
/*
|
|
* Link Acquisition/startConnection as failed retry after
|
|
* some time.
|
|
*/
|
|
public void onGetLinkFailure(int reason) {
|
|
dlogi( "DefaultConnection onGetLinkFailed reason=" + reason);
|
|
/*
|
|
* we will not release the link here. we might get a
|
|
* better link avail call later.
|
|
*/
|
|
}
|
|
|
|
/* a better link is available accept it. */
|
|
public void onBetterLinkAvail(LinkInfo info) {
|
|
dlogi( "DefaultConnection onBetterLinkAvail called");
|
|
logi("onBetterLinkAvail pid= " + getCallingPid());
|
|
switchLink_LP(LinkProvider.ROLE_DEFAULT,getCallingPid(),info, true);
|
|
/* notify to the default network */
|
|
notifyDefaultNwChange(info.getNwId());
|
|
}
|
|
|
|
/* current connection is lost. */
|
|
public void onLinkLost(LinkInfo info) {
|
|
dlogi( "DefaultConnection Link is lost");
|
|
}
|
|
}
|
|
}
|
|
|
|
private DefaultConnection mDefaultConn;
|
|
|
|
class CNESender extends Handler implements Runnable {
|
|
public CNESender(Looper looper) {
|
|
super(looper);
|
|
}
|
|
|
|
// Only allocated once
|
|
byte[] dataLength = new byte[4];
|
|
|
|
// ***** Runnable implementation
|
|
public void run() {
|
|
// setup if needed
|
|
}
|
|
|
|
// ***** Handler implemementation
|
|
public void handleMessage(Message msg) {
|
|
CNERequest rr = (CNERequest) (msg.obj);
|
|
CNERequest req = null;
|
|
|
|
switch (msg.what) {
|
|
case EVENT_SEND:
|
|
/**
|
|
* mRequestMessagePending++ already happened for
|
|
* every EVENT_SEND, thus we must make sure
|
|
* mRequestMessagePending-- happens once and only
|
|
* once
|
|
*/
|
|
boolean alreadySubtracted = false;
|
|
try {
|
|
LocalSocket s;
|
|
s = mSocket;
|
|
if (s == null) {
|
|
rr.release();
|
|
mRequestMessagesPending--;
|
|
alreadySubtracted = true;
|
|
return;
|
|
}
|
|
synchronized (mRequestsList) {
|
|
mRequestsList.add(rr);
|
|
}
|
|
mRequestMessagesPending--;
|
|
alreadySubtracted = true;
|
|
byte[] data;
|
|
data = rr.mp.marshall();
|
|
rr.mp.recycle();
|
|
rr.mp = null;
|
|
if (data.length > CNE_MAX_COMMAND_BYTES) {
|
|
throw new RuntimeException("Parcel larger than max bytes allowed! "
|
|
+ data.length);
|
|
}
|
|
|
|
// parcel length in big endian
|
|
dataLength[0] = dataLength[1] = 0;
|
|
dataLength[2] = (byte) ((data.length >> 8) & 0xff);
|
|
dataLength[3] = (byte) ((data.length) & 0xff);
|
|
// dlogi( "writing packet: " + data.length + " bytes");
|
|
s.getOutputStream().write(dataLength);
|
|
s.getOutputStream().write(data);
|
|
} catch (IOException ex) {
|
|
logw("IOException " + ex);
|
|
req = findAndRemoveRequestFromList(rr.mSerial);
|
|
// make sure this request has not already been
|
|
// handled, eg, if CNEReceiver cleared the list.
|
|
if (req != null || !alreadySubtracted) {
|
|
rr.release();
|
|
}
|
|
} catch (RuntimeException exc) {
|
|
logw("Uncaught exception " + exc);
|
|
req = findAndRemoveRequestFromList(rr.mSerial);
|
|
// make sure this request has not already been
|
|
// handled, eg, if CNEReceiver cleared the list.
|
|
if (req != null || !alreadySubtracted) {
|
|
// rr.onError(GENERIC_FAILURE);
|
|
rr.release();
|
|
}
|
|
}
|
|
|
|
if (!alreadySubtracted) {
|
|
mRequestMessagesPending--;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Reads in a single CNE message off the wire. A CNE message
|
|
* consists of a 4-byte little-endian length and a subsequent
|
|
* series of bytes. The final message (length header omitted) is
|
|
* read into <code>buffer</code> and the length of the final
|
|
* message (less header) is returned. A return value of -1
|
|
* indicates end-of-stream.
|
|
*
|
|
* @param is
|
|
* non-null; Stream to read from
|
|
* @param buffer
|
|
* Buffer to fill in. Must be as large as maximum
|
|
* message size, or an ArrayOutOfBounds exception will
|
|
* be thrown.
|
|
* @return Length of message less header, or -1 on end of stream.
|
|
* @throws IOException
|
|
*/
|
|
private static int readCneMessage(InputStream is, byte[] buffer) throws IOException {
|
|
int countRead;
|
|
int offset;
|
|
int remaining;
|
|
int messageLength;
|
|
|
|
// First, read in the length of the message
|
|
offset = 0;
|
|
remaining = 4;
|
|
do {
|
|
countRead = is.read(buffer, offset, remaining);
|
|
|
|
if (countRead < 0) {
|
|
logw("Hit EOS reading message length");
|
|
return -1;
|
|
}
|
|
|
|
offset += countRead;
|
|
remaining -= countRead;
|
|
} while (remaining > 0);
|
|
|
|
messageLength = ((buffer[0] & 0xff) << 24)
|
|
| ((buffer[1] & 0xff) << 16)
|
|
| ((buffer[2] & 0xff) << 8)
|
|
| (buffer[3] & 0xff);
|
|
// Then, re-use the buffer and read in the message itself
|
|
offset = 0;
|
|
remaining = messageLength;
|
|
do {
|
|
countRead = is.read(buffer, offset, remaining);
|
|
|
|
if (countRead < 0) {
|
|
logw("Hit EOS reading message. messageLength=" + messageLength
|
|
+ " remaining=" + remaining);
|
|
return -1;
|
|
}
|
|
|
|
offset += countRead;
|
|
remaining -= countRead;
|
|
} while (remaining > 0);
|
|
|
|
return messageLength;
|
|
}
|
|
|
|
class CNEReceiver implements Runnable {
|
|
byte[] buffer;
|
|
|
|
CNEReceiver() {
|
|
buffer = new byte[CNE_MAX_COMMAND_BYTES];
|
|
}
|
|
|
|
public void run() {
|
|
int retryCount = 0;
|
|
|
|
try {
|
|
for (;;) {
|
|
LocalSocket s = null;
|
|
LocalSocketAddress l;
|
|
|
|
try {
|
|
dlogi( "CNE creating socket");
|
|
s = new LocalSocket();
|
|
l = new LocalSocketAddress(SOCKET_NAME_CNE,
|
|
LocalSocketAddress.Namespace.RESERVED);
|
|
s.connect(l);
|
|
} catch (IOException ex) {
|
|
try {
|
|
if (s != null) {
|
|
s.close();
|
|
}
|
|
} catch (IOException ex2) {
|
|
// ignore failure to close after failure to connect
|
|
}
|
|
|
|
// don't print an error message after the the
|
|
// first time or after the 8th time
|
|
if (retryCount == 8) {
|
|
logw("Couldn't find '" + SOCKET_NAME_CNE + "' socket after "
|
|
+ retryCount + " times, continuing to retry silently");
|
|
} else if (retryCount > 0 && retryCount < 8) {
|
|
dlogi("Couldn't find '" + SOCKET_NAME_CNE
|
|
+ "' socket; retrying after timeout");
|
|
}
|
|
|
|
try {
|
|
Thread.sleep(SOCKET_OPEN_RETRY_MILLIS);
|
|
} catch (InterruptedException er) {
|
|
dlogi("cnd socket open retry timer was interrupted");
|
|
}
|
|
retryCount++;
|
|
continue;
|
|
}
|
|
retryCount = 0;
|
|
mSocket = s;
|
|
dlogi( "Connected to '" + SOCKET_NAME_CNE + "' socket");
|
|
isCndUp = true;
|
|
// send the init request to lower layer cne
|
|
sendInitReq();
|
|
|
|
/* wait until CNE gets created */
|
|
synchronized (mService) {
|
|
/* send DefaultNwPref */
|
|
sendDefaultNwPref(mNetworkPreference);
|
|
|
|
/* start the default connection now */
|
|
try {
|
|
mDefaultConn.startConnection();
|
|
} catch (NullPointerException e) {
|
|
logw("Exception mDefaultConn is null" + e);
|
|
}
|
|
}
|
|
int length = 0;
|
|
try {
|
|
InputStream is = mSocket.getInputStream();
|
|
|
|
for (;;) {
|
|
Parcel p;
|
|
|
|
length = readCneMessage(is, buffer);
|
|
|
|
if (length < 0) {
|
|
// End-of-stream reached
|
|
break;
|
|
}
|
|
p = Parcel.obtain();
|
|
p.unmarshall(buffer, 0, length);
|
|
p.setDataPosition(0);
|
|
// dlogi( "Read packet: " + length + " bytes");
|
|
processResponse(p);
|
|
p.recycle();
|
|
}
|
|
} catch (java.io.IOException ex) {
|
|
dlogi( "'" + SOCKET_NAME_CNE + "' socket closed" + ex);
|
|
} catch (Throwable tr) {
|
|
logw("Uncaught exception read length=" + length + "Exception:"
|
|
+ tr.toString());
|
|
}
|
|
dlogi("Disconnected from '" + SOCKET_NAME_CNE + "' socket");
|
|
/*
|
|
* end the default connection it will get started
|
|
* again when connection gets established
|
|
*/
|
|
try {
|
|
mDefaultConn.endConnection();
|
|
} catch (NullPointerException e) {
|
|
logw("Exception mDefaultConn is null" + e);
|
|
}
|
|
isCndUp = false;
|
|
try {
|
|
mSocket.close();
|
|
} catch (IOException ex) {
|
|
}
|
|
|
|
mSocket = null;
|
|
CNERequest.resetSerial();
|
|
|
|
// Clear request list on close
|
|
synchronized (mRequestsList) {
|
|
for (int i = 0, sz = mRequestsList.size(); i < sz; i++) {
|
|
CNERequest rr = mRequestsList.get(i);
|
|
// rr.onError(RADIO_NOT_AVAILABLE);
|
|
rr.release();
|
|
}
|
|
|
|
mRequestsList.clear();
|
|
}
|
|
}
|
|
} catch (Throwable tr) {
|
|
logw("Uncaught exception " + tr);
|
|
}
|
|
}
|
|
}
|
|
|
|
private ITelephony getPhone() {
|
|
return ITelephony.Stub.asInterface(ServiceManager.getService("phone"));
|
|
}
|
|
|
|
// ***** Constructor
|
|
public CNE(Context context, ConnectivityService conn) {
|
|
mRequestMessagesPending = 0;
|
|
mContext = context;
|
|
mService = conn;
|
|
mSenderThread = new HandlerThread("CNESender");
|
|
mSenderThread.start();
|
|
|
|
Looper looper = mSenderThread.getLooper();
|
|
mSender = new CNESender(looper);
|
|
|
|
IntentFilter filter = new IntentFilter();
|
|
filter.addAction(Intent.ACTION_BATTERY_CHANGED);
|
|
filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
|
|
filter.addAction(WifiManager.RSSI_CHANGED_ACTION);
|
|
filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
|
|
filter.addAction(WifiManager.NO_MORE_WIFI_LOCKS);
|
|
filter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
|
|
filter.addAction(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED);
|
|
//QOS intents
|
|
filter.addAction(TelephonyIntents.ACTION_QOS_STATE_IND);
|
|
filter.addAction(Intent.ACTION_SHUTDOWN);
|
|
|
|
context.registerReceiver(mIntentReceiver, filter);
|
|
activeRegsList = new ConcurrentHashMap<Integer, RegInfo>();
|
|
//Initialize a map to save all registration needs and capabilities.
|
|
activeCapabilities = Collections.synchronizedMap(new HashMap<Integer, ExtraLinkCapabilities>());
|
|
//Maintain separate container to track QoS specific registrations. Does
|
|
//not distinguish between linksocket and linkdatagramsockets.
|
|
mQosTrackers = Collections.synchronizedCollection(new ArrayList<QoSTracker>());
|
|
|
|
/* create the default connection no metatdata right now */
|
|
Map<LinkRequirement, String> linkReqs = null;
|
|
mDefaultConn = new DefaultConnection(linkReqs);
|
|
mWifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
|
|
mTelephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
|
|
|
|
// register for phone state notifications.
|
|
mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_SIGNAL_STRENGTHS
|
|
+ PhoneStateListener.LISTEN_DATA_ACTIVITY);
|
|
|
|
mReceiver = new CNEReceiver();
|
|
mReceiverThread = new Thread(mReceiver, "CNEReceiver");
|
|
mReceiverThread.start();
|
|
}
|
|
|
|
private void send(CNERequest rr) {
|
|
Message msg = mSender.obtainMessage(EVENT_SEND, rr);
|
|
// acquireWakeLock();
|
|
msg.sendToTarget();
|
|
}
|
|
|
|
private void processResponse(Parcel p) {
|
|
int type;
|
|
type = p.readInt();
|
|
if (type == RESPONSE_UNSOLICITED) {
|
|
processUnsolicited(p);
|
|
} else if (type == RESPONSE_SOLICITED) {
|
|
processSolicited(p);
|
|
}
|
|
}
|
|
|
|
private CNERequest findAndRemoveRequestFromList(int serial) {
|
|
synchronized (mRequestsList) {
|
|
for (int i = 0, s = mRequestsList.size(); i < s; i++) {
|
|
CNERequest rr = mRequestsList.get(i);
|
|
if (rr.mSerial == serial) {
|
|
mRequestsList.remove(i);
|
|
return rr;
|
|
}
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private void processSolicited(Parcel p) {
|
|
int serial, error;
|
|
serial = p.readInt();
|
|
error = p.readInt();
|
|
CNERequest rr;
|
|
rr = findAndRemoveRequestFromList(serial);
|
|
if (rr == null) {
|
|
logw("Unexpected solicited response! sn: " + serial + " error: " + error);
|
|
return;
|
|
}
|
|
if (error != 0) {
|
|
rr.onError(error);
|
|
rr.release();
|
|
return;
|
|
}
|
|
rr.release();
|
|
}
|
|
|
|
private void processUnsolicited(Parcel p) {
|
|
dlogi( "processUnsolicited called");
|
|
int response;
|
|
|
|
response = p.readInt();
|
|
switch (response) {
|
|
case CNE_RESPONSE_REG_ROLE: {
|
|
handleRegRoleRsp(p);
|
|
break;
|
|
}
|
|
case CNE_RESPONSE_GET_BEST_NW: {
|
|
handleGetCompatibleNwsRsp(p);
|
|
break;
|
|
}
|
|
case CNE_RESPONSE_CONFIRM_NW: {
|
|
handleConfNwRsp(p);
|
|
break;
|
|
}
|
|
case CNE_RESPONSE_DEREG_ROLE: {
|
|
handleDeRegRoleRsp(p);
|
|
break;
|
|
}
|
|
case CNE_REQUEST_BRING_RAT_DOWN: {
|
|
handleRatDownMsg(p);
|
|
break;
|
|
}
|
|
case CNE_REQUEST_BRING_RAT_UP: {
|
|
handleRatUpMsg(p);
|
|
break;
|
|
}
|
|
case CNE_NOTIFY_MORE_PREFERED_RAT_AVAIL: {
|
|
handleMorePrefNwAvailEvent(p);
|
|
break;
|
|
}
|
|
case CNE_NOTIFY_RAT_LOST: {
|
|
handleRatLostEvent(p);
|
|
break;
|
|
}
|
|
case CNE_REQUEST_START_SCAN_WLAN: {
|
|
handleStartScanWlanMsg(p);
|
|
break;
|
|
}
|
|
case CNE_NOTIFY_INFLIGHT_STATUS: {
|
|
handleNotifyInFlightStatusMsg(p);
|
|
break;
|
|
}
|
|
case CNE_NOTIFY_FMC_STATUS: {
|
|
handleFmcStatusMsg(p);
|
|
break;
|
|
}
|
|
case CNE_NOTIFY_HOST_ROUTING_IP: {
|
|
handleHostRoutingIpMsg(p);
|
|
break;
|
|
}
|
|
default: {
|
|
logw("UNKOWN Unsolicited Event " + response);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
private int NetworkStateToInt(NetworkInfo.State state) {
|
|
switch (state) {
|
|
case CONNECTING: {
|
|
return 0;
|
|
}
|
|
case CONNECTED: {
|
|
return 1;
|
|
}
|
|
case SUSPENDED: {
|
|
return 2;
|
|
}
|
|
case DISCONNECTING: {
|
|
return 3;
|
|
}
|
|
case DISCONNECTED: {
|
|
return 4;
|
|
}
|
|
case UNKNOWN: {
|
|
return 5;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
private PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
|
|
@Override
|
|
public void onSignalStrengthsChanged(SignalStrength signalStrength) {
|
|
mSignalStrength = signalStrength;
|
|
if (mTelephonyManager != null) {
|
|
ConnectivityManager cm = (ConnectivityManager) mContext
|
|
.getSystemService(Context.CONNECTIVITY_SERVICE);
|
|
NetworkInfo networkInfo = cm.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
|
|
NetworkInfo.State networkState = (networkInfo == null ? NetworkInfo.State.UNKNOWN
|
|
: networkInfo.getState());
|
|
AddressInfo wwanV4AddrInfo = getWwanAddrInfo(Phone.APN_TYPE_DEFAULT,
|
|
IPVersion.INET);
|
|
int roaming = (int) (mTelephonyManager.isNetworkRoaming() ? 1 : 0);
|
|
int type = networkInfo.getSubtype();
|
|
int rssi = getSignalStrength(type);
|
|
Timestamp ts = new Timestamp(System.currentTimeMillis());
|
|
String tsStr = ts.toString();
|
|
String logmsg = "CNE onSignalStrengthsChanged -"
|
|
+ " type:" + type
|
|
+ " strength:" + rssi
|
|
+ " ipV4Addr:" + wwanV4AddrInfo.ipAddr
|
|
+ " roaming:" + roaming
|
|
+ " iface:" + wwanV4AddrInfo.ifName
|
|
+ " timeStamp:" + tsStr;
|
|
dlogi(logmsg);
|
|
updateWwanStatus(type, NetworkStateToInt(networkState), rssi, roaming,
|
|
wwanV4AddrInfo.ipAddr, wwanV4AddrInfo.ifName, tsStr);
|
|
} else {
|
|
logw("onSignalStrengthsChanged null TelephonyManager");
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onDataActivity(int activity) {
|
|
// update dormancy status if necessary
|
|
if (activity == TelephonyManager.DATA_ACTIVITY_DORMANT) {
|
|
// only update if needed, isWwanDormant should be true
|
|
if (isWwanDormant == false) {
|
|
isWwanDormant = true;
|
|
updateWwanDormancyStatus();
|
|
}
|
|
} else {
|
|
// only update if needed, isWwanDormant should be false
|
|
if (isWwanDormant == true) {
|
|
isWwanDormant = false;
|
|
updateWwanDormancyStatus();
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
private NetworkInfo.State convertToNetworkState(Phone.DataState dataState) {
|
|
switch (dataState) {
|
|
case DISCONNECTED:
|
|
return NetworkInfo.State.DISCONNECTED;
|
|
case CONNECTING:
|
|
return NetworkInfo.State.CONNECTING;
|
|
case CONNECTED:
|
|
return NetworkInfo.State.CONNECTED;
|
|
case SUSPENDED:
|
|
return NetworkInfo.State.SUSPENDED;
|
|
default:
|
|
return NetworkInfo.State.UNKNOWN;
|
|
}
|
|
}
|
|
|
|
private int getSignalStrength(int networkType) {
|
|
if (mSignalStrength == null) {
|
|
logw("getSignalStrength mSignalStrength in null");
|
|
return -1;
|
|
}
|
|
dlogi( "getSignalStrength networkType= " + networkType);
|
|
switch (networkType) {
|
|
case TelephonyManager.NETWORK_TYPE_GPRS:
|
|
case TelephonyManager.NETWORK_TYPE_EDGE:
|
|
case TelephonyManager.NETWORK_TYPE_UMTS:
|
|
case TelephonyManager.NETWORK_TYPE_HSDPA:
|
|
case TelephonyManager.NETWORK_TYPE_HSUPA:
|
|
case TelephonyManager.NETWORK_TYPE_HSPA:
|
|
// dBm = -113 + 2*asu
|
|
return (-113 + 2 * (mSignalStrength.getGsmSignalStrength()));
|
|
case TelephonyManager.NETWORK_TYPE_1xRTT:
|
|
case TelephonyManager.NETWORK_TYPE_CDMA:
|
|
return (mSignalStrength.getCdmaDbm());
|
|
// return(mSignalStrength.getCdmaEcio());
|
|
case TelephonyManager.NETWORK_TYPE_EVDO_0:
|
|
case TelephonyManager.NETWORK_TYPE_EVDO_A:
|
|
return (mSignalStrength.getEvdoDbm());
|
|
// return(mSignalStrength.getEvdoSnr());
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/* API functions */
|
|
public boolean updateBatteryStatus(int status, int pluginType, int level) {
|
|
|
|
CNERequest rr = CNERequest.obtain(CNE_REQUEST_UPDATE_BATTERY_INFO);
|
|
if (rr == null) {
|
|
logw("updateBatteryStatus: rr=NULL");
|
|
return false;
|
|
}
|
|
dlogi("UpdateBatteryStatus status=" + status + " pluginType=" + pluginType
|
|
+ " level=" + level);
|
|
rr.mp.writeInt(3); // num of ints that are getting written
|
|
rr.mp.writeInt(status);
|
|
rr.mp.writeInt(pluginType);
|
|
rr.mp.writeInt(level);
|
|
send(rr);
|
|
return true;
|
|
}
|
|
|
|
public boolean updateWlanStatus(int type, int state, int rssi, String ssid, String ipAddr,
|
|
String iface, String timeStamp) {
|
|
|
|
CNERequest rr = CNERequest.obtain(CNE_REQUEST_UPDATE_WLAN_INFO);
|
|
if (rr == null) {
|
|
logw("updateWlanStatus: rr=NULL - no updated");
|
|
return false;
|
|
}
|
|
|
|
dlogi("UpdateWlanStatus type=" + type
|
|
+ " state=" + state
|
|
+ " rssi=" + rssi
|
|
+ " ssid=" + ssid
|
|
+ " ipAddr=" + ipAddr
|
|
+ " iface=" + iface
|
|
+ " timeStamp=" + timeStamp);
|
|
|
|
rr.mp.writeInt(type);
|
|
rr.mp.writeInt(state);
|
|
rr.mp.writeInt(rssi);
|
|
rr.mp.writeString(ssid);
|
|
rr.mp.writeString(ipAddr);
|
|
rr.mp.writeString(iface);
|
|
rr.mp.writeString(timeStamp);
|
|
send(rr);
|
|
return true;
|
|
}
|
|
|
|
public boolean updateWlanScanResults(List<ScanResult> scanResults) {
|
|
|
|
CNERequest rr = CNERequest.obtain(CNE_REQUEST_UPDATE_WLAN_SCAN_RESULTS);
|
|
if (rr == null) {
|
|
logw("updateWlanScanResults: rr=NULL");
|
|
return false;
|
|
}
|
|
|
|
if (scanResults != null) {
|
|
dlogi("CNE- updateWlanScanResults: scanResults size = " + scanResults.size());
|
|
rr.mp.writeInt(scanResults.size()); // write number of elements
|
|
for (int i = scanResults.size() - 1; i >= 0; i--) {
|
|
ScanResult scanResult = scanResults.get(i);
|
|
rr.mp.writeInt(scanResult.level);
|
|
rr.mp.writeInt(scanResult.frequency);
|
|
rr.mp.writeString(scanResult.SSID);
|
|
rr.mp.writeString(scanResult.BSSID);
|
|
rr.mp.writeString(scanResult.capabilities);
|
|
|
|
// Need to check for empty string before writeString?
|
|
// if (TextUtils.isEmpty(scanResult.capabilities))
|
|
}
|
|
}
|
|
send(rr);
|
|
return true;
|
|
}
|
|
|
|
/** {@hide} */
|
|
public boolean updateWwanStatus(int type, int state, int rssi, int roaming, String ipV4Addr,
|
|
String iface, String timeStamp) {
|
|
|
|
CNERequest rr = CNERequest.obtain(CNE_REQUEST_UPDATE_WWAN_INFO);
|
|
if (rr == null) {
|
|
logw("updateWwanStatus: rr=NULL - no updated");
|
|
return false;
|
|
}
|
|
dlogi("UpdateWwanStatus type=" + type
|
|
+ " state=" + state
|
|
+ " rssi=" + rssi
|
|
+ " roaming=" + roaming
|
|
+ " ipV4Addr=" + ipV4Addr
|
|
+ " iface=" + iface
|
|
+ " timeStamp=" + timeStamp);
|
|
rr.mp.writeInt(type);
|
|
rr.mp.writeInt(state);
|
|
rr.mp.writeInt(rssi);
|
|
rr.mp.writeInt(roaming);
|
|
rr.mp.writeString(ipV4Addr);
|
|
rr.mp.writeString(iface);
|
|
rr.mp.writeString(timeStamp);
|
|
send(rr);
|
|
return true;
|
|
}
|
|
|
|
/** {@hide} */
|
|
private boolean updateWwanDormancyStatus() {
|
|
// send isWwanDormant to cnd
|
|
|
|
CNERequest rr = CNERequest.obtain(CNE_REQUEST_UPDATE_WWAN_DORMANCY_INFO);
|
|
if (rr == null) {
|
|
logw("updateWwanDormancyStatus: rr=NULL - not updated");
|
|
return false;
|
|
}
|
|
dlogi("updateWwanDormancyStatus dormancy=" + isWwanDormant);
|
|
|
|
rr.mp.writeInt(isWwanDormant ? 1 : 0);
|
|
send(rr);
|
|
return true;
|
|
}
|
|
|
|
/** {@hide} */
|
|
private boolean updateDefaultNetwork() {
|
|
// notify cnd which network is the default
|
|
|
|
CNERequest rr = CNERequest.obtain(CNE_REQUEST_UPDATE_DEFAULT_NETWORK_INFO);
|
|
if (rr == null) {
|
|
logw("updateDefaultNetwork: rr=NULL - not updated");
|
|
return false;
|
|
}
|
|
dlogi("updateDefaultNetwork default = " + mDefaultNetwork);
|
|
|
|
rr.mp.writeInt(mDefaultNetwork);
|
|
send(rr);
|
|
return true;
|
|
}
|
|
|
|
/** {@hide} */
|
|
public boolean notifyRatConnectStatus(int type, int status, String ipV4Addr) {
|
|
|
|
CNERequest rr = CNERequest.obtain(CNE_NOTIFY_RAT_CONNECT_STATUS);
|
|
|
|
if (rr == null) {
|
|
logw("notifyRatConnectStatus: rr=NULL");
|
|
return false;
|
|
}
|
|
dlogi("notifyRatConnectStatus ratType=" + type
|
|
+ " status=" + status
|
|
+ " ipV4Addr=" + ipV4Addr);
|
|
rr.mp.writeInt(type);
|
|
rr.mp.writeInt(status);
|
|
rr.mp.writeString(ipV4Addr);
|
|
send(rr);
|
|
return true;
|
|
}
|
|
|
|
/** {@hide} */
|
|
public void sendDefaultNwPref2Cne(int preference) {
|
|
mNetworkPreference = preference;
|
|
}
|
|
|
|
/** {@hide} */
|
|
public void sendDefaultNwPref(int preference) {
|
|
CNERequest rr = CNERequest.obtain(CNE_NOTIFY_DEFAULT_NW_PREF);
|
|
if (rr != null) {
|
|
rr.mp.writeInt(1); // num of ints that are getting written
|
|
int rat = CNE_RAT_WLAN;
|
|
if (preference == ConnectivityManager.TYPE_MOBILE) {
|
|
rat = CNE_RAT_WWAN;
|
|
}
|
|
rr.mp.writeInt(rat);
|
|
send(rr);
|
|
} else {
|
|
logw("sendDefaultNwPref2Cne: rr=NULL");
|
|
}
|
|
return;
|
|
}
|
|
|
|
private boolean sendRegRoleReq(int role, int roleRegId, int fwLinkBw, int revLinkBw) {
|
|
dlogw( "sendRegRoleReq(role=" + role + ", roleRegId=" + roleRegId +
|
|
", fwLinkBw=" + fwLinkBw + ", revLinkBwBw=" + revLinkBw + ") EX");
|
|
|
|
CNERequest rr = CNERequest.obtain(CNE_REQUEST_REG_ROLE);
|
|
if (rr == null) {
|
|
logw("sendRegRoleReq: rr=NULL");
|
|
return false;
|
|
}
|
|
|
|
rr.mp.writeInt(4); // num of ints that are getting written
|
|
rr.mp.writeInt(role);
|
|
rr.mp.writeInt(roleRegId);
|
|
rr.mp.writeInt(fwLinkBw);
|
|
rr.mp.writeInt(revLinkBw);
|
|
send(rr);
|
|
|
|
return true;
|
|
}
|
|
|
|
private boolean sendInitReq() {
|
|
CNERequest rr = CNERequest.obtain(CNE_REQUEST_INIT);
|
|
if (rr == null) {
|
|
logw("sendinitReq: rr=NULL");
|
|
return false;
|
|
}
|
|
send(rr);
|
|
return true;
|
|
}
|
|
|
|
private boolean sendGetCompatibleNwsReq(int roleRegId) {
|
|
CNERequest rr = CNERequest.obtain(CNE_REQUEST_GET_COMPATIBLE_NWS);
|
|
if (rr == null) {
|
|
logw("sendGetCompatibleNwsReq: rr=NULL");
|
|
return false;
|
|
}
|
|
rr.mp.writeInt(1); // num of ints that are getting written
|
|
rr.mp.writeInt(roleRegId);
|
|
dlogi( "Sending GetCompatibleNwsReq roleRegId=" + roleRegId);
|
|
send(rr);
|
|
return true;
|
|
}
|
|
|
|
// change name and params one is added
|
|
private boolean sendConfirmNwReq(int roleRegId, int ifaceId, int confirmation,
|
|
int notifyIfBetterNwAvail, int newIfaceId) {
|
|
CNERequest rr = CNERequest.obtain(CNE_REQUEST_CONF_NW);
|
|
if (rr == null) {
|
|
logw("sendConfirmNwReq: rr=NULL");
|
|
return false;
|
|
}
|
|
rr.mp.writeInt(5); // num of ints that are getting written
|
|
rr.mp.writeInt(roleRegId);
|
|
rr.mp.writeInt(ifaceId);
|
|
rr.mp.writeInt(confirmation);
|
|
rr.mp.writeInt(notifyIfBetterNwAvail);
|
|
rr.mp.writeInt(newIfaceId);
|
|
|
|
send(rr);
|
|
return true;
|
|
|
|
}
|
|
|
|
private boolean sendDeregRoleReq(int roleRegId) {
|
|
|
|
CNERequest rr = CNERequest.obtain(CNE_REQUEST_DEREG_ROLE);
|
|
if (rr == null) {
|
|
logw("sendDeregRoleReq: rr=NULL");
|
|
return false;
|
|
}
|
|
rr.mp.writeInt(1); // num of ints that are getting written
|
|
rr.mp.writeInt(roleRegId);
|
|
dlogi( "sendDeregRoleReq:");
|
|
|
|
send(rr);
|
|
return true;
|
|
}
|
|
|
|
private int getRegId(int pid, int role) {
|
|
int regId = CNE_REGID_INVALID;
|
|
Iterator<Entry<Integer, RegInfo>> activeRegsIter = activeRegsList.entrySet().iterator();
|
|
while (activeRegsIter.hasNext()) {
|
|
RegInfo regInfo = activeRegsIter.next().getValue();
|
|
if (regInfo.role == role && regInfo.pid == pid) {
|
|
regId = regInfo.regId;
|
|
break;
|
|
}
|
|
}
|
|
return regId;
|
|
}
|
|
|
|
private void handleRegRoleRsp(Parcel p) {
|
|
int numInts = p.readInt();
|
|
int roleRegId = p.readInt();
|
|
int evtStatus = p.readInt();
|
|
dlogi("handleRegRoleRsp called with numInts = " + numInts + " RoleRegId = "
|
|
+ roleRegId + " evtStatus = " + evtStatus);
|
|
/* does this role already exists? */
|
|
RegInfo regInfo = activeRegsList.get(roleRegId);
|
|
if (regInfo != null) {
|
|
if (evtStatus == STATUS_SUCCESS) {
|
|
/*
|
|
* register role was success so get the compatible
|
|
* networks
|
|
*/
|
|
sendGetCompatibleNwsReq(roleRegId);
|
|
return;
|
|
} else {
|
|
IConSvcEventListener listener = regInfo.cbInfo.listener;
|
|
if (listener != null) {
|
|
try {
|
|
listener.onGetLinkFailure(LinkNotifier.FAILURE_GENERAL);
|
|
} catch (RemoteException e) {
|
|
logw("handleRegRoleRsp listener is null");
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
dlogw( "handleRegRoleRsp regId=" + roleRegId + " does not exists");
|
|
return;
|
|
}
|
|
}
|
|
|
|
private void handleGetCompatibleNwsRsp(Parcel p) {
|
|
int roleRegId = p.readInt();
|
|
int evtStatus = p.readInt();
|
|
dlogi("handleGetCompatibleNwsRsp called for roleRegId = " + roleRegId
|
|
+ " evtStatus = " + evtStatus);
|
|
/* does this role already exists? */
|
|
RegInfo regInfo = activeRegsList.get(roleRegId);
|
|
if (regInfo != null) {
|
|
//now handle CnE/LinkSocket notifications.
|
|
if (evtStatus == STATUS_SUCCESS) {
|
|
/* save the returned info */
|
|
regInfo.activeRat = p.readInt();
|
|
/* save the oldCompatibleRatsList */
|
|
ArrayList<RatInfo> prevCompatibleRatsList = regInfo.compatibleRatsList;
|
|
ArrayList<RatInfo> newCompatibleRatsList = new ArrayList<RatInfo>();
|
|
for (int i = 0; i < CNE_RAT_MAX; i++) {
|
|
int nextRat = p.readInt();
|
|
if (nextRat != CNE_RAT_INVALID && nextRat != CNE_RAT_NONE) {
|
|
RatInfo ratInfo = new RatInfo();
|
|
ratInfo.rat = nextRat;
|
|
if (nextRat == regInfo.activeRat) {
|
|
ratInfo.status = RatTriedStatus.RAT_STATUS_TRIED;
|
|
} else {
|
|
/* try to preserve the old rat status */
|
|
int index = prevCompatibleRatsList.indexOf(ratInfo);
|
|
if (index != -1) {
|
|
RatInfo oldRatInfo = prevCompatibleRatsList.get(index);
|
|
ratInfo.status = oldRatInfo.status;
|
|
} else {
|
|
ratInfo.status = RatTriedStatus.RAT_STATUS_NOT_TRIED;
|
|
}
|
|
}
|
|
newCompatibleRatsList.add(ratInfo);
|
|
}
|
|
}
|
|
String ipAddr = new String(p.readString());
|
|
int flBwEst = p.readInt();
|
|
int rlBwEst = p.readInt();
|
|
dlogi("ipV4Addr = " + ipAddr + " flBwEst = " + flBwEst
|
|
+ " rlBwEst = " + rlBwEst);
|
|
prevCompatibleRatsList.clear();
|
|
regInfo.compatibleRatsList = newCompatibleRatsList;
|
|
regInfo.dump();
|
|
/* call the call backs */
|
|
/*
|
|
* over here the implementation is to use what ever
|
|
* the connectivityEngine has passed as the best rat,
|
|
* if AndroidConSvc is not satisifed or wants to do
|
|
* give a different nw then, it can confirm the
|
|
* bestrat given by Cne negative, and do not call the
|
|
* onLinkAvail callback yet and request a new one
|
|
*/
|
|
IConSvcEventListener listener = regInfo.cbInfo.listener;
|
|
if (listener != null) {
|
|
try {
|
|
LinkInfo linkInfo = new LinkInfo(ipAddr, flBwEst, rlBwEst,
|
|
regInfo.activeRat);
|
|
if (DBG) {
|
|
Log.d(LOCAL_TAG,"DEBUG:: created LinkInfo with ip "
|
|
+ linkInfo.getIpAddr().getHostAddress());
|
|
}
|
|
regInfo.notificationsSent = regInfo.notificationsSent
|
|
| CNE_MASK_ON_LINK_AVAIL_SENT;
|
|
listener.onLinkAvail(linkInfo);
|
|
} catch (RemoteException e) {
|
|
logw("handleGetCompNwsRsp listener is null");
|
|
}
|
|
}
|
|
return;
|
|
} else {
|
|
IConSvcEventListener listener = regInfo.cbInfo.listener;
|
|
if (listener != null) {
|
|
try {
|
|
listener.onGetLinkFailure(LinkNotifier.FAILURE_NO_LINKS);
|
|
} catch (RemoteException e) {
|
|
logw("handleGetCompNwsRsp listener is null");
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
dlogw( "handleGetCompatibleNwsRsp role does not exists");
|
|
return;
|
|
}
|
|
|
|
}
|
|
|
|
private void handleConfNwRsp(Parcel p) {
|
|
int numInts = p.readInt();
|
|
int roleRegId = p.readInt();
|
|
int evtStatus = p.readInt();
|
|
dlogi("handleConfNwRsp called with numInts = " + numInts
|
|
+ " regRoleId = " + roleRegId + " evtStatus = " + evtStatus);
|
|
}
|
|
|
|
private void handleDeRegRoleRsp(Parcel p) {
|
|
int numInts = p.readInt();
|
|
int roleRegId = p.readInt();
|
|
int evtStatus = p.readInt();
|
|
dlogi("handleDeRegRoleRsp called with numInts = " + numInts
|
|
+ " roleRegId = " + roleRegId + " evtStatus = " + evtStatus);
|
|
/* clean up */
|
|
/* does this role already exists? */
|
|
RegInfo regInfo = activeRegsList.get(roleRegId);
|
|
if (regInfo != null) {
|
|
regInfo.compatibleRatsList.clear();
|
|
activeRegsList.remove(roleRegId);
|
|
} else {
|
|
logw("handleDeRegRoleRsp role does not exists");
|
|
return;
|
|
}
|
|
}
|
|
|
|
private void handleMorePrefNwAvailEvent(Parcel p) {
|
|
int roleRegId = p.readInt();
|
|
int betterRat = p.readInt();
|
|
String ipAddr = new String(p.readString());
|
|
int flBwEst = p.readInt();
|
|
int rlBwEst = p.readInt();
|
|
dlogi("handleMorePrefNwAvailEvent called for roleRegId = " + roleRegId
|
|
+ " betterRat = " + betterRat
|
|
+ " ipAddr = " + ipAddr
|
|
+ " flBwEst = " + flBwEst
|
|
+ " rlBwEst = " + rlBwEst);
|
|
/* does this role already exists? */
|
|
RegInfo regInfo = activeRegsList.get(roleRegId);
|
|
if (regInfo != null) {
|
|
/* save the betterRat info */
|
|
regInfo.betterRat = betterRat;
|
|
/* call the call backs */
|
|
IConSvcEventListener listener = regInfo.cbInfo.listener;
|
|
if (listener != null) {
|
|
try {
|
|
LinkInfo linkInfo = new LinkInfo(ipAddr, flBwEst, rlBwEst, betterRat);
|
|
regInfo.notificationsSent
|
|
= regInfo.notificationsSent | CNE_MASK_ON_BETTER_LINK_AVAIL_SENT;
|
|
listener.onBetterLinkAvail(linkInfo);
|
|
} catch (RemoteException e) {
|
|
logw("handleMorePrefNwAvailEvt listener is null");
|
|
}
|
|
}
|
|
} else {
|
|
logw("handleMorePrefNwAvailEvent role does not exists");
|
|
return;
|
|
}
|
|
}
|
|
|
|
private void handleRatLostEvent(Parcel p) {
|
|
int numInts = p.readInt();
|
|
int roleRegId = p.readInt();
|
|
int rat = p.readInt();
|
|
dlogi("handleRatLostEvent called with numInts = " + numInts
|
|
+ " roleRegId = " + roleRegId + " rat = " + rat);
|
|
/* does this role already exists? */
|
|
RegInfo regInfo = activeRegsList.get(roleRegId);
|
|
if (regInfo != null) {
|
|
if (regInfo.activeRat == rat) {
|
|
IConSvcEventListener listener = regInfo.cbInfo.listener;
|
|
if (listener != null) {
|
|
try {
|
|
LinkInfo linkInfo = new LinkInfo(null, LinkInfo.INF_UNSPECIFIED,
|
|
LinkInfo.INF_UNSPECIFIED, rat);
|
|
listener.onLinkLost(linkInfo);
|
|
} catch (RemoteException e) {
|
|
logw("handleRatLostEvent listener is null");
|
|
}
|
|
}
|
|
} else {
|
|
dlogw("Rat lost is not for the active rat=" + regInfo.activeRat);
|
|
}
|
|
} else {
|
|
dlogw( "handleRatLostEvent role does not exists");
|
|
return;
|
|
}
|
|
}
|
|
|
|
private void handleRatDownMsg(Parcel p) {
|
|
int ratType = p.readInt();
|
|
dlogi( "handleRatDownMsg called ratType = " + ratType);
|
|
if (mService != null) {
|
|
mService.bringDownRat(ratType);
|
|
}
|
|
return;
|
|
}
|
|
|
|
private void handleRatUpMsg(Parcel p) {
|
|
int ratType = p.readInt();
|
|
dlogi( "handleRatUpMsg called ratType = " + ratType);
|
|
switch (ratType) {
|
|
case CNE_RAT_WLAN:
|
|
handleWlanBringUp();
|
|
break;
|
|
case CNE_RAT_WWAN:
|
|
handleWwanBringUp();
|
|
break;
|
|
default:
|
|
dlogw( "UnHandled Rat Type: " + ratType);
|
|
}
|
|
return;
|
|
}
|
|
|
|
private void handleWlanBringUp() {
|
|
try {
|
|
ConnectivityManager cm = (ConnectivityManager) mContext
|
|
.getSystemService(Context.CONNECTIVITY_SERVICE);
|
|
NetworkInfo networkInfo = cm.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
|
|
NetworkInfo.State networkState = networkInfo.getState();
|
|
if (networkState == NetworkInfo.State.CONNECTED) {
|
|
AddressInfo wlanV4AddrInfo = getWlanAddrInfo(IPVersion.INET);
|
|
notifyRatConnectStatus(CNE_RAT_WLAN, NetworkStateToInt(networkState),
|
|
wlanV4AddrInfo.ipAddr);
|
|
|
|
} else {
|
|
mService.bringUpRat(CNE_RAT_WLAN);
|
|
}
|
|
} catch (NullPointerException e) {
|
|
logw("handleWlanBringUp " + e);
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
|
|
private void handleWwanBringUp() {
|
|
try {
|
|
ConnectivityManager cm = (ConnectivityManager) mContext
|
|
.getSystemService(Context.CONNECTIVITY_SERVICE);
|
|
NetworkInfo networkInfo = cm.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
|
|
NetworkInfo.State networkState = networkInfo.getState();
|
|
if (networkState == NetworkInfo.State.CONNECTED) {
|
|
AddressInfo wwanV4AddrInfo = getWwanAddrInfo(Phone.APN_TYPE_DEFAULT,
|
|
IPVersion.INET);
|
|
notifyRatConnectStatus(CNE_RAT_WWAN, NetworkStateToInt(networkState),
|
|
wwanV4AddrInfo.ipAddr);
|
|
} else {
|
|
// tell telephony service to bypass all network
|
|
// checking
|
|
if (getFmcObj() != null) {
|
|
if (getFmcObj().dsAvail == true) {
|
|
try {
|
|
// bypass connectivity and subscription
|
|
// checking, and bring up data call
|
|
ITelephony mPhone = getPhone();
|
|
if (mPhone == null)
|
|
logw("handle wwan bring up found null telephony object");
|
|
else
|
|
mPhone.setDataReadinessChecks(false, false, true);
|
|
} catch (RemoteException e) {
|
|
logw("remoteException while calling setDataReadinessChecks");
|
|
}
|
|
}
|
|
}
|
|
mService.bringUpRat(CNE_RAT_WWAN);
|
|
}
|
|
} catch (NullPointerException e) {
|
|
logw("handleWwanBringUp " + e);
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
|
|
private void handleStartScanWlanMsg(Parcel p) {
|
|
dlogi( "handleStartScanWlanMsg called");
|
|
if (!mWifiManager.isWifiEnabled()) return;
|
|
mWifiManager.startScanActive();
|
|
}
|
|
|
|
private void handleNotifyInFlightStatusMsg(Parcel p) {
|
|
dlogi( "handleNotifyInFlightStatusMsg called");
|
|
boolean on;
|
|
p.readInt(); // numInts - unused
|
|
int status = p.readInt();
|
|
if (status == STATUS_INFLIGHT) {
|
|
on = true;
|
|
} else {
|
|
on = false;
|
|
}
|
|
Settings.System.putInt(mContext.getContentResolver(), Settings.System.AIRPLANE_MODE_ON,
|
|
on ? 1 : 0);
|
|
Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
|
|
intent.putExtra("state", on);
|
|
mContext.sendBroadcast(intent);
|
|
|
|
}
|
|
|
|
private void handleFmcStatusMsg(Parcel p) {
|
|
FmcRegInfo rInfo = getFmcObj();
|
|
p.readInt(); // numInts - unused
|
|
int status = p.readInt();
|
|
if (rInfo != null) {
|
|
dlogi("handleFmcStatusMsg fmc_status="
|
|
+ FmcNotifier.FMC_STATUS_STR[status]);
|
|
if (status == FmcNotifier.FMC_STATUS_ENABLED) {
|
|
AddressInfo wwanV4AddrInfo = getWwanAddrInfo(Phone.APN_TYPE_DEFAULT,
|
|
IPVersion.INET);
|
|
dlogi("handleFmcStatusMsg fmc_status=" + FmcNotifier.FMC_STATUS_STR[status] +
|
|
",hostIp=" + hostRoutingIpAddr + ",interface=" + activeWlanIfName);
|
|
IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
|
|
INetworkManagementService nms = INetworkManagementService.Stub.asInterface(b);
|
|
try {
|
|
nms.addDstRoute(activeWlanIfName, hostRoutingIpAddr, activeWlanGatewayAddr);
|
|
if (mService != null) {
|
|
mService.setDefaultRoute(ConnectivityManager.TYPE_MOBILE, IPVersion.INET);
|
|
}
|
|
} catch (Exception e) {
|
|
logw("Error adding host routing");
|
|
}
|
|
rInfo.enabled = true;
|
|
} else if (status == FmcNotifier.FMC_STATUS_REGISTRATION_SUCCESS) {
|
|
dlogi("handleFmcStatusMsg REGISTRATION_SUCCESS - fmcEnabled="
|
|
+ rInfo.enabled );
|
|
if (rInfo.enabled == true) {
|
|
IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
|
|
INetworkManagementService nms = INetworkManagementService.Stub.asInterface(b);
|
|
try {
|
|
nms.replaceV4DefaultRoute(activeWlanIfName, activeWlanGatewayAddr);
|
|
nms.delDstRoute(hostRoutingIpAddr);
|
|
} catch (Exception e) {
|
|
logw("Error adding host routing");
|
|
}
|
|
rInfo.enabled = false;
|
|
}
|
|
} else {
|
|
if ((status == FmcNotifier.FMC_STATUS_CLOSED)
|
|
|| (status == FmcNotifier.FMC_STATUS_FAILURE)
|
|
|| (status == FmcNotifier.FMC_STATUS_DS_NOT_AVAIL)
|
|
|| (status == FmcNotifier.FMC_STATUS_RETRIED)) {
|
|
rInfo.enabled = false;
|
|
rInfo.dsAvail = false;
|
|
}
|
|
}
|
|
if (status == FmcNotifier.FMC_STATUS_CLOSED) {
|
|
if (rInfo.actionStop) {
|
|
onFmcStatus(status);
|
|
setFmcObj(null);
|
|
} else {
|
|
onFmcStatus(FmcNotifier.FMC_STATUS_FAILURE);
|
|
}
|
|
} else {
|
|
onFmcStatus(status);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void handleHostRoutingIpMsg(Parcel p) {
|
|
String ipAddr = new String(p.readString());
|
|
dlogi( "handleHostRoutingIpMsg ip=" + ipAddr);
|
|
hostRoutingIpAddr = ipAddr;
|
|
if (getFmcObj() != null) {
|
|
getFmcObj().dsAvail = true;
|
|
}
|
|
return;
|
|
}
|
|
|
|
/** {@hide} */
|
|
public void setDefaultConnectionNwPref(int preference) {
|
|
dlogi( "setDefaultConnectionNwPref called with pref = " + preference);
|
|
/* does this role already exists? */
|
|
RegInfo regInfo = activeRegsList.get(CNE_DEFAULT_CON_REGID);
|
|
if (regInfo != null) {
|
|
/*
|
|
* preference can be either wlan or wwan send the
|
|
* confirmNw with the next best wwan or wlan rat in the
|
|
* compatiblerats list for this role
|
|
*/
|
|
if (preference != regInfo.activeRat) {
|
|
sendConfirmNwReq(regInfo.regId, regInfo.activeRat, 0, // not satisfied with this
|
|
regInfo.cbInfo.isNotifBetterRat ? 1 : 0, preference);
|
|
}
|
|
} else {
|
|
logw("Default Registration does not exists");
|
|
}
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* the dlogic of the function will decide what is the next rat to
|
|
* try. here we follow the order provided by the connectivity
|
|
* engine. AndroidConSvc can change this dlogic what ever it wants
|
|
*/
|
|
private int getNextRatToTry(ArrayList<RatInfo> ratList) {
|
|
int candidateRat = CNE_RAT_INVALID;
|
|
/* index 0 is the active rat */
|
|
for (int index = 1; index < ratList.size(); index++) {
|
|
RatInfo ratInfo = (RatInfo) ratList.get(index);
|
|
if (ratInfo.status == RatTriedStatus.RAT_STATUS_NOT_TRIED) {
|
|
candidateRat = ratInfo.rat;
|
|
ratInfo.status = RatTriedStatus.RAT_STATUS_TRIED;
|
|
break;
|
|
}
|
|
}
|
|
dlogi( "getNextRatToTry called NextRatToTry= " + candidateRat);
|
|
return candidateRat;
|
|
}
|
|
|
|
/**
|
|
* Convert a data rate string such as "2Mbps" to an integer
|
|
* representing the data rate in bps. If no data rate is given
|
|
* then bps will be assumed.
|
|
* @param rate a data rate string, such as "100kbps" or "2Mbps"
|
|
* @return the data rate in bps as an integer
|
|
*/
|
|
private static int parseBwString(String rate) {
|
|
if (rate == null) return 0;
|
|
|
|
int rateMultiple = 1; // defaults to bps
|
|
if (rate.toLowerCase().endsWith("kbps") || rate.endsWith("kbit/s") || rate.endsWith("kb/s")) {
|
|
rateMultiple = 1000; // 1,000 bps per 1 Mbps
|
|
} else if (rate.toLowerCase().endsWith("mbps") || rate.endsWith("Mbit/s") || rate.endsWith("Mb/s")) {
|
|
rateMultiple = 1000000; // 1,000,000 bps per 1 Mbps
|
|
} else if (rate.toLowerCase().endsWith("gbps") || rate.endsWith("Gbit/s") || rate.endsWith("Gb/s")) {
|
|
rateMultiple = 1000000000; // 1,000,000,000 bps per 1 Gbps
|
|
}
|
|
|
|
// find first non-numeric character, and trim
|
|
int trimPosition = rate.length();
|
|
for (int i = 0; i < rate.length(); i++) {
|
|
if (rate.charAt(i) <= '0' || rate.charAt(i) >= '9') {
|
|
trimPosition = i;
|
|
break;
|
|
}
|
|
}
|
|
rate = rate.substring(0, trimPosition);
|
|
if (rate.length() == 0) rate = "0";
|
|
|
|
return (Integer.parseInt(rate) * rateMultiple);
|
|
}
|
|
|
|
// generate SocketId's
|
|
// TODO find a better way to assign unique ids per socket/registration since
|
|
// the regid may overlap with default connection when num registrations
|
|
// exceed max integer value.
|
|
synchronized private static int getNextSocketId() {
|
|
if (mRoleRegId == Integer.MAX_VALUE) mRoleRegId = 0;
|
|
// Using the LP roleregid as the socket id since both LP and LS APIs aren't
|
|
// supposed to coexist concurrently.
|
|
return mRoleRegId;
|
|
}
|
|
|
|
/** {@hide} */
|
|
public boolean getLink_LP(int role, Map<LinkRequirement, String> linkReqs, int pid,
|
|
IBinder listener) {
|
|
dlogi( "getLink called for role = " + role);
|
|
/* did the app(pid) register for this role already? */
|
|
if (getRegId(pid, role) != CNE_REGID_INVALID) {
|
|
logw("Multpl same role reg's not allowed by single app");
|
|
return false;
|
|
} else {
|
|
/* new role registration for this app (pid) */
|
|
RegInfo regInfo = new RegInfo(listener);
|
|
regInfo.role = role;
|
|
regInfo.pid = pid;
|
|
IConSvcEventListener evtListener =
|
|
(IConSvcEventListener) IConSvcEventListener.Stub.asInterface(listener);
|
|
regInfo.cbInfo.listener = evtListener;
|
|
regInfo.cbInfo.isNotifBetterRat = false;
|
|
dlogi( "activeRegsList.size before = " + activeRegsList.size());
|
|
activeRegsList.put(regInfo.regId, regInfo);
|
|
dlogi( "activeRolesList.size after = " + activeRegsList.size());
|
|
|
|
int fwLinkBwReq = 0;
|
|
int revLinkBwReq = 0;
|
|
if (linkReqs != null) {
|
|
for (Map.Entry<LinkRequirement, String> e : linkReqs.entrySet()) {
|
|
LinkRequirement key = e.getKey();
|
|
String value = e.getValue();
|
|
if (value == null) continue;
|
|
|
|
switch (key) {
|
|
case FW_LINK_BW:
|
|
fwLinkBwReq = parseBwString(value);
|
|
if (fwLinkBwReq == -1) {
|
|
logw("Invalid bandwidth req. value: " + value);
|
|
return false;
|
|
}
|
|
break;
|
|
case REV_LINK_BW:
|
|
revLinkBwReq = parseBwString(value);
|
|
if (revLinkBwReq == -1) {
|
|
logw("Invalid bandwidth req. value: " + value);
|
|
return false;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
sendRegRoleReq(role, regInfo.regId, fwLinkBwReq, revLinkBwReq);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
/** {@hide} */
|
|
public boolean reportLinkSatisfaction_LP(int role, int pid, LinkInfo info, boolean isSatisfied,
|
|
boolean isNotifyBetterLink) {
|
|
dlogi("reporting connection satisfaction role = " + role + "isSatisfied = "
|
|
+ isSatisfied + "isNotifyBetterLink" + isNotifyBetterLink);
|
|
int regId = getRegId(pid, role);
|
|
RegInfo regInfo = activeRegsList.get(regId);
|
|
if (regInfo != null) {
|
|
if ((regInfo.notificationsSent
|
|
& CNE_MASK_ON_LINK_AVAIL_SENT) == CNE_MASK_ON_LINK_AVAIL_SENT) {
|
|
regInfo.cbInfo.isNotifBetterRat = isNotifyBetterLink;
|
|
int ratToTry = CNE_RAT_NONE;
|
|
if (!isSatisfied) {
|
|
ratToTry = getNextRatToTry(regInfo.compatibleRatsList);
|
|
}
|
|
sendConfirmNwReq(regInfo.regId, info == null ? regInfo.activeRat : info.getNwId(),
|
|
isSatisfied ? 1 : 0, isNotifyBetterLink ? 1 : 0, ratToTry);
|
|
/*
|
|
* if the app was not satisfied and ratToTry was
|
|
* CNE_RAT_INVALID then call back the app saying
|
|
* connection not possible
|
|
*/
|
|
if (!isSatisfied && ratToTry == CNE_RAT_INVALID) {
|
|
// call the callback
|
|
try {
|
|
IConSvcEventListener evtListener = regInfo.cbInfo.listener;
|
|
if (evtListener != null) {
|
|
evtListener.onGetLinkFailure(LinkNotifier.FAILURE_NO_LINKS);
|
|
}
|
|
return true;
|
|
} catch (RemoteException e) {
|
|
logw("remoteException while calling onConnectionComplete");
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
} else {
|
|
logw("OnLinkAvail notification was not sent yet.");
|
|
return false;
|
|
}
|
|
} else {
|
|
logw("App did not register for this role");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/** {@hide} */
|
|
public boolean releaseLink_LP(int role, int pid) {
|
|
int regId;
|
|
if (role < 0) { // linksocket releasing link
|
|
dlogi( "releasing link for socket = " + pid);
|
|
regId = pid;
|
|
} else { // linkprovider releasing link
|
|
dlogi( "releasing link for role = " + role);
|
|
regId = getRegId(pid, role);
|
|
}
|
|
|
|
//activeRegsList is the superset, check if QoS regestration is present inside
|
|
//of this superset and remove it from that subset as well.
|
|
RegInfo regInfo = activeRegsList.get(regId);
|
|
if (regInfo != null) {
|
|
sendDeregRoleReq(regInfo.regId);
|
|
/* clean up */
|
|
try {
|
|
regInfo.mBinder.unlinkToDeath(regInfo, 0);
|
|
} catch (NoSuchElementException exp) {
|
|
logw("bindrLinkToDeatch was not registered");
|
|
}
|
|
regInfo.compatibleRatsList.clear();
|
|
activeRegsList.remove(regId);
|
|
// clean up QoS handler
|
|
QoSTracker qt = findQosBySocketId(regId);
|
|
if (qt != null) {
|
|
qt.stopQosTransaction(); //release associated QoS
|
|
synchronized(mQosTrackers) {
|
|
try {
|
|
mQosTrackers.remove(qt);
|
|
dlogd("stopped tracking qos for socket: " + regId);
|
|
} catch (Exception e) {
|
|
dlogd("release link, error while remove qos tracker: " + e);
|
|
}
|
|
}
|
|
}
|
|
// clean up needs for this registration.
|
|
synchronized(activeCapabilities) {
|
|
if (activeCapabilities.containsKey(regId)) {
|
|
activeCapabilities.remove(regId);
|
|
} else {
|
|
dlogi("registration was not a link socket");
|
|
}
|
|
}
|
|
return true;
|
|
} else {
|
|
logw("App did not register for this role");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/** {@hide} */
|
|
public boolean switchLink_LP(int role, int pid, LinkInfo info, boolean isNotifyBetterLink) {
|
|
dlogi( "switch link for role = " + role);
|
|
int regId = getRegId(pid, role);
|
|
RegInfo regInfo = activeRegsList.get(regId);
|
|
if (regInfo != null) {
|
|
if ((regInfo.notificationsSent & CNE_MASK_ON_BETTER_LINK_AVAIL_SENT)
|
|
== CNE_MASK_ON_BETTER_LINK_AVAIL_SENT) {
|
|
regInfo.activeRat = info == null ? regInfo.betterRat : info.getNwId();
|
|
sendConfirmNwReq(regInfo.regId, regInfo.activeRat, CNE_LINK_SATISFIED,
|
|
isNotifyBetterLink ? 1 : 0, CNE_RAT_NONE);
|
|
return true;
|
|
} else {
|
|
logw("OnBetterLinkAvail notification was not sent yet.");
|
|
return false;
|
|
}
|
|
} else {
|
|
logw("App did not register for this role");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/** {@hide} */
|
|
public boolean rejectSwitch_LP(int role, int pid, LinkInfo info, boolean isNotifyBetterLink) {
|
|
dlogi( "rejectSwitch for role = " + role);
|
|
int regId = getRegId(pid, role);
|
|
RegInfo regInfo = activeRegsList.get(regId);
|
|
if (regInfo != null) {
|
|
if ((regInfo.notificationsSent & CNE_MASK_ON_BETTER_LINK_AVAIL_SENT)
|
|
== CNE_MASK_ON_BETTER_LINK_AVAIL_SENT) {
|
|
sendConfirmNwReq(regInfo.regId, info == null ? regInfo.betterRat : info.getNwId(),
|
|
CNE_LINK_NOT_SATISFIED, isNotifyBetterLink ? 1 : 0, CNE_RAT_NONE);
|
|
return true;
|
|
} else {
|
|
logw("OnBetterLinkAvail notification was not sent yet.");
|
|
return false;
|
|
}
|
|
} else {
|
|
logw("App did not register for this role");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/** {@hide} */
|
|
public static boolean configureSsid(String newStr) {
|
|
try {
|
|
boolean strMatched = false;
|
|
File file = new File("/data/ssidconfig.txt");
|
|
|
|
// Read file to buffer
|
|
BufferedReader reader = new BufferedReader(new FileReader(file));
|
|
String line = "";
|
|
String oldtext = "";
|
|
String oldStr = "";
|
|
// Get token of new string
|
|
StringTokenizer newst = new StringTokenizer(newStr, ":");
|
|
String newToken = newst.nextToken();
|
|
dlogi( "configureSsid: newToken: " + newToken);
|
|
while ((line = reader.readLine()) != null) {
|
|
oldtext += line + "\r\n";
|
|
StringTokenizer oldst = new StringTokenizer(line, ":");
|
|
while (oldst.hasMoreTokens()) {
|
|
String oldToken = oldst.nextToken();
|
|
dlogi( "configureSsid: oldToken: " + oldToken);
|
|
if (newToken.equals(oldToken)) {
|
|
dlogi( "configSsid entry matched");
|
|
// Save string to be replaced
|
|
oldStr = line;
|
|
strMatched = true;
|
|
}
|
|
}
|
|
}
|
|
if (!strMatched) {
|
|
dlogi( "configSsid entry not matched");
|
|
return false;
|
|
}
|
|
// To replace new text in file
|
|
String newtext = oldtext.replaceAll(oldStr, newStr);
|
|
reader.close();
|
|
FileWriter writer = new FileWriter("/data/ssidconfig.txt");
|
|
writer.write(newtext);
|
|
writer.close();
|
|
return true;
|
|
} catch (IOException ioe) {
|
|
ioe.printStackTrace();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/** {@hide} */
|
|
public void notifyDefaultNwChange(int nwId) {
|
|
dlogi("notifyDefaultNwChange - nwId: " + nwId
|
|
+ " activeWwanV4Ifname:" + activeWwanV4IfName
|
|
+ " activeWwanV6Ifname:" + activeWwanV6IfName
|
|
+ " activeWlanIfname:" + activeWlanIfName);
|
|
mDefaultNetwork = nwId;
|
|
// Change default network interface in main table to new one
|
|
if ((nwId == ConnectivityManager.TYPE_WIFI) && (activeWlanIfName != null)) {
|
|
// Need to delete the host entry
|
|
if (mRemoveHostEntry) {
|
|
mRemoveHostEntry = false;
|
|
IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
|
|
INetworkManagementService nms = INetworkManagementService.Stub.asInterface(b);
|
|
try {
|
|
nms.delDstRoute(hostRoutingIpAddr);
|
|
} catch (Exception e) {
|
|
logw("Error deleting host routing");
|
|
}
|
|
}
|
|
}
|
|
if (mService != null) {
|
|
mService.setDefaultRoute(nwId, IPVersion.INET);
|
|
} else {
|
|
dlogw( "notifyDefaultNwChange: mService in NULL");
|
|
}
|
|
updateDefaultNetwork();
|
|
return;
|
|
}
|
|
|
|
/* Fmc Related */
|
|
private FmcRegInfo fmcRegInfo = null;
|
|
|
|
/** {@hide} */
|
|
private class FmcRegInfo implements IBinder.DeathRecipient {
|
|
protected boolean enabled; // true when FMC is enabled
|
|
protected boolean actionStop; // when stop got pushed
|
|
protected boolean dsAvail; // DS is available
|
|
protected int lastSendStatus; // last send status to OEM
|
|
protected String ssid; // CT AP
|
|
private IBinder mBinder;
|
|
IFmcEventListener mListener;
|
|
|
|
/* constructor */
|
|
public FmcRegInfo(IBinder binder) {
|
|
mBinder = binder;
|
|
mListener = null;
|
|
enabled = false;
|
|
actionStop = false;
|
|
dsAvail = false;
|
|
ssid = "";
|
|
lastSendStatus = -1;
|
|
dlogi( "FmcRegInfo - binder=" + mBinder);
|
|
if (mBinder != null) {
|
|
mListener = (IFmcEventListener) IFmcEventListener.Stub.asInterface(binder);
|
|
try {
|
|
dlogi( "FmcRegInfo: linking binder to death");
|
|
mBinder.linkToDeath(this, 0);
|
|
} catch (RemoteException e) {
|
|
binderDied();
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
}
|
|
|
|
public void binderDied() {
|
|
mListener = null;
|
|
dlogi( "CNE@FmcRegInfo: RemoteException.");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* {@hide} return: true when send out status successfully
|
|
*/
|
|
private void onFmcStatus(int status) {
|
|
FmcRegInfo rInfo;
|
|
rInfo = getFmcObj();
|
|
if (rInfo != null) {
|
|
rInfo.lastSendStatus = status;
|
|
if ((rInfo.lastSendStatus == FmcNotifier.FMC_STATUS_CLOSED)
|
|
|| (rInfo.lastSendStatus == FmcNotifier.FMC_STATUS_FAILURE)
|
|
|| (rInfo.lastSendStatus == FmcNotifier.FMC_STATUS_DS_NOT_AVAIL)
|
|
|| (rInfo.lastSendStatus == FmcNotifier.FMC_STATUS_RETRIED)) {
|
|
mRemoveHostEntry = true;
|
|
try {
|
|
// tell telephony service to do all necessary
|
|
// network checking, no data call
|
|
ITelephony mPhone = getPhone();
|
|
if (mPhone == null)
|
|
logw("handle wwan bring up found null telephony object");
|
|
else
|
|
mPhone.setDataReadinessChecks(true, true, false);
|
|
} catch (RemoteException e) {
|
|
logw("remoteException while calling setDataReadinessChecks");
|
|
}
|
|
}
|
|
dlogi( "onFmcStatus: mRemoveHostEntry=" + mRemoveHostEntry);
|
|
if (rInfo.mListener != null) {
|
|
try {
|
|
// Check status and send to OEM
|
|
dlogi("onFmcStatus: fmc_status=" + FmcNotifier.FMC_STATUS_STR[status]);
|
|
rInfo.mListener.onFmcStatus(status);
|
|
} catch (RemoteException e) {
|
|
logw("onFmcStatus: exception onFmcStatus");
|
|
}
|
|
}
|
|
} else {
|
|
logw("onFmcStatus: regInfo = null");
|
|
}
|
|
}
|
|
|
|
/** {@hide} */
|
|
private FmcRegInfo getFmcObj() {
|
|
return fmcRegInfo;
|
|
}
|
|
|
|
/** {@hide} */
|
|
private void setFmcObj(FmcRegInfo obj) {
|
|
fmcRegInfo = obj;
|
|
}
|
|
|
|
/** {@hide} */
|
|
private FmcRegInfo reestablishBinder(FmcRegInfo rSrc, IBinder binder) {
|
|
FmcRegInfo r = null;
|
|
if (binder != rSrc.mBinder) {
|
|
dlogi( "reestablishBinder: new binder");
|
|
r = new FmcRegInfo(binder);
|
|
r.ssid = rSrc.ssid;
|
|
r.enabled = rSrc.enabled;
|
|
r.dsAvail = rSrc.dsAvail;
|
|
r.lastSendStatus = rSrc.lastSendStatus;
|
|
setFmcObj(r);
|
|
} else {
|
|
dlogi( "reestablishBinder: existing binder");
|
|
r = rSrc;
|
|
}
|
|
return r;
|
|
}
|
|
|
|
/** {@hide} */
|
|
public boolean startFmc(IBinder binder) {
|
|
boolean ok = true;
|
|
boolean reqToStart = true;
|
|
FmcRegInfo rInfo = getFmcObj();
|
|
dlogi( "startFmc: ");
|
|
if (rInfo != null) {
|
|
rInfo = reestablishBinder(rInfo, binder);
|
|
if (rInfo.enabled) { // already enabled
|
|
onFmcStatus(FmcNotifier.FMC_STATUS_ENABLED);
|
|
reqToStart = false;
|
|
} else if (rInfo.dsAvail) {
|
|
onFmcStatus(FmcNotifier.FMC_STATUS_REGISTRATION_SUCCESS);
|
|
reqToStart = false;
|
|
}
|
|
} else { /* new OEM registration for this app */
|
|
setFmcObj(new FmcRegInfo(binder));
|
|
}
|
|
if (reqToStart) {
|
|
|
|
ConnectivityManager cm = (ConnectivityManager) mContext
|
|
.getSystemService(Context.CONNECTIVITY_SERVICE);
|
|
NetworkInfo networkInfo = cm.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
|
|
NetworkInfo.State networkState = (networkInfo
|
|
== null ? NetworkInfo.State.UNKNOWN : networkInfo.getState());
|
|
dlogi("CNE@startFmc: wifi state=" + networkState);
|
|
if (networkState != NetworkInfo.State.CONNECTED) {
|
|
dlogi("CNE@startFmc: wifi not ready");
|
|
onFmcStatus(FmcNotifier.FMC_STATUS_FAILURE);
|
|
return ok;
|
|
}
|
|
onFmcStatus(FmcNotifier.FMC_STATUS_INITIALIZED);
|
|
// send enableFMC to Cnd
|
|
CNERequest rr = CNERequest.obtain(CNE_REQUEST_START_FMC_CMD);
|
|
if (rr == null) {
|
|
ok = false;
|
|
logw("startFmc: rr=NULL.");
|
|
onFmcStatus(FmcNotifier.FMC_STATUS_FAILURE);
|
|
} else {
|
|
WifiInfo wifiInfo = mWifiManager.getConnectionInfo();
|
|
String ssid = wifiInfo.getSSID();
|
|
|
|
rr.mp.writeString(ssid);
|
|
send(rr);
|
|
}
|
|
}
|
|
return ok;
|
|
}
|
|
|
|
/** {@hide} */
|
|
public boolean stopFmc(IBinder binder) {
|
|
boolean ok = true;
|
|
FmcRegInfo rInfo = getFmcObj();
|
|
dlogi( "stopFmc");
|
|
if (rInfo != null) {
|
|
rInfo.actionStop = true;
|
|
onFmcStatus(FmcNotifier.FMC_STATUS_SHUTTING_DOWN);
|
|
// Send stopFMC to cnd
|
|
CNERequest rr = CNERequest.obtain(CNE_REQUEST_STOP_FMC_CMD);
|
|
if (rr == null) {
|
|
logw("stopFmc: rr=NULL.");
|
|
ok = false;
|
|
} else {
|
|
send(rr);
|
|
}
|
|
} else {
|
|
onFmcStatus(FmcNotifier.FMC_STATUS_NOT_YET_STARTED);
|
|
}
|
|
return ok;
|
|
}
|
|
|
|
/** {@hide} */
|
|
public boolean getFmcStatus(IBinder binder) {
|
|
boolean ok = false;
|
|
FmcRegInfo rInfo = getFmcObj();
|
|
dlogi( "getFmcStatus: ");
|
|
if (rInfo != null) {
|
|
rInfo = reestablishBinder(rInfo, binder);
|
|
if (rInfo.lastSendStatus != -1) {
|
|
onFmcStatus(rInfo.lastSendStatus); // re-send last status
|
|
ok = true;
|
|
}
|
|
}
|
|
return ok;
|
|
}
|
|
|
|
/* LinkSocket API Implementations */
|
|
|
|
/**
|
|
* Translate a LinkSocket requestLink to a CNE getLink
|
|
* @param cap
|
|
* @param binder
|
|
* @return socket id
|
|
*/
|
|
public int requestLink(LinkCapabilities cap, IBinder binder) {
|
|
ILinkSocketMessageHandler callback = ILinkSocketMessageHandler.Stub.asInterface(binder);
|
|
|
|
// translating role strings to integers
|
|
// hard coded until we figure out what the new XML file is going to look like in QCNE
|
|
int roleInt = 0;
|
|
String roleString = cap.get(LinkCapabilities.Key.RW_ROLE);
|
|
dlogv( "Converting Role: " + roleString);
|
|
if (roleString == null) roleInt = 0;
|
|
else if (roleString.equals(LinkCapabilities.Role.DEFAULT)) roleInt = 0;
|
|
else if (roleString.equals(LinkCapabilities.Role.VIDEO_STREAMING_1080P)) roleInt = 1;
|
|
else if (roleString.equals(LinkCapabilities.Role.WEB_BROWSER)) roleInt = 2;
|
|
//QOS roles start upwards of 1000; hardcoding until policy file includes
|
|
//QOS params.
|
|
else if (roleString.equals(LinkCapabilities.Role.QOS_VIDEO_TELEPHONY)) roleInt = 1001;
|
|
else logd(roleString + " is not a known role.");
|
|
|
|
// translate LinkCapabilities into a HashMap
|
|
dlogv( "Converting Bandwidth Req's");
|
|
Map<LinkRequirement, String> reqs = new HashMap<LinkRequirement, String>();
|
|
//FIXME pass all requirements to QCNE when it understands QOS roles.
|
|
//For now, do not pass bandwidth requirements of a QoS role to CnE
|
|
if (roleInt < QOS_ROLE_MIN || roleInt > QOS_ROLE_MAX) {
|
|
reqs.put(LinkRequirement.FW_LINK_BW,
|
|
cap.get(LinkCapabilities.Key.RW_REQUIRED_FWD_BW) + "kbps");
|
|
reqs.put(LinkRequirement.REV_LINK_BW,
|
|
cap.get(LinkCapabilities.Key.RW_REQUIRED_REV_BW) + "kbps");
|
|
}
|
|
|
|
// generate a socket id, use it in place of a PID
|
|
dlogv( "Generating a SocketID");
|
|
int id = getNextSocketId();
|
|
|
|
// maintain every socket's needs. Do this for QoS and non-QoS sockets
|
|
ExtraLinkCapabilities mcap = new ExtraLinkCapabilities();
|
|
// initialize QoS state for all sockets
|
|
mcap.put(LinkCapabilities.Key.RO_QOS_STATE, "inactive");
|
|
dlogv("Populating capabilities locally for: " + cap);
|
|
for(Entry<Integer, String> need : cap.entrySet()) {
|
|
if (need == null) continue;
|
|
mcap.put(need.getKey(), need.getValue());
|
|
}
|
|
dlogv("Populated capabilities locally as: " + mcap);
|
|
// Synchronous access to activeCapabilities as they are updated by
|
|
// multiple threads
|
|
synchronized(activeCapabilities) {
|
|
if (activeCapabilities.containsKey(id)) {
|
|
logw("discarding needs for duplicate socket id");
|
|
} else {
|
|
activeCapabilities.put(id, mcap);
|
|
}
|
|
}
|
|
if (roleInt >= QOS_ROLE_MIN && roleInt <= QOS_ROLE_MAX) {
|
|
if (findQosBySocketId(id) == null) {
|
|
synchronized(mQosTrackers) {
|
|
try {
|
|
if (!mQosTrackers.add(new QoSTracker(id, callback, mcap)))
|
|
dlogd("failed to track qos request for socket: " + id);
|
|
} catch (Exception e) {
|
|
dlogd("error while tracking qos request from the socket:" + id + " " + e);
|
|
}
|
|
}
|
|
} else {
|
|
dlogd("ignoring duplicate request link of qos role for socket: " + id);
|
|
}
|
|
}
|
|
// translate the call
|
|
dlogv( "Making the call to getLink");
|
|
this.getLink_LP(roleInt, reqs, id, new CneCallbackAdapter(id, callback));
|
|
|
|
return id;
|
|
}
|
|
|
|
/**
|
|
* Translate a LinkSocket releaseLink to a CNE releaseLink
|
|
* @param id
|
|
*/
|
|
public void releaseLink(int id) {
|
|
//socket does not use mapping from a role and pid to regid.
|
|
//setting role to -1 always.
|
|
this.releaseLink_LP(-1, id);
|
|
}
|
|
|
|
/** @hide
|
|
* Minimum available forward link (download) bandwidth for the socket.
|
|
* This value is in kilobits per second (kbps).
|
|
*/
|
|
public String getMinAvailableForwardBandwidth(int id) {
|
|
QoSTracker qt = findQosBySocketId(id);
|
|
LinkCapabilities cap = (qt == null) ? getSocketXCapabilities(id) : qt.getQosCapabilities();
|
|
if (cap == null) return null;
|
|
dlogd("getMinAvailableForwardBandiwdth ("+id+")");
|
|
return cap.get(LinkCapabilities.Key.RO_MIN_AVAILABLE_FWD_BW);
|
|
}
|
|
|
|
/** @hide
|
|
* Maximum available forward link (download) bandwidth for the socket.
|
|
* This value is in kilobits per second (kbps).
|
|
*/
|
|
public String getMaxAvailableForwardBandwidth(int id) {
|
|
QoSTracker qt = findQosBySocketId(id);
|
|
LinkCapabilities cap = (qt == null) ? getSocketXCapabilities(id) : qt.getQosCapabilities();
|
|
if (cap == null) return null;
|
|
dlogd("getMaxAvailableForwardBandiwdth ("+id+")");
|
|
return cap.get(LinkCapabilities.Key.RO_MAX_AVAILABLE_FWD_BW);
|
|
}
|
|
|
|
/** @hide
|
|
* Minimum available reverse link (upload) bandwidth for the socket.
|
|
* This value is in kilobits per second (kbps).
|
|
*/
|
|
public String getMinAvailableReverseBandwidth(int id) {
|
|
QoSTracker qt = findQosBySocketId(id);
|
|
LinkCapabilities cap = (qt == null) ? getSocketXCapabilities(id) : qt.getQosCapabilities();
|
|
if (cap == null) return null;
|
|
dlogd("getMinAvailableReverseBandiwdth ("+id+")");
|
|
return cap.get(LinkCapabilities.Key.RO_MIN_AVAILABLE_REV_BW);
|
|
}
|
|
|
|
/** @hide
|
|
* Maximum available reverse link (upload) bandwidth for the socket.
|
|
* This value is in kilobits per second (kbps).
|
|
*/
|
|
public String getMaxAvailableReverseBandwidth(int id) {
|
|
QoSTracker qt = findQosBySocketId(id);
|
|
LinkCapabilities cap = (qt == null) ? getSocketXCapabilities(id) : qt.getQosCapabilities();
|
|
if (cap == null) return null;
|
|
dlogd("getMaxAvailableReverseBandiwdth ("+id+")");
|
|
return cap.get(LinkCapabilities.Key.RO_MAX_AVAILABLE_REV_BW);
|
|
}
|
|
|
|
/** @hide
|
|
* Current estimated downlink latency of the socket, in milliseconds.
|
|
*/
|
|
public String getCurrentFwdLatency(int id) {
|
|
QoSTracker qt = findQosBySocketId(id);
|
|
LinkCapabilities cap = (qt == null) ? getSocketXCapabilities(id) : qt.getQosCapabilities();
|
|
if (cap == null) return null;
|
|
dlogd("getCurrentForwardLatency ("+id+")");
|
|
return cap.get(LinkCapabilities.Key.RO_CURRENT_FWD_LATENCY);
|
|
}
|
|
|
|
/** @hide
|
|
* Current estimated uplink latency of the socket, in milliseconds.
|
|
*/
|
|
public String getCurrentRevLatency(int id) {
|
|
QoSTracker qt = findQosBySocketId(id);
|
|
LinkCapabilities cap = (qt == null) ? getSocketXCapabilities(id) : qt.getQosCapabilities();
|
|
if (cap == null) return null;
|
|
dlogd("getCurrentReverseLatency ("+id+")");
|
|
return cap.get(LinkCapabilities.Key.RO_CURRENT_REV_LATENCY);
|
|
}
|
|
|
|
/** @hide
|
|
* Current state for Quality of Service for this socket
|
|
*/
|
|
public String getQosState(int id) {
|
|
QoSTracker qt = findQosBySocketId(id);
|
|
LinkCapabilities cap = (qt == null) ? getSocketXCapabilities(id) : qt.getQosCapabilities();
|
|
if (cap == null) return null;
|
|
dlogd("getQosState ("+id+")");
|
|
return cap.get(LinkCapabilities.Key.RO_QOS_STATE);
|
|
}
|
|
|
|
/** @hide
|
|
* An integer representing the network type.
|
|
* @see ConnectivityManager
|
|
*/
|
|
public int getNetworkType(int id) {
|
|
return -1;
|
|
}
|
|
|
|
/** @hide
|
|
* start qos transaction request for the specified socket id and port
|
|
*/
|
|
public boolean requestQoS(int id, int aport, String localAddress) {
|
|
QoSTracker qt = findQosBySocketId(id);
|
|
LinkCapabilities cap = getSocketXCapabilities(id);
|
|
if (qt == null || cap == null) return false;
|
|
QosSpec mySpec = prepareQosSpec(aport, localAddress, cap);
|
|
if (mySpec == null) {
|
|
dlogw("requestQos failed to prepare qos spec for socket: " + id);
|
|
return false;
|
|
}
|
|
qt.startQosTransaction(mySpec);
|
|
return true;
|
|
}
|
|
|
|
/** @hide
|
|
* Adapter class to translates CNE callbacks to LinkSocket callbacks.
|
|
*/
|
|
public class CneCallbackAdapter extends IConSvcEventListener.Stub {
|
|
|
|
protected int mId = 0;
|
|
protected ILinkSocketMessageHandler mLsmh;
|
|
|
|
public CneCallbackAdapter(int id, ILinkSocketMessageHandler lsmh) {
|
|
mId = id;
|
|
mLsmh = lsmh;
|
|
}
|
|
|
|
/**
|
|
* Translate a CNE onLinkAvail to a LinkSocket onLinkAvail.
|
|
*
|
|
* A reportLinkSatisfaction event is sent as soon as an
|
|
* onLinkAvail callback is received because LinkSocket just
|
|
* assumes that the application is satisfied.
|
|
*/
|
|
public void onLinkAvail(LinkInfo info) throws RemoteException {
|
|
android.util.Log.v("CneCallbackAdapter", "onLinkAvail EX");
|
|
reportLinkSatisfaction_LP(0, mId, info, true, true);
|
|
LinkProperties prop = new LinkProperties();
|
|
prop.addAddress(info.getIpAddr());
|
|
mLsmh.onLinkAvail(prop);
|
|
}
|
|
|
|
/**
|
|
* Translate a CNE onBetterLinkAvail to a LinkSocket onBetterLinkAvail.
|
|
*
|
|
* A rejectSwitch is sent as soon as a onBetterLinkAvail is received
|
|
* because LinkSocket doesn't have the same concept of a link switch
|
|
* as CNE.
|
|
*/
|
|
public void onBetterLinkAvail(LinkInfo info) throws RemoteException {
|
|
android.util.Log.v("CneCallbackAdapter", "onBetterLinkAvail EX");
|
|
rejectSwitch_LP(0, mId, info, true);
|
|
mLsmh.onBetterLinkAvail();
|
|
}
|
|
|
|
/**
|
|
* Translate a CNE onLinkLost to a LinkSocket onLinkLost.
|
|
*/
|
|
public void onLinkLost(LinkInfo info) throws RemoteException {
|
|
android.util.Log.v("CneCallbackAdapter", "onLinkLost EX");
|
|
mLsmh.onLinkLost();
|
|
}
|
|
|
|
/**
|
|
* Translate a CNE onGetLinkFailure to a LinkSocket onGetLinkFailure.
|
|
*/
|
|
public void onGetLinkFailure(int reason) throws RemoteException {
|
|
android.util.Log.v("CneCallbackAdapter", "onGetLinkFailure EX");
|
|
mLsmh.onGetLinkFailure(reason);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* QOS
|
|
*/
|
|
// Creates QosSpec for a give role type
|
|
private QosSpec prepareQosSpec(int localPort, String localAddress, LinkCapabilities myCap) {
|
|
/*
|
|
* prepare QoSSpec as per the role defined.
|
|
* TODO retrieve flow information from the carrier policy xml file
|
|
* based on the role type.
|
|
* TODO retrieve technology type from CNE and use applicable 3GPP
|
|
* or 3GPP2 params from the config file.
|
|
|
|
* FIXME For now we are hardcoding this flow information based on
|
|
* videoe_telephony role type. for qos_video_telephony role on a
|
|
* 3GPP radio there are two flows, incoming and outgoing with both
|
|
* of their classification as STREAMING. Latency and bit rates
|
|
* will be provided by the application via link capabilities.
|
|
*/
|
|
// Some parameters in the qos spec will always be the same no matter what,
|
|
// declare them here
|
|
final String TOS_MASK = "255";
|
|
final String SRC_PORT_RANGE = "0";
|
|
final String defaultLatency = "100";
|
|
final String maxFwdBw = "16000000";
|
|
final String maxRevBw = "5000000";
|
|
final String minBw = "64000";
|
|
|
|
dlogd("preparing qosspec");
|
|
|
|
QosSpec mQosSpec = new QosSpec();
|
|
//for umts, create two flows per spec, viz tx and rx.
|
|
QosSpec.QosPipe txPipe = mQosSpec.createPipe();
|
|
QosSpec.QosPipe rxPipe = mQosSpec.createPipe();
|
|
txPipe.put(QosSpec.QosSpecKey.SPEC_INDEX, "0");
|
|
rxPipe.put(QosSpec.QosSpecKey.SPEC_INDEX, "0");
|
|
txPipe.put(QosSpec.QosSpecKey.FLOW_DIRECTION,
|
|
Integer.toString(QosSpec.QosDirection.QOS_TX));
|
|
rxPipe.put(QosSpec.QosSpecKey.FLOW_DIRECTION,
|
|
Integer.toString(QosSpec.QosDirection.QOS_RX));
|
|
txPipe.put(QosSpec.QosSpecKey.FLOW_TRAFFIC_CLASS,
|
|
Integer.toString(QosSpec.QosClass.STREAMING));
|
|
rxPipe.put(QosSpec.QosSpecKey.FLOW_TRAFFIC_CLASS,
|
|
Integer.toString(QosSpec.QosClass.STREAMING));
|
|
|
|
/*
|
|
* lookup needs and translate to QosSpec
|
|
* The Flow and filter information is translated from capabilities
|
|
* to QoS Spec as follows:
|
|
* ------------------------------------------------------------------------------------
|
|
* Capabilities key QosSpec direction and key combo
|
|
* ------------------------------------------------------------------------------------
|
|
* RW_DESIRED_FWD_BW ==> QOS_RX, FLOW_DATA_RATE_MAX
|
|
* RW_REQUIRED_FWD_BW ==> QOS_RX, FLOW_DATA_RATE_MIN
|
|
* RW_DESIRED_REV_BW ==> QOS_TX, FLOW_DATA_RATE_MAX
|
|
* RW_REQUIRED_REV_BW ==> QOS_TX, FLOW_DATA_RATE_MIN
|
|
* RW_MAX_ALLOWED_FWD_LATENCY ==> QOS_RX, FLOW_LATENCY
|
|
* RW_MAX_ALLOWED_REV_LATENCY ==> QOS_TX, FLOW_LATENCY
|
|
* RO_TRANSPORT_PROTO_TYPE,
|
|
* RW_DEST_PORTS ==> QOS_TX,
|
|
* FILTER_[PROTO]_DESTINATION_PORT_START,
|
|
* FILTER_[PROTO]_DESTINATION_PORT_RANGE
|
|
* localPort (local) ==> FILTER_[PROTO]_SOURCE_PORT_START,
|
|
* SOURCE_PORT_RANGE(local) ==> FILTER_[PROTO]_SOURCE_PORT_RANGE
|
|
*
|
|
* RW_DEST_IP_ADDRESSES ==> QOS_TX, FILTER_IPV4_DESTINATION_ADDR
|
|
* localAddress (local) ==> QOS_TX, FILTER_IPV4_SOURCE_ADDR,
|
|
* ==> QOS_RX, FILTER_IPV4_DESTINATION_ADDR
|
|
* RW_FILTERSPEC_IP_TOS ==> QOS_TX, FILTER_IPV4_TOS
|
|
* TOS_MASK (local) ==> QOS_TX, FILTER_IPV4_TOS_MASK
|
|
* ------------------------------------------------------------------------------------
|
|
*
|
|
* *******************************************************************
|
|
* IMPORTANT: if the capability is not defined then do not populate
|
|
* the spec with null values, the modem will reject it.
|
|
* *******************************************************************
|
|
*/
|
|
if (myCap == null) {
|
|
dlogw("prepareQosSpec failed because needs is null");
|
|
return null;
|
|
}
|
|
|
|
String temp = null;
|
|
// apply flow spec, convert unit for capabilities kbps to qosspec bps
|
|
if ((temp = myCap.get(LinkCapabilities.Key.RW_DESIRED_REV_BW)) != null) {
|
|
txPipe.put(QosSpec.QosSpecKey.FLOW_DATA_RATE_MAX, temp);
|
|
} else {
|
|
txPipe.put(QosSpec.QosSpecKey.FLOW_DATA_RATE_MAX, maxRevBw);
|
|
}
|
|
if ((temp = myCap.get(LinkCapabilities.Key.RW_REQUIRED_REV_BW)) != null) {
|
|
txPipe.put(QosSpec.QosSpecKey.FLOW_DATA_RATE_MIN, temp);
|
|
} else {
|
|
txPipe.put(QosSpec.QosSpecKey.FLOW_DATA_RATE_MIN, minBw);
|
|
}
|
|
if ((temp = myCap.get(LinkCapabilities.Key.RW_MAX_ALLOWED_REV_LATENCY)) != null) {
|
|
txPipe.put(QosSpec.QosSpecKey.FLOW_LATENCY, temp);
|
|
} else {
|
|
txPipe.put(QosSpec.QosSpecKey.FLOW_LATENCY, defaultLatency);
|
|
}
|
|
if ((temp = myCap.get(LinkCapabilities.Key.RW_DESIRED_FWD_BW)) != null) {
|
|
rxPipe.put(QosSpec.QosSpecKey.FLOW_DATA_RATE_MAX, temp);
|
|
} else {
|
|
rxPipe.put(QosSpec.QosSpecKey.FLOW_DATA_RATE_MAX, maxFwdBw);
|
|
}
|
|
if ((temp = myCap.get(LinkCapabilities.Key.RW_REQUIRED_FWD_BW)) != null) {
|
|
rxPipe.put(QosSpec.QosSpecKey.FLOW_DATA_RATE_MIN, temp);
|
|
} else {
|
|
rxPipe.put(QosSpec.QosSpecKey.FLOW_DATA_RATE_MIN, minBw);
|
|
}
|
|
if ((temp = myCap.get(LinkCapabilities.Key.RW_MAX_ALLOWED_FWD_LATENCY)) != null) {
|
|
rxPipe.put(QosSpec.QosSpecKey.FLOW_LATENCY, temp);
|
|
} else {
|
|
rxPipe.put(QosSpec.QosSpecKey.FLOW_LATENCY, defaultLatency);
|
|
}
|
|
|
|
//create transport specific port keys, and use them to apply
|
|
//the filter spec for ports
|
|
int srcPortStartKey, srcPortRangeKey;
|
|
int dstPortStartKey, dstPortRangeKey, dstPortStartVal, dstPortRangeVal;
|
|
|
|
if ((temp = myCap.get(LinkCapabilities.Key.RO_TRANSPORT_PROTO_TYPE)) == null) {
|
|
logw("prepareQosSpec failed - transport protocol is not set");
|
|
return null;
|
|
}
|
|
if (temp.equals("udp")) {
|
|
srcPortStartKey = QosSpec.QosSpecKey.FILTER_UDP_SOURCE_PORT_START;
|
|
srcPortRangeKey = QosSpec.QosSpecKey.FILTER_UDP_SOURCE_PORT_RANGE;
|
|
dstPortStartKey = QosSpec.QosSpecKey.FILTER_UDP_DESTINATION_PORT_START;
|
|
dstPortRangeKey = QosSpec.QosSpecKey.FILTER_UDP_DESTINATION_PORT_RANGE;
|
|
} else if (temp.equals("tcp")) {
|
|
srcPortStartKey = QosSpec.QosSpecKey.FILTER_TCP_SOURCE_PORT_START;
|
|
srcPortRangeKey = QosSpec.QosSpecKey.FILTER_TCP_SOURCE_PORT_RANGE;
|
|
dstPortStartKey = QosSpec.QosSpecKey.FILTER_TCP_DESTINATION_PORT_START;
|
|
dstPortRangeKey = QosSpec.QosSpecKey.FILTER_TCP_DESTINATION_PORT_RANGE;
|
|
} else {
|
|
logw("prepareQosSpec failed - unrecognized transport: " + temp);
|
|
return null;
|
|
}
|
|
|
|
//Passed by linkSocket after it is bound to a suitable port.
|
|
//dlogi("using localport for source filter: " + localPort);
|
|
//txPipe.put(srcPortStartKey, Integer.toString(localPort)); // disable for trials
|
|
//txPipe.put(srcPortRangeKey, SRC_PORT_RANGE);
|
|
if ((temp = myCap.get(LinkCapabilities.Key.RW_DEST_PORTS)) != null) {
|
|
//validate if destination port is formatted correctly.
|
|
if (temp.contains("-")) {
|
|
String []tok = temp.split("-");
|
|
if ((tok.length != 2) ||
|
|
((dstPortStartVal = Integer.parseInt(tok[0])) >
|
|
(dstPortRangeVal = Integer.parseInt(tok[1])))) {
|
|
dlogw("startQos failed due to invalid dst port format: "
|
|
+ temp);
|
|
return null;
|
|
}
|
|
dstPortRangeVal = dstPortRangeVal - dstPortStartVal;
|
|
} else {
|
|
dstPortStartVal = Integer.parseInt(temp);
|
|
dstPortRangeVal = 0;
|
|
}
|
|
txPipe.put(dstPortStartKey, Integer.toString(dstPortStartVal));
|
|
txPipe.put(dstPortRangeKey, Integer.toString(dstPortRangeVal));
|
|
rxPipe.put(srcPortStartKey, Integer.toString(dstPortStartVal));
|
|
rxPipe.put(srcPortRangeKey, Integer.toString(dstPortRangeVal));
|
|
}
|
|
// apply local address to the filter spec as source for outgoing
|
|
//txPipe.put(QosSpec.QosSpecKey.FILTER_IPV4_SOURCE_ADDR, localAddress); //not allowed in rel 8
|
|
// apply local port to the filter spec as dest port for incoming flow
|
|
//rxPipe.put(dstPortStartKey, Integer.toString(localPort)); // disable for trials
|
|
//rxPipe.put(dstPortRangeKey, SRC_PORT_RANGE);
|
|
// apply local address to the filter spec as destination for the incoming flow
|
|
//rxPipe.put(QosSpec.QosSpecKey.FILTER_IPV4_DESTINATION_ADDR, localAddress); //not allowed in rel 8
|
|
// apply destination ip filter spec as dst for tx and src for rx flow
|
|
if ((temp = myCap.get(LinkCapabilities.Key.RW_DEST_IP_ADDRESSES)) != null) {
|
|
txPipe.put(QosSpec.QosSpecKey.FILTER_IPV4_DESTINATION_ADDR, temp);
|
|
rxPipe.put(QosSpec.QosSpecKey.FILTER_IPV4_SOURCE_ADDR, temp);
|
|
}
|
|
// apply TOS
|
|
if ((temp = myCap.get(LinkCapabilities.Key.RW_FILTERSPEC_IP_TOS)) != null) {
|
|
txPipe.put(QosSpec.QosSpecKey.FILTER_IPV4_TOS, temp);
|
|
txPipe.put(QosSpec.QosSpecKey.FILTER_IPV4_TOS_MASK, TOS_MASK);
|
|
}
|
|
dlogi("prepared qos spec: " + mQosSpec);
|
|
return mQosSpec;
|
|
}
|
|
|
|
private QoSTracker findQosBySocketId(int id) {
|
|
QoSTracker tracker = null;
|
|
synchronized(mQosTrackers) {
|
|
for (QoSTracker qt : mQosTrackers) {
|
|
if (qt == null) continue;
|
|
if (id == qt.getSocketId()) {
|
|
tracker = qt;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return tracker;
|
|
}
|
|
|
|
private QoSTracker findQosByQosId(int id) {
|
|
QoSTracker tracker = null;
|
|
synchronized(mQosTrackers) {
|
|
for (QoSTracker qt : mQosTrackers) {
|
|
if (qt == null) continue;
|
|
if (id == qt.getQosId()) {
|
|
tracker = qt;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return tracker;
|
|
}
|
|
|
|
private ExtraLinkCapabilities getSocketXCapabilities(int id) {
|
|
return activeCapabilities.get(id);
|
|
}
|
|
|
|
private static void logd (String s) {
|
|
Log.d(LOG_TAG,s);
|
|
}
|
|
private static void loge (String s) {
|
|
Log.e(LOG_TAG,s);
|
|
}
|
|
private static void logw (String s) {
|
|
Log.w(LOG_TAG,s);
|
|
}
|
|
private static void logv (String s) {
|
|
Log.v(LOG_TAG,s);
|
|
}
|
|
private static void logi (String s) {
|
|
Log.i(LOG_TAG,s);
|
|
}
|
|
|
|
private static void dlogd (String s) {
|
|
if (DBG) Log.d(LOCAL_TAG,s);
|
|
}
|
|
private static void dloge (String s) {
|
|
if (DBG) Log.e(LOCAL_TAG,s);
|
|
}
|
|
private static void dlogw (String s) {
|
|
if (DBG) Log.w(LOCAL_TAG,s);
|
|
}
|
|
private static void dlogv (String s) {
|
|
if (DBG) Log.v(LOCAL_TAG,s);
|
|
}
|
|
private static void dlogi (String s) {
|
|
if (DBG) Log.i(LOCAL_TAG,s);
|
|
}
|
|
}
|