org.mitre.svmp.activities.AppRTCActivity.java Source code

Java tutorial

Introduction

Here is the source code for org.mitre.svmp.activities.AppRTCActivity.java

Source

/*
 * Copyright (c) 2013 The MITRE Corporation, All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this work 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.
 */

// Derived from AppRTCActivity from the libjingle / webrtc AppRTCDemo
// example application distributed under the following license.
/*
 * libjingle
 * Copyright 2013, Google Inc.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *  1. Redistributions of source code must retain the above copyright notice,
 *     this list of conditions and the following disclaimer.
 *  2. Redistributions in binary form must reproduce the above copyright notice,
 *     this list of conditions and the following disclaimer in the documentation
 *     and/or other materials provided with the distribution.
 *  3. The name of the author may not be used to endorse or promote products
 *     derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package org.mitre.svmp.activities;

import android.app.Activity;
import android.app.ProgressDialog;
import android.content.*;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.graphics.Color;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;
import android.view.*;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
import org.json.JSONObject;
import org.mitre.svmp.apprtc.*;
import org.mitre.svmp.client.*;
import org.mitre.svmp.common.*;
import org.mitre.svmp.performance.PerformanceAdapter;
import org.mitre.svmp.protocol.SVMPProtocol.Request;
import org.mitre.svmp.protocol.SVMPProtocol.Response;
import org.mitre.svmp.services.SessionService;
import org.webrtc.*;
import org.mitre.svmp.common.StateMachine.STATE;
import com.citicrowd.oval.R;

/**
 * @author Joe Portner Base activity for establishing an AppRTC connection
 */
public class AppRTCActivity extends Activity implements StateObserver, MessageHandler, Constants {

    private static final String TAG = AppRTCActivity.class.getName();

    protected AppRTCClient appRtcClient;
    protected PerformanceAdapter performanceAdapter;
    private boolean bound = false;

    private Toast logToast;

    // Synchronize on quit[0] to avoid teardown-related crashes.
    private final Boolean[] quit = new Boolean[] { false };

    protected DatabaseHandler dbHandler;
    protected ConnectionInfo connectionInfo;

    protected boolean proxying = false; // if this is true, we have finished the
    // handshakes and the connection is
    // running
    protected ProgressDialog pd;

    protected ImageView appLoadingImgVw;
    protected TextView preparingTextView;

