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 110 // Create ScanFilter object, to force scan even with screen OFF 111 // using deviceName string of "dummy" for example 112 ScanFilter scanFilterDeviceName = new ScanFilter.Builder().setDeviceName( 113 "dummy").build(); 114 // Add the object to FilterList 115 mScanFilterList.add(scanFilterDeviceName); 116 117 Log.d(TAG, "End BleScanListener()"); 118 } 119 120 /** 121 * Function to be called by BleScanReceiver to start 122 * Initial Bluetooth scan alarm 123 * 124 * @param scanMode - scan mode 125 * @param startTime - time when the first scan needs to be started 126 * @param scanTime - time for the scan is lasted 127 * @param noScanTime - time when the scan is stopped 128 * @param numAlarms - number of alarms to start and to stop scan 129 * 130 */ firstAlarm(int scanMode, int startTime, int scanTime, int noScanTime, int numAlarms)131 public void firstAlarm(int scanMode, int startTime, int scanTime, 132 int noScanTime, int numAlarms) { 133 Log.d(TAG, "First Alarm for scan mode: " + scanMode); 134 mScanTime = scanTime; 135 mNoScanTime = noScanTime; 136 mNumAlarms = numAlarms; 137 mFirstScanTime = startTime; 138 139 mScanSettings = new ScanSettings.Builder().setScanMode( 140 scanMode).build(); 141 142 Intent alarmIntent = new Intent(BleScanListener.BLESCAN); 143 alarmIntent.putExtra("com.android.pmc.BLESCAN.Action", START_SCAN); 144 alarmIntent.putExtra("com.android.pmc.BLESCAN.CurrentAlarm", INIT_ALARM_NO); 145 long triggerTime = SystemClock.elapsedRealtime() + startTime * 1000; 146 mAlarmManager.setExactAndAllowWhileIdle( 147 AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerTime, 148 PendingIntent.getBroadcast(mContext, 0, 149 alarmIntent, PendingIntent.FLAG_UPDATE_CURRENT)); 150 } 151 152 /** 153 * Function to be called by onReceive() to start subsequent alarm 154 * 155 * @param intent - intent to get extra data 156 * @param timeInterval - time for alarm to trigger next alarm 157 * @param nextAction - next action for the alarm 158 * 159 */ repeatAlarm(Intent intent, int timeInterval, int nextAction)160 public void repeatAlarm(Intent intent, int timeInterval, 161 int nextAction) { 162 163 int currentAlarm = intent.getIntExtra("com.android.pmc.BLESCAN.CurrentAlarm", 0); 164 Log.d(TAG, "repeatAlarm() currentAlarm: " + currentAlarm); 165 if (currentAlarm == 0) { 166 Log.d(TAG, "Received Alarm with no currentAlarm"); 167 return; 168 } 169 if (currentAlarm >= mNumAlarms) { 170 mPMCStatusLogger.flash(); // To flash out timestamps into log file 171 Log.d(TAG, "All alarms are done"); 172 return; 173 } 174 Log.d(TAG, "Next Action: " + nextAction); 175 Intent alarmIntent = new Intent(BleScanListener.BLESCAN); 176 alarmIntent.putExtra("com.android.pmc.BLESCAN.Action", nextAction); 177 alarmIntent.putExtra("com.android.pmc.BLESCAN.CurrentAlarm", ++currentAlarm); 178 long triggerTime = SystemClock.elapsedRealtime() 179 + timeInterval * 1000; 180 mAlarmManager.setExactAndAllowWhileIdle( 181 AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerTime, 182 PendingIntent.getBroadcast(mContext, 0, 183 alarmIntent, PendingIntent.FLAG_UPDATE_CURRENT)); 184 } 185 186 /** 187 * Callback will be called for AlarmManager to start Bluetooth LE scan 188 * 189 * @param context - system will provide a context to this function 190 * @param intent - system will provide an intent to this function 191 */ 192 @Override onReceive(Context context, Intent intent)193 public void onReceive(Context context, Intent intent) { 194 if (!intent.getAction().equals(BLESCAN)) { 195 return; 196 } 197 int action = intent.getIntExtra("com.android.pmc.BLESCAN.Action", 0); 198 Log.d(TAG, "onReceive() Action: " + action); 199 if (action == -1) { 200 Log.e(TAG, "Received Alarm with no Action"); 201 return; 202 } 203 if (action == START_SCAN) { 204 Log.v(TAG, "Before Start Scan"); 205 mScanStartTime = System.currentTimeMillis(); 206 mBleScanner.startScan(mScanFilterList, mScanSettings, 207 mScanCallback); 208 repeatAlarm(intent, mScanTime, STOP_SCAN); 209 } else if (action == STOP_SCAN) { 210 Log.v(TAG, "Before Stop scan"); 211 mScanEndTime = System.currentTimeMillis(); 212 mPMCStatusLogger.logAlarmTimes(mScanStartTime / 1000.0, mScanEndTime / 1000.0); 213 mBleScanner.stopScan(mScanCallback); 214 if ((mScanEndTime - mStartTestTime) 215 < ((mScanTime + mNoScanTime) * mNumAlarms / 2 + mFirstScanTime) * 1000) { 216 repeatAlarm(intent, mNoScanTime, START_SCAN); 217 } else { 218 mPMCStatusLogger.flash(); // To flash out timestamps into log file 219 Log.d(TAG, "Time is up to end"); 220 } 221 } else { 222 Log.e(TAG, "Unknown Action"); 223 } 224 } 225 } 226 227 /** 228 * Constructor to be called by PMC 229 * 230 * @param context - PMC will provide a context 231 * @param alarmManager - PMC will provide alarmManager 232 */ BleScanReceiver(Context context, AlarmManager alarmManager)233 public BleScanReceiver(Context context, AlarmManager alarmManager) { 234 // prepare for setting alarm service 235 mContext = context; 236 mAlarmManager = alarmManager; 237 mAlarmScanListener = new BleScanListener(); 238 239 // RegisterAlarmReceiver for BleScanListener 240 mContext.registerReceiver(mAlarmScanListener, 241 new IntentFilter(BleScanListener.BLESCAN)); 242 243 } 244 245 /** 246 * Method to receive the broadcast from python client 247 * 248 * @param context - system will provide a context to this function 249 * @param intent - system will provide an intent to this function 250 */ 251 @Override onReceive(Context context, Intent intent)252 public void onReceive(Context context, Intent intent) { 253 if (intent.getAction().equals(BLE_SCAN_INTENT)) { 254 Bundle extras = intent.getExtras(); 255 int scanMode = -1, startTime = 0, scanTime = 0, noScanTime = 0; 256 int repetitions = 1; 257 String str; 258 259 mStartTestTime = System.currentTimeMillis(); 260 mPMCStatusLogger = new PMCStatusLogger(TAG + ".log", TAG); 261 262 if (extras == null) { 263 Log.e(TAG, "No parameters specified"); 264 return; 265 } 266 267 if (!extras.containsKey("ScanMode")) { 268 Log.e(TAG, "No scan mode specified"); 269 return; 270 } 271 str = extras.getString("ScanMode"); 272 Log.d(TAG, "Scan Mode = " + str); 273 scanMode = Integer.valueOf(str); 274 275 if (!extras.containsKey("StartTime")) { 276 Log.e(TAG, "No Start Time specified"); 277 return; 278 } 279 str = extras.getString("StartTime"); 280 Log.d(TAG, "Start Time = " + str); 281 startTime = Integer.valueOf(str); 282 283 if (!extras.containsKey("ScanTime")) { 284 Log.e(TAG, "No Scan Time specified"); 285 return; 286 } 287 str = extras.getString("ScanTime"); 288 Log.d(TAG, "Scan Time = " + str); 289 scanTime = Integer.valueOf(str); 290 291 if (extras.containsKey("Repetitions")) { 292 293 str = extras.getString("Repetitions"); 294 Log.d(TAG, "Repetitions = " + str); 295 repetitions = Integer.valueOf(str); 296 297 if (!extras.containsKey("NoScanTime")) { 298 Log.e(TAG, "No NoScan Time specified"); 299 return; 300 } 301 str = extras.getString("NoScanTime"); 302 Log.d(TAG, "NoScan Time = " + str); 303 noScanTime = Integer.valueOf(str); 304 } 305 if (scanTime == 0 || startTime == 0 || scanMode == -1) { 306 Log.d(TAG, "Invalid paramters"); 307 return; 308 } 309 mAlarmScanListener.firstAlarm(scanMode, startTime, 310 scanTime, noScanTime, repetitions * 2); 311 if (mBleScanner != null && mScanFilterList != null && mScanSettings != null 312 && mScanCallback != null) { 313 mPMCStatusLogger.logStatus("READY"); 314 } else { 315 Log.e(TAG, "BLE scanner is not ready to start test"); 316 } 317 } 318 } 319 } 320