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