FileDocCategorySizeDatePackage
RemoteImService.javaAPI DocAndroid 1.5 API19970Wed May 06 22:42:46 BST 2009com.android.im.service

RemoteImService

public class RemoteImService extends android.app.Service

Fields Summary
private static final String[]
ACCOUNT_PROJECTION
private static final int
ACCOUNT_ID_COLUMN
private static final int
ACCOUNT_PROVIDER_COLUMN
private static final int
ACCOUNT_USERNAME_COLUMN
private static final int
ACCOUNT_PASSOWRD_COLUMN
static final String
TAG
private static final int
EVENT_SHOW_TOAST
private static final int
EVENT_NETWORK_STATE_CHANGED
private StatusBarNotifier
mStatusBarNotifier
private android.os.Handler
mServiceHandler
android.net.NetworkConnectivityListener
mNetworkConnectivityListener
private int
mNetworkType
private boolean
mNeedCheckAutoLogin
private boolean
mBackgroundDataEnabled
private SettingsMonitor
mSettingsMonitor
Vector
mConnections
final android.os.RemoteCallbackList
mRemoteListeners
private HashMap
mPlugins
private final IRemoteImService.Stub
mBinder
Constructors Summary
public RemoteImService()


      
        mConnections = new Vector<ImConnectionAdapter>();
        mPlugins = new HashMap<Long, ImPluginInfo>();
    
Methods Summary
private voidautoLogin()

        Log.d(TAG, "Scaning accounts and login automatically");

        ContentResolver resolver = getContentResolver();

        String where = Im.Account.KEEP_SIGNED_IN + "=1 AND " + Im.Account.ACTIVE + "=1";
        Cursor cursor = resolver.query(Im.Account.CONTENT_URI,
                ACCOUNT_PROJECTION, where, null, null);
        if (cursor == null) {
            Log.w(TAG, "Can't query account!");
            return;
        }
        while (cursor.moveToNext()) {
            long accountId = cursor.getLong(ACCOUNT_ID_COLUMN);
            long providerId = cursor.getLong(ACCOUNT_PROVIDER_COLUMN);
            String username = cursor.getString(ACCOUNT_USERNAME_COLUMN);
            String password = cursor.getString(ACCOUNT_PASSOWRD_COLUMN);

            IImConnection conn = createConnection(providerId);

            try {
                conn.login(accountId, username, password, true);
            } catch (RemoteException e) {
                Log.w(TAG, "Logging error while automatically login!");
            }
        }
        cursor.close();
    
com.android.im.IImConnectioncreateConnection(long providerId)

        Map<String, String> settings = loadProviderSettings(providerId);
        String protocol = settings.get(ImConfigNames.PROTOCOL_NAME);
        if(!"IMPS".equals(protocol)) {
            Log.e(TAG, "Unsupported protocol: " + protocol);
            return null;
        }
        ImpsConnectionConfig config = new ImpsConnectionConfig(settings);
        ConnectionFactory factory = ConnectionFactory.getInstance();
        try {
            ImConnection conn = factory.createConnection(config);
            ImConnectionAdapter result = new ImConnectionAdapter(providerId,
                    conn, this);
            mConnections.add(result);

            final int N = mRemoteListeners.beginBroadcast();
            for (int i = 0; i < N; i++) {
                IConnectionCreationListener listener = mRemoteListeners.getBroadcastItem(i);
                try {
                    listener.onConnectionCreated(result);
                } catch (RemoteException e) {
                    // The RemoteCallbackList will take care of removing the
                    // dead listeners.
                }
            }
            mRemoteListeners.finishBroadcast();
            return result;
        } catch (ImException e) {
            Log.e(TAG, "Error creating connection", e);
            return null;
        }
    
