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