@Deprecated
 @Override
 public void startSync(long mailboxId, boolean userRequest, int deltaMessageCount)
     throws RemoteException {
   final Mailbox mailbox = Mailbox.restoreMailboxWithId(mContext, mailboxId);
   if (mailbox == null) return;
   final Account account = Account.restoreAccountWithId(mContext, mailbox.mAccountKey);
   if (account == null) return;
   final EmailServiceInfo info = EmailServiceUtils.getServiceInfoForAccount(mContext, account.mId);
   final android.accounts.Account acct =
       new android.accounts.Account(account.mEmailAddress, info.accountType);
   final Bundle extras = Mailbox.createSyncBundle(mailboxId);
   if (userRequest) {
     extras.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
     extras.putBoolean(ContentResolver.SYNC_EXTRAS_DO_NOT_RETRY, true);
     extras.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
   }
   if (deltaMessageCount != 0) {
     extras.putInt(Mailbox.SYNC_EXTRA_DELTA_MESSAGE_COUNT, deltaMessageCount);
   }
   ContentResolver.requestSync(acct, EmailContent.AUTHORITY, extras);
   LogUtils.i(
       Logging.LOG_TAG,
       "requestSync EmailServiceStub startSync %s, %s",
       account.toString(),
       extras.toString());
 }
  @Override
  public void loadMore(long messageId) throws RemoteException {
    /// M: We Can't load more in low storage state @{
    if (StorageLowState.checkIfStorageLow(mContext)) {
      LogUtils.e(Logging.LOG_TAG, "Can't load more due to low storage");
      return;
    }
    /// @}
    // Load a message for view...
    try {
      // 1. Resample the message, in case it disappeared or synced while
      // this command was in queue
      final EmailContent.Message message =
          EmailContent.Message.restoreMessageWithId(mContext, messageId);
      if (message == null) {
        return;
      }
      if (message.mFlagLoaded == EmailContent.Message.FLAG_LOADED_COMPLETE) {
        // We should NEVER get here
        return;
      }

      // 2. Open the remote folder.
      // TODO combine with common code in loadAttachment
      final Account account = Account.restoreAccountWithId(mContext, message.mAccountKey);
      final Mailbox mailbox = Mailbox.restoreMailboxWithId(mContext, message.mMailboxKey);
      if (account == null || mailbox == null) {
        // mListeners.loadMessageForViewFailed(messageId, "null account or mailbox");
        return;
      }
      TrafficStats.setThreadStatsTag(TrafficFlags.getSyncFlags(mContext, account));

      final Store remoteStore = Store.getInstance(account, mContext);
      final String remoteServerId;
      // If this is a search result, use the protocolSearchInfo field to get the
      // correct remote location
      if (!TextUtils.isEmpty(message.mProtocolSearchInfo)) {
        remoteServerId = message.mProtocolSearchInfo;
      } else {
        remoteServerId = mailbox.mServerId;
      }
      final Folder remoteFolder = remoteStore.getFolder(remoteServerId);
      remoteFolder.open(OpenMode.READ_WRITE);

      // 3. Set up to download the entire message
      final Message remoteMessage = remoteFolder.getMessage(message.mServerId);
      final FetchProfile fp = new FetchProfile();
      fp.add(FetchProfile.Item.BODY);
      remoteFolder.fetch(new Message[] {remoteMessage}, fp, null);

      // 4. Write to provider
      Utilities.copyOneMessageToProvider(
          mContext, remoteMessage, account, mailbox, EmailContent.Message.FLAG_LOADED_COMPLETE);
    } catch (MessagingException me) {
      if (Logging.LOGD) LogUtils.v(Logging.LOG_TAG, "", me);

    } catch (RuntimeException rte) {
      LogUtils.d(Logging.LOG_TAG, "RTE During loadMore");
    }
  }
 /**
  * Return a list of all Accounts in EmailProvider. Because the result of this call may be used in
  * account reconciliation, an exception is thrown if the result cannot be guaranteed accurate
  *
  * @param context the caller's context
  * @param accounts a list that Accounts will be added into
  * @return the list of Accounts
  * @throws ProviderUnavailableException if the list of Accounts cannot be guaranteed valid
  */
 @Override
 public AccountList collectAccounts(Context context, AccountList accounts) {
   ContentResolver resolver = context.getContentResolver();
   Cursor c = resolver.query(Account.CONTENT_URI, Account.CONTENT_PROJECTION, null, null, null);
   // We must throw here; callers might use the information we provide for reconciliation, etc.
   if (c == null) throw new ProviderUnavailableException();
   try {
     ContentValues cv = new ContentValues();
     while (c.moveToNext()) {
       long hostAuthId = c.getLong(Account.CONTENT_HOST_AUTH_KEY_RECV_COLUMN);
       if (hostAuthId > 0) {
         HostAuth ha = HostAuth.restoreHostAuthWithId(context, hostAuthId);
         if (ha != null && ha.mProtocol.equals(Eas.PROTOCOL)) {
           Account account = new Account();
           account.restore(c);
           // Cache the HostAuth
           account.mHostAuthRecv = ha;
           accounts.add(account);
           // Fixup flags for inbox (should accept moved mail)
           Mailbox inbox = Mailbox.restoreMailboxOfType(context, account.mId, Mailbox.TYPE_INBOX);
           if (inbox != null && ((inbox.mFlags & Mailbox.FLAG_ACCEPTS_MOVED_MAIL) == 0)) {
             cv.put(MailboxColumns.FLAGS, inbox.mFlags | Mailbox.FLAG_ACCEPTS_MOVED_MAIL);
             resolver.update(
                 ContentUris.withAppendedId(Mailbox.CONTENT_URI, inbox.mId), cv, null, null);
           }
         }
       }
     }
   } finally {
     c.close();
   }
   return accounts;
 }
  @Override
  protected void onMessageShown(long messageId, Mailbox mailbox) {
    super.onMessageShown(messageId, mailbox);

    Account account = Account.restoreAccountWithId(mContext, getAccountId());
    boolean supportsMove = account.supportsMoveMessages(mContext) && mailbox.canHaveMessagesMoved();
    if (mSupportsMove != supportsMove) {
      mSupportsMove = supportsMove;
      Activity host = getActivity();
      if (host != null) {
        host.invalidateOptionsMenu();
      }
    }

    // Disable forward/reply buttons as necessary.
    enableReplyForwardButtons(Mailbox.isMailboxTypeReplyAndForwardable(mailbox.mType));
  }
 /**
  * This is the remote call from the Email app, currently unused. TODO: remove this when it's
  * been deleted from IEmailService.aidl.
  */
 @Deprecated
 @Override
 public void startSync(long mailboxId, boolean userRequest, int deltaMessageCount)
     throws RemoteException {
   SyncManager exchangeService = INSTANCE;
   if (exchangeService == null) return;
   checkExchangeServiceServiceRunning();
   Mailbox m = Mailbox.restoreMailboxWithId(exchangeService, mailboxId);
   if (m == null) return;
   Account acct = Account.restoreAccountWithId(exchangeService, m.mAccountKey);
   if (acct == null) return;
   // If this is a user request and we're being held, release the hold; this allows us to
   // try again (the hold might have been specific to this account and released already)
   if (userRequest) {
     if (onSyncDisabledHold(acct)) {
       releaseSyncHolds(exchangeService, AbstractSyncService.EXIT_ACCESS_DENIED, acct);
       log("User requested sync of account in sync disabled hold; releasing");
     } else if (onSecurityHold(acct)) {
       releaseSyncHolds(exchangeService, AbstractSyncService.EXIT_SECURITY_FAILURE, acct);
       log("User requested sync of account in security hold; releasing");
     }
     if (sConnectivityHold) {
       return;
     }
   }
   if (m.mType == Mailbox.TYPE_OUTBOX) {
     // We're using SERVER_ID to indicate an error condition (it has no other use for
     // sent mail)  Upon request to sync the Outbox, we clear this so that all messages
     // are candidates for sending.
     ContentValues cv = new ContentValues();
     cv.put(SyncColumns.SERVER_ID, 0);
     exchangeService
         .getContentResolver()
         .update(
             Message.CONTENT_URI,
             cv,
             WHERE_MAILBOX_KEY,
             new String[] {Long.toString(mailboxId)});
     // Clear the error state; the Outbox sync will be started from checkMailboxes
     exchangeService.mSyncErrorMap.remove(mailboxId);
     kick("start outbox");
     // Outbox can't be synced in EAS
     return;
   } else if (!isSyncable(m)) {
     return;
   }
   startManualSync(
       mailboxId,
       userRequest
           ? ExchangeService.SYNC_UI_REQUEST
           : ExchangeService.SYNC_SERVICE_START_SYNC,
       null);
 }
 @Override
 public void onSelected(Account account, long mailboxId) {
   String shortcutName;
   /* M: Get display name from FolderProperty, not from database.
    * Aiming to accomplish multi-language.*/
   Mailbox mailbox = Mailbox.restoreMailboxWithId(this, mailboxId);
   if (Account.isNormalAccount(account.mId) && (mailbox.mType != Mailbox.TYPE_INBOX)) {
     shortcutName = FolderProperties.getInstance(this).getDisplayName(mailbox);
   } else {
     shortcutName = account.getDisplayName();
   }
   setupShortcut(account, mailboxId, shortcutName);
   finish();
 }
 /**
  * Persists this folder. We will always perform the proper database operation (e.g. 'save' or
  * 'update'). As an optimization, if a folder has not been modified, no database operations are
  * performed.
  */
 void save(Context context) {
   final Mailbox mailbox = mMailbox;
   if (!mailbox.isSaved()) {
     mailbox.save(context);
     mHash = mailbox.getHashes();
   } else {
     Object[] hash = mailbox.getHashes();
     if (!Arrays.equals(mHash, hash)) {
       mailbox.update(context, mailbox.toContentValues());
       mHash = hash; // Save updated hash
     }
   }
 }
 /**
  * Stop any ping in progress if required
  *
  * @param mailbox whose service has started
  */
 @Override
 public void onStartService(Mailbox mailbox) {
   // If this is a ping mailbox, stop the ping
   if (mailbox.mSyncInterval != Mailbox.CHECK_INTERVAL_PING) return;
   long accountMailboxId =
       Mailbox.findMailboxOfType(this, mailbox.mAccountKey, Mailbox.TYPE_EAS_ACCOUNT_MAILBOX);
   // If our ping is running, stop it
   final AbstractSyncService svc = getRunningService(accountMailboxId);
   if (svc != null) {
     log("Stopping ping due to sync of mailbox: " + mailbox.mDisplayName);
     // Don't block; reset might perform network activity
     new Thread(
             new Runnable() {
               @Override
               public void run() {
                 svc.reset();
               }
             })
         .start();
   }
 }
  /**
   * M: Get the count of the sendable mails of the specified account
   *
   * @param accountId the id of the account
   * @return -1 if error, else the sendable message count
   */
  private static int getSendableMessageCount(Context context, long accountId) {
    int count = 0;
    long outboxId = Mailbox.findMailboxOfType(context, accountId, Mailbox.TYPE_OUTBOX);
    if (outboxId == Mailbox.NO_MAILBOX) {
      return -1;
    }
    ContentResolver resolver = context.getContentResolver();
    Cursor c =
        resolver.query(
            EmailContent.Message.CONTENT_URI,
            EmailContent.Message.ID_COLUMN_PROJECTION,
            EmailContent.Message.MAILBOX_KEY + "=?",
            new String[] {Long.toString(outboxId)},
            null);
    try {
      if (c == null || c.getCount() <= 0) {
        return -1;
      }

      while (c.moveToNext()) {
        long messageId = c.getLong(0);
        if (Utility.hasUnloadedAttachments(context, messageId)) {
          LogUtils.logFeature(
              LogTag.SENDMAIL_TAG, "Can't send #" + messageId + "; unloaded attachments");
          continue;
        } else {
          count++;
        }
      }
    } finally {
      if (c != null) {
        c.close();
      }
    }

    return count;
  }
 @Override
 public void hostChanged(long accountId) throws RemoteException {
   SyncManager exchangeService = INSTANCE;
   if (exchangeService == null) return;
   ConcurrentHashMap<Long, SyncError> syncErrorMap = exchangeService.mSyncErrorMap;
   // Go through the various error mailboxes
   for (long mailboxId : syncErrorMap.keySet()) {
     SyncError error = syncErrorMap.get(mailboxId);
     // If it's a login failure, look a little harder
     Mailbox m = Mailbox.restoreMailboxWithId(exchangeService, mailboxId);
     // If it's for the account whose host has changed, clear the error
     // If the mailbox is no longer around, remove the entry in the map
     if (m == null) {
       syncErrorMap.remove(mailboxId);
     } else if (error != null && m.mAccountKey == accountId) {
       error.fatal = false;
       error.holdEndTime = 0;
     }
   }
   // Stop any running syncs
   exchangeService.stopAccountSyncs(accountId, true);
   // Kick ExchangeService
   kick("host changed");
 }
