• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package com.android.email.service;
18 
19 import android.accounts.AccountManager;
20 import android.app.IntentService;
21 import android.content.ComponentName;
22 import android.content.ContentResolver;
23 import android.content.ContentUris;
24 import android.content.ContentValues;
25 import android.content.Context;
26 import android.content.Intent;
27 import android.content.pm.PackageManager;
28 import android.database.Cursor;
29 import android.net.Uri;
30 import android.util.Log;
31 
32 import com.android.email.Email;
33 import com.android.email.Preferences;
34 import com.android.email.SecurityPolicy;
35 import com.android.email.VendorPolicyLoader;
36 import com.android.email.activity.setup.AccountSettings;
37 import com.android.emailcommon.Logging;
38 import com.android.emailcommon.provider.Account;
39 import com.android.emailcommon.provider.EmailContent.AccountColumns;
40 import 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  */
55 public 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 
EmailBroadcastProcessorService()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      */
processBroadcastIntent(Context context, Intent broadcastIntent)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      */
processDevicePolicyMessage(Context context, int message)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
onHandleIntent(Intent intent)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 
enableComponentsIfNecessary()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      */
onBootCompleted()140     private void onBootCompleted() {
141         performOneTimeInitialization();
142 
143         enableComponentsIfNecessary();
144 
145         // Starts the service for Exchange, if supported.
146         EmailServiceUtils.startExchangeService(this);
147     }
148 
performOneTimeInitialization()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      */
setImapDeletePolicy(Context context)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 
setComponentEnabled(Class<?> clazz, boolean enabled)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 
onSystemAccountChanged()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