@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");
    }
  }
 @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());
 }
 /**
  * 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();
 }
 @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");
 }
  @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);
      }
    }
  }
Example #7
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;
  }