示例#11
0
  /// M: The folder sync must be synchronized, otherwise the folders may be duplicate
  @Override
  public synchronized void updateFolderList(long accountId) throws RemoteException {
    /// M: We Can't updateFolderList in low storage state @{
    if (StorageLowState.checkIfStorageLow(mContext)) {
      LogUtils.e(Logging.LOG_TAG, "Can't updateFolderList due to low storage");
      return;
    }
    /// @}
    final Account account = Account.restoreAccountWithId(mContext, accountId);
    if (account == null) {
      return;
    }
    long inboxId = -1;
    TrafficStats.setThreadStatsTag(TrafficFlags.getSyncFlags(mContext, account));
    Cursor localFolderCursor = null;
    try {
      // Step 0: Make sure the default system mailboxes exist.
      for (final int type : Mailbox.REQUIRED_FOLDER_TYPES) {
        if (Mailbox.findMailboxOfType(mContext, accountId, type) == Mailbox.NO_MAILBOX) {
          final Mailbox mailbox = Mailbox.newSystemMailbox(mContext, accountId, type);
          mailbox.save(mContext);
          if (type == Mailbox.TYPE_INBOX) {
            inboxId = mailbox.mId;
          }
        }
      }

      // Step 1: Get remote mailboxes
      final Store store = Store.getInstance(account, mContext);
      final Folder[] remoteFolders = store.updateFolders();
      final HashSet<String> remoteFolderNames = new HashSet<String>();
      for (final Folder remoteFolder : remoteFolders) {
        remoteFolderNames.add(remoteFolder.getName());
      }

      // Step 2: Get local mailboxes
      localFolderCursor =
          mContext
              .getContentResolver()
              .query(
                  Mailbox.CONTENT_URI,
                  MAILBOX_PROJECTION,
                  EmailContent.MailboxColumns.ACCOUNT_KEY + "=?",
                  new String[] {String.valueOf(account.mId)},
                  null);

      // Step 3: Remove any local mailbox not on the remote list
      while (localFolderCursor.moveToNext()) {
        final String mailboxPath = localFolderCursor.getString(MAILBOX_COLUMN_SERVER_ID);
        // Short circuit if we have a remote mailbox with the same name
        if (remoteFolderNames.contains(mailboxPath)) {
          continue;
        }

        final int mailboxType = localFolderCursor.getInt(MAILBOX_COLUMN_TYPE);
        final long mailboxId = localFolderCursor.getLong(MAILBOX_COLUMN_ID);
        switch (mailboxType) {
          case Mailbox.TYPE_INBOX:
          case Mailbox.TYPE_DRAFTS:
          case Mailbox.TYPE_OUTBOX:
          case Mailbox.TYPE_SENT:
          case Mailbox.TYPE_TRASH:
          case Mailbox.TYPE_SEARCH:
            // Never, ever delete special mailboxes
            break;
          default:
            // Drop all attachment files related to this mailbox
            AttachmentUtilities.deleteAllMailboxAttachmentFiles(mContext, accountId, mailboxId);
            // Delete the mailbox; database triggers take care of related
            // Message, Body and Attachment records
            Uri uri = ContentUris.withAppendedId(Mailbox.CONTENT_URI, mailboxId);
            mContext.getContentResolver().delete(uri, null, null);
            break;
        }
      }
    } catch (MessagingException me) {
      LogUtils.i(Logging.LOG_TAG, me, "Error in updateFolderList");
      // We'll hope this is temporary
    } finally {
      if (localFolderCursor != null) {
        localFolderCursor.close();
      }
      // If we just created the inbox, sync it
      if (inboxId != -1) {
        startSync(inboxId, true, 0);
      }
    }
  }
