@Override
 public void handleSuccess(GlobalSession globalSession) {
   Logger.info(LOG_TAG, "GlobalSession indicated success.");
   Logger.debug(LOG_TAG, "Prefs target: " + globalSession.config.prefsPath);
   globalSession.config.persistToPrefs();
   notifyMonitor();
 }
Exemplo n.º 2
0
  /**
   * Reset timestamps and possibly set syncID.
   *
   * @param syncID if non-null, new syncID to persist.
   */
  protected void resetLocal(String syncID) {
    // Clear both timestamps.
    SynchronizerConfiguration config;
    try {
      config = this.getConfig();
    } catch (Exception e) {
      Logger.warn(LOG_TAG, "Unable to reset " + this + ": fetching config failed.", e);
      return;
    }

    if (syncID != null) {
      config.syncID = syncID;
      Logger.info(LOG_TAG, "Setting syncID for " + this + " to '" + syncID + "'.");
    }
    config.localBundle.setTimestamp(0L);
    config.remoteBundle.setTimestamp(0L);
    persistConfig(config);
    Logger.info(LOG_TAG, "Reset timestamps for " + this);
  }
 public void finish(final RepositorySessionFinishDelegate delegate) {
   if (this.status == SessionStatus.ACTIVE) {
     this.status = SessionStatus.DONE;
     delegate.deferredFinishDelegate(delegateQueue).onFinishSucceeded(this, this.getBundle(null));
   } else {
     Logger.error(LOG_TAG, "Tried to finish() an unstarted or already finished session");
     Exception e = new InvalidSessionTransitionException(null);
     delegate.deferredFinishDelegate(delegateQueue).onFinishFailed(e);
   }
   Logger.info(LOG_TAG, "Shutting down work queues.");
   //   storeWorkQueue.shutdown();
   //   delegateQueue.shutdown();
 }
Exemplo n.º 4
0
  /**
   * We synced this engine! Persist timestamps and advance the session.
   *
   * @param synchronizer the <code>Synchronizer</code> that succeeded.
   */
  @Override
  public void onSynchronized(Synchronizer synchronizer) {
    Logger.debug(LOG_TAG, "onSynchronized.");

    SynchronizerConfiguration newConfig = synchronizer.save();
    if (newConfig != null) {
      persistConfig(newConfig);
    } else {
      Logger.warn(LOG_TAG, "Didn't get configuration from synchronizer after success.");
    }

    Logger.info(LOG_TAG, "Advancing session.");
    session.advance();
  }
Exemplo n.º 5
0
  /**
   * Synchronously wipe the server.
   *
   * <p>Logs and re-throws an exception on failure.
   */
  public void wipeServer() throws Exception {
    final WipeWaiter monitor = new WipeWaiter();

    final Runnable doWipe =
        new Runnable() {
          @Override
          public void run() {
            wipeServer(
                session,
                new WipeServerDelegate() {
                  @Override
                  public void onWiped(long timestamp) {
                    synchronized (monitor) {
                      monitor.notify();
                    }
                  }

                  @Override
                  public void onWipeFailed(Exception e) {
                    synchronized (monitor) {
                      monitor.notify(e, false);
                    }
                  }
                });
          }
        };

    final Thread wiping = new Thread(doWipe);
    synchronized (monitor) {
      wiping.start();
      try {
        monitor.wait();
      } catch (InterruptedException e) {
        Logger.error(LOG_TAG, "Server wipe interrupted.");
      }
    }

    if (!monitor.wipeSucceeded) {
      Logger.error(LOG_TAG, "Failed to wipe server.");
      throw monitor.error;
    }

    Logger.info(LOG_TAG, "Wiping server complete.");
  }
