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