diff options
Diffstat (limited to 'mobile/android/stumbler/java/org/mozilla/mozstumbler/service/utils')
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(); + } +} |