/** * Handle password expiration - if any accounts appear to have triggered this, put up warnings, or * even shut them down. * * <p>NOTE: If there are multiple accounts with password expiration policies, the device password * will be set to expire in the shortest required interval (most secure). The logic in this method * operates based on the aggregate setting - irrespective of which account caused the expiration. * In other words, all accounts (that require expiration) will run/stop based on the requirements * of the account with the shortest interval. */ private void onPasswordExpiring(Context context) { // 1. Do we have any accounts that matter here? long nextExpiringAccountId = findShortestExpiration(context); // 2. If not, exit immediately if (nextExpiringAccountId == -1) { return; } // 3. If yes, are we warning or expired? long expirationDate = getDPM().getPasswordExpiration(mAdminName); long timeUntilExpiration = expirationDate - System.currentTimeMillis(); boolean expired = timeUntilExpiration < 0; final NotificationController nc = NotificationControllerCreatorHolder.getInstance(context); if (!expired) { // 4. If warning, simply put up a generic notification and report that it came from // the shortest-expiring account. nc.showPasswordExpiringNotificationSynchronous(nextExpiringAccountId); } else { // 5. Actually expired - find all accounts that expire passwords, and wipe them boolean wiped = wipeExpiredAccounts(context); if (wiped) { nc.showPasswordExpiredNotificationSynchronous(nextExpiringAccountId); } } }
/** Convenience method; see javadoc below */ public static void setAccountHoldFlag(Context context, long accountId, boolean newState) { Account account = Account.restoreAccountWithId(context, accountId); if (account != null) { setAccountHoldFlag(context, account, newState); if (newState) { // Make sure there's a notification up final NotificationController nc = NotificationControllerCreatorHolder.getInstance(context); nc.showSecurityNeededNotification(account); } } }
/** * API: Sync service should call this any time a sync fails due to isActive() returning false. * This will kick off the notify-acquire-admin-state process and/or increase the security level. * The caller needs to write the required policies into this account before making this call. * Should not be called from UI thread - uses DB lookups to prepare new notifications * * @param accountId the account for which sync cannot proceed */ public void policiesRequired(long accountId) { Account account = Account.restoreAccountWithId(mContext, accountId); // In case the account has been deleted, just return if (account == null) return; if (account.mPolicyKey == 0) return; Policy policy = Policy.restorePolicyWithId(mContext, account.mPolicyKey); if (policy == null) return; if (DebugUtils.DEBUG) { LogUtils.d(TAG, "policiesRequired for " + account.mDisplayName + ": " + policy); } // Mark the account as "on hold". setAccountHoldFlag(mContext, account, true); // Put up an appropriate notification final NotificationController nc = NotificationControllerCreatorHolder.getInstance(mContext); if (policy.mProtocolPoliciesUnsupported == null) { nc.showSecurityNeededNotification(account); } else { nc.showSecurityUnsupportedNotification(account); } }
/** * Callback from EmailBroadcastProcessorService. This provides the workers for the * DeviceAdminReceiver calls. These should perform the work directly and not use async threads for * completion. */ public static void onDeviceAdminReceiverMessage(Context context, int message) { SecurityPolicy instance = SecurityPolicy.getInstance(context); switch (message) { case DEVICE_ADMIN_MESSAGE_ENABLED: instance.onAdminEnabled(true); break; case DEVICE_ADMIN_MESSAGE_DISABLED: instance.onAdminEnabled(false); break; case DEVICE_ADMIN_MESSAGE_PASSWORD_CHANGED: // TODO make a small helper for this // Clear security holds (if any) Account.clearSecurityHoldOnAllAccounts(context); // Cancel any active notifications (if any are posted) final NotificationController nc = NotificationControllerCreatorHolder.getInstance(context); nc.cancelPasswordExpirationNotifications(); break; case DEVICE_ADMIN_MESSAGE_PASSWORD_EXPIRING: instance.onPasswordExpiring(instance.mContext); break; } }
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(); } }
/** * Called from the notification's intent receiver to register that the notification can be cleared * now. */ public void clearNotification() { final NotificationController nc = NotificationControllerCreatorHolder.getInstance(mContext); nc.cancelSecurityNeededNotification(); }
public void setAccountPolicy(long accountId, Policy policy, String securityKey, boolean notify) { Account account = Account.restoreAccountWithId(mContext, accountId); // In case the account has been deleted, just return if (account == null) { return; } Policy oldPolicy = null; if (account.mPolicyKey > 0) { oldPolicy = Policy.restorePolicyWithId(mContext, account.mPolicyKey); } // If attachment policies have changed, fix up any affected attachment records if (oldPolicy != null && securityKey != null) { if ((oldPolicy.mDontAllowAttachments != policy.mDontAllowAttachments) || (oldPolicy.mMaxAttachmentSize != policy.mMaxAttachmentSize)) { Policy.setAttachmentFlagsForNewPolicy(mContext, account, policy); } } boolean policyChanged = (oldPolicy == null) || !oldPolicy.equals(policy); if (!policyChanged && (TextUtilities.stringOrNullEquals(securityKey, account.mSecuritySyncKey))) { LogUtils.d(Logging.LOG_TAG, "setAccountPolicy; policy unchanged"); } else { setAccountPolicy(mContext, account, policy, securityKey); policiesUpdated(); } boolean setHold = false; final NotificationController nc = NotificationControllerCreatorHolder.getInstance(mContext); if (policy.mProtocolPoliciesUnsupported != null) { // We can't support this, reasons in unsupportedRemotePolicies LogUtils.d( Logging.LOG_TAG, "Notify policies for " + account.mDisplayName + " not supported."); setHold = true; if (notify) { nc.showSecurityUnsupportedNotification(account); } // Erase data Uri uri = EmailProvider.uiUri("uiaccountdata", accountId); mContext.getContentResolver().delete(uri, null, null); } else if (isActive(policy)) { if (policyChanged) { LogUtils.d(Logging.LOG_TAG, "Notify policies for " + account.mDisplayName + " changed."); if (notify) { // Notify that policies changed nc.showSecurityChangedNotification(account); } } else { LogUtils.d(Logging.LOG_TAG, "Policy is active and unchanged; do not notify."); } } else { setHold = true; LogUtils.d( Logging.LOG_TAG, "Notify policies for " + account.mDisplayName + " are not being enforced."); if (notify) { // Put up a notification nc.showSecurityNeededNotification(account); } } // Set/clear the account hold. setAccountHoldFlag(mContext, account, setHold); }