Exemplo n.º 6
0
  /**
   * We failed to sync this engine! Do not persist timestamps (which means that the next sync will
   * include this sync's data), but do advance the session (if we didn't get a Retry-After header).
   *
   * @param synchronizer the <code>Synchronizer</code> that failed.
   */
  @Override
  public void onSynchronizeFailed(
      Synchronizer synchronizer, Exception lastException, String reason) {
    Logger.warn(LOG_TAG, "Synchronize failed: " + reason, lastException);

    // This failure could be due to a 503 or a 401 and it could have headers.
    // Interrogate the headers but only abort the global session if Retry-After header is set.
    if (lastException instanceof HTTPFailureException) {
      SyncStorageResponse response = ((HTTPFailureException) lastException).response;
      if (response.retryAfterInSeconds() > 0) {
        session.handleHTTPError(response, reason); // Calls session.abort().
        return;
      } else {
        session.interpretHTTPFailure(response.httpResponse()); // Does not call session.abort().
      }
    }

    Logger.info(LOG_TAG, "Advancing session even though stage failed. Timestamps not persisted.");
    session.advance();
  }
Exemplo n.º 7
0
  @Override
  public void execute() throws NoSuchStageException {
    final String name = getEngineName();
    Logger.debug(LOG_TAG, "Starting execute for " + name);

    try {
      if (!this.isEnabled()) {
        Logger.info(LOG_TAG, "Skipping stage " + name + ".");
        session.advance();
        return;
      }
    } catch (MetaGlobalException.MetaGlobalMalformedSyncIDException e) {
      // Bad engine syncID. This should never happen. Wipe the server.
      try {
        session.updateMetaGlobalWith(
            name, new EngineSettings(Utils.generateGuid(), this.getStorageVersion()));
        Logger.info(
            LOG_TAG, "Wiping server because malformed engine sync ID was found in meta/global.");
        wipeServer();
        Logger.info(LOG_TAG, "Wiped server after malformed engine sync ID found in meta/global.");
      } catch (Exception ex) {
        session.abort(
            ex, "Failed to wipe server after malformed engine sync ID found in meta/global.");
      }
    } catch (MetaGlobalException.MetaGlobalMalformedVersionException e) {
      // Bad engine version. This should never happen. Wipe the server.
      try {
        session.updateMetaGlobalWith(
            name, new EngineSettings(Utils.generateGuid(), this.getStorageVersion()));
        Logger.info(
            LOG_TAG, "Wiping server because malformed engine version was found in meta/global.");
        wipeServer();
        Logger.info(LOG_TAG, "Wiped server after malformed engine version found in meta/global.");
      } catch (Exception ex) {
        session.abort(
            ex, "Failed to wipe server after malformed engine version found in meta/global.");
      }
    } catch (MetaGlobalException.MetaGlobalStaleClientSyncIDException e) {
      // Our syncID is wrong. Reset client and take the server syncID.
      Logger.warn(
          LOG_TAG,
          "Remote engine syncID different from local engine syncID:"
              + " resetting local engine and assuming remote engine syncID.");
      this.resetLocal(e.serverSyncID);
    } catch (MetaGlobalException e) {
      session.abort(e, "Inappropriate meta/global; refusing to execute " + name + " stage.");
      return;
    }

    Synchronizer synchronizer;
    try {
      synchronizer = this.getConfiguredSynchronizer(session);
    } catch (NoCollectionKeysSetException e) {
      session.abort(e, "No CollectionKeys.");
      return;
    } catch (URISyntaxException e) {
      session.abort(e, "Invalid URI syntax for server repository.");
      return;
    } catch (NonObjectJSONException e) {
      session.abort(e, "Invalid persisted JSON for config.");
      return;
    } catch (IOException e) {
      session.abort(e, "Invalid persisted JSON for config.");
      return;
    } catch (ParseException e) {
      session.abort(e, "Invalid persisted JSON for config.");
      return;
    }

    Logger.debug(LOG_TAG, "Invoking synchronizer.");
    synchronizer.synchronize(session.getContext(), this);
    Logger.debug(LOG_TAG, "Reached end of execute.");
  }
