Home | History | Annotate | Download | only in comp
      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
     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 
     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      */
     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      */
     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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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 
    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> {
    224         T call(ICrossUserService service) throws RemoteException;
    225     }
    226 }
    227