EmailServiceProxy.java revision aeee10e57ef4d931e7708fde218d590453a82aea
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
17package com.android.emailcommon.service;
18
19import com.android.emailcommon.Api;
20import com.android.emailcommon.Device;
21import com.android.emailcommon.mail.MessagingException;
22import com.android.emailcommon.provider.EmailContent.HostAuth;
23import com.android.emailcommon.provider.Policy;
24
25import android.content.Context;
26import android.content.Intent;
27import android.os.Bundle;
28import android.os.IBinder;
29import android.os.RemoteException;
30import android.util.Log;
31
32import 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
49public 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 protocol the protocol used by the account (e.g. "imap", "pop3", or "eas)
190     * @param host the address of the host server (in the form x.y.z)
191     * @param userName the username credential for the account
192     * @param password the password credential for the account
193     * @param port the port used to connect to the host server
194     * @param ssl whether or not a secure (SSL) socket should be used for the connection
195     * @param trustCertificates whether or not self-signed or otherwise unverified server
196     * certificates should be allowed when connecting to the host
197     * @return a Bundle as described above
198     */
199    public Bundle validate(final String protocol, final String host, final String userName,
200            final String password, final int port, final boolean ssl,
201            final boolean trustCertificates) throws RemoteException {
202        setTask(new ProxyTask() {
203            public void run() throws RemoteException{
204                if (mCallback != null) mService.setCallback(mCallback);
205                mReturn = mService.validate(protocol, host, userName, password, port, ssl,
206                        trustCertificates);
207            }
208        }, "validate");
209        waitForCompletion();
210        if (mReturn == null) {
211            Bundle bundle = new Bundle();
212            bundle.putInt(VALIDATE_BUNDLE_RESULT_CODE, MessagingException.UNSPECIFIED_EXCEPTION);
213            return bundle;
214        } else {
215            Bundle bundle = (Bundle) mReturn;
216            bundle.setClassLoader(Policy.class.getClassLoader());
217            Log.v(TAG, "validate returns " + bundle.getInt(VALIDATE_BUNDLE_RESULT_CODE));
218            return bundle;
219        }
220    }
221
222    /**
223     * Attempt to determine a user's host address and credentials from an email address and
224     * password. The result is returned in a Bundle which MUST include an error code and MAY (on
225     * success) include a HostAuth record sufficient to enable the service to validate the user's
226     * account.
227     *
228     * @param userName the user's email address
229     * @param password the user's password
230     * @return a Bundle as described above
231     */
232    public Bundle autoDiscover(final String userName, final String password)
233            throws RemoteException {
234        setTask(new ProxyTask() {
235            public void run() throws RemoteException{
236                if (mCallback != null) mService.setCallback(mCallback);
237                mReturn = mService.autoDiscover(userName, password);
238            }
239        }, "autoDiscover");
240        waitForCompletion();
241        if (mReturn == null) {
242            return null;
243        } else {
244            Bundle bundle = (Bundle) mReturn;
245            bundle.setClassLoader(HostAuth.class.getClassLoader());
246            Log.v(TAG, "autoDiscover returns " + bundle.getInt(AUTO_DISCOVER_BUNDLE_ERROR_CODE));
247            return bundle;
248        }
249    }
250
251    /**
252     * Request that the service reload the folder list for the specified account. The service
253     * MUST use the syncMailboxListStatus callback to indicate "starting" and "finished"
254     *
255     * @param accoundId the id of the account whose folder list is to be updated
256     */
257    public void updateFolderList(final long accountId) throws RemoteException {
258        setTask(new ProxyTask() {
259            public void run() throws RemoteException {
260                if (mCallback != null) mService.setCallback(mCallback);
261                mService.updateFolderList(accountId);
262            }
263        }, "updateFolderList");
264    }
265
266    /**
267     * Specify the debug flags selected by the user.  The service SHOULD log debug information as
268     * requested.
269     *
270     * @param flags an integer whose bits represent logging flags as defined in DEBUG_* flags above
271     */
272    public void setLogging(final int flags) throws RemoteException {
273        setTask(new ProxyTask() {
274            public void run() throws RemoteException {
275                if (mCallback != null) mService.setCallback(mCallback);
276                mService.setLogging(flags);
277            }
278        }, "setLogging");
279    }
280
281    /**
282     * Set the global callback object to be used by the service; the service MUST always use the
283     * most recently set callback object
284     *
285     * @param cb a callback object through which all service callbacks are executed
286     */
287    public void setCallback(final IEmailServiceCallback cb) throws RemoteException {
288        setTask(new ProxyTask() {
289            public void run() throws RemoteException {
290                mService.setCallback(cb);
291            }
292        }, "setCallback");
293    }
294
295    /**
296     * Alert the sync adapter that the account's host information has (or may have) changed; the
297     * service MUST stop all in-process or pending syncs, clear error states related to the
298     * account and its mailboxes, and restart necessary sync adapters (e.g. pushed mailboxes)
299     *
300     * @param accountId the id of the account whose host information has changed
301     */
302    public void hostChanged(final long accountId) throws RemoteException {
303        setTask(new ProxyTask() {
304            public void run() throws RemoteException {
305                mService.hostChanged(accountId);
306            }
307        }, "hostChanged");
308    }
309
310    /**
311     * Send a meeting response for the specified message
312     *
313     * @param messageId the id of the message containing the meeting request
314     * @param response the response code, as defined in EmailServiceConstants
315     */
316    public void sendMeetingResponse(final long messageId, final int response)
317            throws RemoteException {
318        setTask(new ProxyTask() {
319            public void run() throws RemoteException {
320                if (mCallback != null) mService.setCallback(mCallback);
321                mService.sendMeetingResponse(messageId, response);
322            }
323        }, "sendMeetingResponse");
324    }
325
326    /**
327     * Not yet used; intended to request the sync adapter to load a complete message
328     *
329     * @param messageId the id of the message to be loaded
330     */
331    public void loadMore(long messageId) throws RemoteException {
332    }
333
334    /**
335     * Not yet used
336     *
337     * @param accountId the account in which the folder is to be created
338     * @param name the name of the folder to be created
339    */
340    public boolean createFolder(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 name the name of the folder to be deleted
349     */
350    public boolean deleteFolder(long accountId, String name) throws RemoteException {
351        return false;
352    }
353
354    /**
355     * Not yet used
356     *
357     * @param accountId the account in which the folder resides
358     * @param oldName the name of the existing folder
359     * @param newName the new name for the folder
360     */
361    public boolean renameFolder(long accountId, String oldName, String newName)
362            throws RemoteException {
363        return false;
364    }
365
366    /**
367     * Request the service to delete the account's PIM (personal information management) data. This
368     * data includes any data that is 1) associated with the account and 2) created/stored by the
369     * service or its sync adapters and 3) not stored in the EmailProvider database (e.g. contact
370     * and calendar information).
371     *
372     * @param accountId the account whose data is to be deleted
373     */
374    public void deleteAccountPIMData(final long accountId) throws RemoteException {
375        setTask(new ProxyTask() {
376            public void run() throws RemoteException {
377                mService.deleteAccountPIMData(accountId);
378            }
379        }, "deleteAccountPIMData");
380    }
381
382
383    /**
384     * PRELIMINARY
385     * Search for messages given a query string.  The string is interpreted as the logical AND of
386     * terms separated by white space.  The search is performed on the specified mailbox in the
387     * specified account (including subfolders, as specified by the includeSubfolders parameter).
388     * At most numResults messages matching the query term(s) will be added to the mailbox specified
389     * as destMailboxId. If mailboxId is -1, the entire account will be searched. If firstResult is
390     * specified and non-zero, results will be added starting with the firstResult'th match (i.e.
391     * for the continuation of a previous search)
392     *
393     * @param accountId the id of the account to be searched
394     * @param mailboxId the id of the mailbox to be searched; if -1, all mailboxes should be
395     * searched
396     * @param includeSubfolders if true, all subfolders of the specified mailbox should be searched
397     * @param query the terms to be searched (the search MUST find messages whose contents
398     * include all of the search terms in the query
399     * @param numResults specifies the maximum number of results returned in this request
400     * @param firstResult if zero, specifies a "new" query; otherwise, asks for a continuation of
401     * the previous query(ies) starting with the firstResult'th match (0 based)
402     * @param destMailboxId the id of the mailbox into which search results are appended
403     * @return the total number of matches for this search (regardless of how many were requested)
404     */
405    public int searchMessages(final long accountId, final long mailboxId,
406            final boolean includeSubfolders, final String query, final int numResults,
407            final int firstResult, final long destMailboxId) throws RemoteException {
408        setTask(new ProxyTask() {
409            public void run() throws RemoteException{
410                if (mCallback != null) mService.setCallback(mCallback);
411                mReturn = mService.searchMessages(accountId, mailboxId, includeSubfolders, query,
412                        numResults, firstResult, destMailboxId);
413            }
414        }, "searchMessages");
415        waitForCompletion();
416        if (mReturn == null) {
417            return 0;
418        } else {
419            return (Integer)mReturn;
420        }
421    }
422    public IBinder asBinder() {
423        return null;
424    }
425}
426