/**
* This file is public domain see
* http://code.google.com/p/codenameone/issues/detail?id=148
* http://supportforums.blackberry.com/t5/Java-Development/Sample-HTTP-Connection-code-and-BIS-B-Access/td-p/653175
* Authored and contributed by Peter Strange
*/
package com.codename1.impl.blackberry;
import net.rim.device.api.servicebook.ServiceBook;
import net.rim.device.api.servicebook.ServiceRecord;
import net.rim.device.api.synchronization.ConverterUtilities;
import net.rim.device.api.system.CoverageInfo;
import net.rim.device.api.system.RadioInfo;
import net.rim.device.api.util.DataBuffer;
import net.rim.device.api.util.TLEUtilities;
public class TransportDetective {
/**
* CoverageInfo.COVERAGE_DIRECT is only available in 4.5 or newer
*/
private static final int COVERAGE_DIRECT = 1;
// ServiceRecord for detected transports in the service book
ServiceRecord srTcpCellularWithApn, srWap, srWap2, srMds, srBis, srTcpWiFi;
// Transport type constants
public static final int TRANSPORT_TCP_CELLULAR = 1; // Represents TCP Cellular transport also known as Direct TCP
public static final int TRANSPORT_WAP = 2; // Represents the Wap 1.0 and Wap 1.1 transport types
public static final int TRANSPORT_WAP2 = 4; // Represents the Wap 2.0 transport type
public static final int TRANSPORT_MDS = 8; // Represents the MDS transport type
public static final int TRANSPORT_BIS_B = 16; // Represents the Blackberry Internet Service transport type
public static final int TRANSPORT_TCP_WIFI = 32; // Represents the WIFI transport type
public static final int TCP_CELLULAR_APN_SERVICE_BOOK = 64; // Represents the availability of TCP_CELLULAR service book that has APN information pre-populated. Please note that this is not a new transport. This simply indicates that your application do not need to know the APN information for TCP_CELLULAR since the device already knows it. Please see URLFactory.getHttpTcpCellularUrlUsingServiceRecord(ServiceRecord tcpServiceRecord)
// Following descriptins added so that options can be displayed to users
public static String TRANSPORT_TCP_CELLULAR_DESCRIPTION = "Carrier";
public static String TRANSPORT_WAP_DESCRIPTION = "Wap 1";
public static String TRANSPORT_WAP2_DESCRIPTION = "Wap 2";
public static String TRANSPORT_MDS_DESCRIPTION = "BES/MDS";
public static String TRANSPORT_BIS_B_DESCRIPTION = "BIS-B";
public static String TRANSPORT_TCP_WIFI_DESCRIPTION = "WiFi";
public static String TRANSPORT_TCP_CELLULAR_APN_DESCRIPTION = "Carrier (Preconfigured)";
// Constants used for TRANSPORT_BIS_B and TRANSPORT_MDS type detection
private static final byte SERVICE_RECORD_TYPE_IPPP_TAG = 0x06;
private static final byte SERVICE_RECORD_TYPE_IPPP_CORPORATE = 0x00;
private static final byte SERVICE_RECORD_TYPE_IPPP_PUBLIC = 0x01;
private static final byte SERVICE_RECORD_TYPE_IPPP_PROVISIONING = 0x02;
// Constants used for TRANSPORT_TCP_WIFI and TRANSPORT_WAP2 type detection
private static final byte ENCODED_TYPE_INTERFACE = 0x13;
private static final byte ENCODED_TYPE_MMSC_URL = 0x0D;
private static final byte ENCODED_TYPE_HTTP_PROXY_ADDRESS = 0x01;
private static final byte ENCODED_TYPE_PROXY_AUTH_USERNAME_TYPE = 0x09;
private static final byte AUTH_TYPE_BBAUTH_TOKEN_NEGOTIATION = 0x1C;
// Interface name string of TCP WiFi transport service book.
private static final String SERVICE_RECORD_TYPE_WPTCP_INTERFACE_WIFI = "wifi";
// Interface name string of TCP Cellular transport service book. Commented because it is not used at present
//private static final String SERVICE_RECORD_TYPE_WPTCP_INTERFACE_CELLULAR = "cellular";
// Constant used for TRANSPORT_WAP type detection
private static final byte SERVICE_RECORD_TYPE_WAP_MMSC_URL_TAG = 0x09;
// Mask representing available transport service books
private int _availableTransportServices = 0;
// Mask representing available transport coverage
private int _availableTransportCoverage = 0;
private ServiceBook _serviceBook;
public TransportDetective(){
_serviceBook = ServiceBook.getSB();
}
/**
* Determines if a specific transport is available based on service book availability. In other words this method will return true only
* for those available transports for which there is a service book present on the device. This does not guarantee connection capability.
* You also need sufficient coverage for a transport to work.
* @param transport One of TRANSPORT_* constants.
* @return True if available. Otherwise false.
*/
public synchronized boolean isTransportServiceAvailable(int transport){
int available = getAvailableTransportServices();
if((available & transport) > 0)
return true;
else
return false;
}
/**
* Determines if a specific transport is available based on connectivity and service availability. In other words this method will return true only
* for those available transports for which there is sufficient coverage on the device and there is a service book present. Although having a transport
* in coverage means that we can attempt to create a connection over this transport, connections can still fail due to other network factors (e.g. server
* outage)
* @param transport One of TRANSPORT_* constants.
* @return True if available. Otherwise false.
*/
public synchronized boolean isCoverageAvailable(int transport){
int available = getAvailableTransportCoverage();
if((available & transport) > 0)
return true;
else
return false;
}
/**
* Gets available transports for which a service book is present.
* @return Available transports based on service book.
*/
public synchronized int getAvailableTransportServices(){
updateTransportServiceAvailability();
return _availableTransportServices;
}
/**
* Gets available transports for which coverage is present.
* @return Available transports based on coverage.
*/
public synchronized int getAvailableTransportCoverage(){
updateTransportCoverageAvailability();
return _availableTransportCoverage;
}
/**
* Determines if service record for TCP Cellular APN is present. If yes connections via TCP_CELLULAR can be attempted without knowing
* the APN information for carriers.
* @return true, if present. false otherwise.
*/
public boolean isTcpCellularApnServiceRecordAvailable(){
updateTransportServiceAvailability();
return ((_availableTransportServices & TCP_CELLULAR_APN_SERVICE_BOOK) > 0);
}
/** Getters for ServiceRecord of each transport */
public ServiceRecord getSrWap() {
return srWap;
}
public ServiceRecord getSrWap2() {
return srWap2;
}
public ServiceRecord getSrMds() {
return srMds;
}
public ServiceRecord getSrBis() {
return srBis;
}
public ServiceRecord getSrTcpWiFi() {
return srTcpWiFi;
}
public ServiceRecord getSrTcpCellularWithApn() {
return srTcpCellularWithApn;
}
/**
* Updates available transport service availability.
*/
private void updateTransportServiceAvailability() {
// reset cached availability
_availableTransportServices = 0;
ServiceRecord[] records = _serviceBook.getRecords();
boolean cellularDataServiceSupported = isCellularDataServiceSupported();
if ( cellularDataServiceSupported ) {
_availableTransportServices |= TRANSPORT_TCP_CELLULAR;
}
boolean wifiSupported = isWifiSupported();
for( int i = 0; i < records.length; i++ ) {
ServiceRecord serviceRecord = records[ i ];
if( serviceRecord.isValid() && ( serviceRecord.getType() == ServiceRecord.SRT_ACTIVE ) ) {
int transportType = determineTransportType( serviceRecord );
if (( transportType != -1 ) ) {
if ( (( transportType == TRANSPORT_MDS || ( transportType & (TRANSPORT_BIS_B)) > 0 )
&& ( cellularDataServiceSupported || wifiSupported ))
|| (( transportType == TRANSPORT_WAP || transportType == TRANSPORT_WAP2 )
&& ( cellularDataServiceSupported ))
|| (( transportType == TRANSPORT_TCP_WIFI )
&& ( wifiSupported ))
|| (( transportType == TCP_CELLULAR_APN_SERVICE_BOOK)
&& ( cellularDataServiceSupported )) ) {
_availableTransportServices |= transportType ;
}
}
}
}
}
/**
* Updates transport coverage availability.
*/
private void updateTransportCoverageAvailability() {
// reset cached availability
// In 3G network it works...
_availableTransportCoverage = 0;
// Required for 2G networks
int _coverageStatus = CoverageInfo.getCoverageStatus();
if ((_coverageStatus & COVERAGE_DIRECT) > 0
|| CoverageInfo.isCoverageSufficient(COVERAGE_DIRECT, RadioInfo.getSupportedWAFs(), false)) {
_availableTransportCoverage |= TRANSPORT_TCP_CELLULAR;
_availableTransportCoverage |= TRANSPORT_WAP2;
_availableTransportCoverage |= TRANSPORT_WAP;
}
if ((_coverageStatus & CoverageInfo.COVERAGE_BIS_B) > 0
|| CoverageInfo.isCoverageSufficient(CoverageInfo.COVERAGE_BIS_B)
|| CoverageInfo.isCoverageSufficient(CoverageInfo.COVERAGE_BIS_B, RadioInfo.getSupportedWAFs(), false)) {
_availableTransportCoverage |= TRANSPORT_BIS_B;
}
if ((_coverageStatus & CoverageInfo.COVERAGE_MDS) > 0
|| CoverageInfo.isCoverageSufficient(CoverageInfo.COVERAGE_MDS)
|| CoverageInfo.isCoverageSufficient(CoverageInfo.COVERAGE_MDS, RadioInfo.getSupportedWAFs(), false)) {
_availableTransportCoverage |= TRANSPORT_MDS;
}
if (CoverageInfo.isCoverageSufficient(COVERAGE_DIRECT, RadioInfo.WAF_WLAN, false)) {
_availableTransportCoverage |= TRANSPORT_TCP_WIFI;
}
updateTransportServiceAvailability();
_availableTransportCoverage &= _availableTransportServices;
}
/**
* Determines if Data Service is enabled via Cellular radio.
*
* @return <b>true</b> if enabled. <b>false</b> otherwise.
*/
public boolean isCellularDataServiceSupported() {
return ((RadioInfo.getSupportedWAFs() & (RadioInfo.WAF_3GPP | RadioInfo.WAF_CDMA | RadioInfo.WAF_IDEN)) > 0);
}
/**
* Determines if WiFi is supported.
* @return <b>true</b> if enabled. <b>false</b> otherwise.
*/
private boolean isWifiSupported() {
return ( (RadioInfo.getSupportedWAFs() & RadioInfo.WAF_WLAN) > 0 );
}
/**
* Given a service record, determines the transport it represents.
* @param serviceRecord A service record.
* @return One of TRANSPORT_* constants.
*/
private int determineTransportType( ServiceRecord serviceRecord ) {
String cid = serviceRecord.getCid();
// MDS and BIS
if( "ippp".equalsIgnoreCase( cid )) {
int ippType = getEncodedIntFromIPPPServiceRecord( serviceRecord, SERVICE_RECORD_TYPE_IPPP_TAG );
if ( ippType == SERVICE_RECORD_TYPE_IPPP_PUBLIC ) {
int result = 0;
srBis = serviceRecord;
result |= TRANSPORT_BIS_B;
return result;
} else if (( ippType == SERVICE_RECORD_TYPE_IPPP_CORPORATE )
|| ( ippType != SERVICE_RECORD_TYPE_IPPP_PROVISIONING )){
srMds = serviceRecord;
return TRANSPORT_MDS;
}
}
// WiFi and WAP 2.0
if( "wptcp".equalsIgnoreCase( cid ) ) {
String interfaceName = getEncodedStringFieldFromWptcpServiceRecord( serviceRecord, ENCODED_TYPE_INTERFACE );
if ( SERVICE_RECORD_TYPE_WPTCP_INTERFACE_WIFI.equals( interfaceName ) ) {
srTcpWiFi = serviceRecord;
return TRANSPORT_TCP_WIFI;
} else {
String mmscUrl = getEncodedStringFieldFromWptcpServiceRecord( serviceRecord, ENCODED_TYPE_MMSC_URL );
String httpProxyAddress = getEncodedStringFieldFromWptcpServiceRecord( serviceRecord, ENCODED_TYPE_HTTP_PROXY_ADDRESS );
if ( ( mmscUrl == null) || ( mmscUrl.trim().length() == 0 ) ) {
if ( getEncodedIntFieldFromWptcpServiceRecord(serviceRecord, ENCODED_TYPE_PROXY_AUTH_USERNAME_TYPE ) != AUTH_TYPE_BBAUTH_TOKEN_NEGOTIATION ) {
if ( ( httpProxyAddress == null ) || ( httpProxyAddress.trim().length() == 0 ) ) {
srTcpCellularWithApn = serviceRecord;
return TCP_CELLULAR_APN_SERVICE_BOOK;
} else {
srWap2 = serviceRecord;
return TRANSPORT_WAP2;
}
}
}
}
}
// Wap1.0
if( "wap".equalsIgnoreCase( cid ) ) {
String mmscUrl = getEncodedStringFieldFromWapServiceRecord( serviceRecord, SERVICE_RECORD_TYPE_WAP_MMSC_URL_TAG );
if ( ( mmscUrl == null) || ( mmscUrl.trim().length() == 0 ) ) {
srWap = serviceRecord;
return TRANSPORT_WAP;
}
}
return -1;
}
/**
* Determines the int Value corresponding to the parameter tag from the IPPP Service Record's encoded Application Data
*
* @param ipppServiceRecord
* an IPPP ServiceRecord. Must not be null.
*
* @param typeByteCode
* a byte representing the IPPP Application Data specific parameter tag.
*
* @return an int representing the value assigned to the particular parameter tag or -1 if the encoded value is not found
*
*/
private static int getEncodedIntFromIPPPServiceRecord( ServiceRecord ipppServiceRecord, byte typeByteCode ) {
int intVal = -1;
byte[] applicationData = ipppServiceRecord.getApplicationData();
if( applicationData != null ) {
DataBuffer buffer = new DataBuffer(applicationData, 0, applicationData.length, true);
try {
if(ConverterUtilities.findType(buffer, typeByteCode)){
intVal = ConverterUtilities.readInt(buffer);
}
} catch( Throwable e ) {
}
}
return intVal;
}
/**
* Determines the String Value corresponding to the parameter tag from the WPTCP Service Record's encoded Application Data
*
* @param wptcpServiceRecord
* a WPTCP ServiceRecord. Must not be null.
*
* @param typeByteCode
* a byte representing the WPTCP Application Data specific parameter tag.
*
* @return a String representing the value assigned to the particular parameter tag or <b>null</b> if the encoded value does not exist.
*
*/
private static String getEncodedStringFieldFromWptcpServiceRecord( ServiceRecord wptcpServiceRecord, byte typeByteCode ) {
String stringData = null;
byte[] applicationData = wptcpServiceRecord.getApplicationData();
if( applicationData != null ) {
DataBuffer buffer = new DataBuffer( applicationData, 0, applicationData.length, true );
try {
// skip version
buffer.readByte();
if(TLEUtilities.findType(buffer, typeByteCode)){
stringData = TLEUtilities.readStringField(buffer, typeByteCode);
}
} catch( Throwable e ) {
}
}
return stringData;
}
/**
* Determines the int Value corresponding to the parameter tag from the WPTCP Service Record's encoded Application Data
*
* @param wptcpServiceRecord
* a WPTCP ServiceRecord. Must not be null.
*
* @param typeByteCode
* a byte representing the WPTCP Application Data specific parameter tag.
*
* @return an int representing the value assigned to the particular parameter tag or <b>null</b> if the encoded value does not exist.
*
*/
private static int getEncodedIntFieldFromWptcpServiceRecord( ServiceRecord wptcpServiceRecord, byte typeByteCode ) {
int intData = -1;
byte[] applicationData = wptcpServiceRecord.getApplicationData();
if( applicationData != null ) {
DataBuffer buffer = new DataBuffer( applicationData, 0, applicationData.length, true );
try {
// skip version
buffer.readByte();
if(TLEUtilities.findType(buffer, typeByteCode)){
intData = TLEUtilities.readIntegerField(buffer, typeByteCode);
}
} catch( Throwable e ) {
}
}
return intData;
}
/**
* Determines the String Value corresponding to the parameter tag from the WAP Service Record's encoded Application Data
*
* @param wapServiceRecord
* an WAP ServiceRecord
*
* @param typeByteCode
* a byte representing the WAP Application Data specific parameter tag
*
* @return a String representing the value assigned to the particular parameter tag or <b>null</b> if the encoded value does not exist.
*
*/
private static String getEncodedStringFieldFromWapServiceRecord( ServiceRecord wapServiceRecord, byte typeByteCode ) {
String stringData = null;
byte[] applicationData = wapServiceRecord.getApplicationData();
if( applicationData != null ) {
DataBuffer buffer = new DataBuffer( applicationData, 0, applicationData.length, true );
try {
//skip version
buffer.readByte();
if(TLEUtilities.findType(buffer, typeByteCode)){
stringData = TLEUtilities.readStringField(buffer, typeByteCode);
}
} catch( Throwable e ) {
}
}
return stringData;
}
public int getBestTransportForActiveCoverage() {
int availableTransports = getAvailableTransportCoverage();
int iCurTransport = -1;
if ((availableTransports & TransportDetective.TRANSPORT_TCP_WIFI) > 0) {
iCurTransport = TransportDetective.TRANSPORT_TCP_WIFI;
} else if (RadioInfo.getState() == RadioInfo.STATE_ON
&& RadioInfo.getSignalLevel() != RadioInfo.LEVEL_NO_COVERAGE) {
if ((availableTransports & TransportDetective.TRANSPORT_MDS) > 0) {
iCurTransport = TransportDetective.TRANSPORT_MDS;
} else if ((availableTransports & TransportDetective.TRANSPORT_BIS_B) > 0) {
iCurTransport = TransportDetective.TRANSPORT_BIS_B;
}
if (iCurTransport == -1 && ((availableTransports & TransportDetective.TCP_CELLULAR_APN_SERVICE_BOOK) > 0)) {
iCurTransport = TransportDetective.TCP_CELLULAR_APN_SERVICE_BOOK;
} else if (iCurTransport == -1 && ((availableTransports & TransportDetective.TRANSPORT_TCP_CELLULAR) > 0)) {
iCurTransport = TransportDetective.TRANSPORT_TCP_CELLULAR;
}
}
return iCurTransport;
}
}