<commit_before>/*
 * Copyright (C) 2012 Pixmob (http://github.com/pixmob)
 *
 * 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.
 */
package org.pixmob.freemobile.netstat;

import static org.pixmob.freemobile.netstat.BuildConfig.DEBUG;
import static org.pixmob.freemobile.netstat.Constants.SP_NAME;
import static org.pixmob.freemobile.netstat.Constants.TAG;

import java.io.IOException;
import java.net.HttpURLConnection;
import java.util.Calendar;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;

import org.json.JSONException;
import org.json.JSONObject;
import org.pixmob.freemobile.netstat.content.NetstatContract.Events;
import org.pixmob.freemobile.netstat.util.DateUtils;
import org.pixmob.httpclient.HttpClient;
import org.pixmob.httpclient.HttpClientException;
import org.pixmob.httpclient.HttpResponse;
import org.pixmob.httpclient.HttpResponseHandler;

import android.app.AlarmManager;
import android.app.IntentService;
import android.app.PendingIntent;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.database.CharArrayBuffer;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Build;
import android.os.PowerManager;
import android.support.v4.util.LongSparseArray;
import android.text.format.DateFormat;
import android.util.Log;

/**
 * This background service uploads statistics.
 * @author Pixmob
 */
public class SyncService extends IntentService {
    private static final int SERVER_API_VERSION = 1;
    private static final String EXTRA_DEVICE_REG = "org.pixmob.freemobile.netstat.deviceReg";
    private static final long DAY_IN_MILLISECONDS = 86400 * 1000;
    private static final int SYNC_UPLOADED = 1;
    private static final int SYNC_PENDING = 0;
    private static String httpUserAgent;
    private SharedPreferences prefs;
    private ConnectivityManager cm;
    private PowerManager pm;
    private SQLiteOpenHelper dbHelper;

    public SyncService() {
        super("FreeMobileNetstat/Sync");
    }

    public static void schedule(Context context, boolean enabled) {
        final AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
        final PendingIntent syncIntent = PendingIntent.getService(context, 0, new Intent(context,
                SyncService.class), PendingIntent.FLAG_CANCEL_CURRENT);
        if (enabled) {
            if (DEBUG) {
                Log.d(TAG, "Scheduling synchronization");
            }
            am.setInexactRepeating(AlarmManager.RTC, System.currentTimeMillis() + 1000,
                    AlarmManager.INTERVAL_HALF_HOUR, syncIntent);
        } else {
            if (DEBUG) {
                Log.d(TAG, "Synchronization schedule canceled");
            }
            am.cancel(syncIntent);
        }
    }

    @Override
    public void onCreate() {
        super.onCreate();
        prefs = getSharedPreferences(SP_NAME, MODE_PRIVATE);
        cm = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
        pm = (PowerManager) getSystemService(POWER_SERVICE);
        dbHelper = new UploadDatabaseHelper(this);
    }

