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