Fixed batch scan returning status 12 when restarted.

Also fixed no batch scan results when both truncated and
full batch are enabled.

Added a BatchScanParams to hold parameters for batch scan.

Bug:16850169
Bug:17213118
Change-Id: Ib3bb7442aa2657912ef4377f825388ac8e1010bb
diff --git a/android/app/src/com/android/bluetooth/Utils.java b/android/app/src/com/android/bluetooth/Utils.java
index 0c85a7a..56d5a64 100644
--- a/android/app/src/com/android/bluetooth/Utils.java
+++ b/android/app/src/com/android/bluetooth/Utils.java
@@ -31,6 +31,7 @@
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
 import java.util.UUID;
+import java.util.concurrent.TimeUnit;
 
 /**
  * @hide
@@ -38,6 +39,8 @@
 
 final public class Utils {
     private static final String TAG = "BluetoothUtils";
+    private static final int MICROS_PER_UNIT = 625;
+
     static final int BD_ADDR_LEN = 6; // bytes
     static final int BD_UUID_LEN = 16; // bytes
 
@@ -217,4 +220,10 @@
                 "Need BLUETOOTH_ADMIN permission");
     }
 
+    /**
+     * Converts {@code millisecond} to unit. Each unit is 0.625 millisecond.
+     */
+    public static int millsToUnit(int milliseconds) {
+        return (int) (TimeUnit.MILLISECONDS.toMicros(milliseconds) / MICROS_PER_UNIT);
+    }
 }
diff --git a/android/app/src/com/android/bluetooth/gatt/AdvertiseManager.java b/android/app/src/com/android/bluetooth/gatt/AdvertiseManager.java
index 20b4759..a2c8593 100644
--- a/android/app/src/com/android/bluetooth/gatt/AdvertiseManager.java
+++ b/android/app/src/com/android/bluetooth/gatt/AdvertiseManager.java
@@ -238,7 +238,6 @@
         // Add some randomness to the advertising min/max interval so the controller can do some
         // optimization.
         private static final int ADVERTISING_INTERVAL_DELTA_UNIT = 10;
-        private static final int ADVERTISING_INTERVAL_MICROS_PER_UNIT = 625;
 
         // The following constants should be kept the same as those defined in bt stack.
         private static final int ADVERTISING_CHANNEL_37 = 1 << 0;
@@ -417,22 +416,17 @@
         private long getAdvertisingIntervalUnit(AdvertiseSettings settings) {
             switch (settings.getMode()) {
                 case AdvertiseSettings.ADVERTISE_MODE_LOW_POWER:
-                    return millsToUnit(ADVERTISING_INTERVAL_HIGH_MILLS);
+                    return Utils.millsToUnit(ADVERTISING_INTERVAL_HIGH_MILLS);
                 case AdvertiseSettings.ADVERTISE_MODE_BALANCED:
-                    return millsToUnit(ADVERTISING_INTERVAL_MEDIUM_MILLS);
+                    return Utils.millsToUnit(ADVERTISING_INTERVAL_MEDIUM_MILLS);
                 case AdvertiseSettings.ADVERTISE_MODE_LOW_LATENCY:
-                    return millsToUnit(ADVERTISING_INTERVAL_LOW_MILLS);
+                    return Utils.millsToUnit(ADVERTISING_INTERVAL_LOW_MILLS);
                 default:
                     // Shouldn't happen, just in case.
-                    return millsToUnit(ADVERTISING_INTERVAL_HIGH_MILLS);
+                    return Utils.millsToUnit(ADVERTISING_INTERVAL_HIGH_MILLS);
             }
         }
 
-        private long millsToUnit(int millisecond) {
-            return TimeUnit.MILLISECONDS.toMicros(millisecond)
-                    / ADVERTISING_INTERVAL_MICROS_PER_UNIT;
-        }
-
         // Native functions
         private native void gattClientDisableAdvNative(int client_if);
 
