1 /* 2 * Copyright (C) 2006 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.internal.telephony; 18 19 20 import android.content.Context; 21 import android.os.RegistrantList; 22 import android.os.Registrant; 23 import android.os.Handler; 24 import android.os.AsyncResult; 25 import android.telephony.TelephonyManager; 26 27 /** 28 * {@hide} 29 */ 30 public abstract class BaseCommands implements CommandsInterface { 31 //***** Instance Variables 32 protected Context mContext; 33 protected RadioState mState = RadioState.RADIO_UNAVAILABLE; 34 protected Object mStateMonitor = new Object(); 35 36 protected RegistrantList mRadioStateChangedRegistrants = new RegistrantList(); 37 protected RegistrantList mOnRegistrants = new RegistrantList(); 38 protected RegistrantList mAvailRegistrants = new RegistrantList(); 39 protected RegistrantList mOffOrNotAvailRegistrants = new RegistrantList(); 40 protected RegistrantList mNotAvailRegistrants = new RegistrantList(); 41 protected RegistrantList mCallStateRegistrants = new RegistrantList(); 42 protected RegistrantList mVoiceNetworkStateRegistrants = new RegistrantList(); 43 protected RegistrantList mDataNetworkStateRegistrants = new RegistrantList(); 44 protected RegistrantList mVoiceRadioTechChangedRegistrants = new RegistrantList(); 45 protected RegistrantList mIccStatusChangedRegistrants = new RegistrantList(); 46 protected RegistrantList mVoicePrivacyOnRegistrants = new RegistrantList(); 47 protected RegistrantList mVoicePrivacyOffRegistrants = new RegistrantList(); 48 protected Registrant mUnsolOemHookRawRegistrant; 49 protected RegistrantList mOtaProvisionRegistrants = new RegistrantList(); 50 protected RegistrantList mCallWaitingInfoRegistrants = new RegistrantList(); 51 protected RegistrantList mDisplayInfoRegistrants = new RegistrantList(); 52 protected RegistrantList mSignalInfoRegistrants = new RegistrantList(); 53 protected RegistrantList mNumberInfoRegistrants = new RegistrantList(); 54 protected RegistrantList mRedirNumInfoRegistrants = new RegistrantList(); 55 protected RegistrantList mLineControlInfoRegistrants = new RegistrantList(); 56 protected RegistrantList mT53ClirInfoRegistrants = new RegistrantList(); 57 protected RegistrantList mT53AudCntrlInfoRegistrants = new RegistrantList(); 58 protected RegistrantList mRingbackToneRegistrants = new RegistrantList(); 59 protected RegistrantList mResendIncallMuteRegistrants = new RegistrantList(); 60 protected RegistrantList mCdmaSubscriptionChangedRegistrants = new RegistrantList(); 61 protected RegistrantList mCdmaPrlChangedRegistrants = new RegistrantList(); 62 protected RegistrantList mExitEmergencyCallbackModeRegistrants = new RegistrantList(); 63 protected RegistrantList mRilConnectedRegistrants = new RegistrantList(); 64 protected RegistrantList mIccRefreshRegistrants = new RegistrantList(); 65 protected RegistrantList mRilCellInfoListRegistrants = new RegistrantList(); 66 67 protected Registrant mGsmSmsRegistrant; 68 protected Registrant mCdmaSmsRegistrant; 69 protected Registrant mNITZTimeRegistrant; 70 protected Registrant mSignalStrengthRegistrant; 71 protected Registrant mUSSDRegistrant; 72 protected Registrant mSmsOnSimRegistrant; 73 protected Registrant mSmsStatusRegistrant; 74 protected Registrant mSsnRegistrant; 75 protected Registrant mCatSessionEndRegistrant; 76 protected Registrant mCatProCmdRegistrant; 77 protected Registrant mCatEventRegistrant; 78 protected Registrant mCatCallSetUpRegistrant; 79 protected Registrant mIccSmsFullRegistrant; 80 protected Registrant mEmergencyCallbackModeRegistrant; 81 protected Registrant mRingRegistrant; 82 protected Registrant mRestrictedStateRegistrant; 83 protected Registrant mGsmBroadcastSmsRegistrant; 84 85 // Preferred network type received from PhoneFactory. 86 // This is used when establishing a connection to the 87 // vendor ril so it starts up in the correct mode. 88 protected int mPreferredNetworkType; 89 // CDMA subscription received from PhoneFactory 90 protected int mCdmaSubscription; 91 // Type of Phone, GSM or CDMA. Set by CDMAPhone or GSMPhone. 92 protected int mPhoneType; 93 // RIL Version 94 protected int mRilVersion = -1; 95 96 public BaseCommands(Context context) { 97 mContext = context; // May be null (if so we won't log statistics) 98 } 99 100 //***** CommandsInterface implementation 101 102 @Override 103 public RadioState getRadioState() { 104 return mState; 105 } 106 107 @Override 108 public void registerForRadioStateChanged(Handler h, int what, Object obj) { 109 Registrant r = new Registrant (h, what, obj); 110 111 synchronized (mStateMonitor) { 112 mRadioStateChangedRegistrants.add(r); 113 r.notifyRegistrant(); 114 } 115 } 116 117 @Override 118 public void unregisterForRadioStateChanged(Handler h) { 119 synchronized (mStateMonitor) { 120 mRadioStateChangedRegistrants.remove(h); 121 } 122 } 123 124 @Override 125 public void registerForOn(Handler h, int what, Object obj) { 126 Registrant r = new Registrant (h, what, obj); 127 128 synchronized (mStateMonitor) { 129 mOnRegistrants.add(r); 130 131 if (mState.isOn()) { 132 r.notifyRegistrant(new AsyncResult(null, null, null)); 133 } 134 } 135 } 136 @Override 137 public void unregisterForOn(Handler h) { 138 synchronized (mStateMonitor) { 139 mOnRegistrants.remove(h); 140 } 141 } 142 143 144 @Override 145 public void registerForAvailable(Handler h, int what, Object obj) { 146 Registrant r = new Registrant (h, what, obj); 147 148 synchronized (mStateMonitor) { 149 mAvailRegistrants.add(r); 150 151 if (mState.isAvailable()) { 152 r.notifyRegistrant(new AsyncResult(null, null, null)); 153 } 154 } 155 } 156 157 @Override 158 public void unregisterForAvailable(Handler h) { 159 synchronized(mStateMonitor) { 160 mAvailRegistrants.remove(h); 161 } 162 } 163 164 @Override 165 public void registerForNotAvailable(Handler h, int what, Object obj) { 166 Registrant r = new Registrant (h, what, obj); 167 168 synchronized (mStateMonitor) { 169 mNotAvailRegistrants.add(r); 170 171 if (!mState.isAvailable()) { 172 r.notifyRegistrant(new AsyncResult(null, null, null)); 173 } 174 } 175 } 176 177 @Override 178 public void unregisterForNotAvailable(Handler h) { 179 synchronized (mStateMonitor) { 180 mNotAvailRegistrants.remove(h); 181 } 182 } 183 184 @Override 185 public void registerForOffOrNotAvailable(Handler h, int what, Object obj) { 186 Registrant r = new Registrant (h, what, obj); 187 188 synchronized (mStateMonitor) { 189 mOffOrNotAvailRegistrants.add(r); 190 191 if (mState == RadioState.RADIO_OFF || !mState.isAvailable()) { 192 r.notifyRegistrant(new AsyncResult(null, null, null)); 193 } 194 } 195 } 196 @Override 197 public void unregisterForOffOrNotAvailable(Handler h) { 198 synchronized(mStateMonitor) { 199 mOffOrNotAvailRegistrants.remove(h); 200 } 201 } 202 203 @Override 204 public void registerForCallStateChanged(Handler h, int what, Object obj) { 205 Registrant r = new Registrant (h, what, obj); 206 207 mCallStateRegistrants.add(r); 208 } 209 210 @Override 211 public void unregisterForCallStateChanged(Handler h) { 212 mCallStateRegistrants.remove(h); 213 } 214 215 @Override 216 public void registerForVoiceNetworkStateChanged(Handler h, int what, Object obj) { 217 Registrant r = new Registrant (h, what, obj); 218 219 mVoiceNetworkStateRegistrants.add(r); 220 } 221 222 @Override 223 public void unregisterForVoiceNetworkStateChanged(Handler h) { 224 mVoiceNetworkStateRegistrants.remove(h); 225 } 226 227 @Override 228 public void registerForDataNetworkStateChanged(Handler h, int what, Object obj) { 229 Registrant r = new Registrant (h, what, obj); 230 231 mDataNetworkStateRegistrants.add(r); 232 } 233 234 @Override 235 public void unregisterForDataNetworkStateChanged(Handler h) { 236 mDataNetworkStateRegistrants.remove(h); 237 } 238 239 @Override 240 public void registerForVoiceRadioTechChanged(Handler h, int what, Object obj) { 241 Registrant r = new Registrant (h, what, obj); 242 mVoiceRadioTechChangedRegistrants.add(r); 243 } 244 245 @Override 246 public void unregisterForVoiceRadioTechChanged(Handler h) { 247 mVoiceRadioTechChangedRegistrants.remove(h); 248 } 249 250 @Override 251 public void registerForIccStatusChanged(Handler h, int what, Object obj) { 252 Registrant r = new Registrant (h, what, obj); 253 mIccStatusChangedRegistrants.add(r); 254 } 255 256 @Override 257 public void unregisterForIccStatusChanged(Handler h) { 258 mIccStatusChangedRegistrants.remove(h); 259 } 260 261 @Override 262 public void setOnNewGsmSms(Handler h, int what, Object obj) { 263 mGsmSmsRegistrant = new Registrant (h, what, obj); 264 } 265 266 @Override 267 public void unSetOnNewGsmSms(Handler h) { 268 mGsmSmsRegistrant.clear(); 269 } 270 271 @Override 272 public void setOnNewCdmaSms(Handler h, int what, Object obj) { 273 mCdmaSmsRegistrant = new Registrant (h, what, obj); 274 } 275 276 @Override 277 public void unSetOnNewCdmaSms(Handler h) { 278 mCdmaSmsRegistrant.clear(); 279 } 280 281 @Override 282 public void setOnNewGsmBroadcastSms(Handler h, int what, Object obj) { 283 mGsmBroadcastSmsRegistrant = new Registrant (h, what, obj); 284 } 285 286 @Override 287 public void unSetOnNewGsmBroadcastSms(Handler h) { 288 mGsmBroadcastSmsRegistrant.clear(); 289 } 290 291 @Override 292 public void setOnSmsOnSim(Handler h, int what, Object obj) { 293 mSmsOnSimRegistrant = new Registrant (h, what, obj); 294 } 295 296 @Override 297 public void unSetOnSmsOnSim(Handler h) { 298 mSmsOnSimRegistrant.clear(); 299 } 300 301 @Override 302 public void setOnSmsStatus(Handler h, int what, Object obj) { 303 mSmsStatusRegistrant = new Registrant (h, what, obj); 304 } 305 306 @Override 307 public void unSetOnSmsStatus(Handler h) { 308 mSmsStatusRegistrant.clear(); 309 } 310 311 @Override 312 public void setOnSignalStrengthUpdate(Handler h, int what, Object obj) { 313 mSignalStrengthRegistrant = new Registrant (h, what, obj); 314 } 315 316 @Override 317 public void unSetOnSignalStrengthUpdate(Handler h) { 318 mSignalStrengthRegistrant.clear(); 319 } 320 321 @Override 322 public void setOnNITZTime(Handler h, int what, Object obj) { 323 mNITZTimeRegistrant = new Registrant (h, what, obj); 324 } 325 326 @Override 327 public void unSetOnNITZTime(Handler h) { 328 mNITZTimeRegistrant.clear(); 329 } 330 331 @Override 332 public void setOnUSSD(Handler h, int what, Object obj) { 333 mUSSDRegistrant = new Registrant (h, what, obj); 334 } 335 336 @Override 337 public void unSetOnUSSD(Handler h) { 338 mUSSDRegistrant.clear(); 339 } 340 341 @Override 342 public void setOnSuppServiceNotification(Handler h, int what, Object obj) { 343 mSsnRegistrant = new Registrant (h, what, obj); 344 } 345 346 @Override 347 public void unSetOnSuppServiceNotification(Handler h) { 348 mSsnRegistrant.clear(); 349 } 350 351 @Override 352 public void setOnCatSessionEnd(Handler h, int what, Object obj) { 353 mCatSessionEndRegistrant = new Registrant (h, what, obj); 354 } 355 356 @Override 357 public void unSetOnCatSessionEnd(Handler h) { 358 mCatSessionEndRegistrant.clear(); 359 } 360 361 @Override 362 public void setOnCatProactiveCmd(Handler h, int what, Object obj) { 363 mCatProCmdRegistrant = new Registrant (h, what, obj); 364 } 365 366 @Override 367 public void unSetOnCatProactiveCmd(Handler h) { 368 mCatProCmdRegistrant.clear(); 369 } 370 371 @Override 372 public void setOnCatEvent(Handler h, int what, Object obj) { 373 mCatEventRegistrant = new Registrant (h, what, obj); 374 } 375 376 @Override 377 public void unSetOnCatEvent(Handler h) { 378 mCatEventRegistrant.clear(); 379 } 380 381 @Override 382 public void setOnCatCallSetUp(Handler h, int what, Object obj) { 383 mCatCallSetUpRegistrant = new Registrant (h, what, obj); 384 } 385 386 @Override 387 public void unSetOnCatCallSetUp(Handler h) { 388 mCatCallSetUpRegistrant.clear(); 389 } 390 391 @Override 392 public void setOnIccSmsFull(Handler h, int what, Object obj) { 393 mIccSmsFullRegistrant = new Registrant (h, what, obj); 394 } 395 396 @Override 397 public void unSetOnIccSmsFull(Handler h) { 398 mIccSmsFullRegistrant.clear(); 399 } 400 401 @Override 402 public void registerForIccRefresh(Handler h, int what, Object obj) { 403 Registrant r = new Registrant (h, what, obj); 404 mIccRefreshRegistrants.add(r); 405 } 406 @Override 407 public void setOnIccRefresh(Handler h, int what, Object obj) { 408 registerForIccRefresh(h, what, obj); 409 } 410 411 @Override 412 public void setEmergencyCallbackMode(Handler h, int what, Object obj) { 413 mEmergencyCallbackModeRegistrant = new Registrant (h, what, obj); 414 } 415 416 @Override 417 public void unregisterForIccRefresh(Handler h) { 418 mIccRefreshRegistrants.remove(h); 419 } 420 @Override 421 public void unsetOnIccRefresh(Handler h) { 422 unregisterForIccRefresh(h); 423 } 424 425 @Override 426 public void setOnCallRing(Handler h, int what, Object obj) { 427 mRingRegistrant = new Registrant (h, what, obj); 428 } 429 430 @Override 431 public void unSetOnCallRing(Handler h) { 432 mRingRegistrant.clear(); 433 } 434 435 @Override 436 public void registerForInCallVoicePrivacyOn(Handler h, int what, Object obj) { 437 Registrant r = new Registrant (h, what, obj); 438 mVoicePrivacyOnRegistrants.add(r); 439 } 440 441 @Override 442 public void unregisterForInCallVoicePrivacyOn(Handler h){ 443 mVoicePrivacyOnRegistrants.remove(h); 444 } 445 446 @Override 447 public void registerForInCallVoicePrivacyOff(Handler h, int what, Object obj) { 448 Registrant r = new Registrant (h, what, obj); 449 mVoicePrivacyOffRegistrants.add(r); 450 } 451 452 @Override 453 public void unregisterForInCallVoicePrivacyOff(Handler h){ 454 mVoicePrivacyOffRegistrants.remove(h); 455 } 456 457 @Override 458 public void setOnRestrictedStateChanged(Handler h, int what, Object obj) { 459 mRestrictedStateRegistrant = new Registrant (h, what, obj); 460 } 461 462 @Override 463 public void unSetOnRestrictedStateChanged(Handler h) { 464 mRestrictedStateRegistrant.clear(); 465 } 466 467 @Override 468 public void registerForDisplayInfo(Handler h, int what, Object obj) { 469 Registrant r = new Registrant (h, what, obj); 470 mDisplayInfoRegistrants.add(r); 471 } 472 473 @Override 474 public void unregisterForDisplayInfo(Handler h) { 475 mDisplayInfoRegistrants.remove(h); 476 } 477 478 @Override 479 public void registerForCallWaitingInfo(Handler h, int what, Object obj) { 480 Registrant r = new Registrant (h, what, obj); 481 mCallWaitingInfoRegistrants.add(r); 482 } 483 484 @Override 485 public void unregisterForCallWaitingInfo(Handler h) { 486 mCallWaitingInfoRegistrants.remove(h); 487 } 488 489 @Override 490 public void registerForSignalInfo(Handler h, int what, Object obj) { 491 Registrant r = new Registrant (h, what, obj); 492 mSignalInfoRegistrants.add(r); 493 } 494 495 public void setOnUnsolOemHookRaw(Handler h, int what, Object obj) { 496 mUnsolOemHookRawRegistrant = new Registrant (h, what, obj); 497 } 498 499 public void unSetOnUnsolOemHookRaw(Handler h) { 500 mUnsolOemHookRawRegistrant.clear(); 501 } 502 503 @Override 504 public void unregisterForSignalInfo(Handler h) { 505 mSignalInfoRegistrants.remove(h); 506 } 507 508 @Override 509 public void registerForCdmaOtaProvision(Handler h,int what, Object obj){ 510 Registrant r = new Registrant (h, what, obj); 511 mOtaProvisionRegistrants.add(r); 512 } 513 514 @Override 515 public void unregisterForCdmaOtaProvision(Handler h){ 516 mOtaProvisionRegistrants.remove(h); 517 } 518 519 @Override 520 public void registerForNumberInfo(Handler h,int what, Object obj) { 521 Registrant r = new Registrant (h, what, obj); 522 mNumberInfoRegistrants.add(r); 523 } 524 525 @Override 526 public void unregisterForNumberInfo(Handler h){ 527 mNumberInfoRegistrants.remove(h); 528 } 529 530 @Override 531 public void registerForRedirectedNumberInfo(Handler h,int what, Object obj) { 532 Registrant r = new Registrant (h, what, obj); 533 mRedirNumInfoRegistrants.add(r); 534 } 535 536 @Override 537 public void unregisterForRedirectedNumberInfo(Handler h) { 538 mRedirNumInfoRegistrants.remove(h); 539 } 540 541 @Override 542 public void registerForLineControlInfo(Handler h, int what, Object obj) { 543 Registrant r = new Registrant (h, what, obj); 544 mLineControlInfoRegistrants.add(r); 545 } 546 547 @Override 548 public void unregisterForLineControlInfo(Handler h) { 549 mLineControlInfoRegistrants.remove(h); 550 } 551 552 @Override 553 public void registerFoT53ClirlInfo(Handler h,int what, Object obj) { 554 Registrant r = new Registrant (h, what, obj); 555 mT53ClirInfoRegistrants.add(r); 556 } 557 558 @Override 559 public void unregisterForT53ClirInfo(Handler h) { 560 mT53ClirInfoRegistrants.remove(h); 561 } 562 563 @Override 564 public void registerForT53AudioControlInfo(Handler h,int what, Object obj) { 565 Registrant r = new Registrant (h, what, obj); 566 mT53AudCntrlInfoRegistrants.add(r); 567 } 568 569 @Override 570 public void unregisterForT53AudioControlInfo(Handler h) { 571 mT53AudCntrlInfoRegistrants.remove(h); 572 } 573 574 @Override 575 public void registerForRingbackTone(Handler h, int what, Object obj) { 576 Registrant r = new Registrant (h, what, obj); 577 mRingbackToneRegistrants.add(r); 578 } 579 580 @Override 581 public void unregisterForRingbackTone(Handler h) { 582 mRingbackToneRegistrants.remove(h); 583 } 584 585 @Override 586 public void registerForResendIncallMute(Handler h, int what, Object obj) { 587 Registrant r = new Registrant (h, what, obj); 588 mResendIncallMuteRegistrants.add(r); 589 } 590 591 @Override 592 public void unregisterForResendIncallMute(Handler h) { 593 mResendIncallMuteRegistrants.remove(h); 594 } 595 596 @Override 597 public void registerForCdmaSubscriptionChanged(Handler h, int what, Object obj) { 598 Registrant r = new Registrant (h, what, obj); 599 mCdmaSubscriptionChangedRegistrants.add(r); 600 } 601 602 @Override 603 public void unregisterForCdmaSubscriptionChanged(Handler h) { 604 mCdmaSubscriptionChangedRegistrants.remove(h); 605 } 606 607 @Override 608 public void registerForCdmaPrlChanged(Handler h, int what, Object obj) { 609 Registrant r = new Registrant (h, what, obj); 610 mCdmaPrlChangedRegistrants.add(r); 611 } 612 613 @Override 614 public void unregisterForCdmaPrlChanged(Handler h) { 615 mCdmaPrlChangedRegistrants.remove(h); 616 } 617 618 @Override 619 public void registerForExitEmergencyCallbackMode(Handler h, int what, Object obj) { 620 Registrant r = new Registrant (h, what, obj); 621 mExitEmergencyCallbackModeRegistrants.add(r); 622 } 623 624 @Override 625 public void unregisterForExitEmergencyCallbackMode(Handler h) { 626 mExitEmergencyCallbackModeRegistrants.remove(h); 627 } 628 629 /** 630 * {@inheritDoc} 631 */ 632 @Override 633 public void registerForRilConnected(Handler h, int what, Object obj) { 634 Registrant r = new Registrant (h, what, obj); 635 mRilConnectedRegistrants.add(r); 636 if (mRilVersion != -1) { 637 r.notifyRegistrant(new AsyncResult(null, new Integer(mRilVersion), null)); 638 } 639 } 640 641 @Override 642 public void unregisterForRilConnected(Handler h) { 643 mRilConnectedRegistrants.remove(h); 644 } 645 646 /** 647 * {@inheritDoc} 648 */ 649 @Override 650 public void setCurrentPreferredNetworkType() { 651 } 652 653 //***** Protected Methods 654 /** 655 * Store new RadioState and send notification based on the changes 656 * 657 * This function is called only by RIL.java when receiving unsolicited 658 * RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED 659 * 660 * RadioState has 3 values : RADIO_OFF, RADIO_UNAVAILABLE, RADIO_ON. 661 * 662 * @param newState new RadioState decoded from RIL_UNSOL_RADIO_STATE_CHANGED 663 */ 664 protected void setRadioState(RadioState newState) { 665 RadioState oldState; 666 667 synchronized (mStateMonitor) { 668 oldState = mState; 669 mState = newState; 670 671 if (oldState == mState) { 672 // no state transition 673 return; 674 } 675 676 mRadioStateChangedRegistrants.notifyRegistrants(); 677 678 if (mState.isAvailable() && !oldState.isAvailable()) { 679 mAvailRegistrants.notifyRegistrants(); 680 onRadioAvailable(); 681 } 682 683 if (!mState.isAvailable() && oldState.isAvailable()) { 684 mNotAvailRegistrants.notifyRegistrants(); 685 } 686 687 if (mState.isOn() && !oldState.isOn()) { 688 mOnRegistrants.notifyRegistrants(); 689 } 690 691 if ((!mState.isOn() || !mState.isAvailable()) 692 && !((!oldState.isOn() || !oldState.isAvailable())) 693 ) { 694 mOffOrNotAvailRegistrants.notifyRegistrants(); 695 } 696 } 697 } 698 699 protected void onRadioAvailable() { 700 } 701 702 /** 703 * {@inheritDoc} 704 */ 705 @Override 706 public int getLteOnCdmaMode() { 707 return TelephonyManager.getLteOnCdmaModeStatic(); 708 } 709 710 /** 711 * {@inheritDoc} 712 */ 713 @Override 714 public void registerForCellInfoList(Handler h, int what, Object obj) { 715 Registrant r = new Registrant (h, what, obj); 716 mRilCellInfoListRegistrants.add(r); 717 } 718 @Override 719 public void unregisterForCellInfoList(Handler h) { 720 mRilCellInfoListRegistrants.remove(h); 721 } 722 723 @Override 724 public void testingEmergencyCall() {} 725 726 @Override 727 public int getRilVersion() { 728 return mRilVersion; 729 } 730 } 731