1 /*
2  * Copyright (C) 2017 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 com.android.cts.comp;
18 
19 import static junit.framework.Assert.assertEquals;
20 import static junit.framework.Assert.assertFalse;
21 import static junit.framework.Assert.assertNotNull;
22 import static junit.framework.Assert.assertTrue;
23 import static junit.framework.Assert.fail;
24 
25 import android.app.admin.DevicePolicyManager;
26 import android.content.ComponentName;
27 import android.content.Context;
28 import android.content.Intent;
29 import android.content.ServiceConnection;
30 import android.os.IBinder;
31 import android.os.IInterface;
32 import android.os.Looper;
33 import android.os.Process;
34 import android.os.RemoteException;
35 import android.os.UserHandle;
36 import android.test.AndroidTestCase;
37 import android.test.MoreAsserts;
38 import android.util.Log;
39 
40 import java.util.List;
41 import java.util.concurrent.LinkedBlockingQueue;
42 import java.util.concurrent.TimeUnit;
43 
44 /**
45  * Testing various scenarios when a profile owner / device owner tries to bind a service
46  * in the other profile, and everything is setup correctly.
47  */
48 public class BindDeviceAdminServiceGoodSetupTest extends AndroidTestCase {
49 
50     private static final String TAG = "BindDeviceAdminTest";
51 
52     private static final String NON_MANAGING_PACKAGE = AdminReceiver.COMP_DPC_2_PACKAGE_NAME;
53     private static final ServiceConnection EMPTY_SERVICE_CONNECTION = new ServiceConnection() {
54         @Override
55         public void onServiceConnected(ComponentName name, IBinder service) {}
56 
57         @Override
58         public void onServiceDisconnected(ComponentName name) {}
59     };
60     private static final IInterface NOT_IN_MAIN_THREAD_POISON_PILL = () -> null;
61 
62     private DevicePolicyManager mDpm;
63     private List<UserHandle> mTargetUsers;
64 
65     @Override
setUp()66     public void setUp() {
67         mDpm = (DevicePolicyManager)
68                 mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
69         assertEquals(AdminReceiver.COMP_DPC_PACKAGE_NAME, mContext.getPackageName());
70 
71         mTargetUsers = mDpm.getBindDeviceAdminTargetUsers(AdminReceiver.getComponentName(mContext));
72         assertTrue("No target users found", mTargetUsers.size() > 0);
73     }
74 
testOnlyDeviceOwnerCanHaveMoreThanOneTargetUser()75     public void testOnlyDeviceOwnerCanHaveMoreThanOneTargetUser() {
76         if (!mDpm.isDeviceOwnerApp(AdminReceiver.getComponentName(mContext).getPackageName())) {
77             assertEquals(1, mTargetUsers.size());
78         }
79     }
80 
81     /**
82      * If the intent is implicit, expected to throw {@link IllegalArgumentException}.
83      */
testCannotBind_implicitIntent()84     public void testCannotBind_implicitIntent() throws Exception {
85         final Intent implicitIntent = new Intent(Intent.ACTION_VIEW);
86         for (UserHandle targetUser : mTargetUsers) {
87             try {
88                 bind(implicitIntent, EMPTY_SERVICE_CONNECTION, targetUser);
89                 fail("IllegalArgumentException should be thrown for target user " + targetUser);
90             } catch (IllegalArgumentException ex) {
91                 MoreAsserts.assertContainsRegex("Service intent must be explicit", ex.getMessage());
92             }
93         }
94     }
95 
96     /**
97      * If the intent is not resolvable, it should return {@code null}.
98      */
testCannotBind_notResolvableIntent()99     public void testCannotBind_notResolvableIntent() throws Exception {
100         final Intent notResolvableIntent = new Intent();
101         notResolvableIntent.setClassName(mContext, "NotExistService");
102         for (UserHandle targetUser : mTargetUsers) {
103             assertFalse("Should not be allowed to bind to target user " + targetUser,
104                     bind(notResolvableIntent, EMPTY_SERVICE_CONNECTION, targetUser));
105         }
106     }
107 
108     /**
109      * Make sure we cannot bind unprotected service.
110      */
testCannotBind_unprotectedCrossUserService()111     public void testCannotBind_unprotectedCrossUserService() throws Exception {
112         final Intent serviceIntent = new Intent(mContext, UnprotectedCrossUserService.class);
113         for (UserHandle targetUser : mTargetUsers) {
114             try {
115                 bind(serviceIntent, EMPTY_SERVICE_CONNECTION, targetUser);
116                 fail("SecurityException should be thrown for target user " + targetUser);
117             } catch (SecurityException ex) {
118                 MoreAsserts.assertContainsRegex(
119                         "must be protected by BIND_DEVICE_ADMIN", ex.getMessage());
120             }
121         }
122     }
123 
124     /**
125      * Talk to a DPC package that is neither device owner nor profile owner.
126      */
testCheckCannotBind_nonManagingPackage()127     public void testCheckCannotBind_nonManagingPackage() throws Exception {
128         final Intent serviceIntent = new Intent();
129         serviceIntent.setClassName(NON_MANAGING_PACKAGE, ProtectedCrossUserService.class.getName());
130         for (UserHandle targetUser : mTargetUsers) {
131             try {
132                 bind(serviceIntent, EMPTY_SERVICE_CONNECTION, targetUser);
133                 fail("SecurityException should be thrown for target user " + targetUser);
134             } catch (SecurityException ex) {
135                 MoreAsserts.assertContainsRegex("Only allow to bind service", ex.getMessage());
136             }
137         }
138     }
139 
140     /**
141      * Talk to the same DPC in same user, that is talking to itself.
142      */
testCannotBind_sameUser()143     public void testCannotBind_sameUser() throws Exception {
144         try {
145             final Intent serviceIntent = new Intent(mContext, ProtectedCrossUserService.class);
146             bind(serviceIntent, EMPTY_SERVICE_CONNECTION, Process.myUserHandle());
147             fail("IllegalArgumentException should be thrown");
148         } catch (IllegalArgumentException ex) {
149             MoreAsserts.assertContainsRegex("target user id must be different", ex.getMessage());
150         }
151     }
152 
153     /**
154      * Send a String to other side and expect the exact same string is echoed back.
155      */
testCrossProfileCall_echo()156     public void testCrossProfileCall_echo() throws Exception {
157         final String ANSWER = "42";
158         for (UserHandle targetUser : mTargetUsers) {
159             assertCrossProfileCall(ANSWER, service -> service.echo(ANSWER), targetUser);
160         }
161     }
162 
163     /**
164      * Make sure we are talking to the target user.
165      */
testCrossProfileCall_getUserHandle()166     public void testCrossProfileCall_getUserHandle() throws Exception {
167         for (UserHandle targetUser : mTargetUsers) {
168             assertCrossProfileCall(targetUser, service -> service.getUserHandle(), targetUser);
169         }
170     }
171 
172     /**
173      * Convenient method for you to execute a cross user call and assert the return value of it.
174      * @param expected The expected result of the cross user call.
175      * @param callable It is called when the service is bound, use this to make the service call.
176      * @param targetUserHandle Which user are we talking to.
177      * @param <T> The return type of the service call.
178      */
assertCrossProfileCall( T expected, CrossUserCallable<T> callable, UserHandle targetUserHandle)179     private <T> void assertCrossProfileCall(
180             T expected, CrossUserCallable<T> callable, UserHandle targetUserHandle)
181             throws Exception {
182         final LinkedBlockingQueue<IInterface> queue = new LinkedBlockingQueue<>();
183         final ServiceConnection serviceConnection = new ServiceConnection() {
184             @Override
185             public void onServiceConnected(ComponentName name, IBinder service) {
186                 Log.d(TAG, "onServiceConnected is called in " + Thread.currentThread().getName());
187                 // Ensure onServiceConnected is running in main thread.
188                 if (Looper.myLooper() != Looper.getMainLooper()) {
189                     // Not running in main thread, failed the test.
190                     Log.e(TAG, "onServiceConnected is not running in main thread!");
191                     queue.add(NOT_IN_MAIN_THREAD_POISON_PILL);
192                     return;
193                 }
194                 queue.add(ICrossUserService.Stub.asInterface(service));
195             }
196 
197             @Override
198             public void onServiceDisconnected(ComponentName name) {
199                 Log.d(TAG, "onServiceDisconnected is called");
200             }
201         };
202         final Intent serviceIntent = new Intent(mContext, ProtectedCrossUserService.class);
203         assertTrue(bind(serviceIntent, serviceConnection, targetUserHandle));
204         IInterface service = queue.poll(5, TimeUnit.SECONDS);
205         assertNotNull("binding to the target service timed out", service);
206         try {
207             if (NOT_IN_MAIN_THREAD_POISON_PILL.equals(service)) {
208                 fail("onServiceConnected should be called in main thread");
209             }
210             ICrossUserService crossUserService = (ICrossUserService) service;
211             assertEquals(expected, callable.call(crossUserService));
212         } finally {
213             mContext.unbindService(serviceConnection);
214         }
215     }
216 
bind(Intent serviceIntent, ServiceConnection serviceConnection, UserHandle userHandle)217     private boolean bind(Intent serviceIntent, ServiceConnection serviceConnection,
218             UserHandle userHandle) {
219         return mDpm.bindDeviceAdminServiceAsUser(AdminReceiver.getComponentName(mContext),
220                 serviceIntent, serviceConnection, Context.BIND_AUTO_CREATE, userHandle);
221     }
222 
223     interface CrossUserCallable<T> {
call(ICrossUserService service)224         T call(ICrossUserService service) throws RemoteException;
225     }
226 }
227