private voidfindAvaiablePlugins()

        PackageManager pm = getPackageManager();
        List<ResolveInfo> plugins = pm.queryIntentServices(
                new Intent(ImPluginConstants.PLUGIN_ACTION_NAME), PackageManager.GET_META_DATA);
        for (ResolveInfo info : plugins) {
            Log.d(TAG, "Found plugin " + info);

            ServiceInfo serviceInfo = info.serviceInfo;
            if (serviceInfo == null) {
                Log.e(TAG, "Ignore bad IM plugin: " + info);
                continue;
            }
            String providerName = null;
            String providerFullName = null;
            String signUpUrl = null;
            Bundle metaData = serviceInfo.metaData;
            if (metaData != null) {
                providerName = metaData.getString(ImPluginConstants.METADATA_PROVIDER_NAME);
                providerFullName = metaData.getString(ImPluginConstants.METADATA_PROVIDER_FULL_NAME);
                signUpUrl = metaData.getString(ImPluginConstants.METADATA_SIGN_UP_URL);
            }
            if (TextUtils.isEmpty(providerName) || TextUtils.isEmpty(providerFullName)) {
                Log.e(TAG, "Ignore bad IM plugin: " + info + ". Lack of required meta data");
                continue;
            }

            ImPluginInfo pluginInfo = new ImPluginInfo(providerName, serviceInfo.packageName,
                    serviceInfo.name, serviceInfo.applicationInfo.sourceDir);

            Map<String, String> config = loadProviderConfigFromPlugin(pluginInfo);
            if (config == null) {
                Log.e(TAG, "Ignore bad IM plugin");
                break;
            }

            config.put(ImConfigNames.PLUGIN_PATH, pluginInfo.mSrcPath);
            config.put(ImConfigNames.PLUGIN_CLASS, pluginInfo.mClassName);
            long providerId = DatabaseUtils.updateProviderDb(getContentResolver(),
                    providerName, providerFullName, signUpUrl, config);
            mPlugins.put(providerId, pluginInfo);
        }
    
public StatusBarNotifiergetStatusBarNotifier()

        return mStatusBarNotifier;
    
voidhandleBackgroundDataSettingChange()

        if (!isBackgroundDataEnabled()) {
            for (ImConnectionAdapter conn : mConnections) {
                conn.logout();
            }
        }
    
private booleanisBackgroundDataEnabled()

        return mBackgroundDataEnabled;
    
private booleanisNetworkAvailable()

        return mNetworkConnectivityListener.getState() == State.CONNECTED;
    
private java.util.MaploadProviderConfigFromPlugin(com.android.im.plugin.ImPluginInfo pluginInfo)

        // XXX Load the plug-in implementation directly from the apk rather than
        // binding to the service and call through IPC Binder API. This is much
        // more effective since we don't need to start the service in other
        // process. We can not run the plug-in service in the same process as a
        // local service because that the interface is defined in a shared
        // library in order to compile the plug-in separately. In this case, the
        // interface will be loaded by two class loader separately and a
        // ClassCastException will be thrown if we cast the binder to the
        // interface.
        PathClassLoader loader = new PathClassLoader(pluginInfo.mSrcPath, getClassLoader());
        try {
            Class cls = loader.loadClass(pluginInfo.mClassName);
            Method m = cls.getMethod("onBind", Intent.class);
            IImPlugin plugin = (IImPlugin)m.invoke(cls.newInstance(), new Object[]{null});
            return plugin.getProviderConfig();
        } catch (ClassNotFoundException e) {
            Log.e(TAG, "Could not find plugin class", e);
        } catch (IllegalAccessException e) {
            Log.e(TAG, "Could not create plugin instance", e);
        } catch (InstantiationException e) {
            Log.e(TAG, "Could not create plugin instance", e);
        } catch (SecurityException e) {
            Log.e(TAG, "Could not load config from the plugin", e);
        } catch (NoSuchMethodException e) {
            Log.e(TAG, "Could not load config from the plugin", e);
        } catch (IllegalArgumentException e) {
            Log.e(TAG, "Could not load config from the plugin", e);
        } catch (InvocationTargetException e) {
            Log.e(TAG, "Could not load config from the plugin", e);
        } catch (RemoteException e) {
            Log.e(TAG, "Could not load config from the plugin", e);
        }
        return null;
    
private java.util.MaploadProviderSettings(long providerId)

        ContentResolver cr = getContentResolver();
        Map<String, String> settings = Im.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"))) {
            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 = 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;
    
