1 /* 2 * Copyright (C) 2021 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 android.telephony.mockmodem; 18 19 import android.hardware.radio.RadioError; 20 import android.hardware.radio.RadioIndicationType; 21 import android.hardware.radio.RadioResponseInfo; 22 import android.hardware.radio.messaging.IRadioMessaging; 23 import android.hardware.radio.messaging.IRadioMessagingIndication; 24 import android.hardware.radio.messaging.IRadioMessagingResponse; 25 import android.hardware.radio.messaging.SendSmsResult; 26 import android.os.RemoteException; 27 import android.support.annotation.GuardedBy; 28 import android.util.ArraySet; 29 import android.util.Log; 30 31 import java.util.Set; 32 import java.util.concurrent.CopyOnWriteArrayList; 33 import java.util.concurrent.Executor; 34 35 public class IRadioMessagingImpl extends IRadioMessaging.Stub { 36 private static final String TAG = "MRMSG"; 37 38 private final MockModemService mService; 39 private IRadioMessagingResponse mRadioMessagingResponse; 40 private IRadioMessagingIndication mRadioMessagingIndication; 41 42 @GuardedBy("mGsmBroadcastConfigSet") 43 private final Set<Integer> mGsmBroadcastConfigSet = new ArraySet<Integer>(); 44 45 @GuardedBy("mCdmaBroadcastConfigSet") 46 private final Set<Integer> mCdmaBroadcastConfigSet = new ArraySet<Integer>(); 47 48 private CopyOnWriteArrayList<CallBackWithExecutor> mBroadcastCallbacks = 49 new CopyOnWriteArrayList<>(); 50 51 private MockModemConfigInterface mMockModemConfigInterface; 52 private int mSubId; 53 private String mTag; 54 private final MockMessagingService mMockMessagingService; 55 56 public interface BroadcastCallback { onGsmBroadcastActivated()57 void onGsmBroadcastActivated(); onCdmaBroadcastActivated()58 void onCdmaBroadcastActivated(); 59 } 60 61 public static class CallBackWithExecutor { 62 public Executor mExecutor; 63 public BroadcastCallback mCallback; 64 CallBackWithExecutor(Executor executor, BroadcastCallback callback)65 public CallBackWithExecutor(Executor executor, BroadcastCallback callback) { 66 mExecutor = executor; 67 mCallback = callback; 68 } 69 } 70 IRadioMessagingImpl( MockModemService service, MockModemConfigInterface configInterface, int instanceId)71 public IRadioMessagingImpl( 72 MockModemService service, MockModemConfigInterface configInterface, int instanceId) { 73 mTag = TAG + "-" + instanceId; 74 Log.d(mTag, "Instantiated"); 75 76 this.mService = service; 77 mMockModemConfigInterface = configInterface; 78 mSubId = instanceId; 79 mMockMessagingService = new MockMessagingService(instanceId); 80 } 81 82 // Implementation of IRadioMessaging functions 83 @Override setResponseFunctions( IRadioMessagingResponse radioMessagingResponse, IRadioMessagingIndication radioMessagingIndication)84 public void setResponseFunctions( 85 IRadioMessagingResponse radioMessagingResponse, 86 IRadioMessagingIndication radioMessagingIndication) { 87 Log.d(mTag, "setResponseFunctions"); 88 mRadioMessagingResponse = radioMessagingResponse; 89 mRadioMessagingIndication = radioMessagingIndication; 90 mService.countDownLatch(MockModemService.LATCH_RADIO_INTERFACES_READY); 91 } 92 93 @Override acknowledgeIncomingGsmSmsWithPdu(int serial, boolean success, String ackPdu)94 public void acknowledgeIncomingGsmSmsWithPdu(int serial, boolean success, String ackPdu) { 95 Log.d(mTag, "acknowledgeIncomingGsmSmsWithPdu"); 96 97 RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED); 98 try { 99 mRadioMessagingResponse.acknowledgeIncomingGsmSmsWithPduResponse(rsp); 100 } catch (RemoteException ex) { 101 Log.e(mTag, "Failed to acknowledgeIncomingGsmSmsWithPdu from AIDL. Exception" + ex); 102 } 103 } 104 105 @Override acknowledgeLastIncomingCdmaSms( int serial, android.hardware.radio.messaging.CdmaSmsAck smsAck)106 public void acknowledgeLastIncomingCdmaSms( 107 int serial, android.hardware.radio.messaging.CdmaSmsAck smsAck) { 108 Log.d(mTag, "acknowledgeLastIncomingCdmaSms"); 109 110 RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED); 111 try { 112 mRadioMessagingResponse.acknowledgeLastIncomingCdmaSmsResponse(rsp); 113 } catch (RemoteException ex) { 114 Log.e(mTag, "Failed to acknowledgeLastIncomingCdmaSms from AIDL. Exception" + ex); 115 } 116 } 117 118 @Override acknowledgeLastIncomingGsmSms(int serial, boolean success, int cause)119 public void acknowledgeLastIncomingGsmSms(int serial, boolean success, int cause) { 120 Log.d(mTag, "acknowledgeLastIncomingGsmSms"); 121 122 RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED); 123 try { 124 mRadioMessagingResponse.acknowledgeLastIncomingGsmSmsResponse(rsp); 125 } catch (RemoteException ex) { 126 Log.e(mTag, "Failed to acknowledgeLastIncomingGsmSms from AIDL. Exception" + ex); 127 } 128 } 129 130 @Override deleteSmsOnRuim(int serial, int index)131 public void deleteSmsOnRuim(int serial, int index) { 132 Log.d(mTag, "deleteSmsOnRuim"); 133 134 RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED); 135 try { 136 mRadioMessagingResponse.deleteSmsOnRuimResponse(rsp); 137 } catch (RemoteException ex) { 138 Log.e(mTag, "Failed to deleteSmsOnRuim from AIDL. Exception" + ex); 139 } 140 } 141 142 @Override deleteSmsOnSim(int serial, int index)143 public void deleteSmsOnSim(int serial, int index) { 144 Log.d(mTag, "deleteSmsOnSim"); 145 146 RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED); 147 try { 148 mRadioMessagingResponse.deleteSmsOnSimResponse(rsp); 149 } catch (RemoteException ex) { 150 Log.e(mTag, "Failed to deleteSmsOnSim from AIDL. Exception" + ex); 151 } 152 } 153 154 @Override getCdmaBroadcastConfig(int serial)155 public void getCdmaBroadcastConfig(int serial) { 156 Log.d(mTag, "getCdmaBroadcastConfig"); 157 158 RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED); 159 try { 160 mRadioMessagingResponse.getCdmaBroadcastConfigResponse(rsp, null); 161 } catch (RemoteException ex) { 162 Log.e(mTag, "Failed to getCdmaBroadcastConfig from AIDL. Exception" + ex); 163 } 164 } 165 166 @Override getGsmBroadcastConfig(int serial)167 public void getGsmBroadcastConfig(int serial) { 168 Log.d(mTag, "getGsmBroadcastConfig"); 169 170 RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED); 171 try { 172 mRadioMessagingResponse.getGsmBroadcastConfigResponse(rsp, null); 173 } catch (RemoteException ex) { 174 Log.e(mTag, "Failed to getGsmBroadcastConfig from AIDL. Exception" + ex); 175 } 176 } 177 178 @Override getSmscAddress(int serial)179 public void getSmscAddress(int serial) { 180 Log.d(mTag, "getSmscAddress"); 181 182 String smsc = mMockMessagingService.getSmscAddress(); 183 RadioResponseInfo rsp = mService.makeSolRsp(serial); 184 try { 185 mRadioMessagingResponse.getSmscAddressResponse(rsp, smsc); 186 } catch (RemoteException ex) { 187 Log.e(mTag, "Failed to getSmscAddress from AIDL. Exception" + ex); 188 } 189 } 190 191 @Override reportSmsMemoryStatus(int serial, boolean available)192 public void reportSmsMemoryStatus(int serial, boolean available) { 193 Log.d(mTag, "reportSmsMemoryStatus"); 194 195 RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED); 196 try { 197 mRadioMessagingResponse.reportSmsMemoryStatusResponse(rsp); 198 } catch (RemoteException ex) { 199 Log.e(mTag, "Failed to reportSmsMemoryStatus from AIDL. Exception" + ex); 200 } 201 } 202 203 @Override responseAcknowledgement()204 public void responseAcknowledgement() { 205 Log.d(mTag, "responseAcknowledgement"); 206 // TODO 207 } 208 209 @Override sendCdmaSms(int serial, android.hardware.radio.messaging.CdmaSmsMessage sms)210 public void sendCdmaSms(int serial, android.hardware.radio.messaging.CdmaSmsMessage sms) { 211 Log.d(mTag, "sendCdmaSms"); 212 213 RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED); 214 try { 215 mRadioMessagingResponse.sendCdmaSmsResponse(rsp, null); 216 } catch (RemoteException ex) { 217 Log.e(mTag, "Failed to sendCdmaSms from AIDL. Exception" + ex); 218 } 219 } 220 221 @Override sendCdmaSmsExpectMore( int serial, android.hardware.radio.messaging.CdmaSmsMessage sms)222 public void sendCdmaSmsExpectMore( 223 int serial, android.hardware.radio.messaging.CdmaSmsMessage sms) { 224 Log.d(mTag, "sendCdmaSmsExpectMore"); 225 226 RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED); 227 try { 228 mRadioMessagingResponse.sendCdmaSmsExpectMoreResponse(rsp, null); 229 } catch (RemoteException ex) { 230 Log.e(mTag, "Failed to sendCdmaSmsExpectMore from AIDL. Exception" + ex); 231 } 232 } 233 234 @Override sendImsSms(int serial, android.hardware.radio.messaging.ImsSmsMessage message)235 public void sendImsSms(int serial, android.hardware.radio.messaging.ImsSmsMessage message) { 236 Log.d(mTag, "sendImsSms"); 237 238 android.hardware.radio.messaging.SendSmsResult sms = new SendSmsResult(); 239 sms.messageRef = 0; 240 sms.ackPDU = "ack"; 241 sms.errorCode = 0; 242 RadioResponseInfo rsp = mService.makeSolRsp(serial); 243 try { 244 mRadioMessagingResponse.sendImsSmsResponse(rsp, sms); 245 } catch (RemoteException ex) { 246 Log.e(mTag, "Failed to sendImsSms from AIDL. Exception" + ex); 247 } 248 } 249 250 @Override sendSms(int serial, android.hardware.radio.messaging.GsmSmsMessage message)251 public void sendSms(int serial, android.hardware.radio.messaging.GsmSmsMessage message) { 252 Log.d(mTag, "sendSms"); 253 254 android.hardware.radio.messaging.SendSmsResult sms = new SendSmsResult(); 255 sms.messageRef = 0; 256 sms.ackPDU = "ack"; 257 sms.errorCode = 0; 258 RadioResponseInfo rsp = mService.makeSolRsp(serial); 259 try { 260 mRadioMessagingResponse.sendSmsResponse(rsp, sms); 261 } catch (RemoteException ex) { 262 Log.e(mTag, "Failed to sendSms from AIDL. Exception" + ex); 263 } 264 } 265 266 @Override sendSmsExpectMore( int serial, android.hardware.radio.messaging.GsmSmsMessage message)267 public void sendSmsExpectMore( 268 int serial, android.hardware.radio.messaging.GsmSmsMessage message) { 269 Log.d(mTag, "sendSmsExpectMore"); 270 271 RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED); 272 try { 273 mRadioMessagingResponse.sendSmsExpectMoreResponse(rsp, null); 274 } catch (RemoteException ex) { 275 Log.e(mTag, "Failed to sendSmsExpectMore from AIDL. Exception" + ex); 276 } 277 } 278 279 @Override setCdmaBroadcastActivation(int serial, boolean activate)280 public void setCdmaBroadcastActivation(int serial, boolean activate) { 281 Log.d(mTag, "setCdmaBroadcastActivation, activate = " + activate); 282 283 RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.NONE); 284 try { 285 mRadioMessagingResponse.setCdmaBroadcastActivationResponse(rsp); 286 } catch (RemoteException ex) { 287 Log.e(mTag, "Failed to setCdmaBroadcastActivation from AIDL. Exception" + ex); 288 } 289 if (activate) { 290 for (CallBackWithExecutor callbackWithExecutor : mBroadcastCallbacks) { 291 callbackWithExecutor.mExecutor.execute( 292 () -> callbackWithExecutor.mCallback.onCdmaBroadcastActivated()); 293 } 294 } 295 } 296 297 @Override setCdmaBroadcastConfig( int serial, android.hardware.radio.messaging.CdmaBroadcastSmsConfigInfo[] configInfo)298 public void setCdmaBroadcastConfig( 299 int serial, android.hardware.radio.messaging.CdmaBroadcastSmsConfigInfo[] configInfo) { 300 Log.d(mTag, "setCdmaBroadcastConfig"); 301 302 int error = RadioError.NONE; 303 if (configInfo == null) { 304 error = RadioError.INVALID_ARGUMENTS; 305 } else { 306 synchronized (mCdmaBroadcastConfigSet) { 307 mCdmaBroadcastConfigSet.clear(); 308 for (int i = 0; i < configInfo.length; i++) { 309 Log.d(mTag, "configInfo serviceCategory" + configInfo[i].serviceCategory); 310 mCdmaBroadcastConfigSet.add(configInfo[i].serviceCategory); 311 } 312 } 313 } 314 RadioResponseInfo rsp = mService.makeSolRsp(serial, error); 315 try { 316 mRadioMessagingResponse.setCdmaBroadcastConfigResponse(rsp); 317 } catch (RemoteException ex) { 318 Log.e(mTag, "Failed to setCdmaBroadcastConfig from AIDL. Exception" + ex); 319 } 320 } 321 322 @Override setGsmBroadcastActivation(int serial, boolean activate)323 public void setGsmBroadcastActivation(int serial, boolean activate) { 324 Log.d(mTag, "setGsmBroadcastActivation, activate = " + activate); 325 326 RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.NONE); 327 try { 328 mRadioMessagingResponse.setGsmBroadcastActivationResponse(rsp); 329 } catch (RemoteException ex) { 330 Log.e(mTag, "Failed to setGsmBroadcastActivation from AIDL. Exception" + ex); 331 } 332 if (activate) { 333 for (CallBackWithExecutor callbackWithExecutor : mBroadcastCallbacks) { 334 callbackWithExecutor.mExecutor.execute( 335 () -> callbackWithExecutor.mCallback.onGsmBroadcastActivated()); 336 } 337 } 338 } 339 340 @Override setGsmBroadcastConfig( int serial, android.hardware.radio.messaging.GsmBroadcastSmsConfigInfo[] configInfo)341 public void setGsmBroadcastConfig( 342 int serial, android.hardware.radio.messaging.GsmBroadcastSmsConfigInfo[] configInfo) { 343 Log.d(mTag, "setGsmBroadcastConfig"); 344 345 int error = RadioError.NONE; 346 if (configInfo == null) { 347 error = RadioError.INVALID_ARGUMENTS; 348 } else { 349 synchronized (mGsmBroadcastConfigSet) { 350 mGsmBroadcastConfigSet.clear(); 351 for (int i = 0; i < configInfo.length; i++) { 352 int startId = configInfo[i].fromServiceId; 353 int endId = configInfo[i].toServiceId; 354 boolean selected = configInfo[i].selected; 355 Log.d( 356 mTag, 357 "configInfo from: " 358 + startId 359 + ", to: " 360 + endId 361 + ", selected: " 362 + selected); 363 if (selected) { 364 for (int j = startId; j <= endId; j++) { 365 mGsmBroadcastConfigSet.add(j); 366 } 367 } 368 } 369 } 370 } 371 RadioResponseInfo rsp = mService.makeSolRsp(serial, error); 372 try { 373 mRadioMessagingResponse.setGsmBroadcastConfigResponse(rsp); 374 } catch (RemoteException ex) { 375 Log.e(mTag, "Failed to setGsmBroadcastConfig from AIDL. Exception" + ex); 376 } 377 } 378 379 @Override setSmscAddress(int serial, String smsc)380 public void setSmscAddress(int serial, String smsc) { 381 Log.d(mTag, "setSmscAddress"); 382 383 RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED); 384 try { 385 mRadioMessagingResponse.setSmscAddressResponse(rsp); 386 } catch (RemoteException ex) { 387 Log.e(mTag, "Failed to setSmscAddress from AIDL. Exception" + ex); 388 } 389 } 390 391 @Override writeSmsToRuim( int serial, android.hardware.radio.messaging.CdmaSmsWriteArgs cdmaSms)392 public void writeSmsToRuim( 393 int serial, android.hardware.radio.messaging.CdmaSmsWriteArgs cdmaSms) { 394 Log.d(mTag, "writeSmsToRuim"); 395 396 int index = 0; 397 RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED); 398 try { 399 mRadioMessagingResponse.writeSmsToRuimResponse(rsp, index); 400 } catch (RemoteException ex) { 401 Log.e(mTag, "Failed to writeSmsToRuim from AIDL. Exception" + ex); 402 } 403 } 404 405 @Override writeSmsToSim( int serial, android.hardware.radio.messaging.SmsWriteArgs smsWriteArgs)406 public void writeSmsToSim( 407 int serial, android.hardware.radio.messaging.SmsWriteArgs smsWriteArgs) { 408 Log.d(mTag, "writeSmsToSim"); 409 410 int index = 0; 411 RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED); 412 try { 413 mRadioMessagingResponse.writeSmsToSimResponse(rsp, index); 414 } catch (RemoteException ex) { 415 Log.e(mTag, "Failed to writeSmsToSim from AIDL. Exception" + ex); 416 } 417 } 418 cdmaNewSms(android.hardware.radio.messaging.CdmaSmsMessage msg)419 public void cdmaNewSms(android.hardware.radio.messaging.CdmaSmsMessage msg) { 420 Log.d(mTag, "cdmaNewSms"); 421 422 if (mRadioMessagingIndication != null) { 423 try { 424 mRadioMessagingIndication.cdmaNewSms(RadioIndicationType.UNSOLICITED, msg); 425 } catch (RemoteException ex) { 426 Log.e(mTag, "Failed to cdmaNewSms indication from AIDL. Exception" + ex); 427 } 428 } else { 429 Log.e(mTag, "null mRadioMessagingIndication"); 430 } 431 } 432 cdmaRuimSmsStorageFull()433 public void cdmaRuimSmsStorageFull() { 434 Log.d(mTag, "cdmaRuimSmsStorageFull"); 435 436 if (mRadioMessagingIndication != null) { 437 try { 438 mRadioMessagingIndication.cdmaRuimSmsStorageFull(RadioIndicationType.UNSOLICITED); 439 } catch (RemoteException ex) { 440 Log.e( 441 mTag, 442 "Failed to cdmaRuimSmsStorageFull indication from AIDL. Exception" + ex); 443 } 444 } else { 445 Log.e(mTag, "null mRadioMessagingIndication"); 446 } 447 } 448 newBroadcastSms(byte[] data)449 public void newBroadcastSms(byte[] data) { 450 Log.d(mTag, "newBroadcastSms"); 451 452 if (mRadioMessagingIndication != null) { 453 try { 454 mRadioMessagingIndication.newBroadcastSms(RadioIndicationType.UNSOLICITED, data); 455 } catch (RemoteException ex) { 456 Log.e(mTag, "Failed to newBroadcastSms indication from AIDL. Exception" + ex); 457 } 458 } else { 459 Log.e(mTag, "null mRadioMessagingIndication"); 460 } 461 } 462 newSms(byte[] pdu)463 public void newSms(byte[] pdu) { 464 Log.d(mTag, "newSms"); 465 466 if (mRadioMessagingIndication != null) { 467 try { 468 mRadioMessagingIndication.newSms(RadioIndicationType.UNSOLICITED, pdu); 469 } catch (RemoteException ex) { 470 Log.e(mTag, "Failed to newSms indication from AIDL. Exception" + ex); 471 } 472 } else { 473 Log.e(mTag, "null mRadioMessagingIndication"); 474 } 475 } 476 newSmsOnSim(int recordNumber)477 public void newSmsOnSim(int recordNumber) { 478 Log.d(mTag, "newSmsOnSim"); 479 480 if (mRadioMessagingIndication != null) { 481 try { 482 mRadioMessagingIndication.newSmsOnSim( 483 RadioIndicationType.UNSOLICITED, recordNumber); 484 } catch (RemoteException ex) { 485 Log.e(mTag, "Failed to newSmsOnSim indication from AIDL. Exception" + ex); 486 } 487 } else { 488 Log.e(mTag, "null mRadioMessagingIndication"); 489 } 490 } 491 newSmsStatusReport(byte[] pdu)492 public void newSmsStatusReport(byte[] pdu) { 493 Log.d(mTag, "newSmsStatusReport"); 494 495 if (mRadioMessagingIndication != null) { 496 try { 497 mRadioMessagingIndication.newSmsStatusReport(RadioIndicationType.UNSOLICITED, pdu); 498 } catch (RemoteException ex) { 499 Log.e(mTag, "Failed to newSmsStatusReport indication from AIDL. Exception" + ex); 500 } 501 } else { 502 Log.e(mTag, "null mRadioMessagingIndication"); 503 } 504 } 505 simSmsStorageFull()506 public void simSmsStorageFull() { 507 Log.d(mTag, "simSmsStorageFull"); 508 509 if (mRadioMessagingIndication != null) { 510 try { 511 mRadioMessagingIndication.simSmsStorageFull(RadioIndicationType.UNSOLICITED); 512 } catch (RemoteException ex) { 513 Log.e(mTag, "Failed to simSmsStorageFull indication from AIDL. Exception" + ex); 514 } 515 } else { 516 Log.e(mTag, "null mRadioMessagingIndication"); 517 } 518 } 519 520 @Override getInterfaceHash()521 public String getInterfaceHash() { 522 return IRadioMessaging.HASH; 523 } 524 525 @Override getInterfaceVersion()526 public int getInterfaceVersion() { 527 return IRadioMessaging.VERSION; 528 } 529 getGsmBroadcastConfigSet()530 public Set<Integer> getGsmBroadcastConfigSet() { 531 synchronized (mGsmBroadcastConfigSet) { 532 Log.d(mTag, "getBroadcastConfigSet. " + mGsmBroadcastConfigSet); 533 return mGsmBroadcastConfigSet; 534 } 535 } 536 getCdmaBroadcastConfigSet()537 public Set<Integer> getCdmaBroadcastConfigSet() { 538 synchronized (mCdmaBroadcastConfigSet) { 539 Log.d(mTag, "getBroadcastConfigSet. " + mCdmaBroadcastConfigSet); 540 return mCdmaBroadcastConfigSet; 541 } 542 } 543 registerBroadcastCallback(CallBackWithExecutor callback)544 public void registerBroadcastCallback(CallBackWithExecutor callback) { 545 mBroadcastCallbacks.add(callback); 546 } unregisterBroadcastCallback(CallBackWithExecutor callback)547 public void unregisterBroadcastCallback(CallBackWithExecutor callback) { 548 mBroadcastCallbacks.remove(callback); 549 } 550 } 551