// Path: library/src/main/java/com/reelyactive/blesdk/support/ble/util/Clock.java
// public interface Clock {
// 
//   /**
//    * @return current time in milliseconds
//    */
//   public long currentTimeMillis();
// 
//   /**
//    * @return time since the device was booted, in nanoseconds
//    */
//   public long elapsedRealtimeNanos();
// 
// }
// 
// Path: library/src/main/java/com/reelyactive/blesdk/support/ble/util/Logger.java
// public class Logger {
// 
//   public static final String TAG = "BleScanCompatLib";
// 
//   public static void logVerbose(String message) {
//     if (Log.isLoggable(TAG, Log.VERBOSE)) {
//       Log.v(TAG, message);
//     }
//   }
// 
//   public static void logWarning(String message) {
//     if (Log.isLoggable(TAG, Log.WARN)) {
//       Log.w(TAG, message);
//     }
//   }
// 
//   public static void logDebug(String message) {
//     if (Log.isLoggable(TAG, Log.DEBUG)) {
//       Log.d(TAG, message);
//     }
//   }
// 
//   public static void logInfo(String message) {
//     if (Log.isLoggable(TAG, Log.INFO)) {
//       Log.i(TAG, message);
//     }
//   }
// 
//   public static void logError(String message, Exception... e) {
//     if (Log.isLoggable(TAG, Log.ERROR)) {
//       if (e == null || e.length == 0) {
//         Log.e(TAG, message);
//       } else {
//         Log.e(TAG, message, e[0]);
//       }
//     }
//   }
// }
// 
// Path: library/src/main/java/com/reelyactive/blesdk/support/ble/util/SystemClock.java
// public class SystemClock implements Clock {
// 
//   /**
//    * @return current time in milliseconds
//    */
//   @Override
//   public long currentTimeMillis() {
//     return System.currentTimeMillis();
//   }
// 
//   /**
//    * @return time since the device was booted, in nanoseconds
//    */
//   @Override
//   @TargetApi(VERSION_CODES.JELLY_BEAN_MR1)
//   public long elapsedRealtimeNanos() {
//     return android.os.SystemClock.elapsedRealtimeNanos();
//   }
// }


import android.annotation.TargetApi;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothManager;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import com.reelyactive.blesdk.support.ble.util.Clock;
import com.reelyactive.blesdk.support.ble.util.Logger;
import com.reelyactive.blesdk.support.ble.util.SystemClock;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
package com.reelyactive.blesdk.support.ble;/*
 * Copyright 2014 Google Inc. All rights reserved.
 *
 * 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.
 */




/**
 * Implements Bluetooth LE scan related API on top of
 * {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2} and later.
 * <p>
 * This class delivers a callback on found, updated, and lost for devices matching a
 * {@link ScanFilter} filter during scan cycles.
 * <p>
 * A scan cycle comprises a period when the Bluetooth Adapter is active and a period when the
 * Bluetooth adapter is idle. Having an idle period is energy efficient for long lived scans.
 * <p>
 * This class can be accessed on multiple threads:
 * <ul>
 * <li> main thread (user) can call any of the BluetoothLeScanner APIs
 * <li> IntentService worker thread can call {@link #onNewScanCycle()}
 * <li> AIDL binder thread can call {@link #leScanCallback.onLeScan()}
 * </ul>
 *
 * @see <a href="http://go/ble-glossary">BLE Glossary</a>
 */
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
class JbBluetoothLeScannerCompat extends BluetoothLeScannerCompat {

