OLD | NEW |
(Empty) | |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "media/base/android/dialog_surface_callback.h" |
| 6 |
| 7 #include <algorithm> |
| 8 #include <limits> |
| 9 #include <map> |
| 10 #include <memory> |
| 11 #include <utility> |
| 12 |
| 13 #include "base/android/build_info.h" |
| 14 #include "base/android/jni_android.h" |
| 15 #include "base/android/jni_array.h" |
| 16 #include "base/android/jni_string.h" |
| 17 #include "base/bind.h" |
| 18 #include "base/lazy_instance.h" |
| 19 #include "base/location.h" |
| 20 #include "base/logging.h" |
| 21 #include "base/numerics/safe_conversions.h" |
| 22 #include "base/strings/string_util.h" |
| 23 #include "base/synchronization/lock.h" |
| 24 #include "base/synchronization/waitable_event.h" |
| 25 #include "base/threading/thread_task_runner_handle.h" |
| 26 #include "jni/DialogSurfaceCallback_jni.h" |
| 27 |
| 28 using base::android::AttachCurrentThread; |
| 29 |
| 30 namespace media { |
| 31 |
| 32 static long next_id_ = 1; |
| 33 |
| 34 // Map of ids to DialogSurfaceCallbacks. This is called from multiple threads. |
| 35 class DialogCallbackMap { |
| 36 public: |
| 37 // Associate |thiz| with |id|. |
| 38 void Insert(long id, DialogSurfaceCallback* thiz) { |
| 39 base::AutoLock _l(lock_); |
| 40 map_[id] = thiz; |
| 41 } |
| 42 |
| 43 // Forget about |id|. |
| 44 void Erase(long id) { |
| 45 base::AutoLock _l(lock_); |
| 46 map_.erase(id); |
| 47 } |
| 48 |
| 49 // Return the associated callback for |id|, or null. |
| 50 DialogSurfaceCallback* Lookup(long id) { |
| 51 base::AutoLock _l(lock_); |
| 52 return LookupLocked(id); |
| 53 } |
| 54 |
| 55 // Map |id| to the correct task runner for callbacks, or null. |
| 56 scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunner(long id) { |
| 57 scoped_refptr<base::SingleThreadTaskRunner> task_runner; |
| 58 base::AutoLock _l(lock_); |
| 59 DialogSurfaceCallback* thiz = LookupLocked(id); |
| 60 // While under lock, |thiz| cannot be removed from the map. |
| 61 if (thiz) |
| 62 task_runner = thiz->GetTaskRunner(); |
| 63 |
| 64 return task_runner; |
| 65 } |
| 66 |
| 67 private: |
| 68 // Lookup |id|. The caller must hold |lock_|. |
| 69 DialogSurfaceCallback* LookupLocked(long id) { |
| 70 DialogSurfaceCallback* thiz = nullptr; |
| 71 Map::const_iterator it = map_.find(id); |
| 72 if (it != map_.end()) |
| 73 thiz = it->second; |
| 74 |
| 75 return thiz; |
| 76 } |
| 77 |
| 78 // Alternatively, we could put in a callback that is bound to the correct |
| 79 // thread, and use a weak ref. However, since we still need a default |
| 80 // action when the weak ref is cleared to unblock the callback, it ends up |
| 81 // not being any simpler. |
| 82 using Map = std::map<long, DialogSurfaceCallback*>; |
| 83 Map map_; |
| 84 |
| 85 base::Lock lock_; |
| 86 }; |
| 87 |
| 88 static base::LazyInstance<DialogCallbackMap>::Leaky g_map = |
| 89 LAZY_INSTANCE_INITIALIZER; |
| 90 |
| 91 DialogSurfaceCallback::DialogSurfaceCallback( |
| 92 const DialogSurfaceController::Callback& callback) |
| 93 : task_runner_(base::ThreadTaskRunnerHandle::Get()), |
| 94 id_(next_id_++), |
| 95 callback_(callback) { |
| 96 JNIEnv* env = AttachCurrentThread(); |
| 97 CHECK(env); |
| 98 j_surface_callback_.Reset(Java_DialogSurfaceCallback_create(env, id_)); |
| 99 g_map.Pointer()->Insert(id_, this); |
| 100 } |
| 101 |
| 102 DialogSurfaceCallback::~DialogSurfaceCallback() { |
| 103 DCHECK(thread_checker_.CalledOnValidThread()); |
| 104 g_map.Pointer()->Erase(id_); |
| 105 } |
| 106 |
| 107 bool DialogSurfaceCallback::RegisterDialogSurfaceCallback(JNIEnv* env) { |
| 108 return RegisterNativesImpl(env); |
| 109 } |
| 110 |
| 111 scoped_refptr<base::SingleThreadTaskRunner> |
| 112 DialogSurfaceCallback::GetTaskRunner() const { |
| 113 // May be called on any thread. |
| 114 return task_runner_; |
| 115 } |
| 116 |
| 117 bool DialogSurfaceCallback::CalledOnValidThread() const { |
| 118 return thread_checker_.CalledOnValidThread(); |
| 119 } |
| 120 |
| 121 void DialogSurfaceCallback::OnCallbackProperThread( |
| 122 long id, |
| 123 DialogSurfaceController::CallbackOp what, |
| 124 base::WaitableEvent* event) { |
| 125 // A callback has happened on some other thread. We're run on the correct |
| 126 // thread in response to it. |
| 127 DialogSurfaceCallback* thiz = g_map.Pointer()->Lookup(id); |
| 128 // |thiz| cannot be deleted here, since we're on the main thread. However, |
| 129 // it might have been deleted earlier, so it can be null. |
| 130 |
| 131 if (thiz) { |
| 132 DCHECK(thiz->CalledOnValidThread()); |
| 133 thiz->callback_.Run(what); |
| 134 } |
| 135 |
| 136 // If this is a synchronous callback, then signal that we've completed. |
| 137 if (event) |
| 138 event->Signal(); |
| 139 } |
| 140 |
| 141 void OnDialogSurfaceCallbackCallback(JNIEnv* env, |
| 142 const JavaParamRef<jclass>& jcaller, |
| 143 jlong id, |
| 144 jint what) { |
| 145 // Post back to the main thread. We get a strong ref here to the task |
| 146 // runner, so that it's okay if the controller is deleted. Holding the |
| 147 // lock during the callback is not safe; the caller might require it to |
| 148 // complete the callback. |
| 149 scoped_refptr<base::SingleThreadTaskRunner> task_runner = |
| 150 g_map.Pointer()->GetTaskRunner(id); |
| 151 |
| 152 if (task_runner) { |
| 153 // TODO(liberato): check the thread, and just call if needed. That way, |
| 154 // we'll work regardless of how the java classes handle threading. |
| 155 base::WaitableEvent event(false, false); |
| 156 base::Closure cb = base::Bind( |
| 157 &DialogSurfaceCallback::OnCallbackProperThread, id, |
| 158 static_cast<DialogSurfaceController::CallbackOp>(what), &event); |
| 159 task_runner->PostTask(FROM_HERE, cb); |
| 160 event.Wait(); |
| 161 } |
| 162 } |
| 163 |
| 164 } // namespace media |
OLD | NEW |