    LinearLayout ll;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_apprtc);
        getWindow().getDecorView().setBackgroundColor(Color.WHITE);

        ll = (LinearLayout) findViewById(R.id.vsvLinear);
        preparingTextView = (TextView) findViewById(R.id.preparingTextView);
        appLoadingImgVw = (ImageView) findViewById(R.id.appLoadingImgVw);

        // lock the application to the natural "up" orientation of the physical
        // device
        // noinspection MagicConstant
        setRequestedOrientation(getDeviceDefaultOrientation());

        // connect to the database
        dbHandler = new DatabaseHandler(this);

        // adapter that helps record performance measurements
        performanceAdapter = new PerformanceAdapter();

        // Since the error-handling of this demo consists of throwing
        // RuntimeExceptions and we assume that'll terminate the app, we install
        // this default handler so it's applied to background threads as well.
        Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
            public void uncaughtException(Thread t, Throwable e) {
                e.printStackTrace();
                System.exit(-1);
            }
        });

        // Get info passed to Intent
        final Intent intent = getIntent();
        connectionInfo = dbHandler.getConnectionInfo(intent.getIntExtra("connectionID", 1));

        if (connectionInfo != null)
            connectToRoom();
        else
            logAndToast(R.string.appRTC_toast_connection_notFound);
    }

    @Override
    public void onWindowFocusChanged(boolean hasFocus) {
        super.onWindowFocusChanged(hasFocus);
    }

    // returns what value we should request for screen orientation, either
    // portrait or landscape
    private int getDeviceDefaultOrientation() {
        WindowManager windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
        Configuration config = getResources().getConfiguration();
        int rotation = windowManager.getDefaultDisplay().getRotation();

        int value = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
        if (((rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180)
                && config.orientation == Configuration.ORIENTATION_LANDSCAPE)
                || ((rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270)
                        && config.orientation == Configuration.ORIENTATION_PORTRAIT))
            value = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;

        return value;
    }

    // called from PCObserver
    public MediaConstraints getPCConstraints() {
        MediaConstraints value = null;
        if (appRtcClient != null)
            value = appRtcClient.getSignalingParams().pcConstraints;
        return value;
    }

    public void changeToErrorState() {
        if (appRtcClient != null)
            appRtcClient.changeToErrorState();
    }

    protected void connectToRoom() {
        logAndToast(R.string.appRTC_toast_connection_start);
        startProgressDialog();

        bindService(new Intent(this, SessionService.class), serviceConnection, 0);
    }

    /** Defines callbacks for service binding, passed to bindService() */
    private ServiceConnection serviceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName className, IBinder iBinder) {
            // We've bound to SessionService, cast the IBinder and get
            // SessionService instance

            Log.i("info", "Service Connected");
            appRtcClient = (AppRTCClient) iBinder;
            Log.i("checking", appRtcClient.toString());
            performanceAdapter.setPerformanceData(appRtcClient.getPerformance());
            bound = true;

            // after we have bound to the service, begin the connection
            appRtcClient.connectToRoom(AppRTCActivity.this);
        }

        @Override
        public void onServiceDisconnected(ComponentName arg0) {
            Log.i("info", "Service disonnected");
        }
    };

    protected void startProgressDialog() {
        pd = new ProgressDialog(AppRTCActivity.this);
        pd.setCanceledOnTouchOutside(false);
        pd.setTitle(R.string.appRTC_progressDialog_title);
        pd.setMessage(getResources().getText(R.string.appRTC_progressDialog_message));
        pd.setIndeterminate(true);
        pd.setOnCancelListener(new DialogInterface.OnCancelListener() {
            @Override
            public void onCancel(DialogInterface dialog) {
                disconnectAndExit();
            }
        });
        pd.show();
    }

    public void stopProgressDialog() {
        if (pd != null) {
            pd.dismiss();
            pd = null;
        }
    }

    @Override
    public void onPause() {
        super.onPause();
        if (proxying)
            disconnectAndExit();
    }

    public void onPause(boolean videoRunning) {
        super.onPause();
        if (!videoRunning) {
            if (proxying)
                disconnectAndExit();
        }

    }

    // Log |msg| and Toast about it.
    public void logAndToast(final int resID) {
        Log.d(TAG, getResources().getString(resID));
        this.runOnUiThread(new Runnable() {
            public void run() {
                if (logToast != null) {
                    logToast.cancel();
                }
                logToast = Toast.makeText(AppRTCActivity.this, resID, Toast.LENGTH_SHORT);
                // logToast.show();
            }
        });
    }

    // called from PCObserver, SDPObserver, RotationHandler, and TouchHandler
    public void sendMessage(Request msg) {
        if (appRtcClient != null)
            appRtcClient.sendMessage(msg);
    }

    // MessageHandler interface method
    // Called when the client connection is established
    public void onOpen() {
        proxying = true;
        logAndToast(R.string.appRTC_toast_clientHandler_start);
    }

    // MessageHandler interface method
    // Called when a message is sent from the server, and the SessionService
    // doesn't consume it
    public boolean onMessage(Response data) {
        switch (data.getType()) {
        case AUTH:
            if (data.hasAuthResponse()) {
                switch (data.getAuthResponse().getType()) {
                case SESSION_MAX_TIMEOUT:
                    needAuth(R.string.svmpActivity_toast_sessionMaxTimeout, false);
                    break;
                }
            }
            break;
        default:
            Log.e(TAG, "Unexpected protocol message of type " + data.getType().name());
        }
        return true;
    }

    // when authentication fails, or a session maxTimeout or idleTimeout message
    // is received, stop the
    // AppRTCActivity, close the connection, and cause the ConnectionList
    // activity to reconnect to this
    // connectionID
    public void needAuth(int messageResID, boolean passwordChange) {
        // clear timed out session information from memory
        dbHandler.clearSessionInfo(connectionInfo);
        // send a result message to the calling activity so it will show the
        // authentication dialog again
        Intent intent = new Intent();
        intent.putExtra("connectionID", connectionInfo.getConnectionID());
        if (messageResID > 0)
            logAndToast(messageResID);
        setResult(passwordChange ? SvmpActivity.RESULT_NEEDPASSWORDCHANGE : SvmpActivity.RESULT_NEEDAUTH, intent);

        disconnectAndExit();
    }

    // Disconnect from remote resources, dispose of local resources, and exit.
    protected void disconnectAndExit() {
        proxying = false;
        synchronized (quit[0]) {
            if (quit[0]) {
                return;
            }
            quit[0] = true;

            // allow child classes to clean up their components
            onDisconnectAndExit();

            // Unbind from the service
            if (bound) {
                if (SessionService.getState() == STATE.RUNNING) {
                    JSONObject json = new JSONObject();
                    AppRTCHelper.jsonPut(json, "type", "bye");
                    try {
                        appRtcClient.sendMessage(AppRTCHelper.makeWebRTCRequest(json));
                    } catch (Exception e) {
                        // don't care
                    }
                }
                unbindService(serviceConnection);
                bound = false;
                appRtcClient.disconnectFromRoom();
                appRtcClient = null;
                performanceAdapter.clearPerformanceData();
            }

            stopProgressDialog(); // prevent resource leak if we disconnect
            // while the progress dialog is still up

            // if the useBackground preference is unchecked, stop the session
            // service before finishing
            boolean useBackground = Utility.getPrefBool(this, R.string.preferenceKey_connection_useBackground,
                    R.string.preferenceValue_connection_useBackground);
            if (!useBackground)
                stopService(new Intent(this, SessionService.class));

            if (!isFinishing())
                finish();
        }
    }

    // override in child classes
    protected void onDisconnectAndExit() {
    }

    public boolean isConnected() {
        return proxying;
    }

    public void onStateChange(STATE oldState, STATE newState, int resID) {
        boolean exit = false;

        switch (newState) {
        case CONNECTED:
            break;
        case AUTH:
            break;
        case RUNNING:
            break;
        case ERROR:
            // we are in an error state, check the previous state and act
            // appropriately
            switch (oldState) {
            case STARTED: // failed to authenticate and transition to AUTH
            case CONNECTED: // failed to receive ready message and transition to
                // RUNNING (can fail auth to proxy)
                if (resID == R.string.appRTC_toast_svmpAuthenticator_fail) {
                    // our authentication was rejected, exit and bring up the
                    // auth prompt when the connection list resumes
                    needAuth(resID, false);
                } else if (resID == R.string.svmpActivity_toast_needPasswordChange
                        || resID == R.string.appRTC_toast_svmpAuthenticator_passwordChangeFail) {
                    needAuth(resID, true);
                }
                // otherwise, we had an SSL error, display the failure message
                // and return to the connection list
                break;
            case AUTH: // failed to connect the WebSocket and transition to
                       // CONNECTED
                       // the socket connection failed, display the failure message and
                       // return to the connection list
                break;
            case RUNNING: // failed after already running
                break;
            }

            exit = true;
            break;
        default:
            break;
        }

        // if the state change included a message, log it and display a toast
        // popup message
        if (resID > 0 && !quit[0])
            logAndToast(resID);

        if (exit)
            // finish this activity and return to the connection list
            disconnectAndExit();
    }
}