| /* |
| * Copyright (C) 2011 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.emailcommon.provider; |
| |
| import android.content.ContentProviderOperation; |
| import android.content.ContentProviderResult; |
| import android.content.ContentResolver; |
| import android.content.ContentUris; |
| import android.content.ContentValues; |
| import android.content.Context; |
| import android.content.OperationApplicationException; |
| import android.database.Cursor; |
| import android.media.RingtoneManager; |
| import android.net.ConnectivityManager; |
| import android.net.NetworkInfo; |
| import android.net.Uri; |
| import android.os.Parcel; |
| import android.os.Parcelable; |
| import android.os.RemoteException; |
| import android.text.TextUtils; |
| |
| import com.android.emailcommon.provider.EmailContent.AccountColumns; |
| import com.android.emailcommon.utility.Utility; |
| import com.android.mail.utils.LogUtils; |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.UUID; |
| |
| public final class Account extends EmailContent implements AccountColumns, Parcelable { |
| public static final String TABLE_NAME = "Account"; |
| |
| // Define all pseudo account IDs here to avoid conflict with one another. |
| /** |
| * Pseudo account ID to represent a "combined account" that includes messages and mailboxes |
| * from all defined accounts. |
| * |
| * <em>IMPORTANT</em>: This must never be stored to the database. |
| */ |
| public static final long ACCOUNT_ID_COMBINED_VIEW = 0x1000000000000000L; |
| /** |
| * Pseudo account ID to represent "no account". This may be used any time the account ID |
| * may not be known or when we want to specifically select "no" account. |
| * |
| * <em>IMPORTANT</em>: This must never be stored to the database. |
| */ |
| public static final long NO_ACCOUNT = -1L; |
| |
| /** |
| * Whether or not the user has asked for notifications of new mail in this account |
| * |
| * @deprecated Used only for migration |
| */ |
| @Deprecated |
| public final static int FLAGS_NOTIFY_NEW_MAIL = 1<<0; |
| /** |
| * Whether or not the user has asked for vibration notifications with all new mail |
| * |
| * @deprecated Used only for migration |
| */ |
| @Deprecated |
| public final static int FLAGS_VIBRATE = 1<<1; |
| // Bit mask for the account's deletion policy (see DELETE_POLICY_x below) |
| public static final int FLAGS_DELETE_POLICY_MASK = 1<<2 | 1<<3; |
| public static final int FLAGS_DELETE_POLICY_SHIFT = 2; |
| // Whether the account is in the process of being created; any account reconciliation code |
| // MUST ignore accounts with this bit set; in addition, ContentObservers for this data |
| // SHOULD consider the state of this flag during operation |
| public static final int FLAGS_INCOMPLETE = 1<<4; |
| // Security hold is used when the device is not in compliance with security policies |
| // required by the server; in this state, the user MUST be alerted to the need to update |
| // security settings. Sync adapters SHOULD NOT attempt to sync when this flag is set. |
| public static final int FLAGS_SECURITY_HOLD = 1<<5; |
| // Whether the account supports "smart forward" (i.e. the server appends the original |
| // message along with any attachments to the outgoing message) |
| public static final int FLAGS_SUPPORTS_SMART_FORWARD = 1<<7; |
| // Whether the account should try to cache attachments in the background |
| public static final int FLAGS_BACKGROUND_ATTACHMENTS = 1<<8; |
| // Available to sync adapter |
| public static final int FLAGS_SYNC_ADAPTER = 1<<9; |
| // Sync disabled is a status commanded by the server; the sync adapter SHOULD NOT try to |
| // sync mailboxes in this account automatically. A manual sync request to sync a mailbox |
| // with sync disabled SHOULD try to sync and report any failure result via the UI. |
| public static final int FLAGS_SYNC_DISABLED = 1<<10; |
| // Whether or not server-side search is supported by this account |
| public static final int FLAGS_SUPPORTS_SEARCH = 1<<11; |
| // Whether or not server-side search supports global search (i.e. all mailboxes); only valid |
| // if FLAGS_SUPPORTS_SEARCH is true |
| public static final int FLAGS_SUPPORTS_GLOBAL_SEARCH = 1<<12; |
| // Whether or not the initial folder list has been loaded |
| public static final int FLAGS_INITIAL_FOLDER_LIST_LOADED = 1<<13; |
| |
| // Deletion policy (see FLAGS_DELETE_POLICY_MASK, above) |
| public static final int DELETE_POLICY_NEVER = 0; |
| public static final int DELETE_POLICY_7DAYS = 1<<0; // not supported |
| public static final int DELETE_POLICY_ON_DELETE = 1<<1; |
| |
| // Sentinel values for the mSyncInterval field of both Account records |
| public static final int CHECK_INTERVAL_NEVER = -1; |
| public static final int CHECK_INTERVAL_PUSH = -2; |
| |
| public static Uri CONTENT_URI; |
| public static Uri RESET_NEW_MESSAGE_COUNT_URI; |
| public static Uri NOTIFIER_URI; |
| |
| public static void initAccount() { |
| CONTENT_URI = Uri.parse(EmailContent.CONTENT_URI + "/account"); |
| RESET_NEW_MESSAGE_COUNT_URI = Uri.parse(EmailContent.CONTENT_URI + "/resetNewMessageCount"); |
| NOTIFIER_URI = Uri.parse(EmailContent.CONTENT_NOTIFIER_URI + "/account"); |
| } |
| |
| public String mDisplayName; |
| public String mEmailAddress; |
| public String mSyncKey; |
| public int mSyncLookback; |
| public int mSyncInterval; |
| public long mHostAuthKeyRecv; |
| public long mHostAuthKeySend; |
| public int mFlags; |
| public String mCompatibilityUuid; |
| public String mSenderName; |
| /** @deprecated Used only for migration */ |
| @Deprecated |
| private String mRingtoneUri; |
| public String mProtocolVersion; |
| public int mNewMessageCount; |
| public String mSecuritySyncKey; |
| public String mSignature; |
| public long mPolicyKey; |
| public long mPingDuration; |
| |
| // Convenience for creating/working with an account |
| public transient HostAuth mHostAuthRecv; |
| public transient HostAuth mHostAuthSend; |
| public transient Policy mPolicy; |
| |
| public static final int CONTENT_ID_COLUMN = 0; |
| public static final int CONTENT_DISPLAY_NAME_COLUMN = 1; |
| public static final int CONTENT_EMAIL_ADDRESS_COLUMN = 2; |
| public static final int CONTENT_SYNC_KEY_COLUMN = 3; |
| public static final int CONTENT_SYNC_LOOKBACK_COLUMN = 4; |
| public static final int CONTENT_SYNC_INTERVAL_COLUMN = 5; |
| public static final int CONTENT_HOST_AUTH_KEY_RECV_COLUMN = 6; |
| public static final int CONTENT_HOST_AUTH_KEY_SEND_COLUMN = 7; |
| public static final int CONTENT_FLAGS_COLUMN = 8; |
| public static final int CONTENT_COMPATIBILITY_UUID_COLUMN = 9; |
| public static final int CONTENT_SENDER_NAME_COLUMN = 10; |
| public static final int CONTENT_RINGTONE_URI_COLUMN = 11; |
| public static final int CONTENT_PROTOCOL_VERSION_COLUMN = 12; |
| public static final int CONTENT_NEW_MESSAGE_COUNT_COLUMN = 13; |
| public static final int CONTENT_SECURITY_SYNC_KEY_COLUMN = 14; |
| public static final int CONTENT_SIGNATURE_COLUMN = 15; |
| public static final int CONTENT_POLICY_KEY_COLUMN = 16; |
| public static final int CONTENT_PING_DURATION_COLUMN = 17; |
| |
| public static final String[] CONTENT_PROJECTION = new String[] { |
| RECORD_ID, AccountColumns.DISPLAY_NAME, |
| AccountColumns.EMAIL_ADDRESS, AccountColumns.SYNC_KEY, AccountColumns.SYNC_LOOKBACK, |
| AccountColumns.SYNC_INTERVAL, AccountColumns.HOST_AUTH_KEY_RECV, |
| AccountColumns.HOST_AUTH_KEY_SEND, AccountColumns.FLAGS, |
| AccountColumns.COMPATIBILITY_UUID, AccountColumns.SENDER_NAME, |
| AccountColumns.RINGTONE_URI, AccountColumns.PROTOCOL_VERSION, |
| AccountColumns.NEW_MESSAGE_COUNT, AccountColumns.SECURITY_SYNC_KEY, |
| AccountColumns.SIGNATURE, AccountColumns.POLICY_KEY, AccountColumns.PING_DURATION |
| }; |
| |
| public static final int CONTENT_MAILBOX_TYPE_COLUMN = 1; |
| |
| /** |
| * This projection is for listing account id's only |
| */ |
| public static final String[] ID_TYPE_PROJECTION = new String[] { |
| RECORD_ID, MailboxColumns.TYPE |
| }; |
| |
| public static final int ACCOUNT_FLAGS_COLUMN_ID = 0; |
| public static final int ACCOUNT_FLAGS_COLUMN_FLAGS = 1; |
| public static final String[] ACCOUNT_FLAGS_PROJECTION = new String[] { |
| AccountColumns.ID, AccountColumns.FLAGS}; |
| |
| public static final String MAILBOX_SELECTION = |
| MessageColumns.MAILBOX_KEY + " =?"; |
| |
| public static final String UNREAD_COUNT_SELECTION = |
| MessageColumns.MAILBOX_KEY + " =? and " + MessageColumns.FLAG_READ + "= 0"; |
| |
| private static final String UUID_SELECTION = AccountColumns.COMPATIBILITY_UUID + " =?"; |
| |
| public static final String SECURITY_NONZERO_SELECTION = |
| Account.POLICY_KEY + " IS NOT NULL AND " + Account.POLICY_KEY + "!=0"; |
| |
| private static final String FIND_INBOX_SELECTION = |
| MailboxColumns.TYPE + " = " + Mailbox.TYPE_INBOX + |
| " AND " + MailboxColumns.ACCOUNT_KEY + " =?"; |
| |
| /** |
| * no public constructor since this is a utility class |
| */ |
| public Account() { |
| mBaseUri = CONTENT_URI; |
| |
| // other defaults (policy) |
| mRingtoneUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION).toString(); |
| mSyncInterval = -1; |
| mSyncLookback = -1; |
| mFlags = 0; |
| mCompatibilityUuid = UUID.randomUUID().toString(); |
| } |
| |
| public static Account restoreAccountWithId(Context context, long id) { |
| return EmailContent.restoreContentWithId(context, Account.class, |
| Account.CONTENT_URI, Account.CONTENT_PROJECTION, id); |
| } |
| |
| /** |
| * Returns {@code true} if the given account ID is a "normal" account. Normal accounts |
| * always have an ID greater than {@code 0} and not equal to any pseudo account IDs |
| * (such as {@link #ACCOUNT_ID_COMBINED_VIEW}) |
| */ |
| public static boolean isNormalAccount(long accountId) { |
| return (accountId > 0L) && (accountId != ACCOUNT_ID_COMBINED_VIEW); |
| } |
| |
| /** |
| * Refresh an account that has already been loaded. This is slightly less expensive |
| * that generating a brand-new account object. |
| */ |
| public void refresh(Context context) { |
| Cursor c = context.getContentResolver().query(getUri(), Account.CONTENT_PROJECTION, |
| null, null, null); |
| try { |
| c.moveToFirst(); |
| restore(c); |
| } finally { |
| if (c != null) { |
| c.close(); |
| } |
| } |
| } |
| |
| @Override |
| public void restore(Cursor cursor) { |
| mId = cursor.getLong(CONTENT_ID_COLUMN); |
| mBaseUri = CONTENT_URI; |
| mDisplayName = cursor.getString(CONTENT_DISPLAY_NAME_COLUMN); |
| mEmailAddress = cursor.getString(CONTENT_EMAIL_ADDRESS_COLUMN); |
| mSyncKey = cursor.getString(CONTENT_SYNC_KEY_COLUMN); |
| mSyncLookback = cursor.getInt(CONTENT_SYNC_LOOKBACK_COLUMN); |
| mSyncInterval = cursor.getInt(CONTENT_SYNC_INTERVAL_COLUMN); |
| mHostAuthKeyRecv = cursor.getLong(CONTENT_HOST_AUTH_KEY_RECV_COLUMN); |
| mHostAuthKeySend = cursor.getLong(CONTENT_HOST_AUTH_KEY_SEND_COLUMN); |
| mFlags = cursor.getInt(CONTENT_FLAGS_COLUMN); |
| mCompatibilityUuid = cursor.getString(CONTENT_COMPATIBILITY_UUID_COLUMN); |
| mSenderName = cursor.getString(CONTENT_SENDER_NAME_COLUMN); |
| mRingtoneUri = cursor.getString(CONTENT_RINGTONE_URI_COLUMN); |
| mProtocolVersion = cursor.getString(CONTENT_PROTOCOL_VERSION_COLUMN); |
| mNewMessageCount = cursor.getInt(CONTENT_NEW_MESSAGE_COUNT_COLUMN); |
| mSecuritySyncKey = cursor.getString(CONTENT_SECURITY_SYNC_KEY_COLUMN); |
| mSignature = cursor.getString(CONTENT_SIGNATURE_COLUMN); |
| mPolicyKey = cursor.getLong(CONTENT_POLICY_KEY_COLUMN); |
| mPingDuration = cursor.getLong(CONTENT_PING_DURATION_COLUMN); |
| } |
| |
| private static long getId(Uri u) { |
| return Long.parseLong(u.getPathSegments().get(1)); |
| } |
| |
| public long getId() { |
| return mId; |
| } |
| |
| /** |
| * @return the user-visible name for the account |
| */ |
| public String getDisplayName() { |
| return mDisplayName; |
| } |
| |
| /** |
| * Set the description. Be sure to call save() to commit to database. |
| * @param description the new description |
| */ |
| public void setDisplayName(String description) { |
| mDisplayName = description; |
| } |
| |
| /** |
| * @return the email address for this account |
| */ |
| public String getEmailAddress() { |
| return mEmailAddress; |
| } |
| |
| /** |
| * Set the Email address for this account. Be sure to call save() to commit to database. |
| * @param emailAddress the new email address for this account |
| */ |
| public void setEmailAddress(String emailAddress) { |
| mEmailAddress = emailAddress; |
| } |
| |
| /** |
| * @return the sender's name for this account |
| */ |
| public String getSenderName() { |
| return mSenderName; |
| } |
| |
| /** |
| * Set the sender's name. Be sure to call save() to commit to database. |
| * @param name the new sender name |
| */ |
| public void setSenderName(String name) { |
| mSenderName = name; |
| } |
| |
| public String getSignature() { |
| return mSignature; |
| } |
| |
| public void setSignature(String signature) { |
| mSignature = signature; |
| } |
| |
| /** |
| * @return the minutes per check (for polling) |
| * TODO define sentinel values for "never", "push", etc. See Account.java |
| */ |
| public int getSyncInterval() { |
| return mSyncInterval; |
| } |
| |
| /** |
| * Set the minutes per check (for polling). Be sure to call save() to commit to database. |
| * TODO define sentinel values for "never", "push", etc. See Account.java |
| * @param minutes the number of minutes between polling checks |
| */ |
| public void setSyncInterval(int minutes) { |
| mSyncInterval = minutes; |
| } |
| |
| /** |
| * @return One of the {@code Account.SYNC_WINDOW_*} constants that represents the sync |
| * lookback window. |
| * TODO define sentinel values for "all", "1 month", etc. See Account.java |
| */ |
| public int getSyncLookback() { |
| return mSyncLookback; |
| } |
| |
| /** |
| * Set the sync lookback window. Be sure to call save() to commit to database. |
| * TODO define sentinel values for "all", "1 month", etc. See Account.java |
| * @param value One of the {@link com.android.emailcommon.service.SyncWindow} constants |
| */ |
| public void setSyncLookback(int value) { |
| mSyncLookback = value; |
| } |
| |
| /** |
| * @return the current ping duration. |
| */ |
| public long getPingDuration() { |
| return mPingDuration; |
| } |
| |
| /** |
| * Set the ping duration. Be sure to call save() to commit to database. |
| */ |
| public void setPingDuration(long value) { |
| mPingDuration = value; |
| } |
| |
| /** |
| * @return the flags for this account |
| */ |
| public int getFlags() { |
| return mFlags; |
| } |
| |
| /** |
| * Set the flags for this account |
| * @param newFlags the new value for the flags |
| */ |
| public void setFlags(int newFlags) { |
| mFlags = newFlags; |
| } |
| |
| /** |
| * @return the ringtone Uri for this account |
| * @deprecated Used only for migration |
| */ |
| @Deprecated |
| public String getRingtone() { |
| return mRingtoneUri; |
| } |
| |
| /** |
| * Set the "delete policy" as a simple 0,1,2 value set. |
| * @param newPolicy the new delete policy |
| */ |
| public void setDeletePolicy(int newPolicy) { |
| mFlags &= ~FLAGS_DELETE_POLICY_MASK; |
| mFlags |= (newPolicy << FLAGS_DELETE_POLICY_SHIFT) & FLAGS_DELETE_POLICY_MASK; |
| } |
| |
| /** |
| * Return the "delete policy" as a simple 0,1,2 value set. |
| * @return the current delete policy |
| */ |
| public int getDeletePolicy() { |
| return (mFlags & FLAGS_DELETE_POLICY_MASK) >> FLAGS_DELETE_POLICY_SHIFT; |
| } |
| |
| /** |
| * Return the Uuid associated with this account. This is primarily for compatibility |
| * with accounts set up by previous versions, because there are externals references |
| * to the Uuid (e.g. desktop shortcuts). |
| */ |
| public String getUuid() { |
| return mCompatibilityUuid; |
| } |
| |
| public HostAuth getOrCreateHostAuthSend(Context context) { |
| if (mHostAuthSend == null) { |
| if (mHostAuthKeySend != 0) { |
| mHostAuthSend = HostAuth.restoreHostAuthWithId(context, mHostAuthKeySend); |
| } else { |
| mHostAuthSend = new HostAuth(); |
| } |
| } |
| return mHostAuthSend; |
| } |
| |
| public HostAuth getOrCreateHostAuthRecv(Context context) { |
| if (mHostAuthRecv == null) { |
| if (mHostAuthKeyRecv != 0) { |
| mHostAuthRecv = HostAuth.restoreHostAuthWithId(context, mHostAuthKeyRecv); |
| } else { |
| mHostAuthRecv = new HostAuth(); |
| } |
| } |
| return mHostAuthRecv; |
| } |
| |
| /** |
| * For compatibility while converting to provider model, generate a "local store URI" |
| * |
| * @return a string in the form of a Uri, as used by the other parts of the email app |
| */ |
| public String getLocalStoreUri(Context context) { |
| return "local://localhost/" + context.getDatabasePath(getUuid() + ".db"); |
| } |
| |
| /** |
| * @return true if the account supports "search". |
| */ |
| public static boolean supportsServerSearch(Context context, long accountId) { |
| Account account = Account.restoreAccountWithId(context, accountId); |
| if (account == null) return false; |
| return (account.mFlags & Account.FLAGS_SUPPORTS_SEARCH) != 0; |
| } |
| |
| /** |
| * @return {@link Uri} to this {@link Account} in the |
| * {@code content://com.android.email.provider/account/UUID} format, which is safe to use |
| * for desktop shortcuts. |
| * |
| * <p>We don't want to store _id in shortcuts, because |
| * {@link com.android.email.provider.AccountBackupRestore} won't preserve it. |
| */ |
| public Uri getShortcutSafeUri() { |
| return getShortcutSafeUriFromUuid(mCompatibilityUuid); |
| } |
| |
| /** |
| * @return {@link Uri} to an {@link Account} with a {@code uuid}. |
| */ |
| public static Uri getShortcutSafeUriFromUuid(String uuid) { |
| return CONTENT_URI.buildUpon().appendEncodedPath(uuid).build(); |
| } |
| |
| /** |
| * Parse {@link Uri} in the {@code content://com.android.email.provider/account/ID} format |
| * where ID = account id (used on Eclair, Android 2.0-2.1) or UUID, and return _id of |
| * the {@link Account} associated with it. |
| * |
| * @param context context to access DB |
| * @param uri URI of interest |
| * @return _id of the {@link Account} associated with ID, or -1 if none found. |
| */ |
| public static long getAccountIdFromShortcutSafeUri(Context context, Uri uri) { |
| // Make sure the URI is in the correct format. |
| if (!"content".equals(uri.getScheme()) |
| || !EmailContent.AUTHORITY.equals(uri.getAuthority())) { |
| return -1; |
| } |
| |
| final List<String> ps = uri.getPathSegments(); |
| if (ps.size() != 2 || !"account".equals(ps.get(0))) { |
| return -1; |
| } |
| |
| // Now get the ID part. |
| final String id = ps.get(1); |
| |
| // First, see if ID can be parsed as long. (Eclair-style) |
| // (UUIDs have '-' in them, so they are always non-parsable.) |
| try { |
| return Long.parseLong(id); |
| } catch (NumberFormatException ok) { |
| // OK, it's not a long. Continue... |
| } |
| |
| // Now id is a UUId. |
| return getAccountIdFromUuid(context, id); |
| } |
| |
| /** |
| * @return ID of the account with the given UUID. |
| */ |
| public static long getAccountIdFromUuid(Context context, String uuid) { |
| return Utility.getFirstRowLong(context, |
| CONTENT_URI, ID_PROJECTION, |
| UUID_SELECTION, new String[] {uuid}, null, 0, -1L); |
| } |
| |
| /** |
| * Return the id of the default account. If one hasn't been explicitly specified, return the |
| * first one in the database. If no account exists, returns {@link #NO_ACCOUNT}. |
| * |
| * @param context the caller's context |
| * @param lastUsedAccountId the last used account id, which is the basis of the default account |
| */ |
| public static long getDefaultAccountId(final Context context, final long lastUsedAccountId) { |
| final Cursor cursor = context.getContentResolver().query( |
| CONTENT_URI, ID_PROJECTION, null, null, null); |
| |
| long firstAccount = NO_ACCOUNT; |
| |
| try { |
| if (cursor != null && cursor.moveToFirst()) { |
| do { |
| final long accountId = cursor.getLong(Account.ID_PROJECTION_COLUMN); |
| |
| if (accountId == lastUsedAccountId) { |
| return accountId; |
| } |
| |
| if (firstAccount == NO_ACCOUNT) { |
| firstAccount = accountId; |
| } |
| } while (cursor.moveToNext()); |
| } |
| } finally { |
| if (cursor != null) { |
| cursor.close(); |
| } |
| } |
| |
| return firstAccount; |
| } |
| |
| /** |
| * Given an account id, return the account's protocol |
| * @param context the caller's context |
| * @param accountId the id of the account to be examined |
| * @return the account's protocol (or null if the Account or HostAuth do not exist) |
| */ |
| public static String getProtocol(Context context, long accountId) { |
| Account account = Account.restoreAccountWithId(context, accountId); |
| if (account != null) { |
| return account.getProtocol(context); |
| } |
| return null; |
| } |
| |
| /** |
| * Return the account's protocol |
| * @param context the caller's context |
| * @return the account's protocol (or null if the HostAuth doesn't not exist) |
| */ |
| public String getProtocol(Context context) { |
| HostAuth hostAuth = HostAuth.restoreHostAuthWithId(context, mHostAuthKeyRecv); |
| if (hostAuth != null) { |
| return hostAuth.mProtocol; |
| } |
| return null; |
| } |
| |
| /** |
| * Return a corresponding account manager object using the passed in type |
| * |
| * @param type We can't look up the account type from here, so pass it in |
| * @return system account object |
| */ |
| public android.accounts.Account getAccountManagerAccount(String type) { |
| return new android.accounts.Account(mEmailAddress, type); |
| } |
| |
| /** |
| * Return the account ID for a message with a given id |
| * |
| * @param context the caller's context |
| * @param messageId the id of the message |
| * @return the account ID, or -1 if the account doesn't exist |
| */ |
| public static long getAccountIdForMessageId(Context context, long messageId) { |
| return Message.getKeyColumnLong(context, messageId, MessageColumns.ACCOUNT_KEY); |
| } |
| |
| /** |
| * Return the account for a message with a given id |
| * @param context the caller's context |
| * @param messageId the id of the message |
| * @return the account, or null if the account doesn't exist |
| */ |
| public static Account getAccountForMessageId(Context context, long messageId) { |
| long accountId = getAccountIdForMessageId(context, messageId); |
| if (accountId != -1) { |
| return Account.restoreAccountWithId(context, accountId); |
| } |
| return null; |
| } |
| |
| /** |
| * @return true if an {@code accountId} is assigned to any existing account. |
| */ |
| public static boolean isValidId(Context context, long accountId) { |
| return null != Utility.getFirstRowLong(context, CONTENT_URI, ID_PROJECTION, |
| ID_SELECTION, new String[] {Long.toString(accountId)}, null, |
| ID_PROJECTION_COLUMN); |
| } |
| |
| /** |
| * Check a single account for security hold status. |
| */ |
| public static boolean isSecurityHold(Context context, long accountId) { |
| return (Utility.getFirstRowLong(context, |
| ContentUris.withAppendedId(Account.CONTENT_URI, accountId), |
| ACCOUNT_FLAGS_PROJECTION, null, null, null, ACCOUNT_FLAGS_COLUMN_FLAGS, 0L) |
| & Account.FLAGS_SECURITY_HOLD) != 0; |
| } |
| |
| /** |
| * @return id of the "inbox" mailbox, or -1 if not found. |
| */ |
| public static long getInboxId(Context context, long accountId) { |
| return Utility.getFirstRowLong(context, Mailbox.CONTENT_URI, ID_PROJECTION, |
| FIND_INBOX_SELECTION, new String[] {Long.toString(accountId)}, null, |
| ID_PROJECTION_COLUMN, -1L); |
| } |
| |
| /** |
| * Clear all account hold flags that are set. |
| * |
| * (This will trigger watchers, and in particular will cause EAS to try and resync the |
| * account(s).) |
| */ |
| public static void clearSecurityHoldOnAllAccounts(Context context) { |
| ContentResolver resolver = context.getContentResolver(); |
| Cursor c = resolver.query(Account.CONTENT_URI, ACCOUNT_FLAGS_PROJECTION, |
| SECURITY_NONZERO_SELECTION, null, null); |
| try { |
| while (c.moveToNext()) { |
| int flags = c.getInt(ACCOUNT_FLAGS_COLUMN_FLAGS); |
| |
| if (0 != (flags & FLAGS_SECURITY_HOLD)) { |
| ContentValues cv = new ContentValues(); |
| cv.put(AccountColumns.FLAGS, flags & ~FLAGS_SECURITY_HOLD); |
| long accountId = c.getLong(ACCOUNT_FLAGS_COLUMN_ID); |
| Uri uri = ContentUris.withAppendedId(Account.CONTENT_URI, accountId); |
| resolver.update(uri, cv, null, null); |
| } |
| } |
| } finally { |
| c.close(); |
| } |
| } |
| |
| /** |
| * Given an account id, determine whether the account is currently prohibited from automatic |
| * sync, due to roaming while the account's policy disables this |
| * @param context the caller's context |
| * @param accountId the account id |
| * @return true if the account can't automatically sync due to roaming; false otherwise |
| */ |
| public static boolean isAutomaticSyncDisabledByRoaming(Context context, long accountId) { |
| Account account = Account.restoreAccountWithId(context, accountId); |
| // Account being deleted; just return |
| if (account == null) return false; |
| long policyKey = account.mPolicyKey; |
| // If no security policy, we're good |
| if (policyKey <= 0) return false; |
| |
| ConnectivityManager cm = |
| (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE); |
| NetworkInfo info = cm.getActiveNetworkInfo(); |
| // If we're not on mobile, we're good |
| if (info == null || (info.getType() != ConnectivityManager.TYPE_MOBILE)) return false; |
| // If we're not roaming, we're good |
| if (!info.isRoaming()) return false; |
| Policy policy = Policy.restorePolicyWithId(context, policyKey); |
| // Account being deleted; just return |
| if (policy == null) return false; |
| return policy.mRequireManualSyncWhenRoaming; |
| } |
| |
| /* |
| * Override this so that we can store the HostAuth's first and link them to the Account |
| * (non-Javadoc) |
| * @see com.android.email.provider.EmailContent#save(android.content.Context) |
| */ |
| @Override |
| public Uri save(Context context) { |
| if (isSaved()) { |
| throw new UnsupportedOperationException(); |
| } |
| // This logic is in place so I can (a) short circuit the expensive stuff when |
| // possible, and (b) override (and throw) if anyone tries to call save() or update() |
| // directly for Account, which are unsupported. |
| if (mHostAuthRecv == null && mHostAuthSend == null && mPolicy != null) { |
| return super.save(context); |
| } |
| |
| int index = 0; |
| int recvIndex = -1; |
| int sendIndex = -1; |
| |
| // Create operations for saving the send and recv hostAuths |
| // Also, remember which operation in the array they represent |
| ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>(); |
| if (mHostAuthRecv != null) { |
| recvIndex = index++; |
| ops.add(ContentProviderOperation.newInsert(mHostAuthRecv.mBaseUri) |
| .withValues(mHostAuthRecv.toContentValues()) |
| .build()); |
| } |
| if (mHostAuthSend != null) { |
| sendIndex = index++; |
| ops.add(ContentProviderOperation.newInsert(mHostAuthSend.mBaseUri) |
| .withValues(mHostAuthSend.toContentValues()) |
| .build()); |
| } |
| |
| // Now do the Account |
| ContentValues cv = null; |
| if (recvIndex >= 0 || sendIndex >= 0) { |
| cv = new ContentValues(); |
| if (recvIndex >= 0) { |
| cv.put(Account.HOST_AUTH_KEY_RECV, recvIndex); |
| } |
| if (sendIndex >= 0) { |
| cv.put(Account.HOST_AUTH_KEY_SEND, sendIndex); |
| } |
| } |
| |
| ContentProviderOperation.Builder b = ContentProviderOperation.newInsert(mBaseUri); |
| b.withValues(toContentValues()); |
| if (cv != null) { |
| b.withValueBackReferences(cv); |
| } |
| ops.add(b.build()); |
| |
| try { |
| ContentProviderResult[] results = |
| context.getContentResolver().applyBatch(EmailContent.AUTHORITY, ops); |
| // If saving, set the mId's of the various saved objects |
| if (recvIndex >= 0) { |
| long newId = getId(results[recvIndex].uri); |
| mHostAuthKeyRecv = newId; |
| mHostAuthRecv.mId = newId; |
| } |
| if (sendIndex >= 0) { |
| long newId = getId(results[sendIndex].uri); |
| mHostAuthKeySend = newId; |
| mHostAuthSend.mId = newId; |
| } |
| Uri u = results[index].uri; |
| mId = getId(u); |
| return u; |
| } catch (RemoteException e) { |
| // There is nothing to be done here; fail by returning null |
| } catch (OperationApplicationException e) { |
| // There is nothing to be done here; fail by returning null |
| } |
| return null; |
| } |
| |
| @Override |
| public ContentValues toContentValues() { |
| ContentValues values = new ContentValues(); |
| values.put(AccountColumns.DISPLAY_NAME, mDisplayName); |
| values.put(AccountColumns.EMAIL_ADDRESS, mEmailAddress); |
| values.put(AccountColumns.SYNC_KEY, mSyncKey); |
| values.put(AccountColumns.SYNC_LOOKBACK, mSyncLookback); |
| values.put(AccountColumns.SYNC_INTERVAL, mSyncInterval); |
| values.put(AccountColumns.HOST_AUTH_KEY_RECV, mHostAuthKeyRecv); |
| values.put(AccountColumns.HOST_AUTH_KEY_SEND, mHostAuthKeySend); |
| values.put(AccountColumns.FLAGS, mFlags); |
| values.put(AccountColumns.COMPATIBILITY_UUID, mCompatibilityUuid); |
| values.put(AccountColumns.SENDER_NAME, mSenderName); |
| values.put(AccountColumns.RINGTONE_URI, mRingtoneUri); |
| values.put(AccountColumns.PROTOCOL_VERSION, mProtocolVersion); |
| values.put(AccountColumns.NEW_MESSAGE_COUNT, mNewMessageCount); |
| values.put(AccountColumns.SECURITY_SYNC_KEY, mSecuritySyncKey); |
| values.put(AccountColumns.SIGNATURE, mSignature); |
| values.put(AccountColumns.POLICY_KEY, mPolicyKey); |
| values.put(AccountColumns.PING_DURATION, mPingDuration); |
| return values; |
| } |
| |
| /** |
| * Supports Parcelable |
| */ |
| @Override |
| public int describeContents() { |
| return 0; |
| } |
| |
| /** |
| * Supports Parcelable |
| */ |
| public static final Parcelable.Creator<Account> CREATOR |
| = new Parcelable.Creator<Account>() { |
| @Override |
| public Account createFromParcel(Parcel in) { |
| return new Account(in); |
| } |
| |
| @Override |
| public Account[] newArray(int size) { |
| return new Account[size]; |
| } |
| }; |
| |
| /** |
| * Supports Parcelable |
| */ |
| @Override |
| public void writeToParcel(Parcel dest, int flags) { |
| // mBaseUri is not parceled |
| dest.writeLong(mId); |
| dest.writeString(mDisplayName); |
| dest.writeString(mEmailAddress); |
| dest.writeString(mSyncKey); |
| dest.writeInt(mSyncLookback); |
| dest.writeInt(mSyncInterval); |
| dest.writeLong(mHostAuthKeyRecv); |
| dest.writeLong(mHostAuthKeySend); |
| dest.writeInt(mFlags); |
| dest.writeString(mCompatibilityUuid); |
| dest.writeString(mSenderName); |
| dest.writeString(mRingtoneUri); |
| dest.writeString(mProtocolVersion); |
| dest.writeInt(mNewMessageCount); |
| dest.writeString(mSecuritySyncKey); |
| dest.writeString(mSignature); |
| dest.writeLong(mPolicyKey); |
| |
| if (mHostAuthRecv != null) { |
| dest.writeByte((byte)1); |
| mHostAuthRecv.writeToParcel(dest, flags); |
| } else { |
| dest.writeByte((byte)0); |
| } |
| |
| if (mHostAuthSend != null) { |
| dest.writeByte((byte)1); |
| mHostAuthSend.writeToParcel(dest, flags); |
| } else { |
| dest.writeByte((byte)0); |
| } |
| } |
| |
| /** |
| * Supports Parcelable |
| */ |
| public Account(Parcel in) { |
| mBaseUri = Account.CONTENT_URI; |
| mId = in.readLong(); |
| mDisplayName = in.readString(); |
| mEmailAddress = in.readString(); |
| mSyncKey = in.readString(); |
| mSyncLookback = in.readInt(); |
| mSyncInterval = in.readInt(); |
| mHostAuthKeyRecv = in.readLong(); |
| mHostAuthKeySend = in.readLong(); |
| mFlags = in.readInt(); |
| mCompatibilityUuid = in.readString(); |
| mSenderName = in.readString(); |
| mRingtoneUri = in.readString(); |
| mProtocolVersion = in.readString(); |
| mNewMessageCount = in.readInt(); |
| mSecuritySyncKey = in.readString(); |
| mSignature = in.readString(); |
| mPolicyKey = in.readLong(); |
| |
| mHostAuthRecv = null; |
| if (in.readByte() == 1) { |
| mHostAuthRecv = new HostAuth(in); |
| } |
| |
| mHostAuthSend = null; |
| if (in.readByte() == 1) { |
| mHostAuthSend = new HostAuth(in); |
| } |
| } |
| |
| /** |
| * For debugger support only - DO NOT use for code. |
| */ |
| @Override |
| public String toString() { |
| StringBuilder sb = new StringBuilder('['); |
| if (mHostAuthRecv != null && mHostAuthRecv.mProtocol != null) { |
| sb.append(mHostAuthRecv.mProtocol); |
| sb.append(':'); |
| } |
| if (mDisplayName != null) sb.append(mDisplayName); |
| sb.append(':'); |
| if (mEmailAddress != null) sb.append(mEmailAddress); |
| sb.append(':'); |
| if (mSenderName != null) sb.append(mSenderName); |
| sb.append(']'); |
| return sb.toString(); |
| } |
| } |