/**
  * 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;
 }
  public static void updateAccountManagerType(
      Context context, android.accounts.Account amAccount, final Map<String, String> protocolMap) {
    final ContentResolver resolver = context.getContentResolver();
    final Cursor c =
        resolver.query(
            Account.CONTENT_URI,
            Account.CONTENT_PROJECTION,
            AccountColumns.EMAIL_ADDRESS + "=?",
            new String[] {amAccount.name},
            null);
    // That's odd, isn't it?
    if (c == null) return;
    try {
      if (c.moveToNext()) {
        // Get the EmailProvider Account/HostAuth
        final Account account = new Account();
        account.restore(c);
        final HostAuth hostAuth = HostAuth.restoreHostAuthWithId(context, account.mHostAuthKeyRecv);
        if (hostAuth == null) {
          return;
        }

        final String newProtocol = protocolMap.get(hostAuth.mProtocol);
        if (newProtocol == null) {
          // This account doesn't need updating.
          return;
        }

        LogUtils.w(Logging.LOG_TAG, "Converting " + amAccount.name + " to " + newProtocol);

        final ContentValues accountValues = new ContentValues();
        int oldFlags = account.mFlags;

        // Mark the provider account incomplete so it can't get reconciled away
        account.mFlags |= Account.FLAGS_INCOMPLETE;
        accountValues.put(AccountColumns.FLAGS, account.mFlags);
        final Uri accountUri = ContentUris.withAppendedId(Account.CONTENT_URI, account.mId);
        resolver.update(accountUri, accountValues, null, null);

        // Change the HostAuth to reference the new protocol; this has to be done before
        // trying to create the AccountManager account (below)
        final ContentValues hostValues = new ContentValues();
        hostValues.put(HostAuth.PROTOCOL, newProtocol);
        resolver.update(
            ContentUris.withAppendedId(HostAuth.CONTENT_URI, hostAuth.mId), hostValues, null, null);
        LogUtils.w(Logging.LOG_TAG, "Updated HostAuths");

        try {
          // Get current settings for the existing AccountManager account
          boolean email = ContentResolver.getSyncAutomatically(amAccount, EmailContent.AUTHORITY);
          if (!email) {
            // Try our old provider name
            email = ContentResolver.getSyncAutomatically(amAccount, "com.android.email.provider");
          }
          final boolean contacts =
              ContentResolver.getSyncAutomatically(amAccount, ContactsContract.AUTHORITY);
          final boolean calendar =
              ContentResolver.getSyncAutomatically(amAccount, CalendarContract.AUTHORITY);
          LogUtils.w(
              Logging.LOG_TAG,
              "Email: " + email + ", Contacts: " + contacts + "," + " Calendar: " + calendar);

          // Get sync keys for calendar/contacts
          final String amName = amAccount.name;
          final String oldType = amAccount.type;
          ContentProviderClient client =
              context
                  .getContentResolver()
                  .acquireContentProviderClient(CalendarContract.CONTENT_URI);
          byte[] calendarSyncKey = null;
          try {
            calendarSyncKey =
                SyncStateContract.Helpers.get(
                    client,
                    asCalendarSyncAdapter(SyncState.CONTENT_URI, amName, oldType),
                    new android.accounts.Account(amName, oldType));
          } catch (RemoteException e) {
            LogUtils.w(Logging.LOG_TAG, "Get calendar key FAILED");
          } finally {
            client.release();
          }
          client =
              context
                  .getContentResolver()
                  .acquireContentProviderClient(ContactsContract.AUTHORITY_URI);
          byte[] contactsSyncKey = null;
          try {
            contactsSyncKey =
                SyncStateContract.Helpers.get(
                    client,
                    ContactsContract.SyncState.CONTENT_URI,
                    new android.accounts.Account(amName, oldType));
          } catch (RemoteException e) {
            LogUtils.w(Logging.LOG_TAG, "Get contacts key FAILED");
          } finally {
            client.release();
          }
          if (calendarSyncKey != null) {
            LogUtils.w(Logging.LOG_TAG, "Got calendar key: " + new String(calendarSyncKey));
          }
          if (contactsSyncKey != null) {
            LogUtils.w(Logging.LOG_TAG, "Got contacts key: " + new String(contactsSyncKey));
          }

          // Set up a new AccountManager account with new type and old settings
          AccountManagerFuture<?> amFuture =
              setupAccountManagerAccount(context, account, email, calendar, contacts, null);
          finishAccountManagerBlocker(amFuture);
          LogUtils.w(Logging.LOG_TAG, "Created new AccountManager account");

          // TODO: Clean up how we determine the type.
          final String accountType = protocolMap.get(hostAuth.mProtocol + "_type");
          // Move calendar and contacts data from the old account to the new one.
          // We must do this before deleting the old account or the data is lost.
          moveCalendarData(context.getContentResolver(), amName, oldType, accountType);
          moveContactsData(context.getContentResolver(), amName, oldType, accountType);

          // Delete the AccountManager account
          amFuture = AccountManager.get(context).removeAccount(amAccount, null, null);
          finishAccountManagerBlocker(amFuture);
          LogUtils.w(Logging.LOG_TAG, "Deleted old AccountManager account");

          // Restore sync keys for contacts/calendar

          if (accountType != null && calendarSyncKey != null && calendarSyncKey.length != 0) {
            client =
                context
                    .getContentResolver()
                    .acquireContentProviderClient(CalendarContract.CONTENT_URI);
            try {
              SyncStateContract.Helpers.set(
                  client,
                  asCalendarSyncAdapter(SyncState.CONTENT_URI, amName, accountType),
                  new android.accounts.Account(amName, accountType),
                  calendarSyncKey);
              LogUtils.w(Logging.LOG_TAG, "Set calendar key...");
            } catch (RemoteException e) {
              LogUtils.w(Logging.LOG_TAG, "Set calendar key FAILED");
            } finally {
              client.release();
            }
          }
          if (accountType != null && contactsSyncKey != null && contactsSyncKey.length != 0) {
            client =
                context
                    .getContentResolver()
                    .acquireContentProviderClient(ContactsContract.AUTHORITY_URI);
            try {
              SyncStateContract.Helpers.set(
                  client,
                  ContactsContract.SyncState.CONTENT_URI,
                  new android.accounts.Account(amName, accountType),
                  contactsSyncKey);
              LogUtils.w(Logging.LOG_TAG, "Set contacts key...");
            } catch (RemoteException e) {
              LogUtils.w(Logging.LOG_TAG, "Set contacts key FAILED");
            }
          }

          // That's all folks!
          LogUtils.w(Logging.LOG_TAG, "Account update completed.");
        } finally {
          // Clear the incomplete flag on the provider account
          accountValues.put(AccountColumns.FLAGS, oldFlags);
          resolver.update(accountUri, accountValues, null, null);
          LogUtils.w(Logging.LOG_TAG, "[Incomplete flag cleared]");
        }
      }
    } finally {
      c.close();
    }
  }