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