Exemplo n.º 8
0
  /**
   * Synchronously wipe this stage by instantiating a local repository session and wiping that.
   *
   * <p>Logs and re-throws an exception on failure.
   */
  @Override
  public void wipeLocal() throws Exception {
    // Reset, then clear data.
    this.resetLocal();

    final WipeWaiter monitor = new WipeWaiter();
    final Context context = session.getContext();
    final Repository r = this.getLocalRepository();

    final Runnable doWipe =
        new Runnable() {
          @Override
          public void run() {
            r.createSession(
                new RepositorySessionCreationDelegate() {

                  @Override
                  public void onSessionCreated(final RepositorySession session) {
                    try {
                      session.begin(
                          new RepositorySessionBeginDelegate() {

                            @Override
                            public void onBeginSucceeded(final RepositorySession session) {
                              session.wipe(
                                  new RepositorySessionWipeDelegate() {
                                    @Override
                                    public void onWipeSucceeded() {
                                      try {
                                        session.finish(
                                            new RepositorySessionFinishDelegate() {

                                              @Override
                                              public void onFinishSucceeded(
                                                  RepositorySession session,
                                                  RepositorySessionBundle bundle) {
                                                // Hurrah.
                                                synchronized (monitor) {
                                                  monitor.notify();
                                                }
                                              }

                                              @Override
                                              public void onFinishFailed(Exception ex) {
                                                // Assume that no finish => no wipe.
                                                synchronized (monitor) {
                                                  monitor.notify(ex, true);
                                                }
                                              }

                                              @Override
                                              public RepositorySessionFinishDelegate
                                                  deferredFinishDelegate(ExecutorService executor) {
                                                return this;
                                              }
                                            });
                                      } catch (InactiveSessionException e) {
                                        // Cannot happen. Call for safety.
                                        synchronized (monitor) {
                                          monitor.notify(e, true);
                                        }
                                      }
                                    }

                                    @Override
                                    public void onWipeFailed(Exception ex) {
                                      session.abort();
                                      synchronized (monitor) {
                                        monitor.notify(ex, true);
                                      }
                                    }

                                    @Override
                                    public RepositorySessionWipeDelegate deferredWipeDelegate(
                                        ExecutorService executor) {
                                      return this;
                                    }
                                  });
                            }

                            @Override
                            public void onBeginFailed(Exception ex) {
                              session.abort();
                              synchronized (monitor) {
                                monitor.notify(ex, true);
                              }
                            }

                            @Override
                            public RepositorySessionBeginDelegate deferredBeginDelegate(
                                ExecutorService executor) {
                              return this;
                            }
                          });
                    } catch (InvalidSessionTransitionException e) {
                      session.abort();
                      synchronized (monitor) {
                        monitor.notify(e, true);
                      }
                    }
                  }

                  @Override
                  public void onSessionCreateFailed(Exception ex) {
                    synchronized (monitor) {
                      monitor.notify(ex, false);
                    }
                  }

                  @Override
                  public RepositorySessionCreationDelegate deferredCreationDelegate() {
                    return this;
                  }
                },
                context);
          }
        };

    final Thread wiping = new Thread(doWipe);
    synchronized (monitor) {
      wiping.start();
      try {
        monitor.wait();
      } catch (InterruptedException e) {
        Logger.error(LOG_TAG, "Wipe interrupted.");
      }
    }

    if (!monitor.sessionSucceeded) {
      Logger.error(LOG_TAG, "Failed to create session for wipe.");
      throw monitor.error;
    }

    if (!monitor.wipeSucceeded) {
      Logger.error(LOG_TAG, "Failed to wipe session.");
      throw monitor.error;
    }

    Logger.info(LOG_TAG, "Wiping stage complete.");
  }
 public void purgeDeleted() throws NullCursorException {
   String where = BrowserContract.SyncColumns.IS_DELETED + "= 1";
   Uri uri = getUri();
   Logger.info(LOG_TAG, "Purging deleted from: " + uri);
   context.getContentResolver().delete(uri, where, null);
 }
 // Implementing GlobalSession callbacks.
 @Override
 public void handleError(GlobalSession globalSession, Exception ex) {
   Logger.info(LOG_TAG, "GlobalSession indicated error.");
   this.processException(globalSession, ex);
 }
  @Override
  public void onPerformSync(
      final Account account,
      final Bundle extras,
      final String authority,
      final ContentProviderClient provider,
      final SyncResult syncResult) {
    Logger.resetLogging();
    Utils.reseedSharedRandom(); // Make sure we don't work with the same random seed for too long.

    // Set these so that we don't need to thread them through assorted calls and callbacks.
    this.syncResult = syncResult;
    this.localAccount = account;

    SyncAccountParameters params;
    try {
      params =
          SyncAccounts.blockingFromAndroidAccountV0(
              mContext, AccountManager.get(mContext), this.localAccount);
    } catch (Exception e) {
      // Updates syncResult and (harmlessly) calls notifyMonitor().
      processException(null, e);
      return;
    }

    // params and the following fields are non-null at this point.
    final String username = params.username; // Encoded with Utils.usernameFromAccount.
    final String password = params.password;
    final String serverURL = params.serverURL;
    final String syncKey = params.syncKey;

    final AtomicBoolean setNextSync = new AtomicBoolean(true);
    final SyncAdapter self = this;
    final Runnable runnable =
        new Runnable() {
          @Override
          public void run() {
            Logger.trace(LOG_TAG, "AccountManagerCallback invoked.");
            // TODO: N.B.: Future must not be used on the main thread.
            try {
              Logger.info(
                  LOG_TAG,
                  "Syncing account named " + account.name + " for authority " + authority + ".");

              // We dump this information right away to help with debugging.
              Logger.debug(LOG_TAG, "Username: "******"Server:   " + serverURL);
              if (Logger.LOG_PERSONAL_INFORMATION) {
                Logger.debug(LOG_TAG, "Password: "******"Sync key: " + syncKey);
              } else {
                Logger.debug(LOG_TAG, "Password? " + (password != null));
                Logger.debug(LOG_TAG, "Sync key? " + (syncKey != null));
              }

              // Support multiple accounts by mapping each server/account pair to a branch of the
              // shared preferences space.
              final String product = GlobalConstants.BROWSER_INTENT_PACKAGE;
              final String profile = Constants.DEFAULT_PROFILE;
              final long version = SyncConfiguration.CURRENT_PREFS_VERSION;
              self.accountSharedPreferences =
                  Utils.getSharedPreferences(
                      mContext, product, username, serverURL, profile, version);

              Logger.info(
                  LOG_TAG,
                  "Client is named '"
                      + getClientName()
                      + "'"
                      + ", has client guid "
                      + getAccountGUID()
                      + ", and has "
                      + getClientsCount()
                      + " clients.");

              thisSyncIsForced =
                  (extras != null)
                      && (extras.getBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, false));
              long delay = delayMilliseconds();
              if (delay > 0) {
                if (thisSyncIsForced) {
                  Logger.info(
                      LOG_TAG, "Forced sync: overruling remaining backoff of " + delay + "ms.");
                } else {
                  Logger.info(LOG_TAG, "Not syncing: must wait another " + delay + "ms.");
                  long remainingSeconds = delay / 1000;
                  syncResult.delayUntil = remainingSeconds + BACKOFF_PAD_SECONDS;
                  setNextSync.set(false);
                  self.notifyMonitor();
                  return;
                }
              }

              final String prefsPath =
                  Utils.getPrefsPath(product, username, serverURL, profile, version);
              self.performSync(
                  account,
                  extras,
                  authority,
                  provider,
                  syncResult,
                  username,
                  password,
                  prefsPath,
                  serverURL,
                  syncKey);
            } catch (Exception e) {
              self.processException(null, e);
              return;
            }
          }
        };

    synchronized (syncMonitor) {
      // Perform the work in a new thread from within this synchronized block,
      // which allows us to be waiting on the monitor before the callback can
      // notify us in a failure case. Oh, concurrent programming.
      new Thread(runnable).start();

      // Start our stale connection monitor thread.
      ConnectionMonitorThread stale = new ConnectionMonitorThread();
      stale.start();

      Logger.trace(LOG_TAG, "Waiting on sync monitor.");
      try {
        syncMonitor.wait();

        if (setNextSync.get()) {
          long interval = getSyncInterval();
          long next = System.currentTimeMillis() + interval;
          Logger.info(
              LOG_TAG,
              "Setting minimum next sync time to " + next + " (" + interval + "ms from now).");
          extendEarliestNextSync(next);
        }
        Logger.info(
            LOG_TAG,
            "Sync took "
                + Utils.formatDuration(syncStartTimestamp, System.currentTimeMillis())
                + ".");
      } catch (InterruptedException e) {
        Logger.warn(LOG_TAG, "Waiting on sync monitor interrupted.", e);
      } finally {
        // And we're done with HTTP stuff.
        stale.shutdown();
      }
    }
  }