Chromium Code Reviews| 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| from any thread. | |
| 38 void Insert(long id, DialogSurfaceCallback* thiz) { | |
| 39 base::AutoLock _l(lock_); | |
| 40 map_[id] = thiz; | |
|
boliu
2017/01/04 01:48:44
check nothing is being overridden here?
liberato (no reviews please)
2017/01/11 22:17:56
Done.
| |
| 41 } | |
| 42 | |
| 43 // Forget about |id| from any thread. It's okay if it's not present. | |
| 44 // if it's not present. | |
| 45 void Erase(long id) { | |
| 46 base::AutoLock _l(lock_); | |
| 47 map_.erase(id); | |
| 48 } | |
| 49 | |
| 50 // Return the associated callback for |id|, or null, from any thread. | |
| 51 DialogSurfaceCallback* Lookup(long id) { | |
| 52 base::AutoLock _l(lock_); | |
| 53 return LookupLocked(id); | |
| 54 } | |
| 55 | |
| 56 // Map |id| to the correct task runner for callbacks, or null. Thread safe. | |
| 57 scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunner(long id) { | |
| 58 scoped_refptr<base::SingleThreadTaskRunner> task_runner; | |
| 59 base::AutoLock _l(lock_); | |
| 60 DialogSurfaceCallback* thiz = LookupLocked(id); | |
| 61 // While under lock, |thiz| cannot be removed from the map. Get a strong | |
| 62 // ref to it. | |
| 63 if (thiz) | |
| 64 task_runner = thiz->GetTaskRunner(); | |
|
boliu
2017/01/04 01:48:44
is it overkill to support callbacks from multiple
liberato (no reviews please)
2017/01/11 22:17:56
there's a replacement for AVDA that's in the works
boliu
2017/01/12 20:24:18
But this CL does exactly that right now..
liberato (no reviews please)
2017/02/03 21:28:32
Done.
| |
| 65 | |
| 66 return task_runner; | |
| 67 } | |
| 68 | |
| 69 private: | |
| 70 // Lookup |id|. The caller must hold |lock_|. | |
| 71 DialogSurfaceCallback* LookupLocked(long id) { | |
| 72 DialogSurfaceCallback* thiz = nullptr; | |
|
boliu
2017/01/04 01:48:44
lock_.AssertAcquired()
liberato (no reviews please)
2017/01/11 22:17:56
Done.
| |
| 73 Map::const_iterator it = map_.find(id); | |
| 74 if (it != map_.end()) | |
| 75 thiz = it->second; | |
| 76 | |
| 77 return thiz; | |
| 78 } | |
| 79 | |
| 80 // Alternatively, we could put in a callback that is bound to the correct | |
| 81 // thread, and use a weak ref. However, since we still need a default | |
| 82 // action when the weak ref is cleared to unblock the callback, it ends up | |
| 83 // not being any simpler. | |
| 84 using Map = std::map<long, DialogSurfaceCallback*>; | |
| 85 Map map_; | |
| 86 | |
| 87 base::Lock lock_; | |
| 88 }; | |
| 89 | |
| 90 static base::LazyInstance<DialogCallbackMap>::Leaky g_map = | |
| 91 LAZY_INSTANCE_INITIALIZER; | |
| 92 | |
| 93 DialogSurfaceCallback::DialogSurfaceCallback( | |
| 94 const DialogSurface::Callback& callback) | |
| 95 : task_runner_(base::ThreadTaskRunnerHandle::Get()), | |
| 96 id_(next_id_++), | |
| 97 callback_(callback) { | |
| 98 JNIEnv* env = AttachCurrentThread(); | |
| 99 CHECK(env); | |
| 100 j_surface_callback_.Reset(Java_DialogSurfaceCallback_create(env, id_)); | |
| 101 | |
| 102 g_map.Pointer()->Insert(id_, this); | |
| 103 } | |
| 104 | |
| 105 DialogSurfaceCallback::~DialogSurfaceCallback() { | |
| 106 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 107 g_map.Pointer()->Erase(id_); | |
| 108 } | |
| 109 | |
| 110 void DialogSurfaceCallback::SetAndroidSurfaceCB( | |
| 111 const AndroidSurfaceCB& android_surface_cb) { | |
| 112 android_surface_cb_ = android_surface_cb; | |
| 113 } | |
| 114 | |
| 115 bool DialogSurfaceCallback::RegisterDialogSurfaceCallback(JNIEnv* env) { | |
| 116 return RegisterNativesImpl(env); | |
| 117 } | |
| 118 | |
| 119 scoped_refptr<base::SingleThreadTaskRunner> | |
| 120 DialogSurfaceCallback::GetTaskRunner() const { | |
| 121 // May be called on any thread. | |
| 122 return task_runner_; | |
| 123 } | |
| 124 | |
| 125 bool DialogSurfaceCallback::CalledOnValidThread() const { | |
| 126 return thread_checker_.CalledOnValidThread(); | |
| 127 } | |
| 128 | |
| 129 void DialogSurfaceCallback::OnCallbackProperThread( | |
|
boliu
2017/01/04 01:48:44
hmm, if we could have passed a WeakPtr opaquely th
liberato (no reviews please)
2017/01/11 22:17:56
good question. never thought of it.
i think that
boliu
2017/01/12 20:24:18
What if DialogSurfaceCallback is refcoutnedthreads
liberato (no reviews please)
2017/02/03 21:28:32
it can work.
there are lots of thread hopping iss
liberato (no reviews please)
2017/02/07 00:20:20
ah, the problem with my attempt with WeakRefs was
| |
| 130 long id, | |
| 131 DialogSurface::CallbackOp what, | |
| 132 base::android::ScopedJavaGlobalRef<jobject> jobject_opt, | |
| 133 base::WaitableEvent* event) { | |
| 134 // A callback has happened on some other thread. We're run on the correct | |
| 135 // thread in response to it. | |
| 136 DialogSurfaceCallback* thiz = g_map.Pointer()->Lookup(id); | |
| 137 // |thiz| cannot be deleted here, since we're on the main thread. However, | |
| 138 // it might have been deleted earlier, so it can be null. | |
| 139 | |
| 140 // If this is SURFACE_DESTROYED, then no further callbacks are permitted. | |
| 141 // Erase the map entry in case the IDialogSurface decides to send another. | |
| 142 if (what == DialogSurface::CallbackOp::SURFACE_DESTROYED) | |
| 143 g_map.Pointer()->Erase(id); | |
| 144 | |
| 145 if (thiz) { | |
| 146 DCHECK(thiz->CalledOnValidThread()); | |
| 147 // Send the java surface to the DialogSurface before the callback. | |
| 148 if (what == DialogSurface::CallbackOp::SURFACE_CREATED && | |
| 149 !thiz->android_surface_cb_.is_null()) { | |
| 150 thiz->android_surface_cb_.Run(jobject_opt); | |
| 151 } | |
| 152 | |
| 153 // Run the client-supplied callback. | |
| 154 thiz->callback_.Run(what); | |
| 155 } | |
| 156 | |
| 157 // If this is a synchronous callback, then signal that we've completed. | |
| 158 if (event) | |
| 159 event->Signal(); | |
| 160 } | |
| 161 | |
| 162 void OnDialogSurfaceCallbackCreated( | |
| 163 JNIEnv* env, | |
| 164 const base::android::JavaParamRef<jclass>& jcaller, | |
| 165 jlong id, | |
| 166 const base::android::JavaParamRef<jobject>& jsurface) { | |
| 167 // Called from some random thread in java. | |
| 168 // Post back to the main thread. We get a strong ref here to the task | |
| 169 // runner, so that it's okay if the controller is deleted. Holding the | |
| 170 // lock during the callback is not safe; the caller might require it to | |
| 171 // complete the callback. | |
| 172 // Remember that we're on a random thread, but |g_map| is thread safe. | |
| 173 if (scoped_refptr<base::SingleThreadTaskRunner> task_runner = | |
| 174 g_map.Pointer()->GetTaskRunner(id)) { | |
| 175 base::Closure cb = base::Bind( | |
| 176 &DialogSurfaceCallback::OnCallbackProperThread, id, | |
| 177 DialogSurface::CallbackOp::SURFACE_CREATED, | |
| 178 base::android::ScopedJavaGlobalRef<jobject>(jsurface), nullptr); | |
| 179 task_runner->PostTask(FROM_HERE, cb); | |
| 180 } | |
| 181 } | |
| 182 | |
| 183 void OnDialogSurfaceCallbackDestroyed( | |
| 184 JNIEnv* env, | |
| 185 const base::android::JavaParamRef<jclass>& jcaller, | |
| 186 jlong id) { | |
| 187 // Called from some random thread in java. | |
| 188 // SURFACE_DESTROYED must be synchronous, so that the client can stop using | |
| 189 // the surface before Android destroys it. | |
| 190 if (scoped_refptr<base::SingleThreadTaskRunner> task_runner = | |
| 191 g_map.Pointer()->GetTaskRunner(id)) { | |
| 192 // TODO(liberato): check the thread, and just call if needed. That way, | |
| 193 // we'll work regardless of how the java classes handle threading. | |
| 194 base::WaitableEvent event(base::WaitableEvent::ResetPolicy::AUTOMATIC, | |
| 195 base::WaitableEvent::InitialState::NOT_SIGNALED); | |
| 196 base::Closure cb = base::Bind( | |
| 197 &DialogSurfaceCallback::OnCallbackProperThread, id, | |
| 198 DialogSurface::CallbackOp::SURFACE_DESTROYED, nullptr, &event); | |
| 199 task_runner->PostTask(FROM_HERE, cb); | |
| 200 event.Wait(); | |
|
boliu
2017/01/04 01:48:44
why does this need to be synchronous?
I think blo
liberato (no reviews please)
2017/01/11 22:17:56
it has to be synchronous because android's surface
boliu
2017/01/12 20:24:18
But what if that happens?
Can emulate chrome's sy
liberato (no reviews please)
2017/02/03 21:28:32
Done. added IAndroidOverlayCompletion which gets
| |
| 201 } | |
| 202 } | |
| 203 | |
| 204 } // namespace media | |
| OLD | NEW |