EmailBroadcastProcessorService.java revision f5418f1f93b02e7fab9f15eb201800b65510998e
1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.email.service;
18
19import com.android.email.Email;
20import com.android.email.ExchangeUtils;
21import com.android.email.Preferences;
22import com.android.email.SecurityPolicy;
23import com.android.email.VendorPolicyLoader;
24import com.android.email.activity.setup.AccountSettings;
25import com.android.email.mail.Store;
26import com.android.email.widget.WidgetManager;
27import com.android.emailcommon.Logging;
28import com.android.emailcommon.provider.Account;
29import com.android.emailcommon.provider.EmailContent.AccountColumns;
30import com.android.emailcommon.provider.HostAuth;
31
32import android.accounts.AccountManager;
33import android.app.IntentService;
34import android.content.ComponentName;
35import android.content.ContentResolver;
36import android.content.ContentUris;
37import android.content.ContentValues;
38import android.content.Context;
39import android.content.Intent;
40import android.content.pm.PackageManager;
41import android.database.Cursor;
42import android.net.Uri;
43import android.util.Log;
44
45/**
46 * The service that really handles broadcast intents on a worker thread.
47 *
48 * We make it a service, because:
49 * <ul>
50 *   <li>So that it's less likely for the process to get killed.
51 *   <li>Even if it does, the Intent that have started it will be re-delivered by the system,
52 *   and we can start the process again.  (Using {@link #setIntentRedelivery}).
53 * </ul>
54 *
55 * This also handles the DeviceAdminReceiver in SecurityPolicy, because it is also
56 * a BroadcastReceiver and requires the same processing semantics.
57 */
58public class EmailBroadcastProcessorService extends IntentService {
59    // Action used for BroadcastReceiver entry point
60    private static final String ACTION_BROADCAST = "broadcast_receiver";
61
62    // Dialing "*#*#36245#*#*" to open the debug screen.   "36245" = "email"
63    private static final String ACTION_SECRET_CODE = "android.provider.Telephony.SECRET_CODE";
64    private static final String SECRET_CODE_HOST_DEBUG_SCREEN = "36245";
65
66    // This is a helper used to process DeviceAdminReceiver messages
67    private static final String ACTION_DEVICE_POLICY_ADMIN = "com.android.email.devicepolicy";
68    private static final String EXTRA_DEVICE_POLICY_ADMIN = "message_code";
69
70    public EmailBroadcastProcessorService() {
71        // Class name will be the thread name.
72        super(EmailBroadcastProcessorService.class.getName());
73
74        // Intent should be redelivered if the process gets killed before completing the job.
75        setIntentRedelivery(true);
76    }
77
78    /**
79     * Entry point for {@link EmailBroadcastReceiver}.
80     */
81    public static void processBroadcastIntent(Context context, Intent broadcastIntent) {
82        Intent i = new Intent(context, EmailBroadcastProcessorService.class);
83        i.setAction(ACTION_BROADCAST);
84        i.putExtra(Intent.EXTRA_INTENT, broadcastIntent);
85        context.startService(i);
86    }
87
88    /**
89     * Entry point for {@link com.android.email.SecurityPolicy.PolicyAdmin}.  These will
90     * simply callback to {@link
91     * com.android.email.SecurityPolicy#onDeviceAdminReceiverMessage(Context, int)}.
92     */
93    public static void processDevicePolicyMessage(Context context, int message) {
94        Intent i = new Intent(context, EmailBroadcastProcessorService.class);
95        i.setAction(ACTION_DEVICE_POLICY_ADMIN);
96        i.putExtra(EXTRA_DEVICE_POLICY_ADMIN, message);
97        context.startService(i);
98    }
99
100    @Override
101    protected void onHandleIntent(Intent intent) {
102        // This method is called on a worker thread.
103
104        // Dispatch from entry point
105        final String action = intent.getAction();
106        if (ACTION_BROADCAST.equals(action)) {
107            final Intent broadcastIntent = intent.getParcelableExtra(Intent.EXTRA_INTENT);
108            final String broadcastAction = broadcastIntent.getAction();
109
110            if (Intent.ACTION_BOOT_COMPLETED.equals(broadcastAction)) {
111                onBootCompleted();
112
113            // TODO: Do a better job when we get ACTION_DEVICE_STORAGE_LOW.
114            //       The code below came from very old code....
115            } else if (Intent.ACTION_DEVICE_STORAGE_LOW.equals(broadcastAction)) {
116                // Stop IMAP/POP3 poll.
117                MailService.actionCancel(this);
118            } else if (Intent.ACTION_DEVICE_STORAGE_OK.equals(broadcastAction)) {
119                enableComponentsIfNecessary();
120            } else if (ACTION_SECRET_CODE.equals(broadcastAction)
121                    && SECRET_CODE_HOST_DEBUG_SCREEN.equals(broadcastIntent.getData().getHost())) {
122                AccountSettings.actionSettingsWithDebug(this);
123            } else if (AccountManager.LOGIN_ACCOUNTS_CHANGED_ACTION.equals(broadcastAction)) {
124                onSystemAccountChanged();
125            }
126        } else if (ACTION_DEVICE_POLICY_ADMIN.equals(action)) {
127            int message = intent.getIntExtra(EXTRA_DEVICE_POLICY_ADMIN, -1);
128            SecurityPolicy.onDeviceAdminReceiverMessage(this, message);
129        }
130    }
131
132    private void enableComponentsIfNecessary() {
133        if (Email.setServicesEnabledSync(this)) {
134            // At least one account exists.
135            // TODO probably we should check if it's a POP/IMAP account.
136            MailService.actionReschedule(this);
137        }
138    }
139
140    /**
141     * Handles {@link Intent#ACTION_BOOT_COMPLETED}.  Called on a worker thread.
142     */
143    private void onBootCompleted() {
144        performOneTimeInitialization();
145
146        enableComponentsIfNecessary();
147
148        // Starts the service for Exchange, if supported.
149        ExchangeUtils.startExchangeService(this);
150    }
151
152    private void performOneTimeInitialization() {
153        final Preferences pref = Preferences.getPreferences(this);
154        int progress = pref.getOneTimeInitializationProgress();
155        final int initialProgress = progress;
156
157        if (progress < 1) {
158            Log.i(Logging.LOG_TAG, "Onetime initialization: 1");
159            progress = 1;
160            if (VendorPolicyLoader.getInstance(this).useAlternateExchangeStrings()) {
161                setComponentEnabled(EasAuthenticatorServiceAlternate.class, true);
162                setComponentEnabled(EasAuthenticatorService.class, false);
163            }
164
165            ExchangeUtils.enableEasCalendarSync(this);
166        }
167
168        if (progress < 2) {
169            Log.i(Logging.LOG_TAG, "Onetime initialization: 2");
170            progress = 2;
171            setImapDeletePolicy(this);
172        }
173
174        // Add your initialization steps here.
175        // Use "progress" to skip the initializations that's already done before.
176        // Using this preference also makes it safe when a user skips an upgrade.  (i.e. upgrading
177        // version N to version N+2)
178
179        if (progress != initialProgress) {
180            pref.setOneTimeInitializationProgress(progress);
181            Log.i(Logging.LOG_TAG, "Onetime initialization: completed.");
182        }
183    }
184
185    /**
186     * Sets the delete policy to the correct value for all IMAP accounts. This will have no
187     * effect on either EAS or POP3 accounts.
188     */
189    /*package*/ static void setImapDeletePolicy(Context context) {
190        ContentResolver resolver = context.getContentResolver();
191        Cursor c = resolver.query(Account.CONTENT_URI, Account.CONTENT_PROJECTION,
192                null, null, null);
193        try {
194            while (c.moveToNext()) {
195                long recvAuthKey = c.getLong(Account.CONTENT_HOST_AUTH_KEY_RECV_COLUMN);
196                HostAuth recvAuth = HostAuth.restoreHostAuthWithId(context, recvAuthKey);
197                if (Store.STORE_SCHEME_IMAP.equals(recvAuth.mProtocol)) {
198                    int flags = c.getInt(Account.CONTENT_FLAGS_COLUMN);
199                    flags &= ~Account.FLAGS_DELETE_POLICY_MASK;
200                    flags |= Account.DELETE_POLICY_ON_DELETE << Account.FLAGS_DELETE_POLICY_SHIFT;
201                    ContentValues cv = new ContentValues();
202                    cv.put(AccountColumns.FLAGS, flags);
203                    long accountId = c.getLong(Account.CONTENT_ID_COLUMN);
204                    Uri uri = ContentUris.withAppendedId(Account.CONTENT_URI, accountId);
205                    resolver.update(uri, cv, null, null);
206                }
207            }
208        } finally {
209            c.close();
210        }
211    }
212
213    private void setComponentEnabled(Class<?> clazz, boolean enabled) {
214        final ComponentName c = new ComponentName(this, clazz.getName());
215        getPackageManager().setComponentEnabledSetting(c,
216                enabled ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
217                        : PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
218                PackageManager.DONT_KILL_APP);
219    }
220
221    private void onSystemAccountChanged() {
222        Log.i(Logging.LOG_TAG, "System accounts updated.");
223        MailService.reconcilePopImapAccountsSync(this);
224
225        // If the exchange service wasn't already running, starting it will cause exchange account
226        // reconciliation to be performed.  The service stops itself it there are no EAS accounts.
227        ExchangeUtils.startExchangeService(this);
228    }
229}
230