示例#12
0
  @Override
  public void loadAttachment(
      final IEmailServiceCallback cb, final long attachmentId, final boolean background)
      throws RemoteException {
    /// M: We Can't load attachment in low storage state @{
    if (StorageLowState.checkIfStorageLow(mContext)) {
      LogUtils.e(Logging.LOG_TAG, "Can't load attachment due to low storage");
      cb.loadAttachmentStatus(0, attachmentId, EmailServiceStatus.SUCCESS, 0);
      return;
    }
    /// @}
    Folder remoteFolder = null;
    try {
      // 1. Check if the attachment is already here and return early in that case
      Attachment attachment = Attachment.restoreAttachmentWithId(mContext, attachmentId);
      if (attachment == null) {
        cb.loadAttachmentStatus(0, attachmentId, EmailServiceStatus.ATTACHMENT_NOT_FOUND, 0);
        return;
      }
      final long messageId = attachment.mMessageKey;

      final EmailContent.Message message =
          EmailContent.Message.restoreMessageWithId(mContext, attachment.mMessageKey);
      if (message == null) {
        cb.loadAttachmentStatus(messageId, attachmentId, EmailServiceStatus.MESSAGE_NOT_FOUND, 0);
        return;
      }

      // If the message is loaded, just report that we're finished
      if (Utility.attachmentExists(mContext, attachment)
          && attachment.mUiState == UIProvider.AttachmentState.SAVED) {
        cb.loadAttachmentStatus(messageId, attachmentId, EmailServiceStatus.SUCCESS, 0);
        return;
      }

      // Say we're starting...
      cb.loadAttachmentStatus(messageId, attachmentId, EmailServiceStatus.IN_PROGRESS, 0);

      // 2. Open the remote folder.
      final Account account = Account.restoreAccountWithId(mContext, message.mAccountKey);
      Mailbox mailbox = Mailbox.restoreMailboxWithId(mContext, message.mMailboxKey);

      if (mailbox.mType == Mailbox.TYPE_OUTBOX
          /// M: View an attachment which comes from refMessage need sourceKey to identify
          || mailbox.mType == Mailbox.TYPE_DRAFTS) {
        long sourceId =
            Utility.getFirstRowLong(
                mContext,
                Body.CONTENT_URI,
                new String[] {BodyColumns.SOURCE_MESSAGE_KEY},
                BodyColumns.MESSAGE_KEY + "=?",
                new String[] {Long.toString(messageId)},
                null,
                0,
                -1L);
        if (sourceId != -1) {
          EmailContent.Message sourceMsg =
              EmailContent.Message.restoreMessageWithId(mContext, sourceId);
          if (sourceMsg != null) {
            mailbox = Mailbox.restoreMailboxWithId(mContext, sourceMsg.mMailboxKey);
            message.mServerId = sourceMsg.mServerId;
          }
        }
      } else if (mailbox.mType == Mailbox.TYPE_SEARCH && message.mMainMailboxKey != 0) {
        mailbox = Mailbox.restoreMailboxWithId(mContext, message.mMainMailboxKey);
      }

      if (account == null || mailbox == null) {
        // If the account/mailbox are gone, just report success; the UI handles this
        cb.loadAttachmentStatus(messageId, attachmentId, EmailServiceStatus.SUCCESS, 0);
        return;
      }
      TrafficStats.setThreadStatsTag(TrafficFlags.getAttachmentFlags(mContext, account));

      final Store remoteStore = Store.getInstance(account, mContext);
      remoteFolder = remoteStore.getFolder(mailbox.mServerId);
      remoteFolder.open(OpenMode.READ_WRITE);

      // 3. Generate a shell message in which to retrieve the attachment,
      // and a shell BodyPart for the attachment.  Then glue them together.
      final Message storeMessage = remoteFolder.createMessage(message.mServerId);
      final MimeBodyPart storePart = new MimeBodyPart();
      storePart.setSize((int) attachment.mSize);
      storePart.setHeader(MimeHeader.HEADER_ANDROID_ATTACHMENT_STORE_DATA, attachment.mLocation);
      storePart.setHeader(
          MimeHeader.HEADER_CONTENT_TYPE,
          String.format("%s;\n name=\"%s\"", attachment.mMimeType, attachment.mFileName));

      // TODO is this always true for attachments?  I think we dropped the
      // true encoding along the way
      /// M: set encoding type according to data base record.
      String encoding = attachment.mEncoding;
      if (TextUtils.isEmpty(encoding)) {
        encoding = "base64";
      }
      storePart.setHeader(MimeHeader.HEADER_CONTENT_TRANSFER_ENCODING, encoding);

      final MimeMultipart multipart = new MimeMultipart();
      multipart.setSubType("mixed");
      multipart.addBodyPart(storePart);

      storeMessage.setHeader(MimeHeader.HEADER_CONTENT_TYPE, "multipart/mixed");
      storeMessage.setBody(multipart);

      // 4. Now ask for the attachment to be fetched
      final FetchProfile fp = new FetchProfile();
      fp.add(storePart);
      remoteFolder.fetch(
          new Message[] {storeMessage},
          fp,
          new MessageRetrievalListenerBridge(messageId, attachmentId, cb));

      // If we failed to load the attachment, throw an Exception here, so that
      // AttachmentDownloadService knows that we failed
      if (storePart.getBody() == null) {
        throw new MessagingException("Attachment not loaded.");
      }

      // Save the attachment to wherever it's going
      AttachmentUtilities.saveAttachment(
          mContext, storePart.getBody().getInputStream(), attachment);

      // 6. Report success
      cb.loadAttachmentStatus(messageId, attachmentId, EmailServiceStatus.SUCCESS, 0);

    } catch (MessagingException me) {
      LogUtils.i(Logging.LOG_TAG, me, "Error loading attachment");

      final ContentValues cv = new ContentValues(1);
      cv.put(AttachmentColumns.UI_STATE, UIProvider.AttachmentState.FAILED);
      final Uri uri = ContentUris.withAppendedId(Attachment.CONTENT_URI, attachmentId);
      mContext.getContentResolver().update(uri, cv, null, null);

      cb.loadAttachmentStatus(0, attachmentId, EmailServiceStatus.CONNECTION_ERROR, 0);
    } finally {
      if (remoteFolder != null) {
        remoteFolder.close(false);
      }
    }
  }
    private void onChangeInBackground() {
      try {
        Cursor c =
            mResolver.query(
                Calendars.CONTENT_URI,
                new String[] {Calendars.SYNC_EVENTS},
                Calendars._ID + "=?",
                new String[] {Long.toString(mCalendarId)},
                null);
        if (c == null) return;
        // Get its sync events; if it's changed, we've got work to do
        try {
          if (c.moveToFirst()) {
            long newSyncEvents = c.getLong(0);
            if (newSyncEvents != mSyncEvents) {
              log("_sync_events changed for calendar in " + mAccountName);
              Mailbox mailbox =
                  Mailbox.restoreMailboxOfType(INSTANCE, mAccountId, Mailbox.TYPE_CALENDAR);
              // Sanity check for mailbox deletion
              if (mailbox == null) return;
              ContentValues cv = new ContentValues();
              if (newSyncEvents == 0) {
                // When sync is disabled, we're supposed to delete
                // all events in the calendar
                log("Deleting events and setting syncKey to 0 for " + mAccountName);
                // First, stop any sync that's ongoing
                stopManualSync(mailbox.mId);
                // Set the syncKey to 0 (reset)
                EasSyncService service = EasSyncService.getServiceForMailbox(INSTANCE, mailbox);

                // CalendarSyncAdapter is gone, and this class is deprecated.
                // Just leaving this commented out code here for reference:
                // Reset the sync key locally and stop syncing
                //                                CalendarSyncAdapter adapter =
                //                                    new CalendarSyncAdapter(service);
                //                                try {
                //                                    adapter.setSyncKey("0", false);
                //                                } catch (IOException e) {
                //                                    // The provider can't be reached; nothing to
                // be done
                //                                }

                cv.put(Mailbox.SYNC_KEY, "0");
                cv.put(Mailbox.SYNC_INTERVAL, Mailbox.CHECK_INTERVAL_NEVER);
                mResolver.update(
                    ContentUris.withAppendedId(Mailbox.CONTENT_URI, mailbox.mId), cv, null, null);
                // Delete all events using the sync adapter
                // parameter so that the deletion is only local
                Uri eventsAsSyncAdapter =
                    eventsAsSyncAdapter(
                        Events.CONTENT_URI, mAccountName, Eas.EXCHANGE_ACCOUNT_MANAGER_TYPE);
                mResolver.delete(
                    eventsAsSyncAdapter,
                    WHERE_CALENDAR_ID,
                    new String[] {Long.toString(mCalendarId)});
              } else {
                // Make this a push mailbox and kick; this will start
                // a resync of the Calendar; the account mailbox will
                // ping on this during the next cycle of the ping loop
                cv.put(Mailbox.SYNC_INTERVAL, Mailbox.CHECK_INTERVAL_PUSH);
                mResolver.update(
                    ContentUris.withAppendedId(Mailbox.CONTENT_URI, mailbox.mId), cv, null, null);
                kick("calendar sync changed");
              }

              // Save away the new value
              mSyncEvents = newSyncEvents;
            }
          }
        } finally {
          c.close();
        }
      } catch (ProviderUnavailableException e) {
        LogUtils.w(TAG, "Observer failed; provider unavailable");
      }
    }
