RemoteImService.java
上传用户:szyujian
上传日期:2016-09-20
资源大小:320k
文件大小:20k
源码类别:

android开发

开发平台:

C/C++

  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. package com.android.im.service;
  18. import java.lang.reflect.InvocationTargetException;
  19. import java.lang.reflect.Method;
  20. import java.util.ArrayList;
  21. import java.util.HashMap;
  22. import java.util.List;
  23. import java.util.Map;
  24. import java.util.Vector;
  25. import android.app.AlarmManager;
  26. import android.app.PendingIntent;
  27. import android.app.Service;
  28. import android.content.ContentResolver;
  29. import android.content.ContentUris;
  30. import android.content.ContentValues;
  31. import android.content.Intent;
  32. import android.content.pm.PackageManager;
  33. import android.content.pm.ResolveInfo;
  34. import android.content.pm.ServiceInfo;
  35. import android.database.Cursor;
  36. import android.net.ConnectivityManager;
  37. import android.net.NetworkConnectivityListener;
  38. import android.net.Uri;
  39. import android.net.NetworkConnectivityListener.State;
  40. import android.net.NetworkInfo;
  41. import android.os.Bundle;
  42. import android.os.RemoteException;
  43. import android.os.Handler;
  44. import android.os.IBinder;
  45. import android.os.Message;
  46. import android.os.SystemClock;
  47. import android.os.SystemProperties;
  48. import android.provider.Im;
  49. import android.telephony.TelephonyManager;
  50. import android.text.TextUtils;
  51. import android.util.Log;
  52. import android.widget.Toast;
  53. import com.android.im.IConnectionCreationListener;
  54. import com.android.im.IImConnection;
  55. import com.android.im.IRemoteImService;
  56. import com.android.im.engine.ConnectionFactory;
  57. import com.android.im.engine.ImConnection;
  58. import com.android.im.engine.ImException;
  59. import com.android.im.imps.ImpsConnectionConfig;
  60. import com.android.im.imps.TcpCirAlarmService;
  61. import com.android.im.imps.ImpsConnectionConfig.CirMethod;
  62. import com.android.im.plugin.IImPlugin;
  63. import com.android.im.plugin.ImConfigNames;
  64. import com.android.im.plugin.ImPluginConstants;
  65. import com.android.im.plugin.ImPluginInfo;
  66. import com.android.im.plugin.ImpsConfigNames;
  67. import dalvik.system.PathClassLoader;
  68. public class RemoteImService extends Service {
  69.     private static final String[] ACCOUNT_PROJECTION = {
  70.         Im.Account._ID,
  71.         Im.Account.PROVIDER,
  72.         Im.Account.USERNAME,
  73.         Im.Account.PASSWORD,
  74.     };
  75.     private static final int ACCOUNT_ID_COLUMN = 0;
  76.     private static final int ACCOUNT_PROVIDER_COLUMN = 1;
  77.     private static final int ACCOUNT_USERNAME_COLUMN = 2;
  78.     private static final int ACCOUNT_PASSOWRD_COLUMN = 3;
  79.     static final String TAG = "ImService";
  80.     private static final int EVENT_SHOW_TOAST = 100;
  81.     private static final int EVENT_NETWORK_STATE_CHANGED = 200;
  82.     private StatusBarNotifier mStatusBarNotifier;
  83.     private Handler mServiceHandler;
  84.     NetworkConnectivityListener mNetworkConnectivityListener;
  85.     private int mNetworkType;
  86.     private boolean mNeedCheckAutoLogin;
  87.     Vector<ImConnectionAdapter> mConnections;
  88.     private PendingIntent mTcpCirAlarmSender;
  89.     private HashMap<Long, ImPluginInfo> mPlugins;
  90.     public RemoteImService() {
  91.         mConnections = new Vector<ImConnectionAdapter>();
  92.         mPlugins = new HashMap<Long, ImPluginInfo>();
  93.     }
  94.     @Override
  95.     public void onCreate() {
  96.         Log.d(TAG, "ImService started");
  97.         mStatusBarNotifier = new StatusBarNotifier(this);
  98.         mServiceHandler = new ServiceHandler();
  99.         mNetworkConnectivityListener = new NetworkConnectivityListener();
  100.         mNetworkConnectivityListener.registerHandler(mServiceHandler, EVENT_NETWORK_STATE_CHANGED);
  101.         mNetworkConnectivityListener.startListening(this);
  102.         findAvaiablePlugins();
  103.     }
  104.     private void findAvaiablePlugins() {
  105.         PackageManager pm = getPackageManager();
  106.         List<ResolveInfo> plugins = pm.queryIntentServices(
  107.                 new Intent(ImPluginConstants.PLUGIN_ACTION_NAME), PackageManager.GET_META_DATA);
  108.         for (ResolveInfo info : plugins) {
  109.             Log.d(TAG, "Found plugin " + info);
  110.             ServiceInfo serviceInfo = info.serviceInfo;
  111.             if (serviceInfo == null) {
  112.                 Log.e(TAG, "Ignore bad IM plugin: " + info);
  113.                 continue;
  114.             }
  115.             String providerName = null;
  116.             String providerFullName = null;
  117.             String signUpUrl = null;
  118.             Bundle metaData = serviceInfo.metaData;
  119.             if (metaData != null) {
  120.                 providerName = metaData.getString(ImPluginConstants.METADATA_PROVIDER_NAME);
  121.                 providerFullName = metaData.getString(ImPluginConstants.METADATA_PROVIDER_FULL_NAME);
  122.                 signUpUrl = metaData.getString(ImPluginConstants.METADATA_SIGN_UP_URL);
  123.             }
  124.             if (TextUtils.isEmpty(providerName) || TextUtils.isEmpty(providerFullName)) {
  125.                 Log.e(TAG, "Ignore bad IM plugin: " + info + ". Lack of required meta data");
  126.                 continue;
  127.             }
  128.             ImPluginInfo pluginInfo = new ImPluginInfo(providerName, serviceInfo.packageName,
  129.                     serviceInfo.name, serviceInfo.applicationInfo.sourceDir);
  130.             Map<String, String> config = loadProviderConfigFromPlugin(pluginInfo);
  131.             if (config == null) {
  132.                 Log.e(TAG, "Ignore bad IM plugin");
  133.                 break;
  134.             }
  135.             config.put(ImConfigNames.PLUGIN_PATH, pluginInfo.mSrcPath);
  136.             config.put(ImConfigNames.PLUGIN_CLASS, pluginInfo.mClassName);
  137.             long providerId = updateProviderDb(providerName, providerFullName, signUpUrl,
  138.                     config);
  139.             mPlugins.put(providerId, pluginInfo);
  140.         }
  141.     }
  142.     private Map<String, String> loadProviderConfigFromPlugin(ImPluginInfo pluginInfo) {
  143.         // XXX Load the plug-in implementation directly from the apk rather than
  144.         // binding to the service and call through IPC Binder API. This is much
  145.         // more effective since we don't need to start the service in other
  146.         // process. We can not run the plug-in service in the same process as a
  147.         // local service because that the interface is defined in a shared
  148.         // library in order to compile the plug-in separately. In this case, the
  149.         // interface will be loaded by two class loader separately and a
  150.         // ClassCastException will be thrown if we cast the binder to the
  151.         // interface.
  152.         PathClassLoader loader = new PathClassLoader(pluginInfo.mSrcPath, getClassLoader());
  153.         try {
  154.             Class cls = loader.loadClass(pluginInfo.mClassName);
  155.             Method m = cls.getMethod("onBind", Intent.class);
  156.             IImPlugin plugin = (IImPlugin)m.invoke(cls.newInstance(), new Object[]{null});
  157.             return plugin.getProviderConfig();
  158.         } catch (ClassNotFoundException e) {
  159.             Log.e(TAG, "Could not find plugin class", e);
  160.         } catch (IllegalAccessException e) {
  161.             Log.e(TAG, "Could not create plugin instance", e);
  162.         } catch (InstantiationException e) {
  163.             Log.e(TAG, "Could not create plugin instance", e);
  164.         } catch (SecurityException e) {
  165.             Log.e(TAG, "Could not load config from the plugin", e);
  166.         } catch (NoSuchMethodException e) {
  167.             Log.e(TAG, "Could not load config from the plugin", e);
  168.         } catch (IllegalArgumentException e) {
  169.             Log.e(TAG, "Could not load config from the plugin", e);
  170.         } catch (InvocationTargetException e) {
  171.             Log.e(TAG, "Could not load config from the plugin", e);
  172.         } catch (RemoteException e) {
  173.             Log.e(TAG, "Could not load config from the plugin", e);
  174.         }
  175.         return null;
  176.     }
  177.     private long updateProviderDb(String providerName, String providerFullName,
  178.             String signUpUrl, Map<String, String> config) {
  179.         long providerId;
  180.         ContentResolver cr = getContentResolver();
  181.         String where = Im.Provider.NAME + "=?";
  182.         String[] selectionArgs = new String[]{providerName};
  183.         Cursor c = cr.query(Im.Provider.CONTENT_URI, null, where, selectionArgs, null);
  184.         if (c == null) {
  185.             return -1;
  186.         }
  187.         if (c.moveToFirst()) {
  188.             providerId = c.getLong(c.getColumnIndexOrThrow(Im.Provider._ID));
  189.         } else {
  190.             ContentValues values = new ContentValues(3);
  191.             values.put(Im.Provider.NAME, providerName);
  192.             values.put(Im.Provider.FULLNAME, providerFullName);
  193.             values.put(Im.Provider.SIGNUP_URL, signUpUrl);
  194.             Uri result = cr.insert(Im.Provider.CONTENT_URI, values);
  195.             providerId = ContentUris.parseId(result);
  196.             ContentValues[] settingValues = new ContentValues[config.size()];
  197.             int index = 0;
  198.             for (Map.Entry<String, String> entry : config.entrySet()) {
  199.                 ContentValues settingValue = new ContentValues();
  200.                 settingValue.put(Im.ProviderSettings.PROVIDER, providerId);
  201.                 settingValue.put(Im.ProviderSettings.NAME, entry.getKey());
  202.                 settingValue.put(Im.ProviderSettings.VALUE, entry.getValue());
  203.                 settingValues[index++] = settingValue;
  204.             }
  205.             cr.bulkInsert(Im.ProviderSettings.CONTENT_URI, settingValues);
  206.         }
  207.         c.close();
  208.         return providerId;
  209.     }
  210.     public void startTcpCirAlarm() {
  211.         if (mTcpCirAlarmSender != null) {
  212.             return;
  213.         }
  214.         mTcpCirAlarmSender = PendingIntent.getService(this, 0,
  215.                 new Intent(this, TcpCirAlarmService.class), 0);
  216.         long firstTime = SystemClock.elapsedRealtime();
  217.         AlarmManager am = (AlarmManager)getSystemService(ALARM_SERVICE);
  218.         am.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, firstTime,
  219.                 TcpCirAlarmService.INTERVAL, mTcpCirAlarmSender);
  220.     }
  221.     public void stopTcpCirAlarm() {
  222.         if (mTcpCirAlarmSender != null) {
  223.             AlarmManager am = (AlarmManager)getSystemService(ALARM_SERVICE);
  224.             am.cancel(mTcpCirAlarmSender);
  225.             mTcpCirAlarmSender = null;
  226.         }
  227.     }
  228.     @Override
  229.     public void onStart(Intent intent, int startId) {
  230.         super.onStart(intent, startId);
  231.         mNeedCheckAutoLogin = intent.getBooleanExtra(ImServiceConstants.EXTRA_CHECK_AUTO_LOGIN, false);
  232.         Log.d(TAG, "ImService.onStart, checkAutoLogin=" + mNeedCheckAutoLogin);
  233.         // Check and login accounts if network is ready, otherwise it's checked
  234.         // when the network becomes available.
  235.         if (mNeedCheckAutoLogin &&
  236.                 mNetworkConnectivityListener.getState() == State.CONNECTED) {
  237.             mNeedCheckAutoLogin = false;
  238.             autoLogin();
  239.         }
  240.     }
  241.     private void autoLogin() {
  242.         Log.d(TAG, "Scaning accounts and login automatically");
  243.         ContentResolver resolver = getContentResolver();
  244.         String where = Im.Account.KEEP_SIGNED_IN + "=1 AND " + Im.Account.ACTIVE + "=1";
  245.         Cursor cursor = resolver.query(Im.Account.CONTENT_URI,
  246.                 ACCOUNT_PROJECTION, where, null, null);
  247.         if (cursor == null) {
  248.             Log.w(TAG, "Can't query account!");
  249.             return;
  250.         }
  251.         while (cursor.moveToNext()) {
  252.             long accountId = cursor.getLong(ACCOUNT_ID_COLUMN);
  253.             long providerId = cursor.getLong(ACCOUNT_PROVIDER_COLUMN);
  254.             String username = cursor.getString(ACCOUNT_USERNAME_COLUMN);
  255.             String password = cursor.getString(ACCOUNT_PASSOWRD_COLUMN);
  256.             IImConnection conn = createConnection(providerId);
  257.             try {
  258.                 conn.login(accountId, username, password, true);
  259.             } catch (RemoteException e) {
  260.                 Log.w(TAG, "Logging error while automatically login!");
  261.             }
  262.         }
  263.         cursor.close();
  264.     }
  265.     private Map<String, String> loadProviderSettings(long providerId) {
  266.         ContentResolver cr = getContentResolver();
  267.         Map<String, String> settings = Im.ProviderSettings.queryProviderSettings(cr, providerId);
  268.         NetworkInfo networkInfo = mNetworkConnectivityListener.getNetworkInfo();
  269.         // Insert a fake msisdn on emulator. We don't need this on device
  270.         // because the mobile network will take care of it.
  271.         if ("1".equals(SystemProperties.get("ro.kernel.qemu"))) {
  272.             settings.put(ImpsConfigNames.MSISDN, "1231231234");
  273.         } else if (networkInfo != null && networkInfo.getType() == ConnectivityManager.TYPE_WIFI) {
  274.             // Wi-Fi network won't insert a MSISDN, we should get from the SIM
  275.             // card. Assume we can always get the correct MSISDN from SIM, otherwise,
  276.             // the sign in would fail and an error message should be shown to warn
  277.             // the user to contact their operator.
  278.             String msisdn = TelephonyManager.getDefault().getLine1Number();
  279.             if (!TextUtils.isEmpty(msisdn)) {
  280.                 settings.put(ImpsConfigNames.MSISDN, msisdn);
  281.             } else {
  282.                 // TODO: This should be removed. We can't fetch phone number from
  283.                 // the test T-Mobile SIMs. Use a fake phone number so that we can
  284.                 // work with our test SIMs right now. This can't happen with T-Mobile
  285.                 // production SIMs
  286.                 Log.w(TAG, "Can not get phone number from SIM, use a fake one");
  287.                 settings.put(ImpsConfigNames.MSISDN, "1231231234");
  288.             }
  289.         }
  290.         return settings;
  291.     }
  292.     @Override
  293.     public void onDestroy() {
  294.         Log.w(TAG, "ImService stopped.");
  295.         for (ImConnectionAdapter conn : mConnections) {
  296.             conn.logout();
  297.         }
  298.         mNetworkConnectivityListener.unregisterHandler(mServiceHandler);
  299.         mNetworkConnectivityListener.stopListening();
  300.         mNetworkConnectivityListener = null;
  301.         stopTcpCirAlarm();
  302.     }
  303.     @Override
  304.     public IBinder onBind(Intent intent) {
  305.         return mBinder;
  306.     }
  307.     public void showToast(CharSequence text, int duration) {
  308.         Message msg = Message.obtain(mServiceHandler, EVENT_SHOW_TOAST, duration, 0, text);
  309.         msg.sendToTarget();
  310.     }
  311.     public StatusBarNotifier getStatusBarNotifier() {
  312.         return mStatusBarNotifier;
  313.     }
  314.     public void scheduleReconnect(long delay) {
  315.         if (!isNetworkAvailable()) {
  316.             // Don't schedule reconnect if no network available. We will try to
  317.             // reconnect when network state become CONNECTED.
  318.             return;
  319.         }
  320.         mServiceHandler.postDelayed(new Runnable() {
  321.             public void run() {
  322.                 reestablishConnections();
  323.             }
  324.         }, delay);
  325.     }
  326.     IImConnection createConnection(long providerId) {
  327.         Map<String, String> settings = loadProviderSettings(providerId);
  328.         String protocol = settings.get(ImConfigNames.PROTOCOL_NAME);
  329.         if(!"IMPS".equals(protocol)) {
  330.             Log.e(TAG, "Unsupported protocol: " + protocol);
  331.             return null;
  332.         }
  333.         ImpsConnectionConfig config = new ImpsConnectionConfig(settings);
  334.         ConnectionFactory factory = ConnectionFactory.getInstance();
  335.         try {
  336.             ImConnection conn = factory.createConnection(config);
  337.             ImConnectionAdapter result = new ImConnectionAdapter(providerId,
  338.                     conn, this);
  339.             mConnections.add(result);
  340.             if (config.getCirChannelBinding() == CirMethod.STCP) {
  341.                 startTcpCirAlarm();
  342.             }
  343.             mListenerMgr.notifyConnectionCreated(result);
  344.             return result;
  345.         } catch (ImException e) {
  346.             Log.e(TAG, "Error creating connection", e);
  347.             return null;
  348.         }
  349.     }
  350.     void removeConnection(IImConnection connection) {
  351.         mConnections.remove(connection);
  352.     }
  353.     private boolean isNetworkAvailable() {
  354.         return mNetworkConnectivityListener.getState() == State.CONNECTED;
  355.     }
  356.     void networkStateChanged() {
  357.         if (mNetworkConnectivityListener == null) {
  358.             return;
  359.         }
  360.         NetworkInfo networkInfo = mNetworkConnectivityListener.getNetworkInfo();
  361.         NetworkInfo.State state = networkInfo.getState();
  362.         Log.d(TAG, "networkStateChanged:" + state);
  363.         int oldType = mNetworkType;
  364.         mNetworkType = networkInfo.getType();
  365. // Notify the connection that network type has changed. Note that this
  366. // only work for connected connections, we need to reestablish if it's
  367. // suspended.
  368.         if (mNetworkType != oldType
  369.                 && isNetworkAvailable()) {
  370.             for (ImConnectionAdapter conn : mConnections) {
  371.                 conn.networkTypeChanged();
  372.             }
  373.         }
  374.         switch (state) {
  375.             case CONNECTED:
  376.                 if (mNeedCheckAutoLogin) {
  377.                     mNeedCheckAutoLogin = false;
  378.                     autoLogin();
  379.                     break;
  380.                 }
  381.                 reestablishConnections();
  382.                 break;
  383.             case DISCONNECTED:
  384.                 if (!isNetworkAvailable()) {
  385.                     suspendConnections();
  386.                 }
  387.                 break;
  388.         }
  389.     }
  390.     // package private for inner class access
  391.     void reestablishConnections() {
  392.         if (!isNetworkAvailable()) {
  393.             return;
  394.         }
  395.         for (ImConnectionAdapter conn : mConnections) {
  396.             int connState = conn.getState();
  397.             if (connState == ImConnection.SUSPENDED) {
  398.                 conn.reestablishSession();
  399.             }
  400.         }
  401.     }
  402.     private void suspendConnections() {
  403.         for (ImConnectionAdapter conn : mConnections) {
  404.             if (conn.getState() != ImConnection.LOGGED_IN) {
  405.                 continue;
  406.             }
  407.             conn.suspend();
  408.         }
  409.     }
  410.     private final IRemoteImService.Stub mBinder = new IRemoteImService.Stub() {
  411.         public List getAllPlugins() {
  412.             return new ArrayList(mPlugins.values());
  413.         }
  414.         public void addConnectionCreatedListener(IConnectionCreationListener listener) {
  415.             mListenerMgr.addRemoteListener(listener);
  416.         }
  417.         public void removeConnectionCreatedListener(IConnectionCreationListener listener) {
  418.             mListenerMgr.removeRemoteListener(listener);
  419.         }
  420.         public IImConnection createConnection(long providerId) {
  421.             return RemoteImService.this.createConnection(providerId);
  422.         }
  423.         public List getActiveConnections() {
  424.             ArrayList<IBinder> result = new ArrayList<IBinder>(mConnections.size());
  425.             for(IImConnection conn : mConnections) {
  426.                 result.add(conn.asBinder());
  427.             }
  428.             return result;
  429.         }
  430.         public void dismissNotifications(long providerId) {
  431.             mStatusBarNotifier.dismissNotifications(providerId);
  432.         }
  433.     };
  434.     final ConnectionListenerManager mListenerMgr = new ConnectionListenerManager();
  435.     private final static class ConnectionListenerManager
  436.             extends RemoteListenerManager<IConnectionCreationListener> {
  437.         public ConnectionListenerManager(){
  438.         }
  439.         public void notifyConnectionCreated(final ImConnectionAdapter conn) {
  440.             notifyRemoteListeners(new ListenerInvocation<IConnectionCreationListener>() {
  441.                 public void invoke(IConnectionCreationListener remoteListener)
  442.                         throws RemoteException {
  443.                     remoteListener.onConnectionCreated(conn);
  444.                 }
  445.             });
  446.         }
  447.     }
  448.     private final class ServiceHandler extends Handler {
  449.         public ServiceHandler() {
  450.         }
  451.         @Override
  452.         public void handleMessage(Message msg) {
  453.             switch (msg.what) {
  454.                 case EVENT_SHOW_TOAST:
  455.                     Toast.makeText(RemoteImService.this,
  456.                             (CharSequence) msg.obj, msg.arg1).show();
  457.                     break;
  458.                 case EVENT_NETWORK_STATE_CHANGED:
  459.                     networkStateChanged();
  460.                     break;
  461.                 default:
  462.             }
  463.         }
  464.     }
  465. }