1/* 2 * Copyright (C) 2007 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 17#include <stdlib.h> 18#include <stdint.h> 19#include <math.h> 20#include <sys/types.h> 21 22#include <utils/Errors.h> 23#include <utils/Log.h> 24#include <utils/StopWatch.h> 25 26#include <ui/GraphicBuffer.h> 27#include <ui/PixelFormat.h> 28#include <ui/FramebufferNativeWindow.h> 29#include <ui/Rect.h> 30#include <ui/Region.h> 31 32#include <hardware/copybit.h> 33 34#include "LayerBuffer.h" 35#include "SurfaceFlinger.h" 36#include "DisplayHardware/DisplayHardware.h" 37 38namespace android { 39 40// --------------------------------------------------------------------------- 41 42gralloc_module_t const* LayerBuffer::sGrallocModule = 0; 43 44// --------------------------------------------------------------------------- 45 46LayerBuffer::LayerBuffer(SurfaceFlinger* flinger, DisplayID display, 47 const sp<Client>& client) 48 : LayerBaseClient(flinger, display, client), 49 mNeedsBlending(false), mBlitEngine(0) 50{ 51} 52 53LayerBuffer::~LayerBuffer() 54{ 55 if (mBlitEngine) { 56 copybit_close(mBlitEngine); 57 } 58} 59 60void LayerBuffer::onFirstRef() 61{ 62 LayerBaseClient::onFirstRef(); 63 mSurface = new SurfaceLayerBuffer(mFlinger, this); 64 65 hw_module_t const* module = (hw_module_t const*)sGrallocModule; 66 if (!module) { 67 // NOTE: technically there is a race here, but it shouldn't 68 // cause any problem since hw_get_module() always returns 69 // the same value. 70 if (hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module) == 0) { 71 sGrallocModule = (gralloc_module_t const *)module; 72 } 73 } 74 75 if (hw_get_module(COPYBIT_HARDWARE_MODULE_ID, &module) == 0) { 76 copybit_open(module, &mBlitEngine); 77 } 78} 79 80sp<LayerBaseClient::Surface> LayerBuffer::createSurface() const 81{ 82 return mSurface; 83} 84 85status_t LayerBuffer::ditch() 86{ 87 mSurface.clear(); 88 return NO_ERROR; 89} 90 91bool LayerBuffer::needsBlending() const { 92 return mNeedsBlending; 93} 94 95void LayerBuffer::setNeedsBlending(bool blending) { 96 mNeedsBlending = blending; 97} 98 99void LayerBuffer::postBuffer(ssize_t offset) 100{ 101 sp<Source> source(getSource()); 102 if (source != 0) 103 source->postBuffer(offset); 104} 105 106void LayerBuffer::unregisterBuffers() 107{ 108 sp<Source> source(clearSource()); 109 if (source != 0) 110 source->unregisterBuffers(); 111} 112 113uint32_t LayerBuffer::doTransaction(uint32_t flags) 114{ 115 sp<Source> source(getSource()); 116 if (source != 0) 117 source->onTransaction(flags); 118 uint32_t res = LayerBase::doTransaction(flags); 119 // we always want filtering for these surfaces 120 mNeedsFiltering = !(mFlags & DisplayHardware::SLOW_CONFIG); 121 return res; 122} 123 124void LayerBuffer::unlockPageFlip(const Transform& planeTransform, 125 Region& outDirtyRegion) 126{ 127 // this code-path must be as tight as possible, it's called each time 128 // the screen is composited. 129 sp<Source> source(getSource()); 130 if (source != 0) 131 source->onVisibilityResolved(planeTransform); 132 LayerBase::unlockPageFlip(planeTransform, outDirtyRegion); 133} 134 135void LayerBuffer::validateVisibility(const Transform& globalTransform) 136{ 137 sp<Source> source(getSource()); 138 if (source != 0) 139 source->onvalidateVisibility(globalTransform); 140 LayerBase::validateVisibility(globalTransform); 141} 142 143void LayerBuffer::drawForSreenShot() const 144{ 145 const DisplayHardware& hw(graphicPlane(0).displayHardware()); 146 clearWithOpenGL( Region(hw.bounds()) ); 147} 148 149void LayerBuffer::onDraw(const Region& clip) const 150{ 151 sp<Source> source(getSource()); 152 if (LIKELY(source != 0)) { 153 source->onDraw(clip); 154 } else { 155 clearWithOpenGL(clip); 156 } 157} 158 159void LayerBuffer::serverDestroy() 160{ 161 sp<Source> source(clearSource()); 162 if (source != 0) { 163 source->destroy(); 164 } 165} 166 167/** 168 * This creates a "buffer" source for this surface 169 */ 170status_t LayerBuffer::registerBuffers(const ISurface::BufferHeap& buffers) 171{ 172 Mutex::Autolock _l(mLock); 173 if (mSource != 0) 174 return INVALID_OPERATION; 175 176 sp<BufferSource> source = new BufferSource(*this, buffers); 177 178 status_t result = source->getStatus(); 179 if (result == NO_ERROR) { 180 mSource = source; 181 } 182 return result; 183} 184 185/** 186 * This creates an "overlay" source for this surface 187 */ 188sp<OverlayRef> LayerBuffer::createOverlay(uint32_t w, uint32_t h, int32_t f, 189 int32_t orientation) 190{ 191 sp<OverlayRef> result; 192 Mutex::Autolock _l(mLock); 193 if (mSource != 0) 194 return result; 195 196 sp<OverlaySource> source = new OverlaySource(*this, &result, w, h, f, orientation); 197 if (result != 0) { 198 mSource = source; 199 } 200 return result; 201} 202 203sp<LayerBuffer::Source> LayerBuffer::getSource() const { 204 Mutex::Autolock _l(mLock); 205 return mSource; 206} 207 208sp<LayerBuffer::Source> LayerBuffer::clearSource() { 209 sp<Source> source; 210 Mutex::Autolock _l(mLock); 211 source = mSource; 212 mSource.clear(); 213 return source; 214} 215 216// ============================================================================ 217// LayerBuffer::SurfaceLayerBuffer 218// ============================================================================ 219 220LayerBuffer::SurfaceLayerBuffer::SurfaceLayerBuffer( 221 const sp<SurfaceFlinger>& flinger, const sp<LayerBuffer>& owner) 222 : LayerBaseClient::Surface(flinger, owner->getIdentity(), owner) 223{ 224} 225 226LayerBuffer::SurfaceLayerBuffer::~SurfaceLayerBuffer() 227{ 228 unregisterBuffers(); 229} 230 231status_t LayerBuffer::SurfaceLayerBuffer::registerBuffers( 232 const ISurface::BufferHeap& buffers) 233{ 234 sp<LayerBuffer> owner(getOwner()); 235 if (owner != 0) 236 return owner->registerBuffers(buffers); 237 return NO_INIT; 238} 239 240void LayerBuffer::SurfaceLayerBuffer::postBuffer(ssize_t offset) 241{ 242 sp<LayerBuffer> owner(getOwner()); 243 if (owner != 0) 244 owner->postBuffer(offset); 245} 246 247void LayerBuffer::SurfaceLayerBuffer::unregisterBuffers() 248{ 249 sp<LayerBuffer> owner(getOwner()); 250 if (owner != 0) 251 owner->unregisterBuffers(); 252} 253 254sp<OverlayRef> LayerBuffer::SurfaceLayerBuffer::createOverlay( 255 uint32_t w, uint32_t h, int32_t format, int32_t orientation) { 256 sp<OverlayRef> result; 257 sp<LayerBuffer> owner(getOwner()); 258 if (owner != 0) 259 result = owner->createOverlay(w, h, format, orientation); 260 return result; 261} 262 263// ============================================================================ 264// LayerBuffer::Buffer 265// ============================================================================ 266 267LayerBuffer::Buffer::Buffer(const ISurface::BufferHeap& buffers, 268 ssize_t offset, size_t bufferSize) 269 : mBufferHeap(buffers), mSupportsCopybit(false) 270{ 271 NativeBuffer& src(mNativeBuffer); 272 src.crop.l = 0; 273 src.crop.t = 0; 274 src.crop.r = buffers.w; 275 src.crop.b = buffers.h; 276 277 src.img.w = buffers.hor_stride ?: buffers.w; 278 src.img.h = buffers.ver_stride ?: buffers.h; 279 src.img.format = buffers.format; 280 src.img.base = (void*)(intptr_t(buffers.heap->base()) + offset); 281 src.img.handle = 0; 282 283 gralloc_module_t const * module = LayerBuffer::getGrallocModule(); 284 if (module && module->perform) { 285 int err = module->perform(module, 286 GRALLOC_MODULE_PERFORM_CREATE_HANDLE_FROM_BUFFER, 287 buffers.heap->heapID(), bufferSize, 288 offset, buffers.heap->base(), 289 &src.img.handle); 290 291 // we can fail here is the passed buffer is purely software 292 mSupportsCopybit = (err == NO_ERROR); 293 } 294 } 295 296LayerBuffer::Buffer::~Buffer() 297{ 298 NativeBuffer& src(mNativeBuffer); 299 if (src.img.handle) { 300 native_handle_delete(src.img.handle); 301 } 302} 303 304// ============================================================================ 305// LayerBuffer::Source 306// LayerBuffer::BufferSource 307// LayerBuffer::OverlaySource 308// ============================================================================ 309 310LayerBuffer::Source::Source(LayerBuffer& layer) 311 : mLayer(layer) 312{ 313} 314LayerBuffer::Source::~Source() { 315} 316void LayerBuffer::Source::onDraw(const Region& clip) const { 317} 318void LayerBuffer::Source::onTransaction(uint32_t flags) { 319} 320void LayerBuffer::Source::onVisibilityResolved( 321 const Transform& planeTransform) { 322} 323void LayerBuffer::Source::postBuffer(ssize_t offset) { 324} 325void LayerBuffer::Source::unregisterBuffers() { 326} 327 328// --------------------------------------------------------------------------- 329 330LayerBuffer::BufferSource::BufferSource(LayerBuffer& layer, 331 const ISurface::BufferHeap& buffers) 332 : Source(layer), mStatus(NO_ERROR), mBufferSize(0) 333{ 334 if (buffers.heap == NULL) { 335 // this is allowed, but in this case, it is illegal to receive 336 // postBuffer(). The surface just erases the framebuffer with 337 // fully transparent pixels. 338 mBufferHeap = buffers; 339 mLayer.setNeedsBlending(false); 340 return; 341 } 342 343 status_t err = (buffers.heap->heapID() >= 0) ? NO_ERROR : NO_INIT; 344 if (err != NO_ERROR) { 345 LOGE("LayerBuffer::BufferSource: invalid heap (%s)", strerror(err)); 346 mStatus = err; 347 return; 348 } 349 350 PixelFormatInfo info; 351 err = getPixelFormatInfo(buffers.format, &info); 352 if (err != NO_ERROR) { 353 LOGE("LayerBuffer::BufferSource: invalid format %d (%s)", 354 buffers.format, strerror(err)); 355 mStatus = err; 356 return; 357 } 358 359 if (buffers.hor_stride<0 || buffers.ver_stride<0) { 360 LOGE("LayerBuffer::BufferSource: invalid parameters " 361 "(w=%d, h=%d, xs=%d, ys=%d)", 362 buffers.w, buffers.h, buffers.hor_stride, buffers.ver_stride); 363 mStatus = BAD_VALUE; 364 return; 365 } 366 367 mBufferHeap = buffers; 368 mLayer.setNeedsBlending((info.h_alpha - info.l_alpha) > 0); 369 mBufferSize = info.getScanlineSize(buffers.hor_stride)*buffers.ver_stride; 370 mLayer.forceVisibilityTransaction(); 371} 372 373LayerBuffer::BufferSource::~BufferSource() 374{ 375 class MessageDestroyTexture : public MessageBase { 376 SurfaceFlinger* flinger; 377 GLuint name; 378 public: 379 MessageDestroyTexture( 380 SurfaceFlinger* flinger, GLuint name) 381 : flinger(flinger), name(name) { } 382 virtual bool handler() { 383 glDeleteTextures(1, &name); 384 return true; 385 } 386 }; 387 388 if (mTexture.name != -1U) { 389 // GL textures can only be destroyed from the GL thread 390 getFlinger()->mEventQueue.postMessage( 391 new MessageDestroyTexture(getFlinger(), mTexture.name) ); 392 } 393 if (mTexture.image != EGL_NO_IMAGE_KHR) { 394 EGLDisplay dpy(getFlinger()->graphicPlane(0).getEGLDisplay()); 395 eglDestroyImageKHR(dpy, mTexture.image); 396 } 397} 398 399void LayerBuffer::BufferSource::postBuffer(ssize_t offset) 400{ 401 ISurface::BufferHeap buffers; 402 { // scope for the lock 403 Mutex::Autolock _l(mBufferSourceLock); 404 buffers = mBufferHeap; 405 if (buffers.heap != 0) { 406 const size_t memorySize = buffers.heap->getSize(); 407 if ((size_t(offset) + mBufferSize) > memorySize) { 408 LOGE("LayerBuffer::BufferSource::postBuffer() " 409 "invalid buffer (offset=%d, size=%d, heap-size=%d", 410 int(offset), int(mBufferSize), int(memorySize)); 411 return; 412 } 413 } 414 } 415 416 sp<Buffer> buffer; 417 if (buffers.heap != 0) { 418 buffer = new LayerBuffer::Buffer(buffers, offset, mBufferSize); 419 if (buffer->getStatus() != NO_ERROR) 420 buffer.clear(); 421 setBuffer(buffer); 422 mLayer.invalidate(); 423 } 424} 425 426void LayerBuffer::BufferSource::unregisterBuffers() 427{ 428 Mutex::Autolock _l(mBufferSourceLock); 429 mBufferHeap.heap.clear(); 430 mBuffer.clear(); 431 mLayer.invalidate(); 432} 433 434sp<LayerBuffer::Buffer> LayerBuffer::BufferSource::getBuffer() const 435{ 436 Mutex::Autolock _l(mBufferSourceLock); 437 return mBuffer; 438} 439 440void LayerBuffer::BufferSource::setBuffer(const sp<LayerBuffer::Buffer>& buffer) 441{ 442 Mutex::Autolock _l(mBufferSourceLock); 443 mBuffer = buffer; 444} 445 446void LayerBuffer::BufferSource::onDraw(const Region& clip) const 447{ 448 sp<Buffer> ourBuffer(getBuffer()); 449 if (UNLIKELY(ourBuffer == 0)) { 450 // nothing to do, we don't have a buffer 451 mLayer.clearWithOpenGL(clip); 452 return; 453 } 454 455 status_t err = NO_ERROR; 456 NativeBuffer src(ourBuffer->getBuffer()); 457 const Rect transformedBounds(mLayer.getTransformedBounds()); 458 459#if defined(EGL_ANDROID_image_native_buffer) 460 if (GLExtensions::getInstance().haveDirectTexture()) { 461 err = INVALID_OPERATION; 462 if (ourBuffer->supportsCopybit()) { 463 copybit_device_t* copybit = mLayer.mBlitEngine; 464 if (copybit && err != NO_ERROR) { 465 // create our EGLImageKHR the first time 466 err = initTempBuffer(); 467 if (err == NO_ERROR) { 468 // NOTE: Assume the buffer is allocated with the proper USAGE flags 469 const NativeBuffer& dst(mTempBuffer); 470 region_iterator clip(Region(Rect(dst.crop.r, dst.crop.b))); 471 copybit->set_parameter(copybit, COPYBIT_TRANSFORM, 0); 472 copybit->set_parameter(copybit, COPYBIT_PLANE_ALPHA, 0xFF); 473 copybit->set_parameter(copybit, COPYBIT_DITHER, COPYBIT_ENABLE); 474 err = copybit->stretch(copybit, &dst.img, &src.img, 475 &dst.crop, &src.crop, &clip); 476 if (err != NO_ERROR) { 477 clearTempBufferImage(); 478 } 479 } 480 } 481 } 482 } 483#endif 484 else { 485 err = INVALID_OPERATION; 486 } 487 488 if (err != NO_ERROR) { 489 // slower fallback 490 GGLSurface t; 491 t.version = sizeof(GGLSurface); 492 t.width = src.crop.r; 493 t.height = src.crop.b; 494 t.stride = src.img.w; 495 t.vstride= src.img.h; 496 t.format = src.img.format; 497 t.data = (GGLubyte*)src.img.base; 498 const Region dirty(Rect(t.width, t.height)); 499 mTextureManager.loadTexture(&mTexture, dirty, t); 500 } 501 502 mLayer.setBufferTransform(mBufferHeap.transform); 503 mLayer.drawWithOpenGL(clip, mTexture); 504} 505 506status_t LayerBuffer::BufferSource::initTempBuffer() const 507{ 508 // figure out the size we need now 509 const ISurface::BufferHeap& buffers(mBufferHeap); 510 uint32_t w = mLayer.mTransformedBounds.width(); 511 uint32_t h = mLayer.mTransformedBounds.height(); 512 if (buffers.w * h != buffers.h * w) { 513 int t = w; w = h; h = t; 514 } 515 516 // we're in the copybit case, so make sure we can handle this blit 517 // we don't have to keep the aspect ratio here 518 copybit_device_t* copybit = mLayer.mBlitEngine; 519 const int down = copybit->get(copybit, COPYBIT_MINIFICATION_LIMIT); 520 const int up = copybit->get(copybit, COPYBIT_MAGNIFICATION_LIMIT); 521 if (buffers.w > w*down) w = buffers.w / down; 522 else if (w > buffers.w*up) w = buffers.w*up; 523 if (buffers.h > h*down) h = buffers.h / down; 524 else if (h > buffers.h*up) h = buffers.h*up; 525 526 if (mTexture.image != EGL_NO_IMAGE_KHR) { 527 // we have an EGLImage, make sure the needed size didn't change 528 if (w!=mTexture.width || h!= mTexture.height) { 529 // delete the EGLImage and texture 530 clearTempBufferImage(); 531 } else { 532 // we're good, we have an EGLImageKHR and it's (still) the 533 // right size 534 return NO_ERROR; 535 } 536 } 537 538 // figure out if we need linear filtering 539 if (buffers.w * h == buffers.h * w) { 540 // same pixel area, don't use filtering 541 mLayer.mNeedsFiltering = false; 542 } 543 544 // Allocate a temporary buffer and create the corresponding EGLImageKHR 545 // once the EGLImage has been created we don't need the 546 // graphic buffer reference anymore. 547 sp<GraphicBuffer> buffer = new GraphicBuffer( 548 w, h, HAL_PIXEL_FORMAT_RGB_565, 549 GraphicBuffer::USAGE_HW_TEXTURE | 550 GraphicBuffer::USAGE_HW_2D); 551 552 status_t err = buffer->initCheck(); 553 if (err == NO_ERROR) { 554 NativeBuffer& dst(mTempBuffer); 555 dst.img.w = buffer->getStride(); 556 dst.img.h = h; 557 dst.img.format = buffer->getPixelFormat(); 558 dst.img.handle = (native_handle_t *)buffer->handle; 559 dst.img.base = 0; 560 dst.crop.l = 0; 561 dst.crop.t = 0; 562 dst.crop.r = w; 563 dst.crop.b = h; 564 565 EGLDisplay dpy(getFlinger()->graphicPlane(0).getEGLDisplay()); 566 err = mTextureManager.initEglImage(&mTexture, dpy, buffer); 567 } 568 569 return err; 570} 571 572void LayerBuffer::BufferSource::clearTempBufferImage() const 573{ 574 // delete the image 575 EGLDisplay dpy(getFlinger()->graphicPlane(0).getEGLDisplay()); 576 eglDestroyImageKHR(dpy, mTexture.image); 577 578 // and the associated texture (recreate a name) 579 glDeleteTextures(1, &mTexture.name); 580 Texture defaultTexture; 581 mTexture = defaultTexture; 582} 583 584// --------------------------------------------------------------------------- 585 586LayerBuffer::OverlaySource::OverlaySource(LayerBuffer& layer, 587 sp<OverlayRef>* overlayRef, 588 uint32_t w, uint32_t h, int32_t format, int32_t orientation) 589 : Source(layer), mVisibilityChanged(false), 590 mOverlay(0), mOverlayHandle(0), mOverlayDevice(0), mOrientation(orientation) 591{ 592 overlay_control_device_t* overlay_dev = getFlinger()->getOverlayEngine(); 593 if (overlay_dev == NULL) { 594 // overlays not supported 595 return; 596 } 597 598 mOverlayDevice = overlay_dev; 599 overlay_t* overlay = overlay_dev->createOverlay(overlay_dev, w, h, format); 600 if (overlay == NULL) { 601 // couldn't create the overlay (no memory? no more overlays?) 602 return; 603 } 604 605 // enable dithering... 606 overlay_dev->setParameter(overlay_dev, overlay, 607 OVERLAY_DITHER, OVERLAY_ENABLE); 608 609 mOverlay = overlay; 610 mWidth = overlay->w; 611 mHeight = overlay->h; 612 mFormat = overlay->format; 613 mWidthStride = overlay->w_stride; 614 mHeightStride = overlay->h_stride; 615 mInitialized = false; 616 617 mOverlayHandle = overlay->getHandleRef(overlay); 618 619 sp<OverlayChannel> channel = new OverlayChannel( &layer ); 620 621 *overlayRef = new OverlayRef(mOverlayHandle, channel, 622 mWidth, mHeight, mFormat, mWidthStride, mHeightStride); 623 getFlinger()->signalEvent(); 624} 625 626LayerBuffer::OverlaySource::~OverlaySource() 627{ 628 if (mOverlay && mOverlayDevice) { 629 overlay_control_device_t* overlay_dev = mOverlayDevice; 630 overlay_dev->destroyOverlay(overlay_dev, mOverlay); 631 } 632} 633 634void LayerBuffer::OverlaySource::onDraw(const Region& clip) const 635{ 636 // this would be where the color-key would be set, should we need it. 637 GLclampf red = 0; 638 GLclampf green = 0; 639 GLclampf blue = 0; 640 mLayer.clearWithOpenGL(clip, red, green, blue, 0); 641} 642 643void LayerBuffer::OverlaySource::onTransaction(uint32_t flags) 644{ 645 const Layer::State& front(mLayer.drawingState()); 646 const Layer::State& temp(mLayer.currentState()); 647 if (temp.sequence != front.sequence) { 648 mVisibilityChanged = true; 649 } 650} 651 652void LayerBuffer::OverlaySource::onvalidateVisibility(const Transform&) 653{ 654 mVisibilityChanged = true; 655} 656 657void LayerBuffer::OverlaySource::onVisibilityResolved( 658 const Transform& planeTransform) 659{ 660 // this code-path must be as tight as possible, it's called each time 661 // the screen is composited. 662 if (UNLIKELY(mOverlay != 0)) { 663 if (mVisibilityChanged || !mInitialized) { 664 mVisibilityChanged = false; 665 mInitialized = true; 666 const Rect bounds(mLayer.getTransformedBounds()); 667 int x = bounds.left; 668 int y = bounds.top; 669 int w = bounds.width(); 670 int h = bounds.height(); 671 672 // we need a lock here to protect "destroy" 673 Mutex::Autolock _l(mOverlaySourceLock); 674 if (mOverlay) { 675 overlay_control_device_t* overlay_dev = mOverlayDevice; 676 overlay_dev->setPosition(overlay_dev, mOverlay, x,y,w,h); 677 // we need to combine the layer orientation and the 678 // user-requested orientation. 679 Transform finalTransform(Transform(mLayer.getOrientation()) * 680 Transform(mOrientation)); 681 overlay_dev->setParameter(overlay_dev, mOverlay, 682 OVERLAY_TRANSFORM, finalTransform.getOrientation()); 683 overlay_dev->commit(overlay_dev, mOverlay); 684 } 685 } 686 } 687} 688 689void LayerBuffer::OverlaySource::destroy() 690{ 691 // we need a lock here to protect "onVisibilityResolved" 692 Mutex::Autolock _l(mOverlaySourceLock); 693 if (mOverlay && mOverlayDevice) { 694 overlay_control_device_t* overlay_dev = mOverlayDevice; 695 overlay_dev->destroyOverlay(overlay_dev, mOverlay); 696 mOverlay = 0; 697 } 698} 699 700// --------------------------------------------------------------------------- 701}; // namespace android 702