1 /* 2 * Copyright (C) 2009 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.app.backup; 18 19 import android.annotation.RequiresPermission; 20 import android.annotation.SystemApi; 21 import android.content.ComponentName; 22 import android.content.Context; 23 import android.os.Bundle; 24 import android.os.Handler; 25 import android.os.Message; 26 import android.os.RemoteException; 27 import android.os.ServiceManager; 28 import android.util.Log; 29 import android.util.Pair; 30 31 /** 32 * The interface through which an application interacts with the Android backup service to 33 * request backup and restore operations. 34 * Applications instantiate it using the constructor and issue calls through that instance. 35 * <p> 36 * When an application has made changes to data which should be backed up, a 37 * call to {@link #dataChanged()} will notify the backup service. The system 38 * will then schedule a backup operation to occur in the near future. Repeated 39 * calls to {@link #dataChanged()} have no further effect until the backup 40 * operation actually occurs. 41 * <p> 42 * A backup or restore operation for your application begins when the system launches the 43 * {@link android.app.backup.BackupAgent} subclass you've declared in your manifest. See the 44 * documentation for {@link android.app.backup.BackupAgent} for a detailed description 45 * of how the operation then proceeds. 46 * <p> 47 * Several attributes affecting the operation of the backup and restore mechanism 48 * can be set on the <code> 49 * <a href="{@docRoot}guide/topics/manifest/application-element.html"><application></a></code> 50 * tag in your application's AndroidManifest.xml file. 51 * 52 * <div class="special reference"> 53 * <h3>Developer Guides</h3> 54 * <p>For more information about using BackupManager, read the 55 * <a href="{@docRoot}guide/topics/data/backup.html">Data Backup</a> developer guide.</p></div> 56 * 57 * @attr ref android.R.styleable#AndroidManifestApplication_allowBackup 58 * @attr ref android.R.styleable#AndroidManifestApplication_backupAgent 59 * @attr ref android.R.styleable#AndroidManifestApplication_killAfterRestore 60 * @attr ref android.R.styleable#AndroidManifestApplication_restoreAnyVersion 61 */ 62 public class BackupManager { 63 private static final String TAG = "BackupManager"; 64 65 // BackupObserver status codes 66 /** 67 * Indicates that backup succeeded. 68 * 69 * @hide 70 */ 71 @SystemApi 72 public static final int SUCCESS = 0; 73 74 /** 75 * Indicates that backup is either not enabled at all or 76 * backup for the package was rejected by backup service 77 * or backup transport, 78 * 79 * @hide 80 */ 81 @SystemApi 82 public static final int ERROR_BACKUP_NOT_ALLOWED = -2001; 83 84 /** 85 * The requested app is not installed on the device. 86 * 87 * @hide 88 */ 89 @SystemApi 90 public static final int ERROR_PACKAGE_NOT_FOUND = -2002; 91 92 /** 93 * The backup operation was cancelled. 94 * 95 * @hide 96 */ 97 @SystemApi 98 public static final int ERROR_BACKUP_CANCELLED = -2003; 99 100 /** 101 * The transport for some reason was not in a good state and 102 * aborted the entire backup request. This is a transient 103 * failure and should not be retried immediately. 104 * 105 * @hide 106 */ 107 @SystemApi 108 public static final int ERROR_TRANSPORT_ABORTED = BackupTransport.TRANSPORT_ERROR; 109 110 /** 111 * Returned when the transport was unable to process the 112 * backup request for a given package, for example if the 113 * transport hit a transient network failure. The remaining 114 * packages provided to {@link #requestBackup(String[], BackupObserver)} 115 * will still be attempted. 116 * 117 * @hide 118 */ 119 @SystemApi 120 public static final int ERROR_TRANSPORT_PACKAGE_REJECTED = 121 BackupTransport.TRANSPORT_PACKAGE_REJECTED; 122 123 /** 124 * Returned when the transport reject the attempt to backup because 125 * backup data size exceeded current quota limit for this package. 126 * 127 * @hide 128 */ 129 @SystemApi 130 public static final int ERROR_TRANSPORT_QUOTA_EXCEEDED = 131 BackupTransport.TRANSPORT_QUOTA_EXCEEDED; 132 133 /** 134 * The {@link BackupAgent} for the requested package failed for some reason 135 * and didn't provide appropriate backup data. 136 * 137 * @hide 138 */ 139 @SystemApi 140 public static final int ERROR_AGENT_FAILURE = BackupTransport.AGENT_ERROR; 141 142 /** 143 * Intent extra when any subsidiary backup-related UI is launched from Settings: does 144 * device policy or configuration permit backup operations to run at all? 145 * 146 * @hide 147 */ 148 public static final String EXTRA_BACKUP_SERVICES_AVAILABLE = "backup_services_available"; 149 150 /** 151 * If this flag is passed to {@link #requestBackup(String[], BackupObserver, int)}, 152 * BackupManager will pass a blank old state to BackupAgents of requested packages. 153 * 154 * @hide 155 */ 156 @SystemApi 157 public static final int FLAG_NON_INCREMENTAL_BACKUP = 1; 158 159 /** 160 * Use with {@link #requestBackup} to force backup of 161 * package meta data. Typically you do not need to explicitly request this be backed up as it is 162 * handled internally by the BackupManager. If you are requesting backups with 163 * FLAG_NON_INCREMENTAL, this package won't automatically be backed up and you have to 164 * explicitly request for its backup. 165 * 166 * @hide 167 */ 168 @SystemApi 169 public static final String PACKAGE_MANAGER_SENTINEL = "@pm@"; 170 171 172 /** 173 * This error code is passed to {@link SelectBackupTransportCallback#onFailure(int)} 174 * if the requested transport is unavailable. 175 * 176 * @hide 177 */ 178 @SystemApi 179 public static final int ERROR_TRANSPORT_UNAVAILABLE = -1; 180 181 /** 182 * This error code is passed to {@link SelectBackupTransportCallback#onFailure(int)} if the 183 * requested transport is not a valid BackupTransport. 184 * 185 * @hide 186 */ 187 @SystemApi 188 public static final int ERROR_TRANSPORT_INVALID = -2; 189 190 private Context mContext; 191 private static IBackupManager sService; 192 193 private static void checkServiceBinder() { 194 if (sService == null) { 195 sService = IBackupManager.Stub.asInterface( 196 ServiceManager.getService(Context.BACKUP_SERVICE)); 197 } 198 } 199 200 /** 201 * Constructs a BackupManager object through which the application can 202 * communicate with the Android backup system. 203 * 204 * @param context The {@link android.content.Context} that was provided when 205 * one of your application's {@link android.app.Activity Activities} 206 * was created. 207 */ 208 public BackupManager(Context context) { 209 mContext = context; 210 } 211 212 /** 213 * Notifies the Android backup system that your application wishes to back up 214 * new changes to its data. A backup operation using your application's 215 * {@link android.app.backup.BackupAgent} subclass will be scheduled when you 216 * call this method. 217 */ 218 public void dataChanged() { 219 checkServiceBinder(); 220 if (sService != null) { 221 try { 222 sService.dataChanged(mContext.getPackageName()); 223 } catch (RemoteException e) { 224 Log.d(TAG, "dataChanged() couldn't connect"); 225 } 226 } 227 } 228 229 /** 230 * Convenience method for callers who need to indicate that some other package 231 * needs a backup pass. This can be useful in the case of groups of packages 232 * that share a uid. 233 * <p> 234 * This method requires that the application hold the "android.permission.BACKUP" 235 * permission if the package named in the argument does not run under the same uid 236 * as the caller. 237 * 238 * @param packageName The package name identifying the application to back up. 239 */ 240 public static void dataChanged(String packageName) { 241 checkServiceBinder(); 242 if (sService != null) { 243 try { 244 sService.dataChanged(packageName); 245 } catch (RemoteException e) { 246 Log.e(TAG, "dataChanged(pkg) couldn't connect"); 247 } 248 } 249 } 250 251 /** 252 * Restore the calling application from backup. The data will be restored from the 253 * current backup dataset if the application has stored data there, or from 254 * the dataset used during the last full device setup operation if the current 255 * backup dataset has no matching data. If no backup data exists for this application 256 * in either source, a nonzero value will be returned. 257 * 258 * <p>If this method returns zero (meaning success), the OS will attempt to retrieve 259 * a backed-up dataset from the remote transport, instantiate the application's 260 * backup agent, and pass the dataset to the agent's 261 * {@link android.app.backup.BackupAgent#onRestore(BackupDataInput, int, android.os.ParcelFileDescriptor) onRestore()} 262 * method. 263 * 264 * @param observer The {@link RestoreObserver} to receive callbacks during the restore 265 * operation. This must not be null. 266 * 267 * @return Zero on success; nonzero on error. 268 */ 269 public int requestRestore(RestoreObserver observer) { 270 return requestRestore(observer, null); 271 } 272 273 // system APIs start here 274 275 /** 276 * Restore the calling application from backup. The data will be restored from the 277 * current backup dataset if the application has stored data there, or from 278 * the dataset used during the last full device setup operation if the current 279 * backup dataset has no matching data. If no backup data exists for this application 280 * in either source, a nonzero value will be returned. 281 * 282 * <p>If this method returns zero (meaning success), the OS will attempt to retrieve 283 * a backed-up dataset from the remote transport, instantiate the application's 284 * backup agent, and pass the dataset to the agent's 285 * {@link android.app.backup.BackupAgent#onRestore(BackupDataInput, int, android.os.ParcelFileDescriptor) onRestore()} 286 * method. 287 * 288 * @param observer The {@link RestoreObserver} to receive callbacks during the restore 289 * operation. This must not be null. 290 * 291 * @param monitor the {@link BackupManagerMonitor} to receive callbacks during the restore 292 * operation. 293 * 294 * @return Zero on success; nonzero on error. 295 * 296 * @hide 297 */ 298 @SystemApi 299 public int requestRestore(RestoreObserver observer, BackupManagerMonitor monitor) { 300 int result = -1; 301 checkServiceBinder(); 302 if (sService != null) { 303 RestoreSession session = null; 304 try { 305 IRestoreSession binder = sService.beginRestoreSession(mContext.getPackageName(), 306 null); 307 if (binder != null) { 308 session = new RestoreSession(mContext, binder); 309 result = session.restorePackage(mContext.getPackageName(), observer, monitor); 310 } 311 } catch (RemoteException e) { 312 Log.e(TAG, "restoreSelf() unable to contact service"); 313 } finally { 314 if (session != null) { 315 session.endRestoreSession(); 316 } 317 } 318 } 319 return result; 320 } 321 322 /** 323 * Begin the process of restoring data from backup. See the 324 * {@link android.app.backup.RestoreSession} class for documentation on that process. 325 * @hide 326 */ 327 @SystemApi 328 @RequiresPermission(android.Manifest.permission.BACKUP) 329 public RestoreSession beginRestoreSession() { 330 RestoreSession session = null; 331 checkServiceBinder(); 332 if (sService != null) { 333 try { 334 // All packages, current transport 335 IRestoreSession binder = sService.beginRestoreSession(null, null); 336 if (binder != null) { 337 session = new RestoreSession(mContext, binder); 338 } 339 } catch (RemoteException e) { 340 Log.e(TAG, "beginRestoreSession() couldn't connect"); 341 } 342 } 343 return session; 344 } 345 346 /** 347 * Enable/disable the backup service entirely. When disabled, no backup 348 * or restore operations will take place. Data-changed notifications will 349 * still be observed and collected, however, so that changes made while the 350 * mechanism was disabled will still be backed up properly if it is enabled 351 * at some point in the future. 352 * 353 * @hide 354 */ 355 @SystemApi 356 @RequiresPermission(android.Manifest.permission.BACKUP) 357 public void setBackupEnabled(boolean isEnabled) { 358 checkServiceBinder(); 359 if (sService != null) { 360 try { 361 sService.setBackupEnabled(isEnabled); 362 } catch (RemoteException e) { 363 Log.e(TAG, "setBackupEnabled() couldn't connect"); 364 } 365 } 366 } 367 368 /** 369 * Report whether the backup mechanism is currently enabled. 370 * 371 * @hide 372 */ 373 @SystemApi 374 @RequiresPermission(android.Manifest.permission.BACKUP) 375 public boolean isBackupEnabled() { 376 checkServiceBinder(); 377 if (sService != null) { 378 try { 379 return sService.isBackupEnabled(); 380 } catch (RemoteException e) { 381 Log.e(TAG, "isBackupEnabled() couldn't connect"); 382 } 383 } 384 return false; 385 } 386 387 /** 388 * Enable/disable data restore at application install time. When enabled, app 389 * installation will include an attempt to fetch the app's historical data from 390 * the archival restore dataset (if any). When disabled, no such attempt will 391 * be made. 392 * 393 * @hide 394 */ 395 @SystemApi 396 @RequiresPermission(android.Manifest.permission.BACKUP) 397 public void setAutoRestore(boolean isEnabled) { 398 checkServiceBinder(); 399 if (sService != null) { 400 try { 401 sService.setAutoRestore(isEnabled); 402 } catch (RemoteException e) { 403 Log.e(TAG, "setAutoRestore() couldn't connect"); 404 } 405 } 406 } 407 408 /** 409 * Identify the currently selected transport. 410 * @return The name of the currently active backup transport. In case of 411 * failure or if no transport is currently active, this method returns {@code null}. 412 * 413 * @hide 414 */ 415 @SystemApi 416 @RequiresPermission(android.Manifest.permission.BACKUP) 417 public String getCurrentTransport() { 418 checkServiceBinder(); 419 if (sService != null) { 420 try { 421 return sService.getCurrentTransport(); 422 } catch (RemoteException e) { 423 Log.e(TAG, "getCurrentTransport() couldn't connect"); 424 } 425 } 426 return null; 427 } 428 429 /** 430 * Request a list of all available backup transports' names. 431 * 432 * @hide 433 */ 434 @SystemApi 435 @RequiresPermission(android.Manifest.permission.BACKUP) 436 public String[] listAllTransports() { 437 checkServiceBinder(); 438 if (sService != null) { 439 try { 440 return sService.listAllTransports(); 441 } catch (RemoteException e) { 442 Log.e(TAG, "listAllTransports() couldn't connect"); 443 } 444 } 445 return null; 446 } 447 448 /** 449 * Specify the current backup transport. 450 * 451 * @param transport The name of the transport to select. This should be one 452 * of the names returned by {@link #listAllTransports()}. This is the String returned by 453 * {@link BackupTransport#name()} for the particular transport. 454 * @return The name of the previously selected transport. If the given transport 455 * name is not one of the currently available transports, no change is made to 456 * the current transport setting and the method returns null. 457 * 458 * @hide 459 */ 460 @Deprecated 461 @SystemApi 462 @RequiresPermission(android.Manifest.permission.BACKUP) 463 public String selectBackupTransport(String transport) { 464 checkServiceBinder(); 465 if (sService != null) { 466 try { 467 return sService.selectBackupTransport(transport); 468 } catch (RemoteException e) { 469 Log.e(TAG, "selectBackupTransport() couldn't connect"); 470 } 471 } 472 return null; 473 } 474 475 /** 476 * Specify the current backup transport and get notified when the transport is ready to be used. 477 * This method is async because BackupManager might need to bind to the specified transport 478 * which is in a separate process. 479 * 480 * @param transport ComponentName of the service hosting the transport. This is different from 481 * the transport's name that is returned by {@link BackupTransport#name()}. 482 * @param listener A listener object to get a callback on the transport being selected. 483 * 484 * @hide 485 */ 486 @SystemApi 487 @RequiresPermission(android.Manifest.permission.BACKUP) 488 public void selectBackupTransport(ComponentName transport, 489 SelectBackupTransportCallback listener) { 490 checkServiceBinder(); 491 if (sService != null) { 492 try { 493 SelectTransportListenerWrapper wrapper = listener == null ? 494 null : new SelectTransportListenerWrapper(mContext, listener); 495 sService.selectBackupTransportAsync(transport, wrapper); 496 } catch (RemoteException e) { 497 Log.e(TAG, "selectBackupTransportAsync() couldn't connect"); 498 } 499 } 500 } 501 502 /** 503 * Schedule an immediate backup attempt for all pending key/value updates. This 504 * is primarily intended for transports to use when they detect a suitable 505 * opportunity for doing a backup pass. If there are no pending updates to 506 * be sent, no action will be taken. Even if some updates are pending, the 507 * transport will still be asked to confirm via the usual requestBackupTime() 508 * method. 509 * 510 * @hide 511 */ 512 @SystemApi 513 @RequiresPermission(android.Manifest.permission.BACKUP) 514 public void backupNow() { 515 checkServiceBinder(); 516 if (sService != null) { 517 try { 518 sService.backupNow(); 519 } catch (RemoteException e) { 520 Log.e(TAG, "backupNow() couldn't connect"); 521 } 522 } 523 } 524 525 /** 526 * Ask the framework which dataset, if any, the given package's data would be 527 * restored from if we were to install it right now. 528 * 529 * @param packageName The name of the package whose most-suitable dataset we 530 * wish to look up 531 * @return The dataset token from which a restore should be attempted, or zero if 532 * no suitable data is available. 533 * 534 * @hide 535 */ 536 @SystemApi 537 @RequiresPermission(android.Manifest.permission.BACKUP) 538 public long getAvailableRestoreToken(String packageName) { 539 checkServiceBinder(); 540 if (sService != null) { 541 try { 542 return sService.getAvailableRestoreToken(packageName); 543 } catch (RemoteException e) { 544 Log.e(TAG, "getAvailableRestoreToken() couldn't connect"); 545 } 546 } 547 return 0; 548 } 549 550 /** 551 * Ask the framework whether this app is eligible for backup. 552 * 553 * @param packageName The name of the package. 554 * @return Whether this app is eligible for backup. 555 * 556 * @hide 557 */ 558 @SystemApi 559 @RequiresPermission(android.Manifest.permission.BACKUP) 560 public boolean isAppEligibleForBackup(String packageName) { 561 checkServiceBinder(); 562 if (sService != null) { 563 try { 564 return sService.isAppEligibleForBackup(packageName); 565 } catch (RemoteException e) { 566 Log.e(TAG, "isAppEligibleForBackup(pkg) couldn't connect"); 567 } 568 } 569 return false; 570 } 571 572 /** 573 * Request an immediate backup, providing an observer to which results of the backup operation 574 * will be published. The Android backup system will decide for each package whether it will 575 * be full app data backup or key/value-pair-based backup. 576 * 577 * <p>If this method returns {@link BackupManager#SUCCESS}, the OS will attempt to backup all 578 * provided packages using the remote transport. 579 * 580 * @param packages List of package names to backup. 581 * @param observer The {@link BackupObserver} to receive callbacks during the backup 582 * operation. Could be {@code null}. 583 * @return {@link BackupManager#SUCCESS} on success; nonzero on error. 584 * @exception IllegalArgumentException on null or empty {@code packages} param. 585 * 586 * @hide 587 */ 588 @SystemApi 589 @RequiresPermission(android.Manifest.permission.BACKUP) 590 public int requestBackup(String[] packages, BackupObserver observer) { 591 return requestBackup(packages, observer, null, 0); 592 } 593 594 /** 595 * Request an immediate backup, providing an observer to which results of the backup operation 596 * will be published. The Android backup system will decide for each package whether it will 597 * be full app data backup or key/value-pair-based backup. 598 * 599 * <p>If this method returns {@link BackupManager#SUCCESS}, the OS will attempt to backup all 600 * provided packages using the remote transport. 601 * 602 * @param packages List of package names to backup. 603 * @param observer The {@link BackupObserver} to receive callbacks during the backup 604 * operation. Could be {@code null}. 605 * @param monitor The {@link BackupManagerMonitorWrapper} to receive callbacks of important 606 * events during the backup operation. Could be {@code null}. 607 * @param flags {@link #FLAG_NON_INCREMENTAL_BACKUP}. 608 * @return {@link BackupManager#SUCCESS} on success; nonzero on error. 609 * @throws IllegalArgumentException on null or empty {@code packages} param. 610 * @hide 611 */ 612 @SystemApi 613 @RequiresPermission(android.Manifest.permission.BACKUP) 614 public int requestBackup(String[] packages, BackupObserver observer, 615 BackupManagerMonitor monitor, int flags) { 616 checkServiceBinder(); 617 if (sService != null) { 618 try { 619 BackupObserverWrapper observerWrapper = observer == null 620 ? null 621 : new BackupObserverWrapper(mContext, observer); 622 BackupManagerMonitorWrapper monitorWrapper = monitor == null 623 ? null 624 : new BackupManagerMonitorWrapper(monitor); 625 return sService.requestBackup(packages, observerWrapper, monitorWrapper, flags); 626 } catch (RemoteException e) { 627 Log.e(TAG, "requestBackup() couldn't connect"); 628 } 629 } 630 return -1; 631 } 632 633 /** 634 * Cancel all running backups. After this call returns, no currently running backups will 635 * interact with the selected transport. 636 * 637 * @hide 638 */ 639 @SystemApi 640 @RequiresPermission(android.Manifest.permission.BACKUP) 641 public void cancelBackups() { 642 checkServiceBinder(); 643 if (sService != null) { 644 try { 645 sService.cancelBackups(); 646 } catch (RemoteException e) { 647 Log.e(TAG, "cancelBackups() couldn't connect."); 648 } 649 } 650 } 651 652 /* 653 * We wrap incoming binder calls with a private class implementation that 654 * redirects them into main-thread actions. This serializes the backup 655 * progress callbacks nicely within the usual main-thread lifecycle pattern. 656 */ 657 @SystemApi 658 private class BackupObserverWrapper extends IBackupObserver.Stub { 659 final Handler mHandler; 660 final BackupObserver mObserver; 661 662 static final int MSG_UPDATE = 1; 663 static final int MSG_RESULT = 2; 664 static final int MSG_FINISHED = 3; 665 666 BackupObserverWrapper(Context context, BackupObserver observer) { 667 mHandler = new Handler(context.getMainLooper()) { 668 @Override 669 public void handleMessage(Message msg) { 670 switch (msg.what) { 671 case MSG_UPDATE: 672 Pair<String, BackupProgress> obj = 673 (Pair<String, BackupProgress>) msg.obj; 674 mObserver.onUpdate(obj.first, obj.second); 675 break; 676 case MSG_RESULT: 677 mObserver.onResult((String)msg.obj, msg.arg1); 678 break; 679 case MSG_FINISHED: 680 mObserver.backupFinished(msg.arg1); 681 break; 682 default: 683 Log.w(TAG, "Unknown message: " + msg); 684 break; 685 } 686 } 687 }; 688 mObserver = observer; 689 } 690 691 // Binder calls into this object just enqueue on the main-thread handler 692 @Override 693 public void onUpdate(String currentPackage, BackupProgress backupProgress) { 694 mHandler.sendMessage( 695 mHandler.obtainMessage(MSG_UPDATE, Pair.create(currentPackage, backupProgress))); 696 } 697 698 @Override 699 public void onResult(String currentPackage, int status) { 700 mHandler.sendMessage( 701 mHandler.obtainMessage(MSG_RESULT, status, 0, currentPackage)); 702 } 703 704 @Override 705 public void backupFinished(int status) { 706 mHandler.sendMessage( 707 mHandler.obtainMessage(MSG_FINISHED, status, 0)); 708 } 709 } 710 711 private class SelectTransportListenerWrapper extends ISelectBackupTransportCallback.Stub { 712 713 private final Handler mHandler; 714 private final SelectBackupTransportCallback mListener; 715 716 SelectTransportListenerWrapper(Context context, SelectBackupTransportCallback listener) { 717 mHandler = new Handler(context.getMainLooper()); 718 mListener = listener; 719 } 720 721 @Override 722 public void onSuccess(final String transportName) { 723 mHandler.post(new Runnable() { 724 @Override 725 public void run() { 726 mListener.onSuccess(transportName); 727 } 728 }); 729 } 730 731 @Override 732 public void onFailure(final int reason) { 733 mHandler.post(new Runnable() { 734 @Override 735 public void run() { 736 mListener.onFailure(reason); 737 } 738 }); 739 } 740 } 741 742 private class BackupManagerMonitorWrapper extends IBackupManagerMonitor.Stub { 743 final BackupManagerMonitor mMonitor; 744 745 BackupManagerMonitorWrapper(BackupManagerMonitor monitor) { 746 mMonitor = monitor; 747 } 748 749 @Override 750 public void onEvent(final Bundle event) throws RemoteException { 751 mMonitor.onEvent(event); 752 } 753 } 754 755 } 756