<commit_before>package com.radiusnetworks.bluetooth;

/**
 *  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.annotation.TargetApi;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.AsyncTask;
import android.util.Log;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;

/**
 * This class provides relief for Android Bug 67272.  This bug in the Bluedroid stack causes crashes
 * in Android's BluetoothService when scanning for BLE devices encounters a large number of unique
 * devices.  It is rare for most users but can be problematic for those with apps scanning for
 * Bluetooth LE devices in the background (e.g. iBeacon-enabled apps), especially when these users
 * are around Bluetooth LE devices that randomize their mac address like Gimbal beacons.
 * This class can both recover from crashes and prevent crashes from happening in the first place
 * More details on the bug can be found at the following URLs:
 * https://code.google.com/p/android/issues/detail?id=67272
 * https://github.com/RadiusNetworks/android-ibeacon-service/issues/16
 * Version 1.0
 * Created by dyoung on 3/24/14.
 */

@TargetApi(5)
@SuppressWarnings("ALL")
public class BluetoothCrashResolver {
    private static final String TAG = "BluetoothCrashResolver";
    private static final boolean PREEMPTIVE_ACTION_ENABLED = true;
    /**
     * This is not the same file that bluedroid uses.  This is just to maintain state of this module
     */
    private static final String DISTINCT_BLUETOOTH_ADDRESSES_FILE = "BluetoothCrashResolverState.txt";
    private static final long MIN_TIME_BETWEEN_STATE_SAVES_MILLIS = 60000l;
    /**
     * It is very likely a crash if Bluetooth turns off and comes
     * back on in an extremely short interval.  Testing on a Nexus 4 shows
     * that when the BluetoothService crashes, the time between the STATE_OFF
     * and the STATE_TURNING_ON ranges from 0ms-684ms
     * Out of 3614 samples:
     * 99.4% (3593) < 600 ms
     * 84.7% (3060) < 500 ms
     * So we will assume any power off sequence of < 600ms to be a crash
     * <p/>
     * While it is possible to manually turn bluetooth off then back on in
     * about 600ms, but it is pretty hard to do.
     */
    private static final long SUSPICIOUSLY_SHORT_BLUETOOTH_OFF_INTERVAL_MILLIS = 600l;
    /**
     * The Bluedroid stack can only track only 1990 unique Bluetooth mac addresses without crashing
     */
    private static final int BLUEDROID_MAX_BLUETOOTH_MAC_COUNT = 1990;
    /**
     * The discovery process will pare back the mac address list to 256, but more may
     * be found in the time we let the discovery process run, depending hon how many BLE
     * devices are around.
     */
    private static final int BLUEDROID_POST_DISCOVERY_ESTIMATED_BLUETOOTH_MAC_COUNT = 400;
    /**
     * It takes a little over 2 seconds after discovery is started before the pared-down mac file
     * is written to persistent storage.  We let discovery run for a few more seconds just to be
     * sure.
     */
    private static final int TIME_TO_LET_DISCOVERY_RUN_MILLIS = 5000;  /* if 0, it means forever */
    private boolean debugEnabled = false;
    private boolean recoveryInProgress = false;
    private boolean discoveryStartConfirmed = false;
    private long lastBluetoothOffTime = 0l;
    private long lastBluetoothTurningOnTime = 0l;
    private final BroadcastReceiver receiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();

            if (action.equals(BluetoothAdapter.ACTION_DISCOVERY_FINISHED)) {
                if (recoveryInProgress) {
                    if (isDebugEnabled()) Log.d(TAG, "Bluetooth discovery finished");
                    finishRecovery();
                } else {
                    if (isDebugEnabled()) Log.d(TAG, "Bluetooth discovery finished (external)");
                }
            }
            if (action.equals(BluetoothAdapter.ACTION_DISCOVERY_STARTED)) {
                if (recoveryInProgress) {
                    discoveryStartConfirmed = true;
                    if (isDebugEnabled()) Log.d(TAG, "Bluetooth discovery started");
                } else {
                    if (isDebugEnabled()) Log.d(TAG, "Bluetooth discovery started (external)");
                }
            }