diff --git a/android/app/src/com/android/bluetooth/gatt/GattService.java b/android/app/src/com/android/bluetooth/gatt/GattService.java
index a6e283c..6d5c513 100644
--- a/android/app/src/com/android/bluetooth/gatt/GattService.java
+++ b/android/app/src/com/android/bluetooth/gatt/GattService.java
@@ -980,7 +980,7 @@
             if (app == null) return;
             app.callback.onBatchScanResults(new ArrayList<ScanResult>(results));
         } else {
-            for (ScanClient client : mScanManager.getBatchScanQueue()) {
+            for (ScanClient client : mScanManager.getFullBatchScanQueue()) {
                 // Deliver results for each client.
                 deliverBatchScan(client, results);
             }
diff --git a/android/app/src/com/android/bluetooth/gatt/ScanManager.java b/android/app/src/com/android/bluetooth/gatt/ScanManager.java
index d22e8de..1281fe8 100644
--- a/android/app/src/com/android/bluetooth/gatt/ScanManager.java
+++ b/android/app/src/com/android/bluetooth/gatt/ScanManager.java
@@ -36,11 +36,9 @@
 import com.android.bluetooth.btservice.AdapterService;
 
 import java.util.ArrayDeque;
-import java.util.ArrayList;
 import java.util.Deque;
 import java.util.HashMap;
 import java.util.HashSet;
-import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.CountDownLatch;
@@ -58,6 +56,7 @@
     // Result type defined in bt stack. Need to be accessed by GattService.
     static final int SCAN_RESULT_TYPE_TRUNCATED = 1;
     static final int SCAN_RESULT_TYPE_FULL = 2;
+    static final int SCAN_RESULT_TYPE_BOTH = 3;
 
     // Internal messages for handling BLE scan operations.
     private static final int MSG_START_BLE_SCAN = 0;
@@ -71,10 +70,8 @@
     private static final int OPERATION_TIME_OUT_MILLIS = 500;
 
     private int mLastConfiguredScanSetting = Integer.MIN_VALUE;
-    private int mLastConfiguredBatchFullScanSetting = Integer.MIN_VALUE;
-    private int mLastConfiguredBatchFullClientIf = Integer.MIN_VALUE;
-    private int mLastConfiguredBatchTruncScanSetting = Integer.MIN_VALUE;
-    private int mLastConfiguredBatchTruncClientIf = Integer.MIN_VALUE;
+    // Scan parameters for batch scan.
+    private BatchScanParams mBatchScanParms;
 
     private GattService mService;
     private BroadcastReceiver mBatchAlarmReceiver;
@@ -120,6 +117,21 @@
         return mBatchClients;
     }
 
+    /**
+     * Returns a set of full batch scan clients.
+     */
+    Set<ScanClient> getFullBatchScanQueue() {
+        // TODO: split full batch scan clients and truncated batch clients so we don't need to
+        // construct this every time.
+        Set<ScanClient> fullBatchClients = new HashSet<ScanClient>();
+        for (ScanClient client : mBatchClients) {
+            if (client.settings.getScanResultType() == ScanSettings.SCAN_RESULT_TYPE_FULL) {
+                fullBatchClients.add(client);
+            }
+        }
+        return fullBatchClients;
+    }
+
     void startScan(ScanClient client) {
         sendMessage(MSG_START_BLE_SCAN, client);
     }
@@ -195,8 +207,6 @@
             if (isBatchClient(client)) {
                 mBatchClients.add(client);
                 mScanNative.startBatchScan(client);
-                mScanNative.configureBatchScanParams(SCAN_RESULT_TYPE_FULL);
-                mScanNative.configureBatchScanParams(SCAN_RESULT_TYPE_TRUNCATED);
             } else {
                 mRegularScanClients.add(client);
                 mScanNative.startRegularScan(client);
@@ -249,6 +259,35 @@
         }
     }
 
+    /**
+     * Parameters for batch scans.
+     */
+    class BatchScanParams {
+        int scanMode;
+        int fullScanClientIf;
+        int truncatedScanClientIf;
+
+        BatchScanParams() {
+            scanMode = -1;
+            fullScanClientIf = -1;
+            truncatedScanClientIf = -1;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj) {
+                return true;
+            }
+            if (obj == null || getClass() != obj.getClass()) {
+                return false;
+            }
+            BatchScanParams other = (BatchScanParams) obj;
+            return scanMode == other.scanMode && fullScanClientIf == other.fullScanClientIf
+                    && truncatedScanClientIf == other.truncatedScanClientIf;
+
+        }
+    }
+
     private class ScanNative {
 
         // Delivery mode defined in bt stack.
@@ -335,68 +374,23 @@
             }
         }
 
