blob: be3eb112921f69d556f5f7b79f1976863395b3dd [file] [log] [blame]
/*
* Copyright (C) 2022 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 com.android.tests.sdksandbox.endtoend;
import static android.app.sdksandbox.SdkSandboxManager.EXTRA_DISPLAY_ID;
import static android.app.sdksandbox.SdkSandboxManager.EXTRA_HEIGHT_IN_PIXELS;
import static android.app.sdksandbox.SdkSandboxManager.EXTRA_HOST_TOKEN;
import static android.app.sdksandbox.SdkSandboxManager.EXTRA_WIDTH_IN_PIXELS;
import static android.app.sdksandbox.SdkSandboxManager.LOAD_SDK_INTERNAL_ERROR;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.fail;
import static org.junit.Assume.assumeTrue;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.sdksandbox.AppOwnedSdkSandboxInterface;
import android.app.sdksandbox.LoadSdkException;
import android.app.sdksandbox.SandboxedSdk;
import android.app.sdksandbox.SdkSandboxManager;
import android.app.sdksandbox.testutils.FakeLoadSdkCallback;
import android.app.sdksandbox.testutils.FakeRequestSurfacePackageCallback;
import android.app.sdksandbox.testutils.FakeSdkSandboxProcessDeathCallback;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PermissionInfo;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Parcel;
import android.os.RemoteException;
import androidx.lifecycle.Lifecycle;
import androidx.test.core.app.ActivityScenario;
import androidx.test.ext.junit.rules.ActivityScenarioRule;
import androidx.test.platform.app.InstrumentationRegistry;
import com.android.compatibility.common.util.DeviceConfigStateHelper;
import com.android.ctssdkprovider.IActivityStarter;
import com.android.ctssdkprovider.ICtsSdkProviderApi;
import com.android.modules.utils.build.SdkLevel;
import com.google.common.truth.Expect;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import java.util.List;
/** End-to-end tests of {@link SdkSandboxManager} APIs. */
@RunWith(JUnit4.class)
public class SdkSandboxManagerTest {
private static final String NON_EXISTENT_SDK = "com.android.not_exist";
private static final String APP_OWNED_SDK_SANDBOX_INTERFACE_NAME =
"com.android.ctsappownedsdksandboxinterface";
private static final String SDK_NAME_1 = "com.android.ctssdkprovider";
private static final String SDK_NAME_2 = "com.android.emptysdkprovider";
private static final String TEST_OPTION = "test-option";
private static final String OPTION_THROW_INTERNAL_ERROR = "internal-error";
private static final String OPTION_THROW_REQUEST_SURFACE_PACKAGE_ERROR = "rsp-error";
private static final String NAMESPACE_WINDOW_MANAGER = "window_manager";
private static final String ASM_RESTRICTIONS_ENABLED =
"ActivitySecurity__asm_restrictions_enabled";
@Rule
public final ActivityScenarioRule<TestActivity> mRule =
new ActivityScenarioRule<>(TestActivity.class);
@Rule public final Expect mExpect = Expect.create();
private ActivityScenario<TestActivity> mScenario;
private SdkSandboxManager mSdkSandboxManager;
private final DeviceConfigStateHelper mDeviceConfig =
new DeviceConfigStateHelper(NAMESPACE_WINDOW_MANAGER);
@Before
public void setup() {
final Context context = InstrumentationRegistry.getInstrumentation().getContext();
mSdkSandboxManager = context.getSystemService(SdkSandboxManager.class);
mScenario = mRule.getScenario();
mDeviceConfig.set(ASM_RESTRICTIONS_ENABLED, "1");
}
@After
public void tearDown() {
try {
mSdkSandboxManager.unloadSdk(SDK_NAME_1);
mSdkSandboxManager.unloadSdk(SDK_NAME_2);
mDeviceConfig.close();
} catch (Exception ignored) {
}
}
@Test
public void testGetSdkSandboxState() {
int state = SdkSandboxManager.getSdkSandboxState();
assertThat(state).isEqualTo(SdkSandboxManager.SDK_SANDBOX_STATE_ENABLED_PROCESS_ISOLATION);
}
@Test
public void testLoadSdkSuccessfully() {
final FakeLoadSdkCallback callback = new FakeLoadSdkCallback();
mSdkSandboxManager.loadSdk(SDK_NAME_1, new Bundle(), Runnable::run, callback);
callback.assertLoadSdkIsSuccessful();
assertNotNull(callback.getSandboxedSdk());
assertNotNull(callback.getSandboxedSdk().getInterface());
}
@Test
public void testRegisterAndGetAppOwnedSdkSandboxInterface() throws Exception {
try {
IBinder iBinder = new Binder();
mSdkSandboxManager.registerAppOwnedSdkSandboxInterface(
new AppOwnedSdkSandboxInterface(
APP_OWNED_SDK_SANDBOX_INTERFACE_NAME,
/*version=*/ 0,
/*interfaceIBinder=*/ iBinder));
final List<AppOwnedSdkSandboxInterface> appOwnedSdkSandboxInterfaceList =
mSdkSandboxManager.getAppOwnedSdkSandboxInterfaces();
assertThat(appOwnedSdkSandboxInterfaceList).hasSize(1);
assertThat(appOwnedSdkSandboxInterfaceList.get(0).getName())
.isEqualTo(APP_OWNED_SDK_SANDBOX_INTERFACE_NAME);
assertThat(appOwnedSdkSandboxInterfaceList.get(0).getVersion()).isEqualTo(0);
assertThat(appOwnedSdkSandboxInterfaceList.get(0).getInterface()).isEqualTo(iBinder);
} finally {
mSdkSandboxManager.unregisterAppOwnedSdkSandboxInterface(
APP_OWNED_SDK_SANDBOX_INTERFACE_NAME);
}
}
@Test
public void testUnregisterAppOwnedSdkSandboxInterface() throws Exception {
mSdkSandboxManager.registerAppOwnedSdkSandboxInterface(
new AppOwnedSdkSandboxInterface(
APP_OWNED_SDK_SANDBOX_INTERFACE_NAME,
/*version=*/ 0,
/*interfaceIBinder=*/ new Binder()));
mSdkSandboxManager.unregisterAppOwnedSdkSandboxInterface(
APP_OWNED_SDK_SANDBOX_INTERFACE_NAME);
assertThat(mSdkSandboxManager.getAppOwnedSdkSandboxInterfaces()).hasSize(0);
}
@Test
public void testRegisterAppOwnedSdkSandboxInterfaceAlreadyRegistered() throws Exception {
try {
mSdkSandboxManager.registerAppOwnedSdkSandboxInterface(
new AppOwnedSdkSandboxInterface(
APP_OWNED_SDK_SANDBOX_INTERFACE_NAME,
/*version=*/ 0,
/*interfaceIBinder=*/ new Binder()));
assertThrows(
RuntimeException.class,
() ->
mSdkSandboxManager.registerAppOwnedSdkSandboxInterface(
new AppOwnedSdkSandboxInterface(
APP_OWNED_SDK_SANDBOX_INTERFACE_NAME,
/*version=*/ 0,
/*interfaceIBinder=*/ new Binder())));
} finally {
mSdkSandboxManager.unregisterAppOwnedSdkSandboxInterface(
APP_OWNED_SDK_SANDBOX_INTERFACE_NAME);
}
}
@Test
public void testGetSandboxedSdkSuccessfully() {
loadSdk();
List<SandboxedSdk> sandboxedSdks = mSdkSandboxManager.getSandboxedSdks();
assertThat(sandboxedSdks.size()).isEqualTo(1);
assertThat(sandboxedSdks.get(0).getSharedLibraryInfo().getName()).isEqualTo(SDK_NAME_1);
mSdkSandboxManager.unloadSdk(SDK_NAME_1);
List<SandboxedSdk> sandboxedSdksAfterUnload = mSdkSandboxManager.getSandboxedSdks();
assertThat(sandboxedSdksAfterUnload.size()).isEqualTo(0);
}
@Test
public void testLoadSdkAndCheckClassloader() throws Exception {
ICtsSdkProviderApi sdk = loadSdk();
sdk.checkClassloaders();
}
@Test
public void testGetOpPackageName() throws Exception {
ICtsSdkProviderApi sdk = loadSdk();
final PackageManager pm =
InstrumentationRegistry.getInstrumentation().getContext().getPackageManager();
assertThat(sdk.getOpPackageName()).isEqualTo(pm.getSdkSandboxPackageName());
}
@Test
public void testRetryLoadSameSdkShouldFail() {
FakeLoadSdkCallback callback = new FakeLoadSdkCallback();
mSdkSandboxManager.loadSdk(SDK_NAME_1, new Bundle(), Runnable::run, callback);
callback.assertLoadSdkIsSuccessful();
callback = new FakeLoadSdkCallback();
mSdkSandboxManager.loadSdk(SDK_NAME_1, new Bundle(), Runnable::run, callback);
callback.assertLoadSdkIsUnsuccessful();
assertThat(callback.getLoadSdkErrorCode())
.isEqualTo(SdkSandboxManager.LOAD_SDK_ALREADY_LOADED);
}
@Test
public void testLoadNonExistentSdk() {
final FakeLoadSdkCallback callback = new FakeLoadSdkCallback();
mSdkSandboxManager.loadSdk(NON_EXISTENT_SDK, new Bundle(), Runnable::run, callback);
callback.assertLoadSdkIsUnsuccessful();
assertThat(callback.getLoadSdkErrorCode())
.isEqualTo(SdkSandboxManager.LOAD_SDK_NOT_FOUND);
LoadSdkException loadSdkException = callback.getLoadSdkException();
assertThat(loadSdkException.getExtraInformation()).isNotNull();
assertThat(loadSdkException.getExtraInformation().isEmpty()).isTrue();
}
@Test
public void testLoadSdkWithInternalErrorShouldFail() throws Exception {
final FakeLoadSdkCallback callback = new FakeLoadSdkCallback();
Bundle params = new Bundle();
params.putString(TEST_OPTION, OPTION_THROW_INTERNAL_ERROR);
mSdkSandboxManager.loadSdk(SDK_NAME_1, params, Runnable::run, callback);
callback.assertLoadSdkIsUnsuccessful();
assertThat(callback.getLoadSdkErrorCode())
.isEqualTo(SdkSandboxManager.LOAD_SDK_SDK_DEFINED_ERROR);
}
@Test
public void testUnloadAndReloadSdk() throws Exception {
final FakeLoadSdkCallback callback = new FakeLoadSdkCallback();
mSdkSandboxManager.loadSdk(SDK_NAME_1, new Bundle(), Runnable::run, callback);
callback.assertLoadSdkIsSuccessful();
mSdkSandboxManager.unloadSdk(SDK_NAME_1);
// Wait till SDK is unloaded.
Thread.sleep(2000);
// Calls to an unloaded SDK should fail.
final FakeRequestSurfacePackageCallback requestSurfacePackageCallback =
new FakeRequestSurfacePackageCallback();
mSdkSandboxManager.requestSurfacePackage(
SDK_NAME_1,
getRequestSurfacePackageParams(),
Runnable::run,
requestSurfacePackageCallback);
assertThat(requestSurfacePackageCallback.isRequestSurfacePackageSuccessful()).isFalse();
assertThat(requestSurfacePackageCallback.getSurfacePackageErrorCode())
.isEqualTo(SdkSandboxManager.REQUEST_SURFACE_PACKAGE_SDK_NOT_LOADED);
// SDK can be reloaded after being unloaded.
final FakeLoadSdkCallback callback2 = new FakeLoadSdkCallback();
mSdkSandboxManager.loadSdk(SDK_NAME_1, new Bundle(), Runnable::run, callback2);
callback2.assertLoadSdkIsSuccessful();
}
@Test
public void testUnloadNonexistentSdk() {
final FakeLoadSdkCallback callback = new FakeLoadSdkCallback();
mSdkSandboxManager.loadSdk(SDK_NAME_1, new Bundle(), Runnable::run, callback);
callback.assertLoadSdkIsSuccessful();
final String nonexistentSdk = "com.android.nonexistent";
// Unloading does nothing - call should go through without error.
mSdkSandboxManager.unloadSdk(nonexistentSdk);
}
@Test
public void testReloadingSdkDoesNotInvalidateIt() {
final FakeLoadSdkCallback callback = new FakeLoadSdkCallback();
mSdkSandboxManager.loadSdk(SDK_NAME_1, new Bundle(), Runnable::run, callback);
callback.assertLoadSdkIsSuccessful();
SandboxedSdk sandboxedSdk = callback.getSandboxedSdk();
assertNotNull(sandboxedSdk.getInterface());
// Attempt to load the SDK again and see that it fails.
final FakeLoadSdkCallback reloadCallback = new FakeLoadSdkCallback();
mSdkSandboxManager.loadSdk(SDK_NAME_1, new Bundle(), Runnable::run, reloadCallback);
reloadCallback.assertLoadSdkIsUnsuccessful();
// SDK's interface should still be obtainable.
assertNotNull(sandboxedSdk.getInterface());
// Further calls to the SDK should still be valid.
final FakeRequestSurfacePackageCallback surfacePackageCallback =
new FakeRequestSurfacePackageCallback();
mSdkSandboxManager.requestSurfacePackage(
SDK_NAME_1,
getRequestSurfacePackageParams(),
Runnable::run,
surfacePackageCallback);
assertThat(surfacePackageCallback.isRequestSurfacePackageSuccessful()).isTrue();
}
@Test
public void testReloadingSdkAfterKillingSandboxIsSuccessful() throws Exception {
// Kill the sandbox if it already exists from previous tests
killSandboxIfExists();
FakeSdkSandboxProcessDeathCallback callback = new FakeSdkSandboxProcessDeathCallback();
mSdkSandboxManager.addSdkSandboxProcessDeathCallback(Runnable::run, callback);
assertThat(callback.getSdkSandboxDeathCount()).isEqualTo(0);
// Killing the sandbox and loading the same SDKs again multiple times should work
for (int i = 1; i <= 3; ++i) {
// The same SDKs should be able to be loaded again after sandbox death
loadMultipleSdks();
killSandbox();
assertThat(callback.getSdkSandboxDeathCount()).isEqualTo(i);
}
}
@Test
public void testAddSdkSandboxProcessDeathCallback_BeforeStartingSandbox() throws Exception {
// Kill the sandbox if it already exists from previous tests
killSandboxIfExists();
// Add a sandbox lifecycle callback before starting the sandbox
FakeSdkSandboxProcessDeathCallback lifecycleCallback =
new FakeSdkSandboxProcessDeathCallback();
mSdkSandboxManager.addSdkSandboxProcessDeathCallback(Runnable::run, lifecycleCallback);
// Bring up the sandbox
loadSdk();
killSandbox();
assertThat(lifecycleCallback.getSdkSandboxDeathCount()).isEqualTo(1);
}
@Test
public void testAddSdkSandboxProcessDeathCallback_AfterStartingSandbox() throws Exception {
// Bring up the sandbox
loadSdk();
// Add a sandbox lifecycle callback before starting the sandbox
FakeSdkSandboxProcessDeathCallback lifecycleCallback =
new FakeSdkSandboxProcessDeathCallback();
mSdkSandboxManager.addSdkSandboxProcessDeathCallback(Runnable::run, lifecycleCallback);
killSandbox();
assertThat(lifecycleCallback.getSdkSandboxDeathCount()).isEqualTo(1);
}
@Test
public void testRegisterMultipleSdkSandboxProcessDeathCallbacks() throws Exception {
// Kill the sandbox if it already exists from previous tests
killSandboxIfExists();
// Add a sandbox lifecycle callback before starting the sandbox
FakeSdkSandboxProcessDeathCallback lifecycleCallback1 =
new FakeSdkSandboxProcessDeathCallback();
mSdkSandboxManager.addSdkSandboxProcessDeathCallback(Runnable::run, lifecycleCallback1);
// Bring up the sandbox
loadSdk();
// Add another sandbox lifecycle callback after starting it
FakeSdkSandboxProcessDeathCallback lifecycleCallback2 =
new FakeSdkSandboxProcessDeathCallback();
mSdkSandboxManager.addSdkSandboxProcessDeathCallback(Runnable::run, lifecycleCallback2);
killSandbox();
assertThat(lifecycleCallback1.getSdkSandboxDeathCount()).isEqualTo(1);
assertThat(lifecycleCallback2.getSdkSandboxDeathCount()).isEqualTo(1);
}
@Test
public void testRemoveSdkSandboxProcessDeathCallback() throws Exception {
// Bring up the sandbox
loadSdk();
// Add and remove a sandbox lifecycle callback
FakeSdkSandboxProcessDeathCallback lifecycleCallback1 =
new FakeSdkSandboxProcessDeathCallback();
mSdkSandboxManager.addSdkSandboxProcessDeathCallback(Runnable::run, lifecycleCallback1);
mSdkSandboxManager.removeSdkSandboxProcessDeathCallback(lifecycleCallback1);
// Add a lifecycle callback but don't remove it
FakeSdkSandboxProcessDeathCallback lifecycleCallback2 =
new FakeSdkSandboxProcessDeathCallback();
mSdkSandboxManager.addSdkSandboxProcessDeathCallback(Runnable::run, lifecycleCallback2);
killSandbox();
assertThat(lifecycleCallback1.getSdkSandboxDeathCount()).isEqualTo(0);
assertThat(lifecycleCallback2.getSdkSandboxDeathCount()).isEqualTo(1);
}
@Test
public void testRequestSurfacePackageSuccessfully() {
loadSdk();
final FakeRequestSurfacePackageCallback surfacePackageCallback =
new FakeRequestSurfacePackageCallback();
mSdkSandboxManager.requestSurfacePackage(
SDK_NAME_1,
getRequestSurfacePackageParams(),
Runnable::run,
surfacePackageCallback);
assertThat(surfacePackageCallback.isRequestSurfacePackageSuccessful()).isTrue();
}
@Test
public void testRequestSurfacePackageWithInternalErrorShouldFail() {
loadSdk();
final FakeRequestSurfacePackageCallback surfacePackageCallback =
new FakeRequestSurfacePackageCallback();
Bundle params = getRequestSurfacePackageParams();
params.putString(TEST_OPTION, OPTION_THROW_REQUEST_SURFACE_PACKAGE_ERROR);
mSdkSandboxManager.requestSurfacePackage(
SDK_NAME_1, params, Runnable::run, surfacePackageCallback);
assertThat(surfacePackageCallback.isRequestSurfacePackageSuccessful()).isFalse();
assertThat(surfacePackageCallback.getSurfacePackageErrorCode())
.isEqualTo(SdkSandboxManager.REQUEST_SURFACE_PACKAGE_INTERNAL_ERROR);
assertThat(surfacePackageCallback.getExtraErrorInformation()).isNotNull();
assertThat(surfacePackageCallback.getExtraErrorInformation().isEmpty()).isTrue();
}
@Test
public void testRequestSurfacePackage_SandboxDiesAfterLoadingSdk() throws Exception {
loadSdk();
assertThat(killSandboxIfExists()).isTrue();
final FakeRequestSurfacePackageCallback surfacePackageCallback =
new FakeRequestSurfacePackageCallback();
mSdkSandboxManager.requestSurfacePackage(
SDK_NAME_1,
getRequestSurfacePackageParams(),
Runnable::run,
surfacePackageCallback);
assertThat(surfacePackageCallback.isRequestSurfacePackageSuccessful()).isFalse();
assertThat(surfacePackageCallback.getSurfacePackageErrorCode())
.isEqualTo(SdkSandboxManager.REQUEST_SURFACE_PACKAGE_SDK_NOT_LOADED);
}
@Test
public void testResourcesAndAssets() throws Exception {
ICtsSdkProviderApi sdk = loadSdk();
sdk.checkResourcesAndAssets();
}
@Test
public void testLoadSdkInBackgroundFails() throws Exception {
mScenario.moveToState(Lifecycle.State.DESTROYED);
// Wait for the activity to be destroyed
Thread.sleep(1000);
final FakeLoadSdkCallback callback = new FakeLoadSdkCallback();
mSdkSandboxManager.loadSdk(SDK_NAME_1, new Bundle(), Runnable::run, callback);
LoadSdkException thrown = callback.getLoadSdkException();
assertEquals(LOAD_SDK_INTERNAL_ERROR, thrown.getLoadSdkErrorCode());
assertThat(thrown).hasMessageThat().contains("does not run in the foreground");
}
@Test
public void testSandboxApisAreUsableAfterUnbindingSandbox() throws Exception {
FakeLoadSdkCallback callback1 = new FakeLoadSdkCallback();
mSdkSandboxManager.loadSdk(SDK_NAME_1, new Bundle(), Runnable::run, callback1);
callback1.assertLoadSdkIsSuccessful();
// Move the app to the background and bring it back to the foreground again.
mScenario.recreate();
// Loading another sdk should work without issue
FakeLoadSdkCallback callback2 = new FakeLoadSdkCallback();
mSdkSandboxManager.loadSdk(SDK_NAME_2, new Bundle(), Runnable::run, callback2);
callback2.assertLoadSdkIsSuccessful();
// Requesting surface package from the first loaded sdk should work.
final FakeRequestSurfacePackageCallback surfacePackageCallback =
new FakeRequestSurfacePackageCallback();
mSdkSandboxManager.requestSurfacePackage(
SDK_NAME_1,
getRequestSurfacePackageParams(),
Runnable::run,
surfacePackageCallback);
assertThat(surfacePackageCallback.isRequestSurfacePackageSuccessful()).isTrue();
}
/** Checks that {@code SdkSandbox.apk} only requests normal permissions in its manifest. */
// TODO: This should probably be a separate test module
@Test
public void testSdkSandboxPermissions() throws Exception {
final PackageManager pm =
InstrumentationRegistry.getInstrumentation().getContext().getPackageManager();
final PackageInfo sdkSandboxPackage =
pm.getPackageInfo(
pm.getSdkSandboxPackageName(),
PackageManager.PackageInfoFlags.of(PackageManager.GET_PERMISSIONS));
for (int i = 0; i < sdkSandboxPackage.requestedPermissions.length; i++) {
final String permissionName = sdkSandboxPackage.requestedPermissions[i];
final PermissionInfo permissionInfo = pm.getPermissionInfo(permissionName, 0);
mExpect.withMessage("SdkSandbox.apk requests non-normal permission " + permissionName)
.that(permissionInfo.getProtection())
.isEqualTo(PermissionInfo.PROTECTION_NORMAL);
}
}
// TODO(b/244730098): The test below needs to be moved from e2e.
// It is not and e2e test.
@Test
public void testLoadSdkExceptionWriteToParcel() {
final Bundle bundle = new Bundle();
bundle.putChar("testKey", /*testValue=*/ 'C');
final String errorMessage = "Error Message";
final Exception cause = new Exception(errorMessage);
final LoadSdkException exception = new LoadSdkException(cause, bundle);
final Parcel parcel = Parcel.obtain();
exception.writeToParcel(parcel, /*flags=*/ 0);
// Create LoadSdkException with the same parcel
parcel.setDataPosition(0); // rewind
final LoadSdkException exceptionCheck = LoadSdkException.CREATOR.createFromParcel(parcel);
assertThat(exceptionCheck.getLoadSdkErrorCode()).isEqualTo(exception.getLoadSdkErrorCode());
assertThat(exceptionCheck.getMessage()).isEqualTo(exception.getMessage());
assertThat(exceptionCheck.getExtraInformation().getChar("testKey"))
.isEqualTo(exception.getExtraInformation().getChar("testKey"));
assertThat(exceptionCheck.getExtraInformation().keySet()).containsExactly("testKey");
}
// TODO(b/244730098): The test below needs to be moved from e2e.
// It is not and e2e test.
@Test
public void testLoadSdkExceptionDescribeContents() throws Exception {
final LoadSdkException exception = new LoadSdkException(new Exception(), new Bundle());
assertThat(exception.describeContents()).isEqualTo(0);
}
// TODO(b/244730098): The test below needs to be moved from e2e.
// It is not and e2e test.
@Test
public void testSandboxedSdkDescribeContents() throws Exception {
final SandboxedSdk sandboxedSdk = new SandboxedSdk(new Binder());
assertThat(sandboxedSdk.describeContents()).isEqualTo(0);
}
@Test
public void testSdkAndAppProcessImportanceIsAligned_AppIsBackgrounded() throws Exception {
// Sandbox and app priority is aligned only in U+.
assumeTrue(SdkLevel.isAtLeastU());
ICtsSdkProviderApi sdk = loadSdk();
assertThat(sdk.getProcessImportance()).isEqualTo(getAppProcessImportance());
// Move the app to the background.
mScenario.moveToState(Lifecycle.State.DESTROYED);
Thread.sleep(1000);
assertThat(sdk.getProcessImportance()).isEqualTo(getAppProcessImportance());
}
@Test
public void testSdkAndAppProcessImportanceIsAligned_AppIsBackgroundedAndForegrounded()
throws Exception {
// Sandbox and app priority is aligned only in U+.
assumeTrue(SdkLevel.isAtLeastU());
ICtsSdkProviderApi sdk = loadSdk();
assertThat(sdk.getProcessImportance()).isEqualTo(getAppProcessImportance());
// Move the app to the background and bring it back to the foreground again.
mScenario.recreate();
// The sandbox should have foreground importance again.
assertThat(sdk.getProcessImportance()).isEqualTo(getAppProcessImportance());
}
@Test
public void testStartSdkSandboxedActivities() {
assumeTrue(SdkLevel.isAtLeastU());
// Load SDK in sandbox
ICtsSdkProviderApi sdk = loadSdk();
mRule.getScenario()
.onActivity(
clientActivity -> {
ActivityStarter activityStarter1 = new ActivityStarter(clientActivity);
try {
sdk.startActivity(activityStarter1);
// Wait for the activity to start and send confirmation back
Thread.sleep(1000);
assertThat(activityStarter1.isActivityStarted()).isTrue();
} catch (Exception e) {
fail(
"Exception is thrown while starting activity: "
+ e.getMessage());
}
// Start another sandbox activity is important, to make sure that the
// system is not restarting the sandbox activities after test is done.
ActivityStarter activityStarter2 = new ActivityStarter(clientActivity);
try {
sdk.startActivity(activityStarter2);
// Wait for the activity to start and send confirmation back
Thread.sleep(1000);
assertThat(activityStarter2.isActivityStarted()).isTrue();
} catch (Exception e) {
fail(
"Exception is thrown while starting activity: "
+ e.getMessage());
}
});
}
@Test
public void testStartSdkSandboxedActivityFailIfTheHandlerUnregistered() {
assumeTrue(SdkLevel.isAtLeastU());
// Load SDK in sandbox
ICtsSdkProviderApi sdk = loadSdk();
mRule.getScenario()
.onActivity(
activity -> {
ActivityStarter activityStarter = new ActivityStarter(activity);
try {
sdk.startActivityAfterUnregisterHandler(activityStarter);
// Wait for the activity to be initiated and destroyed.
Thread.sleep(1000);
assertThat(activityStarter.isActivityStarted()).isFalse();
} catch (Exception e) {
fail(
"Exception is thrown while starting activity: "
+ e.getMessage());
}
});
}
// Helper method to load SDK_NAME_1
private ICtsSdkProviderApi loadSdk() {
final FakeLoadSdkCallback callback = new FakeLoadSdkCallback();
mSdkSandboxManager.loadSdk(SDK_NAME_1, new Bundle(), Runnable::run, callback);
callback.assertLoadSdkIsSuccessful();
final SandboxedSdk sandboxedSdk = callback.getSandboxedSdk();
assertNotNull(sandboxedSdk);
return ICtsSdkProviderApi.Stub.asInterface(callback.getSandboxedSdk().getInterface());
}
private int getAppProcessImportance() {
ActivityManager.RunningAppProcessInfo processInfo =
new ActivityManager.RunningAppProcessInfo();
ActivityManager.getMyMemoryState(processInfo);
return processInfo.importance;
}
private class ActivityStarter extends IActivityStarter.Stub {
private Activity mActivity;
private boolean mActivityStarted = false;
ActivityStarter(Activity activity) {
this.mActivity = activity;
}
@Override
public void startActivity(IBinder token) throws RemoteException {
mSdkSandboxManager.startSdkSandboxActivity(mActivity, token);
}
// SDK will call this function to notify that the activity is successfully created
@Override
public void activityStartedSuccessfully() {
mActivityStarted = true;
}
public boolean isActivityStarted() {
return mActivityStarted;
}
}
private Bundle getRequestSurfacePackageParams() {
Bundle params = new Bundle();
params.putInt(EXTRA_WIDTH_IN_PIXELS, 500);
params.putInt(EXTRA_HEIGHT_IN_PIXELS, 500);
params.putInt(EXTRA_DISPLAY_ID, 0);
params.putBinder(EXTRA_HOST_TOKEN, new Binder());
return params;
}
private void loadMultipleSdks() {
FakeLoadSdkCallback callback = new FakeLoadSdkCallback();
mSdkSandboxManager.loadSdk(SDK_NAME_1, new Bundle(), Runnable::run, callback);
callback.assertLoadSdkIsSuccessful();
FakeLoadSdkCallback callback2 = new FakeLoadSdkCallback();
mSdkSandboxManager.loadSdk(SDK_NAME_2, new Bundle(), Runnable::run, callback2);
callback2.assertLoadSdkIsSuccessful();
}
// Returns true if the sandbox was already likely existing, false otherwise.
private boolean killSandboxIfExists() throws Exception {
FakeSdkSandboxProcessDeathCallback callback = new FakeSdkSandboxProcessDeathCallback();
mSdkSandboxManager.addSdkSandboxProcessDeathCallback(Runnable::run, callback);
killSandbox();
return callback.getSdkSandboxDeathCount() > 0;
}
private void killSandbox() throws Exception {
// TODO(b/241542162): Avoid using reflection as a workaround once test apis can be run
// without issue.
mSdkSandboxManager.getClass().getMethod("stopSdkSandbox").invoke(mSdkSandboxManager);
}
}