summaryrefslogtreecommitdiffstats
path: root/mobile/android/stumbler/java/org/mozilla/mozstumbler/service/utils
diff options
context:
space:
mode:
Diffstat (limited to 'mobile/android/stumbler/java/org/mozilla/mozstumbler/service/utils')
-rw-r--r--mobile/android/stumbler/java/org/mozilla/mozstumbler/service/utils/AbstractCommunicator.java158
-rw-r--r--mobile/android/stumbler/java/org/mozilla/mozstumbler/service/utils/NetworkUtils.java32
-rw-r--r--mobile/android/stumbler/java/org/mozilla/mozstumbler/service/utils/PersistentIntentService.java85
-rw-r--r--mobile/android/stumbler/java/org/mozilla/mozstumbler/service/utils/TelemetryWrapper.java35
-rw-r--r--mobile/android/stumbler/java/org/mozilla/mozstumbler/service/utils/Zipper.java48
5 files changed, 358 insertions, 0 deletions
diff --git a/mobile/android/stumbler/java/org/mozilla/mozstumbler/service/utils/AbstractCommunicator.java b/mobile/android/stumbler/java/org/mozilla/mozstumbler/service/utils/AbstractCommunicator.java
new file mode 100644
index 000000000..70816371a
--- /dev/null
+++ b/mobile/android/stumbler/java/org/mozilla/mozstumbler/service/utils/AbstractCommunicator.java
@@ -0,0 +1,158 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.mozstumbler.service.utils;
+
+import android.os.Build;
+import android.util.Log;
+
+import org.mozilla.mozstumbler.service.AppGlobals;
+import org.mozilla.mozstumbler.service.Prefs;
+
+import java.io.BufferedOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+public abstract class AbstractCommunicator {
+
+ private static final String LOG_TAG = AppGlobals.makeLogTag(AbstractCommunicator.class.getSimpleName());
+ private static final String NICKNAME_HEADER = "X-Nickname";
+ private static final String USER_AGENT_HEADER = "User-Agent";
+ private HttpURLConnection mHttpURLConnection;
+ private final String mUserAgent;
+ private static int sBytesSentTotal = 0;
+ private static String sMozApiKey;
+
+ public abstract String getUrlString();
+
+ public static class HttpErrorException extends IOException {
+ private static final long serialVersionUID = -5404095858043243126L;
+ public final int responseCode;
+
+ public HttpErrorException(int responseCode) {
+ super();
+ this.responseCode = responseCode;
+ }
+
+ public boolean isTemporary() {
+ return responseCode >= 500 && responseCode <= 599;
+ }
+ }
+
+ public static class SyncSummary {
+ public int numIoExceptions;
+ public int totalBytesSent;
+ }
+
+ public static class NetworkSendResult {
+ public int bytesSent;
+ // Zero is no error, for HTTP error cases, set this code to the error
+ public int errorCode = -1;
+ }
+
+ public abstract NetworkSendResult cleanSend(byte[] data);
+
+ public String getNickname() {
+ return null;
+ }
+
+ public AbstractCommunicator() {
+ Prefs prefs = Prefs.getInstanceWithoutContext();
+ mUserAgent = (prefs != null)? prefs.getUserAgent() : "fennec-stumbler-unset-user-agent";
+ }
+
+ private void openConnectionAndSetHeaders() {
+ try {
+ Prefs prefs = Prefs.getInstanceWithoutContext();
+ if (sMozApiKey == null || prefs != null) {
+ sMozApiKey = prefs.getMozApiKey();
+ }
+ URL url = new URL(getUrlString() + "?key=" + sMozApiKey);
+ mHttpURLConnection = (HttpURLConnection) url.openConnection();
+ mHttpURLConnection.setRequestMethod("POST");
+ } catch (MalformedURLException e) {
+ throw new IllegalArgumentException(e);
+ } catch (IOException e) {
+ Log.e(LOG_TAG, "Couldn't open a connection: " + e);
+ }
+ mHttpURLConnection.setDoOutput(true);
+ mHttpURLConnection.setRequestProperty(USER_AGENT_HEADER, mUserAgent);
+ mHttpURLConnection.setRequestProperty("Content-Type", "application/json");
+
+ // Workaround for a bug in Android mHttpURLConnection. When the library
+ // reuses a stale connection, the connection may fail with an EOFException
+ if (Build.VERSION.SDK_INT > 13 && Build.VERSION.SDK_INT < 19) {
+ mHttpURLConnection.setRequestProperty("Connection", "Close");
+ }
+ String nickname = getNickname();
+ if (nickname != null) {
+ mHttpURLConnection.setRequestProperty(NICKNAME_HEADER, nickname);
+ }
+ }
+
+ private byte[] zipData(byte[] data) throws IOException {
+ byte[] output = Zipper.zipData(data);
+ return output;
+ }
+
+ private void sendData(byte[] data) throws IOException{
+ mHttpURLConnection.setFixedLengthStreamingMode(data.length);
+ OutputStream out = new BufferedOutputStream(mHttpURLConnection.getOutputStream());
+ out.write(data);
+ out.flush();
+ int code = mHttpURLConnection.getResponseCode();
+ final boolean isSuccessCode2XX = (code/100 == 2);
+ if (!isSuccessCode2XX) {
+ throw new HttpErrorException(code);
+ }
+ }
+
+ public enum ZippedState { eNotZipped, eAlreadyZipped };
+ /* Return the number of bytes sent. */
+ public int send(byte[] data, ZippedState isAlreadyZipped) throws IOException {
+ openConnectionAndSetHeaders();
+ String logMsg;
+ try {
+ if (isAlreadyZipped != ZippedState.eAlreadyZipped) {
+ data = zipData(data);
+ }
+ mHttpURLConnection.setRequestProperty("Content-Encoding","gzip");
+ } catch (IOException e) {
+ Log.e(LOG_TAG, "Couldn't compress and send data, falling back to plain-text: ", e);
+ close();
+ }
+
+ try {
+ sendData(data);
+ } finally {
+ close();
+ }
+ sBytesSentTotal += data.length;
+ logMsg = "Send data: " + String.format("%.2f", data.length / 1024.0) + " kB";
+ logMsg += " Session Total:" + String.format("%.2f", sBytesSentTotal / 1024.0) + " kB";
+ AppGlobals.guiLogInfo(logMsg, "#FFFFCC", true);
+ Log.d(LOG_TAG, logMsg);
+ return data.length;
+ }
+
+ public InputStream getInputStream() {
+ try {
+ return mHttpURLConnection.getInputStream();
+ } catch (IOException e) {
+ return mHttpURLConnection.getErrorStream();
+ }
+ }
+
+ public void close() {
+ if (mHttpURLConnection == null) {
+ return;
+ }
+ mHttpURLConnection.disconnect();
+ mHttpURLConnection = null;
+ }
+}
diff --git a/mobile/android/stumbler/java/org/mozilla/mozstumbler/service/utils/NetworkUtils.java b/mobile/android/stumbler/java/org/mozilla/mozstumbler/service/utils/NetworkUtils.java
new file mode 100644
index 000000000..b3b33b02a
--- /dev/null
+++ b/mobile/android/stumbler/java/org/mozilla/mozstumbler/service/utils/NetworkUtils.java
@@ -0,0 +1,32 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.mozstumbler.service.utils;
+
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.util.Log;
+import org.mozilla.mozstumbler.service.AppGlobals;
+
+public final class NetworkUtils {
+ private static final String LOG_TAG = AppGlobals.makeLogTag(NetworkUtils.class.getSimpleName());
+
+ ConnectivityManager mConnectivityManager;
+
+ public NetworkUtils(Context context) {
+ mConnectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
+ }
+
+ public synchronized boolean isWifiAvailable() {
+ if (mConnectivityManager == null) {
+ Log.e(LOG_TAG, "ConnectivityManager is null!");
+ return false;
+ }
+
+ NetworkInfo aNet = mConnectivityManager.getActiveNetworkInfo();
+ return (aNet != null && aNet.getType() == ConnectivityManager.TYPE_WIFI);
+ }
+
+}
diff --git a/mobile/android/stumbler/java/org/mozilla/mozstumbler/service/utils/PersistentIntentService.java b/mobile/android/stumbler/java/org/mozilla/mozstumbler/service/utils/PersistentIntentService.java
new file mode 100644
index 000000000..8387c7edd
--- /dev/null
+++ b/mobile/android/stumbler/java/org/mozilla/mozstumbler/service/utils/PersistentIntentService.java
@@ -0,0 +1,85 @@
+package org.mozilla.mozstumbler.service.utils;
+
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * 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.
+ */
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+
+/* This code is copied from android IntentService, with stopSelf commented out. */
+public abstract class PersistentIntentService extends Service {
+ private volatile Looper mServiceLooper;
+ private volatile ServiceHandler mServiceHandler;
+ private final String mName;
+ private boolean mRedelivery;
+
+ private final class ServiceHandler extends Handler {
+ public ServiceHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ onHandleIntent((Intent) msg.obj);
+ // stopSelf(msg.arg1); <-- modified from original file
+ }
+ }
+
+ public PersistentIntentService(String name) {
+ super();
+ mName = name;
+ }
+
+ public void setIntentRedelivery(boolean enabled) {
+ mRedelivery = enabled;
+ }
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
+ thread.start();
+ mServiceLooper = thread.getLooper();
+ mServiceHandler = new ServiceHandler(mServiceLooper);
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ Message msg = mServiceHandler.obtainMessage();
+ msg.arg1 = startId;
+ msg.obj = intent;
+ mServiceHandler.sendMessage(msg);
+ return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
+ }
+
+ @Override
+ public void onDestroy() {
+ mServiceLooper.quit();
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return null;
+ }
+
+ protected abstract void onHandleIntent(Intent intent);
+}
+
diff --git a/mobile/android/stumbler/java/org/mozilla/mozstumbler/service/utils/TelemetryWrapper.java b/mobile/android/stumbler/java/org/mozilla/mozstumbler/service/utils/TelemetryWrapper.java
new file mode 100644
index 000000000..91cde26f2
--- /dev/null
+++ b/mobile/android/stumbler/java/org/mozilla/mozstumbler/service/utils/TelemetryWrapper.java
@@ -0,0 +1,35 @@
+package org.mozilla.mozstumbler.service.utils;
+
+import android.util.Log;
+import org.mozilla.mozstumbler.service.AppGlobals;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+public class TelemetryWrapper {
+ private static final String LOG_TAG = AppGlobals.makeLogTag(TelemetryWrapper.class.getSimpleName());
+ private static Method mAddToHistogram;
+
+ public static void addToHistogram(String key, int value) {
+ if (mAddToHistogram == null) {
+ try {
+ Class<?> telemetry = Class.forName("org.mozilla.gecko.Telemetry");
+ mAddToHistogram = telemetry.getMethod("addToHistogram", String.class, int.class);
+ } catch (ClassNotFoundException e) {
+ Log.d(LOG_TAG, "Class not found!");
+ return;
+ } catch (NoSuchMethodException e) {
+ Log.d(LOG_TAG, "Method not found!");
+ return;
+ }
+ }
+
+ if (mAddToHistogram != null) {
+ try {
+ mAddToHistogram.invoke(null, key, value);
+ }
+ catch (IllegalArgumentException | InvocationTargetException | IllegalAccessException e) {
+ Log.d(LOG_TAG, "Got exception invoking.");
+ }
+ }
+ }
+}
diff --git a/mobile/android/stumbler/java/org/mozilla/mozstumbler/service/utils/Zipper.java b/mobile/android/stumbler/java/org/mozilla/mozstumbler/service/utils/Zipper.java
new file mode 100644
index 000000000..90e0ee7f5
--- /dev/null
+++ b/mobile/android/stumbler/java/org/mozilla/mozstumbler/service/utils/Zipper.java
@@ -0,0 +1,48 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.mozstumbler.service.utils;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.zip.GZIPInputStream;
+import java.util.zip.GZIPOutputStream;
+
+public class Zipper {
+ public static byte[] zipData(byte[] data) throws IOException {
+ final ByteArrayOutputStream os = new ByteArrayOutputStream();
+ GZIPOutputStream gstream = new GZIPOutputStream(os);
+ byte[] output;
+ try {
+ gstream.write(data);
+ gstream.finish();
+ output = os.toByteArray();
+ } finally {
+ gstream.close();
+ os.close();
+ }
+ return output;
+ }
+
+ public static String unzipData(byte[] data) throws IOException {
+ StringBuilder result = new StringBuilder();
+ final ByteArrayInputStream bs = new ByteArrayInputStream(data);
+ GZIPInputStream gstream = new GZIPInputStream(bs);
+ try {
+ InputStreamReader reader = new InputStreamReader(gstream);
+ BufferedReader in = new BufferedReader(reader);
+ String read;
+ while ((read = in.readLine()) != null) {
+ result.append(read);
+ }
+ } finally {
+ gstream.close();
+ bs.close();
+ }
+ return result.toString();
+ }
+}