blob: 2dd4135db9a4d2bb83590dcd22bb028d82287ead [file] [log] [blame]
Mathias Agopian875d8e12013-06-07 15:35:48 -07001/*
2 * Copyright 2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Mark Salyzyn7823e122016-09-29 08:08:05 -070017#include <log/log.h>
Mathias Agopian3f844832013-08-07 21:24:32 -070018#include <ui/Rect.h>
19#include <ui/Region.h>
Mathias Agopian875d8e12013-06-07 15:35:48 -070020
21#include "RenderEngine.h"
Mathias Agopian3f844832013-08-07 21:24:32 -070022#include "GLES20RenderEngine.h"
Mathias Agopian875d8e12013-06-07 15:35:48 -070023#include "GLExtensions.h"
Mathias Agopian3f844832013-08-07 21:24:32 -070024#include "Mesh.h"
Mathias Agopian875d8e12013-06-07 15:35:48 -070025
Fabien Sanglardc93afd52017-03-13 13:02:42 -070026#include <vector>
27#include <SurfaceFlinger.h>
28
Jiyong Park00b15b82017-08-10 20:30:56 +090029extern "C" EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name);
Jesse Hall19e87292013-12-23 21:02:15 -080030
Mathias Agopian875d8e12013-06-07 15:35:48 -070031// ---------------------------------------------------------------------------
32namespace android {
33// ---------------------------------------------------------------------------
34
Jesse Hall19e87292013-12-23 21:02:15 -080035static bool findExtension(const char* exts, const char* name) {
36 if (!exts)
37 return false;
38 size_t len = strlen(name);
Jesse Hall05f8c702013-12-23 20:44:38 -080039
Jesse Hall19e87292013-12-23 21:02:15 -080040 const char* pos = exts;
41 while ((pos = strstr(pos, name)) != NULL) {
42 if (pos[len] == '\0' || pos[len] == ' ')
43 return true;
44 pos += len;
Mathias Agopian2185f8b2013-09-18 16:20:26 -070045 }
46
Jesse Hall19e87292013-12-23 21:02:15 -080047 return false;
48}
49
Chia-I Wub2c76242017-11-09 17:17:07 -080050std::unique_ptr<RenderEngine> RenderEngine::create(EGLDisplay display,
51 int hwcFormat, uint32_t featureFlags) {
Jesse Hall19e87292013-12-23 21:02:15 -080052 // EGL_ANDROIDX_no_config_context is an experimental extension with no
53 // written specification. It will be replaced by something more formal.
54 // SurfaceFlinger is using it to allow a single EGLContext to render to
55 // both a 16-bit primary display framebuffer and a 32-bit virtual display
56 // framebuffer.
57 //
Courtney Goeltzenleuchter0ebaac32017-04-13 12:17:03 -060058 // EGL_KHR_no_config_context is official extension to allow creating a
59 // context that works with any surface of a display.
60 //
Jesse Hall19e87292013-12-23 21:02:15 -080061 // The code assumes that ES2 or later is available if this extension is
62 // supported.
63 EGLConfig config = EGL_NO_CONFIG;
Courtney Goeltzenleuchter0ebaac32017-04-13 12:17:03 -060064 if (!findExtension(eglQueryStringImplementationANDROID(display, EGL_EXTENSIONS),
65 "EGL_ANDROIDX_no_config_context") &&
66 !findExtension(eglQueryStringImplementationANDROID(display, EGL_EXTENSIONS),
67 "EGL_KHR_no_config_context")) {
Steven Thomasd7f49c52017-07-26 18:48:28 -070068 config = chooseEglConfig(display, hwcFormat, /*logConfig*/ true);
Jesse Hall19e87292013-12-23 21:02:15 -080069 }
70
71 EGLint renderableType = 0;
72 if (config == EGL_NO_CONFIG) {
73 renderableType = EGL_OPENGL_ES2_BIT;
74 } else if (!eglGetConfigAttrib(display, config,
75 EGL_RENDERABLE_TYPE, &renderableType)) {
76 LOG_ALWAYS_FATAL("can't query EGLConfig RENDERABLE_TYPE");
77 }
78 EGLint contextClientVersion = 0;
Mathias Agopian2185f8b2013-09-18 16:20:26 -070079 if (renderableType & EGL_OPENGL_ES2_BIT) {
80 contextClientVersion = 2;
81 } else if (renderableType & EGL_OPENGL_ES_BIT) {
82 contextClientVersion = 1;
83 } else {
84 LOG_ALWAYS_FATAL("no supported EGL_RENDERABLE_TYPEs");
85 }
86
Fabien Sanglardc93afd52017-03-13 13:02:42 -070087 std::vector<EGLint> contextAttributes;
88 contextAttributes.reserve(6);
89 contextAttributes.push_back(EGL_CONTEXT_CLIENT_VERSION);
90 contextAttributes.push_back(contextClientVersion);
Mathias Agopian875d8e12013-06-07 15:35:48 -070091#ifdef EGL_IMG_context_priority
Fabien Sanglardc93afd52017-03-13 13:02:42 -070092 if (SurfaceFlinger::useContextPriority) {
93 contextAttributes.push_back(EGL_CONTEXT_PRIORITY_LEVEL_IMG);
94 contextAttributes.push_back(EGL_CONTEXT_PRIORITY_HIGH_IMG);
95 }
Mathias Agopian875d8e12013-06-07 15:35:48 -070096#endif
Fabien Sanglardc93afd52017-03-13 13:02:42 -070097 contextAttributes.push_back(EGL_NONE);
98 contextAttributes.push_back(EGL_NONE);
99
100 EGLContext ctxt = eglCreateContext(display, config, NULL,
101 contextAttributes.data());
Mathias Agopian875d8e12013-06-07 15:35:48 -0700102
103 // if can't create a GL context, we can only abort.
104 LOG_ALWAYS_FATAL_IF(ctxt==EGL_NO_CONTEXT, "EGLContext creation failed");
105
106
107 // now figure out what version of GL did we actually get
108 // NOTE: a dummy surface is not needed if KHR_create_context is supported
109
Jesse Hall19e87292013-12-23 21:02:15 -0800110 EGLConfig dummyConfig = config;
111 if (dummyConfig == EGL_NO_CONFIG) {
Steven Thomasd7f49c52017-07-26 18:48:28 -0700112 dummyConfig = chooseEglConfig(display, hwcFormat, /*logConfig*/ true);
Jesse Hall19e87292013-12-23 21:02:15 -0800113 }
Mathias Agopian875d8e12013-06-07 15:35:48 -0700114 EGLint attribs[] = { EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE, EGL_NONE };
Jesse Hall19e87292013-12-23 21:02:15 -0800115 EGLSurface dummy = eglCreatePbufferSurface(display, dummyConfig, attribs);
Mathias Agopian875d8e12013-06-07 15:35:48 -0700116 LOG_ALWAYS_FATAL_IF(dummy==EGL_NO_SURFACE, "can't create dummy pbuffer");
117 EGLBoolean success = eglMakeCurrent(display, dummy, dummy, ctxt);
118 LOG_ALWAYS_FATAL_IF(!success, "can't make dummy pbuffer current");
119
120 GLExtensions& extensions(GLExtensions::getInstance());
121 extensions.initWithGLStrings(
122 glGetString(GL_VENDOR),
123 glGetString(GL_RENDERER),
124 glGetString(GL_VERSION),
125 glGetString(GL_EXTENSIONS));
126
127 GlesVersion version = parseGlesVersion( extensions.getVersion() );
128
129 // initialize the renderer while GL is current
130
Chia-I Wub2c76242017-11-09 17:17:07 -0800131 std::unique_ptr<RenderEngine> engine;
Mathias Agopian875d8e12013-06-07 15:35:48 -0700132 switch (version) {
133 case GLES_VERSION_1_0:
Mathias Agopian875d8e12013-06-07 15:35:48 -0700134 case GLES_VERSION_1_1:
Fabien Sanglardefb93452016-10-04 14:02:11 -0700135 LOG_ALWAYS_FATAL("SurfaceFlinger requires OpenGL ES 2.0 minimum to run.");
Mathias Agopian875d8e12013-06-07 15:35:48 -0700136 break;
137 case GLES_VERSION_2_0:
138 case GLES_VERSION_3_0:
Chia-I Wub2c76242017-11-09 17:17:07 -0800139 engine = std::make_unique<GLES20RenderEngine>(featureFlags);
Mathias Agopian875d8e12013-06-07 15:35:48 -0700140 break;
141 }
Jesse Hall05f8c702013-12-23 20:44:38 -0800142 engine->setEGLHandles(config, ctxt);
Mathias Agopian875d8e12013-06-07 15:35:48 -0700143
144 ALOGI("OpenGL ES informations:");
145 ALOGI("vendor : %s", extensions.getVendor());
146 ALOGI("renderer : %s", extensions.getRenderer());
147 ALOGI("version : %s", extensions.getVersion());
148 ALOGI("extensions: %s", extensions.getExtension());
Michael Lentine9ae79d82014-07-30 16:42:12 -0700149 ALOGI("GL_MAX_TEXTURE_SIZE = %zu", engine->getMaxTextureSize());
150 ALOGI("GL_MAX_VIEWPORT_DIMS = %zu", engine->getMaxViewportDims());
Mathias Agopian875d8e12013-06-07 15:35:48 -0700151
152 eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
153 eglDestroySurface(display, dummy);
154
155 return engine;
156}
157
Pablo Ceballos53390e12015-08-04 11:25:59 -0700158RenderEngine::RenderEngine() : mEGLConfig(NULL), mEGLContext(EGL_NO_CONTEXT) {
Mathias Agopian875d8e12013-06-07 15:35:48 -0700159}
160
161RenderEngine::~RenderEngine() {
162}
163
Jesse Hall05f8c702013-12-23 20:44:38 -0800164void RenderEngine::setEGLHandles(EGLConfig config, EGLContext ctxt) {
165 mEGLConfig = config;
Mathias Agopian875d8e12013-06-07 15:35:48 -0700166 mEGLContext = ctxt;
167}
168
Jesse Hall05f8c702013-12-23 20:44:38 -0800169EGLContext RenderEngine::getEGLConfig() const {
170 return mEGLConfig;
171}
172
Mathias Agopian875d8e12013-06-07 15:35:48 -0700173EGLContext RenderEngine::getEGLContext() const {
174 return mEGLContext;
175}
176
177void RenderEngine::checkErrors() const {
178 do {
179 // there could be more than one error flag
180 GLenum error = glGetError();
181 if (error == GL_NO_ERROR)
182 break;
183 ALOGE("GL error 0x%04x", int(error));
184 } while (true);
185}
186
187RenderEngine::GlesVersion RenderEngine::parseGlesVersion(const char* str) {
188 int major, minor;
189 if (sscanf(str, "OpenGL ES-CM %d.%d", &major, &minor) != 2) {
190 if (sscanf(str, "OpenGL ES %d.%d", &major, &minor) != 2) {
191 ALOGW("Unable to parse GL_VERSION string: \"%s\"", str);
192 return GLES_VERSION_1_0;
193 }
194 }
195
196 if (major == 1 && minor == 0) return GLES_VERSION_1_0;
197 if (major == 1 && minor >= 1) return GLES_VERSION_1_1;
198 if (major == 2 && minor >= 0) return GLES_VERSION_2_0;
199 if (major == 3 && minor >= 0) return GLES_VERSION_3_0;
200
201 ALOGW("Unrecognized OpenGL ES version: %d.%d", major, minor);
202 return GLES_VERSION_1_0;
203}
204
Mathias Agopian3f844832013-08-07 21:24:32 -0700205void RenderEngine::fillRegionWithColor(const Region& region, uint32_t height,
206 float red, float green, float blue, float alpha) {
207 size_t c;
208 Rect const* r = region.getArray(&c);
209 Mesh mesh(Mesh::TRIANGLES, c*6, 2);
Mathias Agopianff2ed702013-09-01 21:36:12 -0700210 Mesh::VertexArray<vec2> position(mesh.getPositionArray<vec2>());
Mathias Agopian3f844832013-08-07 21:24:32 -0700211 for (size_t i=0 ; i<c ; i++, r++) {
Mathias Agopian5cdc8992013-08-13 20:51:23 -0700212 position[i*6 + 0].x = r->left;
213 position[i*6 + 0].y = height - r->top;
214 position[i*6 + 1].x = r->left;
215 position[i*6 + 1].y = height - r->bottom;
216 position[i*6 + 2].x = r->right;
217 position[i*6 + 2].y = height - r->bottom;
218 position[i*6 + 3].x = r->left;
219 position[i*6 + 3].y = height - r->top;
220 position[i*6 + 4].x = r->right;
221 position[i*6 + 4].y = height - r->bottom;
222 position[i*6 + 5].x = r->right;
223 position[i*6 + 5].y = height - r->top;
Mathias Agopian3f844832013-08-07 21:24:32 -0700224 }
Mathias Agopian19733a32013-08-28 18:13:56 -0700225 setupFillWithColor(red, green, blue, alpha);
226 drawMesh(mesh);
Mathias Agopian3f844832013-08-07 21:24:32 -0700227}
228
Riley Andrews9707f4d2014-10-23 16:17:04 -0700229void RenderEngine::flush() {
230 glFlush();
231}
232
Mathias Agopian3f844832013-08-07 21:24:32 -0700233void RenderEngine::clearWithColor(float red, float green, float blue, float alpha) {
234 glClearColor(red, green, blue, alpha);
235 glClear(GL_COLOR_BUFFER_BIT);
236}
237
238void RenderEngine::setScissor(
239 uint32_t left, uint32_t bottom, uint32_t right, uint32_t top) {
240 glScissor(left, bottom, right, top);
241 glEnable(GL_SCISSOR_TEST);
242}
243
244void RenderEngine::disableScissor() {
245 glDisable(GL_SCISSOR_TEST);
246}
247
248void RenderEngine::genTextures(size_t count, uint32_t* names) {
249 glGenTextures(count, names);
250}
251
252void RenderEngine::deleteTextures(size_t count, uint32_t const* names) {
253 glDeleteTextures(count, names);
254}
255
Mathias Agopiand5556842013-09-19 17:08:37 -0700256void RenderEngine::readPixels(size_t l, size_t b, size_t w, size_t h, uint32_t* pixels) {
257 glReadPixels(l, b, w, h, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
258}
259
Mathias Agopian458197d2013-08-15 14:56:51 -0700260void RenderEngine::dump(String8& result) {
261 const GLExtensions& extensions(GLExtensions::getInstance());
262 result.appendFormat("GLES: %s, %s, %s\n",
263 extensions.getVendor(),
264 extensions.getRenderer(),
265 extensions.getVersion());
266 result.appendFormat("%s\n", extensions.getExtension());
267}
268
Mathias Agopian3f844832013-08-07 21:24:32 -0700269// ---------------------------------------------------------------------------
270
271RenderEngine::BindImageAsFramebuffer::BindImageAsFramebuffer(
272 RenderEngine& engine, EGLImageKHR image) : mEngine(engine)
273{
Mathias Agopian458197d2013-08-15 14:56:51 -0700274 mEngine.bindImageAsFramebuffer(image, &mTexName, &mFbName, &mStatus);
275
Mathias Agopian3f844832013-08-07 21:24:32 -0700276 ALOGE_IF(mStatus != GL_FRAMEBUFFER_COMPLETE_OES,
277 "glCheckFramebufferStatusOES error %d", mStatus);
Mathias Agopian3f844832013-08-07 21:24:32 -0700278}
279
280RenderEngine::BindImageAsFramebuffer::~BindImageAsFramebuffer() {
281 // back to main framebuffer
Mathias Agopian458197d2013-08-15 14:56:51 -0700282 mEngine.unbindFramebuffer(mTexName, mFbName);
Mathias Agopian3f844832013-08-07 21:24:32 -0700283}
284
285status_t RenderEngine::BindImageAsFramebuffer::getStatus() const {
286 return mStatus == GL_FRAMEBUFFER_COMPLETE_OES ? NO_ERROR : BAD_VALUE;
287}
288
Mathias Agopian875d8e12013-06-07 15:35:48 -0700289// ---------------------------------------------------------------------------
Jesse Hall05f8c702013-12-23 20:44:38 -0800290
291static status_t selectConfigForAttribute(EGLDisplay dpy, EGLint const* attrs,
292 EGLint attribute, EGLint wanted, EGLConfig* outConfig) {
Jesse Hall05f8c702013-12-23 20:44:38 -0800293 EGLint numConfigs = -1, n = 0;
294 eglGetConfigs(dpy, NULL, 0, &numConfigs);
295 EGLConfig* const configs = new EGLConfig[numConfigs];
296 eglChooseConfig(dpy, attrs, configs, numConfigs, &n);
297
298 if (n) {
299 if (attribute != EGL_NONE) {
300 for (int i=0 ; i<n ; i++) {
301 EGLint value = 0;
302 eglGetConfigAttrib(dpy, configs[i], attribute, &value);
303 if (wanted == value) {
304 *outConfig = configs[i];
305 delete [] configs;
306 return NO_ERROR;
307 }
308 }
309 } else {
310 // just pick the first one
311 *outConfig = configs[0];
312 delete [] configs;
313 return NO_ERROR;
314 }
315 }
316 delete [] configs;
317 return NAME_NOT_FOUND;
318}
319
320class EGLAttributeVector {
321 struct Attribute;
322 class Adder;
323 friend class Adder;
324 KeyedVector<Attribute, EGLint> mList;
325 struct Attribute {
Pablo Ceballos53390e12015-08-04 11:25:59 -0700326 Attribute() : v(0) {};
Chih-Hung Hsiehc4067912016-05-03 14:03:27 -0700327 explicit Attribute(EGLint v) : v(v) { }
Jesse Hall05f8c702013-12-23 20:44:38 -0800328 EGLint v;
329 bool operator < (const Attribute& other) const {
330 // this places EGL_NONE at the end
331 EGLint lhs(v);
332 EGLint rhs(other.v);
333 if (lhs == EGL_NONE) lhs = 0x7FFFFFFF;
334 if (rhs == EGL_NONE) rhs = 0x7FFFFFFF;
335 return lhs < rhs;
336 }
337 };
338 class Adder {
339 friend class EGLAttributeVector;
340 EGLAttributeVector& v;
341 EGLint attribute;
342 Adder(EGLAttributeVector& v, EGLint attribute)
343 : v(v), attribute(attribute) {
344 }
345 public:
346 void operator = (EGLint value) {
347 if (attribute != EGL_NONE) {
Chih-Hung Hsiehc4067912016-05-03 14:03:27 -0700348 v.mList.add(Attribute(attribute), value);
Jesse Hall05f8c702013-12-23 20:44:38 -0800349 }
350 }
351 operator EGLint () const { return v.mList[attribute]; }
352 };
353public:
354 EGLAttributeVector() {
Chih-Hung Hsiehc4067912016-05-03 14:03:27 -0700355 mList.add(Attribute(EGL_NONE), EGL_NONE);
Jesse Hall05f8c702013-12-23 20:44:38 -0800356 }
357 void remove(EGLint attribute) {
358 if (attribute != EGL_NONE) {
Chih-Hung Hsiehc4067912016-05-03 14:03:27 -0700359 mList.removeItem(Attribute(attribute));
Jesse Hall05f8c702013-12-23 20:44:38 -0800360 }
361 }
362 Adder operator [] (EGLint attribute) {
363 return Adder(*this, attribute);
364 }
365 EGLint operator [] (EGLint attribute) const {
366 return mList[attribute];
367 }
368 // cast-operator to (EGLint const*)
369 operator EGLint const* () const { return &mList.keyAt(0).v; }
370};
371
372
373static status_t selectEGLConfig(EGLDisplay display, EGLint format,
374 EGLint renderableType, EGLConfig* config) {
375 // select our EGLConfig. It must support EGL_RECORDABLE_ANDROID if
376 // it is to be used with WIFI displays
377 status_t err;
378 EGLint wantedAttribute;
379 EGLint wantedAttributeValue;
380
381 EGLAttributeVector attribs;
382 if (renderableType) {
383 attribs[EGL_RENDERABLE_TYPE] = renderableType;
384 attribs[EGL_RECORDABLE_ANDROID] = EGL_TRUE;
385 attribs[EGL_SURFACE_TYPE] = EGL_WINDOW_BIT|EGL_PBUFFER_BIT;
386 attribs[EGL_FRAMEBUFFER_TARGET_ANDROID] = EGL_TRUE;
387 attribs[EGL_RED_SIZE] = 8;
388 attribs[EGL_GREEN_SIZE] = 8;
389 attribs[EGL_BLUE_SIZE] = 8;
neo.hee7f39722017-03-21 11:48:36 +0800390 attribs[EGL_ALPHA_SIZE] = 8;
Jesse Hall05f8c702013-12-23 20:44:38 -0800391 wantedAttribute = EGL_NONE;
392 wantedAttributeValue = EGL_NONE;
393 } else {
394 // if no renderable type specified, fallback to a simplified query
395 wantedAttribute = EGL_NATIVE_VISUAL_ID;
396 wantedAttributeValue = format;
397 }
398
399 err = selectConfigForAttribute(display, attribs,
400 wantedAttribute, wantedAttributeValue, config);
401 if (err == NO_ERROR) {
402 EGLint caveat;
403 if (eglGetConfigAttrib(display, *config, EGL_CONFIG_CAVEAT, &caveat))
404 ALOGW_IF(caveat == EGL_SLOW_CONFIG, "EGL_SLOW_CONFIG selected!");
405 }
406
407 return err;
408}
409
Steven Thomasd7f49c52017-07-26 18:48:28 -0700410EGLConfig RenderEngine::chooseEglConfig(EGLDisplay display, int format,
411 bool logConfig) {
Jesse Hall05f8c702013-12-23 20:44:38 -0800412 status_t err;
413 EGLConfig config;
414
415 // First try to get an ES2 config
416 err = selectEGLConfig(display, format, EGL_OPENGL_ES2_BIT, &config);
417 if (err != NO_ERROR) {
418 // If ES2 fails, try ES1
419 err = selectEGLConfig(display, format, EGL_OPENGL_ES_BIT, &config);
420 if (err != NO_ERROR) {
421 // still didn't work, probably because we're on the emulator...
422 // try a simplified query
423 ALOGW("no suitable EGLConfig found, trying a simpler query");
424 err = selectEGLConfig(display, format, 0, &config);
425 if (err != NO_ERROR) {
426 // this EGL is too lame for android
427 LOG_ALWAYS_FATAL("no suitable EGLConfig found, giving up");
428 }
429 }
430 }
431
Steven Thomasd7f49c52017-07-26 18:48:28 -0700432 if (logConfig) {
433 // print some debugging info
434 EGLint r,g,b,a;
435 eglGetConfigAttrib(display, config, EGL_RED_SIZE, &r);
436 eglGetConfigAttrib(display, config, EGL_GREEN_SIZE, &g);
437 eglGetConfigAttrib(display, config, EGL_BLUE_SIZE, &b);
438 eglGetConfigAttrib(display, config, EGL_ALPHA_SIZE, &a);
439 ALOGI("EGL information:");
440 ALOGI("vendor : %s", eglQueryString(display, EGL_VENDOR));
441 ALOGI("version : %s", eglQueryString(display, EGL_VERSION));
442 ALOGI("extensions: %s", eglQueryString(display, EGL_EXTENSIONS));
443 ALOGI("Client API: %s", eglQueryString(display, EGL_CLIENT_APIS)?:"Not Supported");
444 ALOGI("EGLSurface: %d-%d-%d-%d, config=%p", r, g, b, a, config);
445 }
Jesse Hall05f8c702013-12-23 20:44:38 -0800446
447 return config;
448}
449
Dan Stoza4e637772016-07-28 13:31:51 -0700450
451void RenderEngine::primeCache() const {
452 // Getting the ProgramCache instance causes it to prime its shader cache,
453 // which is performed in its constructor
454 ProgramCache::getInstance();
455}
456
Jesse Hall05f8c702013-12-23 20:44:38 -0800457// ---------------------------------------------------------------------------
Mathias Agopian875d8e12013-06-07 15:35:48 -0700458}; // namespace android
459// ---------------------------------------------------------------------------