    // Map of BD_ADDR->com.reelyactive.blesdk.support.ble.ScanResult for replay to new registrations.
    // Entries are evicted after SCAN_LOST_CYCLES cycles.
  /* @VisibleForTesting */ final Map<String, ScanResult> recentScanResults = new ConcurrentHashMap<>();
    /* @VisibleForTesting */ final Map<ScanCallback, ScanClient> serialClients = new ConcurrentHashMap<>();
    private final BluetoothAdapter bluetoothAdapter;
    private BluetoothCrashResolver crashResolver;
    private Handler mainHandler;
    /**
     * The Bluetooth LE callback which will be registered with the OS,
     * to be fired on device discovery.
     */
    private final BluetoothAdapter.LeScanCallback leScanCallback =
            new BluetoothAdapter.LeScanCallback() {
                /**
                 * Callback method called from the OS on each BLE device sighting.
                 * This method is invoked on the AIDL handler thread, so all methods
                 * called here must be synchronized.
                 *
                 * @param device The device discovered
                 * @param rssi The signal strength in dBm it was received at
                 * @param scanRecordBytes The raw byte payload buffer
                 */
                @Override
                public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecordBytes) {
                    long currentTimeInNanos = TimeUnit.MILLISECONDS.toNanos(getClock().currentTimeMillis());
                    ScanResult result = new ScanResult(device, ScanRecord.parseFromBytes(scanRecordBytes), rssi,
                            currentTimeInNanos);
                    onScanResult(device.getAddress(), result);
                    if (crashResolver != null)
                        crashResolver.notifyScannedDevice(device, this);
                }
            };

    /**
     * Package constructor, called from {@link BluetoothLeScannerCompatProvider}.
     */
    JbBluetoothLeScannerCompat(
            Context context, BluetoothManager manager, AlarmManager alarmManager) {
// Path: library/src/main/java/com/reelyactive/blesdk/support/ble/util/Clock.java
// public interface Clock {
// 
//   /**
//    * @return current time in milliseconds
//    */
//   public long currentTimeMillis();
// 
//   /**
//    * @return time since the device was booted, in nanoseconds
//    */
//   public long elapsedRealtimeNanos();
// 
// }
// 
// Path: library/src/main/java/com/reelyactive/blesdk/support/ble/util/Logger.java
// public class Logger {
// 
//   public static final String TAG = "BleScanCompatLib";
// 
//   public static void logVerbose(String message) {
//     if (Log.isLoggable(TAG, Log.VERBOSE)) {
//       Log.v(TAG, message);
//     }
//   }
// 
//   public static void logWarning(String message) {
//     if (Log.isLoggable(TAG, Log.WARN)) {
//       Log.w(TAG, message);
//     }
//   }
// 
//   public static void logDebug(String message) {
//     if (Log.isLoggable(TAG, Log.DEBUG)) {
//       Log.d(TAG, message);
//     }
//   }
// 
//   public static void logInfo(String message) {
//     if (Log.isLoggable(TAG, Log.INFO)) {
//       Log.i(TAG, message);
//     }
//   }
// 
//   public static void logError(String message, Exception... e) {
//     if (Log.isLoggable(TAG, Log.ERROR)) {
//       if (e == null || e.length == 0) {
//         Log.e(TAG, message);
//       } else {
//         Log.e(TAG, message, e[0]);
//       }
//     }
//   }
// }
// 
// Path: library/src/main/java/com/reelyactive/blesdk/support/ble/util/SystemClock.java
// public class SystemClock implements Clock {
// 
//   /**
//    * @return current time in milliseconds
//    */
//   @Override
//   public long currentTimeMillis() {
//     return System.currentTimeMillis();
//   }
// 
//   /**
//    * @return time since the device was booted, in nanoseconds
//    */
//   @Override
//   @TargetApi(VERSION_CODES.JELLY_BEAN_MR1)
//   public long elapsedRealtimeNanos() {
//     return android.os.SystemClock.elapsedRealtimeNanos();
//   }
// }


// Path: library/src/main/java/com/reelyactive/blesdk/support/ble/JbBluetoothLeScannerCompat.java
import android.annotation.TargetApi;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothManager;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import com.reelyactive.blesdk.support.ble.util.Clock;
import com.reelyactive.blesdk.support.ble.util.Logger;
import com.reelyactive.blesdk.support.ble.util.SystemClock;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

package com.reelyactive.blesdk.support.ble;/*
 * Copyright 2014 Google Inc. All rights reserved.
 *
 * 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.
 */




/**
 * Implements Bluetooth LE scan related API on top of
 * {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2} and later.
 * <p>
 * This class delivers a callback on found, updated, and lost for devices matching a
 * {@link ScanFilter} filter during scan cycles.
 * <p>
 * A scan cycle comprises a period when the Bluetooth Adapter is active and a period when the
 * Bluetooth adapter is idle. Having an idle period is energy efficient for long lived scans.
 * <p>
 * This class can be accessed on multiple threads:
 * <ul>
 * <li> main thread (user) can call any of the BluetoothLeScanner APIs
 * <li> IntentService worker thread can call {@link #onNewScanCycle()}
 * <li> AIDL binder thread can call {@link #leScanCallback.onLeScan()}
 * </ul>
 *
 * @see <a href="http://go/ble-glossary">BLE Glossary</a>
 */
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
class JbBluetoothLeScannerCompat extends BluetoothLeScannerCompat {

    // Map of BD_ADDR->com.reelyactive.blesdk.support.ble.ScanResult for replay to new registrations.
    // Entries are evicted after SCAN_LOST_CYCLES cycles.
  /* @VisibleForTesting */ final Map<String, ScanResult> recentScanResults = new ConcurrentHashMap<>();
    /* @VisibleForTesting */ final Map<ScanCallback, ScanClient> serialClients = new ConcurrentHashMap<>();
    private final BluetoothAdapter bluetoothAdapter;
    private BluetoothCrashResolver crashResolver;
    private Handler mainHandler;
    /**
     * The Bluetooth LE callback which will be registered with the OS,
     * to be fired on device discovery.
     */
    private final BluetoothAdapter.LeScanCallback leScanCallback =
            new BluetoothAdapter.LeScanCallback() {
                /**
                 * Callback method called from the OS on each BLE device sighting.
                 * This method is invoked on the AIDL handler thread, so all methods
                 * called here must be synchronized.
                 *
                 * @param device The device discovered
                 * @param rssi The signal strength in dBm it was received at
                 * @param scanRecordBytes The raw byte payload buffer
                 */
                @Override
                public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecordBytes) {
                    long currentTimeInNanos = TimeUnit.MILLISECONDS.toNanos(getClock().currentTimeMillis());
                    ScanResult result = new ScanResult(device, ScanRecord.parseFromBytes(scanRecordBytes), rssi,
                            currentTimeInNanos);
                    onScanResult(device.getAddress(), result);
                    if (crashResolver != null)
                        crashResolver.notifyScannedDevice(device, this);
                }
            };

    /**
     * Package constructor, called from {@link BluetoothLeScannerCompatProvider}.
     */
    JbBluetoothLeScannerCompat(
            Context context, BluetoothManager manager, AlarmManager alarmManager) {
        this(manager, alarmManager, new SystemClock(),