안드로이드 Native 코드 분석 : sp<> - Smart Pointer

 안드로이드 카메라 동작 원리에 대해 공부를 하면서 안드로이드 소스코드를 분석하고 있습니다. 옆에 C++ 서적을 읽어가며 보다보면 어느 정도 이해하고 넘어갈 수 있는 부분들이 많이 있었습니다만 책에서도 찾아보기 어려운 내용이 들이닥치더군요. 그 부분이 바로 sp<T> 구문이었습니다.


/framework/base/core/jni/android_hardware_Camera.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
sp<Camera> get_native_camera(JNIEnv *env, jobject thiz, JNICameraContext** pContext)
{
    sp<Camera> camera;
    Mutex::Autolock _l(sLock);
    JNICameraContext* context = reinterpret_cast<JNICameraContext*>(env->GetIntField(thiz, fields.context));
    if (context != NULL) {
        camera = context->getCamera();
    }
    ALOGV("get_native_camera: context=%p, camera=%p", context, camera.get());
    if (camera == 0) {
        jniThrowRuntimeException(env, "Method called after release()");
    }
 
    if (pContext != NULL) *pContext = context;
    return camera;
}
cs


 코드 내부를 살펴보면 뜬금없이 등장하는 'sp<Camera>' 부분이 영 이해가 되지 않더군요. 이를 헤더를 뒤져가며 찾는 것도 너무나도 어려웠는데 이를 좀 더 자세히 살펴보았습니다.


/frameworks/base/core/jni/android_hardware_Camera.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#define LOG_TAG "Camera-JNI"
#include <utils/Log.h>
 
#include "jni.h"
#include "JNIHelp.h"
#include "android_runtime/AndroidRuntime.h"
#include <android_runtime/android_graphics_SurfaceTexture.h>
#include <android_runtime/android_view_Surface.h>
 
#include <cutils/properties.h>
#include <utils/Vector.h>
 
#include <gui/GLConsumer.h>
#include <gui/Surface.h>
#include <camera/Camera.h>
#include <binder/IMemory.h>
cs

위 헤더파일들 중 #include<binder/IMemory.h>로 이동해 봅니다.


/frameworks/native/include/binder/IMemory.h

1
2
3
4
5
6
7
8
9
10
#ifndef ANDROID_IMEMORY_H
#define ANDROID_IMEMORY_H
 
#include <stdint.h>
#include <sys/types.h>
#include <sys/mman.h>
 
#include <utils/RefBase.h>
#include <utils/Errors.h>
#include <binder/IInterface.h>
cs


 다음으로 안드로이드 JNI의 핵심을 이해할 수 있는 #include<utils/RefBase.h> 로 이동하신 후


/system/core/include/utils/RefBase.h

1
2
3
4
5
6
7
8
9
10
11
12
#ifndef ANDROID_REF_BASE_H
#define ANDROID_REF_BASE_H
 
#include <cutils/atomic.h>
 
#include <stdint.h>
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
 
#include <utils/StrongPointer.h>
#include <utils/TypeHelpers.h>
cs


끝으로 #include<utils/StongPointer.h> 로 이동해 주시면

/system/core/include/utils/StrongPointer.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
template<typename T>
class sp {
public:
    inline sp() : m_ptr(0) { }
 
    sp(T* other);
    sp(const sp<T>& other);
    template<typename U> sp(U* other);
    template<typename U> sp(const sp<U>& other);
 
    ~sp();
 
    // Assignment
 
    sp& operator = (T* other);
    sp& operator = (const sp<T>& other);
 
    template<typename U> sp& operator = (const sp<U>& other);
    template<typename U> sp& operator = (U* other);
 
    //! Special optimization for use by ProcessState (and nobody else).
    void force_set(T* other);
 
    // Reset
 
    void clear();
 
    // Accessors
 
    inline  T&      operator* () const  { return *m_ptr; }
    inline  T*      operator-> () const { return m_ptr;  }
    inline  T*      get() const         { return m_ptr; }
 
    // Operators
 
    COMPARE(==)
    COMPARE(!=)
    COMPARE(>)
    COMPARE(<)
    COMPARE(<=)
    COMPARE(>=)
 
private:    
    template<typename Y> friend class sp;
    template<typename Y> friend class wp;
    void set_pointer(T* ptr);
    T* m_ptr;
};
cs


