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