Index: media/base/android/dialog_surface_callback.cc |
diff --git a/media/base/android/dialog_surface_callback.cc b/media/base/android/dialog_surface_callback.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..2788952bcf5f8a7bd9931c1bd974ccaaa22cc8ad |
--- /dev/null |
+++ b/media/base/android/dialog_surface_callback.cc |
@@ -0,0 +1,164 @@ |
+// Copyright 2016 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "media/base/android/dialog_surface_callback.h" |
+ |
+#include <algorithm> |
+#include <limits> |
+#include <map> |
+#include <memory> |
+#include <utility> |
+ |
+#include "base/android/build_info.h" |
+#include "base/android/jni_android.h" |
+#include "base/android/jni_array.h" |
+#include "base/android/jni_string.h" |
+#include "base/bind.h" |
+#include "base/lazy_instance.h" |
+#include "base/location.h" |
+#include "base/logging.h" |
+#include "base/numerics/safe_conversions.h" |
+#include "base/strings/string_util.h" |
+#include "base/synchronization/lock.h" |
+#include "base/synchronization/waitable_event.h" |
+#include "base/threading/thread_task_runner_handle.h" |
+#include "jni/DialogSurfaceCallback_jni.h" |
+ |
+using base::android::AttachCurrentThread; |
+ |
+namespace media { |
+ |
+static long next_id_ = 1; |
+ |
+// Map of ids to DialogSurfaceCallbacks. This is called from multiple threads. |
+class DialogCallbackMap { |
+ public: |
+ // Associate |thiz| with |id|. |
+ void Insert(long id, DialogSurfaceCallback* thiz) { |
+ base::AutoLock _l(lock_); |
+ map_[id] = thiz; |
+ } |
+ |
+ // Forget about |id|. |
+ void Erase(long id) { |
+ base::AutoLock _l(lock_); |
+ map_.erase(id); |
+ } |
+ |
+ // Return the associated callback for |id|, or null. |
+ DialogSurfaceCallback* Lookup(long id) { |
+ base::AutoLock _l(lock_); |
+ return LookupLocked(id); |
+ } |
+ |
+ // Map |id| to the correct task runner for callbacks, or null. |
+ scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunner(long id) { |
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner; |
+ base::AutoLock _l(lock_); |
+ DialogSurfaceCallback* thiz = LookupLocked(id); |
+ // While under lock, |thiz| cannot be removed from the map. |
+ if (thiz) |
+ task_runner = thiz->GetTaskRunner(); |
+ |
+ return task_runner; |
+ } |
+ |
+ private: |
+ // Lookup |id|. The caller must hold |lock_|. |
+ DialogSurfaceCallback* LookupLocked(long id) { |
+ DialogSurfaceCallback* thiz = nullptr; |
+ Map::const_iterator it = map_.find(id); |
+ if (it != map_.end()) |
+ thiz = it->second; |
+ |
+ return thiz; |
+ } |
+ |
+ // Alternatively, we could put in a callback that is bound to the correct |
+ // thread, and use a weak ref. However, since we still need a default |
+ // action when the weak ref is cleared to unblock the callback, it ends up |
+ // not being any simpler. |
+ using Map = std::map<long, DialogSurfaceCallback*>; |
+ Map map_; |
+ |
+ base::Lock lock_; |
+}; |
+ |
+static base::LazyInstance<DialogCallbackMap>::Leaky g_map = |
+ LAZY_INSTANCE_INITIALIZER; |
+ |
+DialogSurfaceCallback::DialogSurfaceCallback( |
+ const DialogSurfaceController::Callback& callback) |
+ : task_runner_(base::ThreadTaskRunnerHandle::Get()), |
+ id_(next_id_++), |
+ callback_(callback) { |
+ JNIEnv* env = AttachCurrentThread(); |
+ CHECK(env); |
+ j_surface_callback_.Reset(Java_DialogSurfaceCallback_create(env, id_)); |
+ g_map.Pointer()->Insert(id_, this); |
+} |
+ |
+DialogSurfaceCallback::~DialogSurfaceCallback() { |
+ DCHECK(thread_checker_.CalledOnValidThread()); |
+ g_map.Pointer()->Erase(id_); |
+} |
+ |
+bool DialogSurfaceCallback::RegisterDialogSurfaceCallback(JNIEnv* env) { |
+ return RegisterNativesImpl(env); |
+} |
+ |
+scoped_refptr<base::SingleThreadTaskRunner> |
+DialogSurfaceCallback::GetTaskRunner() const { |
+ // May be called on any thread. |
+ return task_runner_; |
+} |
+ |
+bool DialogSurfaceCallback::CalledOnValidThread() const { |
+ return thread_checker_.CalledOnValidThread(); |
+} |
+ |
+void DialogSurfaceCallback::OnCallbackProperThread( |
+ long id, |
+ DialogSurfaceController::CallbackOp what, |
+ base::WaitableEvent* event) { |
+ // A callback has happened on some other thread. We're run on the correct |
+ // thread in response to it. |
+ DialogSurfaceCallback* thiz = g_map.Pointer()->Lookup(id); |
+ // |thiz| cannot be deleted here, since we're on the main thread. However, |
+ // it might have been deleted earlier, so it can be null. |
+ |
+ if (thiz) { |
+ DCHECK(thiz->CalledOnValidThread()); |
+ thiz->callback_.Run(what); |
+ } |
+ |
+ // If this is a synchronous callback, then signal that we've completed. |
+ if (event) |
+ event->Signal(); |
+} |
+ |
+void OnDialogSurfaceCallbackCallback(JNIEnv* env, |
+ const JavaParamRef<jclass>& jcaller, |
+ jlong id, |
+ jint what) { |
+ // Post back to the main thread. We get a strong ref here to the task |
+ // runner, so that it's okay if the controller is deleted. Holding the |
+ // lock during the callback is not safe; the caller might require it to |
+ // complete the callback. |
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner = |
+ g_map.Pointer()->GetTaskRunner(id); |
+ |
+ if (task_runner) { |
+ // TODO(liberato): check the thread, and just call if needed. That way, |
+ // we'll work regardless of how the java classes handle threading. |
+ base::WaitableEvent event(false, false); |
+ base::Closure cb = base::Bind( |
+ &DialogSurfaceCallback::OnCallbackProperThread, id, |
+ static_cast<DialogSurfaceController::CallbackOp>(what), &event); |
+ task_runner->PostTask(FROM_HERE, cb); |
+ event.Wait(); |
+ } |
+} |
+ |
+} // namespace media |