Home | History | Annotate | Download | only in cts
      1 /*
      2  * Copyright (C) 2016 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.externalservice.cts;
     18 
     19 import android.content.ComponentName;
     20 import android.content.Context;
     21 import android.content.Intent;
     22 import android.content.ServiceConnection;
     23 import android.os.ConditionVariable;
     24 import android.os.Handler;
     25 import android.os.IBinder;
     26 import android.os.Looper;
     27 import android.os.Message;
     28 import android.os.Messenger;
     29 import android.os.Parcel;
     30 import android.os.Process;
     31 import android.os.RemoteException;
     32 import android.test.AndroidTestCase;
     33 import android.util.Log;
     34 
     35 import android.externalservice.common.ServiceMessages;
     36 
     37 public class ExternalServiceTest extends AndroidTestCase {
     38     private static final String TAG = "ExternalServiceTest";
     39 
     40     static final String sServicePackage = "android.externalservice.service";
     41 
     42     private Connection mConnection = new Connection();
     43 
     44     private ConditionVariable mCondition = new ConditionVariable(false);
     45 
     46     static final int CONDITION_TIMEOUT = 10 * 1000 /* 10 seconds */;
     47 
     48     public void tearDown() {
     49         if (mConnection.service != null)
     50             getContext().unbindService(mConnection);
     51     }
     52 
     53     /** Tests that an isolatedProcess service cannot be bound to by an external package. */
     54     public void testFailBindIsolated() {
     55         Intent intent = new Intent();
     56         intent.setComponent(new ComponentName(sServicePackage, sServicePackage+".IsolatedService"));
     57         try {
     58             getContext().bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
     59             fail("Should not be able to bind to non-exported, non-external service");
     60         } catch (SecurityException e) {
     61         }
     62     }
     63 
     64     /** Tests that BIND_EXTERNAL_SERVICE does not work with plain isolatedProcess services. */
     65     public void testFailBindExternalIsolated() {
     66         Intent intent = new Intent();
     67         intent.setComponent(new ComponentName(sServicePackage, sServicePackage+".IsolatedService"));
     68         try {
     69             getContext().bindService(intent, mConnection,
     70                     Context.BIND_AUTO_CREATE | Context.BIND_EXTERNAL_SERVICE);
     71             fail("Should not be able to BIND_EXTERNAL_SERVICE to non-exported, non-external service");
     72         } catch (SecurityException e) {
     73         }
     74     }
     75 
     76     /** Tests that BIND_EXTERNAL_SERVICE does not work with exported, isolatedProcess services (
     77      * requires externalService as well). */
     78     public void testFailBindExternalExported() {
     79         Intent intent = new Intent();
     80         intent.setComponent(new ComponentName(sServicePackage, sServicePackage+".ExportedService"));
     81         try {
     82             getContext().bindService(intent, mConnection,
     83                     Context.BIND_AUTO_CREATE | Context.BIND_EXTERNAL_SERVICE);
     84             fail("Should not be able to BIND_EXTERNAL_SERVICE to non-external service");
     85         } catch (SecurityException e) {
     86         }
     87     }
     88 
     89     /** Tests that BIND_EXTERNAL_SERVICE requires that an externalService be exported. */
     90     public void testFailBindExternalNonExported() {
     91         Intent intent = new Intent();
     92         intent.setComponent(
     93                 new ComponentName(sServicePackage, sServicePackage+".ExternalNonExportedService"));
     94         try {
     95             getContext().bindService(intent, mConnection,
     96                     Context.BIND_AUTO_CREATE | Context.BIND_EXTERNAL_SERVICE);
     97             fail("Should not be able to BIND_EXTERNAL_SERVICE to non-exported service");
     98         } catch (SecurityException e) {
     99         }
    100     }
    101 
    102     /** Tests that BIND_EXTERNAL_SERVICE requires the service be an isolatedProcess. */
    103     public void testFailBindExternalNonIsolated() {
    104         Intent intent = new Intent();
    105         intent.setComponent(
    106                 new ComponentName(sServicePackage, sServicePackage+".ExternalNonIsolatedService"));
    107         try {
    108             getContext().bindService(intent, mConnection,
    109                     Context.BIND_AUTO_CREATE | Context.BIND_EXTERNAL_SERVICE);
    110             fail("Should not be able to BIND_EXTERNAL_SERVICE to non-isolated service");
    111         } catch (SecurityException e) {
    112         }
    113     }
    114 
    115     /** Tests that an externalService can only be bound with BIND_EXTERNAL_SERVICE. */
    116     public void testFailBindWithoutBindExternal() {
    117         Intent intent = new Intent();
    118         intent.setComponent(new ComponentName(sServicePackage, sServicePackage+".ExternalService"));
    119         try {
    120             getContext().bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
    121             fail("Should not be able to bind to an external service without BIND_EXTERNAL_SERVICE");
    122         } catch (SecurityException e) {
    123         }
    124     }
    125 
    126     /** Tests that an external service can be bound, and that it runs as a different principal. */
    127     public void testBindExternalService() {
    128         // Start the service and wait for connection.
    129         Intent intent = new Intent();
    130         intent.setComponent(new ComponentName(sServicePackage, sServicePackage+".ExternalService"));
    131 
    132         mCondition.close();
    133         assertTrue(getContext().bindService(intent, mConnection,
    134                     Context.BIND_AUTO_CREATE | Context.BIND_EXTERNAL_SERVICE));
    135 
    136         assertTrue(mCondition.block(CONDITION_TIMEOUT));
    137         assertEquals(getContext().getPackageName(), mConnection.name.getPackageName());
    138         assertNotSame(sServicePackage, mConnection.name.getPackageName());
    139 
    140         // Check the identity of the service.
    141         Messenger remote = new Messenger(mConnection.service);
    142         ServiceIdentity id = identifyService(remote);
    143         assertNotNull(id);
    144 
    145         assertFalse(id.uid == 0 || id.pid == 0);
    146         assertNotEquals(Process.myUid(), id.uid);
    147         assertNotEquals(Process.myPid(), id.pid);
    148         assertEquals(getContext().getPackageName(), id.packageName);
    149     }
    150 
    151     /** Tests that the APK providing the externalService can bind the service itself, and that
    152      * other APKs bind to a different instance of it. */
    153     public void testBindExternalServiceWithRunningOwn() {
    154         // Start the service that will create the externalService.
    155         final Connection creatorConnection = new Connection();
    156         Intent intent = new Intent();
    157         intent.setComponent(new ComponentName(sServicePackage, sServicePackage+".ServiceCreator"));
    158 
    159         mCondition.close();
    160         assertTrue(getContext().bindService(intent, creatorConnection, Context.BIND_AUTO_CREATE));
    161         assertTrue(mCondition.block(CONDITION_TIMEOUT));
    162 
    163         // Get the identity of the creator.
    164         Messenger remoteCreator = new Messenger(creatorConnection.service);
    165         ServiceIdentity creatorId = identifyService(remoteCreator);
    166         assertNotNull(creatorId);
    167         assertFalse(creatorId.uid == 0 || creatorId.pid == 0);
    168 
    169         // Have the creator actually start its service.
    170         final Message creatorMsg =
    171                 Message.obtain(null, ServiceMessages.MSG_CREATE_EXTERNAL_SERVICE);
    172         Handler creatorHandler = new Handler(Looper.getMainLooper()) {
    173             @Override
    174             public void handleMessage(Message msg) {
    175                 Log.d(TAG, "Received message: " + msg);
    176                 switch (msg.what) {
    177                     case ServiceMessages.MSG_CREATE_EXTERNAL_SERVICE_RESPONSE:
    178                         creatorMsg.copyFrom(msg);
    179                         mCondition.open();
    180                         break;
    181                 }
    182                 super.handleMessage(msg);
    183             }
    184         };
    185         Messenger localCreator = new Messenger(creatorHandler);
    186         creatorMsg.replyTo = localCreator;
    187         try {
    188             mCondition.close();
    189             remoteCreator.send(creatorMsg);
    190         } catch (RemoteException e) {
    191             fail("Unexpected remote exception" + e);
    192             return;
    193         }
    194         assertTrue(mCondition.block(CONDITION_TIMEOUT));
    195 
    196         // Get the connection to the creator's service.
    197         assertNotNull(creatorMsg.obj);
    198         Messenger remoteCreatorService = (Messenger) creatorMsg.obj;
    199         ServiceIdentity creatorServiceId = identifyService(remoteCreatorService);
    200         assertNotNull(creatorServiceId);
    201         assertFalse(creatorServiceId.uid == 0 || creatorId.pid == 0);
    202 
    203         // Create an external service from this (the test) process.
    204         intent = new Intent();
    205         intent.setComponent(new ComponentName(sServicePackage, sServicePackage+".ExternalService"));
    206 
    207         mCondition.close();
    208         assertTrue(getContext().bindService(intent, mConnection,
    209                     Context.BIND_AUTO_CREATE | Context.BIND_EXTERNAL_SERVICE));
    210         assertTrue(mCondition.block(CONDITION_TIMEOUT));
    211         ServiceIdentity serviceId = identifyService(new Messenger(mConnection.service));
    212         assertNotNull(serviceId);
    213         assertFalse(serviceId.uid == 0 || serviceId.pid == 0);
    214 
    215         // Make sure that all the processes are unique.
    216         int myUid = Process.myUid();
    217         int myPid = Process.myPid();
    218         String myPkg = getContext().getPackageName();
    219 
    220         assertNotEquals(myUid, creatorId.uid);
    221         assertNotEquals(myUid, creatorServiceId.uid);
    222         assertNotEquals(myUid, serviceId.uid);
    223         assertNotEquals(myPid, creatorId.pid);
    224         assertNotEquals(myPid, creatorServiceId.pid);
    225         assertNotEquals(myPid, serviceId.pid);
    226 
    227         assertNotEquals(creatorId.uid, creatorServiceId.uid);
    228         assertNotEquals(creatorId.uid, serviceId.uid);
    229         assertNotEquals(creatorId.pid, creatorServiceId.pid);
    230         assertNotEquals(creatorId.pid, serviceId.pid);
    231 
    232         assertNotEquals(creatorServiceId.uid, serviceId.uid);
    233         assertNotEquals(creatorServiceId.pid, serviceId.pid);
    234 
    235         assertNotEquals(myPkg, creatorId.packageName);
    236         assertNotEquals(myPkg, creatorServiceId.packageName);
    237         assertEquals(creatorId.packageName, creatorServiceId.packageName);
    238         assertEquals(myPkg, serviceId.packageName);
    239 
    240         getContext().unbindService(creatorConnection);
    241     }
    242 
    243     /** Tests that the binding to an externalService can be changed. */
    244     public void testBindExternalAboveClient() {
    245         // Start the service and wait for connection.
    246         Intent intent = new Intent();
    247         intent.setComponent(new ComponentName(sServicePackage, sServicePackage+".ExternalService"));
    248 
    249         mCondition.close();
    250         Connection initialConn = new Connection();
    251         assertTrue(getContext().bindService(intent, initialConn,
    252                     Context.BIND_AUTO_CREATE | Context.BIND_EXTERNAL_SERVICE));
    253 
    254         assertTrue(mCondition.block(CONDITION_TIMEOUT));
    255 
    256         ServiceIdentity idOne = identifyService(new Messenger(initialConn.service));
    257         assertNotNull(idOne);
    258         assertFalse(idOne.uid == 0 || idOne.pid == 0);
    259 
    260         // Bind the service with a different priority.
    261         mCondition.close();
    262         Connection prioConn = new Connection();
    263         assertTrue(getContext().bindService(intent, prioConn,
    264                     Context.BIND_AUTO_CREATE | Context.BIND_EXTERNAL_SERVICE |
    265                             Context.BIND_ABOVE_CLIENT));
    266 
    267         assertTrue(mCondition.block(CONDITION_TIMEOUT));
    268 
    269         ServiceIdentity idTwo = identifyService(new Messenger(prioConn.service));
    270         assertNotNull(idTwo);
    271         assertFalse(idTwo.uid == 0 || idTwo.pid == 0);
    272 
    273         assertEquals(idOne.uid, idTwo.uid);
    274         assertEquals(idOne.pid, idTwo.pid);
    275         assertEquals(idOne.packageName, idTwo.packageName);
    276         assertNotEquals(Process.myUid(), idOne.uid);
    277         assertNotEquals(Process.myPid(), idOne.pid);
    278         assertEquals(getContext().getPackageName(), idOne.packageName);
    279 
    280         getContext().unbindService(prioConn);
    281         getContext().unbindService(initialConn);
    282     }
    283 
    284     /** Contains information about the security principal of a Service. */
    285     private static class ServiceIdentity {
    286         int uid;
    287         int pid;
    288         String packageName;
    289     }
    290 
    291     /** Given a Messenger, this will message the service to retrieve its UID, PID, and package name.
    292      * On success, returns a ServiceIdentity. On failure, returns null. */
    293     private ServiceIdentity identifyService(Messenger service) {
    294         final ServiceIdentity id = new ServiceIdentity();
    295         Handler handler = new Handler(Looper.getMainLooper()) {
    296             @Override
    297             public void handleMessage(Message msg) {
    298                 Log.d(TAG, "Received message: " + msg);
    299                 switch (msg.what) {
    300                     case ServiceMessages.MSG_IDENTIFY_RESPONSE:
    301                         id.uid = msg.arg1;
    302                         id.pid = msg.arg2;
    303                         id.packageName = msg.getData().getString(ServiceMessages.IDENTIFY_PACKAGE);
    304                         mCondition.open();
    305                         break;
    306                 }
    307                 super.handleMessage(msg);
    308             }
    309         };
    310         Messenger local = new Messenger(handler);
    311 
    312         Message msg = Message.obtain(null, ServiceMessages.MSG_IDENTIFY);
    313         msg.replyTo = local;
    314         try {
    315             mCondition.close();
    316             service.send(msg);
    317         } catch (RemoteException e) {
    318             fail("Unexpected remote exception: " + e);
    319             return null;
    320         }
    321 
    322         if (!mCondition.block(CONDITION_TIMEOUT))
    323             return null;
    324         return id;
    325     }
    326 
    327     private class Connection implements ServiceConnection {
    328         IBinder service = null;
    329         ComponentName name = null;
    330         boolean disconnected = false;
    331 
    332         @Override
    333         public void onServiceConnected(ComponentName name, IBinder service) {
    334             Log.d(TAG, "onServiceConnected " + name);
    335             this.service = service;
    336             this.name = name;
    337             mCondition.open();
    338         }
    339 
    340         @Override
    341         public void onServiceDisconnected(ComponentName name) {
    342             Log.d(TAG, "onServiceDisconnected " + name);
    343         }
    344     }
    345 
    346     private <T> void assertNotEquals(T expected, T actual) {
    347         assertFalse("Expected <" + expected + "> should not be equal to actual <" + actual + ">",
    348                 expected.equals(actual));
    349     }
    350 }
    351