안드로이드 프레임워크 프로그래밍(26) [System Service에서 Activity 함수 호출하기]

 안드로이드 AIDL을 사용하여 RPC통신을 통해 Application 단계에서 Framework 단계의 method를 호출하는 방법에 대해 알아본 바 있습니다. 이번 포스팅에서는 Framework 단계에서 Application의 Activity에 존재하는 method를 호출하는 방법에 대해 알아보도록 하겠습니다.


 포스팅을 시작하기에 앞서 이전에 사용하였던 Application 단계에서 Framework 단계의 기능을 사용한 바 있습니다. 이에 대한 자세한 내용은 아래 포스팅을 참조해주시길 바랍니다. 또한 본 포스팅의 예제 또한 아래 포스팅의 것을 사용하였음을 알립니다.


안드로이드 프레임워크 프로그래밍(4) [시스템서비스 추가하기]

http://elecs.tistory.com/64


 안드로이드 Application을 실행하였을 때 Framework 단계에서 실행하고자 하는 method를 onCreate() 단계에서 등록을 합니다. 그 이후 System Service에서 특정 부분을 호출 받았을 때 Application의 Activity 내에 있는 함수를 실행하는 과정을 구현할 것입니다.


1. AIDL을 통해 Callback 함수 등록 및 해제 함수를 만들어줍니다.


/frameworks/base/core/java/android/os/ITestService.aidl

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
/*
* aidl file : frameworks/base/core/java/android/os/ITestService.aidl
* This file contains definitions of functions which are exposed by service
*/
package android.os;
 
import android.os.IActivityServiceCallback;
 
interface ITestService {
/**
* {@hide}
*/
    void setValue(int val);
    void execCallback(int val);
 
    /**
     * Often you want to allow a service to call back to its clients.
     * This shows how to do so, by registering a callback interface with
     * the service.
     */
    boolean registerCallback(IActivityServiceCallback cb);
    
    /**
     * Remove a previously registered callback interface.
     */
    boolean unregisterCallback(IActivityServiceCallback cb);
 
}
cs

 Activity에서 실행하고자 하는 method는 registerCallback() method를 통해 System Service에 등록할 수 있습니다. execCallback()함수는 이후 Application에서 RPC를 통해 호출을 받은 후 Callback 기능을 구현하기 위해 만든 함수입니다.


2. Activity로부터 호출하고자 하는 함수를 구현할 IActivityServiceCallback.aidl을 생성합니다.


/frameworks/base/core/java/android/os/IActivityServiceCallback.aidl

1
2
3
4
5
6
7
8
9
10
11
12
13
package android.os;
 
/**
 * Example of a callback interface used by IActivityService to send
 * synchronous notifications back to its clients.  Note that this is a
 * one-way interface so the server does not block waiting for the client.
 */
oneway interface IActivityServiceCallback {
    /**
     * Called when the service has a new value for you.
     */
    void callActivityMethod(int value);
}
cs

 위의 과정에서 만들어진 callActivityMethod() method는 이후 Activity에서 Callback 방식으로 구현될 것입니다. 다음으로 System Serivce에서 위의 새로 생성한 함수들을 구현해 보도록 하겠습니다.


/frameworks/base/services/java/com/android/server/TestService.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
/*TestService.java */
package com.android.server;
import android.content.Context;
import android.os.Handler;
import android.os.IActivityServiceCallback;
import android.os.ITestService;
import android.os.Looper;
import android.os.Message;
import android.os.Process;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.util.Log;
 
public class TestService extends ITestService.Stub {
    private static final String TAG = "TestService";
    private TestWorkerThread mWorker;
    private TestWorkerHandler mHandler;
    private Context mContext;
 
    RemoteCallbackList<IActivityServiceCallback> callbacks =
                 new RemoteCallbackList<IActivityServiceCallback>();
 
    public TestService(Context context) {
        super();
        mContext = context;
        mWorker = new TestWorkerThread("TestServiceWorker");
        mWorker.start();
        Log.i(TAG, "Spawned worker thread");
    }
 