voidnetworkStateChanged()

        if (mNetworkConnectivityListener == null) {
            return;
        }
        NetworkInfo networkInfo = mNetworkConnectivityListener.getNetworkInfo();
        NetworkInfo.State state = networkInfo.getState();

        Log.d(TAG, "networkStateChanged:" + state);

        int oldType = mNetworkType;
        mNetworkType = networkInfo.getType();

        // Notify the connection that network type has changed. Note that this
        // only work for connected connections, we need to reestablish if it's
        // suspended.
        if (mNetworkType != oldType
                && isNetworkAvailable()) {
            for (ImConnectionAdapter conn : mConnections) {
                conn.networkTypeChanged();
            }
        }

        switch (state) {
            case CONNECTED:
                if (mNeedCheckAutoLogin) {
                    mNeedCheckAutoLogin = false;
                    autoLogin();
                    break;
                }
                reestablishConnections();
                break;

            case DISCONNECTED:
                if (!isNetworkAvailable()) {
                    suspendConnections();
                }
                break;
        }
    
public android.os.IBinderonBind(android.content.Intent intent)

        return mBinder;
    
public voidonCreate()

        Log.d(TAG, "ImService started");
        mStatusBarNotifier = new StatusBarNotifier(this);
        mServiceHandler = new ServiceHandler();
        mNetworkConnectivityListener = new NetworkConnectivityListener();
        mNetworkConnectivityListener.registerHandler(mServiceHandler, EVENT_NETWORK_STATE_CHANGED);
        mNetworkConnectivityListener.startListening(this);

        mSettingsMonitor = new SettingsMonitor();

        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(ConnectivityManager.ACTION_BACKGROUND_DATA_SETTING_CHANGED);
        registerReceiver(mSettingsMonitor, intentFilter);

        ConnectivityManager manager
            = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
        setBackgroundData(manager.getBackgroundDataSetting());

        findAvaiablePlugins();
        AndroidSystemService.getInstance().initialize(this);
    
public voidonDestroy()

        Log.w(TAG, "ImService stopped.");
        for (ImConnectionAdapter conn : mConnections) {
            conn.logout();
        }

        AndroidSystemService.getInstance().shutdown();

        mNetworkConnectivityListener.unregisterHandler(mServiceHandler);
        mNetworkConnectivityListener.stopListening();
        mNetworkConnectivityListener = null;

        unregisterReceiver(mSettingsMonitor);
    
public voidonStart(android.content.Intent intent, int startId)

        super.onStart(intent, startId);
        mNeedCheckAutoLogin = intent.getBooleanExtra(ImServiceConstants.EXTRA_CHECK_AUTO_LOGIN, false);

        Log.d(TAG, "ImService.onStart, checkAutoLogin=" + mNeedCheckAutoLogin);

        // Check and login accounts if network is ready, otherwise it's checked
        // when the network becomes available.
        if (mNeedCheckAutoLogin &&
                mNetworkConnectivityListener.getState() == State.CONNECTED) {
            mNeedCheckAutoLogin = false;
            autoLogin();
        }
    
voidreestablishConnections()

        if (!isNetworkAvailable()) {
            return;
        }

        for (ImConnectionAdapter conn : mConnections) {
            int connState = conn.getState();
            if (connState == ImConnection.SUSPENDED) {
                conn.reestablishSession();
            }
        }
    
voidremoveConnection(com.android.im.IImConnection connection)

        mConnections.remove(connection);
    
public voidscheduleReconnect(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 voidsetBackgroundData(boolean flag)

        mBackgroundDataEnabled = flag;
    
public voidshowToast(java.lang.CharSequence text, int duration)

        Message msg = Message.obtain(mServiceHandler, EVENT_SHOW_TOAST, duration, 0, text);
        msg.sendToTarget();
    
private voidsuspendConnections()

        for (ImConnectionAdapter conn : mConnections) {
            if (conn.getState() != ImConnection.LOGGED_IN) {
                continue;
            }
            conn.suspend();
        }