blob: 76c6279f28697e128980ee8f87a4e4e852ef010e [file] [log] [blame]
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.externalservice.cts;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.externalservice.common.RunningServiceInfo;
import android.externalservice.common.ServiceMessages;
import android.os.ConditionVariable;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.Messenger;
import android.os.Process;
import android.os.RemoteException;
import android.test.AndroidTestCase;
import android.util.Log;
public class ExternalServiceTest extends AndroidTestCase {
private static final String TAG = "ExternalServiceTest";
static final String sServicePackage = "android.externalservice.service";
private Connection mConnection = new Connection();
private ConditionVariable mCondition = new ConditionVariable(false);
static final int CONDITION_TIMEOUT = 10 * 1000 /* 10 seconds */;
public void tearDown() {
if (mConnection.service != null)
getContext().unbindService(mConnection);
}
/** Tests that an isolatedProcess service cannot be bound to by an external package. */
public void testFailBindIsolated() {
Intent intent = new Intent();
intent.setComponent(new ComponentName(sServicePackage, sServicePackage+".IsolatedService"));
try {
getContext().bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
fail("Should not be able to bind to non-exported, non-external service");
} catch (SecurityException e) {
}
}
/** Tests that BIND_EXTERNAL_SERVICE does not work with plain isolatedProcess services. */
public void testFailBindExternalIsolated() {
Intent intent = new Intent();
intent.setComponent(new ComponentName(sServicePackage, sServicePackage+".IsolatedService"));
try {
getContext().bindService(intent, mConnection,
Context.BIND_AUTO_CREATE | Context.BIND_EXTERNAL_SERVICE);
fail("Should not be able to BIND_EXTERNAL_SERVICE to non-exported, non-external service");
} catch (SecurityException e) {
}
}
/** Tests that BIND_EXTERNAL_SERVICE does not work with exported, isolatedProcess services (
* requires externalService as well). */
public void testFailBindExternalExported() {
Intent intent = new Intent();
intent.setComponent(new ComponentName(sServicePackage, sServicePackage+".ExportedService"));
try {
getContext().bindService(intent, mConnection,
Context.BIND_AUTO_CREATE | Context.BIND_EXTERNAL_SERVICE);
fail("Should not be able to BIND_EXTERNAL_SERVICE to non-external service");
} catch (SecurityException e) {
}
}
/** Tests that BIND_EXTERNAL_SERVICE requires that an externalService be exported. */
public void testFailBindExternalNonExported() {
Intent intent = new Intent();
intent.setComponent(
new ComponentName(sServicePackage, sServicePackage+".ExternalNonExportedService"));
try {
getContext().bindService(intent, mConnection,
Context.BIND_AUTO_CREATE | Context.BIND_EXTERNAL_SERVICE);
fail("Should not be able to BIND_EXTERNAL_SERVICE to non-exported service");
} catch (SecurityException e) {
}
}
/** Tests that BIND_EXTERNAL_SERVICE requires the service be an isolatedProcess. */
public void testFailBindExternalNonIsolated() {
Intent intent = new Intent();
intent.setComponent(
new ComponentName(sServicePackage, sServicePackage+".ExternalNonIsolatedService"));
try {
getContext().bindService(intent, mConnection,
Context.BIND_AUTO_CREATE | Context.BIND_EXTERNAL_SERVICE);
fail("Should not be able to BIND_EXTERNAL_SERVICE to non-isolated service");
} catch (SecurityException e) {
}
}
/** Tests that an externalService can only be bound with BIND_EXTERNAL_SERVICE. */
public void testFailBindWithoutBindExternal() {
Intent intent = new Intent();
intent.setComponent(new ComponentName(sServicePackage, sServicePackage+".ExternalService"));
try {
getContext().bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
fail("Should not be able to bind to an external service without BIND_EXTERNAL_SERVICE");
} catch (SecurityException e) {
}
}
/** Tests that an external service can be bound, and that it runs as a different principal. */
public void testBindExternalService() {
// Start the service and wait for connection.
Intent intent = new Intent();
intent.setComponent(new ComponentName(sServicePackage, sServicePackage+".ExternalService"));
mCondition.close();
assertTrue(getContext().bindService(intent, mConnection,
Context.BIND_AUTO_CREATE | Context.BIND_EXTERNAL_SERVICE));
assertTrue(mCondition.block(CONDITION_TIMEOUT));
assertEquals(getContext().getPackageName(), mConnection.name.getPackageName());
assertNotSame(sServicePackage, mConnection.name.getPackageName());
// Check the identity of the service.
Messenger remote = new Messenger(mConnection.service);
RunningServiceInfo id = identifyService(remote);
assertNotNull(id);
assertFalse(id.uid == 0 || id.pid == 0);
assertNotEquals(Process.myUid(), id.uid);
assertNotEquals(Process.myPid(), id.pid);
assertEquals(getContext().getPackageName(), id.packageName);
assertEquals(-1, id.zygotePid);
}
/** Tests that the APK providing the externalService can bind the service itself, and that
* other APKs bind to a different instance of it. */
public void testBindExternalServiceWithRunningOwn() {
// Start the service that will create the externalService.
final Connection creatorConnection = new Connection();
Intent intent = new Intent();
intent.setComponent(new ComponentName(sServicePackage, sServicePackage+".ServiceCreator"));
mCondition.close();
assertTrue(getContext().bindService(intent, creatorConnection, Context.BIND_AUTO_CREATE));
assertTrue(mCondition.block(CONDITION_TIMEOUT));
// Get the identity of the creator.
Messenger remoteCreator = new Messenger(creatorConnection.service);
RunningServiceInfo creatorId = identifyService(remoteCreator);
assertNotNull(creatorId);
assertFalse(creatorId.uid == 0 || creatorId.pid == 0);
// Have the creator actually start its service.
final Message creatorMsg =
Message.obtain(null, ServiceMessages.MSG_CREATE_EXTERNAL_SERVICE);
Handler creatorHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
Log.d(TAG, "Received message: " + msg);
switch (msg.what) {
case ServiceMessages.MSG_CREATE_EXTERNAL_SERVICE_RESPONSE:
creatorMsg.copyFrom(msg);
mCondition.open();
break;
}
super.handleMessage(msg);
}
};
Messenger localCreator = new Messenger(creatorHandler);
creatorMsg.replyTo = localCreator;
try {
mCondition.close();
remoteCreator.send(creatorMsg);
} catch (RemoteException e) {
fail("Unexpected remote exception" + e);
return;
}
assertTrue(mCondition.block(CONDITION_TIMEOUT));
// Get the connection to the creator's service.
assertNotNull(creatorMsg.obj);
Messenger remoteCreatorService = (Messenger) creatorMsg.obj;
RunningServiceInfo creatorServiceId = identifyService(remoteCreatorService);
assertNotNull(creatorServiceId);
assertFalse(creatorServiceId.uid == 0 || creatorId.pid == 0);
// Create an external service from this (the test) process.
intent = new Intent();
intent.setComponent(new ComponentName(sServicePackage, sServicePackage+".ExternalService"));
mCondition.close();
assertTrue(getContext().bindService(intent, mConnection,
Context.BIND_AUTO_CREATE | Context.BIND_EXTERNAL_SERVICE));
assertTrue(mCondition.block(CONDITION_TIMEOUT));
RunningServiceInfo serviceId = identifyService(new Messenger(mConnection.service));
assertNotNull(serviceId);
assertFalse(serviceId.uid == 0 || serviceId.pid == 0);
// Make sure that all the processes are unique.
int myUid = Process.myUid();
int myPid = Process.myPid();
String myPkg = getContext().getPackageName();
assertNotEquals(myUid, creatorId.uid);
assertNotEquals(myUid, creatorServiceId.uid);
assertNotEquals(myUid, serviceId.uid);
assertNotEquals(myPid, creatorId.pid);
assertNotEquals(myPid, creatorServiceId.pid);
assertNotEquals(myPid, serviceId.pid);
assertNotEquals(creatorId.uid, creatorServiceId.uid);
assertNotEquals(creatorId.uid, serviceId.uid);
assertNotEquals(creatorId.pid, creatorServiceId.pid);
assertNotEquals(creatorId.pid, serviceId.pid);
assertNotEquals(creatorServiceId.uid, serviceId.uid);
assertNotEquals(creatorServiceId.pid, serviceId.pid);
assertEquals(-1, creatorServiceId.zygotePid);
assertEquals(-1, serviceId.zygotePid);
assertNull(creatorServiceId.zygotePackage);
assertNull(serviceId.zygotePackage);
assertNotEquals(myPkg, creatorId.packageName);
assertNotEquals(myPkg, creatorServiceId.packageName);
assertEquals(creatorId.packageName, creatorServiceId.packageName);
assertEquals(myPkg, serviceId.packageName);
getContext().unbindService(creatorConnection);
}
/** Tests that the binding to an externalService can be changed. */
public void testBindExternalAboveClient() {
// Start the service and wait for connection.
Intent intent = new Intent();
intent.setComponent(new ComponentName(sServicePackage, sServicePackage+".ExternalService"));
mCondition.close();
Connection initialConn = new Connection();
assertTrue(getContext().bindService(intent, initialConn,
Context.BIND_AUTO_CREATE | Context.BIND_EXTERNAL_SERVICE));
assertTrue(mCondition.block(CONDITION_TIMEOUT));
RunningServiceInfo idOne = identifyService(new Messenger(initialConn.service));
assertNotNull(idOne);
assertFalse(idOne.uid == 0 || idOne.pid == 0);
// Bind the service with a different priority.
mCondition.close();
Connection prioConn = new Connection();
assertTrue(getContext().bindService(intent, prioConn,
Context.BIND_AUTO_CREATE | Context.BIND_EXTERNAL_SERVICE |
Context.BIND_ABOVE_CLIENT));
assertTrue(mCondition.block(CONDITION_TIMEOUT));
RunningServiceInfo idTwo = identifyService(new Messenger(prioConn.service));
assertNotNull(idTwo);
assertFalse(idTwo.uid == 0 || idTwo.pid == 0);
assertEquals(idOne.uid, idTwo.uid);
assertEquals(idOne.pid, idTwo.pid);
assertEquals(idOne.packageName, idTwo.packageName);
assertNotEquals(Process.myUid(), idOne.uid);
assertNotEquals(Process.myPid(), idOne.pid);
assertEquals(getContext().getPackageName(), idOne.packageName);
getContext().unbindService(prioConn);
getContext().unbindService(initialConn);
}
/** Tests binding an externalService that is started by an app zygote. */
public void testBindExternalServiceWithZygote() {
// Start the service and wait for connection.
Intent intent = new Intent();
intent.setComponent(
new ComponentName(sServicePackage, sServicePackage+".ExternalServiceWithZygote"));
mCondition.close();
assertTrue(getContext().bindService(intent, mConnection,
Context.BIND_AUTO_CREATE | Context.BIND_EXTERNAL_SERVICE));
assertTrue(mCondition.block(CONDITION_TIMEOUT));
assertEquals(getContext().getPackageName(), mConnection.name.getPackageName());
assertNotSame(sServicePackage, mConnection.name.getPackageName());
// Check the identity of the service.
Messenger remote = new Messenger(mConnection.service);
RunningServiceInfo id = identifyService(remote);
assertNotNull(id);
assertFalse(id.uid == 0 || id.pid == 0);
assertNotEquals(Process.myUid(), id.uid);
assertNotEquals(Process.myPid(), id.pid);
assertEquals(getContext().getPackageName(), id.packageName);
assertNotEquals(id.pid, id.zygotePid);
assertNotEquals(Process.myPid(), id.zygotePid);
assertNotEquals(-1, id.zygotePid);
assertEquals(sServicePackage, id.zygotePackage);
}
/** Tests that an externalService that also is also useAppZygote=true shares the same
* zygote, even when bound by different packages.. */
public void testBindExternalServiceWithZygoteWithRunningOwn() {
// Start the service that will create the externalService.
final Connection creatorConnection = new Connection();
Intent intent = new Intent();
intent.setComponent(new ComponentName(sServicePackage, sServicePackage+".ServiceCreator"));
mCondition.close();
assertTrue(getContext().bindService(intent, creatorConnection, Context.BIND_AUTO_CREATE));
assertTrue(mCondition.block(CONDITION_TIMEOUT));
// Have the creator actually start its service.
Messenger remoteCreator = new Messenger(creatorConnection.service);
final Message creatorMsg = Message.obtain(null, ServiceMessages.MSG_CREATE_EXTERNAL_SERVICE,
/* use zygote */ 1, 0);
Handler creatorHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
Log.d(TAG, "Received message: " + msg);
switch (msg.what) {
case ServiceMessages.MSG_CREATE_EXTERNAL_SERVICE_RESPONSE:
creatorMsg.copyFrom(msg);
mCondition.open();
break;
}
super.handleMessage(msg);
}
};
Messenger localCreator = new Messenger(creatorHandler);
creatorMsg.replyTo = localCreator;
try {
mCondition.close();
remoteCreator.send(creatorMsg);
} catch (RemoteException e) {
fail("Unexpected remote exception" + e);
return;
}
assertTrue(mCondition.block(CONDITION_TIMEOUT));
// Get the connection to the creator's service.
assertNotNull(creatorMsg.obj);
Messenger remoteCreatorService = (Messenger) creatorMsg.obj;
RunningServiceInfo creatorServiceId = identifyService(remoteCreatorService);
assertNotNull(creatorServiceId);
assertFalse(creatorServiceId.uid == 0);
// Create an external service from this (the test) process.
intent = new Intent();
intent.setComponent(
new ComponentName(sServicePackage, sServicePackage+".ExternalServiceWithZygote"));
mCondition.close();
assertTrue(getContext().bindService(intent, mConnection,
Context.BIND_AUTO_CREATE | Context.BIND_EXTERNAL_SERVICE));
assertTrue(mCondition.block(CONDITION_TIMEOUT));
RunningServiceInfo serviceId = identifyService(new Messenger(mConnection.service));
assertNotNull(serviceId);
assertFalse(serviceId.uid == 0 || serviceId.pid == 0);
// Make sure that both services were started by the same zygote.
assertNotEquals(-1, creatorServiceId.zygotePid);
assertNotEquals(-1, serviceId.zygotePid);
assertNotEquals(creatorServiceId.pid, creatorServiceId.zygotePid);
assertNotEquals(serviceId.pid, serviceId.zygotePid);
assertEquals(sServicePackage, creatorServiceId.zygotePackage);
assertEquals(sServicePackage, serviceId.zygotePackage);
assertEquals(creatorServiceId.zygotePid, serviceId.zygotePid);
int myUid = Process.myUid();
int myPid = Process.myPid();
String myPkg = getContext().getPackageName();
assertNotEquals(myUid, creatorServiceId.uid);
assertNotEquals(myUid, serviceId.uid);
assertNotEquals(myPid, creatorServiceId.pid);
assertNotEquals(myPid, serviceId.pid);
assertNotEquals(creatorServiceId.uid, serviceId.uid);
assertNotEquals(creatorServiceId.pid, serviceId.pid);
assertNotEquals(myPkg, creatorServiceId.packageName);
assertEquals(myPkg, serviceId.packageName);
getContext().unbindService(creatorConnection);
}
/**
* Test when the flag BIND_EXTERNAL_SERVICE(0x80000000) is set in 64 bits long flags,
* an IllegalArgumentException is thrown. The reason is that integer 0x80000000 is
* automatically converted to long 0xffff_ffff_8000_000, it is not a correct flag any more.
* In 64 bits long flags, use BIND_EXTERNAL_SERVICE_LONG(0x4000_0000_0000_0000L) instead.
*/
public void testFailBindExternalServiceLongFlags() {
// Start the service and wait for connection.
Intent intent = new Intent();
intent.setComponent(new ComponentName(sServicePackage, sServicePackage+".ExternalService"));
mCondition.close();
long longFlags = Context.BIND_EXTERNAL_SERVICE | Context.BIND_AUTO_CREATE;
try {
getContext().bindService(intent, mConnection,
Context.BindServiceFlags.of(longFlags)/* 64 bits long flag */);
fail("Context.BIND_EXTERNAL_SERVICE can not be used in 64 bits bindService() flags");
} catch (IllegalArgumentException e) {
// expect an IllegalArgumentException when BIND_EXTERNAL_SERVICE is used in 64 bits
// flags.
}
}
/**
* Test 64 bits long flag BIND_EXTERNAL_SERVICE_LONG(0x4000_0000_0000_0000L), it deprecates
* 32 bits flag BIND_EXTERNAL_SERVICE(0x80000000) when 64 bits long flags is used.
*/
public void testBindExternalServiceLongFlags() {
// Start the service and wait for connection.
Intent intent = new Intent();
intent.setComponent(new ComponentName(sServicePackage, sServicePackage+".ExternalService"));
mCondition.close();
long longFlags = Context.BIND_EXTERNAL_SERVICE_LONG | Context.BIND_AUTO_CREATE;
assertTrue(getContext().bindService(intent, mConnection,
Context.BindServiceFlags.of(longFlags)/* 64 bits long flag */));
assertTrue(mCondition.block(CONDITION_TIMEOUT));
assertEquals(getContext().getPackageName(), mConnection.name.getPackageName());
assertNotSame(sServicePackage, mConnection.name.getPackageName());
}
/** Given a Messenger, this will message the service to retrieve its UID, PID, and package name.
* On success, returns a RunningServiceInfo. On failure, returns null. */
private RunningServiceInfo identifyService(Messenger service) {
try {
return RunningServiceInfo.identifyService(service, TAG, mCondition);
} catch (RemoteException e) {
fail("Unexpected remote exception: " + e);
return null;
}
}
private class Connection implements ServiceConnection {
IBinder service = null;
ComponentName name = null;
boolean disconnected = false;
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d(TAG, "onServiceConnected " + name);
this.service = service;
this.name = name;
mCondition.open();
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.d(TAG, "onServiceDisconnected " + name);
}
}
private <T> void assertNotEquals(T expected, T actual) {
assertFalse("Expected <" + expected + "> should not be equal to actual <" + actual + ">",
expected.equals(actual));
}
}