summaryrefslogtreecommitdiffstats
path: root/mobile/android/thirdparty/com/adjust/sdk/PackageHandler.java
diff options
context:
space:
mode:
Diffstat (limited to 'mobile/android/thirdparty/com/adjust/sdk/PackageHandler.java')
-rw-r--r--mobile/android/thirdparty/com/adjust/sdk/PackageHandler.java274
1 files changed, 274 insertions, 0 deletions
diff --git a/mobile/android/thirdparty/com/adjust/sdk/PackageHandler.java b/mobile/android/thirdparty/com/adjust/sdk/PackageHandler.java
new file mode 100644
index 000000000..d0a84ccd1
--- /dev/null
+++ b/mobile/android/thirdparty/com/adjust/sdk/PackageHandler.java
@@ -0,0 +1,274 @@
+//
+// PackageHandler.java
+// Adjust
+//
+// Created by Christian Wellenbrock on 2013-06-25.
+// Copyright (c) 2013 adjust GmbH. All rights reserved.
+// See the file MIT-LICENSE for copying permission.
+//
+
+package com.adjust.sdk;
+
+import android.content.Context;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+
+import org.json.JSONObject;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.NotSerializableException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.OptionalDataException;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+// persistent
+public class PackageHandler extends HandlerThread implements IPackageHandler {
+ private static final String PACKAGE_QUEUE_FILENAME = "AdjustIoPackageQueue";
+
+ private final InternalHandler internalHandler;
+ private IRequestHandler requestHandler;
+ private IActivityHandler activityHandler;
+ private List<ActivityPackage> packageQueue;
+ private AtomicBoolean isSending;
+ private boolean paused;
+ private Context context;
+ private ILogger logger;
+
+ public PackageHandler(IActivityHandler activityHandler,
+ Context context,
+ boolean startPaused) {
+ super(Constants.LOGTAG, MIN_PRIORITY);
+ setDaemon(true);
+ start();
+ this.internalHandler = new InternalHandler(getLooper(), this);
+ this.logger = AdjustFactory.getLogger();
+
+ init(activityHandler, context, startPaused);
+
+ Message message = Message.obtain();
+ message.arg1 = InternalHandler.INIT;
+ internalHandler.sendMessage(message);
+ }
+
+ @Override
+ public void init(IActivityHandler activityHandler, Context context, boolean startPaused) {
+ this.activityHandler = activityHandler;
+ this.context = context;
+ this.paused = startPaused;
+ }
+
+ // add a package to the queue
+ @Override
+ public void addPackage(ActivityPackage pack) {
+ Message message = Message.obtain();
+ message.arg1 = InternalHandler.ADD;
+ message.obj = pack;
+ internalHandler.sendMessage(message);
+ }
+
+ // try to send the oldest package
+ @Override
+ public void sendFirstPackage() {
+ Message message = Message.obtain();
+ message.arg1 = InternalHandler.SEND_FIRST;
+ internalHandler.sendMessage(message);
+ }
+
+ // remove oldest package and try to send the next one
+ // (after success or possibly permanent failure)
+ @Override
+ public void sendNextPackage() {
+ Message message = Message.obtain();
+ message.arg1 = InternalHandler.SEND_NEXT;
+ internalHandler.sendMessage(message);
+ }
+
+ // close the package to retry in the future (after temporary failure)
+ @Override
+ public void closeFirstPackage() {
+ isSending.set(false);
+ }
+
+ // interrupt the sending loop after the current request has finished
+ @Override
+ public void pauseSending() {
+ paused = true;
+ }
+
+ // allow sending requests again
+ @Override
+ public void resumeSending() {
+ paused = false;
+ }
+
+ // short info about how failing packages are handled
+ @Override
+ public String getFailureMessage() {
+ return "Will retry later.";
+ }
+
+ @Override
+ public void finishedTrackingActivity(JSONObject jsonResponse) {
+ activityHandler.finishedTrackingActivity(jsonResponse);
+ }
+
+ @Override
+ public void sendClickPackage(ActivityPackage clickPackage) {
+ logger.debug("Sending click package (%s)", clickPackage);
+ logger.verbose("%s", clickPackage.getExtendedString());
+ requestHandler.sendClickPackage(clickPackage);
+ }
+
+ private static final class InternalHandler extends Handler {
+ private static final int INIT = 1;
+ private static final int ADD = 2;
+ private static final int SEND_NEXT = 3;
+ private static final int SEND_FIRST = 4;
+
+ private final WeakReference<PackageHandler> packageHandlerReference;
+
+ protected InternalHandler(Looper looper, PackageHandler packageHandler) {
+ super(looper);
+ this.packageHandlerReference = new WeakReference<PackageHandler>(packageHandler);
+ }
+
+ @Override
+ public void handleMessage(Message message) {
+ super.handleMessage(message);
+
+ PackageHandler packageHandler = packageHandlerReference.get();
+ if (null == packageHandler) {
+ return;
+ }
+
+ switch (message.arg1) {
+ case INIT:
+ packageHandler.initInternal();
+ break;
+ case ADD:
+ ActivityPackage activityPackage = (ActivityPackage) message.obj;
+ packageHandler.addInternal(activityPackage);
+ break;
+ case SEND_FIRST:
+ packageHandler.sendFirstInternal();
+ break;
+ case SEND_NEXT:
+ packageHandler.sendNextInternal();
+ break;
+ }
+ }
+ }
+
+ // internal methods run in dedicated queue thread
+
+ private void initInternal() {
+ requestHandler = AdjustFactory.getRequestHandler(this);
+
+ isSending = new AtomicBoolean();
+
+ readPackageQueue();
+ }
+
+ private void addInternal(ActivityPackage newPackage) {
+ packageQueue.add(newPackage);
+ logger.debug("Added package %d (%s)", packageQueue.size(), newPackage);
+ logger.verbose("%s", newPackage.getExtendedString());
+
+ writePackageQueue();
+ }
+
+ private void sendFirstInternal() {
+ if (packageQueue.isEmpty()) {
+ return;
+ }
+
+ if (paused) {
+ logger.debug("Package handler is paused");
+ return;
+ }
+ if (isSending.getAndSet(true)) {
+ logger.verbose("Package handler is already sending");
+ return;
+ }
+
+ ActivityPackage firstPackage = packageQueue.get(0);
+ requestHandler.sendPackage(firstPackage);
+ }
+
+ private void sendNextInternal() {
+ packageQueue.remove(0);
+ writePackageQueue();
+ isSending.set(false);
+ sendFirstInternal();
+ }
+
+ private void readPackageQueue() {
+ try {
+ FileInputStream inputStream = context.openFileInput(PACKAGE_QUEUE_FILENAME);
+ BufferedInputStream bufferedStream = new BufferedInputStream(inputStream);
+ ObjectInputStream objectStream = new ObjectInputStream(bufferedStream);
+
+ try {
+ Object object = objectStream.readObject();
+ @SuppressWarnings("unchecked")
+ List<ActivityPackage> packageQueue = (List<ActivityPackage>) object;
+ logger.debug("Package handler read %d packages", packageQueue.size());
+ this.packageQueue = packageQueue;
+ return;
+ } catch (ClassNotFoundException e) {
+ logger.error("Failed to find package queue class");
+ } catch (OptionalDataException e) {
+ /* no-op */
+ } catch (IOException e) {
+ logger.error("Failed to read package queue object");
+ } catch (ClassCastException e) {
+ logger.error("Failed to cast package queue object");
+ } finally {
+ objectStream.close();
+ }
+ } catch (FileNotFoundException e) {
+ logger.verbose("Package queue file not found");
+ } catch (Exception e) {
+ logger.error("Failed to read package queue file");
+ }
+
+ // start with a fresh package queue in case of any exception
+ packageQueue = new ArrayList<ActivityPackage>();
+ }
+
+ public static Boolean deletePackageQueue(Context context) {
+ return context.deleteFile(PACKAGE_QUEUE_FILENAME);
+ }
+
+
+ private void writePackageQueue() {
+ try {
+ FileOutputStream outputStream = context.openFileOutput(PACKAGE_QUEUE_FILENAME, Context.MODE_PRIVATE);
+ BufferedOutputStream bufferedStream = new BufferedOutputStream(outputStream);
+ ObjectOutputStream objectStream = new ObjectOutputStream(bufferedStream);
+
+ try {
+ objectStream.writeObject(packageQueue);
+ logger.debug("Package handler wrote %d packages", packageQueue.size());
+ } catch (NotSerializableException e) {
+ logger.error("Failed to serialize packages");
+ } finally {
+ objectStream.close();
+ }
+ } catch (Exception e) {
+ logger.error("Failed to write packages (%s)", e.getLocalizedMessage());
+ e.printStackTrace();
+ }
+ }
+}