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