드디어 우리들이 찾고 있던 sp<T>가 구현된 StrongPointer를 찾아내실 수 있습니다. 과연 이 녀석은 무슨 역할을 하는 것일까요?


 안드로이드 소스코드에 사용되고 있는 sp<T>는 스마트포인터(Smart Pointer)를 말하는 것이며 보통 Struct나 int같이 C에서 기본적으로 제공되고 있는 것들은 *을 통해 간단하게 포인터로 사용하는 것이 가능합니다. sp<T>는 이를 객체인 Class를 포인터처럼 사용할 수 있도록 하기 위해 사용되는 도구라고 설명드릴 수 있겠습니다. 이를 사용하면 사용자는 포인터로 지정된 객체를 실제 객체를 다루듯이 사용하실 수 있다는 장점을 가지고 있습니다.


 Strong Pointer를 쓰는 가장 중요한 이유는 포인터 객체의 할당과 삭제를 직접 제어할 수 있다는 것입니다. Java와 같이 Gabage Collector 기능이 있는 경우 객체를 할당 해준 후 사용하지 않으면 알아서 할당에서 사라지지만 C/C++의 경우 해당 기능이 존재하지 않습니다. 그렇기 때문에 sp<T>를 활용해 메모리를 관리해 준다면 좀 더 쾌적한 프로그램을 만들 수 있을 것입니다.


 위 코드에서 47번째 줄의 내용을 주목해 봅시다. m_ptr이라는 이름의 변수가 있는데 이 변수의 타입은 T, 즉 sp에 전달되는 클래스 자신의 주소값을 저장하는 상황임을 알 수 있습니다.


 그렇다면 여기서 잠시 코드의 내부롤 살짝 살펴보도록 하겠습니다.


1
2
3
4
5
6
template<typename T>
sp<T>::sp(T* other)
        : m_ptr(other) {
    if (other)
        other->incStrong(this);
}
cs

객체의 스마트포인터가 할당된 경우 입니다. 기존에 해당 객체가 존재할 경우 incStrong()를 통해 참조 갯수를 늘립니다.


1
2
3
4
5
template<typename T>
sp<T>::~sp() {
    if (m_ptr)
        m_ptr->decStrong(this);
}
cs

객체의 스마트포인터 하나가 할당이 해제된 경우입니다. decString()를 통해 참조 갯수를 줄입니다.



1
2
3
4
5
6
7
template<typename T>
void sp<T>::clear() {
    if (m_ptr) {
        m_ptr->decStrong(this);
        m_ptr = 0;
    }
}
cs

객체 자체의 할당을 해제합니다. 해당 포인터를 0으로 하여 할당을 해제합니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
template <typename T>
class sp
{
public:
    inline sp() : m_ptr(0) { }
....
    // Accessors
 
    inline  T&      operator* () const  { return *m_ptr; }
    inline  T*      operator-> () const { return m_ptr;  }
    inline  T*      get() const         { return m_ptr; }
....
private:
    template<typename Y> friend class sp;
    template<typename Y> friend class wp;
    void set_pointer(T* ptr);
    T* m_ptr;
};
 
 
cs

 앞에서도 제시되었었던 Smart Pointer인 sp 클래스 내부의 모습입니다. Accessors 주석 아래에 작성된 부분은 Smart Pointer에 연결된 포인터에 접근에 관하여 다루고 있는 모습입니다.


 안드로이드 소스코드를 이해하는 데에는 이 정도의 지식만 알고 넘어가시면 될 듯 합니다. 좀 더 자세한 내용을 알고 싶으신 분들은 위에서 언급한 코드를 직접 살펴보시면 좋을 것입니다.


300x250

SurfaceView에서 SurfaceHolder의 동작원리(Principle of SurfaceHolder in SurfaceView)

안드로이드/프레임워크 2015. 2. 26. 22:52

 짫은 시간에 화면에 다양한 변화를 나타내는 데에 사용되는 SurfaceView에서 사용되는 SurfaceHolder이 어떻게 동작하는 지에 대해 궁금해서 안드로이드 소스를 이리저리 살펴보게 되었습니다. 본 포스팅에서는 실행 방법을 제 나름대로 탐색한 결과에 대해 작성하였습니다.


 시작하기에 앞서 SurfaceView에서 가장 많이 사용되는 Camera 애플리케이션에 대해 알아봅시다. 제가 이전에 작성한 카메라 작동에 관한 포스팅은 아래 링크를 참조해 주시길 바랍니다.


