Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(179)

Side by Side Diff: media/base/android/dialog_surface_callback.cc

Issue 2178973004: DialogSurfaceManager implementation. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: removed IDialogSurfaceActivityMapper from common.aidl Created 4 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698