    public boolean registerCallback(IActivityServiceCallback cb) throws RemoteException{
        boolean flag = false;
        if(cb != null){
            flag = callbacks.register(cb);
        }
        return flag;
    }
 
    public boolean unregisterCallback(IActivityServiceCallback cb) throws RemoteException{
        boolean flag = false;
        if(cb != null){
            flag = callbacks.unregister(cb);
        }
        return flag;
    }
 
 
    public void setValue(int val) {
        Log.i(TAG, "setValue " + val);
        Message msg = Message.obtain();
        msg.what = TestWorkerHandler.MESSAGE_SET;
        msg.arg1 = val;
        mHandler.sendMessage(msg);
    }
 
    public void execCallback(int val){
     Message msg = Message.obtain();
        msg.what = TestWorkerHandler.CALLBACK;
        msg.arg1 = val;
        mHandler.sendMessage(msg);
    }
 
    private class TestWorkerThread extends Thread {
        public TestWorkerThread(String name) {
            super(name);
        }
        public void run() {
            Looper.prepare();
            mHandler = new TestWorkerHandler();
            Looper.loop();
        }
    }
 
    private class TestWorkerHandler extends Handler {
        private static final int MESSAGE_SET = 0;
        private static final int CALLBACK = 1;
 
        @Override
        public void handleMessage(Message msg) {
            try {
                if (msg.what == MESSAGE_SET) {
                    Log.i(TAG, "set message received: " + msg.arg1);
                }
                if (msg.what == CALLBACK) {
                    int n = callbacks.beginBroadcast();
                    for(int i = 0; i < n ; i++){
                        try {
                            Log.i(TAG, "callbacks.beginBroadcast()");
                            callbacks.getBroadcastItem(i).callActivityMethod(msg.arg1);
                        } catch (RemoteException e) {
                            e.printStackTrace();
                        }
                    }
                    callbacks.finishBroadcast();
                }
            } catch (Exception e) {
                // Log, don't crash!
                Log.e(TAG, "Exception in TestWorkerHandler.handleMessage:", e);
            }
        }
    }
}
cs

 RemoteCallbackList 클래스를 통해 Activity로부터 callback을 등록한 후 execCallback() method가 Application으로부터 호출되면 곧 System Service에서 등록한 callback을 실행하게 됩니다. 아래는 이를 구현한 Application 소스코드입니다.


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
package com.example.test;
 
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.os.IActivityServiceCallback;
import android.os.ITestService;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.TextView;
import android.util.Log;
 
 
public class MainActivity extends Activity {
    private static final String DTAG = "HelloServer";
    ITestService om;
 
    IActivityServiceCallback mCallback = new IActivityServiceCallback.Stub() {
        
        @Override
        public void callActivityMethod(int arg0) throws RemoteException {
            // TODO Auto-generated method stub
            final int value = arg0;
            mHandler.post(new Runnable() {
 
                @Override
                public void run() {
                    // TODO Auto-generated method stub
                    setToast("callActivityMethod : " + value);
                }
            });
        }
    };
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        om = ITestService.Stub.asInterface(ServiceManager.getService("Test"));
        try{
            Log.d(DTAG, "Going to call service");
            om.setValue(20);
            om.registerCallback(mCallback);
            Log.d(DTAG, "Service called successfully");
        }catch(Exception e){
            Log.d(DTAG, "FAILED to call service");
            e.printStackTrace();
        }
        
    }
    
    public void OnClick(View v) throws RemoteException{
        switch(v.getId()){
            case R.id.button1:
                om.execCallback(100);
                break;
        }
    }
 
    void setToast(String msg) {
        Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
    }
 
    protected void onDestroy(){
        super.onDestroy();
        try {
            om.unregisterCallback(mCallback);
        } catch (RemoteException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        
    }
 
}
cs

 위 함수를 실행하면 다음과 같은 결과를 얻으실 수 있습니다.