Bug 717096 - Crash may occur when switching between Flash tab. r=blassey
authorGian-Carlo Pascutto <gpascutto@mozilla.com>
Tue, 08 May 2012 10:59:16 +0200
changeset 95751 f18d4a2fce0d7fc4fa87b6d2a916b7b40c7efd3b
parent 95750 9212a3b98a660865e5c4d73c4a52e7c98fc3b36d
child 95752 6c7db62eb29c3a9f16b51fe232153ad13c4cdf79
push id1439
push userlsblakk@mozilla.com
push dateMon, 04 Jun 2012 20:19:22 +0000
treeherdermozilla-aurora@ea74834dccd3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersblassey
bugs717096
milestone15.0a1
Bug 717096 - Crash may occur when switching between Flash tab. r=blassey
dom/plugins/base/android/ANPAudio.cpp
--- a/dom/plugins/base/android/ANPAudio.cpp
+++ b/dom/plugins/base/android/ANPAudio.cpp
@@ -1,10 +1,10 @@
-/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* ***** BEGIN LICENSE BLOCK *****
+/* -*- Mode: c++; c-basic-offset: 2; tab-width: 20; indent-tabs-mode: nil; -*-
+ * ***** BEGIN LICENSE BLOCK *****
  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  *
  * The contents of this file are subject to the Mozilla Public License Version
  * 1.1 (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.mozilla.org/MPL/
  *
  * Software distributed under the License is distributed on an "AS IS" basis,
@@ -57,16 +57,18 @@ struct AudioTrack {
   jmethodID constructor;
   jmethodID flush;
   jmethodID pause;
   jmethodID play;
   jmethodID setvol;
   jmethodID stop;
   jmethodID write;
   jmethodID getpos;
+  jmethodID getstate;
+  jmethodID release;
 };
 
 enum AudioTrackMode {
   MODE_STATIC = 0,
   MODE_STREAM = 1
 };
 
 /* android.media.AudioManager */
@@ -86,30 +88,39 @@ enum AudioFormatChannel {
   CHANNEL_OUT_STEREO = 12
 };
 
 enum AudioFormatEncoding {
   ENCODING_PCM_16BIT = 2,
   ENCODING_PCM_8BIT = 3
 };
 
+enum AudioFormatState {
+  STATE_UNINITIALIZED = 0,
+  STATE_INITIALIZED = 1,
+  STATE_NO_STATIC_DATA = 2
+};
+
 static struct AudioTrack at;
 
 static jclass
 init_jni_bindings(JNIEnv *jenv) {
-  jclass jc = jenv->FindClass("android/media/AudioTrack");
+  jclass jc =
+    (jclass)jenv->NewGlobalRef(jenv->FindClass("android/media/AudioTrack"));
 
   at.constructor = jenv->GetMethodID(jc, "<init>", "(IIIIII)V");
   at.flush       = jenv->GetMethodID(jc, "flush", "()V");
   at.pause       = jenv->GetMethodID(jc, "pause", "()V");
   at.play        = jenv->GetMethodID(jc, "play",  "()V");
   at.setvol      = jenv->GetMethodID(jc, "setStereoVolume",  "(FF)I");
   at.stop        = jenv->GetMethodID(jc, "stop",  "()V");
   at.write       = jenv->GetMethodID(jc, "write", "([BII)I");
   at.getpos      = jenv->GetMethodID(jc, "getPlaybackHeadPosition", "()I");
+  at.getstate    = jenv->GetMethodID(jc, "getState", "()I");
+  at.release     = jenv->GetMethodID(jc, "release", "()V");
 
   return jc;
 }
 
 struct ANPAudioTrack {
   jobject output_unit;
   jclass at_class;
 
@@ -138,17 +149,17 @@ public:
 
 NS_IMETHODIMP
 AudioRunnable::Run()
 {
   JNIEnv* jenv = GetJNIForThread();
   if (!jenv)
     return NS_ERROR_FAILURE;
 
-  mozilla::AutoLocalJNIFrame autoFrame(jenv);
+  mozilla::AutoLocalJNIFrame autoFrame(jenv, 2);
 
   jbyteArray bytearray = jenv->NewByteArray(mTrack->bufferSize);
   if (!bytearray) {
     LOG("AudioRunnable:: Run.  Could not create bytearray");
     return NS_ERROR_FAILURE;
   }
 
   jbyte *byte = jenv->GetByteArrayElements(bytearray, NULL);
@@ -188,16 +199,18 @@ AudioRunnable::Run()
         break;
       }
 
       wroteSoFar += retval;
 
     } while(wroteSoFar < buffer.size);
   }
 
