[tizen] Fix memory leak and for exception case.
[platform/core/uifw/dali-adaptor.git] / dali / internal / system / tizen / capture-impl-tizen.cpp
1 /*
2  * Copyright (c) 2014 Samsung Electronics Co., Ltd.
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
18 // CLASS HEADER
19 #include <capture-impl.h>
20
21 // EXTERNAL INCLUDES
22 #include <dali/public-api/common/vector-wrapper.h>
23 #include <dali/public-api/render-tasks/render-task-list.h>
24 #include <dali/public-api/common/stage.h>
25 #include <dali/integration-api/debug.h>
26 #include <fstream>
27 #include <string.h>
28 #include <cynara-client.h>
29 #include <fcntl.h>
30 #include <unistd.h>
31 #include <system_info.h>
32
33 // INTERNAL INCLUDES
34 #include <adaptor.h>
35
36 namespace
37 {
38 unsigned int TIME_OUT_DURATION = 1000;
39 const int SMACK_LABEL_LENGTH = 255;
40
41 const char* const CYNARA_CHECK_FILE = "/proc/self/attr/current";
42 const char* const SCREEN_SHOT_PRIVILEGE = "http://tizen.org/privilege/screenshot";
43 }
44
45 namespace Dali
46 {
47
48 namespace Internal
49 {
50
51 namespace Adaptor
52 {
53
54 Capture::Capture()
55 : mTimer(),
56   mPath(),
57   mNativeImageSourcePtr( NULL ),
58   mTbmSurface( NULL )
59 {
60 }
61
62 Capture::Capture( Dali::CameraActor cameraActor )
63 : mCameraActor( cameraActor ),
64   mTimer(),
65   mPath(),
66   mNativeImageSourcePtr( NULL ),
67   mTbmSurface( NULL )
68 {
69 }
70
71 Capture::~Capture()
72 {
73 }
74
75 CapturePtr Capture::New()
76 {
77   CapturePtr pWorker = new Capture();
78
79   if( pWorker->CheckPrivilege( SCREEN_SHOT_PRIVILEGE ) == false )
80   {
81     DALI_LOG_ERROR( "Capture privilege error: permission denied\n" );
82     return NULL;
83   }
84
85   return pWorker;
86 }
87
88 CapturePtr Capture::New( Dali::CameraActor cameraActor )
89 {
90   CapturePtr pWorker = new Capture( cameraActor );
91
92   if( pWorker->CheckPrivilege( SCREEN_SHOT_PRIVILEGE ) == false )
93   {
94     DALI_LOG_ERROR( "Capture privilege error: permission denied\n" );
95     return NULL;
96   }
97
98   return pWorker;
99 }
100
101 void Capture::Start( Dali::Actor source, const Dali::Vector2& size, const std::string &path, const Dali::Vector4& clearColor )
102 {
103   DALI_ASSERT_ALWAYS(path.size() > 4 && "Path is invalid.");
104
105   // Increase the reference count focely to avoid application mistake.
106   Reference();
107
108   mPath = path;
109
110   DALI_ASSERT_ALWAYS(source && "Source is NULL.");
111
112   UnsetResources();
113   SetupResources( size, clearColor, source );
114 }
115
116 Dali::Capture::CaptureFinishedSignalType& Capture::FinishedSignal()
117 {
118   return mFinishedSignal;
119 }
120
121 void Capture::CreateSurface( const Vector2& size )
122 {
123   DALI_ASSERT_ALWAYS(!mTbmSurface && "mTbmSurface is already created.");
124
125   mTbmSurface = tbm_surface_create( size.width, size.height, TBM_FORMAT_RGBA8888 );
126 }
127
128 void Capture::DeleteSurface()
129 {
130   DALI_ASSERT_ALWAYS(mTbmSurface && "mTbmSurface is empty.");
131
132   tbm_surface_destroy( mTbmSurface );
133   mTbmSurface = NULL;
134 }
135
136 void Capture::ClearSurface( const Vector2& size )
137 {
138   DALI_ASSERT_ALWAYS(mTbmSurface && "mTbmSurface is empty.");
139
140   tbm_surface_info_s surface_info;
141
142   if( tbm_surface_map( mTbmSurface, TBM_SURF_OPTION_WRITE, &surface_info ) == TBM_SURFACE_ERROR_NONE )
143   {
144     //DALI_ASSERT_ALWAYS(surface_info.bpp == 32 && "unsupported tbm format");
145
146     unsigned char* ptr = surface_info.planes[0].ptr;
147     memset( ptr, 0, surface_info.size ); // TODO: support color
148
149     if( tbm_surface_unmap( mTbmSurface ) != TBM_SURFACE_ERROR_NONE )
150     {
151       DALI_LOG_ERROR( "Fail to unmap tbm_surface\n" );
152     }
153   }
154   else
155   {
156      DALI_ASSERT_ALWAYS(0 && "tbm_surface_map failed");
157   }
158 }
159
160 bool Capture::IsSurfaceCreated()
161 {
162   return mTbmSurface != 0;
163 }
164
165 void Capture::CreateNativeImageSource()
166 {
167   Dali::Adaptor& adaptor = Dali::Adaptor::Get();
168
169   DALI_ASSERT_ALWAYS(adaptor.IsAvailable() && "Dali::Adaptor is not available.");
170
171   DALI_ASSERT_ALWAYS(mTbmSurface && "mTbmSurface is empty.");
172
173   DALI_ASSERT_ALWAYS(!mNativeImageSourcePtr && "NativeImageSource is already created.");
174
175   // create the NativeImageSource object with our surface
176   mNativeImageSourcePtr = Dali::NativeImageSource::New( mTbmSurface );
177 }
178
179 void Capture::DeleteNativeImageSource()
180 {
181   DALI_ASSERT_ALWAYS(mNativeImageSourcePtr && "mNativeImageSource is NULL.");
182
183   mNativeImageSourcePtr.Reset();
184 }
185
186 bool Capture::IsNativeImageSourceCreated()
187 {
188   return mNativeImageSourcePtr;
189 }
190
191 void Capture::CreateFrameBuffer()
192 {
193   DALI_ASSERT_ALWAYS(mNativeImageSourcePtr && "NativeImageSource is NULL.");
194
195   DALI_ASSERT_ALWAYS(!mFrameBuffer && "FrameBuffer is already created.");
196
197   mNativeTexture = Dali::Texture::New( *mNativeImageSourcePtr );
198
199   // Create a FrameBuffer object with no default attachments.
200   mFrameBuffer = Dali::FrameBuffer::New( mNativeTexture.GetWidth(), mNativeTexture.GetHeight(), Dali::FrameBuffer::Attachment::NONE );
201   // Add a color attachment to the FrameBuffer object.
202   mFrameBuffer.AttachColorTexture( mNativeTexture );
203 }
204
205 void Capture::DeleteFrameBuffer()
206 {
207   DALI_ASSERT_ALWAYS(mFrameBuffer && "FrameBuffer is NULL.");
208
209   mFrameBuffer.Reset();
210   mNativeTexture.Reset();
211 }
212
213 bool Capture::IsFrameBufferCreated()
214 {
215   return mFrameBuffer;
216 }
217
218 void Capture::SetupRenderTask( Dali::Actor source, const Dali::Vector4& clearColor )
219 {
220   DALI_ASSERT_ALWAYS(source && "Source is empty.");
221
222   mSource = source;
223
224   // Check the original parent about source.
225   mParent = mSource.GetParent();
226
227   Dali::Stage stage = Dali::Stage::GetCurrent();
228   Dali::Size stageSize = stage.GetSize();
229
230   // Add to stage for rendering the source. If source isn't on the stage then it never be rendered.
231   stage.Add( mSource );
232
233   if( !mCameraActor )
234   {
235     mCameraActor = Dali::CameraActor::New( stageSize );
236     mCameraActor.SetParentOrigin( ParentOrigin::CENTER );
237     mCameraActor.SetAnchorPoint( AnchorPoint::CENTER );
238   }
239
240   stage.Add( mCameraActor );
241
242   DALI_ASSERT_ALWAYS(mFrameBuffer && "Framebuffer is NULL.");
243
244   DALI_ASSERT_ALWAYS(!mRenderTask && "RenderTask is already created.");
245
246   Dali::RenderTaskList taskList = stage.GetRenderTaskList();
247   mRenderTask = taskList.CreateTask();
248   mRenderTask.SetRefreshRate( Dali::RenderTask::REFRESH_ONCE );
249   mRenderTask.SetSourceActor( source );
250   mRenderTask.SetCameraActor( mCameraActor );
251   mRenderTask.SetScreenToFrameBufferFunction( Dali::RenderTask::FULLSCREEN_FRAMEBUFFER_FUNCTION );
252   mRenderTask.SetFrameBuffer( mFrameBuffer );
253   mRenderTask.SetClearColor( clearColor );
254   mRenderTask.SetClearEnabled( true );
255   mRenderTask.SetProperty( Dali::RenderTask::Property::REQUIRES_SYNC, true );
256   mRenderTask.FinishedSignal().Connect( this, &Capture::OnRenderFinished );
257   mRenderTask.GetCameraActor().SetInvertYAxis( true );
258
259   mTimer = Dali::Timer::New( TIME_OUT_DURATION );
260   mTimer.TickSignal().Connect( this, &Capture::OnTimeOut );
261   mTimer.Start();
262 }
263
264 void Capture::UnsetRenderTask()
265 {
266   DALI_ASSERT_ALWAYS(mCameraActor && "CameraActor is NULL.");
267
268   if( mParent )
269   {
270     // Restore the parent of source.
271     mParent.Add( mSource );
272     mParent.Reset();
273   }
274   else
275   {
276     mSource.Unparent();
277   }
278
279   mSource.Reset();
280
281   mTimer.Reset();
282
283   mCameraActor.Unparent();
284   mCameraActor.Reset();
285
286   DALI_ASSERT_ALWAYS(mRenderTask && "RenderTask is NULL.");
287
288   Dali::RenderTaskList taskList = Dali::Stage::GetCurrent().GetRenderTaskList();
289   Dali::RenderTask firstTask = taskList.GetTask( 0u );
290
291   // Stop rendering via frame-buffers as empty handle is used to clear target
292   firstTask.SetFrameBuffer( Dali::FrameBuffer() );
293
294   taskList.RemoveTask( mRenderTask );
295   mRenderTask.Reset();
296 }
297
298 bool Capture::IsRenderTaskSetup()
299 {
300   return mCameraActor && mRenderTask;
301 }
302
303 void Capture::SetupResources( const Dali::Vector2& size, const Dali::Vector4& clearColor, Dali::Actor source )
304 {
305   CreateSurface( size );
306   ClearSurface( size );
307
308   CreateNativeImageSource();
309
310   CreateFrameBuffer();
311
312   SetupRenderTask( source, clearColor );
313 }
314
315 void Capture::UnsetResources()
316 {
317   if( IsRenderTaskSetup() )
318   {
319     UnsetRenderTask();
320   }
321
322   if( IsFrameBufferCreated() )
323   {
324     DeleteFrameBuffer();
325   }
326
327   if( IsNativeImageSourceCreated() )
328   {
329     DeleteNativeImageSource();
330   }
331
332   if( IsSurfaceCreated() )
333   {
334     DeleteSurface();
335   }
336 }
337
338 void Capture::OnRenderFinished( Dali::RenderTask& task )
339 {
340   Dali::Capture::FinishState state = Dali::Capture::FinishState::SUCCEEDED;
341
342   mTimer.Stop();
343
344   if( !Save() )
345   {
346     state = Dali::Capture::FinishState::FAILED;
347     DALI_LOG_ERROR("Fail to Capture mTbmSurface[%p] Path[%s]", mTbmSurface, mPath.c_str());
348   }
349
350   Dali::Capture handle( this );
351   mFinishedSignal.Emit( handle, state );
352
353   UnsetResources();
354
355   // Decrease the reference count forcely. It is increased at Start().
356   Unreference();
357 }
358
359 bool Capture::OnTimeOut()
360 {
361   Dali::Capture::FinishState state = Dali::Capture::FinishState::FAILED;
362
363   Dali::Capture handle( this );
364   mFinishedSignal.Emit( handle, state );
365
366   UnsetResources();
367
368   // Decrease the reference count forcely. It is increased at Start().
369   Unreference();
370
371   return false;
372 }
373
374 bool Capture::Save()
375 {
376   DALI_ASSERT_ALWAYS(mNativeImageSourcePtr && "mNativeImageSourcePtr is NULL");
377
378   return mNativeImageSourcePtr->EncodeToFile( mPath );
379 }
380
381 bool Capture::CheckPrivilege( const char* privilege ) const
382 {
383   cynara* cynara;
384   int fd = 0;
385   int ret = 0;
386   char subjectLabel[SMACK_LABEL_LENGTH + 1] = "";
387   char uid[10] = { 0, };
388   const char* clientSession = "";
389
390   ret = cynara_initialize( &cynara, NULL );
391   if( ret != CYNARA_API_SUCCESS )
392   {
393     return false;
394   }
395
396   fd = open( CYNARA_CHECK_FILE, O_RDONLY );
397   if( fd < 0 )
398   {
399     cynara_finish( cynara );
400     return false;
401   }
402
403   ret = read( fd, subjectLabel, SMACK_LABEL_LENGTH );
404   if( ret < 0 )
405   {
406     close( fd );
407     cynara_finish( cynara );
408     return false;
409   }
410
411   close( fd );
412
413   snprintf( uid, 10, "%d", getuid() );
414
415   ret = cynara_check( cynara, subjectLabel, clientSession, uid, privilege );
416   if( ret != CYNARA_API_ACCESS_ALLOWED )
417   {
418     cynara_finish( cynara );
419     return false;
420   }
421
422   cynara_finish( cynara );
423
424   return true;
425 }
426
427 }  // End of namespace Adaptor
428
429 }  // End of namespace Internal
430
431 }  // End of namespace Dali