Home | History | Annotate | Download | only in service
      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 
     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                 // Force policies to be set in DPM
    110                 SecurityPolicy.getInstance(this);
    111             // TODO: Do a better job when we get ACTION_DEVICE_STORAGE_LOW.
    112             //       The code below came from very old code....
    113             } else if (Intent.ACTION_DEVICE_STORAGE_LOW.equals(broadcastAction)) {
    114                 // Stop IMAP/POP3 poll.
    115                 MailService.actionCancel(this);
    116             } else if (Intent.ACTION_DEVICE_STORAGE_OK.equals(broadcastAction)) {
    117                 enableComponentsIfNecessary();
    118             } else if (ACTION_SECRET_CODE.equals(broadcastAction)
    119                     && SECRET_CODE_HOST_DEBUG_SCREEN.equals(broadcastIntent.getData().getHost())) {
    120                 AccountSettings.actionSettingsWithDebug(this);
    121             } else if (AccountManager.LOGIN_ACCOUNTS_CHANGED_ACTION.equals(broadcastAction)) {
    122                 onSystemAccountChanged();
    123             }
    124         } else if (ACTION_DEVICE_POLICY_ADMIN.equals(action)) {
    125             int message = intent.getIntExtra(EXTRA_DEVICE_POLICY_ADMIN, -1);
    126             SecurityPolicy.onDeviceAdminReceiverMessage(this, message);
    127         }
    128     }
    129 
    130     private void enableComponentsIfNecessary() {
    131         if (Email.setServicesEnabledSync(this)) {
    132             // At least one account exists.
    133             // TODO probably we should check if it's a POP/IMAP account.
    134             MailService.actionReschedule(this);
    135         }
    136     }
    137 
    138     /**
    139      * Handles {@link Intent#ACTION_BOOT_COMPLETED}.  Called on a worker thread.
    140      */
    141     private void onBootCompleted() {
    142         performOneTimeInitialization();
    143 
    144         enableComponentsIfNecessary();
    145 
    146         // Starts the service for Exchange, if supported.
    147         EmailServiceUtils.startExchangeService(this);
    148     }
    149 
    150     private void performOneTimeInitialization() {
    151         final Preferences pref = Preferences.getPreferences(this);
    152         int progress = pref.getOneTimeInitializationProgress();
    153         final int initialProgress = progress;
    154 
    155         if (progress < 1) {
    156             Log.i(Logging.LOG_TAG, "Onetime initialization: 1");
    157             progress = 1;
    158             if (VendorPolicyLoader.getInstance(this).useAlternateExchangeStrings()) {
    159                 setComponentEnabled(EasAuthenticatorServiceAlternate.class, true);
    160                 setComponentEnabled(EasAuthenticatorService.class, false);
    161             }
    162         }
    163 
    164         if (progress < 2) {
    165             Log.i(Logging.LOG_TAG, "Onetime initialization: 2");
    166             progress = 2;
    167             setImapDeletePolicy(this);
    168         }
    169 
    170         // Add your initialization steps here.
    171         // Use "progress" to skip the initializations that's already done before.
    172         // Using this preference also makes it safe when a user skips an upgrade.  (i.e. upgrading
    173         // version N to version N+2)
    174 
    175         if (progress != initialProgress) {
    176             pref.setOneTimeInitializationProgress(progress);
    177             Log.i(Logging.LOG_TAG, "Onetime initialization: completed.");
    178         }
    179     }
    180 
    181     /**
    182      * Sets the delete policy to the correct value for all IMAP accounts. This will have no
    183      * effect on either EAS or POP3 accounts.
    184      */
    185     /*package*/ static void setImapDeletePolicy(Context context) {
    186         ContentResolver resolver = context.getContentResolver();
    187         Cursor c = resolver.query(Account.CONTENT_URI, Account.CONTENT_PROJECTION,
    188                 null, null, null);
    189         try {
    190             while (c.moveToNext()) {
    191                 long recvAuthKey = c.getLong(Account.CONTENT_HOST_AUTH_KEY_RECV_COLUMN);
    192                 HostAuth recvAuth = HostAuth.restoreHostAuthWithId(context, recvAuthKey);
    193                 if (HostAuth.SCHEME_IMAP.equals(recvAuth.mProtocol)) {
    194                     int flags = c.getInt(Account.CONTENT_FLAGS_COLUMN);
    195                     flags &= ~Account.FLAGS_DELETE_POLICY_MASK;
    196                     flags |= Account.DELETE_POLICY_ON_DELETE << Account.FLAGS_DELETE_POLICY_SHIFT;
    197                     ContentValues cv = new ContentValues();
    198                     cv.put(AccountColumns.FLAGS, flags);
    199                     long accountId = c.getLong(Account.CONTENT_ID_COLUMN);
    200                     Uri uri = ContentUris.withAppendedId(Account.CONTENT_URI, accountId);
    201                     resolver.update(uri, cv, null, null);
    202                 }
    203             }
    204         } finally {
    205             c.close();
    206         }
    207     }
    208 
    209     private void setComponentEnabled(Class<?> clazz, boolean enabled) {
    210         final ComponentName c = new ComponentName(this, clazz.getName());
    211         getPackageManager().setComponentEnabledSetting(c,
    212                 enabled ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
    213                         : PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
    214                 PackageManager.DONT_KILL_APP);
    215     }
    216 
    217     private void onSystemAccountChanged() {
    218         Log.i(Logging.LOG_TAG, "System accounts updated.");
    219         MailService.reconcilePopImapAccountsSync(this);
    220 
    221         // If the exchange service wasn't already running, starting it will cause exchange account
    222         // reconciliation to be performed.  The service stops itself it there are no EAS accounts.
    223         EmailServiceUtils.startExchangeService(this);
    224     }
    225 }
    226