package com.apigee.sdk.apm.android;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Build;
import android.os.Handler;
import android.provider.Settings.Secure;
import android.util.Log;
import com.apigee.sdk.ApigeeClient;
import com.apigee.sdk.AppIdentification;
import com.apigee.sdk.DefaultAndroidLog;
import com.apigee.sdk.Logger;
import com.apigee.sdk.apm.android.crashlogging.CrashManager;
import com.apigee.sdk.apm.android.metrics.LowPriorityThreadFactory;
import com.apigee.sdk.apm.android.model.ClientLog;
import com.apigee.sdk.data.client.ApigeeDataClient;
import org.apache.http.client.HttpClient;
import org.apache.http.impl.client.DefaultHttpClient;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/*
* App monitoring server communications:
*
* Crash report upload:
* CrashManager.java (submitStackTrace)
*
* Metrics upload:
* UploadService.java (sendMetrics)
* MonitoringClient.java (postString)
* Retrieve configuration from server:
* CompositeConfigurationServiceImpl.java (retrieveConfigFromServer)
*
*/
/**
* Initializes API BaaS App Monitoring, including crash, usage metrics and log interception.
* Generally, this should be instantiated via ApigeeClient, rather than directly.
*
* @see com.apigee.sdk.ApigeeClient
* @see <a href="http://apigee.com/docs/app-services/content/app-monitoring">App Monitoring documentation</a>
*/
public class ApigeeMonitoringClient implements SessionTimeoutListener {
/**
* @y.exclude
*/
public static final boolean DEFAULT_AUTO_UPLOAD_ENABLED = true;
/**
* @y.exclude
*/
public static final boolean DEFAULT_CRASH_REPORTING_ENABLED = true;
/**
* @y.exclude
*/
public static final int SUBMIT_THREAD_TTL_MILLIS = 180 * 1000;
/**
* @y.exclude
*/
public static final int SESSION_EXPIRATION_MILLIS = 1000 * 60 * 30;
private static ApigeeMonitoringClient singleton = null;
private Handler sendMetricsHandler;
private HttpClient httpClient;
private HttpClient originalHttpClient;
private NetworkMetricsCollectorService collector;
private ApigeeActiveSettings activeSettings;
private MetricsUploadService uploadService;
private DefaultAndroidLog defaultLogger;
private AndroidLog log;
private ArrayList<UploadListener> listListeners;
private AppIdentification appIdentification;
private Context appActivity;
private boolean isActive;
private boolean isInitialized = false;
private boolean monitoringPaused;
private boolean isPartOfSample;
private boolean enableAutoUpload;
private boolean crashReportingEnabled;
private boolean alwaysUploadCrashReports;
private SessionManager sessionManager;
private ApigeeDataClient dataClient;
private static ThreadPoolExecutor sExecutor =
new ThreadPoolExecutor(0, 1, SUBMIT_THREAD_TTL_MILLIS, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(), new LowPriorityThreadFactory());
/**
* Metrics upload interval. Every 300000 milliseconds by default.
*/
public static final int UPLOAD_INTERVAL = 300000; // 5 Minutes
/**
* Default constructor for starting App Monitoring. Generally, should not be called directly.
* Initializing the SDK with ApigeeClient will call this via AppMon.
*
* @param appIdentification an AppIdentification object that identifies the API BaaS organization
* and application to start App Monitoring for
* @param dataClient the instance of DataClient created when ApigeeClient was instantiated
* @param appActivity an application Context object
* @param monitoringOptions a MonitoringOptions object for specifying App Monitoring options
* @return the initialized MonitoringClient
* @throws InitializationException
* @see AppMon
*/
public static synchronized ApigeeMonitoringClient initialize(AppIdentification appIdentification,
ApigeeDataClient dataClient,
Context appActivity,
MonitoringOptions monitoringOptions) throws InitializationException {
// HttpClient defaultClient = AndroidHttpClient.newInstance(appId);
return initialize(appIdentification, dataClient, appActivity, new DefaultHttpClient(), monitoringOptions);
}
/**
* Constructor for starting App Monitoring with a specified HttpClient. Generally, should not be called directly.
* Initializing the SDK with ApigeeClient will call this.
*
* @param appIdentification an AppIdentification object that identifies the API BaaS organization
* and application to start App Monitoring for
* @param dataClient the instance of DataClient created when ApigeeClient was instantiated
* @param appActivity an application Context object
* @param monitoringOptions a MonitoringOptions object for specifying App Monitoring options
* @return the initialized MonitoringClient
* @throws InitializationException
* @see AppMon
*/
public static synchronized ApigeeMonitoringClient initialize(AppIdentification appIdentification,
ApigeeDataClient dataClient,
Context appActivity, HttpClient client,
MonitoringOptions monitoringOptions)
throws InitializationException {
if (singleton == null) {
try {
ApigeeMonitoringClient instance = new ApigeeMonitoringClient(appIdentification, dataClient,
appActivity, client, monitoringOptions);
singleton = instance;
return instance;
} catch (InitializationException e) {
Log.e(ClientLog.TAG_MONITORING_CLIENT, "Cannot instantiate MonitoringClient:" + e.getMessage());
throw e;
} catch (Throwable t) {
t.printStackTrace();
String message;
if( (t != null) && (t.getMessage() != null) ) {
message = t.getMessage();
} else {
message = "unknown";
}
Log.e(ClientLog.TAG_MONITORING_CLIENT, "exception caught:" + message);
return null;
}
} else {
Log.e(ClientLog.TAG_MONITORING_CLIENT, "MonitoringClient is already initialized");
throw new InitializationException("MonitoringClient is already initialized");
}
}
/**
* Returns the MonitoringClient instance
*
* @return the MonitoringClient, or null if the client has not been initialized
*/
public static ApigeeMonitoringClient getInstance() {
if (singleton != null) {
return singleton;
} else {
// throw new
// LoadConfigurationException("Android HttpClientWrapper not initialized");
//Log.w(ClientLog.TAG_MONITORING_CLIENT,
//"Android HttpClientWrapper not initialized. Returning null");
// throw new
// InitializationException("Monitoring Client was not initialized");
// Need to change this function to support auto initialization.
return null;
}
}
/**
* @throws InitializationException
*
*
*
*/
public ApigeeMonitoringClient(AppIdentification appIdentification, ApigeeDataClient dataClient, Context appActivity,
HttpClient client,
MonitoringOptions monitoringOptions) throws InitializationException {
defaultLogger = new DefaultAndroidLog();
initializeInstance(appIdentification, dataClient, appActivity, client, monitoringOptions);
}
protected void initializeInstance(AppIdentification appIdentification, ApigeeDataClient dataClient, Context appActivity,
HttpClient client,
MonitoringOptions monitoringOptions) throws InitializationException {
this.isActive = false;
this.isInitialized = false;
this.monitoringPaused = false;
this.listListeners = new ArrayList<UploadListener>();
if( monitoringOptions != null ) {
this.crashReportingEnabled = monitoringOptions.getCrashReportingEnabled();
this.enableAutoUpload = monitoringOptions.getEnableAutoUpload();
this.alwaysUploadCrashReports = monitoringOptions.getAlwaysUploadCrashReports();
UploadListener uploadListener = monitoringOptions.getUploadListener();
if( uploadListener != null ) {
if (null == this.listListeners) {
this.listListeners = new ArrayList<UploadListener>();
}
this.listListeners.add(uploadListener);
}
} else {
this.crashReportingEnabled = true;
this.enableAutoUpload = true;
this.alwaysUploadCrashReports = true;
}
this.dataClient = dataClient;
this.sessionManager = new SessionManager(SESSION_EXPIRATION_MILLIS,this);
this.sessionManager.openSession();
// First configure the logger
this.appIdentification = appIdentification;
this.originalHttpClient = client;
this.appActivity = appActivity;
if (readUpdateAndApplyConfiguration(client,enableAutoUpload,null))
{
if (enableAutoUpload)
{
Log.i(ClientLog.TAG_MONITORING_CLIENT, "Enabling auto sending of metrics");
} else {
Log.i(ClientLog.TAG_MONITORING_CLIENT, "Auto sending of metrics disabled");
}
log.i(ClientLog.TAG_MONITORING_CLIENT, ClientLog.EVENT_INIT_AGENT);
isInitialized = true;
} else {
isInitialized = false;
isActive = false;
}
}
/**
* Gets the logger being used by Monitoring Client
*
* @return an instance of the default Android logger or
* the Apigee logger
*/
public Logger getLogger() {
return log;
}
/**
* @y.exclude
*/
synchronized protected void initializeSubServices()
{
log = new AndroidLog(activeSettings);
collector = new NetworkMetricsCollector(activeSettings);
httpClient = new HttpClientWrapper(originalHttpClient, appIdentification, collector, activeSettings);
this.uploadService = new UploadService(this, appActivity, appIdentification,
log, collector, activeSettings, sessionManager);
}
/**
* Retrieves boolean indicating whether we should upload crash reports even if the device is not part of sample
* @return boolean indicator
*/
public boolean getAlwaysUploadCrashReports() {
return this.alwaysUploadCrashReports;
}
/**
* Retrieves boolean indicating whether the device is part of sample or not
* @return boolean indicator
*/
public boolean isParticipatingInSample() {
synchronized(this) {
if (this.isInitialized) {
return this.isPartOfSample;
} else {
return false; // at least not yet
}
}
}
/**
* Retrieves boolean indicating whether App Monitoring is enabled and sending data
* @return boolean indicator
*/
private boolean allowedToSendData() {
boolean willSendData = false;
ApigeeActiveSettings settings = this.activeSettings;
if (null != settings) {
boolean monitoringDisabled = settings.getMonitoringDisabled() != null && settings.getMonitoringDisabled();
if (monitoringDisabled) {
Log.i(ClientLog.TAG_MONITORING_CLIENT, "Monitoring disabled in configuration. Not sending data");
} else {
Long samplingRate = settings.getSamplingRate();
if ( samplingRate != null ) {
Random generator = new Random();
int coinflip = generator.nextInt(100);
if (coinflip < samplingRate.intValue()) {
Log.i(ClientLog.TAG_MONITORING_CLIENT, "Monitoring enabled. Sample Rate : " + samplingRate);
this.isPartOfSample = true;
willSendData = true;
} else {
Log.i(ClientLog.TAG_MONITORING_CLIENT, "Monitoring disabled. Sample Rate : " + samplingRate);
this.isPartOfSample = false;
willSendData = false;
}
} else {
Log.i(ClientLog.TAG_MONITORING_CLIENT, "Monitoring Enabled");
this.isPartOfSample = true;
willSendData = true;
}
}
}
return willSendData;
}
/**
* Retrieves the base URL for App Monitoring requests, including the
* API BaaS organization and application
*
* @return the base URL
*/
public String getBaseServerURL() {
String baseServerURL = null;
String baseURL = appIdentification.getBaseURL();
if( baseURL.endsWith("/") ) {
baseServerURL = baseURL +
appIdentification.getOrganizationId() +
"/" +
appIdentification.getApplicationId();
} else {
baseServerURL = baseURL +
"/" +
appIdentification.getOrganizationId() +
"/" +
appIdentification.getApplicationId();
}
return baseServerURL;
}
/**
* Retrieves the URL that App Monitoring config is being download from.
*
* @return the config download URL
*/
public String getConfigDownloadURL() {
return getBaseServerURL() + "/apm/apigeeMobileConfig";
}
/**
* Retrieves the URL that App Monitoring crash reports are being uploaded to.
*
* @return the crash report upload URL
*/
public String getCrashReportUploadURL(String crashFileName) {
return getBaseServerURL() + "/apm/crashLogs/" + crashFileName;
}
/**
* Retrieves the URL that App Monitoring tracked metrics are being uploaded to.
*
* @return the metrics upload URL
*/
public String getMetricsUploadURL() {
return getBaseServerURL() + "/apm/apmMetrics";
}
/**
* Discards all log records and network performance metrics
*/
public void discardAllMetrics() {
if (uploadService != null) {
Log.v(ClientLog.TAG_MONITORING_CLIENT, "Discarding all metrics");
uploadService.clear();
}
}
/**
* Determine if monitoring is currently paused
* @return boolean indicating whether monitoring is currently paused
*/
public boolean isPaused() {
return monitoringPaused;
}
/**
* Pauses monitoring. If monitoring is already paused when pause is called, there is no change to
* monitoring functionality, but a log message is generated.
*/
public void pause() {
if (isInitialized) {
if (!isPaused()) {
Log.i(ClientLog.TAG_MONITORING_CLIENT, ClientLog.EVENT_PAUSE_AGENT);
monitoringPaused = true;
cancelTimer();
// discard all outstanding log records and network metrics
discardAllMetrics();
} else {
Log.v(ClientLog.TAG_MONITORING_CLIENT, "Pause called when monitoring is already paused");
}
} else {
// we've never been initialized, so there's nothing to do
}
}
/**
* Resumes monitoring after being paused. If monitoring is not paused when resume is called, there
* is no change to monitoring functionality, but a log message is generated.
*/
public void resume() {
if (isInitialized) {
if (isPaused()) {
monitoringPaused = false;
Log.i(ClientLog.TAG_MONITORING_CLIENT, ClientLog.EVENT_RESUME_AGENT);
establishTimer();
} else {
Log.v(ClientLog.TAG_MONITORING_CLIENT, "Resume called when monitoring is not paused");
}
} else {
// we've never been initialized, so there's nothing to do
}
}
/**
* Resumes monitoring after being paused. If monitoring is not paused when resume is called, there
* is no change to monitoring functionality, but a log message is generated.
* @deprecated
* @see #resume()
*/
public void resumeAgent() {
resume();
}
/**
* Pauses monitoring. If monitoring is already paused when pause is called, there is no change to
* monitoring functionality, but a log message is generated.
* @deprecated
* @see #pause()
*/
public void pauseAgent() {
pause();
}
/**
* @y.exclude
*/
public String putOrPostString(String httpMethod, String body, String urlAsString, String contentType) {
String response = null;
OutputStream out = null;
InputStream in = null;
try {
URL url = new URL(urlAsString);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
byte[] putOrPostData = body.getBytes();
conn.setDoOutput(true);
conn.setRequestMethod(httpMethod);
conn.setRequestProperty("Content-Length", Integer.toString(putOrPostData.length));
if( contentType != null ) {
conn.setRequestProperty("Content-Type", contentType);
}
conn.setUseCaches(false);
if (httpMethod.equals("POST")) {
Log.v(ClientLog.TAG_MONITORING_CLIENT, "Posting data to '" + urlAsString + "'");
} else {
Log.v(ClientLog.TAG_MONITORING_CLIENT, "Putting data to '" + urlAsString + "'");
}
out = conn.getOutputStream();
out.write(putOrPostData);
out.close();
out = null;
in = conn.getInputStream();
if( in != null ) {
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
StringBuilder sb = new StringBuilder();
String line;
while( (line = reader.readLine()) != null ) {
sb.append(line);
sb.append('\n');
}
response = sb.toString();
Log.v(ClientLog.TAG_MONITORING_CLIENT,"response from server: '" + response + "'");
} else {
response = null;
Log.v(ClientLog.TAG_MONITORING_CLIENT,"no response from server after post");
}
final int responseCode = conn.getResponseCode();
Log.v(ClientLog.TAG_MONITORING_CLIENT,"responseCode from server = " + responseCode);
} catch(Exception e) {
Log.e(ClientLog.TAG_MONITORING_CLIENT,"Unable to post to '" + urlAsString + "'");
Log.e(ClientLog.TAG_MONITORING_CLIENT,e.getLocalizedMessage());
response = null;
} finally {
try {
if( out != null ) {
out.close();
}
if( in != null ) {
in.close();
}
} catch(Exception ignored) {
}
}
return response;
}
/**
* @y.exclude
*/
public String postString(String postBody, String urlAsString, String contentType) {
return putOrPostString("POST", postBody, urlAsString, contentType);
}
/**
* @y.exclude
*/
public String postString(String postBody, String urlAsString) {
return postString(postBody, urlAsString, "application/json; charset=utf-8");
}
/**
* @y.exclude
*/
public String putString(String body, String urlAsString, String contentType) {
return putOrPostString("PUT", body, urlAsString, contentType);
}
/**
* @y.exclude
*/
public String putString(String body, String urlAsString) {
return putString(body, urlAsString, "application/json; charset=utf-8");
}
/**
* @y.exclude
*/
public boolean readUpdateAndApplyConfiguration(HttpClient client,
final boolean enableAutoUpload,
final ConfigurationReloadedListener reloadListener) {
boolean success = true;
this.activeSettings = new ApigeeActiveSettings(this.appActivity,this.appIdentification,this.dataClient,this,client);
this.initializeSubServices();
try {
// loader.loadConfigurations(this.appId);
boolean loadSuccess = this.activeSettings.loadLocalApplicationConfiguration();
if(loadSuccess) {
Log.v(ClientLog.TAG_MONITORING_CLIENT, "Found previous configuration on disk. ");
} else {
Log.v(ClientLog.TAG_MONITORING_CLIENT, "No configuration found on disk. Using default configurations");
}
this.isActive = this.allowedToSendData();
if (this.isActive || this.alwaysUploadCrashReports) {
if (crashReportingEnabled) {
sExecutor.execute(new CrashManagerTask(this));
} else {
Log.i(ClientLog.TAG_MONITORING_CLIENT, "Crash reporting disabled");
}
}
final ApigeeMonitoringClient monitoringClient = this;
// read configuration
sExecutor.execute(new Runnable() {
public void run() {
LoadRemoteConfigTask loadRemoteConfigTask = new LoadRemoteConfigTask(reloadListener);
loadRemoteConfigTask.run();
if (isActive && enableAutoUpload) {
ForcedUploadDataTask uploadDataTask = new ForcedUploadDataTask(monitoringClient);
uploadDataTask.run();
establishTimer();
}
}
});
} catch (LoadConfigurationException e) {
success = false;
} catch (RuntimeException e) {
success = false;
} catch (Throwable t) {
success = false;
}
return success;
}
/**
* Cancels any outstanding upload requests that are set up with our timer mechanism
*/
protected void cancelTimer() {
if (null != sendMetricsHandler) {
sendMetricsHandler.removeCallbacksAndMessages(null);
}
}
/**
* Sets up timer mechanism so that upload will occur at the next scheduled interval
*/
protected void establishTimer() {
if (null != sendMetricsHandler) {
sendMetricsHandler.removeCallbacksAndMessages(null);
} else {
sendMetricsHandler = new Handler(appActivity.getMainLooper());
}
final ApigeeMonitoringClient client = this;
final long uploadIntervalMillis = this.activeSettings.getAgentUploadIntervalInSeconds() * 1000;
Runnable runnable = new Runnable() {
public void run() {
if (isInitialized && isActive)
{
if (!client.isPaused()) {
sExecutor.execute(new UploadDataTask(client));
// schedule the upload for the next interval
sendMetricsHandler.postDelayed(this, uploadIntervalMillis);
} else {
// monitoring is paused and our send loop will terminate on its own
}
} else {
Log.i(ClientLog.TAG_MONITORING_CLIENT, "Configuration was not able to initialize. Not initiating metrics send loop");
}
}
};
// Start the automatic sending of data
sendMetricsHandler.postDelayed(runnable, uploadIntervalMillis);
Log.v(ClientLog.TAG_MONITORING_CLIENT, "Initiating data to be sent on a regular interval");
}
/**
* Retrieves boolean indicator of whether the device is connected to a network
*
* @return boolean indicator
*/
public boolean isDeviceNetworkConnected()
{
boolean networkConnected = true; // assume so
try {
ConnectivityManager connectivityManager = (ConnectivityManager) appActivity
.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
if (networkInfo != null) {
networkConnected = networkInfo.isConnected();
}
} catch( Exception e ) {
}
return networkConnected;
}
/**
* Uploads metrics report.
*
* @return boolean indicator of wehter upload was successful
*/
public boolean uploadMetrics()
{
boolean metricsUploaded = false;
if(isInitialized && isActive)
{
if( ! sessionManager.isSessionValid() ) {
sessionManager.openSession();
}
if( isDeviceNetworkConnected() ) {
Log.i(ClientLog.TAG_MONITORING_CLIENT, "Manually uploading metrics now");
sExecutor.execute(new ForcedUploadDataTask(this));
metricsUploaded = true;
} else {
Log.i(ClientLog.TAG_MONITORING_CLIENT, "uploadMetrics called, device not connected to network");
}
} else {
Log.i(ClientLog.TAG_MONITORING_CLIENT, "Configuration was not able to initialize. Not initiating metrics send loop");
}
return metricsUploaded;
}
/**
* Refreshes the App Monitoring configuration from the server.
*
* @return boolean indicator of whether the refresh was successful
*/
public boolean refreshConfiguration(ConfigurationReloadedListener reloadListener)
{
boolean configurationUpdated = false;
if(isInitialized && isActive)
{
// are we currently connected to network?
if( isDeviceNetworkConnected() ) {
Log.i(ClientLog.TAG_MONITORING_CLIENT, "Manually refreshing configuration now");
configurationUpdated = readUpdateAndApplyConfiguration(this.originalHttpClient,
this.enableAutoUpload,
reloadListener);
} else {
Log.i(ClientLog.TAG_MONITORING_CLIENT, "refreshConfiguration called, device not connected to network");
}
} else {
Log.i(ClientLog.TAG_MONITORING_CLIENT, "Configuration was not able to initialize. Unable to refresh.");
}
return configurationUpdated;
}
/**
* Retrieves the unique device ID
*
* @return device ID
*/
public String getApigeeDeviceId()
{
String android_id = Secure.getString(
appActivity.getContentResolver(), Secure.ANDROID_ID);
return android_id;
}
/**
* @y.exclude
*/
public boolean isAbleToSendDataToServer() {
if (null != this.activeSettings) {
String orgName = this.activeSettings.getOrgName();
String appName = this.activeSettings.getAppName();
Long instaOpsAppId = this.activeSettings.getInstaOpsApplicationId();
return ((orgName != null) &&
(appName != null) &&
(instaOpsAppId != null) &&
(orgName.length() > 0) &&
(appName.length() > 0) &&
(instaOpsAppId.longValue() > 0));
}
return false;
}
/**
* @y.exclude
*/
public void onCrashReportUpload(String crashReport) {
if (listListeners != null) {
Iterator<UploadListener> iterator = listListeners.iterator();
while( iterator.hasNext() ) {
UploadListener listener = iterator.next();
listener.onUploadCrashReport(crashReport);
}
}
}
/**
* @y.exclude
*/
private class LoadRemoteConfigTask implements Runnable {
private ConfigurationReloadedListener reloadListener;
public LoadRemoteConfigTask(ConfigurationReloadedListener reloadListener) {
this.reloadListener = reloadListener;
}
@Override
public void run() {
boolean newConfigsExist = activeSettings.synchronizeConfig();
if(newConfigsExist)
{
Log.v(ClientLog.TAG_MONITORING_CLIENT, "Found a new configuration - re-initializing sub-services");
try {
activeSettings.loadLocalApplicationConfiguration();
if( this.reloadListener != null ) {
this.reloadListener.configurationReloaded();
}
isActive = allowedToSendData();
} catch (LoadConfigurationException e) {
Log.e(ClientLog.TAG_MONITORING_CLIENT, "Error trying to reload application configuration " + e.toString());
} catch (Throwable t) {
Log.e(ClientLog.TAG_MONITORING_CLIENT, "Error trying to reload application configuration " + t.toString());
}
} else {
Log.i(ClientLog.TAG_MONITORING_CLIENT, "Remote configuration same as existing configuration OR sychronization failed, hence doing nothing");
}
}
}
/**
* Task to be executed in the background
* @y.exclude
*/
private class UploadDataTask implements Runnable {
private ApigeeMonitoringClient client;
public UploadDataTask(ApigeeMonitoringClient client) {
this.client = client;
}
@Override
public void run() {
if (!client.isAbleToSendDataToServer()) {
Log.d(ClientLog.TAG_MONITORING_CLIENT, "missing app identification - unable to send data to server");
Log.d(ClientLog.TAG_MONITORING_CLIENT, "attempting to retrieve configuration from server");
client.refreshConfiguration(null);
}
if (client.isAbleToSendDataToServer()) {
//this is a bit of a hack to prevent data from being uploaded if there is no data to upload.
//this is common if the app has been put into the background
if (log.haveLogRecords() || collector.haveSamples()) {
Log.v(ClientLog.TAG_MONITORING_CLIENT, "There are metrics to send. Sending metrics now");
List<UploadListener> listListeners = client.getMetricsUploadListeners();
uploadService.uploadData(listListeners);
} else {
Log.v(ClientLog.TAG_MONITORING_CLIENT, "No metrics to send. Skipping metrics sending");
}
} else {
Log.d(ClientLog.TAG_MONITORING_CLIENT, "unable to send data to server - no app identification");
}
}
}
/**
* Task to be executed in the background
* @y.exclude
*/
private class ForcedUploadDataTask implements Runnable {
private ApigeeMonitoringClient client;
public ForcedUploadDataTask(ApigeeMonitoringClient client) {
this.client = client;
}
@Override
public void run() {
Log.v(ClientLog.TAG_MONITORING_CLIENT, "Sending Metrics via Android Client");
List<UploadListener> listListeners = null;
if( client != null ) {
listListeners = client.getMetricsUploadListeners();
}
uploadService.uploadData(listListeners);
}
}
/*
* Task to be executed in the background
* @y.exclude
*/
private class CrashManagerTask implements Runnable {
private ApigeeMonitoringClient client;
public CrashManagerTask(ApigeeMonitoringClient client) {
this.client = client;
}
@Override
public void run() {
if(CrashManager.appIdentification == null)
{
CrashManager.register(appActivity, log, appIdentification, client);
}
}
}
/**
* Returns an instance of the HttpClient class being used
* by the Monitoring Client
*
* @return the httpClient
*/
public HttpClient getHttpClient() {
if (isInitialized && isActive) {
return httpClient;
} else {
return originalHttpClient;
}
}
/**
* This method instruments http clients that is passed. This method is
* useful when there is very complex initialization of the HTTP Client or if
* the HTTP Client is a custom HTTP client
*
* @return the httpClient
*/
public HttpClient getInstrumentedHttpClient(HttpClient client) {
if (isInitialized && isActive)
return new HttpClientWrapper(client, appIdentification, collector, activeSettings);
else
return client;
}
/**
* Gets the default Android Logger
*
* @return Android Logger
*/
public Logger getAndroidLogger() {
if ((log != null) && isInitialized && isActive) {
return log;
} else {
return defaultLogger;
}
}
/**
* @y.exclude
*/
public ApigeeActiveSettings getActiveSettings() {
return this.activeSettings;
}
/**
* @y.exclude
*/
public void setUploadService(MetricsUploadService uploadService) {
this.uploadService = uploadService;
}
/**
* @y.exclude
*/
public MetricsUploadService getUploadService() {
return uploadService;
}
/**
* @y.exclude
*/
public NetworkMetricsCollectorService getMetricsCollectorService() {
return collector;
}
/**
* Checks if the AppMonitoring client has been initialized
*
* @return boolean indicator
*/
public boolean isInitialized() {
return isInitialized;
}
/**
* @y.exclude
*/
public void onUserInteraction() {
if( isInitialized ) {
if( !isActive ) {
isActive = true;
if( sessionManager != null ) {
sessionManager.resume();
}
}
if( sessionManager != null ) {
if( sessionManager.isSessionValid() ) {
//Log.v(ClientLog.TAG_MONITORING_CLIENT,"updating session activity time");
sessionManager.onUserInteraction();
} else {
Log.d(ClientLog.TAG_MONITORING_CLIENT,"creating new session");
sessionManager.openSession();
}
}
}
}
/**
* @y.exclude
*/
public void onSessionTimeout(String sessionUUID,Date sessionStartTime,Date sessionLastActivityTime) {
log.flush();
collector.flush();
//android.util.Log.i(ClientLog.TAG_MONITORING_CLIENT,"notified that session timed out");
if( isInitialized && isActive )
{
// start a new session
sessionManager.openSession();
}
}
/**
* @y.exclude
*/
public synchronized boolean addMetricsUploadListener(UploadListener metricsUploadListener) {
boolean listenerAdded = false;
if( this.listListeners != null ) {
this.listListeners.add(metricsUploadListener);
listenerAdded = true;
}
return listenerAdded;
}
/**
* @y.exclude
*/
public synchronized boolean removeMetricsUploadListener(UploadListener metricsUploadListener) {
boolean listenerRemoved = false;
if( this.listListeners != null ) {
listenerRemoved = this.listListeners.remove(metricsUploadListener);
}
return listenerRemoved;
}
/**
* @y.exclude
*/
public ArrayList<UploadListener> getMetricsUploadListeners() {
return this.listListeners;
}
/**
* @y.exclude
*/
public static String getDeviceModel() {
return Build.MODEL;
}
/**
* @y.exclude
*/
public static String getDeviceType() {
return Build.TYPE;
}
/**
* @y.exclude
*/
public static String getDeviceOSVersion() {
return Build.VERSION.RELEASE;
}
/**
* @y.exclude
*/
public static String getDevicePlatform() {
return ApigeeClient.SDK_TYPE;
}
/**
* @y.exclude
*/
public static String getSDKVersion() {
return ApigeeClient.SDK_VERSION;
}
/**
* @y.exclude
*/
public String getUniqueIdentifierForApp() {
return appIdentification.getUniqueIdentifier();
}
public AppIdentification getAppIdentification() {
return appIdentification;
}
public SessionManager getSessionManager() {
return sessionManager;
}
}