1 /*
2  * Copyright (C) 2008 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 package android.os.cts;
18 
19 
20 import android.content.ComponentName;
21 import android.content.Context;
22 import android.content.Intent;
23 import android.content.ServiceConnection;
24 import android.os.Binder;
25 import android.os.Handler;
26 import android.os.HandlerThread;
27 import android.os.IBinder;
28 import android.os.Looper;
29 import android.os.Message;
30 import android.os.Messenger;
31 import android.os.Parcel;
32 import android.os.RemoteException;
33 import android.test.AndroidTestCase;
34 
35 public class MessengerTest extends AndroidTestCase {
36 
37     private Messenger mMessenger;
38     private Message mMessage;
39     private boolean mResult;
40     private Messenger mServiceMessenger;
41     private static final int MSG_ARG1 = 100;
42     private static final int MSG_ARG2 = 1000;
43     private static final int WHAT = 2008;
44     private Handler mHandler = new Handler(Looper.getMainLooper()) {
45         @Override
46         public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
47             mResult = true;
48             mMessage = msg;
49             return super.sendMessageAtTime(msg, uptimeMillis);
50         }
51     };
52 
53     private final IBinder mIBinder = new Binder();
54 
55     // Create another messenger to send msg.
56     private ServiceConnection mConnection = new ServiceConnection() {
57         public void onServiceConnected(ComponentName name, IBinder service) {
58             synchronized (MessengerTest.this) {
59                 mServiceMessenger = new Messenger(service);
60                 MessengerTest.this.notifyAll();
61             }
62         }
63 
64         public void onServiceDisconnected(ComponentName name) {
65             mServiceMessenger = null;
66         }
67     };
68 
69     @Override
setUp()70     protected void setUp() throws Exception {
71 
72         super.setUp();
73         mMessenger = new Messenger(mHandler);
74         getContext().bindService(new Intent(mContext, MessengerService.class), mConnection,
75                 Context.BIND_AUTO_CREATE);
76         synchronized (this) {
77             while (mServiceMessenger == null) {
78                 wait();
79             }
80         }
81     }
82 
83     @Override
tearDown()84     protected void tearDown() throws Exception {
85         super.tearDown();
86         getContext().unbindService(mConnection);
87     }
88 
testConstructorAndEquals()89     public void testConstructorAndEquals() {
90         Messenger messenger = new Messenger(mHandler);
91         Messenger objMessenger = new Messenger(mHandler);
92         assertTrue(messenger.equals(objMessenger));
93         messenger = new Messenger(mIBinder);
94         assertFalse(messenger.equals(objMessenger));
95     }
96 
testSend()97     public void testSend() throws RemoteException, InterruptedException {
98         // messenger used by its own thread.
99         Message message = Message.obtain(mHandler, WHAT, MSG_ARG1, MSG_ARG2);
100         mMessenger.send(message);
101         assertTrue(mResult);
102         assertNotNull(mMessage);
103         assertEquals(mMessage.what, message.what);
104         assertEquals(mMessage.arg1, message.arg1);
105         assertEquals(mMessage.arg2, message.arg2);
106 
107         // Used in other process. If the sent msg does not equal to expected, it will throw failure
108         // and the test will fail
109         (new MessengerTestHelper()).doTest(1000, 50);
110     }
111 
testHashCode()112     public void testHashCode() {
113         assertEquals(mMessenger.getBinder().hashCode(), mMessenger.hashCode());
114     }
115 
testGetBinder()116     public void testGetBinder() {
117         Messenger messenger = new Messenger(mIBinder);
118         assertSame(mIBinder, messenger.getBinder());
119         assertNotNull(mMessenger.getBinder());
120     }
121 
testWriteToParcel()122     public void testWriteToParcel() {
123         Parcel parcel = Parcel.obtain();
124         mMessenger.writeToParcel(parcel, 0);
125         parcel.setDataPosition(0);
126         Messenger messenger = Messenger.CREATOR.createFromParcel(parcel);
127         assertTrue(messenger.equals(mMessenger));
128         parcel.recycle();
129     }
130 
testDescribeContents()131     public void testDescribeContents() {
132         assertEquals(0, mMessenger.describeContents());
133     }
134 
testWriteMessengerOrNullToParcel()135     public void testWriteMessengerOrNullToParcel() {
136         Parcel parcelWithMessenger = Parcel.obtain();
137         Messenger.writeMessengerOrNullToParcel(mMessenger, parcelWithMessenger);
138         parcelWithMessenger.setDataPosition(0);
139         Messenger messenger = Messenger.readMessengerOrNullFromParcel(parcelWithMessenger);
140         assertNotNull(messenger);
141         assertTrue(messenger.equals(mMessenger));
142         parcelWithMessenger.recycle();
143 
144         Parcel parcelWithNull = Parcel.obtain();
145         Messenger.writeMessengerOrNullToParcel(null, parcelWithNull);
146         parcelWithNull.setDataPosition(0);
147         messenger = Messenger.readMessengerOrNullFromParcel(parcelWithNull);
148         assertNull(messenger);
149         parcelWithNull.recycle();
150     }
151 
152     /**
153      * This helper class is used for test of MessengerTest. Mainly on control of the message looper.
154      */
155     private class MessengerTestHelper {
156         private boolean mDone = false;
157         private boolean mSuccess = false;
158         private RuntimeException mFailure = null;
159         private Looper mLooper;
160 
161         private Handler mTestHandler;
162         private Messenger mTestMessenger;
163 
init()164         public void init() {
165             synchronized (MessengerTest.this) {
166                 mTestHandler = new Handler() {
167                     public void handleMessage(Message msg) {
168                         MessengerTestHelper.this.handleMessage(msg);
169                     }
170                 };
171                 mTestMessenger = new Messenger(mTestHandler);
172                 try {
173                     MessengerTestHelper.this.executeTest();
174                 } catch (RemoteException e) {
175                     fail(e.getMessage());
176                 }
177             }
178         }
179 
MessengerTestHelper()180         public MessengerTestHelper() {
181         }
182 
executeTest()183         public void executeTest() throws RemoteException {
184             Message msg = Message.obtain();
185             msg.arg1 = MSG_ARG1;
186             msg.arg2 = MSG_ARG2;
187             msg.replyTo = mTestMessenger;
188             // Use another messenger to send msg.
189             mServiceMessenger.send(msg);
190         }
191 
192         /**
193          * This method is used to check if the message sent by another messenger is correctly
194          * handled by this thread. If not equals to expected, there will be a failure thrown.
195          */
handleMessage(Message msg)196         public void handleMessage(Message msg) {
197             if (msg.arg1 != MSG_ARG1) {
198                 failure(new RuntimeException("Message.arg1 is not " + MSG_ARG1 + ", it's "
199                         + msg.arg1));
200                 return;
201             }
202             if (msg.arg2 != MSG_ARG2) {
203                 failure(new RuntimeException("Message.arg2 is not " + MSG_ARG2 + ", it's "
204                         + msg.arg2));
205                 return;
206             }
207             if (!mTestMessenger.equals(msg.replyTo)) {
208                 failure(new RuntimeException("Message.replyTo is not me, it's " + msg.replyTo));
209                 return;
210             }
211             success();
212         }
213 
doTest(long timeout, long interval)214         public void doTest(long timeout, long interval) throws InterruptedException {
215             (new LooperThread()).start();
216 
217             synchronized (this) {
218                 long now = System.currentTimeMillis();
219                 long endTime = now + timeout;
220                 // wait and frequently check if mDone is set.
221                 while (!mDone && now < endTime) {
222                     wait(interval);
223                     now = System.currentTimeMillis();
224                 }
225             }
226 
227             mLooper.quit();
228 
229             if (!mDone) {
230                 throw new RuntimeException("test timed out");
231             }
232             if (!mSuccess) {
233                 throw mFailure;
234             }
235         }
236 
getLooper()237         public Looper getLooper() {
238             return mLooper;
239         }
240 
success()241         public void success() {
242             synchronized (this) {
243                 mSuccess = true;
244                 quit();
245             }
246         }
247 
failure(RuntimeException failure)248         public void failure(RuntimeException failure) {
249             synchronized (this) {
250                 mSuccess = false;
251                 mFailure = failure;
252                 quit();
253             }
254         }
255 
256         class LooperThread extends HandlerThread {
257 
LooperThread()258             public LooperThread() {
259                 super("MessengerLooperThread");
260             }
261 
onLooperPrepared()262             public void onLooperPrepared() {
263                 init();
264                 mLooper = getLooper();
265             }
266 
267             @Override
run()268             public void run() {
269                 super.run();
270                 synchronized (MessengerTestHelper.this) {
271                     mDone = true;
272                     if (!mSuccess && mFailure == null) {
273                         mFailure = new RuntimeException("no failure exception set");
274                     }
275                     MessengerTestHelper.this.notifyAll();
276                 }
277             }
278         }
279 
quit()280         private void quit() {
281             synchronized (this) {
282                 mDone = true;
283                 notifyAll();
284             }
285         }
286     }
287 }
288