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
17package android.app.backup;
18
19import android.annotation.RequiresPermission;
20import android.annotation.SystemApi;
21import android.content.ComponentName;
22import android.content.Context;
23import android.os.Bundle;
24import android.os.Handler;
25import android.os.Message;
26import android.os.RemoteException;
27import android.os.ServiceManager;
28import android.util.Log;
29import 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">&lt;application&gt;</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 */
62public 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