+  jenv->CallVoidMethod(mTrack->output_unit, at.release);
+
   jenv->DeleteGlobalRef(mTrack->output_unit);
   jenv->DeleteGlobalRef(mTrack->at_class);
 
   free(mTrack);
 
   jenv->ReleaseByteArrayElements(bytearray, byte, 0);
 
   return NS_OK;
@@ -252,30 +265,36 @@ anp_audio_newTrack(uint32_t sampleRate, 
     jChannels = CHANNEL_OUT_STEREO;
     break;
   default:
     LOG("Unknown channel count.  defaulting to mono.");
     jChannels = CHANNEL_OUT_MONO;
     break;
   }
 
+  mozilla::AutoLocalJNIFrame autoFrame(jenv);
+
   jobject obj = jenv->NewObject(s->at_class,
                                 at.constructor,
                                 STREAM_MUSIC,
                                 s->rate,
                                 jChannels,
                                 jformat,
                                 s->bufferSize,
                                 MODE_STREAM);
 
-  jthrowable exception = jenv->ExceptionOccurred();
-  if (exception) {
-    LOG("%s fAILED  ", __PRETTY_FUNCTION__);
-    jenv->ExceptionDescribe();
-    jenv->ExceptionClear();
+  if (autoFrame.CheckForException() || obj == NULL) {
+    jenv->DeleteGlobalRef(s->at_class);
+    free(s);
+    return NULL;
+  }
+
+  jint state = jenv->CallIntMethod(obj, at.getstate);
+
+  if (autoFrame.CheckForException() || state == STATE_UNINITIALIZED) {
     jenv->DeleteGlobalRef(s->at_class);
     free(s);
     return NULL;
   }
 
   s->output_unit = jenv->NewGlobalRef(obj);
   return s;
 }
@@ -295,28 +314,35 @@ anp_audio_deleteTrack(ANPAudioTrack* s)
 }
 
 void
 anp_audio_start(ANPAudioTrack* s)
 {
   if (s == NULL || s->output_unit == NULL) {
     return;
   }
-  
+
   if (s->keepGoing) {
     // we are already playing.  Ignore.
     return;
   }
 
   JNIEnv *jenv = GetJNIForThread();
   if (!jenv)
     return;
 
+  mozilla::AutoLocalJNIFrame autoFrame(jenv, 0);
   jenv->CallVoidMethod(s->output_unit, at.play);
 
+  if (autoFrame.CheckForException()) {
+    jenv->DeleteGlobalRef(s->at_class);
+    free(s);
+    return;
+  }
+
   s->isStopped = false;
   s->keepGoing = true;
 
   // AudioRunnable now owns the ANPAudioTrack
   nsRefPtr<AudioRunnable> runnable = new AudioRunnable(s);
 
   nsCOMPtr<nsIThread> thread;
   NS_NewThread(getter_AddRefs(thread), runnable);
@@ -327,30 +353,34 @@ anp_audio_pause(ANPAudioTrack* s)
 {
   if (s == NULL || s->output_unit == NULL) {
     return;
   }
 
   JNIEnv *jenv = GetJNIForThread();
   if (!jenv)
     return;
+
+  mozilla::AutoLocalJNIFrame autoFrame(jenv, 0);
   jenv->CallVoidMethod(s->output_unit, at.pause);
 }
 
 void
 anp_audio_stop(ANPAudioTrack* s)
 {
   if (s == NULL || s->output_unit == NULL) {
     return;
   }
 
   s->isStopped = true;
   JNIEnv *jenv = GetJNIForThread();
   if (!jenv)
     return;
+
+  mozilla::AutoLocalJNIFrame autoFrame(jenv, 0);
   jenv->CallVoidMethod(s->output_unit, at.stop);
 }
 
 bool
 anp_audio_isStopped(ANPAudioTrack* s)
 {
   return s->isStopped;
 }