content: /*
   * Copyright (C) 2010 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 com.android.server.sip;
  
  import android.net.sip.ISipSession;
  import android.net.sip.ISipSessionListener;
  import android.net.sip.SipProfile;
  import android.os.DeadObjectException;
  import android.telephony.Rlog;
  
  /** Class to help safely run a callback in a different thread. */
  class SipSessionListenerProxy extends ISipSessionListener.Stub {
      private static final String TAG = "SipSessionListnerProxy";
  
      private ISipSessionListener mListener;
  
      public void setListener(ISipSessionListener listener) {
          mListener = listener;
      }
  
      public ISipSessionListener getListener() {
          return mListener;
      }
  
      private void proxy(Runnable runnable) {
          // One thread for each calling back.
          // Note: Guarantee ordering if the issue becomes important. Currently,
          // the chance of handling two callback events at a time is none.
          new Thread(runnable, "SipSessionCallbackThread").start();
      }
  
      @Override
      public void onCalling(final ISipSession session) {
          if (mListener == null) return;
          proxy(new Runnable() {
              @Override
              public void run() {
                  try {
                      mListener.onCalling(session);
                  } catch (Throwable t) {
                      handle(t, "onCalling()");
                  }
              }
          });
      }
  
      @Override
      public void onRinging(final ISipSession session, final SipProfile caller,
              final String sessionDescription) {
          if (mListener == null) return;
          proxy(new Runnable() {
              @Override
              public void run() {
                  try {
                      mListener.onRinging(session, caller, sessionDescription);
                  } catch (Throwable t) {
                      handle(t, "onRinging()");
                  }
              }
          });
      }
  
      @Override
      public void onRingingBack(final ISipSession session) {
          if (mListener == null) return;
          proxy(new Runnable() {
              @Override
              public void run() {
                  try {
                      mListener.onRingingBack(session);
                  } catch (Throwable t) {
                      handle(t, "onRingingBack()");
                  }
              }
          });
      }
  
      @Override
      public void onCallEstablished(final ISipSession session,
              final String sessionDescription) {
          if (mListener == null) return;
          proxy(new Runnable() {
              @Override
              public void run() {
                  try {
                      mListener.onCallEstablished(session, sessionDescription);
                  } catch (Throwable t) {
                      handle(t, "onCallEstablished()");
                  }
              }
          });
      }
  
      @Override
      public void onCallEnded(final ISipSession session) {
          if (mListener == null) return;
          proxy(new Runnable() {
              @Override
              public void run() {
                  try {
                      mListener.onCallEnded(session);
                  } catch (Throwable t) {
                      handle(t, "onCallEnded()");
                  }
              }
          });
      }
  
      @Override
      public void onCallTransferring(final ISipSession newSession,
              final String sessionDescription) {
          if (mListener == null) return;
          proxy(new Runnable() {
              @Override
              public void run() {
                  try {
                      mListener.onCallTransferring(newSession, sessionDescription);
                  } catch (Throwable t) {
                      handle(t, "onCallTransferring()");
                  }
              }
          });
      }
  
      @Override
      public void onCallBusy(final ISipSession session) {
          if (mListener == null) return;
          proxy(new Runnable() {
              @Override
              public void run() {
                  try {
                      mListener.onCallBusy(session);
                  } catch (Throwable t) {
                      handle(t, "onCallBusy()");
                  }
              }
          });
      }
  
      @Override
      public void onCallChangeFailed(final ISipSession session,
              final int errorCode, final String message) {
          if (mListener == null) return;
          proxy(new Runnable() {
              @Override
              public void run() {
                  try {
                      mListener.onCallChangeFailed(session, errorCode, message);
                  } catch (Throwable t) {
                      handle(t, "onCallChangeFailed()");
                  }
              }
          });
      }
  
      @Override
      public void onError(final ISipSession session, final int errorCode,
              final String message) {
          if (mListener == null) return;
          proxy(new Runnable() {
              @Override
              public void run() {
                  try {
                      mListener.onError(session, errorCode, message);
                  } catch (Throwable t) {
                      handle(t, "onError()");
                  }
              }
          });
      }
  
      @Override
      public void onRegistering(final ISipSession session) {
          if (mListener == null) return;
          proxy(new Runnable() {
              @Override
              public void run() {
                  try {
                      mListener.onRegistering(session);
                  } catch (Throwable t) {
                      handle(t, "onRegistering()");
                  }
              }
          });
      }
  
      @Override
      public void onRegistrationDone(final ISipSession session,
              final int duration) {
          if (mListener == null) return;
          proxy(new Runnable() {
              @Override
              public void run() {
                  try {
                      mListener.onRegistrationDone(session, duration);
                  } catch (Throwable t) {
                      handle(t, "onRegistrationDone()");
                  }
              }
          });
      }
  
      @Override
      public void onRegistrationFailed(final ISipSession session,
              final int errorCode, final String message) {
          if (mListener == null) return;
          proxy(new Runnable() {
              @Override
              public void run() {
                  try {
                      mListener.onRegistrationFailed(session, errorCode, message);
                  } catch (Throwable t) {
                      handle(t, "onRegistrationFailed()");
                  }
              }
          });
      }
  
      @Override
      public void onRegistrationTimeout(final ISipSession session) {
          if (mListener == null) return;
          proxy(new Runnable() {
              @Override
              public void run() {
                  try {
                      mListener.onRegistrationTimeout(session);
                  } catch (Throwable t) {
                      handle(t, "onRegistrationTimeout()");
                  }
              }
          });
      }
  
      private void handle(Throwable t, String message) {
          if (t instanceof DeadObjectException) {
              mListener = null;
              // This creates race but it's harmless. Just don't log the error
              // when it happens.
          } else if (mListener != null) {
              loge(message, t);
          }
      }
  
      private void log(String s) {
          Rlog.d(TAG, s);
      }
  
      private void loge(String s, Throwable t) {
          Rlog.e(TAG, s, t);
      }
  }
  