http://elecs.tistory.com/69


 SurfaceView를 사용하기 위해서는 SurfaceHolder를 사용해야 합니다. SurfaceHolder에 사용하고자 하는 기능을 넣은 후 이를 등록하면 SurfaceView에서는 이를 토대로 동작을 수행하게 됩니다.


1
2
3
4
5
6
7
8
9
10
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.activity_main);
        
        surfaceView = (SurfaceView)findViewById(R.id.surfaceView);
        
        surfaceHolder = surfaceView.getHolder();
        surfaceHolder.addCallback(surfaceListener);
    }
cs


위 코드에서 확인하는 바와 같이 SurfaceView를 XML로 생성한 SurfaceView와 연결한 후

해당 SurfaceView에 SurfaceHolder를 연결한 후 해당 Holder에 surfaceListener를 추가하는 과정입니다.


여기서 surfaceListener는 다음과 같이 선언됩니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
    private SurfaceHolder.Callback surfaceListener = new SurfaceHolder.Callback() {
        
        @Override
        public void surfaceDestroyed(SurfaceHolder holder) {
            // TODO Auto-generated method stub
            camera.release();
            
        }
        
        @Override
        public void surfaceCreated(SurfaceHolder holder) {
            // TODO Auto-generated method stub
            camera = Camera.open();
            try {
                camera.setPreviewDisplay(holder);
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            
        }
        
        @Override
        public void surfaceChanged(SurfaceHolder holder, int formatint width, int height) {
            // TODO Auto-generated method stub
            Camera.Parameters parameters = camera.getParameters();
            parameters.setPreviewSize(width, height);
            camera.startPreview();
            
        }
    };
cs


 위에서 보시는 것과 같이 SurfaceHolder 내에 있는 Callback 인터페이스 내의 3개의 Method를 선언한 것을 보실 수 있습니다. 이 내용이 SurfaceView 내의 Holder에 적용되는 것이지요.


 이번에는 SurfaceView에서 getHolder()가 동작하는 방식에 대해 보도록 하겠습니다.


/frameworks/base/core/java/android/view/SurfaceView.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
public class SurfaceView extends View {
 
    ....
 
    private final SurfaceHolder mSurfaceHolder = new SurfaceHolder() {
 
        private static final String LOG_TAG = "SurfaceHolder";
 
        @Override
        public boolean isCreating() {
            return mIsCreating;
        }
 
        @Override
        public void addCallback(Callback callback) {
            synchronized (mCallbacks) {
                // This is a linear search, but in practice we'll
                // have only a couple callbacks, so it doesn't matter.
                if (mCallbacks.contains(callback) == false) {
                    mCallbacks.add(callback);
                }
            }
        }
 
    ....
 
    }
 
    ....
 
    /**
     * Return the SurfaceHolder providing access and control over this
     * SurfaceView's underlying surface.
     *
     * @return SurfaceHolder The holder of the surface.
     */
    public SurfaceHolder getHolder() {
        return mSurfaceHolder;
    }
 
}
cs


 SurfaceView 클래스를 선언한 후 getHolder() 함수를 호출하게 되면 위의 소스에서 보는 바와 같이 SurfaceView 클래스 내에 있는 mSurfaceHolder 객체를 return 해주는 구조임을 알 수 있습니다.


 mSurfaceHolder는 SurfaceView 클래스 내부에 Interface인 SurafceHolder.Callback을 정의하여 선언하였음을 확인하실 수 있습니다. 해당 Holder 내에 addCallbacks()가 정의되어있어 Application 단계에서 설정하였던 surfaceListener를 등록하고 있는 모습을 보실 수 있습니다.


 그렇다면 SurfaceView 클래스 내부에 있는 mCallbacks 객체에 등록된 surfaceListener는 어떤 방식으로 동작하는지 살펴보도록 하겠습니다. mCallbacks 또한 SurfaceView.java 내부에서 동작되는 모습을 확인하실 수 있습니다.


 아래 코드의 내용들이 상당히 많습니다만 한 줄 한 줄이 상당히 중요한 관계로 관련 코드를 모두 올려봅니다. 물론 핵심 코드는 볼드체 및 밑줄로 눈에 띄도록 표기하였습니다.


/frameworks/base/core/java/android/view/SurfaceView.java


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
public class SurfaceView extends View {
 
    static private final String TAG = "SurfaceView";
 
    static private final boolean DEBUG = false;
 
    final ArrayList<SurfaceHolder.Callback> mCallbacks
            = new ArrayList<SurfaceHolder.Callback>();
    ....
 
    private void updateWindow(boolean force, boolean redrawNeeded) {
    if (!mHaveFrame) {
            return;
        }
        ViewRootImpl viewRoot = getViewRootImpl();
        if (viewRoot != null) {
            mTranslator = viewRoot.mTranslator;
        }
 
        if (mTranslator != null) {
            mSurface.setCompatibilityTranslator(mTranslator);
        }
 
        int myWidth = mRequestedWidth;
        if (myWidth <= 0) myWidth = getWidth();
        int myHeight = mRequestedHeight;
        if (myHeight <= 0) myHeight = getHeight();
 
        getLocationInWindow(mLocation);
        final boolean creating = mWindow == null;
        final boolean formatChanged = mFormat != mRequestedFormat;
        final boolean sizeChanged = mWidth != myWidth || mHeight != myHeight;
        final boolean visibleChanged = mVisible != mRequestedVisible;
 
        if (force || creating || formatChanged || sizeChanged || visibleChanged
            || mLeft != mLocation[0|| mTop != mLocation[1]
            || mUpdateWindowNeeded || mReportDrawNeeded || redrawNeeded) {
 
            if (DEBUG) Log.i(TAG, "Changes: creating=" + creating
                    + " format=" + formatChanged + " size=" + sizeChanged
                    + " visible=" + visibleChanged
                    + " left=" + (mLeft != mLocation[0])
                    + " top=" + (mTop != mLocation[1]));
 
            ....
            try {
                final boolean visible = mVisible = mRequestedVisible;
                mLeft = mLocation[0];
                mTop = mLocation[1];
                mWidth = myWidth;
                mHeight = myHeight;
                mFormat = mRequestedFormat;
 
        ....
 
 
                mSurfaceLock.lock();
        try {
                    redrawNeeded |= creating | reportDrawNeeded;
 
                    SurfaceHolder.Callback callbacks[] = null;
 
                    final boolean surfaceChanged = (relayoutResult
                            & WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED) != 0;
                    if (mSurfaceCreated && (surfaceChanged || (!visible && visibleChanged))) {
                        mSurfaceCreated = false;
                        if (mSurface.isValid()) {
                            if (DEBUG) Log.i(TAG, "visibleChanged -- surfaceDestroyed");
                            callbacks = getSurfaceCallbacks();
                            for (SurfaceHolder.Callback c : callbacks) {
                                c.surfaceDestroyed(mSurfaceHolder);
                            }
                        }
                    }
 
                    mSurface.transferFrom(mNewSurface);
 
                    if (visible && mSurface.isValid()) {
                        if (!mSurfaceCreated && (surfaceChanged || visibleChanged)) {
                            mSurfaceCreated = true;
                            mIsCreating = true;
                            if (DEBUG) Log.i(TAG, "visibleChanged -- surfaceCreated");
                            if (callbacks == null) {
                                callbacks = getSurfaceCallbacks();
                            }
                            for (SurfaceHolder.Callback c : callbacks) {
                                c.surfaceCreated(mSurfaceHolder);
                            }
                        }
                        if (creating || formatChanged || sizeChanged
                                || visibleChanged || realSizeChanged) {
                            if (DEBUG) Log.i(TAG, "surfaceChanged -- format=" + mFormat
                                    + " w=" + myWidth + " h=" + myHeight);
                            if (callbacks == null) {
                                callbacks = getSurfaceCallbacks();
                            }
                            for (SurfaceHolder.Callback c : callbacks) {
                                c.surfaceChanged(mSurfaceHolder, mFormat, myWidth, myHeight);
                            }
                        }
                        if (redrawNeeded) {
                            if (DEBUG) Log.i(TAG, "surfaceRedrawNeeded");
                            if (callbacks == null) {
                                callbacks = getSurfaceCallbacks();
                            }
                            for (SurfaceHolder.Callback c : callbacks) {
                                if (c instanceof SurfaceHolder.Callback2) {
                                    ((SurfaceHolder.Callback2)c).surfaceRedrawNeeded(
                                            mSurfaceHolder);
                                }
                            }
                        }
                    }
                } finally {
                    mIsCreating = false;
                    if (redrawNeeded) {
                        if (DEBUG) Log.i(TAG, "finishedDrawing");
                        mSession.finishDrawing(mWindow);
                    }
                    mSession.performDeferredDestroy(mWindow);
                }
            } catch (RemoteException ex) {
            }
            if (DEBUG) Log.v(
                TAG, "Layout: x=" + mLayout.x + " y=" + mLayout.y +
                " w=" + mLayout.width + " h=" + mLayout.height +
                ", frame=" + mSurfaceFrame);
        }  
    ....
    private SurfaceHolder.Callback[] getSurfaceCallbacks() {
        SurfaceHolder.Callback callbacks[];
        synchronized (mCallbacks) {
            callbacks = new SurfaceHolder.Callback[mCallbacks.size()];
            mCallbacks.toArray(callbacks);
        }
        return callbacks;
    }
    ....
}
cs

 위 코드를 처음 보는 사람이라면 코드의 양이 다소 방대하여 이해하기 힘드실 수 있습니다. 일단 위에서 강조한 부분에 대해 좀 더 자세히 설명드리도록 하겠습니다.


 final ArrayList<SurfaceHolder.Callback> mCallbacks  = new ArrayList<SurfaceHolder.Callback>();

 mCallBacks는 Java에서 제공하는 ArrayList로 구성된 Object입니다. 이 ArrayList 안에 Application에서 호출했던 addCallbacks()를 통해 Listener가 등록됩니다.


 private void updateWindow(boolean force, boolean redrawNeeded)

 본 함수를 통해 SurfaceView의 화면이 변경될 수 있도록 할 수 있습니다. 해당 함수를 잘 보시면 SurfaceHolder.Callback 인터페이스를 통해 선언한 함수들이 실행되는 것을 보실 수 있습니다.


300x250

[VMware] NX / XD is required. The processor must support and it must be enabled in the BIOS.

공대생의 팁 2015. 2. 21. 21:24

 올해 말에 정식으로 공개될 Windows 10의 체험판을 사용해 보기 위해 VMware를 설치한 후 Windows 10를 설치해보려고 하였을 때 다음과 같은 에러를 맞이하게 되었습니다.



 평소 VirtualBox만 사용하다 보니 이러한 오류는 생전 처음 보는 상황이었습니다. 확인해보니 자신의 CPU 설정에 문제가 있어 위와 같은 상황이 발생한 것이었습니다. Intel사의 CPU의 경우 XD, AMD사의 CPU의 경우 NX 비트 설정때문에 저러한 상황이 발생합니다.

 NX(Not eXecute)와 XD(eXecute Disable)은 모두 같은 것을 의미하는 것으로 프로그램 실행중 특정 비트를 통해 해당 프로그램을 실행하지 않도록 설정하는 Bit를 마련할 것인지를 묻는 것을 의미합니다.


 위와 같은 경고문이 뜨면서 VMware가 더이상 진행이 되지 않는 이유는 자신의 컴퓨터 CPU가 해당 기능을 설정하지 않았기 때문에 발생합니다. 이 설정은 자신의 컴퓨터의 BIOS 설정 모드를 통해 바꾸어 주실 수 있습니다. 컴퓨터를 켜는 순간 바로 BIOS 설정모드(대부분 F2 또는 F10)으로 들어가셔서 CPU 관련 설정 부분을 보시면 해당 설정 유무를 결정할 수 있는 메뉴를 찾으실 수 있습니다. 해당 메뉴를 Enabled로 설정하신 후 컴퓨터를 재부팅 하시면 VMware가 정상적으로 실행되는 것을 보실 수 있습니다.



 위 사진은 Samsung Magic Station에서의 BIOS 설정 화면을 나타낸 것입니다. 보이시는 대로 No Execute Protection을 Enabled로 설정해주시면 되겠습니다.

300x250