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