    @Override
    public void onDestroy() {
        if (dbHelper != null) {
            dbHelper.close();
        }
        super.onDestroy();
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        // Check if statistics upload is enabled.
        if (!prefs.getBoolean(Constants.SP_KEY_UPLOAD_STATS, false)) {
            Log.d(TAG, "Synchronization is disabled: skip sync");
            return;
        }

        // Check if an Internet connection is available.
        final NetworkInfo netInfo = cm.getActiveNetworkInfo();
        if (netInfo == null || !netInfo.isAvailable() || !netInfo.isConnected()) {
            Log.d(TAG, "Network connectivity is not available: skip sync");
            return;
        }

        final PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
        SQLiteDatabase db = null;
        try {
            wl.acquire();
            db = dbHelper.getWritableDatabase();
            run(intent, db);
        } catch (Exception e) {
            Log.e(TAG, "Failed to upload statistics", e);
        } finally {
            if (db != null) {<commit_msg>Use a backoff strategy for data synchronization<commit_after>/*
 * Copyright (C) 2012 Pixmob (http://github.com/pixmob)
 *
 * 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.
 */
package org.pixmob.freemobile.netstat;

import static org.pixmob.freemobile.netstat.BuildConfig.DEBUG;
import static org.pixmob.freemobile.netstat.Constants.SP_NAME;
import static org.pixmob.freemobile.netstat.Constants.TAG;

import java.io.IOException;
import java.net.HttpURLConnection;
import java.util.Calendar;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;

import org.json.JSONException;
import org.json.JSONObject;
import org.pixmob.freemobile.netstat.content.NetstatContract.Events;
import org.pixmob.freemobile.netstat.util.DateUtils;
import org.pixmob.httpclient.HttpClient;
import org.pixmob.httpclient.HttpClientException;
import org.pixmob.httpclient.HttpResponse;
import org.pixmob.httpclient.HttpResponseHandler;

import android.app.AlarmManager;
import android.app.IntentService;
import android.app.PendingIntent;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.database.CharArrayBuffer;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Build;
import android.os.PowerManager;
import android.support.v4.util.LongSparseArray;
import android.text.format.DateFormat;
import android.util.Log;

/**
 * This background service synchronizes data with a remote server.
 * @author Pixmob
 */
public class SyncService extends IntentService {
    private static final int SERVER_API_VERSION = 1;
    private static final String EXTRA_DEVICE_REG = "org.pixmob.freemobile.netstat.deviceReg";
    private static final long DAY_IN_MILLISECONDS = 86400 * 1000;
    private static final int SYNC_UPLOADED = 1;
    private static final int SYNC_PENDING = 0;
    private static final int MAX_SYNC_ERRORS = 4;
    private static final String INTERNAL_SP_NAME = "sync";
    private static final String INTERNAL_SP_KEY_SYNC_ERRORS = "syncErrors";
    private static String httpUserAgent;
    private SharedPreferences prefs;
    private SharedPreferences internalPrefs;
    private SharedPreferences.Editor internalPrefsEditor;
    private ConnectivityManager cm;
    private PowerManager pm;
    private SQLiteOpenHelper dbHelper;

    public SyncService() {
        super("FreeMobileNetstat/Sync");
    }

    public static void schedule(Context context, boolean enabled) {
        final AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
        final PendingIntent syncIntent = PendingIntent.getService(context, 0, new Intent(context,
                SyncService.class), PendingIntent.FLAG_CANCEL_CURRENT);
        if (enabled) {
            // Set the sync period.
            long period = AlarmManager.INTERVAL_HOUR;
            final int syncErrors = context.getSharedPreferences(INTERNAL_SP_NAME, MODE_PRIVATE).getInt(
                    INTERNAL_SP_KEY_SYNC_ERRORS, 0);
            if (syncErrors != 0) {
                // When there was a sync error, the sync period is longer.
                period = AlarmManager.INTERVAL_HOUR * Math.min(syncErrors, MAX_SYNC_ERRORS);
            }

            if (DEBUG) {
                Log.d(TAG, "Scheduling synchronization: every " + period / 1000 / 60 + " minutes");
            }
            am.setInexactRepeating(AlarmManager.RTC, period, period, syncIntent);
        } else {
            if (DEBUG) {
                Log.d(TAG, "Synchronization schedule canceled");
            }
            am.cancel(syncIntent);
        }
    }

    @Override
    public void onCreate() {
        super.onCreate();
        prefs = getSharedPreferences(SP_NAME, MODE_PRIVATE);
        internalPrefs = getSharedPreferences(INTERNAL_SP_NAME, MODE_PRIVATE);
        internalPrefsEditor = internalPrefs.edit();
        cm = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
        pm = (PowerManager) getSystemService(POWER_SERVICE);
        dbHelper = new UploadDatabaseHelper(this);
    }

    @Override
    public void onDestroy() {
        if (dbHelper != null) {
            dbHelper.close();
        }
        super.onDestroy();
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        // Check if statistics upload is enabled.
        if (!prefs.getBoolean(Constants.SP_KEY_UPLOAD_STATS, false)) {
            Log.d(TAG, "Synchronization is disabled: skip sync");
            return;
        }

        // Check if an Internet connection is available.
        final NetworkInfo netInfo = cm.getActiveNetworkInfo();
        if (netInfo == null || !netInfo.isAvailable() || !netInfo.isConnected()) {
            Log.d(TAG, "Network connectivity is not available: skip sync");
            return;
        }

        final PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
        SQLiteDatabase db = null;
        try {
            wl.acquire();
            db = dbHelper.getWritableDatabase();
            run(intent, db);

            // Sync was successful: reset sync error count.
            internalPrefsEditor.remove(INTERNAL_SP_KEY_SYNC_ERRORS).commit();
        } catch (Exception e) {
            Log.e(TAG, "Failed to upload statistics", e);

            // Increment sync errors.
            final int syncErrors = internalPrefs.getInt(INTERNAL_SP_KEY_SYNC_ERRORS, 0);
            internalPrefsEditor.putInt(INTERNAL_SP_KEY_SYNC_ERRORS, syncErrors + 1).commit();

            // Reschedule this service according to the sync error count.
            schedule(this, true);
        } finally {
            if (db != null) {
{'fraction_non_alphanumeric': 0.08834408602150538, 'fraction_numerical': 0.005075268817204301, 'mean_word_length': 3.2047016274864375, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7385675', 'n_tokens_mistral': 3284, 'n_tokens_neox': 3206, 'n_words': 839}