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 static org.junit.Assert.assertEquals;
20 import static org.junit.Assert.assertFalse;
21 import static org.junit.Assert.assertNotNull;
22 import static org.junit.Assert.assertNull;
23 import static org.junit.Assert.assertThrows;
24 import static org.junit.Assert.assertTrue;
25 import static org.junit.Assert.fail;
26 
27 import android.os.Binder;
28 import android.os.IBinder;
29 import android.os.IInterface;
30 import android.os.ParcelFileDescriptor;
31 import android.os.Process;
32 import android.os.WorkSource;
33 import android.platform.test.annotations.AppModeSdkSandbox;
34 import android.platform.test.annotations.IgnoreUnderRavenwood;
35 import android.platform.test.ravenwood.RavenwoodRule;
36 
37 import org.junit.Before;
38 import org.junit.Rule;
39 import org.junit.Test;
40 
41 import java.io.ByteArrayOutputStream;
42 import java.io.FileDescriptor;
43 import java.io.PrintWriter;
44 import java.util.Arrays;
45 import java.util.concurrent.CountDownLatch;
46 import java.util.concurrent.TimeUnit;
47 
48 @AppModeSdkSandbox(reason = "Allow test in the SDK sandbox (does not prevent other modes).")
49 public class BinderTest {
50     @Rule public RavenwoodRule mRavenwood = new RavenwoodRule.Builder().setProcessApp().build();
51 
52     private static final String DESCRIPTOR_GOOGLE = "google";
53     private static final String DESCRIPTOR_ANDROID = "android";
54     private MockBinder mBinder;
55 
56     @Before
setUp()57     public void setUp() throws Exception {
58         mBinder = new MockBinder();
59     }
60 
61     @Test
testSimpleMethods()62     public void testSimpleMethods() {
63         new Binder();
64 
65         assertEquals(Process.myPid(), Binder.getCallingPid());
66         assertEquals(Process.myUid(), Binder.getCallingUid());
67 
68         assertTrue(mBinder.isBinderAlive());
69 
70         mBinder.linkToDeath(new MockDeathRecipient(), 0);
71 
72         assertTrue(mBinder.unlinkToDeath(new MockDeathRecipient(), 0));
73 
74         assertTrue(mBinder.pingBinder());
75 
76         assertTrue(IBinder.getSuggestedMaxIpcSizeBytes() > 0);
77     }
78 
79     @Test
80     @IgnoreUnderRavenwood
testDump()81     public void testDump() {
82         final String[] dumpArgs = new String[]{"one", "two", "three"};
83         mBinder.dump(new FileDescriptor(),
84                 new PrintWriter(new ByteArrayOutputStream()),
85                 dumpArgs);
86 
87         mBinder.dump(new FileDescriptor(), dumpArgs);
88     }
89 
90     @Test
91     @IgnoreUnderRavenwood
testHandleShellCommand()92     public void testHandleShellCommand() throws Exception {
93         String[] cmdArgs = new String[]{"4", "8", "15", "16", "23", "42"};
94 
95         mBinder.handleShellCommand(ParcelFileDescriptor.dup(FileDescriptor.in),
96                 ParcelFileDescriptor.dup(FileDescriptor.out),
97                 ParcelFileDescriptor.dup(FileDescriptor.err), cmdArgs);
98     }
99 
100     @Test
testFlushPendingCommands()101     public void testFlushPendingCommands() {
102         Binder.flushPendingCommands();
103     }
104 
105     @Test
106     @IgnoreUnderRavenwood(reason = "Requires kernel support")
testJoinThreadPool()107     public void testJoinThreadPool() {
108         final CountDownLatch waitLatch = new CountDownLatch(1);
109         final CountDownLatch alertLatch = new CountDownLatch(1);
110         Thread joinThread = new Thread("JoinThreadPool-Thread") {
111             @Override
112             public void run() {
113                 waitLatch.countDown();
114                 Binder.joinThreadPool();
115                 // Should not reach here. Let the main thread know.
116                 alertLatch.countDown();
117             }
118         };
119         joinThread.setDaemon(true);
120         joinThread.start();
121         try {
122             assertTrue(waitLatch.await(10, TimeUnit.SECONDS));
123         } catch (InterruptedException e) {
124             fail("InterruptedException");
125         }
126         try {
127             // This waits a small amount of time, hoping that if joinThreadPool
128             // fails, it fails fast.
129             assertFalse(alertLatch.await(3, TimeUnit.SECONDS));
130         } catch (InterruptedException e) {
131             fail("InterruptedException");
132         }
133         // Confirm that the thread is actually in joinThreadPool.
134         StackTraceElement stack[] = joinThread.getStackTrace();
135         boolean found = false;
136         for (StackTraceElement elem : stack) {
137             if (elem.toString().contains("Binder.joinThreadPool")) {
138                 found = true;
139                 break;
140             }
141         }
142         assertTrue(Arrays.toString(stack), found);
143     }
144 
145     @Test
testClearCallingIdentity()146     public void testClearCallingIdentity() {
147         long token = Binder.clearCallingIdentity();
148         assertTrue(token > 0);
149         Binder.restoreCallingIdentity(token);
150     }
151 
152     @Test
testGetCallingUidOrThrow_throws()153     public void testGetCallingUidOrThrow_throws() throws Exception {
154         assertThrows(IllegalStateException.class, () -> Binder.getCallingUidOrThrow());
155     }
156 
157     @Test
testGetCallingUidOrThrow_insideClearRestoreCallingIdentity_doesNotThrow()158     public void testGetCallingUidOrThrow_insideClearRestoreCallingIdentity_doesNotThrow()
159             throws Exception {
160         long token = Binder.clearCallingIdentity();
161         try {
162             Binder.getCallingUidOrThrow();
163         } finally {
164             Binder.restoreCallingIdentity(token);
165         }
166     }
167 
168     @Test
testGetCallingUidOrThrow_afterClearRestoreCallingIdentity_throws()169     public void testGetCallingUidOrThrow_afterClearRestoreCallingIdentity_throws()
170             throws Exception {
171         long token = Binder.clearCallingIdentity();
172         try {
173             Binder.getCallingUidOrThrow();
174         } finally {
175             Binder.restoreCallingIdentity(token);
176         }
177         // if a token is properly cleared and restored, a subsequent call should throw
178         assertThrows(IllegalStateException.class, () -> Binder.getCallingUidOrThrow());
179     }
180 
181     @Test
testGetCallingUidOrThrow_multipleClearsAreRestoredCorrectly_throws()182     public void testGetCallingUidOrThrow_multipleClearsAreRestoredCorrectly_throws()
183             throws Exception {
184         long outerToken = Binder.clearCallingIdentity();
185         long innerToken = Binder.clearCallingIdentity();
186         try {
187             Binder.getCallingUidOrThrow();
188         } finally {
189             Binder.restoreCallingIdentity(innerToken);
190             Binder.restoreCallingIdentity(outerToken);
191         }
192         // if multiple tokens are cleared and restored in the proper order,
193         // a subsequent call should throw
194         assertThrows(IllegalStateException.class, () -> Binder.getCallingUidOrThrow());
195     }
196 
197     @Test
testGetCallingUidOrThrow_onlyOutermostClearIsRestored_throws()198     public void testGetCallingUidOrThrow_onlyOutermostClearIsRestored_throws() throws Exception {
199         long outerToken = Binder.clearCallingIdentity();
200         long innerToken = Binder.clearCallingIdentity();
201         try {
202             Binder.getCallingUidOrThrow();
203         } finally {
204             Binder.restoreCallingIdentity(outerToken);
205         }
206         // if multiple tokens are cleared, and only the outermost token is restored,
207         // a subsequent call should throw
208         assertThrows(IllegalStateException.class, () -> Binder.getCallingUidOrThrow());
209     }
210 
211     @Test
testGetCallingUidOrThrow_multipleClearsAreRestoredIncorrectly_doesNotThrow()212     public void testGetCallingUidOrThrow_multipleClearsAreRestoredIncorrectly_doesNotThrow()
213             throws Exception {
214         long outerToken = Binder.clearCallingIdentity();
215         long innerToken = Binder.clearCallingIdentity();
216         try {
217             Binder.getCallingUidOrThrow();
218         } finally {
219             Binder.restoreCallingIdentity(outerToken);
220             Binder.restoreCallingIdentity(innerToken);
221         }
222         // if multiple tokens are restored incorrectly,
223         // a subsequent call will not throw
224         Binder.getCallingUidOrThrow();
225     }
226 
227     @Test
testGetCallingUidOrThrow_duplicateClearsAreStoredInSameVariable_doesNotThrow()228     public void testGetCallingUidOrThrow_duplicateClearsAreStoredInSameVariable_doesNotThrow()
229             throws Exception {
230         long token = Binder.clearCallingIdentity();
231         token = Binder.clearCallingIdentity();
232         try {
233             Binder.getCallingUidOrThrow();
234         } finally {
235             Binder.restoreCallingIdentity(token);
236         }
237         // if the same variable is used for multiple clears, a subsequent call will not throw
238         Binder.getCallingUidOrThrow();
239     }
240 
241     @Test
242     @IgnoreUnderRavenwood(blockedBy = WorkSource.class)
testClearCallingWorkSource()243     public void testClearCallingWorkSource() {
244         final long token = Binder.clearCallingWorkSource();
245         Binder.restoreCallingWorkSource(token);
246     }
247 
248     @Test
249     @IgnoreUnderRavenwood(blockedBy = WorkSource.class)
testSetCallingWorkSourceUid()250     public void testSetCallingWorkSourceUid() {
251         final int otherUid = android.os.Process.myUid() + 1;
252         assertFalse(Binder.getCallingWorkSourceUid() == otherUid);
253 
254         final long token = Binder.setCallingWorkSourceUid(otherUid);
255         assertTrue(Binder.getCallingWorkSourceUid() == otherUid);
256         Binder.restoreCallingWorkSource(token);
257 
258         assertFalse(Binder.getCallingWorkSourceUid() == otherUid);
259     }
260 
261     @Test
testInterfaceRelatedMethods()262     public void testInterfaceRelatedMethods() {
263         assertNull(mBinder.getInterfaceDescriptor());
264         mBinder.attachInterface(new MockIInterface(), DESCRIPTOR_GOOGLE);
265         assertEquals(DESCRIPTOR_GOOGLE, mBinder.getInterfaceDescriptor());
266 
267         mBinder.attachInterface(new MockIInterface(), DESCRIPTOR_ANDROID);
268         assertNull(mBinder.queryLocalInterface(DESCRIPTOR_GOOGLE));
269         mBinder.attachInterface(new MockIInterface(), DESCRIPTOR_GOOGLE);
270         assertNotNull(mBinder.queryLocalInterface(DESCRIPTOR_GOOGLE));
271     }
272 
273     private static class MockDeathRecipient implements IBinder.DeathRecipient {
binderDied()274          public void binderDied() {
275 
276          }
277     }
278 
279     private static class MockIInterface implements IInterface {
asBinder()280         public IBinder asBinder() {
281             return new Binder();
282         }
283     }
284 
285     private static class MockBinder extends Binder {
286         @Override
dump(FileDescriptor fd, PrintWriter fout, String[] args)287         public void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
288             super.dump(fd, fout, args);
289         }
290     }
291 
292 }
293