• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2007-2008 Esmertec AG.
3  * Copyright (C) 2007-2008 The Android Open Source Project
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 
18 package com.android.im.service;
19 
20 import java.util.HashMap;
21 import java.util.Map;
22 
23 import android.content.ContentResolver;
24 import android.content.ContentUris;
25 import android.content.ContentValues;
26 import android.database.Cursor;
27 import android.net.Uri;
28 import android.os.RemoteCallbackList;
29 import android.os.RemoteException;
30 import android.util.Log;
31 
32 import com.android.im.IChatSessionManager;
33 import com.android.im.IConnectionListener;
34 import com.android.im.IContactListManager;
35 import com.android.im.IImConnection;
36 import com.android.im.IInvitationListener;
37 import com.android.im.engine.ChatGroupManager;
38 import com.android.im.engine.ConnectionListener;
39 import com.android.im.engine.Contact;
40 import com.android.im.engine.ContactListManager;
41 import com.android.im.engine.ImConnection;
42 import com.android.im.engine.ImErrorInfo;
43 import com.android.im.engine.ImException;
44 import com.android.im.engine.Invitation;
45 import com.android.im.engine.InvitationListener;
46 import com.android.im.engine.LoginInfo;
47 import com.android.im.engine.Presence;
48 import com.android.im.provider.Imps;
49 
50 public class ImConnectionAdapter extends IImConnection.Stub {
51     private static final String TAG = RemoteImService.TAG;
52 
53     private static final String[] SESSION_COOKIE_PROJECTION = {
54         Imps.SessionCookies.NAME,
55         Imps.SessionCookies.VALUE,
56     };
57 
58     private static final int COLUMN_SESSION_COOKIE_NAME = 0;
59     private static final int COLUMN_SESSION_COOKIE_VALUE = 1;
60 
61     ImConnection mConnection;
62     private ConnectionListenerAdapter mConnectionListener;
63     private InvitationListenerAdapter mInvitationListener;
64 
65     final RemoteCallbackList<IConnectionListener> mRemoteConnListeners
66             = new RemoteCallbackList<IConnectionListener>();
67 
68     ChatSessionManagerAdapter mChatSessionManager;
69     ContactListManagerAdapter mContactListManager;
70 
71     ChatGroupManager mGroupManager;
72     RemoteImService mService;
73 
74     long mProviderId = -1;
75     long mAccountId = -1;
76     boolean mAutoLoadContacts;
77     int mConnectionState = ImConnection.DISCONNECTED;
78 
ImConnectionAdapter(long providerId, ImConnection connection, RemoteImService service)79     public ImConnectionAdapter(long providerId, ImConnection connection,
80             RemoteImService service) {
81         mProviderId = providerId;
82         mConnection = connection;
83         mService = service;
84         mConnectionListener = new ConnectionListenerAdapter();
85         mConnection.addConnectionListener(mConnectionListener);
86         if ((connection.getCapability() & ImConnection.CAPABILITY_GROUP_CHAT) != 0) {
87             mGroupManager = mConnection.getChatGroupManager();
88             mInvitationListener = new InvitationListenerAdapter();
89             mGroupManager.setInvitationListener(mInvitationListener);
90         }
91     }
92 
getAdaptee()93     public ImConnection getAdaptee() {
94         return mConnection;
95     }
96 
getContext()97     public RemoteImService getContext() {
98         return mService;
99     }
100 
getProviderId()101     public long getProviderId() {
102         return mProviderId;
103     }
104 
getAccountId()105     public long getAccountId() {
106         return mAccountId;
107     }
108 
getSupportedPresenceStatus()109     public int[] getSupportedPresenceStatus() {
110         return mConnection.getSupportedPresenceStatus();
111     }
112 
networkTypeChanged()113     public void networkTypeChanged() {
114         mConnection.networkTypeChanged();
115     }
116 
reestablishSession()117     void reestablishSession() {
118         mConnectionState = ImConnection.LOGGING_IN;
119 
120         ContentResolver cr = mService.getContentResolver();
121         if ((mConnection.getCapability() & ImConnection.CAPABILITY_SESSION_REESTABLISHMENT) != 0) {
122             HashMap<String, String> cookie = querySessionCookie(cr);
123             if (cookie != null) {
124                 Log.d(TAG, "re-establish session");
125                 try {
126                     mConnection.reestablishSessionAsync(cookie);
127                 } catch (IllegalArgumentException e) {
128                     Log.e(TAG, "Invalid session cookie, probably modified by others.");
129                     clearSessionCookie(cr);
130                 }
131             }
132         }
133     }
134 
getSessionCookiesUri()135     private Uri getSessionCookiesUri() {
136         Uri.Builder builder = Imps.SessionCookies.CONTENT_URI_SESSION_COOKIES_BY.buildUpon();
137         ContentUris.appendId(builder, mProviderId);
138         ContentUris.appendId(builder, mAccountId);
139 
140         return builder.build();
141     }
142 
login(long accountId, String userName, String password, boolean autoLoadContacts)143     public void login(long accountId, String userName, String password,
144             boolean autoLoadContacts) {
145         mAccountId = accountId;
146         mAutoLoadContacts = autoLoadContacts;
147         mConnectionState = ImConnection.LOGGING_IN;
148 
149         mConnection.loginAsync(new LoginInfo(userName, password));
150 
151         mChatSessionManager = new ChatSessionManagerAdapter(this);
152         mContactListManager = new ContactListManagerAdapter(this);
153     }
154 
querySessionCookie(ContentResolver cr)155     private HashMap<String, String> querySessionCookie(ContentResolver cr) {
156         Cursor c = cr.query(getSessionCookiesUri(), SESSION_COOKIE_PROJECTION, null, null, null);
157         if (c == null) {
158             return null;
159         }
160 
161         HashMap<String, String> cookie = null;
162         if (c.getCount() > 0) {
163             cookie = new HashMap<String, String>();
164             while(c.moveToNext()) {
165                 cookie.put(c.getString(COLUMN_SESSION_COOKIE_NAME),
166                     c.getString(COLUMN_SESSION_COOKIE_VALUE));
167             }
168         }
169 
170         c.close();
171         return cookie;
172     }
173 
logout()174     public void logout() {
175         mConnectionState = ImConnection.LOGGING_OUT;
176         mConnection.logoutAsync();
177     }
178 
cancelLogin()179     public synchronized void cancelLogin() {
180         if (mConnectionState >= ImConnection.LOGGED_IN) {
181             // too late
182             return;
183         }
184 
185         logout();
186     }
187 
suspend()188     void suspend() {
189         mConnectionState = ImConnection.SUSPENDING;
190         mConnection.suspend();
191     }
192 
registerConnectionListener(IConnectionListener listener)193     public void registerConnectionListener(IConnectionListener listener) {
194         if (listener != null) {
195             mRemoteConnListeners.register(listener);
196         }
197     }
198 
unregisterConnectionListener(IConnectionListener listener)199     public void unregisterConnectionListener(IConnectionListener listener) {
200         if (listener != null) {
201             mRemoteConnListeners.unregister(listener);
202         }
203     }
204 
setInvitationListener(IInvitationListener listener)205     public void setInvitationListener(IInvitationListener listener) {
206         if(mInvitationListener != null) {
207             mInvitationListener.mRemoteListener = listener;
208         }
209     }
210 
getChatSessionManager()211     public IChatSessionManager getChatSessionManager() {
212         return mChatSessionManager;
213     }
214 
getContactListManager()215     public IContactListManager getContactListManager() {
216         return mContactListManager;
217     }
218 
getChatSessionCount()219     public int getChatSessionCount() {
220         if (mChatSessionManager == null) {
221             return 0;
222         }
223         return mChatSessionManager.getChatSessionCount();
224     }
225 
getLoginUser()226     public Contact getLoginUser() {
227         return mConnection.getLoginUser();
228     }
229 
getUserPresence()230     public Presence getUserPresence() {
231         return mConnection.getUserPresence();
232     }
233 
updateUserPresence(Presence newPresence)234     public int updateUserPresence(Presence newPresence) {
235         try {
236             mConnection.updateUserPresenceAsync(newPresence);
237         } catch (ImException e) {
238             return e.getImError().getCode();
239         }
240 
241         return ImErrorInfo.NO_ERROR;
242     }
243 
getState()244     public int getState() {
245         return mConnectionState;
246     }
247 
rejectInvitation(long id)248     public void rejectInvitation(long id){
249         handleInvitation(id, false);
250     }
251 
acceptInvitation(long id)252     public void acceptInvitation(long id) {
253         handleInvitation(id, true);
254     }
255 
handleInvitation(long id, boolean accept)256     private void handleInvitation(long id, boolean accept) {
257         if(mGroupManager == null) {
258             return;
259         }
260         ContentResolver cr = mService.getContentResolver();
261         Cursor c = cr.query(ContentUris.withAppendedId(Imps.Invitation.CONTENT_URI, id), null, null, null, null);
262         if(c == null) {
263             return;
264         }
265         if(c.moveToFirst()) {
266             String inviteId = c.getString(c.getColumnIndexOrThrow(Imps.Invitation.INVITE_ID));
267             int status;
268             if(accept) {
269                 mGroupManager.acceptInvitationAsync(inviteId);
270                 status = Imps.Invitation.STATUS_ACCEPTED;
271             } else {
272                 mGroupManager.rejectInvitationAsync(inviteId);
273                 status = Imps.Invitation.STATUS_REJECTED;
274             }
275             c.updateInt(c.getColumnIndexOrThrow(Imps.Invitation.STATUS), status);
276             c.commitUpdates();
277         }
278         c.close();
279     }
280 
saveSessionCookie(ContentResolver cr)281     void saveSessionCookie(ContentResolver cr) {
282         HashMap<String, String> cookies = mConnection.getSessionContext();
283 
284         int i = 0;
285         ContentValues[] valuesList = new ContentValues[cookies.size()];
286 
287         for(Map.Entry<String,String> entry : cookies.entrySet()){
288             ContentValues values = new ContentValues(2);
289 
290             values.put(Imps.SessionCookies.NAME, entry.getKey());
291             values.put(Imps.SessionCookies.VALUE, entry.getValue());
292 
293             valuesList[i++] = values;
294         }
295 
296         cr.bulkInsert(getSessionCookiesUri(), valuesList);
297     }
298 
clearSessionCookie(ContentResolver cr)299     void clearSessionCookie(ContentResolver cr) {
300         cr.delete(getSessionCookiesUri(), null, null);
301     }
302 
updateAccountStatusInDb()303     void updateAccountStatusInDb() {
304         Presence p = getUserPresence();
305         int presenceStatus = Imps.Presence.OFFLINE;
306         int connectionStatus = convertConnStateForDb(mConnectionState);
307 
308         if (p != null) {
309             presenceStatus = ContactListManagerAdapter.convertPresenceStatus(p);
310         }
311 
312         ContentResolver cr = mService.getContentResolver();
313         Uri uri = Imps.AccountStatus.CONTENT_URI;
314         ContentValues values = new ContentValues();
315 
316         values.put(Imps.AccountStatus.ACCOUNT, mAccountId);
317         values.put(Imps.AccountStatus.PRESENCE_STATUS, presenceStatus);
318         values.put(Imps.AccountStatus.CONNECTION_STATUS, connectionStatus);
319 
320         cr.insert(uri, values);
321     }
322 
convertConnStateForDb(int state)323     private static int convertConnStateForDb(int state) {
324         switch (state) {
325         case ImConnection.DISCONNECTED:
326         case ImConnection.LOGGING_OUT:
327             return Imps.ConnectionStatus.OFFLINE;
328 
329         case ImConnection.LOGGING_IN:
330             return Imps.ConnectionStatus.CONNECTING;
331 
332         case ImConnection.LOGGED_IN:
333             return Imps.ConnectionStatus.ONLINE;
334 
335         case ImConnection.SUSPENDED:
336         case ImConnection.SUSPENDING:
337             return Imps.ConnectionStatus.SUSPENDED;
338 
339         default:
340             return Imps.ConnectionStatus.OFFLINE;
341         }
342     }
343 
344     final class ConnectionListenerAdapter implements ConnectionListener{
onStateChanged(final int state, final ImErrorInfo error)345         public void onStateChanged(final int state, final ImErrorInfo error) {
346             synchronized (this) {
347                 if (state == ImConnection.LOGGED_IN
348                         && mConnectionState == ImConnection.LOGGING_OUT) {
349                     // A bit tricky here. The engine did login successfully
350                     // but the notification comes a bit late; user has already
351                     // issued a cancelLogin() and that cannot be undone. Here
352                     // we have to ignore the LOGGED_IN event and wait for
353                     // the upcoming DISCONNECTED.
354                     return;
355                 }
356 
357                 if (state != ImConnection.DISCONNECTED) {
358                     mConnectionState = state;
359                 }
360             }
361 
362             ContentResolver cr = mService.getContentResolver();
363             if(state == ImConnection.LOGGED_IN) {
364                 if ((mConnection.getCapability() & ImConnection.CAPABILITY_SESSION_REESTABLISHMENT) != 0){
365                     saveSessionCookie(cr);
366                 }
367 
368                 if(mAutoLoadContacts && mContactListManager.getState()
369                         != ContactListManager.LISTS_LOADED) {
370                     mContactListManager.loadContactLists();
371                 }
372 
373                 for (ChatSessionAdapter session : mChatSessionManager.mActiveSessions.values()) {
374                     session.sendPostponedMessages();
375                 }
376             } else if (state == ImConnection.LOGGING_OUT) {
377                 // The engine has started to logout the connection, remove it
378                 // from the active connection list.
379                 mService.removeConnection(ImConnectionAdapter.this);
380             } else if(state == ImConnection.DISCONNECTED) {
381                 mService.removeConnection(ImConnectionAdapter.this);
382 
383                 clearSessionCookie(cr);
384                 // mContactListManager might still be null if we fail
385                 // immediately in loginAsync (say, an invalid host URL)
386                 if (mContactListManager != null) {
387                     mContactListManager.clearOnLogout();
388                 }
389                 if (mChatSessionManager != null) {
390                     mChatSessionManager.closeAllChatSessions();
391                 }
392 
393                 mConnectionState = state;
394             } else if(state == ImConnection.SUSPENDED && error != null) {
395                 // re-establish failed, schedule to retry
396                 // TODO increase delay after retry failed.
397                 mService.scheduleReconnect(15000);
398             }
399 
400             updateAccountStatusInDb();
401 
402             final int N = mRemoteConnListeners.beginBroadcast();
403             for (int i = 0; i < N; i++) {
404                 IConnectionListener listener = mRemoteConnListeners.getBroadcastItem(i);
405                 try {
406                     listener.onStateChanged(ImConnectionAdapter.this, state, error);
407                 } catch (RemoteException e) {
408                     // The RemoteCallbackList will take care of removing the
409                     // dead listeners.
410                 }
411             }
412             mRemoteConnListeners.finishBroadcast();
413         }
414 
onUserPresenceUpdated()415         public void onUserPresenceUpdated() {
416             updateAccountStatusInDb();
417 
418             final int N = mRemoteConnListeners.beginBroadcast();
419             for (int i = 0; i < N; i++) {
420                 IConnectionListener listener = mRemoteConnListeners.getBroadcastItem(i);
421                 try {
422                     listener.onUserPresenceUpdated(ImConnectionAdapter.this);
423                 } catch (RemoteException e) {
424                     // The RemoteCallbackList will take care of removing the
425                     // dead listeners.
426                 }
427             }
428             mRemoteConnListeners.finishBroadcast();
429         }
430 
onUpdatePresenceError(final ImErrorInfo error)431         public void onUpdatePresenceError(final ImErrorInfo error) {
432             final int N = mRemoteConnListeners.beginBroadcast();
433             for (int i = 0; i < N; i++) {
434                 IConnectionListener listener = mRemoteConnListeners.getBroadcastItem(i);
435                 try {
436                     listener.onUpdatePresenceError(ImConnectionAdapter.this, error);
437                 } catch (RemoteException e) {
438                     // The RemoteCallbackList will take care of removing the
439                     // dead listeners.
440                 }
441             }
442             mRemoteConnListeners.finishBroadcast();
443         }
444     }
445 
446     final class InvitationListenerAdapter implements InvitationListener {
447         IInvitationListener mRemoteListener;
448 
onGroupInvitation(Invitation invitation)449         public void onGroupInvitation(Invitation invitation) {
450             String sender = invitation.getSender().getScreenName();
451             ContentValues values = new ContentValues(7);
452             values.put(Imps.Invitation.PROVIDER, mProviderId);
453             values.put(Imps.Invitation.ACCOUNT, mAccountId);
454             values.put(Imps.Invitation.INVITE_ID, invitation.getInviteID());
455             values.put(Imps.Invitation.SENDER, sender);
456             values.put(Imps.Invitation.GROUP_NAME, invitation.getGroupAddress().getScreenName());
457             values.put(Imps.Invitation.NOTE, invitation.getReason());
458             values.put(Imps.Invitation.STATUS, Imps.Invitation.STATUS_PENDING);
459             ContentResolver resolver = mService.getContentResolver();
460             Uri uri = resolver.insert(Imps.Invitation.CONTENT_URI, values);
461             long id = ContentUris.parseId(uri);
462             try {
463                 if (mRemoteListener != null) {
464                     mRemoteListener.onGroupInvitation(id);
465                     return;
466                 }
467             } catch (RemoteException e) {
468                 Log.i(TAG, "onGroupInvitation: dead listener "
469                         + mRemoteListener +"; removing");
470                 mRemoteListener = null;
471             }
472             // No listener registered or failed to notify the listener, send a
473             // notification instead.
474             mService.getStatusBarNotifier().notifyGroupInvitation(mProviderId, mAccountId, id, sender);
475         }
476     }
477 }
478