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