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