Home | History | Annotate | Download | only in service
      1 /*
      2  * Copyright (C) 2009 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.emailcommon.service;
     18 
     19 import android.content.Context;
     20 import android.content.Intent;
     21 import android.os.Bundle;
     22 import android.os.IBinder;
     23 import android.os.RemoteException;
     24 
     25 import com.android.emailcommon.Api;
     26 import com.android.emailcommon.Device;
     27 import com.android.emailcommon.TempDirectory;
     28 import com.android.emailcommon.mail.MessagingException;
     29 import com.android.emailcommon.provider.Account;
     30 import com.android.emailcommon.provider.HostAuth;
     31 import com.android.emailcommon.provider.Policy;
     32 import com.android.mail.utils.LogUtils;
     33 
     34 import java.io.IOException;
     35 
     36 /**
     37  * The EmailServiceProxy class provides a simple interface for the UI to call into the various
     38  * EmailService classes (e.g. ExchangeService for EAS).  It wraps the service connect/disconnect
     39  * process so that the caller need not be concerned with it.
     40  *
     41  * Use the class like this:
     42  *   new EmailServiceProxy(context, class).loadAttachment(attachmentId, callback)
     43  *
     44  * Methods without a return value return immediately (i.e. are asynchronous); methods with a
     45  * return value wait for a result from the Service (i.e. they should not be called from the UI
     46  * thread) with a default timeout of 30 seconds (settable)
     47  *
     48  * An EmailServiceProxy object cannot be reused (trying to do so generates a RemoteException)
     49  */
     50 
     51 public class EmailServiceProxy extends ServiceProxy implements IEmailService {
     52     private static final String TAG = "EmailServiceProxy";
     53 
     54     public static final String AUTO_DISCOVER_BUNDLE_ERROR_CODE = "autodiscover_error_code";
     55     public static final String AUTO_DISCOVER_BUNDLE_HOST_AUTH = "autodiscover_host_auth";
     56 
     57     public static final String VALIDATE_BUNDLE_RESULT_CODE = "validate_result_code";
     58     public static final String VALIDATE_BUNDLE_POLICY_SET = "validate_policy_set";
     59     public static final String VALIDATE_BUNDLE_ERROR_MESSAGE = "validate_error_message";
     60     public static final String VALIDATE_BUNDLE_UNSUPPORTED_POLICIES =
     61         "validate_unsupported_policies";
     62     public static final String VALIDATE_BUNDLE_PROTOCOL_VERSION = "validate_protocol_version";
     63     public static final String VALIDATE_BUNDLE_REDIRECT_ADDRESS = "validate_redirect_address";
     64 
     65     private Object mReturn = null;
     66     private IEmailService mService;
     67     private final boolean isRemote;
     68 
     69     // Standard debugging
     70     public static final int DEBUG_BIT = 1;
     71     // Verbose (parser) logging
     72     public static final int DEBUG_VERBOSE_BIT = 2;
     73     // File (SD card) logging
     74     public static final int DEBUG_FILE_BIT = 4;
     75     // Enable strict mode
     76     public static final int DEBUG_ENABLE_STRICT_MODE = 8;
     77 
     78     // The first two constructors are used with local services that can be referenced by class
     79     public EmailServiceProxy(Context _context, Class<?> _class) {
     80         super(_context, new Intent(_context, _class));
     81         TempDirectory.setTempDirectory(_context);
     82         isRemote = false;
     83     }
     84 
     85     // The following two constructors are used with remote services that must be referenced by
     86     // a known action or by a prebuilt intent
     87     public EmailServiceProxy(Context _context, Intent _intent) {
     88         super(_context, _intent);
     89         try {
     90             Device.getDeviceId(_context);
     91         } catch (IOException e) {
     92         }
     93         TempDirectory.setTempDirectory(_context);
     94         isRemote = true;
     95     }
     96 
     97     @Override
     98     public void onConnected(IBinder binder) {
     99         mService = IEmailService.Stub.asInterface(binder);
    100     }
    101 
    102     public boolean isRemote() {
    103         return isRemote;
    104     }
    105 
    106     @Override
    107     public int getApiLevel() {
    108         return Api.LEVEL;
    109     }
    110 
    111     /**
    112      * Request an attachment to be loaded; the service MUST give higher priority to
    113      * non-background loading.  The service MUST use the loadAttachmentStatus callback when
    114      * loading has started and stopped and SHOULD send callbacks with progress information if
    115      * possible.
    116      *
    117      * @param cb The {@link IEmailServiceCallback} to use for this operation.
    118      * @param attachmentId the id of the attachment record
    119      * @param background whether or not this request corresponds to a background action (i.e.
    120      * prefetch) vs a foreground action (user request)
    121      */
    122     @Override
    123     public void loadAttachment(final IEmailServiceCallback cb, final long attachmentId,
    124             final boolean background)
    125             throws RemoteException {
    126         setTask(new ProxyTask() {
    127             @Override
    128             public void run() throws RemoteException {
    129                 try {
    130                     mService.loadAttachment(cb, attachmentId, background);
    131                 } catch (RemoteException e) {
    132                     try {
    133                         // Try to send a callback (if set)
    134                         if (cb != null) {
    135                             cb.loadAttachmentStatus(-1, attachmentId,
    136                                     EmailServiceStatus.REMOTE_EXCEPTION, 0);
    137                         }
    138                     } catch (RemoteException e1) {
    139                     }
    140                 }
    141             }
    142         }, "loadAttachment");
    143     }
    144 
    145     /**
    146      * Request the sync of a mailbox; the service MUST send the syncMailboxStatus callback
    147      * indicating "starting" and "finished" (or error), regardless of whether the mailbox is
    148      * actually syncable.
    149      * TODO: Remove this from IEmailService in favor of ContentResolver.requestSync.
    150      *
    151      * @param mailboxId the id of the mailbox record
    152      * @param userRequest whether or not the user specifically asked for the sync
    153      * @param deltaMessageCount amount by which to change the number of messages synced.
    154      */
    155     @Deprecated
    156     @Override
    157     public void startSync(final long mailboxId, final boolean userRequest,
    158             final int deltaMessageCount) throws RemoteException {
    159         setTask(new ProxyTask() {
    160             @Override
    161             public void run() throws RemoteException {
    162                 mService.startSync(mailboxId, userRequest, deltaMessageCount);
    163             }
    164         }, "startSync");
    165     }
    166 
    167     /**
    168      * Request the immediate termination of a mailbox sync. Although the service is not required to
    169      * acknowledge this request, it MUST send a "finished" (or error) syncMailboxStatus callback if
    170      * the sync was started via the startSync service call.
    171      *
    172      * @param mailboxId the id of the mailbox record
    173      */
    174     @Override
    175     public void stopSync(final long mailboxId) throws RemoteException {
    176         setTask(new ProxyTask() {
    177             @Override
    178             public void run() throws RemoteException {
    179                 mService.stopSync(mailboxId);
    180             }
    181         }, "stopSync");
    182     }
    183 
    184     /**
    185      * Validate a user account, given a protocol, host address, port, ssl status, and credentials.
    186      * The result of this call is returned in a Bundle which MUST include a result code and MAY
    187      * include a PolicySet that is required by the account. A successful validation implies a host
    188      * address that serves the specified protocol and credentials sufficient to be authorized
    189      * by the server to do so.
    190      *
    191      * @param hostAuth the hostauth object to validate
    192      * @return a Bundle as described above
    193      */
    194     @Override
    195     public Bundle validate(final HostAuth hostAuth) throws RemoteException {
    196         setTask(new ProxyTask() {
    197             @Override
    198             public void run() throws RemoteException{
    199                 mReturn = mService.validate(hostAuth);
    200             }
    201         }, "validate");
    202         waitForCompletion();
    203         if (mReturn == null) {
    204             Bundle bundle = new Bundle();
    205             bundle.putInt(VALIDATE_BUNDLE_RESULT_CODE, MessagingException.UNSPECIFIED_EXCEPTION);
    206             return bundle;
    207         } else {
    208             Bundle bundle = (Bundle) mReturn;
    209             bundle.setClassLoader(Policy.class.getClassLoader());
    210             LogUtils.v(TAG, "validate returns " + bundle.getInt(VALIDATE_BUNDLE_RESULT_CODE));
    211             return bundle;
    212         }
    213     }
    214 
    215     /**
    216      * Attempt to determine a user's host address and credentials from an email address and
    217      * password. The result is returned in a Bundle which MUST include an error code and MAY (on
    218      * success) include a HostAuth record sufficient to enable the service to validate the user's
    219      * account.
    220      *
    221      * @param userName the user's email address
    222      * @param password the user's password
    223      * @return a Bundle as described above
    224      */
    225     @Override
    226     public Bundle autoDiscover(final String userName, final String password)
    227             throws RemoteException {
    228         setTask(new ProxyTask() {
    229             @Override
    230             public void run() throws RemoteException{
    231                 mReturn = mService.autoDiscover(userName, password);
    232             }
    233         }, "autoDiscover");
    234         waitForCompletion();
    235         if (mReturn == null) {
    236             return null;
    237         } else {
    238             Bundle bundle = (Bundle) mReturn;
    239             bundle.setClassLoader(HostAuth.class.getClassLoader());
    240             LogUtils.v(TAG, "autoDiscover returns "
    241                     + bundle.getInt(AUTO_DISCOVER_BUNDLE_ERROR_CODE));
    242             return bundle;
    243         }
    244     }
    245 
    246     /**
    247      * Request that the service reload the folder list for the specified account. The service
    248      * MUST use the syncMailboxListStatus callback to indicate "starting" and "finished"
    249      *
    250      * @param accountId the id of the account whose folder list is to be updated
    251      */
    252     @Override
    253     public void updateFolderList(final long accountId) throws RemoteException {
    254         setTask(new ProxyTask() {
    255             @Override
    256             public void run() throws RemoteException {
    257                 mService.updateFolderList(accountId);
    258             }
    259         }, "updateFolderList");
    260     }
    261 
    262     /**
    263      * Specify the debug flags selected by the user.  The service SHOULD log debug information as
    264      * requested.
    265      *
    266      * @param flags an integer whose bits represent logging flags as defined in DEBUG_* flags above
    267      */
    268     @Override
    269     public void setLogging(final int flags) throws RemoteException {
    270         setTask(new ProxyTask() {
    271             @Override
    272             public void run() throws RemoteException {
    273                 mService.setLogging(flags);
    274             }
    275         }, "setLogging");
    276     }
    277 
    278     /**
    279      * Alert the sync adapter that the account's host information has (or may have) changed; the
    280      * service MUST stop all in-process or pending syncs, clear error states related to the
    281      * account and its mailboxes, and restart necessary sync adapters (e.g. pushed mailboxes)
    282      *
    283      * @param accountId the id of the account whose host information has changed
    284      */
    285     @Override
    286     public void hostChanged(final long accountId) throws RemoteException {
    287         setTask(new ProxyTask() {
    288             @Override
    289             public void run() throws RemoteException {
    290                 mService.hostChanged(accountId);
    291             }
    292         }, "hostChanged");
    293     }
    294 
    295     /**
    296      * Send a meeting response for the specified message
    297      *
    298      * @param messageId the id of the message containing the meeting request
    299      * @param response the response code, as defined in EmailServiceConstants
    300      */
    301     @Override
    302     public void sendMeetingResponse(final long messageId, final int response)
    303             throws RemoteException {
    304         setTask(new ProxyTask() {
    305             @Override
    306             public void run() throws RemoteException {
    307                 mService.sendMeetingResponse(messageId, response);
    308             }
    309         }, "sendMeetingResponse");
    310     }
    311 
    312     /**
    313      * Request the sync adapter to load a complete message
    314      *
    315      * @param messageId the id of the message to be loaded
    316      */
    317     @Override
    318     public void loadMore(final long messageId) throws RemoteException {
    319         setTask(new ProxyTask() {
    320             @Override
    321             public void run() throws RemoteException {
    322                 mService.loadMore(messageId);
    323             }
    324         }, "startSync");
    325     }
    326 
    327     /**
    328      * Not yet used
    329      *
    330      * @param accountId the account in which the folder is to be created
    331      * @param name the name of the folder to be created
    332     */
    333     @Override
    334     public boolean createFolder(long accountId, String name) throws RemoteException {
    335         return false;
    336     }
    337 
    338     /**
    339      * Not yet used
    340      *
    341      * @param accountId the account in which the folder resides
    342      * @param name the name of the folder to be deleted
    343      */
    344     @Override
    345     public boolean deleteFolder(long accountId, String name) throws RemoteException {
    346         return false;
    347     }
    348 
    349     /**
    350      * Not yet used
    351      *
    352      * @param accountId the account in which the folder resides
    353      * @param oldName the name of the existing folder
    354      * @param newName the new name for the folder
    355      */
    356     @Override
    357     public boolean renameFolder(long accountId, String oldName, String newName)
    358             throws RemoteException {
    359         return false;
    360     }
    361 
    362     /**
    363      * Request the service to delete the account's PIM (personal information management) data. This
    364      * data includes any data that is 1) associated with the account and 2) created/stored by the
    365      * service or its sync adapters and 3) not stored in the EmailProvider database (e.g. contact
    366      * and calendar information).
    367      *
    368      * @param emailAddress the email address for the account whose data should be deleted
    369      */
    370     @Override
    371     public void deleteAccountPIMData(final String emailAddress) throws RemoteException {
    372         setTask(new ProxyTask() {
    373             @Override
    374             public void run() throws RemoteException {
    375                 mService.deleteAccountPIMData(emailAddress);
    376             }
    377         }, "deleteAccountPIMData");
    378     }
    379 
    380     /**
    381      * Search for messages given a query string.  The string is interpreted as the logical AND of
    382      * terms separated by white space.  The search is performed on the specified mailbox in the
    383      * specified account (including subfolders, as specified by the includeSubfolders parameter).
    384      * At most numResults messages matching the query term(s) will be added to the mailbox specified
    385      * as destMailboxId. If mailboxId is -1, the entire account will be searched. If firstResult is
    386      * specified and non-zero, results will be added starting with the firstResult'th match (i.e.
    387      * for the continuation of a previous search)
    388      *
    389      * @param accountId the id of the account to be searched
    390      * @param searchParams the search specification
    391      * @param destMailboxId the id of the mailbox into which search results are appended
    392      * @return the total number of matches for this search (regardless of how many were requested)
    393      */
    394     @Override
    395     public int searchMessages(final long accountId, final SearchParams searchParams,
    396             final long destMailboxId) throws RemoteException {
    397         setTask(new ProxyTask() {
    398             @Override
    399             public void run() throws RemoteException{
    400                 mReturn = mService.searchMessages(accountId, searchParams, destMailboxId);
    401             }
    402         }, "searchMessages");
    403         waitForCompletion();
    404         if (mReturn == null) {
    405             return 0;
    406         } else {
    407             return (Integer)mReturn;
    408         }
    409     }
    410 
    411     /**
    412      * Request the service to send mail in the specified account's Outbox
    413      *
    414      * @param accountId the account whose outgoing mail should be sent
    415      */
    416     @Override
    417     public void sendMail(final long accountId) throws RemoteException {
    418         setTask(new ProxyTask() {
    419             @Override
    420             public void run() throws RemoteException{
    421                 mService.sendMail(accountId);
    422             }
    423         }, "sendMail");
    424     }
    425 
    426     @Override
    427     public int getCapabilities(final Account acct) throws RemoteException {
    428         setTask(new ProxyTask() {
    429             @Override
    430             public void run() throws RemoteException{
    431                 mReturn = mService.getCapabilities(acct);
    432             }
    433         }, "getCapabilities");
    434         waitForCompletion();
    435         if (mReturn == null) {
    436             return 0;
    437         } else {
    438             return (Integer)mReturn;
    439         }
    440     }
    441     /**
    442      * Request that the account be updated for this service; this call is synchronous
    443      *
    444      * @param emailAddress the email address of the account to be updated
    445      */
    446     @Override
    447     public void serviceUpdated(final String emailAddress) throws RemoteException {
    448         setTask(new ProxyTask() {
    449             @Override
    450             public void run() throws RemoteException{
    451                 mService.serviceUpdated(emailAddress);
    452             }
    453         }, "settingsUpdate");
    454         waitForCompletion();
    455     }
    456 
    457 
    458     @Override
    459     public IBinder asBinder() {
    460         return null;
    461     }
    462 }
    463