Java tutorial
/* * 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(); } }