示例#14
0
  public static int searchMessages(
      Context context, long accountId, SearchParams searchParams, long destMailboxId) {
    // Sanity check for arguments
    final int offset = searchParams.mOffset;
    final int limit = searchParams.mLimit;
    final String filter = searchParams.mFilter;
    if (limit < 0 || limit > MAX_SEARCH_RESULTS || offset < 0) return 0;
    // TODO Should this be checked in UI?  Are there guidelines for minimums?
    if (filter == null || filter.length() < MIN_QUERY_LENGTH) return 0;

    int res = 0;
    final Account account = Account.restoreAccountWithId(context, accountId);
    if (account == null) return res;
    final EasSyncService svc = EasSyncService.setupServiceForAccount(context, account);
    if (svc == null) return res;
    final Mailbox searchMailbox = Mailbox.restoreMailboxWithId(context, destMailboxId);
    // Sanity check; account might have been deleted?
    if (searchMailbox == null) return res;
    final ContentValues statusValues = new ContentValues(2);
    try {
      // Set the status of this mailbox to indicate query
      statusValues.put(Mailbox.UI_SYNC_STATUS, UIProvider.SyncStatus.LIVE_QUERY);
      searchMailbox.update(context, statusValues);

      svc.mMailbox = searchMailbox;
      svc.mAccount = account;
      final Serializer s = new Serializer();
      s.start(Tags.SEARCH_SEARCH).start(Tags.SEARCH_STORE);
      s.data(Tags.SEARCH_NAME, "Mailbox");
      s.start(Tags.SEARCH_QUERY).start(Tags.SEARCH_AND);
      s.data(Tags.SYNC_CLASS, "Email");

      // If this isn't an inbox search, then include the collection id
      final Mailbox inbox = Mailbox.restoreMailboxOfType(context, accountId, Mailbox.TYPE_INBOX);
      if (inbox == null) return 0;
      if (searchParams.mMailboxId != inbox.mId) {
        s.data(Tags.SYNC_COLLECTION_ID, inbox.mServerId);
      }

      s.data(Tags.SEARCH_FREE_TEXT, filter);

      // Add the date window if appropriate
      if (searchParams.mStartDate != null) {
        s.start(Tags.SEARCH_GREATER_THAN);
        s.tag(Tags.EMAIL_DATE_RECEIVED);
        s.data(Tags.SEARCH_VALUE, Eas.DATE_FORMAT.format(searchParams.mStartDate));
        s.end(); // SEARCH_GREATER_THAN
      }
      if (searchParams.mEndDate != null) {
        s.start(Tags.SEARCH_LESS_THAN);
        s.tag(Tags.EMAIL_DATE_RECEIVED);
        s.data(Tags.SEARCH_VALUE, Eas.DATE_FORMAT.format(searchParams.mEndDate));
        s.end(); // SEARCH_LESS_THAN
      }
      s.end().end(); // SEARCH_AND, SEARCH_QUERY
      s.start(Tags.SEARCH_OPTIONS);
      if (offset == 0) {
        s.tag(Tags.SEARCH_REBUILD_RESULTS);
      }
      if (searchParams.mIncludeChildren) {
        s.tag(Tags.SEARCH_DEEP_TRAVERSAL);
      }
      // Range is sent in the form first-last (e.g. 0-9)
      s.data(Tags.SEARCH_RANGE, offset + "-" + (offset + limit - 1));
      s.start(Tags.BASE_BODY_PREFERENCE);
      s.data(Tags.BASE_TYPE, Eas.BODY_PREFERENCE_HTML);
      s.data(Tags.BASE_TRUNCATION_SIZE, "20000");
      s.end(); // BASE_BODY_PREFERENCE
      s.end().end().end().done(); // SEARCH_OPTIONS, SEARCH_STORE, SEARCH_SEARCH
      final EasResponse resp = svc.sendHttpClientPost("Search", s.toByteArray());
      try {
        final int code = resp.getStatus();
        if (code == HttpStatus.SC_OK) {
          final InputStream is = resp.getInputStream();
          try {
            final SearchParser sp = new SearchParser(is, svc, filter);
            sp.parse();
            res = sp.getTotalResults();
            /** M: Set and update mailbox flag. @{ */
            int currentCount = offset + sp.getCurrentResults();
            boolean allMessagesLoaded = false;
            if (currentCount >= res) {
              allMessagesLoaded = true;
            }
            searchMailbox.updateAllMessageDownloadFlag(context, allMessagesLoaded);
            /** @} */
          } finally {
            is.close();
          }
        } else {
          svc.userLog("Search returned " + code);
        }
      } finally {
        resp.close();
      }
    } catch (IOException e) {
      svc.userLog("Search exception " + e);
    } finally {
      // TODO: Handle error states
      // Set the status of this mailbox to indicate query over
      statusValues.put(Mailbox.SYNC_TIME, System.currentTimeMillis());
      statusValues.put(Mailbox.UI_SYNC_STATUS, UIProvider.SyncStatus.NO_SYNC);
      searchMailbox.update(context, statusValues);
    }
    // Return the total count
    return res;
  }
