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 25 import com.android.emailcommon.Device; 26 import com.android.emailcommon.TempDirectory; 27 import com.android.emailcommon.mail.MessagingException; 28 import com.android.emailcommon.provider.HostAuth; 29 import com.android.emailcommon.provider.Policy; 30 import com.android.mail.utils.LogUtils; 31 32 import 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 49 public class EmailServiceProxy extends ServiceProxy implements IEmailService { 50 private static final String TAG = "EmailServiceProxy"; 51 52 public static final String AUTO_DISCOVER_BUNDLE_ERROR_CODE = "autodiscover_error_code"; 53 public static final String AUTO_DISCOVER_BUNDLE_HOST_AUTH = "autodiscover_host_auth"; 54 55 public static final String VALIDATE_BUNDLE_RESULT_CODE = "validate_result_code"; 56 public static final String VALIDATE_BUNDLE_POLICY_SET = "validate_policy_set"; 57 public static final String VALIDATE_BUNDLE_ERROR_MESSAGE = "validate_error_message"; 58 public static final String VALIDATE_BUNDLE_UNSUPPORTED_POLICIES = 59 "validate_unsupported_policies"; 60 public static final String VALIDATE_BUNDLE_PROTOCOL_VERSION = "validate_protocol_version"; 61 public static final String VALIDATE_BUNDLE_REDIRECT_ADDRESS = "validate_redirect_address"; 62 63 private Object mReturn = null; 64 private IEmailService mService; 65 private final boolean isRemote; 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 super(_context, new Intent(_context, _class)); 79 TempDirectory.setTempDirectory(_context); 80 isRemote = false; 81 } 82 83 // The following two constructors are used with remote services that must be referenced by 84 // a known action or by a prebuilt intent 85 public EmailServiceProxy(Context _context, Intent _intent) { 86 super(_context, _intent); 87 try { 88 Device.getDeviceId(_context); 89 } catch (IOException e) { 90 } 91 TempDirectory.setTempDirectory(_context); 92 isRemote = true; 93 } 94 95 @Override 96 public void onConnected(IBinder binder) { 97 mService = IEmailService.Stub.asInterface(binder); 98 } 99 100 public boolean isRemote() { 101 return isRemote; 102 } 103 104 /** 105 * Request an attachment to be loaded; the service MUST give higher priority to 106 * non-background loading. The service MUST use the loadAttachmentStatus callback when 107 * loading has started and stopped and SHOULD send callbacks with progress information if 108 * possible. 109 * 110 * @param cb The {@link IEmailServiceCallback} to use for this operation. 111 * @param accountId the id of the account in question 112 * @param attachmentId the id of the attachment record 113 * @param background whether or not this request corresponds to a background action (i.e. 114 * prefetch) vs a foreground action (user request) 115 */ 116 @Override 117 public void loadAttachment(final IEmailServiceCallback cb, final long accountId, 118 final long attachmentId, final boolean background) 119 throws RemoteException { 120 setTask(new ProxyTask() { 121 @Override 122 public void run() throws RemoteException { 123 try { 124 mService.loadAttachment(cb, accountId, attachmentId, background); 125 } catch (RemoteException e) { 126 try { 127 // Try to send a callback (if set) 128 if (cb != null) { 129 cb.loadAttachmentStatus(-1, attachmentId, 130 EmailServiceStatus.REMOTE_EXCEPTION, 0); 131 } 132 } catch (RemoteException e1) { 133 } 134 } 135 } 136 }, "loadAttachment"); 137 } 138 139 /** 140 * Validate a user account, given a protocol, host address, port, ssl status, and credentials. 141 * The result of this call is returned in a Bundle which MUST include a result code and MAY 142 * include a PolicySet that is required by the account. A successful validation implies a host 143 * address that serves the specified protocol and credentials sufficient to be authorized 144 * by the server to do so. 145 * 146 * @param hostAuth the hostauth object to validate 147 * @return a Bundle as described above 148 */ 149 @Override 150 public Bundle validate(final HostAuth hostAuth) throws RemoteException { 151 setTask(new ProxyTask() { 152 @Override 153 public void run() throws RemoteException{ 154 mReturn = mService.validate(hostAuth); 155 } 156 }, "validate"); 157 waitForCompletion(); 158 if (mReturn == null) { 159 Bundle bundle = new Bundle(); 160 bundle.putInt(VALIDATE_BUNDLE_RESULT_CODE, MessagingException.UNSPECIFIED_EXCEPTION); 161 return bundle; 162 } else { 163 Bundle bundle = (Bundle) mReturn; 164 bundle.setClassLoader(Policy.class.getClassLoader()); 165 LogUtils.v(TAG, "validate returns " + bundle.getInt(VALIDATE_BUNDLE_RESULT_CODE)); 166 return bundle; 167 } 168 } 169 170 /** 171 * Attempt to determine a user's host address and credentials from an email address and 172 * password. The result is returned in a Bundle which MUST include an error code and MAY (on 173 * success) include a HostAuth record sufficient to enable the service to validate the user's 174 * account. 175 * 176 * @param userName the user's email address 177 * @param password the user's password 178 * @return a Bundle as described above 179 */ 180 @Override 181 public Bundle autoDiscover(final String userName, final String password) 182 throws RemoteException { 183 setTask(new ProxyTask() { 184 @Override 185 public void run() throws RemoteException{ 186 mReturn = mService.autoDiscover(userName, password); 187 } 188 }, "autoDiscover"); 189 waitForCompletion(); 190 if (mReturn == null) { 191 return null; 192 } else { 193 Bundle bundle = (Bundle) mReturn; 194 bundle.setClassLoader(HostAuth.class.getClassLoader()); 195 LogUtils.v(TAG, "autoDiscover returns " 196 + bundle.getInt(AUTO_DISCOVER_BUNDLE_ERROR_CODE)); 197 return bundle; 198 } 199 } 200 201 /** 202 * Request that the service reload the folder list for the specified account. The service 203 * MUST use the syncMailboxListStatus callback to indicate "starting" and "finished" 204 * 205 * @param accountId the id of the account whose folder list is to be updated 206 */ 207 @Override 208 public void updateFolderList(final long accountId) throws RemoteException { 209 setTask(new ProxyTask() { 210 @Override 211 public void run() throws RemoteException { 212 mService.updateFolderList(accountId); 213 } 214 }, "updateFolderList"); 215 } 216 217 /** 218 * Specify the debug flags selected by the user. The service SHOULD log debug information as 219 * requested. 220 * 221 * @param flags an integer whose bits represent logging flags as defined in DEBUG_* flags above 222 */ 223 @Override 224 public void setLogging(final int flags) throws RemoteException { 225 setTask(new ProxyTask() { 226 @Override 227 public void run() throws RemoteException { 228 mService.setLogging(flags); 229 } 230 }, "setLogging"); 231 } 232 233 /** 234 * Send a meeting response for the specified message 235 * 236 * @param messageId the id of the message containing the meeting request 237 * @param response the response code, as defined in EmailServiceConstants 238 */ 239 @Override 240 public void sendMeetingResponse(final long messageId, final int response) 241 throws RemoteException { 242 setTask(new ProxyTask() { 243 @Override 244 public void run() throws RemoteException { 245 mService.sendMeetingResponse(messageId, response); 246 } 247 }, "sendMeetingResponse"); 248 } 249 250 /** 251 * Request the service to delete the account's PIM (personal information management) data. This 252 * data includes any data that is 1) associated with the account and 2) created/stored by the 253 * service or its sync adapters and 3) not stored in the EmailProvider database (e.g. contact 254 * and calendar information). 255 * 256 * @param emailAddress the email address for the account whose data should be deleted 257 */ 258 @Override 259 public void deleteAccountPIMData(final String emailAddress) throws RemoteException { 260 setTask(new ProxyTask() { 261 @Override 262 public void run() throws RemoteException { 263 mService.deleteAccountPIMData(emailAddress); 264 } 265 }, "deleteAccountPIMData"); 266 } 267 268 /** 269 * Search for messages given a query string. The string is interpreted as the logical AND of 270 * terms separated by white space. The search is performed on the specified mailbox in the 271 * specified account (including subfolders, as specified by the includeSubfolders parameter). 272 * At most numResults messages matching the query term(s) will be added to the mailbox specified 273 * as destMailboxId. If mailboxId is -1, the entire account will be searched. If firstResult is 274 * specified and non-zero, results will be added starting with the firstResult'th match (i.e. 275 * for the continuation of a previous search) 276 * 277 * @param accountId the id of the account to be searched 278 * @param searchParams the search specification 279 * @param destMailboxId the id of the mailbox into which search results are appended 280 * @return the total number of matches for this search (regardless of how many were requested) 281 */ 282 @Override 283 public int searchMessages(final long accountId, final SearchParams searchParams, 284 final long destMailboxId) throws RemoteException { 285 setTask(new ProxyTask() { 286 @Override 287 public void run() throws RemoteException{ 288 mReturn = mService.searchMessages(accountId, searchParams, destMailboxId); 289 } 290 }, "searchMessages"); 291 waitForCompletion(); 292 if (mReturn == null) { 293 return 0; 294 } else { 295 return (Integer)mReturn; 296 } 297 } 298 299 /** 300 * Request the service to send mail in the specified account's Outbox 301 * 302 * @param accountId the account whose outgoing mail should be sent 303 */ 304 @Override 305 public void sendMail(final long accountId) throws RemoteException { 306 setTask(new ProxyTask() { 307 @Override 308 public void run() throws RemoteException{ 309 mService.sendMail(accountId); 310 } 311 }, "sendMail"); 312 } 313 314 /** 315 * Request the service to refresh its push notification status (e.g. to start or stop receiving 316 * them, or to change which folders we want notifications for). 317 * @param accountId The account whose push settings to modify. 318 */ 319 @Override 320 public void pushModify(final long accountId) throws RemoteException { 321 setTask(new ProxyTask() { 322 @Override 323 public void run() throws RemoteException{ 324 mService.pushModify(accountId); 325 } 326 }, "sendMail"); 327 } 328 329 @Override 330 public void sync(final long accountId, final boolean updateFolderList, 331 final int mailboxType, final long[] folders) {} 332 333 @Override 334 public IBinder asBinder() { 335 return null; 336 } 337 } 338