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 import java.io.ByteArrayOutputStream;
20 import java.io.FileDescriptor;
21 import java.io.PrintWriter;
22 import java.util.concurrent.CountDownLatch;
23 import java.util.concurrent.TimeUnit;
24 import java.util.Arrays;
25 
26 import android.content.ComponentName;
27 import android.content.Intent;
28 import android.content.ServiceConnection;
29 import android.os.Binder;
30 import android.os.IBinder;
31 import android.os.IInterface;
32 import android.os.Parcel;
33 import android.os.Process;
34 import android.os.RemoteException;
35 
36 public class BinderTest extends ActivityTestsBase {
37     private static final String DESCRIPTOR_GOOGLE = "google";
38     private static final String DESCRIPTOR_ANDROID = "android";
39     // states of mStartState
40     private static final int STATE_START_1 = 0;
41     private static final int STATE_START_2 = 1;
42     private static final int STATE_UNBIND = 2;
43     private static final int STATE_DESTROY = 3;
44     private static final int STATE_REBIND = 4;
45     private static final int STATE_UNBIND_ONLY = 5;
46     private static final int DELAY_MSEC = 5000;
47     private MockBinder mBinder;
48     private Binder mStartReceiver;
49     private int mStartState;
50     private Intent mService;
51 
52     @Override
setUp()53     protected void setUp() throws Exception {
54         super.setUp();
55         mService = new Intent(
56                 LocalService.SERVICE_LOCAL, null /*uri*/, mContext, LocalService.class);
57         mBinder = new MockBinder();
58         mStartReceiver = new Binder() {
59             @Override
60             protected boolean onTransact(int code, Parcel data, Parcel reply, int flags)
61                              throws RemoteException {
62                 switch (code) {
63                     case LocalService.STARTED_CODE:
64                         data.enforceInterface(LocalService.SERVICE_LOCAL);
65                         int count = data.readInt();
66 
67                         switch (mStartState) {
68                             case STATE_START_1:
69                                 if (count == 1) {
70                                     finishGood();
71                                 } else {
72                                     finishBad("onStart() again on an object when it "
73                                             + "should have been the first time");
74                                 }
75                                 break;
76                             case STATE_START_2:
77                                 if (count == 2) {
78                                     finishGood();
79                                 } else {
80                                     finishBad("onStart() the first time on an object when it "
81                                             + "should have been the second time");
82                                 }
83                                 break;
84                             default:
85                                 finishBad("onStart() was called when not expected (state="
86                                         + mStartState + ")");
87                         }
88                         return true;
89                     case LocalService.DESTROYED_CODE:
90                         data.enforceInterface(LocalService.SERVICE_LOCAL);
91                         if (mStartState == STATE_DESTROY) {
92                             finishGood();
93                         } else {
94                             finishBad("onDestroy() was called when not expected (state="
95                                     + mStartState + ")");
96                         }
97                         return true;
98                     case LocalService.UNBIND_CODE:
99                         data.enforceInterface(LocalService.SERVICE_LOCAL);
100                         switch (mStartState) {
101                             case STATE_UNBIND:
102                                 mStartState = STATE_DESTROY;
103                                 break;
104                             case STATE_UNBIND_ONLY:
105                                 finishGood();
106                                 break;
107                             default:
108                                 finishBad("onUnbind() was called when not expected (state="
109                                         + mStartState + ")");
110                         }
111                         return true;
112                     case LocalService.REBIND_CODE:
113                         data.enforceInterface(LocalService.SERVICE_LOCAL);
114                         if (mStartState == STATE_REBIND) {
115                             finishGood();
116                         } else {
117                             finishBad("onRebind() was called when not expected (state="
118                                     + mStartState + ")");
119                         }
120                         return true;
121                     default:
122                         return super.onTransact(code, data, reply, flags);
123                 }
124             }
125         };
126 
127     }
128 
129     @Override
tearDown()130     protected void tearDown() throws Exception {
131         super.tearDown();
132         mContext.stopService(mService);
133     }
134 
135     // Mock ServiceConnection
136     public class MockServiceConnection implements ServiceConnection {
137         private final boolean mIsDisconnect;
138         private final boolean mSetReporter;
139         private boolean mIsMonitorEnable;
140         private int mCount;
141 
MockServiceConnection(final boolean isDisconnect, final boolean setReporter)142         public MockServiceConnection(final boolean isDisconnect, final boolean setReporter) {
143             mIsDisconnect = isDisconnect;
144             mSetReporter = setReporter;
145             mIsMonitorEnable = !setReporter;
146         }
147 
setMonitor(boolean v)148         void setMonitor(boolean v) {
149             mIsMonitorEnable = v;
150         }
151 
onServiceConnected(ComponentName name, IBinder service)152         public void onServiceConnected(ComponentName name, IBinder service) {
153             if (mSetReporter) {
154                 Parcel data = Parcel.obtain();
155                 data.writeInterfaceToken(LocalService.SERVICE_LOCAL);
156                 data.writeStrongBinder(mStartReceiver);
157 
158                 try {
159                     service.transact(LocalService.SET_REPORTER_CODE, data, null, 0);
160                 } catch (RemoteException e) {
161                     finishBad("DeadObjectException when sending reporting object");
162                 }
163 
164                 data.recycle();
165             }
166 
167             if (mIsMonitorEnable) {
168                 mCount++;
169 
170                 if (mStartState == STATE_START_1) {
171                     if (mCount == 1) {
172                         finishGood();
173                     } else {
174                         finishBad("onServiceConnected() again on an object when it "
175                                 + "should have been the first time");
176                     }
177                 } else if (mStartState == STATE_START_2) {
178                     if (mCount == 2) {
179                         finishGood();
180                     } else {
181                         finishBad("onServiceConnected() the first time on an object "
182                                 + "when it should have been the second time");
183                     }
184                 } else {
185                     finishBad("onServiceConnected() called unexpectedly");
186                 }
187             }
188         }
189 
onServiceDisconnected(ComponentName name)190         public void onServiceDisconnected(ComponentName name) {
191             if (mIsMonitorEnable) {
192                 if (mStartState == STATE_DESTROY) {
193                     if (mIsDisconnect) {
194                         finishGood();
195                     } else {
196                         finishBad("onServiceDisconnected() when it shouldn't have been");
197                     }
198                 } else {
199                     finishBad("onServiceDisconnected() called unexpectedly");
200                 }
201             }
202         }
203     }
204 
testTransact()205     public void testTransact() {
206         MockServiceConnection conn1 = new MockServiceConnection(true, false);
207         MockServiceConnection conn2 = new MockServiceConnection(false, false);
208         boolean success = false;
209 
210         try {
211             // Expect to see the TestConnection connected.
212             mStartState = STATE_START_1;
213             getContext().bindService(mService, conn1, 0);
214             getContext().startService(mService);
215             waitForResultOrThrow(DELAY_MSEC, "existing connection to receive service");
216 
217             // Expect to see the second TestConnection connected.
218             getContext().bindService(mService, conn2, 0);
219             waitForResultOrThrow(DELAY_MSEC, "new connection to receive service");
220 
221             getContext().unbindService(conn2);
222             success = true;
223         } finally {
224             if (!success) {
225                 try {
226                 getContext().stopService(mService);
227                 getContext().unbindService(conn1);
228                 getContext().unbindService(conn2);
229                 } catch (SecurityException e) {
230                     fail(e.getMessage());
231                 }
232             }
233         }
234 
235         // Expect to see the TestConnection disconnected.
236         mStartState = STATE_DESTROY;
237         getContext().stopService(mService);
238         waitForResultOrThrow(DELAY_MSEC, "the existing connection to lose service");
239 
240         getContext().unbindService(conn1);
241 
242         conn1 = new MockServiceConnection(true, true);
243         success = false;
244 
245         try {
246             // Expect to see the TestConnection connected.
247             conn1.setMonitor(true);
248             mStartState = STATE_START_1;
249             getContext().bindService(mService, conn1, 0);
250             getContext().startService(mService);
251             waitForResultOrThrow(DELAY_MSEC, "the existing connection to receive service");
252 
253             success = true;
254         } finally {
255             if (!success) {
256                 try {
257                     getContext().stopService(mService);
258                     getContext().unbindService(conn1);
259                 } catch (Exception e) {
260                     fail(e.getMessage());
261                 }
262             }
263         }
264 
265         // Expect to see the service unbind and then destroyed.
266         conn1.setMonitor(false);
267         mStartState = STATE_UNBIND;
268         getContext().stopService(mService);
269         waitForResultOrThrow(DELAY_MSEC, "the existing connection to lose service");
270 
271         getContext().unbindService(conn1);
272 
273         conn1 = new MockServiceConnection(true, true);
274         success = false;
275 
276         try {
277             // Expect to see the TestConnection connected.
278             conn1.setMonitor(true);
279             mStartState = STATE_START_1;
280             getContext().bindService(mService, conn1, 0);
281             getContext().startService(mService);
282             waitForResultOrThrow(DELAY_MSEC, "existing connection to receive service");
283 
284             success = true;
285         } finally {
286             if (!success) {
287                 try {
288                     getContext().stopService(mService);
289                     getContext().unbindService(conn1);
290                 } catch (Exception e) {
291                     fail(e.getMessage());
292                 }
293             }
294         }
295 
296         // Expect to see the service unbind but not destroyed.
297         conn1.setMonitor(false);
298         mStartState = STATE_UNBIND_ONLY;
299         getContext().unbindService(conn1);
300         waitForResultOrThrow(DELAY_MSEC, "existing connection to unbind service");
301 
302         // Expect to see the service rebound.
303         mStartState = STATE_REBIND;
304         getContext().bindService(mService, conn1, 0);
305         waitForResultOrThrow(DELAY_MSEC, "existing connection to rebind service");
306 
307         // Expect to see the service unbind and then destroyed.
308         mStartState = STATE_UNBIND;
309         getContext().stopService(mService);
310         waitForResultOrThrow(DELAY_MSEC, "existing connection to lose service");
311 
312         getContext().unbindService(conn1);
313     }
314 
testSimpleMethods()315     public void testSimpleMethods() {
316         new Binder();
317 
318         assertEquals(Process.myPid(), Binder.getCallingPid());
319         assertEquals(Process.myUid(), Binder.getCallingUid());
320 
321         final String[] dumpArgs = new String[]{"one", "two", "three"};
322         mBinder.dump(new FileDescriptor(),
323                 new PrintWriter(new ByteArrayOutputStream()),
324                 dumpArgs);
325 
326         mBinder.dump(new FileDescriptor(), dumpArgs);
327         assertTrue(mBinder.isBinderAlive());
328 
329         mBinder.linkToDeath(new MockDeathRecipient(), 0);
330 
331         assertTrue(mBinder.unlinkToDeath(new MockDeathRecipient(), 0));
332 
333         assertTrue(mBinder.pingBinder());
334     }
335 
testFlushPendingCommands()336     public void testFlushPendingCommands() {
337         Binder.flushPendingCommands();
338     }
339 
testJoinThreadPool()340     public void testJoinThreadPool() {
341         final CountDownLatch waitLatch = new CountDownLatch(1);
342         final CountDownLatch alertLatch = new CountDownLatch(1);
343         Thread joinThread = new Thread("JoinThreadPool-Thread") {
344             @Override
345             public void run() {
346                 waitLatch.countDown();
347                 Binder.joinThreadPool();
348                 // Should not reach here. Let the main thread know.
349                 alertLatch.countDown();
350             }
351         };
352         joinThread.setDaemon(true);
353         joinThread.start();
354         try {
355             assertTrue(waitLatch.await(10, TimeUnit.SECONDS));
356         } catch (InterruptedException e) {
357             fail("InterruptedException");
358         }
359         try {
360             // This waits a small amount of time, hoping that if joinThreadPool
361             // fails, it fails fast.
362             assertFalse(alertLatch.await(3, TimeUnit.SECONDS));
363         } catch (InterruptedException e) {
364             fail("InterruptedException");
365         }
366         // Confirm that the thread is actually in joinThreadPool.
367         StackTraceElement stack[] = joinThread.getStackTrace();
368         boolean found = false;
369         for (StackTraceElement elem : stack) {
370             if (elem.toString().contains("Binder.joinThreadPool")) {
371                 found = true;
372                 break;
373             }
374         }
375         assertTrue(Arrays.toString(stack), found);
376     }
377 
testClearCallingIdentity()378     public void testClearCallingIdentity() {
379         long token = Binder.clearCallingIdentity();
380         assertTrue(token > 0);
381         Binder.restoreCallingIdentity(token);
382     }
383 
testInterfaceRelatedMethods()384     public void testInterfaceRelatedMethods() {
385         assertNull(mBinder.getInterfaceDescriptor());
386         mBinder.attachInterface(new MockIInterface(), DESCRIPTOR_GOOGLE);
387         assertEquals(DESCRIPTOR_GOOGLE, mBinder.getInterfaceDescriptor());
388 
389         mBinder.attachInterface(new MockIInterface(), DESCRIPTOR_ANDROID);
390         assertNull(mBinder.queryLocalInterface(DESCRIPTOR_GOOGLE));
391         mBinder.attachInterface(new MockIInterface(), DESCRIPTOR_GOOGLE);
392         assertNotNull(mBinder.queryLocalInterface(DESCRIPTOR_GOOGLE));
393     }
394 
395     private static class MockDeathRecipient implements IBinder.DeathRecipient {
binderDied()396          public void binderDied() {
397 
398          }
399     }
400 
401     private static class MockIInterface implements IInterface {
asBinder()402         public IBinder asBinder() {
403             return new Binder();
404         }
405     }
406 
407     private static class MockBinder extends Binder {
408         @Override
dump(FileDescriptor fd, PrintWriter fout, String[] args)409         public void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
410             super.dump(fd, fout, args);
411         }
412     }
413 
414 }
415