/*
* Copyright (C) 2007-2008 Esmertec AG. Copyright (C) 2007-2008 The Android Open
* Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package info.guardianproject.otr.app.im.service;
import info.guardianproject.cacheword.CacheWordActivityHandler;
import info.guardianproject.cacheword.CacheWordHandler;
import info.guardianproject.cacheword.ICacheWordSubscriber;
import info.guardianproject.otr.IOtrKeyManager;
import info.guardianproject.otr.OtrAndroidKeyManagerImpl;
import info.guardianproject.otr.OtrChatManager;
import info.guardianproject.otr.OtrDebugLogger;
import info.guardianproject.otr.app.im.IConnectionCreationListener;
import info.guardianproject.otr.app.im.IImConnection;
import info.guardianproject.otr.app.im.IRemoteImService;
import info.guardianproject.otr.app.im.ImService;
import info.guardianproject.otr.app.im.R;
import info.guardianproject.otr.app.im.app.ChatFileStore;
import info.guardianproject.otr.app.im.app.DummyActivity;
import info.guardianproject.otr.app.im.app.ImApp;
import info.guardianproject.otr.app.im.app.ImPluginHelper;
import info.guardianproject.otr.app.im.app.NetworkConnectivityListener;
import info.guardianproject.otr.app.im.app.NetworkConnectivityListener.State;
import info.guardianproject.otr.app.im.app.NewChatActivity;
import info.guardianproject.otr.app.im.engine.ConnectionFactory;
import info.guardianproject.otr.app.im.engine.ImConnection;
import info.guardianproject.otr.app.im.engine.ImException;
import info.guardianproject.otr.app.im.plugin.ImPluginInfo;
import info.guardianproject.otr.app.im.provider.Imps;
import info.guardianproject.otr.app.im.provider.Imps.ProviderSettings.QueryMap;
import info.guardianproject.otr.app.im.provider.SQLCipherOpenHelper;
import info.guardianproject.util.Debug;
import info.guardianproject.util.LogCleaner;
import java.util.ArrayList;
import java.util.Date;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import net.java.otr4j.OtrEngineListener;
import net.java.otr4j.OtrKeyManager;
import net.java.otr4j.OtrKeyManagerListener;
import net.java.otr4j.OtrPolicy;
import net.java.otr4j.session.SessionID;
import android.annotation.TargetApi;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.net.NetworkInfo;
import android.net.Uri;
import android.net.Uri.Builder;
import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.PowerManager;
import android.os.PowerManager.WakeLock;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.support.v4.app.NotificationCompat;
import android.util.Log;
import android.widget.Toast;
public class RemoteImService extends Service implements OtrEngineListener, ImService, ICacheWordSubscriber {
private static final String PREV_CONNECTIONS_TRAIL_TAG = "prev_connections";
private static final String CONNECTIONS_TRAIL_TAG = "connections";
private static final String LAST_SWIPE_TRAIL_TAG = "last_swipe";
private static final String SERVICE_DESTROY_TRAIL_TAG = "service_destroy";
private static final String PREV_SERVICE_CREATE_TRAIL_TAG = "prev_service_create";
private static final String SERVICE_CREATE_TRAIL_KEY = "service_create";
private static final String[] ACCOUNT_PROJECTION = { Imps.Account._ID, Imps.Account.PROVIDER,
Imps.Account.USERNAME,
Imps.Account.PASSWORD, };
// TODO why aren't these Imps.Account.* values?
private static final int ACCOUNT_ID_COLUMN = 0;
private static final int ACCOUNT_PROVIDER_COLUMN = 1;
private static final int ACCOUNT_USERNAME_COLUMN = 2;
private static final int ACCOUNT_PASSOWRD_COLUMN = 3;
private static final int EVENT_SHOW_TOAST = 100;
private StatusBarNotifier mStatusBarNotifier;
private Handler mServiceHandler;
private int mNetworkType;
private boolean mNeedCheckAutoLogin;
//private SettingsMonitor mSettingsMonitor;
private OtrChatManager mOtrChatManager;
private ImPluginHelper mPluginHelper;
private Hashtable<String, ImConnectionAdapter> mConnections;
private Imps.ProviderSettings.QueryMap mGlobalSettings;
private Handler mHandler;
final RemoteCallbackList<IConnectionCreationListener> mRemoteListeners = new RemoteCallbackList<IConnectionCreationListener>();
public long mHeartbeatInterval;
private WakeLock mWakeLock;
private NetworkConnectivityListener.State mNetworkState;
private CacheWordHandler mCacheWord = null;
private NotificationManager mNotifyManager;
NotificationCompat.Builder mNotifyBuilder;
private int mNumNotify = 0;
private final static int notifyId = 7777;
private static final String TAG = "GB.ImService";
public long getHeartbeatInterval() {
return mHeartbeatInterval;
}
public static void debug(String msg) {
LogCleaner.debug(TAG, msg);
// Log.d(TAG, msg);
}
public static void debug(String msg, Exception e) {
LogCleaner.error(TAG, msg, e);
}
private void updateOtrPolicy ()
{
int otrPolicy = convertPolicy();
mOtrChatManager.setPolicy(otrPolicy);
}
private synchronized OtrChatManager initOtrChatManager() {
int otrPolicy = convertPolicy();
if (mOtrChatManager == null) {
try
{
OtrKeyManager otrKeyManager = OtrAndroidKeyManagerImpl.getInstance(this);
if (otrKeyManager != null)
{
mOtrChatManager = OtrChatManager.getInstance(otrPolicy, this, otrKeyManager);
mOtrChatManager.addOtrEngineListener(this);
otrKeyManager.addListener(new OtrKeyManagerListener() {
public void verificationStatusChanged(SessionID session) {
boolean isVerified = mOtrChatManager.getKeyManager().isVerified(session);
String msg = session + ": verification status=" + isVerified;
OtrDebugLogger.log(msg);
}
public void remoteVerifiedUs(SessionID session) {
String msg = session + ": remote verified us";
OtrDebugLogger.log(msg);
showToast(getString(R.string.remote_verified_us),Toast.LENGTH_SHORT);
// if (!isRemoteKeyVerified(session))
// showWarning(session, mContext.getApplicationContext().getString(R.string.remote_verified_us));
}
});
}
}
catch (Exception e)
{
OtrDebugLogger.log("unable to init OTR manager", e);
}
} else {
mOtrChatManager.setPolicy(otrPolicy);
}
return mOtrChatManager;
}
private int convertPolicy() {
int otrPolicy = OtrPolicy.OPPORTUNISTIC;
QueryMap gSettings = getGlobalSettings();
if (gSettings != null)
{
String otrModeSelect = gSettings.getOtrMode();
if (otrModeSelect.equals("auto")) {
otrPolicy = OtrPolicy.OPPORTUNISTIC;
} else if (otrModeSelect.equals("disabled")) {
otrPolicy = OtrPolicy.NEVER;
} else if (otrModeSelect.equals("force")) {
otrPolicy = OtrPolicy.OTRL_POLICY_ALWAYS;
} else if (otrModeSelect.equals("requested")) {
otrPolicy = OtrPolicy.OTRL_POLICY_MANUAL;
}
}
return otrPolicy;
}
private synchronized Imps.ProviderSettings.QueryMap getGlobalSettings() {
if (mGlobalSettings == null) {
ContentResolver contentResolver = getContentResolver();
Cursor cursor = contentResolver.query(Imps.ProviderSettings.CONTENT_URI,new String[] {Imps.ProviderSettings.NAME, Imps.ProviderSettings.VALUE},Imps.ProviderSettings.PROVIDER + "=?",new String[] { Long.toString(Imps.ProviderSettings.PROVIDER_ID_FOR_GLOBAL_SETTINGS)},null);
if (cursor == null)
return null;
mGlobalSettings = new Imps.ProviderSettings.QueryMap(cursor, contentResolver, Imps.ProviderSettings.PROVIDER_ID_FOR_GLOBAL_SETTINGS, true, mHandler);
}
return mGlobalSettings;
}
@Override
public void onCreate() {
debug("ImService started");
final String prev = Debug.getTrail(this, SERVICE_CREATE_TRAIL_KEY);
if (prev != null)
Debug.recordTrail(this, PREV_SERVICE_CREATE_TRAIL_TAG, prev);
Debug.recordTrail(this, SERVICE_CREATE_TRAIL_KEY, new Date());
final String prevConnections = Debug.getTrail(this, CONNECTIONS_TRAIL_TAG);
if (prevConnections != null)
Debug.recordTrail(this, PREV_CONNECTIONS_TRAIL_TAG, prevConnections);
Debug.recordTrail(this, CONNECTIONS_TRAIL_TAG, "0");
mConnections = new Hashtable<String, ImConnectionAdapter>();
mHandler = new Handler();
Debug.onServiceStart();
PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "IM_WAKELOCK");
// Clear all account statii to logged-out, since we just got started and we don't want
// leftovers from any previous crash.
clearConnectionStatii();
mStatusBarNotifier = new StatusBarNotifier(this);
mServiceHandler = new ServiceHandler();
//mSettingsMonitor = new SettingsMonitor();
/*
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(ConnectivityManager.ACTION_BACKGROUND_DATA_SETTING_CHANGED);
registerReceiver(mSettingsMonitor, intentFilter);
*/
// setBackgroundData(ImApp.getApplication().isNetworkAvailableAndConnected());
mPluginHelper = ImPluginHelper.getInstance(this);
mPluginHelper.loadAvailablePlugins();
// Have the heartbeat start autoLogin, unless onStart turns this off
mNeedCheckAutoLogin = true;
HeartbeatService.startBeating(getApplicationContext());
}
private void connectToCacheWord ()
{
mCacheWord = new CacheWordActivityHandler(this, (ICacheWordSubscriber)this);
mCacheWord.connectToService();
}
private void startForegroundCompat() {
mNotifyManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);;
mNotifyBuilder = new NotificationCompat.Builder(this)
.setContentTitle(getString(R.string.app_name))
.setSmallIcon(R.drawable.notify_chatsecure);
// note.setOnlyAlertOnce(true);
mNotifyBuilder.setOngoing(true);
mNotifyBuilder.setWhen( System.currentTimeMillis() );
Intent notificationIntent = new Intent(this, NewChatActivity.class);
PendingIntent launchIntent = PendingIntent.getActivity(getApplicationContext(), 0, notificationIntent, 0);
mNotifyBuilder.setContentIntent(launchIntent);
mNotifyBuilder.setContentText(getString(R.string.app_unlocked));
startForeground(notifyId, mNotifyBuilder.build());
}
public void sendHeartbeat() {
Debug.onHeartbeat();
try {
if (mNeedCheckAutoLogin && mNetworkState != NetworkConnectivityListener.State.NOT_CONNECTED) {
debug("autoLogin from heartbeat");
mNeedCheckAutoLogin = false;
autoLogin();
}
mHeartbeatInterval = getGlobalSettings().getHeartbeatInterval();
debug("heartbeat interval: " + mHeartbeatInterval);
for (ImConnectionAdapter conn : mConnections.values())
{
conn.sendHeartbeat();
}
} finally {
return;
}
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
//if the service restarted, then we need to reconnect/reinit to cacheword
if ((flags & START_FLAG_REDELIVERY)!=0) // if crash restart...
{
if (ImApp.mUsingCacheword)
connectToCacheWord();
else
{
if (openEncryptedStores(null, false)) {
try
{
ChatFileStore.initWithoutPassword(this);
}
catch (Exception e)
{
Log.d(ImApp.LOG_TAG,"unable to mount VFS store"); //but let's not crash the whole app right now
}
} else {
connectToCacheWord(); //first time setup
}
}
}
if (intent != null)
{
if (HeartbeatService.HEARTBEAT_ACTION.equals(intent.getAction())) {
// Log.d(TAG, "HEARTBEAT");
if (!mWakeLock.isHeld())
{
try {
mWakeLock.acquire();
sendHeartbeat();
} finally {
mWakeLock.release();
}
}
return START_REDELIVER_INTENT;
}
if (HeartbeatService.NETWORK_STATE_ACTION.equals(intent.getAction())) {
NetworkInfo networkInfo = (NetworkInfo) intent
.getParcelableExtra(HeartbeatService.NETWORK_INFO_EXTRA);
NetworkConnectivityListener.State networkState = State.values()[intent.getIntExtra(HeartbeatService.NETWORK_STATE_EXTRA, 0)];
if (!mWakeLock.isHeld())
{
try {
mWakeLock.acquire();
networkStateChanged(networkInfo, networkState);
} finally {
mWakeLock.release();
}
}
else
{
networkStateChanged(networkInfo, networkState);
}
return START_REDELIVER_INTENT;
}
if (intent.hasExtra(ImServiceConstants.EXTRA_CHECK_AUTO_LOGIN))
mNeedCheckAutoLogin = intent.getBooleanExtra(ImServiceConstants.EXTRA_CHECK_AUTO_LOGIN,
false);
}
debug("ImService.onStart, checkAutoLogin=" + mNeedCheckAutoLogin + " intent =" + intent
+ " startId =" + startId);
// Check and login accounts if network is ready, otherwise it's checked
// when the network becomes available.
if (mNeedCheckAutoLogin && mNetworkState != NetworkConnectivityListener.State.NOT_CONNECTED) {
mNeedCheckAutoLogin = false;
autoLogin();
}
return START_STICKY;
}
@Override
public void onLowMemory() {
}
@Override
public void onTrimMemory(int level) {
/**
* TRIM_MEMORY_COMPLETE, TRIM_MEMORY_MODERATE, TRIM_MEMORY_BACKGROUND,
TRIM_MEMORY_UI_HIDDEN, TRIM_MEMORY_RUNNING_CRITICAL, TRIM_MEMORY_RUNNING_LOW, or
TRIM_MEMORY_RUNNING_MODERATE.
*/
switch (level)
{
case TRIM_MEMORY_BACKGROUND:
case TRIM_MEMORY_UI_HIDDEN:
Log.w(TAG,"in the background/no UI, so we should be efficient");
return;
case TRIM_MEMORY_RUNNING_LOW:
case TRIM_MEMORY_RUNNING_CRITICAL:
Log.w(TAG,"memory is low or critical");
return;
}
}
private void clearConnectionStatii() {
ContentResolver cr = getContentResolver();
ContentValues values = new ContentValues(2);
values.put(Imps.AccountStatus.PRESENCE_STATUS, Imps.Presence.OFFLINE);
values.put(Imps.AccountStatus.CONNECTION_STATUS, Imps.ConnectionStatus.OFFLINE);
try
{
//insert on the "account_status" uri actually replaces the existing value
cr.update(Imps.AccountStatus.CONTENT_URI, values, null, null);
}
catch (Exception e)
{
//this can throw NPE on restart sometimes if database has not been unlocked
debug("database is not unlocked yet. caught NPE from mDbHelper in ImpsProvider");
}
}
private boolean autoLogin() {
// Try empty passphrase. We can't autologin if this fails.
if (!Imps.setEmptyPassphrase(this, true)) {
debug("Cannot autologin with non-empty passphrase");
return false;
}
if (!mConnections.isEmpty()) {
// This can happen because the UI process may be restarted and may think that we need
// to autologin, while we (the Service process) are already up.
debug("Got autoLogin request, but we have one or more connections");
return false;
}
debug("Scanning accounts and login automatically");
ContentResolver resolver = getContentResolver();
String where = Imps.Account.KEEP_SIGNED_IN + "=1 AND " + Imps.Account.ACTIVE + "=1";
Cursor cursor = resolver.query(Imps.Account.CONTENT_URI, ACCOUNT_PROJECTION, where, null,
null);
if (cursor == null) {
Log.w(TAG, "Can't query account!");
return false;
}
while (cursor.moveToNext()) {
long accountId = cursor.getLong(ACCOUNT_ID_COLUMN);
long providerId = cursor.getLong(ACCOUNT_PROVIDER_COLUMN);
IImConnection conn = do_createConnection(providerId, accountId);
try
{
if (conn.getState() != ImConnection.LOGGED_IN)
{
try {
conn.login(null, true, true);
} catch (RemoteException e) {
Log.w(TAG, "Logging error while automatically login!");
}
}
}
catch (Exception e){
Log.d(ImApp.LOG_TAG,"error auto logging into ImConnection",e);
}
}
cursor.close();
return true;
}
private Map<String, String> loadProviderSettings(long providerId) {
ContentResolver cr = getContentResolver();
Map<String, String> settings = Imps.ProviderSettings.queryProviderSettings(cr, providerId);
// NetworkInfo networkInfo = mNetworkConnectivityListener.getNetworkInfo();
// Insert a fake msisdn on emulator. We don't need this on device
// because the mobile network will take care of it.
// if ("1".equals(SystemProperties.get("ro.kernel.qemu"))) {
/*
if (false) {
settings.put(ImpsConfigNames.MSISDN, "15555218135");
} else if (networkInfo != null
&& networkInfo.getType() == ConnectivityManager.TYPE_WIFI) {
if (!TextUtils.isEmpty(settings.get(ImpsConfigNames.SMS_ADDR))) {
// Send authentication through sms if SMS data channel is
// supported and WiFi is used.
settings.put(ImpsConfigNames.SMS_AUTH, "true");
settings.put(ImpsConfigNames.SECURE_LOGIN, "false");
} else {
// Wi-Fi network won't insert a MSISDN, we should get from the SIM
// card. Assume we can always get the correct MSISDN from SIM, otherwise,
// the sign in would fail and an error message should be shown to warn
// the user to contact their operator.
String msisdn = ""; // TODO TelephonyManager.getDefault().getLine1Number();
if (TextUtils.isEmpty(msisdn)) {
Log.w(TAG, "Can not read MSISDN from SIM, use a fake one."
+ " SMS related feature won't work.");
msisdn = "15555218135";
}
settings.put(ImpsConfigNames.MSISDN, msisdn);
}
}*/
return settings;
}
@Override
public void onDestroy() {
Debug.recordTrail(this, SERVICE_DESTROY_TRAIL_TAG, new Date());
if (mCacheWord != null)
mCacheWord.disconnect();
HeartbeatService.stopBeating(getApplicationContext());
Log.w(TAG, "ImService stopped.");
for (ImConnectionAdapter conn : mConnections.values()) {
conn.logout();
}
if (mUseForeground)
stopForeground(true);
if (mGlobalSettings != null)
mGlobalSettings.close();
}
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
public void showToast(CharSequence text, int duration) {
Message msg = Message.obtain(mServiceHandler, EVENT_SHOW_TOAST, duration, 0, text);
msg.sendToTarget();
}
public StatusBarNotifier getStatusBarNotifier() {
return mStatusBarNotifier;
}
public OtrChatManager getOtrChatManager() {
return initOtrChatManager();
}
public void scheduleReconnect(long delay) {
if (!isNetworkAvailable()) {
// Don't schedule reconnect if no network available. We will try to
// reconnect when network state become CONNECTED.
return;
}
mServiceHandler.postDelayed(new Runnable() {
public void run() {
reestablishConnections();
}
}, delay);
}
private boolean mUseForeground = false;
private IImConnection do_createConnection(long providerId, long accountId) {
//make sure OTR is init'd before you create your first connection
initOtrChatManager();
QueryMap gSettings = getGlobalSettings();
if (gSettings == null)
return null;
if (mConnections.size() == 0)
{
mUseForeground = gSettings.getUseForegroundPriority();
if (mUseForeground)
startForegroundCompat();
}
Map<String, String> settings = loadProviderSettings(providerId);
ConnectionFactory factory = ConnectionFactory.getInstance();
try {
ImConnection conn = factory.createConnection(settings, this);
conn.initUser(providerId, accountId);
ImConnectionAdapter imConnectionAdapter =
new ImConnectionAdapter(providerId, accountId, conn, this);
ContentResolver contentResolver = getContentResolver();
Cursor cursor = contentResolver.query(Imps.ProviderSettings.CONTENT_URI,new String[] {Imps.ProviderSettings.NAME, Imps.ProviderSettings.VALUE},Imps.ProviderSettings.PROVIDER + "=?",new String[] { Long.toString(providerId)},null);
if (cursor == null)
throw new ImException ("unable to query the provider settings");
Imps.ProviderSettings.QueryMap providerSettings = new Imps.ProviderSettings.QueryMap(
cursor, contentResolver, providerId, false, null);
String userName = Imps.Account.getUserName(contentResolver, accountId);
String domain = providerSettings.getDomain();
providerSettings.close();
mConnections.put(userName + '@' + domain,imConnectionAdapter);
Debug.recordTrail(this, CONNECTIONS_TRAIL_TAG, "" + mConnections.size());
synchronized (mRemoteListeners)
{
try
{
final int N = mRemoteListeners.beginBroadcast();
for (int i = 0; i < N; i++) {
IConnectionCreationListener listener = mRemoteListeners.getBroadcastItem(i);
try {
listener.onConnectionCreated(imConnectionAdapter);
} catch (RemoteException e) {
// The RemoteCallbackList will take care of removing the
// dead listeners.
}
}
}
finally
{
mRemoteListeners.finishBroadcast();
}
}
return imConnectionAdapter;
} catch (ImException e) {
debug("Error creating connection", e);
return null;
}
}
void removeConnection(ImConnectionAdapter connection) {
mConnections.remove(connection);
if (mConnections.size() == 0)
if (getGlobalSettings().getUseForegroundPriority())
stopForeground(true);
}
boolean isNetworkAvailable ()
{
return mNetworkState == NetworkConnectivityListener.State.CONNECTED;
}
void networkStateChanged(NetworkInfo networkInfo, NetworkConnectivityListener.State networkState) {
mNetworkType = networkInfo != null ? networkInfo.getType() : -1;
debug("networkStateChanged: type=" + networkInfo + " state=" + networkState);
if (mNetworkState != networkState) {
mNetworkState = networkState;
for (ImConnectionAdapter conn : mConnections.values())
conn.networkTypeChanged();
//update the notification
if (mNotifyBuilder != null)
{
String message = "";
if (!isNetworkAvailable())
{
message = getString(R.string.error_suspended_connection);
mNotifyBuilder.setSmallIcon(R.drawable.notify_chatsecure_offline);
}
else
{
message = getString(R.string.app_unlocked);
mNotifyBuilder.setSmallIcon(R.drawable.notify_chatsecure);
}
mNotifyBuilder.setContentText(message);
// Because the ID remains unchanged, the existing notification is
// updated.
mNotifyManager.notify(
notifyId,
mNotifyBuilder.build());
}
}
if (isNetworkAvailable())
{
boolean reConnd = reestablishConnections();
if (!reConnd)
{
if (mNeedCheckAutoLogin) {
mNeedCheckAutoLogin = false;
autoLogin();
}
}
}
else
{
suspendConnections();
}
}
// package private for inner class access
boolean reestablishConnections() {
if (!isNetworkAvailable()) {
return false;
}
for (ImConnectionAdapter conn : mConnections.values()) {
int connState = conn.getState();
if (connState == ImConnection.SUSPENDED) {
conn.reestablishSession();
}
}
return mConnections.values().size() > 0;
}
private void suspendConnections() {
for (ImConnectionAdapter conn : mConnections.values()) {
if (conn.getState() == ImConnection.LOGGED_IN || conn.getState() == ImConnection.LOGGING_IN) {
conn.suspend();
}
}
}
public ImConnectionAdapter getConnection(String username) {
return mConnections.get(username);
}
private final IRemoteImService.Stub mBinder = new IRemoteImService.Stub() {
@Override
public List<ImPluginInfo> getAllPlugins() {
return new ArrayList<ImPluginInfo>(mPluginHelper.getPluginsInfo());
}
@Override
public void addConnectionCreatedListener(IConnectionCreationListener listener) {
if (listener != null) {
mRemoteListeners.register(listener);
}
}
@Override
public void removeConnectionCreatedListener(IConnectionCreationListener listener) {
if (listener != null) {
mRemoteListeners.unregister(listener);
}
}
@Override
public IImConnection createConnection(long providerId, long accountId) {
return RemoteImService.this.do_createConnection(providerId, accountId);
}
@Override
public List getActiveConnections() {
ArrayList<IBinder> result = new ArrayList<IBinder>(mConnections.size());
for (IImConnection conn : mConnections.values()) {
result.add(conn.asBinder());
}
return result;
}
@Override
public void dismissNotifications(long providerId) {
mStatusBarNotifier.dismissNotifications(providerId);
}
@Override
public void dismissChatNotification(long providerId, String username) {
mStatusBarNotifier.dismissChatNotification(providerId, username);
}
@Override
public boolean unlockOtrStore (String password)
{
OtrAndroidKeyManagerImpl.setKeyStorePassword(password);
return true;
}
@Override
public IOtrKeyManager getOtrKeyManager()
{
OtrAndroidKeyManagerImpl keyMgr = OtrAndroidKeyManagerImpl.getInstance(RemoteImService.this);
return keyMgr;
}
@Override
public void setKillProcessOnStop (boolean killProcessOnStop)
{
mKillProcessOnStop = killProcessOnStop;
}
@Override
public void enableDebugLogging (boolean debug)
{
Debug.DEBUG_ENABLED = debug;
}
@Override
public void updateStateFromSettings() throws RemoteException {
updateOtrPolicy ();
}
};
private boolean mKillProcessOnStop = false;
/*
//the concept of "background data is deprecated from Android
// the only thing that matters is checking if Network is available and connected
private final class SettingsMonitor extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (ConnectivityManager.ACTION_BACKGROUND_DATA_SETTING_CHANGED.equals(action)) {
ConnectivityManager manager = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
setBackgroundData(manager.getBackgroundDataSetting());
handleBackgroundDataSettingChange();
}
}
}
*/
private final class ServiceHandler extends Handler {
public ServiceHandler() {
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case EVENT_SHOW_TOAST:
Toast.makeText(RemoteImService.this, (CharSequence) msg.obj, msg.arg1).show();
break;
default:
}
}
}
@Override
public void sessionStatusChanged(SessionID sessionID) {
//this method does nothing!
// Log.d(TAG,"OTR session status changed: " + sessionID.getRemoteUserId());
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
@Override
public void onTaskRemoved(Intent rootIntent) {
Debug.recordTrail(this, LAST_SWIPE_TRAIL_TAG, new Date());
Intent intent = new Intent(this, DummyActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
if (Build.VERSION.SDK_INT >= 11)
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(intent);
}
@Override
public void onCacheWordLocked() {
//do nothing here?
}
@Override
public void onCacheWordOpened() {
byte[] encryptionKey = mCacheWord.getEncryptionKey();
openEncryptedStores(encryptionKey, true);
// this is no longer configurable
// int defaultTimeout = 60 * Integer.parseInt(mPrefs.getString("pref_cacheword_timeout",ImApp.DEFAULT_TIMEOUT_CACHEWORD));
// mCacheWord.setTimeoutSeconds(defaultTimeout);
ChatFileStore.init(this, encryptionKey);
}
@Override
public void onCacheWordUninitialized() {
// TODO Auto-generated method stub
}
private boolean openEncryptedStores(byte[] key, boolean allowCreate) {
String pkey = (key != null) ? new String(SQLCipherOpenHelper.encodeRawKey(key)) : "";
OtrAndroidKeyManagerImpl.setKeyStorePassword(pkey);
if (cursorUnlocked(pkey, allowCreate)) {
return true;
} else {
return false;
}
}
@SuppressWarnings("deprecation")
private boolean cursorUnlocked(String pKey, boolean allowCreate) {
try {
Uri uri = Imps.Provider.CONTENT_URI_WITH_ACCOUNT;
Builder builder = uri.buildUpon();
if (pKey != null)
builder.appendQueryParameter(ImApp.CACHEWORD_PASSWORD_KEY, pKey);
if (!allowCreate)
builder = builder.appendQueryParameter(ImApp.NO_CREATE_KEY, "1");
uri = builder.build();
String[] PROVIDER_PROJECTION = { Imps.Provider._ID};
ContentResolver contentResolver = getContentResolver();
Cursor providerCursor = contentResolver.query(uri,PROVIDER_PROJECTION, Imps.Provider.CATEGORY + "=?" /* selection */,
new String[] { ImApp.IMPS_CATEGORY } /* selection args */,
Imps.Provider.DEFAULT_SORT_ORDER);
if (providerCursor != null)
{
ImPluginHelper.getInstance(this).loadAvailablePlugins();
providerCursor.moveToFirst();
providerCursor.close();
return true;
}
else
{
return false;
}
} catch (Exception e) {
// Only complain if we thought this password should succeed
if (allowCreate) {
Log.e(ImApp.LOG_TAG, e.getMessage(), e);
}
// needs to be unlocked
return false;
}
}
}