            if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
                final int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,
                        BluetoothAdapter.ERROR);
                switch (state) {
                    case BluetoothAdapter.ERROR:
                        if (isDebugEnabled()) Log.d(TAG, "Bluetooth state is ERROR");
                        break;
                    case BluetoothAdapter.STATE_OFF:
                        if (isDebugEnabled()) Log.d(TAG, "Bluetooth state is OFF");
                        lastBluetoothOffTime = new Date().getTime();
                        break;
                    case BluetoothAdapter.STATE_TURNING_OFF:
                        break;
                    case BluetoothAdapter.STATE_ON:
                        if (isDebugEnabled()) Log.d(TAG, "Bluetooth state is ON");
                        if (isDebugEnabled())
                            Log.d(TAG, "Bluetooth was turned off for " + (lastBluetoothTurningOnTime - lastBluetoothOffTime) + " milliseconds");
                        if (lastBluetoothTurningOnTime - lastBluetoothOffTime < SUSPICIOUSLY_SHORT_BLUETOOTH_OFF_INTERVAL_MILLIS) {
                            crashDetected();
                        }
                        break;
                    case BluetoothAdapter.STATE_TURNING_ON:
                        lastBluetoothTurningOnTime = new Date().getTime();
                        if (isDebugEnabled()) Log.d(TAG, "Bluetooth state is TURNING_ON");
                        break;
                }
            }
        }
    };
    private long lastBluetoothCrashDetectionTime = 0l;
    private int detectedCrashCount = 0;
    private int recoveryAttemptCount = 0;
    private boolean lastRecoverySucceeded = false;
    private long lastStateSaveTime = 0l;
    private Context context = null;
    private UpdateNotifier updateNotifier;
    private Set<String> distinctBluetoothAddresses = new HashSet<String>();
    private DiscoveryCanceller discoveryCanceller = new DiscoveryCanceller();

    /**
     * Constructor should be called only once per long-running process that does Bluetooth LE
     * scanning.  Must call start() to make it do anything.
     *
     * @param context the Activity or Service that is doing the Bluetooth scanning
     */
    public BluetoothCrashResolver(Context context) {
        this.context = context.getApplicationContext();
        if (isDebugEnabled()) Log.d(TAG, "constructed");
        loadState();
    }

    /**
     * Starts looking for crashes of the Bluetooth LE system and taking proactive steps to stop
     * crashes from happening.  Proactive steps require calls to notifyScannedDevice(Device device)
     * so that crashes can be predicted ahead of time.
     */
    public void start() {
        IntentFilter filter = new IntentFilter();
        filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
        filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
        filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
        context.registerReceiver(receiver, filter);

        if (isDebugEnabled()) Log.d(TAG, "started listening for BluetoothAdapter events");
    }
<commit_msg>Reformat code using Android Studio 1.2 Beta 3<commit_after>package com.radiusnetworks.bluetooth;

/**
 * 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.annotation.TargetApi;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.AsyncTask;
import android.util.Log;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;

/**
 * This class provides relief for Android Bug 67272.  This bug in the Bluedroid stack causes crashes
 * in Android's BluetoothService when scanning for BLE devices encounters a large number of unique
 * devices.  It is rare for most users but can be problematic for those with apps scanning for
 * Bluetooth LE devices in the background (e.g. iBeacon-enabled apps), especially when these users
 * are around Bluetooth LE devices that randomize their mac address like Gimbal beacons.
 * This class can both recover from crashes and prevent crashes from happening in the first place
 * More details on the bug can be found at the following URLs:
 * https://code.google.com/p/android/issues/detail?id=67272
 * https://github.com/RadiusNetworks/android-ibeacon-service/issues/16
 * Version 1.0
 * Created by dyoung on 3/24/14.
 */

@TargetApi(5)
@SuppressWarnings("ALL")
public class BluetoothCrashResolver {
    private static final String TAG = "BluetoothCrashResolver";
    private static final boolean PREEMPTIVE_ACTION_ENABLED = true;
    /**
     * This is not the same file that bluedroid uses.  This is just to maintain state of this module
     */
    private static final String DISTINCT_BLUETOOTH_ADDRESSES_FILE = "BluetoothCrashResolverState.txt";
    private static final long MIN_TIME_BETWEEN_STATE_SAVES_MILLIS = 60000l;
    /**
     * It is very likely a crash if Bluetooth turns off and comes
     * back on in an extremely short interval.  Testing on a Nexus 4 shows
     * that when the BluetoothService crashes, the time between the STATE_OFF
     * and the STATE_TURNING_ON ranges from 0ms-684ms
     * Out of 3614 samples:
     * 99.4% (3593) < 600 ms
     * 84.7% (3060) < 500 ms
     * So we will assume any power off sequence of < 600ms to be a crash
     * <p/>
     * While it is possible to manually turn bluetooth off then back on in
     * about 600ms, but it is pretty hard to do.
     */
    private static final long SUSPICIOUSLY_SHORT_BLUETOOTH_OFF_INTERVAL_MILLIS = 600l;
    /**
     * The Bluedroid stack can only track only 1990 unique Bluetooth mac addresses without crashing
     */
    private static final int BLUEDROID_MAX_BLUETOOTH_MAC_COUNT = 1990;
    /**
     * The discovery process will pare back the mac address list to 256, but more may
     * be found in the time we let the discovery process run, depending hon how many BLE
     * devices are around.
     */
    private static final int BLUEDROID_POST_DISCOVERY_ESTIMATED_BLUETOOTH_MAC_COUNT = 400;
    /**
     * It takes a little over 2 seconds after discovery is started before the pared-down mac file
     * is written to persistent storage.  We let discovery run for a few more seconds just to be
     * sure.
     */
    private static final int TIME_TO_LET_DISCOVERY_RUN_MILLIS = 5000;  /* if 0, it means forever */
    private boolean debugEnabled = false;
    private boolean recoveryInProgress = false;
    private boolean discoveryStartConfirmed = false;
    private long lastBluetoothOffTime = 0l;
    private long lastBluetoothTurningOnTime = 0l;
    private long lastBluetoothCrashDetectionTime = 0l;
    private int detectedCrashCount = 0;
    private int recoveryAttemptCount = 0;
    private boolean lastRecoverySucceeded = false;
    private long lastStateSaveTime = 0l;
    private Context context = null;
    private UpdateNotifier updateNotifier;
    private Set<String> distinctBluetoothAddresses = new HashSet<String>();
    private DiscoveryCanceller discoveryCanceller = new DiscoveryCanceller();
    private final BroadcastReceiver receiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();