-        void updateAndConfigureScanParam(int clientIf, int curScanSetting, int resultType,
-                int scanWindow, int scanInterval) {
-
-            int discardRule = DISCARD_OLDEST_WHEN_BUFFER_FULL;
-            int addressType = 0;
-
-            if (resultType == SCAN_RESULT_TYPE_FULL) {
-                if (mLastConfiguredBatchFullClientIf != Integer.MIN_VALUE) {
-                    gattClientStopBatchScanNative(mLastConfiguredBatchFullClientIf);
-                }
-                gattClientStartBatchScanNative(clientIf, resultType, scanInterval,
-                            scanWindow, addressType, discardRule);
-                mLastConfiguredBatchFullScanSetting = curScanSetting;
-                mLastConfiguredBatchFullClientIf = clientIf;
-            } else if (resultType == SCAN_RESULT_TYPE_TRUNCATED) {
-                if (mLastConfiguredBatchTruncClientIf != Integer.MIN_VALUE) {
-                    gattClientStopBatchScanNative(mLastConfiguredBatchTruncClientIf);
-                }
-                gattClientStartBatchScanNative(clientIf, resultType, scanInterval,
-                            scanWindow, addressType, discardRule);
-                mLastConfiguredBatchTruncScanSetting = curScanSetting;
-                mLastConfiguredBatchTruncClientIf = clientIf;
-            }
-        }
-
-        int getLastConfiguredBatchScanSetting(int resultType) {
-            if (resultType == SCAN_RESULT_TYPE_FULL) {
-                return mLastConfiguredBatchFullScanSetting;
-            } else if (resultType == SCAN_RESULT_TYPE_TRUNCATED) {
-                return mLastConfiguredBatchTruncScanSetting;
-            }
-            return Integer.MIN_VALUE;
-        }
-
-        void resetBatchScanParam(int resultType) {
-            if (resultType == SCAN_RESULT_TYPE_FULL) {
-                mLastConfiguredBatchFullScanSetting = Integer.MIN_VALUE;
-                mLastConfiguredBatchFullClientIf = Integer.MIN_VALUE;
-            } else if (resultType == SCAN_RESULT_TYPE_TRUNCATED) {
-                mLastConfiguredBatchTruncScanSetting = Integer.MIN_VALUE;
-                mLastConfiguredBatchTruncClientIf = Integer.MIN_VALUE;
-            }
-        }
-
-
         void configureRegularScanParams() {
-            if (DBG) Log.d(TAG, "configureRegularScanParams() - queue=" + mRegularScanClients.size());
+            logd("configureRegularScanParams() - queue=" + mRegularScanClients.size());
             int curScanSetting = Integer.MIN_VALUE;
             ScanClient client = null;
 
-            client = getAggressiveClient(mRegularScanClients, false, SCAN_RESULT_TYPE_FULL);
+            client = getAggressiveClient(mRegularScanClients);
             if (client != null) {
                 curScanSetting = client.settings.getScanMode();
             }
 
-            if (DBG) Log.d(TAG, "configureRegularScanParams() - ScanSetting Scan mode=" + curScanSetting +
+            logd("configureRegularScanParams() - ScanSetting Scan mode=" + curScanSetting +
                     " mLastConfiguredScanSetting=" + mLastConfiguredScanSetting);
 
             if (curScanSetting != Integer.MIN_VALUE) {
                 if (curScanSetting != mLastConfiguredScanSetting) {
                     int scanWindow, scanInterval;
-                    switch (curScanSetting){
+                    switch (curScanSetting) {
                         case ScanSettings.SCAN_MODE_LOW_POWER:
                             scanWindow = SCAN_MODE_LOW_POWER_WINDOW_MS;
                             scanInterval = SCAN_MODE_LOW_POWER_INTERVAL_MS;
@@ -416,8 +410,8 @@
                             break;
                     }
                     // convert scanWindow and scanInterval from ms to LE scan units(0.625ms)
-                    scanWindow = (scanWindow * 1000)/625;
-                    scanInterval = (scanInterval * 1000)/625;
+                    scanWindow = Utils.millsToUnit(scanWindow);
+                    scanInterval = Utils.millsToUnit(scanInterval);
                     gattClientScanNative(false);
                     gattSetScanParametersNative(scanInterval, scanWindow);
                     gattClientScanNative(true);
@@ -425,80 +419,23 @@
                 }
             } else {
                 mLastConfiguredScanSetting = curScanSetting;
-                if (DBG) Log.d(TAG, "configureRegularScanParams() - queue emtpy, scan stopped");
+                logd("configureRegularScanParams() - queue emtpy, scan stopped");
             }
         }
 
-       ScanClient getAggressiveClient(Set<ScanClient> cList, boolean isBatchClientList, int resultType) {
+        ScanClient getAggressiveClient(Set<ScanClient> cList) {
             ScanClient result = null;
             int curScanSetting = Integer.MIN_VALUE;
-            for(ScanClient client : cList) {
-                // ScanClient scan settings are assumed to be monotonically increasing in value for more
-                // power hungry(higher duty cycle) operation
-                // For batch clients, there are 2 possible scan modes Truncated or Full
-                // Match resultType based on client list
-                if (!isBatchClientList ||
-                    (isBatchClientList && resultType == getResultType(client.settings))) {
-                    if (client.settings.getScanMode() > curScanSetting) {
-                        result = client;
-                    }
+            for (ScanClient client : cList) {
+                // ScanClient scan settings are assumed to be monotonically increasing in value for
+                // more power hungry(higher duty cycle) operation.
+                if (client.settings.getScanMode() > curScanSetting) {
+                    result = client;
                 }
             }
             return result;
         }
 
-        void configureBatchScanParams(int resultType) {
-            if (DBG) Log.d(TAG, "configureBathScanParams() - queue=" + mBatchClients.size());
-            int curScanSetting = Integer.MIN_VALUE;
-            int clientIf = Integer.MIN_VALUE;
-            ScanClient client = null;
-            int lastConfiguredBatchScanSetting = getLastConfiguredBatchScanSetting(resultType);
-
-            client = getAggressiveClient(mBatchClients, true, resultType);
-            if (client != null) {
-                curScanSetting = client.settings.getScanMode();
-                clientIf = client.clientIf;
-            }
-
-            if (DBG) Log.d(TAG, "configureBatchScanParams() - ScanSetting Scan mode=" + curScanSetting +
-                    " lastConfiguredBatchScanSetting=" + lastConfiguredBatchScanSetting +
-                    " scanType=" + resultType);
-
-            if (curScanSetting != Integer.MIN_VALUE) {
-                if (curScanSetting != lastConfiguredBatchScanSetting) {
-                    int scanWindow, scanInterval;
-                    switch (curScanSetting){
-                        case ScanSettings.SCAN_MODE_LOW_POWER:
-                            scanWindow = SCAN_MODE_BATCH_LOW_POWER_WINDOW_MS;
-                            scanInterval = SCAN_MODE_BATCH_LOW_POWER_INTERVAL_MS;
-                            break;
-                        case ScanSettings.SCAN_MODE_BALANCED:
-                            scanWindow = SCAN_MODE_BATCH_BALANCED_WINDOW_MS;
-                            scanInterval = SCAN_MODE_BATCH_BALANCED_INTERVAL_MS;
-                            break;
-                        case ScanSettings.SCAN_MODE_LOW_LATENCY:
-                            scanWindow = SCAN_MODE_BATCH_LOW_LATENCY_WINDOW_MS;
-                            scanInterval = SCAN_MODE_BATCH_LOW_LATENCY_INTERVAL_MS;
-                            break;
-                        default:
-                            Log.e(TAG, "Invalid value for curScanSetting " + curScanSetting);
-                            scanWindow = SCAN_MODE_BATCH_LOW_POWER_WINDOW_MS;
-                            scanInterval = SCAN_MODE_BATCH_LOW_POWER_INTERVAL_MS;
-                            break;
-                    }
-                    // convert scanWindow and scanInterval from ms to LE scan units(0.625ms)
-                    scanWindow = (scanWindow * 1000)/625;
-                    scanInterval = (scanInterval * 1000)/625;
-                    updateAndConfigureScanParam(clientIf, curScanSetting, resultType,
-                            scanWindow, scanInterval);
-                }
-            } else {
-                resetBatchScanParam(resultType);
-                if (DBG) Log.d(TAG, "configureBatchScanParams() - queue emtpy," +
-                            "resetting batch scan config params");
-            }
-        }
-
         void startRegularScan(ScanClient client) {
             if (mFilterIndexStack.isEmpty() && isFilteringSupported()) {
                 initFilterIndexStack();
@@ -517,22 +454,112 @@
                 initFilterIndexStack();
             }
             configureScanFilters(client);
-            int fullScanPercent = 50;
-            int notifyThreshold = 95;
-            resetCountDownLatch();
-            logd("configuring batch scan storage, appIf " + client.clientIf);
-            gattClientConfigBatchScanStorageNative(client.clientIf, fullScanPercent,
-                    100 - fullScanPercent, notifyThreshold);
-            waitForCallback();
-            logd("Starting BLE batch scan");
+            // Reset batch scan. May need to stop the existing batch scan and update scan params.
+            resetBatchScan(client);
+        }
+
+        private void resetBatchScan(ScanClient client) {
+            int clientIf = client.clientIf;
+            BatchScanParams batchScanParams = getBatchScanParams();
+            // Stop batch if batch scan params changed and previous params is not null.
+            if (mBatchScanParms != null && (!mBatchScanParms.equals(batchScanParams))) {
+                logd("stopping BLe Batch");
+                resetCountDownLatch();
+                gattClientStopBatchScanNative(clientIf);
+                waitForCallback();
+                // Clear pending results as it's illegal to config storage if there are still
+                // pending results.
+                flushBatchResults(clientIf);
+            }
+            // Start batch if batchScanParams changed and current params is not null.
+            if (batchScanParams != null && (!batchScanParams.equals(mBatchScanParms))) {
+                int notifyThreshold = 95;
+                logd("Starting BLE batch scan");
+                int resultType = getResultType(batchScanParams);
+                int fullScanPercent = getFullScanStoragePercent(resultType);
+                resetCountDownLatch();
+                logd("configuring batch scan storage, appIf " + client.clientIf);
+                gattClientConfigBatchScanStorageNative(client.clientIf, fullScanPercent,
+                        100 - fullScanPercent, notifyThreshold);
+                waitForCallback();
+                resetCountDownLatch();
+                int scanInterval =
+                        Utils.millsToUnit(getBatchScanIntervalMillis(batchScanParams.scanMode));
+                int scanWindow =
+                        Utils.millsToUnit(getBatchScanWindowMillis(batchScanParams.scanMode));
+                gattClientStartBatchScanNative(clientIf, resultType, scanInterval,
+                        scanWindow, 0, DISCARD_OLDEST_WHEN_BUFFER_FULL);
+                waitForCallback();
+            }
+            mBatchScanParms = batchScanParams;
             setBatchAlarm();
         }
 
+        private int getFullScanStoragePercent(int resultType) {
+            switch (resultType) {
+                case SCAN_RESULT_TYPE_FULL:
+                    return 100;
+                case SCAN_RESULT_TYPE_TRUNCATED:
+                    return 0;
+                case SCAN_RESULT_TYPE_BOTH:
+                    return 50;
+                default:
+                    return 50;
+            }
+        }
+
+        private BatchScanParams getBatchScanParams() {
+            if (mBatchClients.isEmpty()) {
+                return null;
+            }
+            BatchScanParams params = new BatchScanParams();
+            // TODO: split full batch scan results and truncated batch scan results to different
+            // collections.
+            for (ScanClient client : mBatchClients) {
+                params.scanMode = Math.max(params.scanMode, client.settings.getScanMode());
+                if (client.settings.getScanResultType() == ScanSettings.SCAN_RESULT_TYPE_FULL) {
+                    params.fullScanClientIf = client.clientIf;
+                } else {
+                    params.truncatedScanClientIf = client.clientIf;
+                }
+            }
+            return params;
+        }
+
+        private int getBatchScanWindowMillis(int scanMode) {
+            switch (scanMode) {
+                case ScanSettings.SCAN_MODE_LOW_LATENCY:
+                    return SCAN_MODE_BATCH_LOW_LATENCY_WINDOW_MS;
+                case ScanSettings.SCAN_MODE_BALANCED:
+                    return SCAN_MODE_BATCH_BALANCED_WINDOW_MS;
+                case ScanSettings.SCAN_MODE_LOW_POWER:
+                    return SCAN_MODE_BATCH_LOW_POWER_WINDOW_MS;
+                default:
+                    return SCAN_MODE_BATCH_LOW_POWER_WINDOW_MS;
+            }
+        }
+
+        private int getBatchScanIntervalMillis(int scanMode) {
+            switch (scanMode) {
+                case ScanSettings.SCAN_MODE_LOW_LATENCY:
+                    return SCAN_MODE_BATCH_LOW_LATENCY_INTERVAL_MS;
+                case ScanSettings.SCAN_MODE_BALANCED:
+                    return SCAN_MODE_BATCH_BALANCED_INTERVAL_MS;
+                case ScanSettings.SCAN_MODE_LOW_POWER:
+                    return SCAN_MODE_BATCH_LOW_POWER_INTERVAL_MS;
+                default:
+                    return SCAN_MODE_BATCH_LOW_POWER_INTERVAL_MS;
+            }
+        }
+
         // Set the batch alarm to be triggered within a short window after batch interval. This
         // allows system to optimize wake up time while still allows a degree of precise control.
         private void setBatchAlarm() {
             // Cancel any pending alarm just in case.
             mAlarmManager.cancel(mBatchScanIntervalIntent);
+            if (mBatchClients.isEmpty()) {
+                return;
+            }
             long batchTriggerIntervalMillis = getBatchTriggerIntervalMillis();
             // Allows the alarm to be triggered within
             // [batchTriggerIntervalMillis, 1.1 * batchTriggerIntervalMillis]
@@ -554,25 +581,25 @@
         }
 
         void stopBatchScan(ScanClient client) {
-            flushBatchResults(client.clientIf);
-            removeScanFilters(client.clientIf);
             mBatchClients.remove(client);
-            configureBatchScanParams(SCAN_RESULT_TYPE_FULL);
-            configureBatchScanParams(SCAN_RESULT_TYPE_TRUNCATED);
-            setBatchAlarm();
+            removeScanFilters(client.clientIf);
+            resetBatchScan(client);
         }
 
         void flushBatchResults(int clientIf) {
             logd("flushPendingBatchResults - clientIf = " + clientIf);
-            ScanClient client = getBatchScanClient(clientIf);
-            if (client == null) {
-                logd("unknown client : " + clientIf);
-                return;
+            if (mBatchScanParms.fullScanClientIf != -1) {
+                resetCountDownLatch();
+                gattClientReadScanReportsNative(mBatchScanParms.fullScanClientIf,
+                        SCAN_RESULT_TYPE_FULL);
+                waitForCallback();
             }
-            int resultType = getResultType(client.settings);
-            resetCountDownLatch();
-            gattClientReadScanReportsNative(client.clientIf, resultType);
-            waitForCallback();
+            if (mBatchScanParms.truncatedScanClientIf != -1) {
+                resetCountDownLatch();
+                gattClientReadScanReportsNative(mBatchScanParms.truncatedScanClientIf,
+                        SCAN_RESULT_TYPE_TRUNCATED);
+                waitForCallback();
+            }
             setBatchAlarm();
         }
 
@@ -659,9 +686,17 @@
         /**
          * Return batch scan result type value defined in bt stack.
          */
-        private int getResultType(ScanSettings settings) {
-            return settings.getScanResultType() == ScanSettings.SCAN_RESULT_TYPE_FULL ?
-                    SCAN_RESULT_TYPE_FULL : SCAN_RESULT_TYPE_TRUNCATED;
+        private int getResultType(BatchScanParams params) {
+            if (params.fullScanClientIf != -1 && params.truncatedScanClientIf != -1) {
+                return SCAN_RESULT_TYPE_BOTH;
+            }
+            if (params.truncatedScanClientIf != -1) {
+                return SCAN_RESULT_TYPE_TRUNCATED;
+            }
+            if (params.fullScanClientIf != -1) {
+                return SCAN_RESULT_TYPE_FULL;
+            }
+            return -1;
         }
 
         // Check if ALLOW_FILTER should be used for the client.
@@ -823,6 +858,6 @@
     }
 
     private void logd(String s) {
-        Log.d(TAG, s);
+        if (DBG) Log.d(TAG, s);
     }
 }