package com.apigee.sdk.apm.android;
import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.location.Criteria;
import android.location.Location;
import android.location.LocationManager;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.provider.Settings.Secure;
import android.telephony.TelephonyManager;
import android.util.Log;
import com.apigee.sdk.AppIdentification;
import com.apigee.sdk.apm.android.model.ClientLog;
import com.apigee.sdk.apm.android.model.ClientMetricsEnvelope;
import com.apigee.sdk.apm.android.model.ClientSessionMetrics;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonGenerationException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
/**
* @y.exclude
*/
public abstract class AbstractUploadService implements MetricsUploadService {
public static final String VALUE_UNKNOWN = "UNKNOWN";
public static final String MSG_PAYLOAD_NOT_SENT = "Payload was not sent. Dropping payload : ";
private Context appActivity;
private AppIdentification appIdentification;
private AndroidLog logger;
private NetworkMetricsCollectorService httpMetrics;
private ApigeeActiveSettings activeSettings;
private SessionManager sessionManager;
private ObjectMapper objectMapper;
private ApigeeMonitoringClient monitoringClient;
protected AbstractUploadService(Context appActivity,
AppIdentification appIdentification,
AndroidLog log,
NetworkMetricsCollectorService httpMetrics,
ApigeeActiveSettings activeSettings,
SessionManager sessionManager,
ApigeeMonitoringClient monitoringClient) {
this.appActivity = appActivity;
this.appIdentification = appIdentification;
this.logger = log;
this.httpMetrics = httpMetrics;
this.activeSettings = activeSettings;
this.sessionManager = sessionManager;
this.objectMapper = new ObjectMapper();
this.monitoringClient = monitoringClient;
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
}
/**
* Discards all log records and network performance metrics
*/
public void clear() {
if (logger != null) {
logger.clear();
}
if (httpMetrics != null) {
httpMetrics.clear();
}
}
/**
* This function gets called to upload data from the client to the server.
* The logic will look like this:
*
* 1. Flush" logger and httpMetricsCollector. 2. Check to see if there is an
* internet connection. If none exists, do nothing. 3. Send data to back-end. If
* exception occurs, drop data
*
* Over time, we'll make the logic more sophisticated
*
* 1. Check to see if there is an internet connection. If none exists: 1a.
* "Flush" logger and httpMetricsCollector. 1b. Write data to local temp
* files 2. If connection does exist, check to see if any data exists in
* temp files. 3a. If data in temp files do exist, flush logger and
* httpMetrics to temp files 3b. Send data in temp files to back-end. If exception
* occurs, do nothing. 4a. If data in temp files do not exist, send data.
* If exception occurs, write data to temp files.
*
*/
public void uploadData(List<UploadListener> listListeners) {
try {
// Check to see if there is data that is written to file, if so,
// send that data first
ClientMetricsEnvelope payload = getDataToUpload();
if ((payload != null) && allowedToSendData()) {
//TODO: review this -- should we create a new session before uploading if we don't
// currently have a valid session?
if( ! sessionManager.isSessionValid() ) {
sessionManager.openSession();
}
try {
sendMetrics(payload);
String payloadAsString = objectMapper
.writeValueAsString(payload);
if (listListeners != null) {
Iterator<UploadListener> iterator = listListeners.iterator();
while( iterator.hasNext() ) {
UploadListener listener = iterator.next();
listener.onUploadMetrics(payloadAsString);
}
}
sendMetrics(payloadAsString);
Log.v(ClientLog.TAG_MONITORING_CLIENT,
"Successfully sent metrics");
} catch (MetricsUploadException e) {
Log.e(ClientLog.TAG_MONITORING_CLIENT,
MSG_PAYLOAD_NOT_SENT
+ e.getMessage());
} catch (JsonGenerationException e) {
Log.e(ClientLog.TAG_MONITORING_CLIENT,
MSG_PAYLOAD_NOT_SENT
+ e.getMessage());
} catch (JsonMappingException e) {
Log.e(ClientLog.TAG_MONITORING_CLIENT,
MSG_PAYLOAD_NOT_SENT
+ e.getMessage());
} catch (IOException e) {
Log.e(ClientLog.TAG_MONITORING_CLIENT,
MSG_PAYLOAD_NOT_SENT
+ e.getMessage());
}
} else {
Log.i(ClientLog.TAG_MONITORING_CLIENT,
"Client was not allowed to send data. Payload was not sent. Dropping payload");
}
} catch (RuntimeException e) {
Log.e(ClientLog.TAG_MONITORING_CLIENT,
"Caught an unhandled run time exception. Swallowing exception and continuing: "
+ e.toString());
}
}
public ApigeeActiveSettings getActiveSettings() {
return activeSettings;
}
public Context getAppActivity() {
return appActivity;
}
public ClientSessionMetrics getSessionMetrics() {
ClientSessionMetrics sessionMetrics = new ClientSessionMetrics();
try {
//set device hardware metadata
sessionMetrics.setDeviceModel(ApigeeMonitoringClient.getDeviceModel());
sessionMetrics.setDeviceOSVersion(ApigeeMonitoringClient.getDeviceOSVersion());
sessionMetrics.setDevicePlatform(ApigeeMonitoringClient.getDevicePlatform());
sessionMetrics.setDeviceType(ApigeeMonitoringClient.getDeviceType());
String android_id = Secure.getString(
appActivity.getContentResolver(), Secure.ANDROID_ID);
sessionMetrics.setDeviceId(android_id);
//session id and start time
String sessionId = sessionManager.getSessionUUID();
if (sessionId == null || sessionId.length() == 0) {
sessionId = sessionManager.openSession();
}
sessionMetrics.setSessionId(sessionId);
sessionMetrics.setTimeStamp(new Date());
sessionMetrics.setSessionStartTime(sessionManager.getSessionStartTime());
sessionMetrics.setSdkVersion(ApigeeMonitoringClient.getSDKVersion());
sessionMetrics.setSdkType(ApigeeMonitoringClient.getDevicePlatform());
// application Id
ApigeeActiveSettings activeSettings = monitoringClient.getActiveSettings();
if (null != activeSettings) {
Long instaOpsAppId = activeSettings.getInstaOpsApplicationId();
if (instaOpsAppId != null) {
sessionMetrics.setAppId(instaOpsAppId);
}
}
//setting all the fields which rely on different permissions.
//TODO: double check if there is a permission that could prohibit SDK from getting application version
sessionMetrics.setApplicationVersion(appActivity
.getPackageManager().getPackageInfo(
appActivity.getPackageName(), 0).versionName);
if ((activeSettings != null) && activeSettings.getLocationCaptureEnabled()) {
if (appActivity.checkCallingOrSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED)
{
Criteria gpsCriteria = new Criteria();
gpsCriteria.setAccuracy(Criteria.ACCURACY_COARSE);
gpsCriteria.setAltitudeRequired(false);
gpsCriteria.setBearingRequired(false);
gpsCriteria.setCostAllowed(true);
gpsCriteria.setPowerRequirement(Criteria.POWER_MEDIUM);
gpsCriteria.setSpeedRequired(false);
LocationManager locationManager = (LocationManager) appActivity
.getSystemService(Context.LOCATION_SERVICE);
String locationProviderString = locationManager
.getBestProvider(gpsCriteria, true);
Location lastKnownLocation;
if (locationProviderString != null) {
lastKnownLocation = locationManager
.getLastKnownLocation(locationProviderString);
if (lastKnownLocation != null) {
sessionMetrics.setLongitude(lastKnownLocation
.getLongitude());
sessionMetrics.setLatitude(lastKnownLocation
.getLatitude());
sessionMetrics.setBearing(lastKnownLocation
.getBearing());
}
}
}
else
{
Log.v(ClientLog.TAG_MONITORING_CLIENT, "GPS was turned off or denied");
sessionMetrics.setLongitude(0d);
sessionMetrics.setLatitude(0d);
sessionMetrics.setBearing(0f);
}
}
if ((this.activeSettings != null) && this.activeSettings.getNetworkCarrierCaptureEnabled()) {
if (appActivity.checkCallingOrSelfPermission(Manifest.permission.ACCESS_NETWORK_STATE) == PackageManager.PERMISSION_GRANTED)
{
ConnectivityManager connectivityManager = (ConnectivityManager) appActivity
.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = connectivityManager
.getActiveNetworkInfo();
if (networkInfo != null) {
//Made change because "mobile" was not upper case
sessionMetrics.setNetworkType(formatString(networkInfo.getTypeName()).toUpperCase());
sessionMetrics.setNetworkSubType(formatString(networkInfo.getSubtypeName()).toUpperCase());
sessionMetrics.setNetworkExtraInfo(formatString(networkInfo.getExtraInfo()).toUpperCase());
}
}
else
{
Log.v(ClientLog.TAG_MONITORING_CLIENT, "Network state permission denied");
sessionMetrics.setNetworkType(VALUE_UNKNOWN);
sessionMetrics.setNetworkSubType(VALUE_UNKNOWN);
sessionMetrics.setNetworkExtraInfo(VALUE_UNKNOWN);
}
if (appActivity.checkCallingOrSelfPermission(Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED)
{
TelephonyManager telephonyManager = (TelephonyManager) appActivity
.getSystemService(Context.TELEPHONY_SERVICE);
if (telephonyManager != null) {
sessionMetrics.setTelephonyDeviceId(telephonyManager
.getDeviceId());
sessionMetrics
.setTelephonyNetworkOperator(telephonyManager
.getNetworkOperator());
sessionMetrics
.setTelephonyNetworkOperatorName(telephonyManager
.getNetworkOperatorName());
//TODO: should telephonyNetworkType be used?
//int telephonyNetworkType = telephonyManager.getNetworkType();
final int telephonyPhoneType = telephonyManager.getPhoneType();
if (telephonyPhoneType == TelephonyManager.PHONE_TYPE_GSM) {
sessionMetrics.setTelephonyPhoneType("GSM");
// envelope.setTelephonyeSignalStrength(telephonyManager.get)
} else if (telephonyPhoneType == TelephonyManager.PHONE_TYPE_CDMA) {
sessionMetrics.setTelephonyPhoneType("CDMA");
} else if (telephonyPhoneType == TelephonyManager.PHONE_TYPE_NONE) {
sessionMetrics.setTelephonyPhoneType(VALUE_UNKNOWN);
} else {
sessionMetrics.setTelephonyPhoneType(VALUE_UNKNOWN);
}
}
}
else
{
Log.v(ClientLog.TAG_MONITORING_CLIENT, "Phone state permission denied");
sessionMetrics.setTelephonyPhoneType(VALUE_UNKNOWN);
sessionMetrics.setTelephonyDeviceId(VALUE_UNKNOWN);
sessionMetrics.setTelephonyNetworkOperator(VALUE_UNKNOWN);
sessionMetrics.setTelephonyNetworkOperatorName(VALUE_UNKNOWN);
}
//Set the network carrier
if ((sessionMetrics.getNetworkType() != null) && sessionMetrics.getNetworkType().equalsIgnoreCase("MOBILE"))
{
String networkOperatorName = sessionMetrics.getTelephonyNetworkOperatorName();
if( (networkOperatorName != null) && (networkOperatorName.length() > 0) ) {
sessionMetrics.setNetworkCarrier(formatString(networkOperatorName));
} else {
sessionMetrics.setNetworkCarrier(VALUE_UNKNOWN);
}
}
else
{
sessionMetrics.setNetworkCarrier(VALUE_UNKNOWN);
}
} else {
Log.v(ClientLog.TAG_MONITORING_CLIENT, "Network capture not enabled");
sessionMetrics.setNetworkType(VALUE_UNKNOWN);
sessionMetrics.setNetworkSubType(VALUE_UNKNOWN);
sessionMetrics.setNetworkExtraInfo(VALUE_UNKNOWN);
sessionMetrics.setNetworkCarrier(VALUE_UNKNOWN);
sessionMetrics.setTelephonyPhoneType(VALUE_UNKNOWN);
sessionMetrics.setTelephonyDeviceId(VALUE_UNKNOWN);
sessionMetrics.setTelephonyNetworkOperator(VALUE_UNKNOWN);
sessionMetrics.setTelephonyNetworkOperatorName(VALUE_UNKNOWN);
}
return sessionMetrics;
}
catch (SecurityException se)
{
Log.e(ClientLog.TAG_MONITORING_CLIENT, "An unknown security exception occurred or permssion was tripped : " + se.getMessage());
}
catch (RuntimeException e) {
Log.v(ClientLog.TAG_MONITORING_CLIENT,
"Runtime exception when constructing session metrics: "
+ e.getMessage());
}
catch (NameNotFoundException e) {
Log.e(ClientLog.TAG_MONITORING_CLIENT, e.getMessage());
}
return sessionMetrics;
}
public ClientMetricsEnvelope constructWebServiceMetricsBeanMessageEnvelop() {
if (!monitoringClient.isAbleToSendDataToServer()) {
return null;
}
ClientMetricsEnvelope envelope = null;
ApigeeActiveSettings activeSettings = monitoringClient.getActiveSettings();
if (null != activeSettings && activeSettings.getApigeeApp() != null ) {
String orgName = activeSettings.getOrgName();
String appName = activeSettings.getAppName();
Long instaOpsAppId = activeSettings.getInstaOpsApplicationId();
envelope = new ClientMetricsEnvelope();
envelope.setTimeStamp(new Date());
envelope.setOrgName(orgName);
envelope.setAppName(appName);
envelope.setInstaOpsApplicationId(instaOpsAppId);
String fullAppName = activeSettings.getFullAppName();
if (fullAppName != null) {
envelope.setFullAppName(fullAppName);
}
}
if (envelope == null) {
Log.w(ClientLog.TAG_MONITORING_CLIENT, "missing app identification fields needed to send data to server");
}
return envelope;
}
public ClientMetricsEnvelope getDataToUpload() {
ClientMetricsEnvelope envelop = constructWebServiceMetricsBeanMessageEnvelop();
if (envelop != null) {
ClientSessionMetrics sm = getSessionMetrics();
envelop.setSessionMetrics(sm);
if (!(httpMetrics.getMetrics().size() == 0)) {
envelop.setMetrics(httpMetrics.flush());
}
if (logger.haveLogRecords()) {
List<ClientLog> logs = logger.flush();
if( logs != null ) {
envelop.setLogs(logs);
}
}
}
return envelop;
}
//This is to deal with the possibility that the android OS returns null
protected String formatString(String s)
{
if (s == null || s.length() == 0)
{
s = VALUE_UNKNOWN;
}
return s;
}
}