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 |