1898283b6fa4f141d5c2a0831b96b19de11d587b3Makoto Onuki/*
2898283b6fa4f141d5c2a0831b96b19de11d587b3Makoto Onuki * Copyright (C) 2010 The Android Open Source Project
3898283b6fa4f141d5c2a0831b96b19de11d587b3Makoto Onuki *
4898283b6fa4f141d5c2a0831b96b19de11d587b3Makoto Onuki * Licensed under the Apache License, Version 2.0 (the "License");
5898283b6fa4f141d5c2a0831b96b19de11d587b3Makoto Onuki * you may not use this file except in compliance with the License.
6898283b6fa4f141d5c2a0831b96b19de11d587b3Makoto Onuki * You may obtain a copy of the License at
7898283b6fa4f141d5c2a0831b96b19de11d587b3Makoto Onuki *
8898283b6fa4f141d5c2a0831b96b19de11d587b3Makoto Onuki *      http://www.apache.org/licenses/LICENSE-2.0
9898283b6fa4f141d5c2a0831b96b19de11d587b3Makoto Onuki *
10898283b6fa4f141d5c2a0831b96b19de11d587b3Makoto Onuki * Unless required by applicable law or agreed to in writing, software
11898283b6fa4f141d5c2a0831b96b19de11d587b3Makoto Onuki * distributed under the License is distributed on an "AS IS" BASIS,
12898283b6fa4f141d5c2a0831b96b19de11d587b3Makoto Onuki * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13898283b6fa4f141d5c2a0831b96b19de11d587b3Makoto Onuki * See the License for the specific language governing permissions and
14898283b6fa4f141d5c2a0831b96b19de11d587b3Makoto Onuki * limitations under the License.
15898283b6fa4f141d5c2a0831b96b19de11d587b3Makoto Onuki */
16898283b6fa4f141d5c2a0831b96b19de11d587b3Makoto Onuki
17898283b6fa4f141d5c2a0831b96b19de11d587b3Makoto Onukipackage com.android.email.service;
18898283b6fa4f141d5c2a0831b96b19de11d587b3Makoto Onuki
19c50b6f685ba26a38b8110f6ca0115b12f84d643cMakoto Onukiimport android.accounts.AccountManager;
20898283b6fa4f141d5c2a0831b96b19de11d587b3Makoto Onukiimport android.app.IntentService;
21898283b6fa4f141d5c2a0831b96b19de11d587b3Makoto Onukiimport android.content.ComponentName;
228c89674b64f6669fa94bf593abad0f1e2255a812Todd Kennedyimport android.content.ContentResolver;
238c89674b64f6669fa94bf593abad0f1e2255a812Todd Kennedyimport android.content.ContentUris;
248c89674b64f6669fa94bf593abad0f1e2255a812Todd Kennedyimport android.content.ContentValues;
25898283b6fa4f141d5c2a0831b96b19de11d587b3Makoto Onukiimport android.content.Context;
26898283b6fa4f141d5c2a0831b96b19de11d587b3Makoto Onukiimport android.content.Intent;
2748b14fde961ef57103f7a31196254e357987f203Yu Ping Huimport android.content.PeriodicSync;
28898283b6fa4f141d5c2a0831b96b19de11d587b3Makoto Onukiimport android.content.pm.PackageManager;
298c89674b64f6669fa94bf593abad0f1e2255a812Todd Kennedyimport android.database.Cursor;
308c89674b64f6669fa94bf593abad0f1e2255a812Todd Kennedyimport android.net.Uri;
3148b14fde961ef57103f7a31196254e357987f203Yu Ping Huimport android.os.Bundle;
3248b14fde961ef57103f7a31196254e357987f203Yu Ping Huimport android.provider.CalendarContract;
3348b14fde961ef57103f7a31196254e357987f203Yu Ping Huimport android.provider.ContactsContract;
34bc8bc050b080287c35241f5ab0c310bbbdb6e805Yu Ping Huimport android.text.TextUtils;
3548b14fde961ef57103f7a31196254e357987f203Yu Ping Huimport android.text.format.DateUtils;
36898283b6fa4f141d5c2a0831b96b19de11d587b3Makoto Onuki
378196f82ce207f0c7a1088996d0f3017a16c3637eTony Mantlerimport com.android.email.EmailIntentService;
3866a47b8dac5e97e37c30b928bc5a227d74baada9Marc Blankimport com.android.email.Preferences;
395c523858385176c33a7456bb84035de78552d22dMarc Blankimport com.android.email.R;
4066a47b8dac5e97e37c30b928bc5a227d74baada9Marc Blankimport com.android.email.SecurityPolicy;
41afe097f318a7eeac9d240d2bedbc5caba7640ea8Yu Ping Huimport com.android.email.provider.AccountReconciler;
4266a47b8dac5e97e37c30b928bc5a227d74baada9Marc Blankimport com.android.emailcommon.Logging;
4366a47b8dac5e97e37c30b928bc5a227d74baada9Marc Blankimport com.android.emailcommon.provider.Account;
4448b14fde961ef57103f7a31196254e357987f203Yu Ping Huimport com.android.emailcommon.provider.EmailContent;
4566a47b8dac5e97e37c30b928bc5a227d74baada9Marc Blankimport com.android.emailcommon.provider.EmailContent.AccountColumns;
4666a47b8dac5e97e37c30b928bc5a227d74baada9Marc Blankimport com.android.emailcommon.provider.HostAuth;
478196f82ce207f0c7a1088996d0f3017a16c3637eTony Mantlerimport com.android.mail.providers.UIProvider;
48560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedyimport com.android.mail.utils.LogUtils;
49ac03b5576516f9e14a089b79b00dd7bf2dd48cfaRégis Décampsimport com.android.mail.utils.NotificationActionUtils;
50bc8bc050b080287c35241f5ab0c310bbbdb6e805Yu Ping Huimport com.google.common.annotations.VisibleForTesting;
5148b14fde961ef57103f7a31196254e357987f203Yu Ping Huimport com.google.common.collect.Maps;
5266a47b8dac5e97e37c30b928bc5a227d74baada9Marc Blank
5348b14fde961ef57103f7a31196254e357987f203Yu Ping Huimport java.util.Collections;
54bc8bc050b080287c35241f5ab0c310bbbdb6e805Yu Ping Huimport java.util.HashSet;
5548b14fde961ef57103f7a31196254e357987f203Yu Ping Huimport java.util.List;
5648b14fde961ef57103f7a31196254e357987f203Yu Ping Huimport java.util.Map;
57bc8bc050b080287c35241f5ab0c310bbbdb6e805Yu Ping Huimport java.util.Set;
58462da178b1be608a74000956c53dc8ecb438c689Yu Ping Hu
59898283b6fa4f141d5c2a0831b96b19de11d587b3Makoto Onuki/**
60898283b6fa4f141d5c2a0831b96b19de11d587b3Makoto Onuki * The service that really handles broadcast intents on a worker thread.
61898283b6fa4f141d5c2a0831b96b19de11d587b3Makoto Onuki *
62898283b6fa4f141d5c2a0831b96b19de11d587b3Makoto Onuki * We make it a service, because:
63898283b6fa4f141d5c2a0831b96b19de11d587b3Makoto Onuki * <ul>
64898283b6fa4f141d5c2a0831b96b19de11d587b3Makoto Onuki *   <li>So that it's less likely for the process to get killed.
65898283b6fa4f141d5c2a0831b96b19de11d587b3Makoto Onuki *   <li>Even if it does, the Intent that have started it will be re-delivered by the system,
66898283b6fa4f141d5c2a0831b96b19de11d587b3Makoto Onuki *   and we can start the process again.  (Using {@link #setIntentRedelivery}).
67898283b6fa4f141d5c2a0831b96b19de11d587b3Makoto Onuki * </ul>
68a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler *
69a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler * This also handles the DeviceAdminReceiver in SecurityPolicy, because it is also
70a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler * a BroadcastReceiver and requires the same processing semantics.
71898283b6fa4f141d5c2a0831b96b19de11d587b3Makoto Onuki */
72898283b6fa4f141d5c2a0831b96b19de11d587b3Makoto Onukipublic class EmailBroadcastProcessorService extends IntentService {
73a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler    // Action used for BroadcastReceiver entry point
74a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler    private static final String ACTION_BROADCAST = "broadcast_receiver";
75a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler
76a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler    // This is a helper used to process DeviceAdminReceiver messages
77a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler    private static final String ACTION_DEVICE_POLICY_ADMIN = "com.android.email.devicepolicy";
78a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler    private static final String EXTRA_DEVICE_POLICY_ADMIN = "message_code";
79a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler
80462da178b1be608a74000956c53dc8ecb438c689Yu Ping Hu    // Action used for EmailUpgradeBroadcastReceiver.
81462da178b1be608a74000956c53dc8ecb438c689Yu Ping Hu    private static final String ACTION_UPGRADE_BROADCAST = "upgrade_broadcast_receiver";
82462da178b1be608a74000956c53dc8ecb438c689Yu Ping Hu
83898283b6fa4f141d5c2a0831b96b19de11d587b3Makoto Onuki    public EmailBroadcastProcessorService() {
84898283b6fa4f141d5c2a0831b96b19de11d587b3Makoto Onuki        // Class name will be the thread name.
85898283b6fa4f141d5c2a0831b96b19de11d587b3Makoto Onuki        super(EmailBroadcastProcessorService.class.getName());
86898283b6fa4f141d5c2a0831b96b19de11d587b3Makoto Onuki
87898283b6fa4f141d5c2a0831b96b19de11d587b3Makoto Onuki        // Intent should be redelivered if the process gets killed before completing the job.
88898283b6fa4f141d5c2a0831b96b19de11d587b3Makoto Onuki        setIntentRedelivery(true);
89898283b6fa4f141d5c2a0831b96b19de11d587b3Makoto Onuki    }
90898283b6fa4f141d5c2a0831b96b19de11d587b3Makoto Onuki
91898283b6fa4f141d5c2a0831b96b19de11d587b3Makoto Onuki    /**
92898283b6fa4f141d5c2a0831b96b19de11d587b3Makoto Onuki     * Entry point for {@link EmailBroadcastReceiver}.
93898283b6fa4f141d5c2a0831b96b19de11d587b3Makoto Onuki     */
94898283b6fa4f141d5c2a0831b96b19de11d587b3Makoto Onuki    public static void processBroadcastIntent(Context context, Intent broadcastIntent) {
95898283b6fa4f141d5c2a0831b96b19de11d587b3Makoto Onuki        Intent i = new Intent(context, EmailBroadcastProcessorService.class);
96a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler        i.setAction(ACTION_BROADCAST);
97898283b6fa4f141d5c2a0831b96b19de11d587b3Makoto Onuki        i.putExtra(Intent.EXTRA_INTENT, broadcastIntent);
98898283b6fa4f141d5c2a0831b96b19de11d587b3Makoto Onuki        context.startService(i);
99898283b6fa4f141d5c2a0831b96b19de11d587b3Makoto Onuki    }
100898283b6fa4f141d5c2a0831b96b19de11d587b3Makoto Onuki
101462da178b1be608a74000956c53dc8ecb438c689Yu Ping Hu    public static void processUpgradeBroadcastIntent(final Context context) {
102462da178b1be608a74000956c53dc8ecb438c689Yu Ping Hu        final Intent i = new Intent(context, EmailBroadcastProcessorService.class);
103462da178b1be608a74000956c53dc8ecb438c689Yu Ping Hu        i.setAction(ACTION_UPGRADE_BROADCAST);
104462da178b1be608a74000956c53dc8ecb438c689Yu Ping Hu        context.startService(i);
105462da178b1be608a74000956c53dc8ecb438c689Yu Ping Hu    }
106462da178b1be608a74000956c53dc8ecb438c689Yu Ping Hu
107a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler    /**
108a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler     * Entry point for {@link com.android.email.SecurityPolicy.PolicyAdmin}.  These will
109a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler     * simply callback to {@link
110a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler     * com.android.email.SecurityPolicy#onDeviceAdminReceiverMessage(Context, int)}.
111a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler     */
112a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler    public static void processDevicePolicyMessage(Context context, int message) {
113a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler        Intent i = new Intent(context, EmailBroadcastProcessorService.class);
114a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler        i.setAction(ACTION_DEVICE_POLICY_ADMIN);
115a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler        i.putExtra(EXTRA_DEVICE_POLICY_ADMIN, message);
116a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler        context.startService(i);
117a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler    }
118a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler
119898283b6fa4f141d5c2a0831b96b19de11d587b3Makoto Onuki    @Override
120898283b6fa4f141d5c2a0831b96b19de11d587b3Makoto Onuki    protected void onHandleIntent(Intent intent) {
121898283b6fa4f141d5c2a0831b96b19de11d587b3Makoto Onuki        // This method is called on a worker thread.
122898283b6fa4f141d5c2a0831b96b19de11d587b3Makoto Onuki
123a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler        // Dispatch from entry point
124a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler        final String action = intent.getAction();
125a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler        if (ACTION_BROADCAST.equals(action)) {
126a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler            final Intent broadcastIntent = intent.getParcelableExtra(Intent.EXTRA_INTENT);
127a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler            final String broadcastAction = broadcastIntent.getAction();
128898283b6fa4f141d5c2a0831b96b19de11d587b3Makoto Onuki
129a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler            if (Intent.ACTION_BOOT_COMPLETED.equals(broadcastAction)) {
130a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler                onBootCompleted();
131c50b6f685ba26a38b8110f6ca0115b12f84d643cMakoto Onuki            } else if (AccountManager.LOGIN_ACCOUNTS_CHANGED_ACTION.equals(broadcastAction)) {
132c50b6f685ba26a38b8110f6ca0115b12f84d643cMakoto Onuki                onSystemAccountChanged();
13319b7e95d646b768ee66ff54497aab3067628869cAndrew Sapperstein            } else if (Intent.ACTION_LOCALE_CHANGED.equals(broadcastAction) ||
13419b7e95d646b768ee66ff54497aab3067628869cAndrew Sapperstein                    UIProvider.ACTION_UPDATE_NOTIFICATION.equals((broadcastAction))) {
1358196f82ce207f0c7a1088996d0f3017a16c3637eTony Mantler                broadcastIntent.setClass(this, EmailIntentService.class);
1368196f82ce207f0c7a1088996d0f3017a16c3637eTony Mantler                startService(broadcastIntent);
137a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler            }
138a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler        } else if (ACTION_DEVICE_POLICY_ADMIN.equals(action)) {
139a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler            int message = intent.getIntExtra(EXTRA_DEVICE_POLICY_ADMIN, -1);
140a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler            SecurityPolicy.onDeviceAdminReceiverMessage(this, message);
141462da178b1be608a74000956c53dc8ecb438c689Yu Ping Hu        } else if (ACTION_UPGRADE_BROADCAST.equals(action)) {
142462da178b1be608a74000956c53dc8ecb438c689Yu Ping Hu            onAppUpgrade();
143898283b6fa4f141d5c2a0831b96b19de11d587b3Makoto Onuki        }
144898283b6fa4f141d5c2a0831b96b19de11d587b3Makoto Onuki    }
145898283b6fa4f141d5c2a0831b96b19de11d587b3Makoto Onuki
146462da178b1be608a74000956c53dc8ecb438c689Yu Ping Hu    private void disableComponent(final Class<?> klass) {
147462da178b1be608a74000956c53dc8ecb438c689Yu Ping Hu        getPackageManager().setComponentEnabledSetting(new ComponentName(this, klass),
148462da178b1be608a74000956c53dc8ecb438c689Yu Ping Hu                PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);
149462da178b1be608a74000956c53dc8ecb438c689Yu Ping Hu    }
150462da178b1be608a74000956c53dc8ecb438c689Yu Ping Hu
1517acf30e9ab077362dc03812583044fb3e23206c3Tony Mantler    private boolean isComponentDisabled(final Class<?> klass) {
1527acf30e9ab077362dc03812583044fb3e23206c3Tony Mantler        return getPackageManager().getComponentEnabledSetting(new ComponentName(this, klass))
1537acf30e9ab077362dc03812583044fb3e23206c3Tony Mantler                == PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
1547acf30e9ab077362dc03812583044fb3e23206c3Tony Mantler    }
1557acf30e9ab077362dc03812583044fb3e23206c3Tony Mantler
156462da178b1be608a74000956c53dc8ecb438c689Yu Ping Hu    private void updateAccountManagerAccountsOfType(final String amAccountType,
15748b14fde961ef57103f7a31196254e357987f203Yu Ping Hu            final Map<String, String> protocolMap) {
158462da178b1be608a74000956c53dc8ecb438c689Yu Ping Hu        final android.accounts.Account[] amAccounts =
159462da178b1be608a74000956c53dc8ecb438c689Yu Ping Hu                AccountManager.get(this).getAccountsByType(amAccountType);
160462da178b1be608a74000956c53dc8ecb438c689Yu Ping Hu
161462da178b1be608a74000956c53dc8ecb438c689Yu Ping Hu        for (android.accounts.Account amAccount: amAccounts) {
162462da178b1be608a74000956c53dc8ecb438c689Yu Ping Hu            EmailServiceUtils.updateAccountManagerType(this, amAccount, protocolMap);
163462da178b1be608a74000956c53dc8ecb438c689Yu Ping Hu        }
164462da178b1be608a74000956c53dc8ecb438c689Yu Ping Hu    }
165462da178b1be608a74000956c53dc8ecb438c689Yu Ping Hu
16648b14fde961ef57103f7a31196254e357987f203Yu Ping Hu    /**
16748b14fde961ef57103f7a31196254e357987f203Yu Ping Hu     * Delete all periodic syncs for an account.
16848b14fde961ef57103f7a31196254e357987f203Yu Ping Hu     * @param amAccount The account for which to disable syncs.
16948b14fde961ef57103f7a31196254e357987f203Yu Ping Hu     * @param authority The authority for which to disable syncs.
17048b14fde961ef57103f7a31196254e357987f203Yu Ping Hu     */
1719e521deb6bb525b33365cc2926cb2d0faa7095e2Scott Kennedy    private static void removePeriodicSyncs(final android.accounts.Account amAccount,
17248b14fde961ef57103f7a31196254e357987f203Yu Ping Hu            final String authority) {
17348b14fde961ef57103f7a31196254e357987f203Yu Ping Hu        final List<PeriodicSync> syncs =
17448b14fde961ef57103f7a31196254e357987f203Yu Ping Hu                ContentResolver.getPeriodicSyncs(amAccount, authority);
17548b14fde961ef57103f7a31196254e357987f203Yu Ping Hu        for (final PeriodicSync sync : syncs) {
17648b14fde961ef57103f7a31196254e357987f203Yu Ping Hu            ContentResolver.removePeriodicSync(amAccount, authority, sync.extras);
17748b14fde961ef57103f7a31196254e357987f203Yu Ping Hu        }
17848b14fde961ef57103f7a31196254e357987f203Yu Ping Hu    }
17948b14fde961ef57103f7a31196254e357987f203Yu Ping Hu
18048b14fde961ef57103f7a31196254e357987f203Yu Ping Hu    /**
18148b14fde961ef57103f7a31196254e357987f203Yu Ping Hu     * Remove all existing periodic syncs for an account type, and add the necessary syncs.
18248b14fde961ef57103f7a31196254e357987f203Yu Ping Hu     * @param amAccountType The account type to handle.
18348b14fde961ef57103f7a31196254e357987f203Yu Ping Hu     * @param syncIntervals The map of all account addresses to sync intervals in the DB.
18448b14fde961ef57103f7a31196254e357987f203Yu Ping Hu     */
18548b14fde961ef57103f7a31196254e357987f203Yu Ping Hu    private void fixPeriodicSyncs(final String amAccountType,
18648b14fde961ef57103f7a31196254e357987f203Yu Ping Hu            final Map<String, Integer> syncIntervals) {
18748b14fde961ef57103f7a31196254e357987f203Yu Ping Hu        final android.accounts.Account[] amAccounts =
18848b14fde961ef57103f7a31196254e357987f203Yu Ping Hu                AccountManager.get(this).getAccountsByType(amAccountType);
18948b14fde961ef57103f7a31196254e357987f203Yu Ping Hu        for (android.accounts.Account amAccount : amAccounts) {
19048b14fde961ef57103f7a31196254e357987f203Yu Ping Hu            // First delete existing periodic syncs.
19148b14fde961ef57103f7a31196254e357987f203Yu Ping Hu            removePeriodicSyncs(amAccount, EmailContent.AUTHORITY);
19248b14fde961ef57103f7a31196254e357987f203Yu Ping Hu            removePeriodicSyncs(amAccount, CalendarContract.AUTHORITY);
19348b14fde961ef57103f7a31196254e357987f203Yu Ping Hu            removePeriodicSyncs(amAccount, ContactsContract.AUTHORITY);
19448b14fde961ef57103f7a31196254e357987f203Yu Ping Hu
19548b14fde961ef57103f7a31196254e357987f203Yu Ping Hu            // Add back a sync for this account if necessary (i.e. the account has a positive
19648b14fde961ef57103f7a31196254e357987f203Yu Ping Hu            // sync interval in the DB). This assumes that the email app requires unique email
19748b14fde961ef57103f7a31196254e357987f203Yu Ping Hu            // addresses for each account, which is currently the case.
19848b14fde961ef57103f7a31196254e357987f203Yu Ping Hu            final Integer syncInterval = syncIntervals.get(amAccount.name);
19948b14fde961ef57103f7a31196254e357987f203Yu Ping Hu            if (syncInterval != null && syncInterval > 0) {
20048b14fde961ef57103f7a31196254e357987f203Yu Ping Hu                // Sync interval is stored in minutes in DB, but we want the value in seconds.
20148b14fde961ef57103f7a31196254e357987f203Yu Ping Hu                ContentResolver.addPeriodicSync(amAccount, EmailContent.AUTHORITY, Bundle.EMPTY,
20248b14fde961ef57103f7a31196254e357987f203Yu Ping Hu                        syncInterval * DateUtils.MINUTE_IN_MILLIS / DateUtils.SECOND_IN_MILLIS);
20348b14fde961ef57103f7a31196254e357987f203Yu Ping Hu            }
20448b14fde961ef57103f7a31196254e357987f203Yu Ping Hu        }
20548b14fde961ef57103f7a31196254e357987f203Yu Ping Hu    }
20648b14fde961ef57103f7a31196254e357987f203Yu Ping Hu
20748b14fde961ef57103f7a31196254e357987f203Yu Ping Hu    /** Projection used for getting sync intervals for all accounts. */
20848b14fde961ef57103f7a31196254e357987f203Yu Ping Hu    private static final String[] ACCOUNT_SYNC_INTERVAL_PROJECTION =
20948b14fde961ef57103f7a31196254e357987f203Yu Ping Hu            { AccountColumns.EMAIL_ADDRESS, AccountColumns.SYNC_INTERVAL };
21048b14fde961ef57103f7a31196254e357987f203Yu Ping Hu    private static final int ACCOUNT_SYNC_INTERVAL_ADDRESS_COLUMN = 0;
21148b14fde961ef57103f7a31196254e357987f203Yu Ping Hu    private static final int ACCOUNT_SYNC_INTERVAL_INTERVAL_COLUMN = 1;
21248b14fde961ef57103f7a31196254e357987f203Yu Ping Hu
21348b14fde961ef57103f7a31196254e357987f203Yu Ping Hu    /**
21448b14fde961ef57103f7a31196254e357987f203Yu Ping Hu     * Get the sync interval for all accounts, as stored in the DB.
21548b14fde961ef57103f7a31196254e357987f203Yu Ping Hu     * @return The map of all sync intervals by account email address.
21648b14fde961ef57103f7a31196254e357987f203Yu Ping Hu     */
21748b14fde961ef57103f7a31196254e357987f203Yu Ping Hu    private Map<String, Integer> getSyncIntervals() {
21848b14fde961ef57103f7a31196254e357987f203Yu Ping Hu        final Cursor c = getContentResolver().query(Account.CONTENT_URI,
21948b14fde961ef57103f7a31196254e357987f203Yu Ping Hu                ACCOUNT_SYNC_INTERVAL_PROJECTION, null, null, null);
22048b14fde961ef57103f7a31196254e357987f203Yu Ping Hu        if (c != null) {
22148b14fde961ef57103f7a31196254e357987f203Yu Ping Hu            final Map<String, Integer> periodicSyncs =
22248b14fde961ef57103f7a31196254e357987f203Yu Ping Hu                    Maps.newHashMapWithExpectedSize(c.getCount());
22348b14fde961ef57103f7a31196254e357987f203Yu Ping Hu            try {
22448b14fde961ef57103f7a31196254e357987f203Yu Ping Hu                while (c.moveToNext()) {
22548b14fde961ef57103f7a31196254e357987f203Yu Ping Hu                    periodicSyncs.put(c.getString(ACCOUNT_SYNC_INTERVAL_ADDRESS_COLUMN),
22648b14fde961ef57103f7a31196254e357987f203Yu Ping Hu                            c.getInt(ACCOUNT_SYNC_INTERVAL_INTERVAL_COLUMN));
22748b14fde961ef57103f7a31196254e357987f203Yu Ping Hu                }
22848b14fde961ef57103f7a31196254e357987f203Yu Ping Hu            } finally {
22948b14fde961ef57103f7a31196254e357987f203Yu Ping Hu                c.close();
23048b14fde961ef57103f7a31196254e357987f203Yu Ping Hu            }
23148b14fde961ef57103f7a31196254e357987f203Yu Ping Hu            return periodicSyncs;
23248b14fde961ef57103f7a31196254e357987f203Yu Ping Hu        }
23348b14fde961ef57103f7a31196254e357987f203Yu Ping Hu        return Collections.emptyMap();
23448b14fde961ef57103f7a31196254e357987f203Yu Ping Hu    }
23548b14fde961ef57103f7a31196254e357987f203Yu Ping Hu
236bc8bc050b080287c35241f5ab0c310bbbdb6e805Yu Ping Hu    @VisibleForTesting
237bc8bc050b080287c35241f5ab0c310bbbdb6e805Yu Ping Hu    protected static void removeNoopUpgrades(final Map<String, String> protocolMap) {
238bc8bc050b080287c35241f5ab0c310bbbdb6e805Yu Ping Hu        final Set<String> keySet = new HashSet<String>(protocolMap.keySet());
239bc8bc050b080287c35241f5ab0c310bbbdb6e805Yu Ping Hu        for (final String key : keySet) {
240bc8bc050b080287c35241f5ab0c310bbbdb6e805Yu Ping Hu            if (TextUtils.equals(key, protocolMap.get(key))) {
241bc8bc050b080287c35241f5ab0c310bbbdb6e805Yu Ping Hu                protocolMap.remove(key);
242bc8bc050b080287c35241f5ab0c310bbbdb6e805Yu Ping Hu            }
243bc8bc050b080287c35241f5ab0c310bbbdb6e805Yu Ping Hu        }
244bc8bc050b080287c35241f5ab0c310bbbdb6e805Yu Ping Hu    }
245bc8bc050b080287c35241f5ab0c310bbbdb6e805Yu Ping Hu
246462da178b1be608a74000956c53dc8ecb438c689Yu Ping Hu    private void onAppUpgrade() {
2477acf30e9ab077362dc03812583044fb3e23206c3Tony Mantler        if (isComponentDisabled(EmailUpgradeBroadcastReceiver.class)) {
2487acf30e9ab077362dc03812583044fb3e23206c3Tony Mantler            return;
2497acf30e9ab077362dc03812583044fb3e23206c3Tony Mantler        }
250bc8bc050b080287c35241f5ab0c310bbbdb6e805Yu Ping Hu        // When upgrading to a version that changes the protocol strings, we need to essentially
251bc8bc050b080287c35241f5ab0c310bbbdb6e805Yu Ping Hu        // rename the account manager type for all existing accounts, so we add new ones and delete
252bc8bc050b080287c35241f5ab0c310bbbdb6e805Yu Ping Hu        // the old.
253462da178b1be608a74000956c53dc8ecb438c689Yu Ping Hu        // We specify the translations in this map. We map from old protocol name to new protocol
254462da178b1be608a74000956c53dc8ecb438c689Yu Ping Hu        // name, and from protocol name + "_type" to new account manager type name. (Email1 did
255462da178b1be608a74000956c53dc8ecb438c689Yu Ping Hu        // not use distinct account manager types for POP and IMAP, but Email2 does, hence this
256462da178b1be608a74000956c53dc8ecb438c689Yu Ping Hu        // weird mapping.)
25748b14fde961ef57103f7a31196254e357987f203Yu Ping Hu        final Map<String, String> protocolMap = Maps.newHashMapWithExpectedSize(4);
258462da178b1be608a74000956c53dc8ecb438c689Yu Ping Hu        protocolMap.put("imap", getString(R.string.protocol_legacy_imap));
259462da178b1be608a74000956c53dc8ecb438c689Yu Ping Hu        protocolMap.put("pop3", getString(R.string.protocol_pop3));
260bc8bc050b080287c35241f5ab0c310bbbdb6e805Yu Ping Hu        removeNoopUpgrades(protocolMap);
261bc8bc050b080287c35241f5ab0c310bbbdb6e805Yu Ping Hu        if (!protocolMap.isEmpty()) {
262bc8bc050b080287c35241f5ab0c310bbbdb6e805Yu Ping Hu            protocolMap.put("imap_type", getString(R.string.account_manager_type_legacy_imap));
263bc8bc050b080287c35241f5ab0c310bbbdb6e805Yu Ping Hu            protocolMap.put("pop3_type", getString(R.string.account_manager_type_pop3));
264bc8bc050b080287c35241f5ab0c310bbbdb6e805Yu Ping Hu            updateAccountManagerAccountsOfType("com.android.email", protocolMap);
265bc8bc050b080287c35241f5ab0c310bbbdb6e805Yu Ping Hu        }
266462da178b1be608a74000956c53dc8ecb438c689Yu Ping Hu
267462da178b1be608a74000956c53dc8ecb438c689Yu Ping Hu        protocolMap.clear();
268462da178b1be608a74000956c53dc8ecb438c689Yu Ping Hu        protocolMap.put("eas", getString(R.string.protocol_eas));
269bc8bc050b080287c35241f5ab0c310bbbdb6e805Yu Ping Hu        removeNoopUpgrades(protocolMap);
270bc8bc050b080287c35241f5ab0c310bbbdb6e805Yu Ping Hu        if (!protocolMap.isEmpty()) {
271bc8bc050b080287c35241f5ab0c310bbbdb6e805Yu Ping Hu            protocolMap.put("eas_type", getString(R.string.account_manager_type_exchange));
272bc8bc050b080287c35241f5ab0c310bbbdb6e805Yu Ping Hu            updateAccountManagerAccountsOfType("com.android.exchange", protocolMap);
273bc8bc050b080287c35241f5ab0c310bbbdb6e805Yu Ping Hu        }
274462da178b1be608a74000956c53dc8ecb438c689Yu Ping Hu
275462da178b1be608a74000956c53dc8ecb438c689Yu Ping Hu        // Disable the old authenticators.
276462da178b1be608a74000956c53dc8ecb438c689Yu Ping Hu        disableComponent(LegacyEmailAuthenticatorService.class);
277462da178b1be608a74000956c53dc8ecb438c689Yu Ping Hu        disableComponent(LegacyEasAuthenticatorService.class);
278462da178b1be608a74000956c53dc8ecb438c689Yu Ping Hu
27948b14fde961ef57103f7a31196254e357987f203Yu Ping Hu        // Fix periodic syncs.
28048b14fde961ef57103f7a31196254e357987f203Yu Ping Hu        final Map<String, Integer> syncIntervals = getSyncIntervals();
281b5eed31d1bdfcb8e8b316c9c49331295718001bdYu Ping Hu        for (final EmailServiceUtils.EmailServiceInfo service
282b5eed31d1bdfcb8e8b316c9c49331295718001bdYu Ping Hu                : EmailServiceUtils.getServiceInfoList(this)) {
28348b14fde961ef57103f7a31196254e357987f203Yu Ping Hu            fixPeriodicSyncs(service.accountType, syncIntervals);
28448b14fde961ef57103f7a31196254e357987f203Yu Ping Hu        }
28548b14fde961ef57103f7a31196254e357987f203Yu Ping Hu
286462da178b1be608a74000956c53dc8ecb438c689Yu Ping Hu        // Disable the upgrade broadcast receiver now that we're fully upgraded.
287462da178b1be608a74000956c53dc8ecb438c689Yu Ping Hu        disableComponent(EmailUpgradeBroadcastReceiver.class);
288462da178b1be608a74000956c53dc8ecb438c689Yu Ping Hu    }
289462da178b1be608a74000956c53dc8ecb438c689Yu Ping Hu
290898283b6fa4f141d5c2a0831b96b19de11d587b3Makoto Onuki    /**
291898283b6fa4f141d5c2a0831b96b19de11d587b3Makoto Onuki     * Handles {@link Intent#ACTION_BOOT_COMPLETED}.  Called on a worker thread.
292898283b6fa4f141d5c2a0831b96b19de11d587b3Makoto Onuki     */
293898283b6fa4f141d5c2a0831b96b19de11d587b3Makoto Onuki    private void onBootCompleted() {
294898283b6fa4f141d5c2a0831b96b19de11d587b3Makoto Onuki        performOneTimeInitialization();
295f53490dc86f5a61ab09024dc4938ce8419c61fc5Marc Blank        reconcileAndStartServices();
296f53490dc86f5a61ab09024dc4938ce8419c61fc5Marc Blank    }
297898283b6fa4f141d5c2a0831b96b19de11d587b3Makoto Onuki
298f53490dc86f5a61ab09024dc4938ce8419c61fc5Marc Blank    private void reconcileAndStartServices() {
2997acf30e9ab077362dc03812583044fb3e23206c3Tony Mantler        /**
3007acf30e9ab077362dc03812583044fb3e23206c3Tony Mantler         *  We can get here before the ACTION_UPGRADE_BROADCAST is received, so make sure the
3017acf30e9ab077362dc03812583044fb3e23206c3Tony Mantler         *  accounts are converted otherwise terrible, horrible things will happen.
3027acf30e9ab077362dc03812583044fb3e23206c3Tony Mantler         */
3037acf30e9ab077362dc03812583044fb3e23206c3Tony Mantler        onAppUpgrade();
304f53490dc86f5a61ab09024dc4938ce8419c61fc5Marc Blank        // Reconcile accounts
305afe097f318a7eeac9d240d2bedbc5caba7640ea8Yu Ping Hu        AccountReconciler.reconcileAccounts(this);
306f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        // Starts remote services, if any
307f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        EmailServiceUtils.startRemoteServices(this);
308898283b6fa4f141d5c2a0831b96b19de11d587b3Makoto Onuki    }
309898283b6fa4f141d5c2a0831b96b19de11d587b3Makoto Onuki
310898283b6fa4f141d5c2a0831b96b19de11d587b3Makoto Onuki    private void performOneTimeInitialization() {
311898283b6fa4f141d5c2a0831b96b19de11d587b3Makoto Onuki        final Preferences pref = Preferences.getPreferences(this);
312898283b6fa4f141d5c2a0831b96b19de11d587b3Makoto Onuki        int progress = pref.getOneTimeInitializationProgress();
313898283b6fa4f141d5c2a0831b96b19de11d587b3Makoto Onuki        final int initialProgress = progress;
314898283b6fa4f141d5c2a0831b96b19de11d587b3Makoto Onuki
315898283b6fa4f141d5c2a0831b96b19de11d587b3Makoto Onuki        if (progress < 1) {
316560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedy            LogUtils.i(Logging.LOG_TAG, "Onetime initialization: 1");
317898283b6fa4f141d5c2a0831b96b19de11d587b3Makoto Onuki            progress = 1;
318ebe49cfeaf0328aa95f5cb82b731f08d7bae7b09Anthony Lee            EmailServiceUtils.enableExchangeComponent(this);
319898283b6fa4f141d5c2a0831b96b19de11d587b3Makoto Onuki        }
320898283b6fa4f141d5c2a0831b96b19de11d587b3Makoto Onuki
3218c89674b64f6669fa94bf593abad0f1e2255a812Todd Kennedy        if (progress < 2) {
322560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedy            LogUtils.i(Logging.LOG_TAG, "Onetime initialization: 2");
3238c89674b64f6669fa94bf593abad0f1e2255a812Todd Kennedy            progress = 2;
3248c89674b64f6669fa94bf593abad0f1e2255a812Todd Kennedy            setImapDeletePolicy(this);
3258c89674b64f6669fa94bf593abad0f1e2255a812Todd Kennedy        }
3268c89674b64f6669fa94bf593abad0f1e2255a812Todd Kennedy
327898283b6fa4f141d5c2a0831b96b19de11d587b3Makoto Onuki        // Add your initialization steps here.
328898283b6fa4f141d5c2a0831b96b19de11d587b3Makoto Onuki        // Use "progress" to skip the initializations that's already done before.
329898283b6fa4f141d5c2a0831b96b19de11d587b3Makoto Onuki        // Using this preference also makes it safe when a user skips an upgrade.  (i.e. upgrading
330898283b6fa4f141d5c2a0831b96b19de11d587b3Makoto Onuki        // version N to version N+2)
331898283b6fa4f141d5c2a0831b96b19de11d587b3Makoto Onuki
332898283b6fa4f141d5c2a0831b96b19de11d587b3Makoto Onuki        if (progress != initialProgress) {
333898283b6fa4f141d5c2a0831b96b19de11d587b3Makoto Onuki            pref.setOneTimeInitializationProgress(progress);
334560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedy            LogUtils.i(Logging.LOG_TAG, "Onetime initialization: completed.");
335898283b6fa4f141d5c2a0831b96b19de11d587b3Makoto Onuki        }
336898283b6fa4f141d5c2a0831b96b19de11d587b3Makoto Onuki    }
337898283b6fa4f141d5c2a0831b96b19de11d587b3Makoto Onuki
3388c89674b64f6669fa94bf593abad0f1e2255a812Todd Kennedy    /**
3398c89674b64f6669fa94bf593abad0f1e2255a812Todd Kennedy     * Sets the delete policy to the correct value for all IMAP accounts. This will have no
3408c89674b64f6669fa94bf593abad0f1e2255a812Todd Kennedy     * effect on either EAS or POP3 accounts.
3418c89674b64f6669fa94bf593abad0f1e2255a812Todd Kennedy     */
3428c89674b64f6669fa94bf593abad0f1e2255a812Todd Kennedy    /*package*/ static void setImapDeletePolicy(Context context) {
3438c89674b64f6669fa94bf593abad0f1e2255a812Todd Kennedy        ContentResolver resolver = context.getContentResolver();
3448c89674b64f6669fa94bf593abad0f1e2255a812Todd Kennedy        Cursor c = resolver.query(Account.CONTENT_URI, Account.CONTENT_PROJECTION,
3458c89674b64f6669fa94bf593abad0f1e2255a812Todd Kennedy                null, null, null);
3468c89674b64f6669fa94bf593abad0f1e2255a812Todd Kennedy        try {
3478c89674b64f6669fa94bf593abad0f1e2255a812Todd Kennedy            while (c.moveToNext()) {
3488c89674b64f6669fa94bf593abad0f1e2255a812Todd Kennedy                long recvAuthKey = c.getLong(Account.CONTENT_HOST_AUTH_KEY_RECV_COLUMN);
3498c89674b64f6669fa94bf593abad0f1e2255a812Todd Kennedy                HostAuth recvAuth = HostAuth.restoreHostAuthWithId(context, recvAuthKey);
3505c523858385176c33a7456bb84035de78552d22dMarc Blank                String legacyImapProtocol = context.getString(R.string.protocol_legacy_imap);
3515c523858385176c33a7456bb84035de78552d22dMarc Blank                if (legacyImapProtocol.equals(recvAuth.mProtocol)) {
3528c89674b64f6669fa94bf593abad0f1e2255a812Todd Kennedy                    int flags = c.getInt(Account.CONTENT_FLAGS_COLUMN);
3538c89674b64f6669fa94bf593abad0f1e2255a812Todd Kennedy                    flags &= ~Account.FLAGS_DELETE_POLICY_MASK;
3548c89674b64f6669fa94bf593abad0f1e2255a812Todd Kennedy                    flags |= Account.DELETE_POLICY_ON_DELETE << Account.FLAGS_DELETE_POLICY_SHIFT;
3558c89674b64f6669fa94bf593abad0f1e2255a812Todd Kennedy                    ContentValues cv = new ContentValues();
3568c89674b64f6669fa94bf593abad0f1e2255a812Todd Kennedy                    cv.put(AccountColumns.FLAGS, flags);
3578c89674b64f6669fa94bf593abad0f1e2255a812Todd Kennedy                    long accountId = c.getLong(Account.CONTENT_ID_COLUMN);
3588c89674b64f6669fa94bf593abad0f1e2255a812Todd Kennedy                    Uri uri = ContentUris.withAppendedId(Account.CONTENT_URI, accountId);
3598c89674b64f6669fa94bf593abad0f1e2255a812Todd Kennedy                    resolver.update(uri, cv, null, null);
3608c89674b64f6669fa94bf593abad0f1e2255a812Todd Kennedy                }
3618c89674b64f6669fa94bf593abad0f1e2255a812Todd Kennedy            }
3628c89674b64f6669fa94bf593abad0f1e2255a812Todd Kennedy        } finally {
3638c89674b64f6669fa94bf593abad0f1e2255a812Todd Kennedy            c.close();
3648c89674b64f6669fa94bf593abad0f1e2255a812Todd Kennedy        }
3658c89674b64f6669fa94bf593abad0f1e2255a812Todd Kennedy    }
3668c89674b64f6669fa94bf593abad0f1e2255a812Todd Kennedy
367c50b6f685ba26a38b8110f6ca0115b12f84d643cMakoto Onuki    private void onSystemAccountChanged() {
368560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedy        LogUtils.i(Logging.LOG_TAG, "System accounts updated.");
369f53490dc86f5a61ab09024dc4938ce8419c61fc5Marc Blank        reconcileAndStartServices();
370ac03b5576516f9e14a089b79b00dd7bf2dd48cfaRégis Décamps        // Resend all notifications, so that there is no notification that points to a removed
371ac03b5576516f9e14a089b79b00dd7bf2dd48cfaRégis Décamps        // account.
372ac03b5576516f9e14a089b79b00dd7bf2dd48cfaRégis Décamps        NotificationActionUtils.resendNotifications(getApplicationContext(),
373ac03b5576516f9e14a089b79b00dd7bf2dd48cfaRégis Décamps                null /* all accounts */, null /* all folders */);
374c50b6f685ba26a38b8110f6ca0115b12f84d643cMakoto Onuki    }
375898283b6fa4f141d5c2a0831b96b19de11d587b3Makoto Onuki}
376