            if (action.equals(BluetoothAdapter.ACTION_DISCOVERY_FINISHED)) {
                if (recoveryInProgress) {
                    if (isDebugEnabled()) Log.d(TAG, "Bluetooth discovery finished");
                    finishRecovery();
                } else {
                    if (isDebugEnabled()) Log.d(TAG, "Bluetooth discovery finished (external)");
                }
            }
            if (action.equals(BluetoothAdapter.ACTION_DISCOVERY_STARTED)) {
                if (recoveryInProgress) {
                    discoveryStartConfirmed = true;
                    if (isDebugEnabled()) Log.d(TAG, "Bluetooth discovery started");
                } else {
                    if (isDebugEnabled()) Log.d(TAG, "Bluetooth discovery started (external)");
                }
            }

            if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
                final int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,
                        BluetoothAdapter.ERROR);
                switch (state) {
                    case BluetoothAdapter.ERROR:
                        if (isDebugEnabled()) Log.d(TAG, "Bluetooth state is ERROR");
                        break;
                    case BluetoothAdapter.STATE_OFF:
                        if (isDebugEnabled()) Log.d(TAG, "Bluetooth state is OFF");
                        lastBluetoothOffTime = new Date().getTime();
                        break;
                    case BluetoothAdapter.STATE_TURNING_OFF:
                        break;
                    case BluetoothAdapter.STATE_ON:
                        if (isDebugEnabled()) Log.d(TAG, "Bluetooth state is ON");
                        if (isDebugEnabled())
                            Log.d(TAG, "Bluetooth was turned off for " + (lastBluetoothTurningOnTime - lastBluetoothOffTime) + " milliseconds");
                        if (lastBluetoothTurningOnTime - lastBluetoothOffTime < SUSPICIOUSLY_SHORT_BLUETOOTH_OFF_INTERVAL_MILLIS) {
                            crashDetected();
                        }
                        break;
                    case BluetoothAdapter.STATE_TURNING_ON:
                        lastBluetoothTurningOnTime = new Date().getTime();
                        if (isDebugEnabled()) Log.d(TAG, "Bluetooth state is TURNING_ON");
                        break;
                }
            }
        }
    };

    /**
     * Constructor should be called only once per long-running process that does Bluetooth LE
     * scanning.  Must call start() to make it do anything.
     *
     * @param context the Activity or Service that is doing the Bluetooth scanning
     */
    public BluetoothCrashResolver(Context context) {
        this.context = context.getApplicationContext();
        if (isDebugEnabled()) Log.d(TAG, "constructed");
        loadState();
    }

    /**
     * Starts looking for crashes of the Bluetooth LE system and taking proactive steps to stop
     * crashes from happening.  Proactive steps require calls to notifyScannedDevice(Device device)
     * so that crashes can be predicted ahead of time.
     */
    public void start() {
        IntentFilter filter = new IntentFilter();
        filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
        filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
        filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
        context.registerReceiver(receiver, filter);

        if (isDebugEnabled()) Log.d(TAG, "started listening for BluetoothAdapter events");
    }

{'fraction_non_alphanumeric': 0.07033315705975675, 'fraction_numerical': 0.01110523532522475, 'mean_word_length': 2.6105218498090794, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 9, 'https://': 4, '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': '2771700', 'n_tokens_mistral': 4659, 'n_tokens_neox': 4334, 'n_words': 1523}