1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.pmc; 18 19 import android.app.AlarmManager; 20 import android.app.PendingIntent; 21 import android.bluetooth.BluetoothAdapter; 22 import android.bluetooth.le.BluetoothLeScanner; 23 import android.bluetooth.le.ScanCallback; 24 import android.bluetooth.le.ScanFilter; 25 import android.bluetooth.le.ScanResult; 26 import android.bluetooth.le.ScanSettings; 27 import android.content.BroadcastReceiver; 28 import android.content.Context; 29 import android.content.Intent; 30 import android.content.IntentFilter; 31 import android.os.Bundle; 32 import android.os.SystemClock; 33 import android.util.Log; 34 35 import java.util.ArrayList; 36 import java.util.List; 37 38 /** 39 * Bluetooth LE Receiver functions for power testing. 40 */ 41 public class BleScanReceiver extends BroadcastReceiver { 42 public static final String TAG = "BLEPOWER"; 43 public static final String BLE_SCAN_INTENT = "com.android.pmc.BLESCAN"; 44 public static final int START_SCAN = 1; 45 public static final int STOP_SCAN = 2; 46 public static final int INIT_ALARM_NO = 1; 47 private final Context mContext; 48 private final AlarmManager mAlarmManager; 49 private final BleScanListener mAlarmScanListener; 50 private BluetoothLeScanner mBleScanner; 51 private ScanSettings mScanSettings; 52 private List<ScanFilter> mScanFilterList; 53 // Use PMCStatusLogger to send status and start & end times back to Python client 54 private PMCStatusLogger mPMCStatusLogger; 55 // Test start time is set when receiving the broadcast message from Python client 56 private long mStartTestTime; 57 58 private ScanCallback mScanCallback = new ScanCallback() { 59 @Override 60 public void onScanResult(int callbackType, ScanResult result) { 61 Log.e(TAG, "Bluetooth scan result: " + result.toString()); 62 } 63 64 @Override 65 public void onScanFailed(int errorCode) { 66 Log.e(TAG, "Scan Failed: " + errorCode); 67 } 68 }; 69 70 /** 71 * Class to provide callback for AlarmManager to start BLE scan alarms 72 */ 73 public class BleScanListener extends BroadcastReceiver { 74 75 public static final String BLESCAN = 76 "com.android.pmc.BLESCAN.ALARM"; 77 78 private int mScanTime; 79 private int mNoScanTime; 80 private int mNumAlarms; 81 private int mFirstScanTime; 82 private long mScanStartTime; 83 private long mScanEndTime; 84 85 /** 86 * Constructor 87 * 88 */ BleScanListener()89 public BleScanListener() { 90 Log.d(TAG, "Start BleScanListener()"); 91 BluetoothAdapter bleAdaptor = BluetoothAdapter.getDefaultAdapter(); 92 93 if (bleAdaptor == null) { 94 Log.e(TAG, "BluetoothAdapter is Null"); 95 return; 96 } else { 97 if (!bleAdaptor.isEnabled()) { 98 Log.d(TAG, "BluetoothAdapter is NOT enabled, enable now"); 99 bleAdaptor.enable(); 100 if (!bleAdaptor.isEnabled()) { 101 Log.e(TAG, "Can't enable Bluetooth"); 102 return; 103 } 104 } 105 } 106 107 mBleScanner = bleAdaptor.getBluetoothLeScanner(); 108 mScanFilterList = new ArrayList<ScanFilter>(); 109 Log.d(TAG, "End BleScanListener()"); 110 } 111 112 /** 113 * Function to be called by BleScanReceiver to start 114 * Initial Bluetooth scan alarm 115 * 116 * @param scanMode - scan mode 117 * @param startTime - time when the first scan needs to be started 118 * @param scanTime - time for the scan is lasted 119 * @param noScanTime - time when the scan is stopped 120 * @param numAlarms - number of alarms to start and to stop scan 121 * 122 */ firstAlarm(int scanMode, int startTime, int scanTime, int noScanTime, int numAlarms)123 public void firstAlarm(int scanMode, int startTime, int scanTime, 124 int noScanTime, int numAlarms) { 125 Log.d(TAG, "First Alarm for scan mode: " + scanMode); 126 mScanTime = scanTime; 127 mNoScanTime = noScanTime; 128 mNumAlarms = numAlarms; 129 mFirstScanTime = startTime; 130 131 mScanSettings = new ScanSettings.Builder().setScanMode( 132 scanMode).build(); 133 134 Intent alarmIntent = new Intent(BleScanListener.BLESCAN); 135 alarmIntent.putExtra("com.android.pmc.BLESCAN.Action", START_SCAN); 136 alarmIntent.putExtra("com.android.pmc.BLESCAN.CurrentAlarm", INIT_ALARM_NO); 137 long triggerTime = SystemClock.elapsedRealtime() + startTime * 1000; 138 mAlarmManager.setExactAndAllowWhileIdle( 139 AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerTime, 140 PendingIntent.getBroadcast(mContext, 0, 141 alarmIntent, PendingIntent.FLAG_UPDATE_CURRENT)); 142 } 143 144 /** 145 * Function to be called by onReceive() to start subsequent alarm 146 * 147 * @param intent - intent to get extra data 148 * @param timeInterval - time for alarm to trigger next alarm 149 * @param nextAction - next action for the alarm 150 * 151 */ repeatAlarm(Intent intent, int timeInterval, int nextAction)152 public void repeatAlarm(Intent intent, int timeInterval, 153 int nextAction) { 154 155 int currentAlarm = intent.getIntExtra("com.android.pmc.BLESCAN.CurrentAlarm", 0); 156 Log.d(TAG, "repeatAlarm() currentAlarm: " + currentAlarm); 157 if (currentAlarm == 0) { 158 Log.d(TAG, "Received Alarm with no currentAlarm"); 159 return; 160 } 161 if (currentAlarm >= mNumAlarms) { 162 mPMCStatusLogger.flash(); // To flash out timestamps into log file 163 Log.d(TAG, "All alarms are done"); 164 return; 165 } 166 Log.d(TAG, "Next Action: " + nextAction); 167 Intent alarmIntent = new Intent(BleScanListener.BLESCAN); 168 alarmIntent.putExtra("com.android.pmc.BLESCAN.Action", nextAction); 169 alarmIntent.putExtra("com.android.pmc.BLESCAN.CurrentAlarm", ++currentAlarm); 170 long triggerTime = SystemClock.elapsedRealtime() 171 + timeInterval * 1000; 172 mAlarmManager.setExactAndAllowWhileIdle( 173 AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerTime, 174 PendingIntent.getBroadcast(mContext, 0, 175 alarmIntent, PendingIntent.FLAG_UPDATE_CURRENT)); 176 } 177 178 /** 179 * Callback will be called for AlarmManager to start Bluetooth LE scan 180 * 181 * @param context - system will provide a context to this function 182 * @param intent - system will provide an intent to this function 183 */ 184 @Override onReceive(Context context, Intent intent)185 public void onReceive(Context context, Intent intent) { 186 if (!intent.getAction().equals(BLESCAN)) { 187 return; 188 } 189 int action = intent.getIntExtra("com.android.pmc.BLESCAN.Action", 0); 190 Log.d(TAG, "onReceive() Action: " + action); 191 if (action == -1) { 192 Log.e(TAG, "Received Alarm with no Action"); 193 return; 194 } 195 if (action == START_SCAN) { 196 Log.v(TAG, "Before Start Scan"); 197 mScanStartTime = System.currentTimeMillis(); 198 mBleScanner.startScan(mScanFilterList, mScanSettings, 199 mScanCallback); 200 repeatAlarm(intent, mScanTime, STOP_SCAN); 201 } else if (action == STOP_SCAN) { 202 Log.v(TAG, "Before Stop scan"); 203 mScanEndTime = System.currentTimeMillis(); 204 mPMCStatusLogger.logAlarmTimes(mScanStartTime / 1000.0, mScanEndTime / 1000.0); 205 mBleScanner.stopScan(mScanCallback); 206 if ((mScanEndTime - mStartTestTime) 207 < ((mScanTime + mNoScanTime) * mNumAlarms / 2 + mFirstScanTime) * 1000) { 208 repeatAlarm(intent, mNoScanTime, START_SCAN); 209 } else { 210 mPMCStatusLogger.flash(); // To flash out timestamps into log file 211 Log.d(TAG, "Time is up to end"); 212 } 213 } else { 214 Log.e(TAG, "Unknown Action"); 215 } 216 } 217 } 218 219 /** 220 * Constructor to be called by PMC 221 * 222 * @param context - PMC will provide a context 223 * @param alarmManager - PMC will provide alarmManager 224 */ BleScanReceiver(Context context, AlarmManager alarmManager)225 public BleScanReceiver(Context context, AlarmManager alarmManager) { 226 // prepare for setting alarm service 227 mContext = context; 228 mAlarmManager = alarmManager; 229 mAlarmScanListener = new BleScanListener(); 230 231 // RegisterAlarmReceiver for BleScanListener 232 mContext.registerReceiver(mAlarmScanListener, 233 new IntentFilter(BleScanListener.BLESCAN)); 234 235 } 236 237 /** 238 * Method to receive the broadcast from python client 239 * 240 * @param context - system will provide a context to this function 241 * @param intent - system will provide an intent to this function 242 */ 243 @Override onReceive(Context context, Intent intent)244 public void onReceive(Context context, Intent intent) { 245 if (intent.getAction().equals(BLE_SCAN_INTENT)) { 246 Bundle extras = intent.getExtras(); 247 int scanMode = -1, startTime = 0, scanTime = 0, noScanTime = 0; 248 int repetitions = 1; 249 String str; 250 251 mStartTestTime = System.currentTimeMillis(); 252 mPMCStatusLogger = new PMCStatusLogger(TAG + ".log", TAG); 253 254 if (extras == null) { 255 Log.e(TAG, "No parameters specified"); 256 return; 257 } 258 259 if (!extras.containsKey("ScanMode")) { 260 Log.e(TAG, "No scan mode specified"); 261 return; 262 } 263 str = extras.getString("ScanMode"); 264 Log.d(TAG, "Scan Mode = " + str); 265 scanMode = Integer.valueOf(str); 266 267 if (!extras.containsKey("StartTime")) { 268 Log.e(TAG, "No Start Time specified"); 269 return; 270 } 271 str = extras.getString("StartTime"); 272 Log.d(TAG, "Start Time = " + str); 273 startTime = Integer.valueOf(str); 274 275 if (!extras.containsKey("ScanTime")) { 276 Log.e(TAG, "No Scan Time specified"); 277 return; 278 } 279 str = extras.getString("ScanTime"); 280 Log.d(TAG, "Scan Time = " + str); 281 scanTime = Integer.valueOf(str); 282 283 if (extras.containsKey("Repetitions")) { 284 285 str = extras.getString("Repetitions"); 286 Log.d(TAG, "Repetitions = " + str); 287 repetitions = Integer.valueOf(str); 288 289 if (!extras.containsKey("NoScanTime")) { 290 Log.e(TAG, "No NoScan Time specified"); 291 return; 292 } 293 str = extras.getString("NoScanTime"); 294 Log.d(TAG, "NoScan Time = " + str); 295 noScanTime = Integer.valueOf(str); 296 } 297 if (scanTime == 0 || startTime == 0 || scanMode == -1) { 298 Log.d(TAG, "Invalid paramters"); 299 return; 300 } 301 mAlarmScanListener.firstAlarm(scanMode, startTime, 302 scanTime, noScanTime, repetitions * 2); 303 if (mBleScanner != null && mScanFilterList != null && mScanSettings != null 304 && mScanCallback != null) { 305 mPMCStatusLogger.logStatus("READY"); 306 } else { 307 Log.e(TAG, "BLE scanner is not ready to start test"); 308 } 309 } 310 } 311 } 312