示例#15
0
  public static void sendMailImpl(Context context, long accountId) {
    /// M: We Can't send mail in low storage state @{
    if (StorageLowState.checkIfStorageLow(context)) {
      LogUtils.e(Logging.LOG_TAG, "Can't send mail due to low storage");
      return;
    }
    /// @}

    /** M: Get the sendable mails count of the account and notify this sending @{ */
    final int count = getSendableMessageCount(context, accountId);
    LogUtils.logFeature(LogTag.SENDMAIL_TAG, "sendable message count [%d]", count);
    if (count <= 0) {
      return;
    }
    SendNotificationProxy.getInstance(context)
        .showSendingNotification(accountId, NotificationController.SEND_MAIL, count);
    /** @} */
    final Account account = Account.restoreAccountWithId(context, accountId);
    TrafficStats.setThreadStatsTag(TrafficFlags.getSmtpFlags(context, account));
    final NotificationController nc = NotificationController.getInstance(context);
    // 1.  Loop through all messages in the account's outbox
    final long outboxId = Mailbox.findMailboxOfType(context, account.mId, Mailbox.TYPE_OUTBOX);
    if (outboxId == Mailbox.NO_MAILBOX) {
      return;
    }
    final ContentResolver resolver = context.getContentResolver();
    final Cursor c =
        resolver.query(
            EmailContent.Message.CONTENT_URI,
            EmailContent.Message.ID_COLUMN_PROJECTION,
            EmailContent.Message.MAILBOX_KEY + "=?",
            new String[] {Long.toString(outboxId)},
            null);
    try {
      // 2.  exit early
      if (c.getCount() <= 0) {
        return;
      }
      final Sender sender = Sender.getInstance(context, account);
      final Store remoteStore = Store.getInstance(account, context);
      final ContentValues moveToSentValues;
      if (remoteStore.requireCopyMessageToSentFolder()) {
        Mailbox sentFolder = Mailbox.restoreMailboxOfType(context, accountId, Mailbox.TYPE_SENT);
        moveToSentValues = new ContentValues();
        moveToSentValues.put(MessageColumns.MAILBOX_KEY, sentFolder.mId);
      } else {
        moveToSentValues = null;
      }

      // 3.  loop through the available messages and send them
      /** M: mark should we cancel the Login Failed Notification. */
      boolean shouldCancelNf = false;
      while (c.moveToNext()) {
        long messageId = -1;
        if (moveToSentValues != null) {
          moveToSentValues.remove(EmailContent.MessageColumns.FLAGS);
        }
        try {
          messageId = c.getLong(0);
          // Don't send messages with unloaded attachments
          if (Utility.hasUnloadedAttachments(context, messageId)) {
            LogUtils.logFeature(
                LogTag.SENDMAIL_TAG, "Can't send #" + messageId + "; unloaded attachments");
            continue;
          }
          sender.sendMessage(messageId);
        } catch (MessagingException me) {
          LogUtils.logFeature(
              LogTag.SENDMAIL_TAG,
              "<<< Smtp send message failed id [%s], exception: %s",
              messageId,
              me);
          // report error for this message, but keep trying others
          if (me instanceof AuthenticationFailedException) {
            shouldCancelNf = false;
            nc.showLoginFailedNotification(account.mId);
          }
          /// M: One mail sent failed
          SendNotificationProxy.getInstance(context)
              .showSendingNotification(account.mId, NotificationController.SEND_FAILED, 1);
          continue;
        }

        /// M: One mail sent complete
        SendNotificationProxy.getInstance(context)
            .showSendingNotification(account.mId, NotificationController.SEND_COMPLETE, 1);

        // 4. move to sent, or delete
        final Uri syncedUri =
            ContentUris.withAppendedId(EmailContent.Message.SYNCED_CONTENT_URI, messageId);
        // Delete all cached files
        AttachmentUtilities.deleteAllCachedAttachmentFiles(context, account.mId, messageId);
        if (moveToSentValues != null) {
          // If this is a forwarded message and it has attachments, delete them, as they
          // duplicate information found elsewhere (on the server).  This saves storage.
          final EmailContent.Message msg =
              EmailContent.Message.restoreMessageWithId(context, messageId);
          if ((msg.mFlags & EmailContent.Message.FLAG_TYPE_FORWARD) != 0) {
            AttachmentUtilities.deleteAllAttachmentFiles(context, account.mId, messageId);
          }
          /// M: un-mark sending status after sending
          final int flags =
              msg.mFlags
                  & ~(EmailContent.Message.FLAG_TYPE_REPLY
                      | EmailContent.Message.FLAG_TYPE_FORWARD
                      | EmailContent.Message.FLAG_TYPE_REPLY_ALL
                      | EmailContent.Message.FLAG_TYPE_ORIGINAL
                      | EmailContent.Message.FLAG_STATUS_SENDING);

          moveToSentValues.put(EmailContent.MessageColumns.FLAGS, flags);
          resolver.update(syncedUri, moveToSentValues, null, null);
        } else {
          AttachmentUtilities.deleteAllAttachmentFiles(context, account.mId, messageId);
          final Uri uri = ContentUris.withAppendedId(EmailContent.Message.CONTENT_URI, messageId);
          resolver.delete(uri, null, null);
          resolver.delete(syncedUri, null, null);
        }
        shouldCancelNf = true;
      }
      if (shouldCancelNf) {
        nc.cancelLoginFailedNotification(account.mId);
      }
    } catch (MessagingException me) {
      if (me instanceof AuthenticationFailedException) {
        nc.showLoginFailedNotification(account.mId);
      }
      /// M: All mails failed to be sent, caused by fail to get instance of store
      SendNotificationProxy.getInstance(context)
          .showSendingNotification(account.mId, NotificationController.SEND_FAILED, c.getCount());
    } finally {
      c.close();
    }
  }
  public static void reloadFolderList(Context context, long accountId, boolean force) {
    SyncManager exchangeService = INSTANCE;
    if (exchangeService == null) return;
    Cursor c =
        context
            .getContentResolver()
            .query(
                Mailbox.CONTENT_URI,
                Mailbox.CONTENT_PROJECTION,
                MailboxColumns.ACCOUNT_KEY + "=? AND " + MailboxColumns.TYPE + "=?",
                new String[] {
                  Long.toString(accountId), Long.toString(Mailbox.TYPE_EAS_ACCOUNT_MAILBOX)
                },
                null);
    try {
      if (c.moveToFirst()) {
        synchronized (sSyncLock) {
          Mailbox mailbox = new Mailbox();
          mailbox.restore(c);
          Account acct = Account.restoreAccountWithId(context, accountId);
          if (acct == null) {
            reloadFolderListFailed(accountId);
            return;
          }
          String syncKey = acct.mSyncKey;
          // No need to reload the list if we don't have one
          if (!force && (syncKey == null || syncKey.equals("0"))) {
            reloadFolderListFailed(accountId);
            return;
          }

          // Change all ping/push boxes to push/hold
          ContentValues cv = new ContentValues();
          cv.put(Mailbox.SYNC_INTERVAL, Mailbox.CHECK_INTERVAL_PUSH_HOLD);
          context
              .getContentResolver()
              .update(
                  Mailbox.CONTENT_URI,
                  cv,
                  WHERE_PUSH_OR_PING_NOT_ACCOUNT_MAILBOX,
                  new String[] {Long.toString(accountId)});
          log("Set push/ping boxes to push/hold");

          long id = mailbox.mId;
          AbstractSyncService svc = exchangeService.mServiceMap.get(id);
          // Tell the service we're done
          if (svc != null) {
            synchronized (svc.getSynchronizer()) {
              svc.stop();
              // Interrupt the thread so that it can stop
              Thread thread = svc.mThread;
              if (thread != null) {
                thread.setName(thread.getName() + " (Stopped)");
                thread.interrupt();
              }
            }
            // Abandon the service
            exchangeService.releaseMailbox(id);
            // And have it start naturally
            kick("reload folder list");
          }
        }
      }
    } finally {
      c.close();
    }
  }
 /** Do DB access on a worker thread. */
 @Override
 protected Boolean doInBackground(Void... params) {
   mInboxId = Account.getInboxId(mContext, mAccountId);
   return Mailbox.isRefreshable(mContext, mMailboxId);
 }