/* * 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.net.ConnectivityManager; import android.net.NetworkInfo; import android.net.Uri; import android.os.Parcel; import android.os.Parcelable; import android.os.RemoteException; import com.android.emailcommon.provider.EmailContent.AccountColumns; import com.android.emailcommon.utility.Utility; 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"; @SuppressWarnings("hiding") public static final Uri CONTENT_URI = Uri.parse(EmailContent.CONTENT_URI + "/account"); public static final Uri ADD_TO_FIELD_URI = Uri.parse(EmailContent.CONTENT_URI + "/accountIdAddToField"); public static final Uri RESET_NEW_MESSAGE_COUNT_URI = Uri.parse(EmailContent.CONTENT_URI + "/resetNewMessageCount"); public static final Uri NOTIFIER_URI = Uri.parse(EmailContent.CONTENT_NOTIFIER_URI + "/account"); public static final Uri DEFAULT_ACCOUNT_ID_URI = Uri.parse(EmailContent.CONTENT_URI + "/account/default"); // 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. * * IMPORTANT: 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. * * IMPORTANT: 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 public final static int FLAGS_NOTIFY_NEW_MAIL = 1<<0; // Whether or not the user has asked for vibration notifications with all new mail public final static int FLAGS_VIBRATE_ALWAYS = 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 or not the user has asked for vibration notifications when the ringer is silent public static final int FLAGS_VIBRATE_WHEN_SILENT = 1<<6; // 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; // 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 String mDisplayName; public String mEmailAddress; public String mSyncKey; public int mSyncLookback; public int mSyncInterval; public long mHostAuthKeyRecv; public long mHostAuthKeySend; public int mFlags; public boolean mIsDefault; // note: callers should use getDefaultAccountId() public String mCompatibilityUuid; public String mSenderName; public String mRingtoneUri; public String mProtocolVersion; public int mNewMessageCount; public String mSecuritySyncKey; public String mSignature; public long mPolicyKey; // For compatibility with Email1 public long mNotifiedMessageId; public int mNotifiedMessageCount; // Convenience for creating/working with an account public transient HostAuth mHostAuthRecv; public transient HostAuth mHostAuthSend; public transient Policy mPolicy; // Might hold the corresponding AccountManager account structure public transient android.accounts.Account mAmAccount; 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_IS_DEFAULT_COLUMN = 9; public static final int CONTENT_COMPATIBILITY_UUID_COLUMN = 10; public static final int CONTENT_SENDER_NAME_COLUMN = 11; public static final int CONTENT_RINGTONE_URI_COLUMN = 12; public static final int CONTENT_PROTOCOL_VERSION_COLUMN = 13; public static final int CONTENT_NEW_MESSAGE_COUNT_COLUMN = 14; public static final int CONTENT_SECURITY_SYNC_KEY_COLUMN = 15; public static final int CONTENT_SIGNATURE_COLUMN = 16; public static final int CONTENT_POLICY_KEY = 17; public static final int CONTENT_NOTIFIED_MESSAGE_ID_COLUMN = 18; public static final int CONTENT_NOTIFIED_MESSAGE_COUNT_COLUMN = 19; 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.IS_DEFAULT, 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.NOTIFIED_MESSAGE_ID, AccountColumns.NOTIFIED_MESSAGE_COUNT }; 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 + " =?"; /** * This projection is for searching for the default account */ private static final String[] DEFAULT_ID_PROJECTION = new String[] { RECORD_ID, IS_DEFAULT }; /** * no public constructor since this is a utility class */ public Account() { mBaseUri = CONTENT_URI; // other defaults (policy) mRingtoneUri = "content://settings/system/notification_sound"; mSyncInterval = -1; mSyncLookback = -1; mFlags = FLAGS_NOTIFY_NEW_MAIL; 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); mIsDefault = cursor.getInt(CONTENT_IS_DEFAULT_COLUMN) == 1; 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); mNotifiedMessageId = cursor.getLong(CONTENT_NOTIFIED_MESSAGE_ID_COLUMN); mNotifiedMessageCount = cursor.getInt(CONTENT_NOTIFIED_MESSAGE_COUNT_COLUMN); } private long getId(Uri u) { return Long.parseLong(u.getPathSegments().get(1)); } /** * @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 flags for this account * @see #FLAGS_NOTIFY_NEW_MAIL * @see #FLAGS_VIBRATE_ALWAYS * @see #FLAGS_VIBRATE_WHEN_SILENT */ public int getFlags() { return mFlags; } /** * Set the flags for this account * @see #FLAGS_NOTIFY_NEW_MAIL * @see #FLAGS_VIBRATE_ALWAYS * @see #FLAGS_VIBRATE_WHEN_SILENT * @param newFlags the new value for the flags */ public void setFlags(int newFlags) { mFlags = newFlags; } /** * @return the ringtone Uri for this account */ public String getRingtone() { return mRingtoneUri; } /** * Set the ringtone Uri for this account * @param newUri the new URI string for the ringtone for this account */ public void setRingtone(String newUri) { mRingtoneUri = newUri; } /** * 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 instance is of an EAS account. * * NOTE This method accesses the DB if {@link #mHostAuthRecv} hasn't been restored yet. * Use caution when you use this on the main thread. */ public boolean isEasAccount(Context context) { return "eas".equals(getProtocol(context)); } public boolean supportsMoveMessages(Context context) { String protocol = getProtocol(context); return "eas".equals(protocol) || "imap".equals(protocol); } /** * @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; } /** * Set the account to be the default account. If this is set to "true", when the account * is saved, all other accounts will have the same value set to "false". * @param newDefaultState the new default state - if true, others will be cleared. */ public void setDefaultAccount(boolean newDefaultState) { mIsDefault = newDefaultState; } /** * @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. * *
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())
|| !AUTHORITY.equals(uri.getAuthority())) {
return -1;
}
final List