blob: 15af3bd1ee00edb60680daa8c76940fcdd8360eb [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.server.sdksandbox;
import static android.Manifest.permission.DUMP;
import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
import static android.app.sdksandbox.ISharedPreferencesSyncCallback.PREFERENCES_SYNC_INTERNAL_ERROR;
import static android.app.sdksandbox.SdkSandboxManager.ACTION_START_SANDBOXED_ACTIVITY;
import static android.app.sdksandbox.SdkSandboxManager.LOAD_SDK_INTERNAL_ERROR;
import static com.android.sdksandbox.service.stats.SdkSandboxStatsLog.SANDBOX_API_CALLED__METHOD__ADD_SDK_SANDBOX_LIFECYCLE_CALLBACK;
import static com.android.sdksandbox.service.stats.SdkSandboxStatsLog.SANDBOX_API_CALLED__METHOD__REMOVE_SDK_SANDBOX_LIFECYCLE_CALLBACK;
import static com.android.sdksandbox.service.stats.SdkSandboxStatsLog.SANDBOX_API_CALLED__METHOD__REQUEST_SURFACE_PACKAGE;
import static com.android.sdksandbox.service.stats.SdkSandboxStatsLog.SANDBOX_API_CALLED__STAGE__SANDBOX_TO_SYSTEM_SERVER;
import static com.android.sdksandbox.service.stats.SdkSandboxStatsLog.SANDBOX_API_CALLED__STAGE__SYSTEM_SERVER_SANDBOX_TO_APP;
import static com.android.sdksandbox.service.stats.SdkSandboxStatsLog.SANDBOX_API_CALLED__STAGE__SYSTEM_SERVER_TO_SANDBOX;
import static com.android.server.wm.ActivityInterceptorCallback.MAINLINE_SDK_SANDBOX_ORDER_ID;
import com.android.modules.utils.build.SdkLevel;
import com.android.sdksandbox.IComputeSdkStorageCallback;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThrows;
import static org.junit.Assume.assumeFalse;
import static org.junit.Assume.assumeTrue;
import static org.mockito.ArgumentMatchers.eq;
import android.Manifest;
import android.app.ActivityManager;
import android.app.sdksandbox.AppOwnedSdkSandboxInterface;
import android.app.sdksandbox.ILoadSdkCallback;
import android.app.sdksandbox.ISdkSandboxManager;
import android.app.sdksandbox.ISharedPreferencesSyncCallback;
import android.app.sdksandbox.LoadSdkException;
import android.app.sdksandbox.SdkSandboxManager;
import android.app.sdksandbox.SharedPreferencesUpdate;
import android.app.sdksandbox.testutils.DeviceSupportUtils;
import android.app.sdksandbox.testutils.FakeLoadSdkCallbackBinder;
import android.app.sdksandbox.testutils.FakeRequestSurfacePackageCallbackBinder;
import android.app.sdksandbox.testutils.FakeSdkSandboxManagerLocal;
import android.app.sdksandbox.testutils.FakeSdkSandboxProcessDeathCallbackBinder;
import android.app.sdksandbox.testutils.FakeSdkSandboxService;
import android.app.sdksandbox.testutils.FakeSharedPreferencesSyncCallback;
import android.app.sdksandbox.testutils.SdkSandboxStorageManagerUtility;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ProviderInfo;
import android.content.pm.UserInfo;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.provider.DeviceConfig;
import android.util.ArrayMap;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.test.platform.app.InstrumentationRegistry;
import com.android.dx.mockito.inline.extended.ExtendedMockito;
import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder;
import com.android.modules.utils.build.SdkLevel;
import com.android.sdksandbox.ISdkSandboxService;
import com.android.sdksandbox.IUnloadSdkCallback;
import com.android.sdksandbox.SandboxLatencyInfo;
import com.android.sdksandbox.service.stats.SdkSandboxStatsLog;
import com.android.server.LocalManagerRegistry;
import com.android.server.SystemService.TargetUser;
import com.android.server.am.ActivityManagerLocal;
import com.android.server.pm.PackageManagerLocal;
import com.android.server.sdksandbox.SdkSandboxStorageManager.StorageDirInfo;
import com.android.server.wm.ActivityInterceptorCallback;
import com.android.server.wm.ActivityInterceptorCallbackRegistry;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.ArgumentMatchers;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoSession;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/**
* Unit tests for {@link SdkSandboxManagerService}.
*/
public class SdkSandboxManagerServiceUnitTest {
private static final String TAG = SdkSandboxManagerServiceUnitTest.class.getSimpleName();
private SdkSandboxManagerService mService;
private ActivityManager mAmSpy;
private FakeSdkSandboxService mSdkSandboxService;
private MockitoSession mStaticMockSession;
private Context mSpyContext;
private SdkSandboxManagerService.Injector mInjector;
private int mClientAppUid;
private PackageManagerLocal mPmLocal;
private ActivityManagerLocal mAmLocal;
private ArgumentCaptor<ActivityInterceptorCallback> mInterceptorCallbackArgumentCaptor =
ArgumentCaptor.forClass(ActivityInterceptorCallback.class);
private SdkSandboxStorageManagerUtility mSdkSandboxStorageManagerUtility;
@Mock private IBinder mAdServicesManager;
private static FakeSdkSandboxProvider sProvider;
private static SdkSandboxPulledAtoms sSdkSandboxPulledAtoms;
private static SdkSandboxManagerService.SdkSandboxSettingsListener sSdkSandboxSettingsListener;
private SdkSandboxStorageManager mSdkSandboxStorageManager;
private static SdkSandboxManagerLocal sSdkSandboxManagerLocal;
private static final String SDK_NAME = "com.android.codeprovider";
private static final String APP_OWNED_SDK_SANDBOX_INTERFACE_NAME = "com.android.testinterface";
private static final String SDK_PROVIDER_PACKAGE = "com.android.codeprovider_1";
private static final String SDK_PROVIDER_RESOURCES_SDK_NAME =
"com.android.codeproviderresources";
private static final String SDK_PROVIDER_RESOURCES_PACKAGE =
"com.android.codeproviderresources_1";
private static final String TEST_PACKAGE = "com.android.server.sdksandbox.tests";
private static final String PROPERTY_DISABLE_SANDBOX = "disable_sdk_sandbox";
private static final long TIME_APP_CALLED_SYSTEM_SERVER = 1;
private static final long TIME_SYSTEM_SERVER_RECEIVED_CALL_FROM_APP = 3;
private static final long START_TIME_TO_LOAD_SANDBOX = 5;
private static final long END_TIME_TO_LOAD_SANDBOX = 7;
private static final long TIME_SYSTEM_SERVER_CALLS_SANDBOX = 9;
private static final long TIME_FAILURE_HANDLED = 11;
private static final long END_TIME_IN_SYSTEM_SERVER = 15;
private static final long TIME_SYSTEM_SERVER_CALLED_SANDBOX = 17;
private static final long TIME_SANDBOX_RECEIVED_CALL_FROM_SYSTEM_SERVER = 18;
private static final long TIME_SANDBOX_CALLED_SDK = 19;
private static final long TIME_SDK_CALL_COMPLETED = 20;
private static final long TIME_SANDBOX_CALLED_SYSTEM_SERVER = 21;
private static final long TIME_SYSTEM_SERVER_RECEIVED_CALL_FROM_SANDBOX = 22;
private static final long TIME_SYSTEM_SERVER_CALLED_APP = 23;
private static final long TIME_SYSTEM_SERVER_COMPLETED_EXECUTION = 24;
private static final String TEST_KEY = "key";
private static final String TEST_VALUE = "value";
private static final SharedPreferencesUpdate TEST_UPDATE =
new SharedPreferencesUpdate(new ArrayList<>(), getTestBundle());
private static final String PROPERTY_ENFORCE_BROADCAST_RECEIVER_RESTRICTIONS =
"enforce_broadcast_receiver_restrictions";
private static final String PROPERTY_ENFORCE_CONTENT_PROVIDER_RESTRICTIONS =
"enforce_content_provider_restrictions";
@Before
public void setup() {
assumeTrue(
DeviceSupportUtils.isSdkSandboxSupported(
InstrumentationRegistry.getInstrumentation().getContext()));
StaticMockitoSessionBuilder mockitoSessionBuilder =
ExtendedMockito.mockitoSession()
.mockStatic(LocalManagerRegistry.class)
.mockStatic(SdkSandboxStatsLog.class)
.spyStatic(Process.class)
.initMocks(this);
if (SdkLevel.isAtLeastU()) {
mockitoSessionBuilder =
mockitoSessionBuilder.mockStatic(ActivityInterceptorCallbackRegistry.class);
}
mStaticMockSession = mockitoSessionBuilder.startMocking();
if (SdkLevel.isAtLeastU()) {
// mock the activity interceptor registry anc capture the callback if called
ActivityInterceptorCallbackRegistry registryMock =
Mockito.mock(ActivityInterceptorCallbackRegistry.class);
ExtendedMockito.doReturn(registryMock)
.when(ActivityInterceptorCallbackRegistry::getInstance);
Mockito.doNothing()
.when(registryMock)
.registerActivityInterceptorCallback(
eq(MAINLINE_SDK_SANDBOX_ORDER_ID),
mInterceptorCallbackArgumentCaptor.capture());
}
Context context = InstrumentationRegistry.getInstrumentation().getContext();
mSpyContext = Mockito.spy(context);
ActivityManager am = context.getSystemService(ActivityManager.class);
mAmSpy = Mockito.spy(Objects.requireNonNull(am));
Mockito.when(mSpyContext.getSystemService(ActivityManager.class)).thenReturn(mAmSpy);
// Required to access <sdk-library> information and DeviceConfig update.
InstrumentationRegistry.getInstrumentation()
.getUiAutomation()
.adoptShellPermissionIdentity(
Manifest.permission.ACCESS_SHARED_LIBRARIES,
Manifest.permission.INSTALL_PACKAGES,
Manifest.permission.READ_DEVICE_CONFIG,
Manifest.permission.WRITE_DEVICE_CONFIG,
// for Context#registerReceiverForAllUsers
Manifest.permission.INTERACT_ACROSS_USERS_FULL);
mSdkSandboxService = Mockito.spy(FakeSdkSandboxService.class);
mSdkSandboxService.setTimeValues(
TIME_SYSTEM_SERVER_CALLED_SANDBOX,
TIME_SANDBOX_RECEIVED_CALL_FROM_SYSTEM_SERVER,
TIME_SANDBOX_CALLED_SDK,
TIME_SDK_CALL_COMPLETED,
TIME_SANDBOX_CALLED_SYSTEM_SERVER);
sProvider = new FakeSdkSandboxProvider(mSdkSandboxService);
// Populate LocalManagerRegistry
mAmLocal = Mockito.mock(ActivityManagerLocal.class);
ExtendedMockito.doReturn(mAmLocal)
.when(() -> LocalManagerRegistry.getManager(ActivityManagerLocal.class));
mPmLocal = Mockito.spy(PackageManagerLocal.class);
sSdkSandboxPulledAtoms = Mockito.spy(new SdkSandboxPulledAtoms());
String testDir = context.getDir("test_dir", Context.MODE_PRIVATE).getPath();
mSdkSandboxStorageManager =
new SdkSandboxStorageManager(
mSpyContext, new FakeSdkSandboxManagerLocal(), mPmLocal, testDir);
mSdkSandboxStorageManagerUtility =
new SdkSandboxStorageManagerUtility(mSdkSandboxStorageManager);
mInjector = Mockito.spy(new InjectorForTest(mSpyContext, mSdkSandboxStorageManager));
mService = new SdkSandboxManagerService(mSpyContext, mInjector);
mService.forceEnableSandbox();
sSdkSandboxManagerLocal = mService.getLocalManager();
assertThat(sSdkSandboxManagerLocal).isNotNull();
sSdkSandboxSettingsListener = mService.getSdkSandboxSettingsListener();
assertThat(sSdkSandboxSettingsListener).isNotNull();
mClientAppUid = Process.myUid();
}
@After
public void tearDown() {
if (sSdkSandboxSettingsListener != null) {
sSdkSandboxSettingsListener.unregisterPropertiesListener();
}
mStaticMockSession.finishMocking();
}
/** Mock the ActivityManager::killUid to avoid SecurityException thrown in test. **/
private void disableKillUid() {
Mockito.lenient().doNothing().when(mAmSpy).killUid(Mockito.anyInt(), Mockito.anyString());
}
private void disableForegroundCheck() {
Mockito.doReturn(IMPORTANCE_FOREGROUND).when(mAmSpy).getUidImportance(Mockito.anyInt());
}
/* Ignores network permission checks. */
private void disableNetworkPermissionChecks() {
Mockito.doNothing().when(mSpyContext).enforceCallingPermission(
Mockito.eq("android.permission.INTERNET"), Mockito.anyString());
Mockito.doNothing().when(mSpyContext).enforceCallingPermission(
Mockito.eq("android.permission.ACCESS_NETWORK_STATE"), Mockito.anyString());
}
@Test
public void testOnUserUnlocking() {
UserInfo userInfo = new UserInfo(/*id=*/ 0, /*name=*/ "tempUser", /*flags=*/ 0);
TargetUser user = new TargetUser(userInfo);
SdkSandboxManagerService.Lifecycle lifeCycle =
new SdkSandboxManagerService.Lifecycle(mSpyContext);
lifeCycle.mService = Mockito.mock(SdkSandboxManagerService.class);
lifeCycle.onUserUnlocking(user);
Mockito.verify(lifeCycle.mService).onUserUnlocking(ArgumentMatchers.eq(/*userId=*/ 0));
}
@Test
public void testSdkSandboxManagerIsRegistered() throws Exception {
ServiceManager.getServiceOrThrow(SdkSandboxManager.SDK_SANDBOX_SERVICE);
}
@Test
public void testRegisterAndGetAppOwnedSdkSandboxInterfaceSuccess() throws Exception {
final IBinder iBinder = new Binder();
mService.registerAppOwnedSdkSandboxInterface(
TEST_PACKAGE,
new AppOwnedSdkSandboxInterface(
APP_OWNED_SDK_SANDBOX_INTERFACE_NAME,
/*version=*/ 0,
/*interfaceIBinder=*/ iBinder),
TIME_APP_CALLED_SYSTEM_SERVER);
final List<AppOwnedSdkSandboxInterface> appOwnedSdkSandboxInterfaceList =
mService.getAppOwnedSdkSandboxInterfaces(
TEST_PACKAGE, TIME_APP_CALLED_SYSTEM_SERVER);
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);
}
@Test
public void testRegisterAppOwnedSdkSandboxInterfaceAlreadyRegistered() throws Exception {
mService.registerAppOwnedSdkSandboxInterface(
TEST_PACKAGE,
new AppOwnedSdkSandboxInterface(
APP_OWNED_SDK_SANDBOX_INTERFACE_NAME,
/*version=*/ 0,
/*interfaceIBinder=*/ new Binder()),
TIME_APP_CALLED_SYSTEM_SERVER);
assertThrows(
IllegalStateException.class,
() ->
mService.registerAppOwnedSdkSandboxInterface(
TEST_PACKAGE,
new AppOwnedSdkSandboxInterface(
APP_OWNED_SDK_SANDBOX_INTERFACE_NAME,
/*version=*/ 0,
/*interfaceIBinder=*/ new Binder()),
TIME_APP_CALLED_SYSTEM_SERVER));
}
@Test
public void testUnregisterAppOwnedSdkSandboxInterface() throws Exception {
mService.registerAppOwnedSdkSandboxInterface(
TEST_PACKAGE,
new AppOwnedSdkSandboxInterface(
APP_OWNED_SDK_SANDBOX_INTERFACE_NAME,
/*version=*/ 0,
/*interfaceIBinder=*/ new Binder()),
TIME_APP_CALLED_SYSTEM_SERVER);
mService.unregisterAppOwnedSdkSandboxInterface(
TEST_PACKAGE, APP_OWNED_SDK_SANDBOX_INTERFACE_NAME, TIME_APP_CALLED_SYSTEM_SERVER);
assertThat(
mService.getAppOwnedSdkSandboxInterfaces(
TEST_PACKAGE, TIME_APP_CALLED_SYSTEM_SERVER))
.hasSize(0);
}
@Test
public void testLoadSdkIsSuccessful() throws Exception {
disableNetworkPermissionChecks();
disableForegroundCheck();
FakeLoadSdkCallbackBinder callback = new FakeLoadSdkCallbackBinder();
mService.loadSdk(
TEST_PACKAGE,
null,
SDK_NAME,
TIME_APP_CALLED_SYSTEM_SERVER,
new Bundle(),
callback);
// Assume sdk sandbox loads successfully
mSdkSandboxService.sendLoadCodeSuccessful();
callback.assertLoadSdkIsSuccessful();
}
@Test
public void testLoadSdkNonExistentCallingPackage() {
FakeLoadSdkCallbackBinder callback = new FakeLoadSdkCallbackBinder();
mService.loadSdk(
"does.not.exist",
null,
SDK_NAME,
TIME_APP_CALLED_SYSTEM_SERVER,
new Bundle(),
callback);
LoadSdkException thrown = callback.getLoadSdkException();
assertEquals(LOAD_SDK_INTERNAL_ERROR, thrown.getLoadSdkErrorCode());
assertThat(thrown).hasMessageThat().contains("does.not.exist not found");
}
@Test
public void testLoadSdkIncorrectCallingPackage() {
FakeLoadSdkCallbackBinder callback = new FakeLoadSdkCallbackBinder();
mService.loadSdk(
SDK_PROVIDER_PACKAGE,
null,
SDK_NAME,
TIME_APP_CALLED_SYSTEM_SERVER,
new Bundle(),
callback);
LoadSdkException thrown = callback.getLoadSdkException();
assertEquals(LOAD_SDK_INTERNAL_ERROR, thrown.getLoadSdkErrorCode());
assertThat(thrown).hasMessageThat().contains("does not belong to uid");
}
@Test
public void testLoadSdkPackageDoesNotExist() {
disableNetworkPermissionChecks();
disableForegroundCheck();
FakeLoadSdkCallbackBinder callback = new FakeLoadSdkCallbackBinder();
mService.loadSdk(
TEST_PACKAGE,
null,
"does.not.exist",
TIME_APP_CALLED_SYSTEM_SERVER,
new Bundle(),
callback);
// Verify loading failed
callback.assertLoadSdkIsUnsuccessful();
assertThat(callback.getLoadSdkErrorCode())
.isEqualTo(SdkSandboxManager.LOAD_SDK_NOT_FOUND);
assertThat(callback.getLoadSdkErrorMsg()).contains("not found for loading");
}
@Test
public void testLoadSdk_errorFromSdkSandbox() throws Exception {
disableNetworkPermissionChecks();
disableForegroundCheck();
FakeLoadSdkCallbackBinder callback = new FakeLoadSdkCallbackBinder();
mService.loadSdk(
TEST_PACKAGE,
null,
SDK_NAME,
TIME_APP_CALLED_SYSTEM_SERVER,
new Bundle(),
callback);
mSdkSandboxService.sendLoadCodeError();
// Verify loading failed
callback.assertLoadSdkIsUnsuccessful();
assertThat(callback.getLoadSdkErrorCode()).isEqualTo(LOAD_SDK_INTERNAL_ERROR);
}
@Test
public void testLoadSdk_errorNoInternet() throws Exception {
FakeLoadSdkCallbackBinder callback = new FakeLoadSdkCallbackBinder();
mService.loadSdk(
TEST_PACKAGE,
null,
SDK_NAME,
TIME_APP_CALLED_SYSTEM_SERVER,
new Bundle(),
callback);
LoadSdkException thrown = callback.getLoadSdkException();
assertEquals(LOAD_SDK_INTERNAL_ERROR, thrown.getLoadSdkErrorCode());
assertThat(thrown).hasMessageThat().contains(android.Manifest.permission.INTERNET);
}
@Test
public void testLoadSdk_errorNoAccessNetworkState() throws Exception {
// Stub out internet permission check
Mockito.doNothing().when(mSpyContext).enforceCallingPermission(
Mockito.eq("android.permission.INTERNET"), Mockito.anyString());
FakeLoadSdkCallbackBinder callback = new FakeLoadSdkCallbackBinder();
mService.loadSdk(
TEST_PACKAGE,
null,
SDK_NAME,
TIME_APP_CALLED_SYSTEM_SERVER,
new Bundle(),
callback);
LoadSdkException thrown = callback.getLoadSdkException();
assertEquals(LOAD_SDK_INTERNAL_ERROR, thrown.getLoadSdkErrorCode());
assertThat(thrown).hasMessageThat().contains(
android.Manifest.permission.ACCESS_NETWORK_STATE);
}
@Test
public void testLoadSdk_successOnFirstLoad_errorOnLoadAgain() throws Exception {
disableNetworkPermissionChecks();
disableForegroundCheck();
// Load it once
{
FakeLoadSdkCallbackBinder callback = new FakeLoadSdkCallbackBinder();
mService.loadSdk(
TEST_PACKAGE,
null,
SDK_NAME,
TIME_APP_CALLED_SYSTEM_SERVER,
new Bundle(),
callback);
// Assume SupplementalProcess loads successfully
mSdkSandboxService.sendLoadCodeSuccessful();
callback.assertLoadSdkIsSuccessful();
}
// Load it again
{
FakeLoadSdkCallbackBinder callback = new FakeLoadSdkCallbackBinder();
mService.loadSdk(
TEST_PACKAGE,
null,
SDK_NAME,
TIME_APP_CALLED_SYSTEM_SERVER,
new Bundle(),
callback);
// Verify loading failed
callback.assertLoadSdkIsUnsuccessful();
assertThat(callback.getLoadSdkErrorCode()).isEqualTo(
SdkSandboxManager.LOAD_SDK_ALREADY_LOADED);
assertThat(callback.getLoadSdkErrorMsg()).contains("has been loaded already");
}
}
@Test
public void testLoadSdk_firstLoadPending_errorOnLoadAgainRequest() throws Exception {
disableNetworkPermissionChecks();
disableForegroundCheck();
// Request to load the SDK, but do not complete loading it
{
FakeLoadSdkCallbackBinder callback = new FakeLoadSdkCallbackBinder();
mService.loadSdk(
TEST_PACKAGE,
null,
SDK_NAME,
TIME_APP_CALLED_SYSTEM_SERVER,
new Bundle(),
callback);
}
// Requesting to load the SDK while the first load is still pending should throw an error
{
FakeLoadSdkCallbackBinder callback = new FakeLoadSdkCallbackBinder();
mService.loadSdk(
TEST_PACKAGE,
null,
SDK_NAME,
TIME_APP_CALLED_SYSTEM_SERVER,
new Bundle(),
callback);
// Verify loading failed
callback.assertLoadSdkIsUnsuccessful();
assertThat(callback.getLoadSdkErrorCode())
.isEqualTo(SdkSandboxManager.LOAD_SDK_ALREADY_LOADED);
assertThat(callback.getLoadSdkErrorMsg()).contains("is currently being loaded");
}
}
@Test
public void testLoadSdk_errorOnFirstLoad_canBeLoadedAgain() throws Exception {
disableNetworkPermissionChecks();
disableForegroundCheck();
// Load code, but make it fail
{
FakeLoadSdkCallbackBinder callback = new FakeLoadSdkCallbackBinder();
mService.loadSdk(
TEST_PACKAGE,
null,
SDK_NAME,
TIME_APP_CALLED_SYSTEM_SERVER,
new Bundle(),
callback);
// Assume sdk load fails
mSdkSandboxService.sendLoadCodeError();
callback.assertLoadSdkIsUnsuccessful();
}
// Caller should be able to retry loading the code
loadSdk(SDK_NAME);
}
@Test
public void testLoadSdk_sandboxDiesInBetween() throws Exception {
disableNetworkPermissionChecks();
disableForegroundCheck();
// Load an sdk
FakeLoadSdkCallbackBinder callback = new FakeLoadSdkCallbackBinder();
mService.loadSdk(
TEST_PACKAGE,
null,
SDK_NAME,
TIME_APP_CALLED_SYSTEM_SERVER,
new Bundle(),
callback);
// Kill the sandbox before the SDK can call the callback
killSandbox();
callback.assertLoadSdkIsUnsuccessful();
assertThat(callback.getLoadSdkErrorCode())
.isEqualTo(SdkSandboxManager.SDK_SANDBOX_PROCESS_NOT_AVAILABLE);
}
@Test
public void testSdkCanBeLoadedAfterSandboxDeath() throws Exception {
loadSdk(SDK_NAME);
killSandbox();
loadSdk(SDK_NAME);
}
@Test
public void testLoadSdk_sandboxIsInitialized() throws Exception {
loadSdk(SDK_NAME);
// Verify that sandbox was initialized
assertThat(mSdkSandboxService.getInitializationCount()).isEqualTo(1);
}
@Test
public void testLoadSdk_sandboxIsInitialized_onlyOnce() throws Exception {
loadSdk(SDK_NAME);
loadSdk(SDK_PROVIDER_RESOURCES_SDK_NAME);
// Verify that sandbox was initialized
assertThat(mSdkSandboxService.getInitializationCount()).isEqualTo(1);
}
@Test
public void testLoadSdk_sandboxIsInitializedAfterRestart() throws Exception {
loadSdk(SDK_NAME);
restartAndSetSandboxService();
// Restarting creates and sets a new sandbox service. Verify that the new one has been
// initialized.
assertThat(mSdkSandboxService.getInitializationCount()).isEqualTo(1);
}
@Test
public void testLoadSdk_sandboxInitializationFails() throws Exception {
disableNetworkPermissionChecks();
disableForegroundCheck();
disableKillUid();
mSdkSandboxService.failInitialization = true;
FakeLoadSdkCallbackBinder callback = new FakeLoadSdkCallbackBinder();
mService.loadSdk(
TEST_PACKAGE,
null,
SDK_NAME,
TIME_APP_CALLED_SYSTEM_SERVER,
new Bundle(),
callback);
// If initialization failed, the sandbox should have died and a reference to the sandbox
// service should not have been set.
final CallingInfo callingInfo = new CallingInfo(Process.myUid(), TEST_PACKAGE);
assertThat(sProvider.getSdkSandboxServiceForApp(callingInfo)).isEqualTo(null);
}
@Test
public void testLoadSdk_sdkDataPrepared_onlyOnce() throws Exception {
loadSdk(SDK_NAME);
loadSdk(SDK_PROVIDER_RESOURCES_SDK_NAME);
// Verify that SDK data was prepared.
Mockito.verify(mPmLocal, Mockito.times(1))
.reconcileSdkData(
Mockito.nullable(String.class),
Mockito.anyString(),
Mockito.anyList(),
Mockito.anyInt(),
Mockito.anyInt(),
Mockito.anyInt(),
Mockito.anyString(),
Mockito.anyInt());
}
@Test
public void testLoadSdk_sdkDataPreparedAfterSandboxRestart() throws Exception {
loadSdk(SDK_NAME);
restartAndSetSandboxService();
// Verify that SDK data was prepared for the newly restarted sandbox.
Mockito.verify(mPmLocal, Mockito.times(1))
.reconcileSdkData(
Mockito.nullable(String.class),
Mockito.anyString(),
Mockito.anyList(),
Mockito.anyInt(),
Mockito.anyInt(),
Mockito.anyInt(),
Mockito.anyString(),
Mockito.anyInt());
}
@Test
public void testRequestSurfacePackageSdkNotLoaded_SandboxExists() throws Exception {
disableNetworkPermissionChecks();
disableForegroundCheck();
FakeLoadSdkCallbackBinder callback = new FakeLoadSdkCallbackBinder();
mService.loadSdk(
TEST_PACKAGE,
null,
SDK_NAME,
TIME_APP_CALLED_SYSTEM_SERVER,
new Bundle(),
callback);
// Assume SupplementalProcess loads successfully
mSdkSandboxService.sendLoadCodeSuccessful();
callback.assertLoadSdkIsSuccessful();
// Trying to request package with not exist SDK packageName
String sdkName = "invalid";
FakeRequestSurfacePackageCallbackBinder surfacePackageCallback =
new FakeRequestSurfacePackageCallbackBinder();
mService.requestSurfacePackage(
TEST_PACKAGE,
sdkName,
new Binder(),
0,
500,
500,
TIME_APP_CALLED_SYSTEM_SERVER,
new Bundle(),
surfacePackageCallback);
assertThat(surfacePackageCallback.isRequestSurfacePackageSuccessful()).isFalse();
assertThat(surfacePackageCallback.getSurfacePackageErrorCode())
.isEqualTo(SdkSandboxManager.REQUEST_SURFACE_PACKAGE_SDK_NOT_LOADED);
}
@Test
public void testRequestSurfacePackageSdkNotLoaded_SandboxDoesNotExist() throws Exception {
disableForegroundCheck();
// Trying to request package when sandbox is not there
String sdkName = "invalid";
FakeRequestSurfacePackageCallbackBinder callback =
new FakeRequestSurfacePackageCallbackBinder();
mService.requestSurfacePackage(
TEST_PACKAGE,
sdkName,
new Binder(),
0,
500,
500,
TIME_APP_CALLED_SYSTEM_SERVER,
new Bundle(),
callback);
assertThat(callback.isRequestSurfacePackageSuccessful()).isFalse();
assertThat(callback.getSurfacePackageErrorCode())
.isEqualTo(SdkSandboxManager.REQUEST_SURFACE_PACKAGE_SDK_NOT_LOADED);
}
@Test
public void testRequestSurfacePackage() throws Exception {
// 1. We first need to collect a proper sdkToken by calling loadCode
loadSdk(SDK_NAME);
// 2. Call request package with the retrieved sdkToken
FakeRequestSurfacePackageCallbackBinder surfacePackageCallback =
new FakeRequestSurfacePackageCallbackBinder();
mService.requestSurfacePackage(
TEST_PACKAGE,
SDK_NAME,
new Binder(),
0,
500,
500,
TIME_APP_CALLED_SYSTEM_SERVER,
new Bundle(),
surfacePackageCallback);
mSdkSandboxService.sendSurfacePackageReady(
new SandboxLatencyInfo(TIME_SYSTEM_SERVER_CALLED_SANDBOX));
assertThat(surfacePackageCallback.isRequestSurfacePackageSuccessful()).isTrue();
}
@Test
public void testRequestSurfacePackageFailedAfterAppDied() throws Exception {
disableKillUid();
disableNetworkPermissionChecks();
disableForegroundCheck();
FakeLoadSdkCallbackBinder callback = Mockito.spy(new FakeLoadSdkCallbackBinder());
Mockito.doReturn(Mockito.mock(Binder.class)).when(callback).asBinder();
ArgumentCaptor<IBinder.DeathRecipient> deathRecipient =
ArgumentCaptor.forClass(IBinder.DeathRecipient.class);
mService.loadSdk(
TEST_PACKAGE,
null,
SDK_NAME,
TIME_APP_CALLED_SYSTEM_SERVER,
new Bundle(),
callback);
mSdkSandboxService.sendLoadCodeSuccessful();
callback.assertLoadSdkIsSuccessful();
Mockito.verify(callback.asBinder())
.linkToDeath(deathRecipient.capture(), ArgumentMatchers.eq(0));
// App Died
deathRecipient.getValue().binderDied();
FakeRequestSurfacePackageCallbackBinder requestSurfacePackageCallback =
new FakeRequestSurfacePackageCallbackBinder();
mService.requestSurfacePackage(
TEST_PACKAGE,
SDK_NAME,
new Binder(),
0,
500,
500,
TIME_APP_CALLED_SYSTEM_SERVER,
new Bundle(),
requestSurfacePackageCallback);
assertThat(requestSurfacePackageCallback.isRequestSurfacePackageSuccessful()).isFalse();
assertThat(requestSurfacePackageCallback.getSurfacePackageErrorCode())
.isEqualTo(SdkSandboxManager.REQUEST_SURFACE_PACKAGE_SDK_NOT_LOADED);
}
@Test
public void testSurfacePackageError() throws Exception {
loadSdk(SDK_NAME);
// Assume SurfacePackage encounters an error.
FakeRequestSurfacePackageCallbackBinder surfacePackageCallback =
new FakeRequestSurfacePackageCallbackBinder();
mSdkSandboxService.sendSurfacePackageError(
SdkSandboxManager.REQUEST_SURFACE_PACKAGE_INTERNAL_ERROR,
"bad surface",
surfacePackageCallback);
assertThat(surfacePackageCallback.getSurfacePackageErrorMsg()).contains("bad surface");
assertThat(surfacePackageCallback.getSurfacePackageErrorCode())
.isEqualTo(SdkSandboxManager.REQUEST_SURFACE_PACKAGE_INTERNAL_ERROR);
}
@Test
public void testRequestSurfacePackage_SandboxDiesInBetween() throws Exception {
disableNetworkPermissionChecks();
disableForegroundCheck();
// Load an sdk
FakeLoadSdkCallbackBinder callback = new FakeLoadSdkCallbackBinder();
mService.loadSdk(
TEST_PACKAGE,
null,
SDK_NAME,
TIME_APP_CALLED_SYSTEM_SERVER,
new Bundle(),
callback);
mSdkSandboxService.sendLoadCodeSuccessful();
callback.assertLoadSdkIsSuccessful();
// Request surface package from the SDK
FakeRequestSurfacePackageCallbackBinder surfacePackageCallback =
new FakeRequestSurfacePackageCallbackBinder();
mService.requestSurfacePackage(
TEST_PACKAGE,
SDK_NAME,
new Binder(),
0,
500,
500,
TIME_APP_CALLED_SYSTEM_SERVER,
new Bundle(),
surfacePackageCallback);
// Kill the sandbox before the SDK can call the callback
killSandbox();
assertThat(surfacePackageCallback.isRequestSurfacePackageSuccessful()).isFalse();
assertThat(surfacePackageCallback.getSurfacePackageErrorCode())
.isEqualTo(SdkSandboxManager.REQUEST_SURFACE_PACKAGE_SDK_NOT_LOADED);
}
@Test
public void testAddSdkSandboxProcessDeathCallback_BeforeStartingSandbox() throws Exception {
// Register for sandbox death event
FakeSdkSandboxProcessDeathCallbackBinder lifecycleCallback =
new FakeSdkSandboxProcessDeathCallbackBinder();
mService.addSdkSandboxProcessDeathCallback(
TEST_PACKAGE, TIME_APP_CALLED_SYSTEM_SERVER, lifecycleCallback);
// Load SDK and start the sandbox
loadSdk(SDK_NAME);
killSandbox();
// Check that death is recorded correctly
assertThat(lifecycleCallback.getSdkSandboxDeathCount()).isEqualTo(1);
}
@Test
public void testAddSdkSandboxProcessDeathCallback_AfterStartingSandbox() throws Exception {
// Load SDK and start the sandbox
loadSdk(SDK_NAME);
// Register for sandbox death event
FakeSdkSandboxProcessDeathCallbackBinder lifecycleCallback =
new FakeSdkSandboxProcessDeathCallbackBinder();
mService.addSdkSandboxProcessDeathCallback(
TEST_PACKAGE, TIME_APP_CALLED_SYSTEM_SERVER, lifecycleCallback);
killSandbox();
// Check that death is recorded correctly
assertThat(lifecycleCallback.getSdkSandboxDeathCount()).isEqualTo(1);
}
@Test
public void testSdkSandboxProcessDeathCallback_AfterRestartingSandbox() throws Exception {
loadSdk(SDK_NAME);
// Register for sandbox death event
FakeSdkSandboxProcessDeathCallbackBinder lifecycleCallback1 =
new FakeSdkSandboxProcessDeathCallbackBinder();
mService.addSdkSandboxProcessDeathCallback(
TEST_PACKAGE, TIME_APP_CALLED_SYSTEM_SERVER, lifecycleCallback1);
killSandbox();
assertThat(lifecycleCallback1.getSdkSandboxDeathCount()).isEqualTo(1);
restartAndSetSandboxService();
// Register for sandbox death event again and verify that death is detected.
FakeSdkSandboxProcessDeathCallbackBinder lifecycleCallback2 =
new FakeSdkSandboxProcessDeathCallbackBinder();
mService.addSdkSandboxProcessDeathCallback(
TEST_PACKAGE, TIME_APP_CALLED_SYSTEM_SERVER, lifecycleCallback2);
assertThat(lifecycleCallback2.getSdkSandboxDeathCount()).isEqualTo(0);
killSandbox();
assertThat(lifecycleCallback2.getSdkSandboxDeathCount()).isEqualTo(1);
}
@Test
public void testMultipleAddSdkSandboxProcessDeathCallbacks() throws Exception {
// Load SDK and start the sandbox
loadSdk(SDK_NAME);
// Register for sandbox death event
FakeSdkSandboxProcessDeathCallbackBinder lifecycleCallback1 =
new FakeSdkSandboxProcessDeathCallbackBinder();
mService.addSdkSandboxProcessDeathCallback(
TEST_PACKAGE, TIME_APP_CALLED_SYSTEM_SERVER, lifecycleCallback1);
// Register for sandbox death event again
FakeSdkSandboxProcessDeathCallbackBinder lifecycleCallback2 =
new FakeSdkSandboxProcessDeathCallbackBinder();
mService.addSdkSandboxProcessDeathCallback(
TEST_PACKAGE, TIME_APP_CALLED_SYSTEM_SERVER, lifecycleCallback2);
killSandbox();
// Check that death is recorded correctly
assertThat(lifecycleCallback1.getSdkSandboxDeathCount()).isEqualTo(1);
assertThat(lifecycleCallback2.getSdkSandboxDeathCount()).isEqualTo(1);
}
@Test
public void testRemoveSdkSandboxProcessDeathCallback() throws Exception {
// Load SDK and start the sandbox
loadSdk(SDK_NAME);
// Register for sandbox death event
FakeSdkSandboxProcessDeathCallbackBinder lifecycleCallback1 =
new FakeSdkSandboxProcessDeathCallbackBinder();
mService.addSdkSandboxProcessDeathCallback(
TEST_PACKAGE, TIME_APP_CALLED_SYSTEM_SERVER, lifecycleCallback1);
// Register for sandbox death event again
FakeSdkSandboxProcessDeathCallbackBinder lifecycleCallback2 =
new FakeSdkSandboxProcessDeathCallbackBinder();
mService.addSdkSandboxProcessDeathCallback(
TEST_PACKAGE, TIME_APP_CALLED_SYSTEM_SERVER, lifecycleCallback2);
// Unregister one of the lifecycle callbacks
mService.removeSdkSandboxProcessDeathCallback(
TEST_PACKAGE, TIME_APP_CALLED_SYSTEM_SERVER, lifecycleCallback1);
killSandbox();
// Check that death is recorded correctly
assertThat(lifecycleCallback1.getSdkSandboxDeathCount()).isEqualTo(0);
assertThat(lifecycleCallback2.getSdkSandboxDeathCount()).isEqualTo(1);
}
@Test
public void testSdkSandboxServiceUnbindingWhenAppDied() throws Exception {
disableKillUid();
disableNetworkPermissionChecks();
disableForegroundCheck();
ILoadSdkCallback.Stub callback = Mockito.spy(ILoadSdkCallback.Stub.class);
int callingUid = Binder.getCallingUid();
final CallingInfo callingInfo = new CallingInfo(callingUid, TEST_PACKAGE);
assertThat(sProvider.getSdkSandboxServiceForApp(callingInfo)).isNull();
mService.loadSdk(
TEST_PACKAGE,
null,
SDK_NAME,
TIME_APP_CALLED_SYSTEM_SERVER,
new Bundle(),
callback);
ArgumentCaptor<IBinder.DeathRecipient> deathRecipient = ArgumentCaptor
.forClass(IBinder.DeathRecipient.class);
Mockito.verify(callback.asBinder(), Mockito.times(1))
.linkToDeath(deathRecipient.capture(), Mockito.eq(0));
assertThat(sProvider.getSdkSandboxServiceForApp(callingInfo)).isNotNull();
deathRecipient.getValue().binderDied();
assertThat(sProvider.getSdkSandboxServiceForApp(callingInfo)).isNull();
}
/** Tests that only allowed intents may be sent from the sdk sandbox. */
@Test
public void testEnforceAllowedToSendBroadcast() {
Intent disallowedIntent = new Intent(Intent.ACTION_SCREEN_ON);
assertThrows(
SecurityException.class,
() -> sSdkSandboxManagerLocal.enforceAllowedToSendBroadcast(disallowedIntent));
}
/** Tests that no broadcast can be sent from the sdk sandbox. */
@Test
public void testCanSendBroadcast() {
assertThat(sSdkSandboxManagerLocal.canSendBroadcast(new Intent())).isFalse();
}
/** Tests that only allowed activities may be started from the sdk sandbox. */
@Test
public void testEnforceAllowedToStartActivity() {
Intent allowedIntent = new Intent(Intent.ACTION_VIEW);
sSdkSandboxManagerLocal.enforceAllowedToStartActivity(allowedIntent);
Intent disallowedIntent = new Intent(Intent.ACTION_SCREEN_OFF);
assertThrows(
SecurityException.class,
() -> sSdkSandboxManagerLocal.enforceAllowedToStartActivity(disallowedIntent));
}
@Test
public void testEnforceAllowedToHostSandboxedActivityFailIfCalledFromSandboxUid()
throws RemoteException {
loadSdk(SDK_NAME);
SecurityException exception =
assertThrows(
SecurityException.class,
() -> {
sSdkSandboxManagerLocal.enforceAllowedToHostSandboxedActivity(
new Intent(),
Process.toSdkSandboxUid(Process.myUid()),
TEST_PACKAGE);
});
assertEquals(
"Sandbox process is not allowed to start sandbox activities.",
exception.getMessage());
}
@Test
public void testEnforceAllowedToHostSandboxedActivityFailForNullIntents() {
SecurityException exception =
assertThrows(
SecurityException.class,
() -> {
sSdkSandboxManagerLocal.enforceAllowedToHostSandboxedActivity(
null, Process.myUid(), TEST_PACKAGE);
});
assertEquals("Intent to start sandbox activity is null.", exception.getMessage());
}
@Test
public void testEnforceAllowedToHostSandboxedActivityFailForNullActions() {
SecurityException exception =
assertThrows(
SecurityException.class,
() -> {
sSdkSandboxManagerLocal.enforceAllowedToHostSandboxedActivity(
new Intent(), Process.myUid(), TEST_PACKAGE);
});
assertEquals(
"Sandbox activity intent must have an action ("
+ ACTION_START_SANDBOXED_ACTIVITY
+ ").",
exception.getMessage());
}
@Test
public void testEnforceAllowedToHostSandboxedActivityFailForWrongAction() {
SecurityException exception =
assertThrows(
SecurityException.class,
() -> {
sSdkSandboxManagerLocal.enforceAllowedToHostSandboxedActivity(
new Intent().setAction(Intent.ACTION_VIEW),
Process.myUid(),
TEST_PACKAGE);
});
assertEquals(
"Sandbox activity intent must have an action ("
+ ACTION_START_SANDBOXED_ACTIVITY
+ ").",
exception.getMessage());
}
@Test
public void testEnforceAllowedToHostSandboxedActivityFailForNullPackage() {
Intent intent = new Intent().setAction(ACTION_START_SANDBOXED_ACTIVITY);
SecurityException exception =
assertThrows(
SecurityException.class,
() -> {
sSdkSandboxManagerLocal.enforceAllowedToHostSandboxedActivity(
intent, Process.myUid(), TEST_PACKAGE);
});
assertEquals(
"Sandbox activity intent's package must be set to the sandbox package",
exception.getMessage());
}
@Test
public void testEnforceAllowedToHostSandboxedActivityFailForIntentsTargetingOtherPackages() {
Intent intent =
new Intent()
.setAction(ACTION_START_SANDBOXED_ACTIVITY)
.setPackage("com.random.package");
SecurityException exception =
assertThrows(
SecurityException.class,
() -> {
sSdkSandboxManagerLocal.enforceAllowedToHostSandboxedActivity(
intent, Process.myUid(), TEST_PACKAGE);
});
assertEquals(
"Sandbox activity intent's package must be set to the sandbox package",
exception.getMessage());
}
@Test
public void testEnforceAllowedToHostSandboxedActivityFailForIntentsWithWrongComponent()
throws Exception {
loadSdk(SDK_NAME);
Intent intent =
new Intent()
.setAction(ACTION_START_SANDBOXED_ACTIVITY)
.setPackage(getSandboxPackageName())
.setComponent(new ComponentName("random", ""));
SecurityException exception =
assertThrows(
SecurityException.class,
() -> {
sSdkSandboxManagerLocal.enforceAllowedToHostSandboxedActivity(
intent, Process.myUid(), TEST_PACKAGE);
});
assertEquals(
"Sandbox activity intent's component must refer to the sandbox package",
exception.getMessage());
}
@Test
public void testEnforceAllowedToHostSandboxedActivityFailIfNoSandboxProcees() {
Intent intent = new Intent().setAction(ACTION_START_SANDBOXED_ACTIVITY);
intent.setPackage(getSandboxPackageName());
SecurityException exception =
assertThrows(
SecurityException.class,
() -> {
sSdkSandboxManagerLocal.enforceAllowedToHostSandboxedActivity(
intent, Process.myUid(), TEST_PACKAGE);
});
assertEquals(
"There is no sandbox process running for the caller uid: " + Process.myUid() + ".",
exception.getMessage());
}
@Test
public void testEnforceAllowedToHostSandboxedActivityFailIfIntentHasNoExtras()
throws RemoteException {
loadSdk(SDK_NAME);
Intent intent = new Intent().setAction(ACTION_START_SANDBOXED_ACTIVITY);
intent.setPackage(getSandboxPackageName());
IllegalArgumentException exception =
assertThrows(
IllegalArgumentException.class,
() -> {
sSdkSandboxManagerLocal.enforceAllowedToHostSandboxedActivity(
intent, Process.myUid(), TEST_PACKAGE);
});
assertEquals(
"Intent should contain an extra params with key = "
+ mService.getSandboxedActivityHandlerKey()
+ " and value is an IBinder that identifies a registered "
+ "SandboxedActivityHandler.",
exception.getMessage());
}
@Test
public void testEnforceAllowedToHostSandboxedActivityFailIfIntentHasNoHandlerExtra()
throws RemoteException {
loadSdk(SDK_NAME);
Intent intent = new Intent().setAction(ACTION_START_SANDBOXED_ACTIVITY);
intent.setPackage(getSandboxPackageName());
intent.putExtras(new Bundle());
IllegalArgumentException exception =
assertThrows(
IllegalArgumentException.class,
() -> {
sSdkSandboxManagerLocal.enforceAllowedToHostSandboxedActivity(
intent, Process.myUid(), TEST_PACKAGE);
});
assertEquals(
"Intent should contain an extra params with key = "
+ mService.getSandboxedActivityHandlerKey()
+ " and value is an IBinder that identifies a registered "
+ "SandboxedActivityHandler.",
exception.getMessage());
}
@Test
public void testEnforceAllowedToHostSandboxedActivityFailIfIntentHasWrongTypeOfHandlerExtra()
throws RemoteException {
loadSdk(SDK_NAME);
Intent intent = new Intent().setAction(ACTION_START_SANDBOXED_ACTIVITY);
intent.setPackage(getSandboxPackageName());
Bundle params = new Bundle();
params.putString(mService.getSandboxedActivityHandlerKey(), "");
intent.putExtras(params);
IllegalArgumentException exception =
assertThrows(
IllegalArgumentException.class,
() -> {
sSdkSandboxManagerLocal.enforceAllowedToHostSandboxedActivity(
intent, Process.myUid(), TEST_PACKAGE);
});
assertEquals(
"Intent should contain an extra params with key = "
+ mService.getSandboxedActivityHandlerKey()
+ " and value is an IBinder that identifies a registered "
+ "SandboxedActivityHandler.",
exception.getMessage());
}
@Test
public void testEnforceAllowedToHostSandboxedActivitySuccessWithoutComponent()
throws Exception {
loadSdk(SDK_NAME);
Intent intent = new Intent().setAction(ACTION_START_SANDBOXED_ACTIVITY);
intent.setPackage(getSandboxPackageName());
Bundle params = new Bundle();
params.putBinder(mService.getSandboxedActivityHandlerKey(), new Binder());
intent.putExtras(params);
sSdkSandboxManagerLocal.enforceAllowedToHostSandboxedActivity(
intent, Process.myUid(), TEST_PACKAGE);
}
@Test
public void testEnforceAllowedToHostSandboxedActivitySuccessWithComponentReferToSandboxPackage()
throws Exception {
loadSdk(SDK_NAME);
Intent intent = new Intent().setAction(ACTION_START_SANDBOXED_ACTIVITY);
intent.setPackage(getSandboxPackageName());
intent.setComponent(new ComponentName(getSandboxPackageName(), ""));
Bundle params = new Bundle();
params.putBinder(mService.getSandboxedActivityHandlerKey(), new Binder());
intent.putExtras(params);
sSdkSandboxManagerLocal.enforceAllowedToHostSandboxedActivity(
intent, Process.myUid(), TEST_PACKAGE);
}
@Test
public void testGetSdkSandboxProcessNameForInstrumentation() throws Exception {
final PackageManager pm =
InstrumentationRegistry.getInstrumentation().getContext().getPackageManager();
final ApplicationInfo info = pm.getApplicationInfo(TEST_PACKAGE, 0);
final String processName =
sSdkSandboxManagerLocal.getSdkSandboxProcessNameForInstrumentation(info);
assertThat(processName).isEqualTo(TEST_PACKAGE + "_sdk_sandbox_instr");
}
/** Tests expected behavior when broadcast receiver restrictions are not available. */
@Test
public void testCanRegisterBroadcastReceiver_deviceConfigUnset() {
ExtendedMockito.when(Process.isSdkSandboxUid(Mockito.anyInt())).thenReturn(true);
sSdkSandboxSettingsListener.onPropertiesChanged(
new DeviceConfig.Properties(
DeviceConfig.NAMESPACE_ADSERVICES,
Map.of(PROPERTY_ENFORCE_BROADCAST_RECEIVER_RESTRICTIONS, "")));
assertThat(
sSdkSandboxManagerLocal.canRegisterBroadcastReceiver(
new IntentFilter(Intent.ACTION_SEND),
/*flags= */ 0,
/*onlyProtectedBroadcasts= */ false))
.isTrue();
}
/** Tests expected behavior when broadcast receiver restrictions are not applied. */
@Test
public void testCanRegisterBroadcastReceiver_restrictionsNotApplied() {
ExtendedMockito.when(Process.isSdkSandboxUid(Mockito.anyInt())).thenReturn(true);
sSdkSandboxSettingsListener.onPropertiesChanged(
new DeviceConfig.Properties(
DeviceConfig.NAMESPACE_ADSERVICES,
Map.of(PROPERTY_ENFORCE_BROADCAST_RECEIVER_RESTRICTIONS, "false")));
assertThat(
sSdkSandboxManagerLocal.canRegisterBroadcastReceiver(
new IntentFilter(Intent.ACTION_SEND),
/*flags= */ 0,
/*onlyProtectedBroadcasts= */ false))
.isTrue();
}
/** Tests expected behavior when broadcast receiver restrictions are applied. */
@Test
public void testCanRegisterBroadcastReceiver_restrictionsApplied() {
ExtendedMockito.when(Process.isSdkSandboxUid(Mockito.anyInt())).thenReturn(true);
sSdkSandboxSettingsListener.onPropertiesChanged(
new DeviceConfig.Properties(
DeviceConfig.NAMESPACE_ADSERVICES,
Map.of(PROPERTY_ENFORCE_BROADCAST_RECEIVER_RESTRICTIONS, "true")));
assertThat(
sSdkSandboxManagerLocal.canRegisterBroadcastReceiver(
new IntentFilter(Intent.ACTION_SEND),
/*flags= */ 0,
/*onlyProtectedBroadcasts= */ false))
.isFalse();
}
/** Tests expected behavior when callingUid is not a sandbox UID. */
@Test
public void testCanRegisterBroadcastReceiver_notSandboxProcess() {
assertThat(
sSdkSandboxManagerLocal.canRegisterBroadcastReceiver(
new IntentFilter(Intent.ACTION_SEND),
/*flags= */ 0,
/*onlyProtectedBroadcasts= */ false))
.isTrue();
}
/** Tests expected behavior when IntentFilter is blank. */
@Test
public void testCanRegisterBroadcastReceiver_blankIntentFilter() {
ExtendedMockito.when(Process.isSdkSandboxUid(Mockito.anyInt())).thenReturn(true);
assertThat(
sSdkSandboxManagerLocal.canRegisterBroadcastReceiver(
new IntentFilter(),
/*flags= */ 0,
/*onlyProtectedBroadcasts= */ false))
.isFalse();
}
/**
* Tests expected behavior when broadcast receiver is registering to an unexported broadcast.
*/
@Test
public void testCanRegisterBroadcastReceiver_exportedBroadcast() {
ExtendedMockito.when(Process.isSdkSandboxUid(Mockito.anyInt())).thenReturn(true);
sSdkSandboxSettingsListener.onPropertiesChanged(
new DeviceConfig.Properties(
DeviceConfig.NAMESPACE_ADSERVICES,
Map.of(PROPERTY_ENFORCE_BROADCAST_RECEIVER_RESTRICTIONS, "true")));
assertThat(
sSdkSandboxManagerLocal.canRegisterBroadcastReceiver(
new IntentFilter(Intent.ACTION_SEND),
/*flags= */ Context.RECEIVER_NOT_EXPORTED,
/*onlyProtectedBroadcasts= */ false))
.isTrue();
}
@Test
public void testNotifyInstrumentationStarted_killsSandboxProcess() throws Exception {
disableKillUid();
// First load SDK.
loadSdk(SDK_NAME);
final CallingInfo callingInfo = new CallingInfo(Process.myUid(), TEST_PACKAGE);
// Check that sdk sandbox for TEST_PACKAGE is bound
assertThat(sProvider.getSdkSandboxServiceForApp(callingInfo)).isNotNull();
sSdkSandboxManagerLocal.notifyInstrumentationStarted(TEST_PACKAGE, Process.myUid());
// Verify that sdk sandbox was killed
Mockito.verify(mAmSpy)
.killUid(Mockito.eq(Process.toSdkSandboxUid(Process.myUid())), Mockito.anyString());
assertThat(sProvider.getSdkSandboxServiceForApp(callingInfo)).isNull();
}
@Test
public void testNotifyInstrumentationStarted_doesNotAllowLoadSdk() throws Exception {
disableKillUid();
// First load SDK.
loadSdk(SDK_NAME);
final CallingInfo callingInfo = new CallingInfo(Process.myUid(), TEST_PACKAGE);
// Check that sdk sandbox for TEST_PACKAGE is bound
assertThat(sProvider.getSdkSandboxServiceForApp(callingInfo)).isNotNull();
sSdkSandboxManagerLocal.notifyInstrumentationStarted(TEST_PACKAGE, Process.myUid());
assertThat(sProvider.getSdkSandboxServiceForApp(callingInfo)).isNull();
// Try load again, it should throw SecurityException
FakeLoadSdkCallbackBinder callback2 = new FakeLoadSdkCallbackBinder();
mService.loadSdk(
TEST_PACKAGE,
callback2.asBinder(),
SDK_NAME,
TIME_APP_CALLED_SYSTEM_SERVER,
new Bundle(),
callback2);
LoadSdkException thrown = callback2.getLoadSdkException();
assertEquals(LOAD_SDK_INTERNAL_ERROR, thrown.getLoadSdkErrorCode());
assertThat(thrown)
.hasMessageThat()
.contains("Currently running instrumentation of this sdk sandbox process");
}
@Test
public void testNotifyInstrumentationFinished_canLoadSdk() throws Exception {
disableKillUid();
disableNetworkPermissionChecks();
disableForegroundCheck();
sSdkSandboxManagerLocal.notifyInstrumentationStarted(TEST_PACKAGE, Process.myUid());
final CallingInfo callingInfo = new CallingInfo(Process.myUid(), TEST_PACKAGE);
assertThat(sProvider.getSdkSandboxServiceForApp(callingInfo)).isNull();
FakeLoadSdkCallbackBinder callback = new FakeLoadSdkCallbackBinder();
mService.loadSdk(
TEST_PACKAGE,
null,
SDK_NAME,
TIME_APP_CALLED_SYSTEM_SERVER,
new Bundle(),
callback);
LoadSdkException thrown = callback.getLoadSdkException();
assertEquals(LOAD_SDK_INTERNAL_ERROR, thrown.getLoadSdkErrorCode());
assertThat(thrown)
.hasMessageThat()
.contains("Currently running instrumentation of this sdk sandbox process");
sSdkSandboxManagerLocal.notifyInstrumentationFinished(TEST_PACKAGE, Process.myUid());
FakeLoadSdkCallbackBinder callback2 = new FakeLoadSdkCallbackBinder();
// Now loading should work
mService.loadSdk(
TEST_PACKAGE,
callback2.asBinder(),
SDK_NAME,
TIME_APP_CALLED_SYSTEM_SERVER,
new Bundle(),
callback2);
mSdkSandboxService.sendLoadCodeSuccessful();
callback2.assertLoadSdkIsSuccessful();
assertThat(sProvider.getSdkSandboxServiceForApp(callingInfo)).isNotNull();
}
@Test
public void testGetSandboxedSdks_afterLoadSdkSuccess() throws Exception {
loadSdk(SDK_NAME);
assertThat(mService.getSandboxedSdks(TEST_PACKAGE, TIME_APP_CALLED_SYSTEM_SERVER))
.hasSize(1);
assertThat(
mService.getSandboxedSdks(TEST_PACKAGE, TIME_APP_CALLED_SYSTEM_SERVER)
.get(0)
.getSharedLibraryInfo()
.getName())
.isEqualTo(SDK_NAME);
}
@Test
public void testGetSandboxedSdks_errorLoadingSdk() throws Exception {
disableNetworkPermissionChecks();
disableForegroundCheck();
FakeLoadSdkCallbackBinder callback = new FakeLoadSdkCallbackBinder();
mService.loadSdk(
TEST_PACKAGE,
null,
SDK_NAME,
TIME_APP_CALLED_SYSTEM_SERVER,
new Bundle(),
callback);
mSdkSandboxService.sendLoadCodeError();
// Verify sdkInfo is missing when loading failed
callback.assertLoadSdkIsUnsuccessful();
assertThat(callback.getLoadSdkErrorCode()).isEqualTo(LOAD_SDK_INTERNAL_ERROR);
assertThat(mService.getSandboxedSdks(TEST_PACKAGE, TIME_APP_CALLED_SYSTEM_SERVER))
.isEmpty();
}
@Test
public void testEnforceAllowedToStartOrBindService_disallowNonExistentPackage() {
Intent intent = new Intent();
intent.setComponent(new ComponentName("nonexistent.package", "test"));
assertThrows(
SecurityException.class,
() -> sSdkSandboxManagerLocal.enforceAllowedToStartOrBindService(intent));
}
@Test
public void testEnforceAllowedToStartOrBindService_allowedPackages() {
Intent intent = new Intent();
intent.setComponent(new ComponentName(mService.getAdServicesPackageName(), "test"));
sSdkSandboxManagerLocal.enforceAllowedToStartOrBindService(intent);
}
@Test
public void testAdServicesPackageIsResolved() {
assertThat(mService.getAdServicesPackageName()).contains("adservices");
}
@Test
public void testUnloadSdkThatIsNotLoaded() throws Exception {
// Load SDK to bring up a sandbox
loadSdk(SDK_NAME);
// Trying to unload an SDK that is not loaded should do nothing - it's a no-op.
mService.unloadSdk(TEST_PACKAGE, SDK_PROVIDER_PACKAGE, TIME_APP_CALLED_SYSTEM_SERVER);
}
@Test
public void testUnloadSdkThatIsLoaded() throws Exception {
disableKillUid();
loadSdk(SDK_NAME);
loadSdk(SDK_PROVIDER_RESOURCES_SDK_NAME);
final CallingInfo callingInfo = new CallingInfo(Process.myUid(), TEST_PACKAGE);
mService.unloadSdk(TEST_PACKAGE, SDK_NAME, TIME_APP_CALLED_SYSTEM_SERVER);
// One SDK should still be loaded, therefore the sandbox should still be alive.
assertThat(sProvider.getSdkSandboxServiceForApp(callingInfo)).isNotNull();
mService.unloadSdk(
TEST_PACKAGE, SDK_PROVIDER_RESOURCES_SDK_NAME, TIME_APP_CALLED_SYSTEM_SERVER);
// No more SDKs should be loaded at this point. Verify that the sandbox has been killed.
if (!SdkLevel.isAtLeastU()) {
// For T, killUid() is used to kill the sandbox.
Mockito.verify(mAmSpy)
.killUid(
Mockito.eq(Process.toSdkSandboxUid(Process.myUid())),
Mockito.anyString());
}
assertThat(sProvider.getSdkSandboxServiceForApp(callingInfo)).isNull();
}
@Test
public void testUnloadSdkThatIsBeingLoaded() throws Exception {
// Ask to load SDK, but don't finish loading it
disableKillUid();
disableNetworkPermissionChecks();
disableForegroundCheck();
FakeLoadSdkCallbackBinder callback = new FakeLoadSdkCallbackBinder();
mService.loadSdk(
TEST_PACKAGE,
null,
SDK_NAME,
TIME_APP_CALLED_SYSTEM_SERVER,
new Bundle(),
callback);
// Trying to unload an SDK that is being loaded should fail
assertThrows(
IllegalArgumentException.class,
() -> mService.unloadSdk(TEST_PACKAGE, SDK_NAME, TIME_APP_CALLED_SYSTEM_SERVER));
// After loading the SDK, unloading should not fail
mSdkSandboxService.sendLoadCodeSuccessful();
callback.assertLoadSdkIsSuccessful();
mService.unloadSdk(TEST_PACKAGE, SDK_NAME, TIME_APP_CALLED_SYSTEM_SERVER);
}
@Test
public void testUnloadSdkAfterKillingSandboxDoesNotThrowException() throws Exception {
loadSdk(SDK_NAME);
killSandbox();
// Unloading SDK should be a no-op
mService.unloadSdk(TEST_PACKAGE, SDK_NAME, TIME_APP_CALLED_SYSTEM_SERVER);
}
@Test
public void test_syncDataFromClient_verifiesCallingPackageName() {
FakeSharedPreferencesSyncCallback callback = new FakeSharedPreferencesSyncCallback();
mService.syncDataFromClient(
"does.not.exist", TIME_APP_CALLED_SYSTEM_SERVER, TEST_UPDATE, callback);
assertEquals(PREFERENCES_SYNC_INTERNAL_ERROR, callback.getErrorCode());
assertThat(callback.getErrorMsg()).contains("does.not.exist not found");
}
@Test
public void test_syncDataFromClient_sandboxServiceIsNotBound() {
// Sync data from client
final FakeSharedPreferencesSyncCallback callback = new FakeSharedPreferencesSyncCallback();
mService.syncDataFromClient(
TEST_PACKAGE,
/*timeAppCalledSystemServer=*/ System.currentTimeMillis(),
TEST_UPDATE,
callback);
// Verify when sandbox is not bound, manager service does not try to sync
assertThat(mSdkSandboxService.getLastSyncUpdate()).isNull();
// Verify on error was called
assertThat(callback.hasError()).isTrue();
assertThat(callback.getErrorCode())
.isEqualTo(ISharedPreferencesSyncCallback.SANDBOX_NOT_AVAILABLE);
assertThat(callback.getErrorMsg()).contains("Sandbox not available");
}
@Test
public void test_syncDataFromClient_sandboxServiceIsNotBound_sandboxStartedLater()
throws Exception {
// Sync data from client
final FakeSharedPreferencesSyncCallback callback = new FakeSharedPreferencesSyncCallback();
mService.syncDataFromClient(
TEST_PACKAGE, TIME_APP_CALLED_SYSTEM_SERVER, TEST_UPDATE, callback);
// Verify on error was called
assertThat(callback.hasError()).isTrue();
callback.resetLatch();
// Now loadSdk so that sandbox is created
loadSdk(SDK_NAME);
// Verify that onSandboxStart was called
assertThat(callback.hasSandboxStarted()).isTrue();
}
@Test
public void test_syncDataFromClient_sandboxServiceIsAlreadyBound_forwardsToSandbox()
throws Exception {
// Ensure a sandbox service is already bound for the client
final CallingInfo callingInfo = new CallingInfo(Process.myUid(), TEST_PACKAGE);
sProvider.bindService(callingInfo, Mockito.mock(ServiceConnection.class));
// Sync data from client
final Bundle data = new Bundle();
final FakeSharedPreferencesSyncCallback callback = new FakeSharedPreferencesSyncCallback();
mService.syncDataFromClient(
TEST_PACKAGE,
/*timeAppCalledSystemServer=*/ System.currentTimeMillis(),
TEST_UPDATE,
callback);
// Verify that manager service calls sandbox to sync data
assertThat(mSdkSandboxService.getLastSyncUpdate()).isSameInstanceAs(TEST_UPDATE);
}
@Test
public void testStopSdkSandbox() throws Exception {
disableKillUid();
loadSdk(SDK_NAME);
Mockito.doNothing()
.when(mSpyContext)
.enforceCallingPermission(
Mockito.eq("com.android.app.sdksandbox.permission.STOP_SDK_SANDBOX"),
Mockito.anyString());
mService.stopSdkSandbox(TEST_PACKAGE);
int callingUid = Binder.getCallingUid();
final CallingInfo callingInfo = new CallingInfo(callingUid, TEST_PACKAGE);
assertThat(sProvider.getSdkSandboxServiceForApp(callingInfo)).isEqualTo(null);
}
@Test(expected = SecurityException.class)
public void testStopSdkSandbox_WithoutPermission() {
mService.stopSdkSandbox(TEST_PACKAGE);
}
@Test
public void testDump_preU() throws Exception {
requiresAtLeastU(false);
mockGrantedPermission(DUMP);
mService.registerAdServicesManagerService(mAdServicesManager);
String dump;
try (StringWriter stringWriter = new StringWriter()) {
// Mock call to mAdServicesManager.dump();
FileDescriptor fd = new FileDescriptor();
PrintWriter writer = new PrintWriter(stringWriter);
String[] args = new String[0];
Mockito.doAnswer(
(inv) -> {
writer.println("FakeAdServiceDump");
return null;
})
.when(mAdServicesManager)
.dump(fd, args);
mService.dump(fd, writer, args);
dump = stringWriter.toString();
}
assertThat(dump).contains("FakeDump");
assertThat(dump).contains("FakeAdServiceDump");
}
@Test
public void testDump_atLeastU() throws Exception {
requiresAtLeastU(true);
mockGrantedPermission(DUMP);
mService.registerAdServicesManagerService(mAdServicesManager);
String dump;
try (StringWriter stringWriter = new StringWriter()) {
mService.dump(new FileDescriptor(), new PrintWriter(stringWriter), new String[0]);
dump = stringWriter.toString();
}
assertThat(dump).contains("FakeDump");
Mockito.verify(mAdServicesManager, Mockito.never())
.dump(ArgumentMatchers.any(), ArgumentMatchers.any());
}
@Test
public void testDump_adServices_preU() throws Exception {
requiresAtLeastU(false);
mockGrantedPermission(DUMP);
mService.registerAdServicesManagerService(mAdServicesManager);
String dump;
try (StringWriter stringWriter = new StringWriter()) {
// Mock call to mAdServicesManager.dump();
FileDescriptor fd = new FileDescriptor();
PrintWriter writer = new PrintWriter(stringWriter);
String[] args = new String[] {"--AdServices"};
Mockito.doAnswer(
(inv) -> {
writer.println("FakeAdServiceDump");
return null;
})
.when(mAdServicesManager)
.dump(fd, args);
mService.dump(fd, writer, args);
dump = stringWriter.toString();
}
assertThat(dump).isEqualTo("AdServices:\n\nFakeAdServiceDump\n\n");
}
@Test
public void testDump_adServices_atLeastU() throws Exception {
requiresAtLeastU(true);
mockGrantedPermission(DUMP);
mService.registerAdServicesManagerService(mAdServicesManager);
String dump;
try (StringWriter stringWriter = new StringWriter()) {
mService.dump(
new FileDescriptor(),
new PrintWriter(stringWriter),
new String[] {"--AdServices"});
dump = stringWriter.toString();
}
assertThat(dump)
.isEqualTo(SdkSandboxManagerService.POST_UDC_DUMP_AD_SERVICES_MESSAGE + "\n");
Mockito.verify(mAdServicesManager, Mockito.never())
.dump(ArgumentMatchers.any(), ArgumentMatchers.any());
}
@Test(expected = SecurityException.class)
public void testDump_WithoutPermission() {
mService.dump(new FileDescriptor(), new PrintWriter(new StringWriter()), new String[0]);
}
@Test
public void testHandleShellCommandExecutesCommand() {
final FileDescriptor in = FileDescriptor.in;
final FileDescriptor out = FileDescriptor.out;
final FileDescriptor err = FileDescriptor.err;
final SdkSandboxShellCommand command = Mockito.mock(SdkSandboxShellCommand.class);
Mockito.when(mInjector.createShellCommand(mService, mSpyContext)).thenReturn(command);
final String[] args = new String[] {"start"};
mService.handleShellCommand(
new ParcelFileDescriptor(in),
new ParcelFileDescriptor(out),
new ParcelFileDescriptor(err),
args);
Mockito.verify(mInjector).createShellCommand(mService, mSpyContext);
Mockito.verify(command).exec(mService, in, out, err, args);
}
@Test
public void testLatencyMetrics_IpcFromAppToSystemServer_RegisterAppOwnedSdkSandboxInterface()
throws Exception {
mService.registerAppOwnedSdkSandboxInterface(
TEST_PACKAGE,
new AppOwnedSdkSandboxInterface(
APP_OWNED_SDK_SANDBOX_INTERFACE_NAME,
/*version=*/ 0,
/*interfaceIBinder=*/ new Binder()),
TIME_APP_CALLED_SYSTEM_SERVER);
ExtendedMockito.verify(
() ->
SdkSandboxStatsLog.write(
SdkSandboxStatsLog.SANDBOX_API_CALLED,
SdkSandboxStatsLog.SANDBOX_API_CALLED__METHOD__LOAD_SDK,
(int)
(TIME_SYSTEM_SERVER_RECEIVED_CALL_FROM_APP
- TIME_APP_CALLED_SYSTEM_SERVER),
/*success=*/ true,
SdkSandboxStatsLog.SANDBOX_API_CALLED__STAGE__APP_TO_SYSTEM_SERVER,
mClientAppUid));
}
@Test
public void testLatencyMetrics_IpcFromAppToSystemServer_UnregisterAppOwnedSdkSandboxInterface()
throws Exception {
mService.unregisterAppOwnedSdkSandboxInterface(
TEST_PACKAGE, APP_OWNED_SDK_SANDBOX_INTERFACE_NAME, TIME_APP_CALLED_SYSTEM_SERVER);
ExtendedMockito.verify(
() ->
SdkSandboxStatsLog.write(
SdkSandboxStatsLog.SANDBOX_API_CALLED,
SdkSandboxStatsLog
.SANDBOX_API_CALLED__METHOD__UNREGISTER_APP_OWNED_SDK_SANDBOX_INTERFACE,
(int)
(TIME_SYSTEM_SERVER_RECEIVED_CALL_FROM_APP
- TIME_APP_CALLED_SYSTEM_SERVER),
/*success=*/ true,
SdkSandboxStatsLog.SANDBOX_API_CALLED__STAGE__APP_TO_SYSTEM_SERVER,
mClientAppUid));
}
@Test
public void testLatencyMetrics_IpcFromAppToSystemServer_GetAppOwnedSdkSandboxInterfaces()
throws Exception {
mService.getAppOwnedSdkSandboxInterfaces(TEST_PACKAGE, TIME_APP_CALLED_SYSTEM_SERVER);
ExtendedMockito.verify(
() ->
SdkSandboxStatsLog.write(
SdkSandboxStatsLog.SANDBOX_API_CALLED,
SdkSandboxStatsLog
.SANDBOX_API_CALLED__METHOD__GET_APP_OWNED_SDK_SANDBOX_INTERFACES,
(int)
(TIME_SYSTEM_SERVER_RECEIVED_CALL_FROM_APP
- TIME_APP_CALLED_SYSTEM_SERVER),
/*success=*/ true,
SdkSandboxStatsLog
.SANDBOX_API_CALLED__STAGE__APP_TO_SYSTEM_SERVER,
mClientAppUid));
}
@Test
public void
testLatencyMetrics_SystemServerAppToSandbox_RegisterAppOwnedSdkSandboxInterface_NoFailure()
throws Exception {
Mockito.when(mInjector.getCurrentTime())
.thenReturn(
TIME_SYSTEM_SERVER_RECEIVED_CALL_FROM_APP,
TIME_SYSTEM_SERVER_CALLS_SANDBOX);
mService.registerAppOwnedSdkSandboxInterface(
TEST_PACKAGE,
new AppOwnedSdkSandboxInterface(
APP_OWNED_SDK_SANDBOX_INTERFACE_NAME,
/*version=*/ 0,
/*interfaceIBinder=*/ new Binder()),
TIME_APP_CALLED_SYSTEM_SERVER);
ExtendedMockito.verify(
() ->
SdkSandboxStatsLog.write(
SdkSandboxStatsLog.SANDBOX_API_CALLED,
SdkSandboxStatsLog
.SANDBOX_API_CALLED__METHOD__REGISTER_APP_OWNED_SDK_SANDBOX_INTERFACE,
(int)
(TIME_SYSTEM_SERVER_CALLS_SANDBOX
- TIME_SYSTEM_SERVER_RECEIVED_CALL_FROM_APP),
/*success=*/ true,
SdkSandboxStatsLog
.SANDBOX_API_CALLED__STAGE__SYSTEM_SERVER_APP_TO_SANDBOX,
mClientAppUid));
}
@Test
public void
testLatencyMetrics_SystemServerAppToSandbox_RegisterAppOwnedSdkSandboxInterface_FailureOnAppDeath()
throws RemoteException {
IBinder binder = Mockito.mock(IBinder.class);
Mockito.doThrow(new RemoteException())
.when(binder)
.linkToDeath(Mockito.any(), Mockito.anyInt());
Mockito.when(mInjector.getCurrentTime())
.thenReturn(TIME_SYSTEM_SERVER_RECEIVED_CALL_FROM_APP, TIME_FAILURE_HANDLED);
mService.registerAppOwnedSdkSandboxInterface(
TEST_PACKAGE,
new AppOwnedSdkSandboxInterface(
APP_OWNED_SDK_SANDBOX_INTERFACE_NAME,
/*version=*/ 0,
/*interfaceIBinder=*/ binder),
TIME_APP_CALLED_SYSTEM_SERVER);
ExtendedMockito.verify(
() ->
SdkSandboxStatsLog.write(
SdkSandboxStatsLog.SANDBOX_API_CALLED,
SdkSandboxStatsLog
.SANDBOX_API_CALLED__METHOD__REGISTER_APP_OWNED_SDK_SANDBOX_INTERFACE,
(int)
(TIME_FAILURE_HANDLED
- TIME_SYSTEM_SERVER_RECEIVED_CALL_FROM_APP),
/*success=*/ false,
SdkSandboxStatsLog
.SANDBOX_API_CALLED__STAGE__SYSTEM_SERVER_APP_TO_SANDBOX,
mClientAppUid));
}
@Test
public void testLatencyMetrics_SystemServerAppToSandbox_UnregisterAppOwnedSdkSandboxInterface()
throws Exception {
Mockito.when(mInjector.getCurrentTime())
.thenReturn(
TIME_SYSTEM_SERVER_RECEIVED_CALL_FROM_APP,
TIME_SYSTEM_SERVER_CALLS_SANDBOX);
mService.unregisterAppOwnedSdkSandboxInterface(
TEST_PACKAGE, APP_OWNED_SDK_SANDBOX_INTERFACE_NAME, TIME_APP_CALLED_SYSTEM_SERVER);
ExtendedMockito.verify(
() ->
SdkSandboxStatsLog.write(
SdkSandboxStatsLog.SANDBOX_API_CALLED,
SdkSandboxStatsLog
.SANDBOX_API_CALLED__METHOD__UNREGISTER_APP_OWNED_SDK_SANDBOX_INTERFACE,
(int)
(TIME_SYSTEM_SERVER_CALLS_SANDBOX
- TIME_SYSTEM_SERVER_RECEIVED_CALL_FROM_APP),
/*success=*/ true,
SdkSandboxStatsLog
.SANDBOX_API_CALLED__STAGE__SYSTEM_SERVER_APP_TO_SANDBOX,
mClientAppUid));
}
@Test
public void testLatencyMetrics_SystemServerAppToSandbox_GetAppOwnedSdkSandboxInterfaces()
throws Exception {
Mockito.when(mInjector.getCurrentTime())
.thenReturn(
TIME_SYSTEM_SERVER_RECEIVED_CALL_FROM_APP,
TIME_SYSTEM_SERVER_CALLS_SANDBOX);
mService.getAppOwnedSdkSandboxInterfaces(TEST_PACKAGE, TIME_APP_CALLED_SYSTEM_SERVER);
ExtendedMockito.verify(
() ->
SdkSandboxStatsLog.write(
SdkSandboxStatsLog.SANDBOX_API_CALLED,
SdkSandboxStatsLog
.SANDBOX_API_CALLED__METHOD__GET_APP_OWNED_SDK_SANDBOX_INTERFACES,
(int)
(TIME_SYSTEM_SERVER_CALLS_SANDBOX
- TIME_SYSTEM_SERVER_RECEIVED_CALL_FROM_APP),
/*success=*/ true,
SdkSandboxStatsLog
.SANDBOX_API_CALLED__STAGE__SYSTEM_SERVER_APP_TO_SANDBOX,
mClientAppUid));
}
@Test
public void testLatencyMetrics_IpcFromAppToSystemServer_LoadSdk() throws Exception {
loadSdk(SDK_NAME);
ExtendedMockito.verify(
() ->
SdkSandboxStatsLog.write(
SdkSandboxStatsLog.SANDBOX_API_CALLED,
SdkSandboxStatsLog.SANDBOX_API_CALLED__METHOD__LOAD_SDK,
(int)
(TIME_SYSTEM_SERVER_RECEIVED_CALL_FROM_APP
- TIME_APP_CALLED_SYSTEM_SERVER),
/*success=*/ true,
SdkSandboxStatsLog.SANDBOX_API_CALLED__STAGE__APP_TO_SYSTEM_SERVER,
mClientAppUid));
}
@Test
public void testLatencyMetrics_IpcFromAppToSystemServer_RequestSurfacePackage()
throws Exception {
loadSdk(SDK_NAME);
// 2. Call request package
FakeRequestSurfacePackageCallbackBinder surfacePackageCallback =
new FakeRequestSurfacePackageCallbackBinder();
mService.requestSurfacePackage(
TEST_PACKAGE,
SDK_NAME,
new Binder(),
0,
500,
500,
TIME_APP_CALLED_SYSTEM_SERVER,
new Bundle(),
surfacePackageCallback);
ExtendedMockito.verify(
() ->
SdkSandboxStatsLog.write(
SdkSandboxStatsLog.SANDBOX_API_CALLED,
SANDBOX_API_CALLED__METHOD__REQUEST_SURFACE_PACKAGE,
(int)
(TIME_SYSTEM_SERVER_RECEIVED_CALL_FROM_APP
- TIME_APP_CALLED_SYSTEM_SERVER),
/*success=*/ true,
SdkSandboxStatsLog.SANDBOX_API_CALLED__STAGE__APP_TO_SYSTEM_SERVER,
mClientAppUid));
}
@Test
public void testLatencyMetrics_IpcFromAppToSystemServer_GetSandboxedSdks() {
mService.getSandboxedSdks(TEST_PACKAGE, TIME_APP_CALLED_SYSTEM_SERVER);
ExtendedMockito.verify(
() ->
SdkSandboxStatsLog.write(
SdkSandboxStatsLog.SANDBOX_API_CALLED,
SdkSandboxStatsLog.SANDBOX_API_CALLED__METHOD__GET_SANDBOXED_SDKS,
(int)
(TIME_SYSTEM_SERVER_RECEIVED_CALL_FROM_APP
- TIME_APP_CALLED_SYSTEM_SERVER),
/*success=*/ true,
SdkSandboxStatsLog.SANDBOX_API_CALLED__STAGE__APP_TO_SYSTEM_SERVER,
mClientAppUid));
}
@Test
public void testLatencyMetrics_IpcFromAppToSystemServer_UnloadSdk() throws Exception {
disableKillUid();
loadSdk(SDK_NAME);
mService.unloadSdk(TEST_PACKAGE, SDK_NAME, TIME_APP_CALLED_SYSTEM_SERVER);
ExtendedMockito.verify(
() ->
SdkSandboxStatsLog.write(
SdkSandboxStatsLog.SANDBOX_API_CALLED,
SdkSandboxStatsLog.SANDBOX_API_CALLED__METHOD__UNLOAD_SDK,
(int)
(TIME_SYSTEM_SERVER_RECEIVED_CALL_FROM_APP
- TIME_APP_CALLED_SYSTEM_SERVER),
/*success=*/ true,
SdkSandboxStatsLog.SANDBOX_API_CALLED__STAGE__APP_TO_SYSTEM_SERVER,
mClientAppUid));
}
@Test
public void testLatencyMetrics_IpcFromAppToSystemServer_SyncDataFromClient() {
// Sync data from client
mService.syncDataFromClient(
TEST_PACKAGE,
TIME_APP_CALLED_SYSTEM_SERVER,
TEST_UPDATE,
Mockito.mock(ISharedPreferencesSyncCallback.class));
ExtendedMockito.verify(
() ->
SdkSandboxStatsLog.write(
SdkSandboxStatsLog.SANDBOX_API_CALLED,
SdkSandboxStatsLog
.SANDBOX_API_CALLED__METHOD__SYNC_DATA_FROM_CLIENT,
(int)
(TIME_SYSTEM_SERVER_RECEIVED_CALL_FROM_APP
- TIME_APP_CALLED_SYSTEM_SERVER),
/*success=*/ true,
SdkSandboxStatsLog.SANDBOX_API_CALLED__STAGE__APP_TO_SYSTEM_SERVER,
mClientAppUid));
}
// TODO(b/242149555): Update tests to use fake for getting time series.
@Test
public void testLatencyMetrics_SystemServerAppToSandbox_LoadSdk() throws Exception {
Mockito.when(mInjector.getCurrentTime())
.thenReturn(
TIME_SYSTEM_SERVER_RECEIVED_CALL_FROM_APP,
START_TIME_TO_LOAD_SANDBOX,
END_TIME_TO_LOAD_SANDBOX,
TIME_SYSTEM_SERVER_CALLS_SANDBOX);
loadSdk(SDK_NAME);
final int timeToLoadSdk = (int) (END_TIME_TO_LOAD_SANDBOX - START_TIME_TO_LOAD_SANDBOX);
ExtendedMockito.verify(
() ->
SdkSandboxStatsLog.write(
SdkSandboxStatsLog.SANDBOX_API_CALLED,
SdkSandboxStatsLog.SANDBOX_API_CALLED__METHOD__LOAD_SDK,
timeToLoadSdk,
/*success=*/ true,
SdkSandboxStatsLog.SANDBOX_API_CALLED__STAGE__LOAD_SANDBOX,
mClientAppUid));
int timeSystemServerAppToSandbox =
(int)
(TIME_SYSTEM_SERVER_CALLS_SANDBOX
- TIME_SYSTEM_SERVER_RECEIVED_CALL_FROM_APP
- timeToLoadSdk);
ExtendedMockito.verify(
() ->
SdkSandboxStatsLog.write(
SdkSandboxStatsLog.SANDBOX_API_CALLED,
SdkSandboxStatsLog.SANDBOX_API_CALLED__METHOD__LOAD_SDK,
timeSystemServerAppToSandbox,
/*success=*/ true,
SdkSandboxStatsLog
.SANDBOX_API_CALLED__STAGE__SYSTEM_SERVER_APP_TO_SANDBOX,
mClientAppUid));
}
@Test
public void testLatencyMetrics_SystemServerSandboxToApp_LoadSdk() throws Exception {
loadSdk(SDK_NAME);
ExtendedMockito.verify(
() ->
SdkSandboxStatsLog.write(
Mockito.eq(SdkSandboxStatsLog.SANDBOX_API_CALLED),
Mockito.eq(SdkSandboxStatsLog.SANDBOX_API_CALLED__METHOD__LOAD_SDK),
Mockito.anyInt(),
Mockito.eq(/*success=*/ true),
Mockito.eq(SANDBOX_API_CALLED__STAGE__SYSTEM_SERVER_TO_SANDBOX),
Mockito.eq(mClientAppUid)));
ExtendedMockito.verify(
() ->
SdkSandboxStatsLog.write(
Mockito.eq(SdkSandboxStatsLog.SANDBOX_API_CALLED),
Mockito.eq(SdkSandboxStatsLog.SANDBOX_API_CALLED__METHOD__LOAD_SDK),
Mockito.anyInt(),
Mockito.eq(/*success=*/ true),
Mockito.eq(SdkSandboxStatsLog.SANDBOX_API_CALLED__STAGE__SANDBOX),
Mockito.eq(mClientAppUid)));
ExtendedMockito.verify(
() ->
SdkSandboxStatsLog.write(
Mockito.eq(SdkSandboxStatsLog.SANDBOX_API_CALLED),
Mockito.eq(SdkSandboxStatsLog.SANDBOX_API_CALLED__METHOD__LOAD_SDK),
Mockito.anyInt(),
Mockito.eq(/*success=*/ true),
Mockito.eq(SANDBOX_API_CALLED__STAGE__SANDBOX_TO_SYSTEM_SERVER),
Mockito.eq(mClientAppUid)));
}
@Test
public void testLatencyMetrics_SystemServerSandboxToAppWithSandboxLatencyInfo_LoadSdk()
throws Exception {
disableNetworkPermissionChecks();
disableForegroundCheck();
Mockito.when(mInjector.getCurrentTime())
.thenReturn(
TIME_SYSTEM_SERVER_RECEIVED_CALL_FROM_APP,
START_TIME_TO_LOAD_SANDBOX,
END_TIME_TO_LOAD_SANDBOX,
TIME_SYSTEM_SERVER_CALLS_SANDBOX,
TIME_SYSTEM_SERVER_RECEIVED_CALL_FROM_SANDBOX);
FakeLoadSdkCallbackBinder callback = new FakeLoadSdkCallbackBinder();
mService.loadSdk(
TEST_PACKAGE,
null,
SDK_NAME,
TIME_APP_CALLED_SYSTEM_SERVER,
new Bundle(),
callback);
mSdkSandboxService.sendLoadCodeSuccessfulWithSandboxLatencies();
ExtendedMockito.verify(
() ->
SdkSandboxStatsLog.write(
SdkSandboxStatsLog.SANDBOX_API_CALLED,
SdkSandboxStatsLog.SANDBOX_API_CALLED__METHOD__LOAD_SDK,
(int)
(TIME_SANDBOX_RECEIVED_CALL_FROM_SYSTEM_SERVER
- TIME_SYSTEM_SERVER_CALLED_SANDBOX),
/*success=*/ true,
SANDBOX_API_CALLED__STAGE__SYSTEM_SERVER_TO_SANDBOX,
mClientAppUid));
int sandboxLatency =
(int)
(TIME_SANDBOX_CALLED_SYSTEM_SERVER
- TIME_SANDBOX_RECEIVED_CALL_FROM_SYSTEM_SERVER
- (TIME_SDK_CALL_COMPLETED - TIME_SANDBOX_CALLED_SDK));
ExtendedMockito.verify(
() ->
SdkSandboxStatsLog.write(
SdkSandboxStatsLog.SANDBOX_API_CALLED,
SdkSandboxStatsLog.SANDBOX_API_CALLED__METHOD__LOAD_SDK,
sandboxLatency,
/*success=*/ true,
SdkSandboxStatsLog.SANDBOX_API_CALLED__STAGE__SANDBOX,
mClientAppUid));
ExtendedMockito.verify(
() ->
SdkSandboxStatsLog.write(
SdkSandboxStatsLog.SANDBOX_API_CALLED,
SdkSandboxStatsLog.SANDBOX_API_CALLED__METHOD__LOAD_SDK,
(int) (TIME_SDK_CALL_COMPLETED - TIME_SANDBOX_CALLED_SDK),
/*success=*/ true,
SdkSandboxStatsLog.SANDBOX_API_CALLED__STAGE__SDK,
mClientAppUid));
ExtendedMockito.verify(
() ->
SdkSandboxStatsLog.write(
SdkSandboxStatsLog.SANDBOX_API_CALLED,
SdkSandboxStatsLog.SANDBOX_API_CALLED__METHOD__LOAD_SDK,
(int)
(TIME_SYSTEM_SERVER_RECEIVED_CALL_FROM_SANDBOX
- TIME_SANDBOX_CALLED_SYSTEM_SERVER),
/*success=*/ true,
SANDBOX_API_CALLED__STAGE__SANDBOX_TO_SYSTEM_SERVER,
mClientAppUid));
}
@Test
public void testLatencyMetrics_SystemServerAppToSandbox_LoadSdk_FailureOnMultiLoad()
throws Exception {
disableNetworkPermissionChecks();
disableForegroundCheck();
Mockito.when(mInjector.getCurrentTime())
.thenReturn(
TIME_SYSTEM_SERVER_RECEIVED_CALL_FROM_APP,
START_TIME_TO_LOAD_SANDBOX,
END_TIME_TO_LOAD_SANDBOX,
TIME_SYSTEM_SERVER_CALLED_SANDBOX,
TIME_SYSTEM_SERVER_RECEIVED_CALL_FROM_SANDBOX,
TIME_SYSTEM_SERVER_CALLED_APP,
TIME_SYSTEM_SERVER_RECEIVED_CALL_FROM_APP,
TIME_FAILURE_HANDLED);
// Load it once
{
FakeLoadSdkCallbackBinder callback = new FakeLoadSdkCallbackBinder();
mService.loadSdk(
TEST_PACKAGE,
null,
SDK_NAME,
TIME_APP_CALLED_SYSTEM_SERVER,
new Bundle(),
callback);
mSdkSandboxService.sendLoadCodeSuccessful();
}
// Load it again
{
FakeLoadSdkCallbackBinder callback = new FakeLoadSdkCallbackBinder();
mService.loadSdk(
TEST_PACKAGE,
null,
SDK_NAME,
TIME_APP_CALLED_SYSTEM_SERVER,
new Bundle(),
callback);
}
int timeSystemServerAppToSandbox =
(int) (TIME_FAILURE_HANDLED - TIME_SYSTEM_SERVER_RECEIVED_CALL_FROM_APP);
ExtendedMockito.verify(
() ->
SdkSandboxStatsLog.write(
SdkSandboxStatsLog.SANDBOX_API_CALLED,
SdkSandboxStatsLog.SANDBOX_API_CALLED__METHOD__LOAD_SDK,
timeSystemServerAppToSandbox,
/*success=*/ false,
SdkSandboxStatsLog
.SANDBOX_API_CALLED__STAGE__SYSTEM_SERVER_APP_TO_SANDBOX,
mClientAppUid));
}
@Test
public void testLatencyMetrics_SystemServerAppToSandbox_LoadSdk_InvalidSdkName()
throws RemoteException {
disableNetworkPermissionChecks();
disableForegroundCheck();
Mockito.when(mInjector.getCurrentTime())
.thenReturn(TIME_SYSTEM_SERVER_RECEIVED_CALL_FROM_APP, TIME_FAILURE_HANDLED);
FakeLoadSdkCallbackBinder callback = new FakeLoadSdkCallbackBinder();
mService.loadSdk(
TEST_PACKAGE,
null,
"RANDOM",
TIME_APP_CALLED_SYSTEM_SERVER,
new Bundle(),
callback);
ExtendedMockito.verify(
() ->
SdkSandboxStatsLog.write(
SdkSandboxStatsLog.SANDBOX_API_CALLED,
SdkSandboxStatsLog.SANDBOX_API_CALLED__METHOD__LOAD_SDK,
(int)
(TIME_FAILURE_HANDLED
- TIME_SYSTEM_SERVER_RECEIVED_CALL_FROM_APP),
/*success=*/ false,
SdkSandboxStatsLog
.SANDBOX_API_CALLED__STAGE__SYSTEM_SERVER_APP_TO_SANDBOX,
mClientAppUid));
ExtendedMockito.verify(
() ->
SdkSandboxStatsLog.write(
Mockito.anyInt(),
Mockito.anyInt(),
Mockito.anyInt(),
Mockito.anyBoolean(),
Mockito.anyInt(),
Mockito.anyInt()),
Mockito.times(2));
}
@Test
public void testLatencyMetrics_SystemServerAppToSandbox_LoadSdk_FailureOnAppDeath()
throws RemoteException {
disableNetworkPermissionChecks();
disableForegroundCheck();
FakeLoadSdkCallbackBinder callback = Mockito.mock(FakeLoadSdkCallbackBinder.class);
IBinder binder = Mockito.mock(IBinder.class);
Mockito.when(callback.asBinder()).thenReturn(binder);
Mockito.doThrow(new RemoteException())
.when(binder)
.linkToDeath(Mockito.any(), Mockito.anyInt());
Mockito.when(mInjector.getCurrentTime())
.thenReturn(TIME_SYSTEM_SERVER_RECEIVED_CALL_FROM_APP, TIME_FAILURE_HANDLED);
mService.loadSdk(
TEST_PACKAGE,
null,
SDK_NAME,
TIME_APP_CALLED_SYSTEM_SERVER,
new Bundle(),
callback);
ExtendedMockito.verify(
() ->
SdkSandboxStatsLog.write(
SdkSandboxStatsLog.SANDBOX_API_CALLED,
SdkSandboxStatsLog.SANDBOX_API_CALLED__METHOD__LOAD_SDK,
(int)
(TIME_FAILURE_HANDLED
- TIME_SYSTEM_SERVER_RECEIVED_CALL_FROM_APP),
/*success=*/ false,
SdkSandboxStatsLog
.SANDBOX_API_CALLED__STAGE__SYSTEM_SERVER_APP_TO_SANDBOX,
mClientAppUid));
ExtendedMockito.verify(
() ->
SdkSandboxStatsLog.write(
Mockito.anyInt(),
Mockito.anyInt(),
Mockito.anyInt(),
Mockito.anyBoolean(),
Mockito.anyInt(),
Mockito.anyInt()),
Mockito.times(2));
}
@Test
public void testLatencyMetrics_SystemServerAppToSandbox_LoadSdk_LoadSandboxFailure() {
disableNetworkPermissionChecks();
disableForegroundCheck();
sProvider.disableBinding();
Mockito.when(mInjector.getCurrentTime())
.thenReturn(
TIME_SYSTEM_SERVER_RECEIVED_CALL_FROM_APP,
START_TIME_TO_LOAD_SANDBOX,
TIME_FAILURE_HANDLED);
FakeLoadSdkCallbackBinder callback = new FakeLoadSdkCallbackBinder();
mService.loadSdk(
TEST_PACKAGE,
null,
SDK_NAME,
TIME_APP_CALLED_SYSTEM_SERVER,
new Bundle(),
callback);
ExtendedMockito.verify(
() ->
SdkSandboxStatsLog.write(
SdkSandboxStatsLog.SANDBOX_API_CALLED,
SdkSandboxStatsLog.SANDBOX_API_CALLED__METHOD__LOAD_SDK,
(int) (TIME_FAILURE_HANDLED - START_TIME_TO_LOAD_SANDBOX),
/*success=*/ false,
SdkSandboxStatsLog.SANDBOX_API_CALLED__STAGE__LOAD_SANDBOX,
mClientAppUid));
}
@Test
public void testLatencyMetrics_SystemServerAppToSandbox_RequestSurfacePackage()
throws Exception {
Mockito.when(mInjector.getCurrentTime())
.thenReturn(
TIME_SYSTEM_SERVER_RECEIVED_CALL_FROM_APP,
START_TIME_TO_LOAD_SANDBOX,
END_TIME_TO_LOAD_SANDBOX,
TIME_SYSTEM_SERVER_CALLED_SANDBOX,
TIME_SYSTEM_SERVER_RECEIVED_CALL_FROM_SANDBOX,
TIME_SYSTEM_SERVER_CALLED_APP,
TIME_SYSTEM_SERVER_RECEIVED_CALL_FROM_APP,
TIME_SYSTEM_SERVER_CALLED_SANDBOX);
loadSdk(SDK_NAME);
// 2. Call request package
FakeRequestSurfacePackageCallbackBinder surfacePackageCallback =
new FakeRequestSurfacePackageCallbackBinder();
mService.requestSurfacePackage(
TEST_PACKAGE,
SDK_NAME,
new Binder(),
0,
500,
500,
TIME_APP_CALLED_SYSTEM_SERVER,
new Bundle(),
surfacePackageCallback);
mSdkSandboxService.sendSurfacePackageReady(
new SandboxLatencyInfo(TIME_SYSTEM_SERVER_CALLED_SANDBOX));
ExtendedMockito.verify(
() ->
SdkSandboxStatsLog.write(
SdkSandboxStatsLog.SANDBOX_API_CALLED,
SANDBOX_API_CALLED__METHOD__REQUEST_SURFACE_PACKAGE,
(int)
(TIME_SYSTEM_SERVER_CALLED_SANDBOX
- TIME_SYSTEM_SERVER_RECEIVED_CALL_FROM_APP),
/*success=*/ true,
SdkSandboxStatsLog
.SANDBOX_API_CALLED__STAGE__SYSTEM_SERVER_APP_TO_SANDBOX,
mClientAppUid));
ExtendedMockito.verify(
() ->
SdkSandboxStatsLog.write(
Mockito.eq(SdkSandboxStatsLog.SANDBOX_API_CALLED),
Mockito.eq(SANDBOX_API_CALLED__METHOD__REQUEST_SURFACE_PACKAGE),
Mockito.anyInt(),
Mockito.eq(/*success=*/ true),
Mockito.eq(SANDBOX_API_CALLED__STAGE__SYSTEM_SERVER_TO_SANDBOX),
Mockito.eq(mClientAppUid)));
ExtendedMockito.verify(
() ->
SdkSandboxStatsLog.write(
Mockito.eq(SdkSandboxStatsLog.SANDBOX_API_CALLED),
Mockito.eq(SANDBOX_API_CALLED__METHOD__REQUEST_SURFACE_PACKAGE),
Mockito.anyInt(),
Mockito.eq(/*success=*/ true),
Mockito.eq(SdkSandboxStatsLog.SANDBOX_API_CALLED__STAGE__SANDBOX),
Mockito.eq(mClientAppUid)));
ExtendedMockito.verify(
() ->
SdkSandboxStatsLog.write(
Mockito.eq(SdkSandboxStatsLog.SANDBOX_API_CALLED),
Mockito.eq(SANDBOX_API_CALLED__METHOD__REQUEST_SURFACE_PACKAGE),
Mockito.anyInt(),
Mockito.eq(/*success=*/ true),
Mockito.eq(SANDBOX_API_CALLED__STAGE__SANDBOX_TO_SYSTEM_SERVER),
Mockito.eq(mClientAppUid)));
}
@Test
public void
testLatencyMetrics_SystemServerAppToSandbox_RequestSurfacePackage_WithSandboxLatencies()
throws Exception {
Mockito.when(mInjector.getCurrentTime())
.thenReturn(
TIME_SYSTEM_SERVER_RECEIVED_CALL_FROM_APP,
START_TIME_TO_LOAD_SANDBOX,
END_TIME_TO_LOAD_SANDBOX,
TIME_SYSTEM_SERVER_CALLED_SANDBOX,
TIME_SYSTEM_SERVER_RECEIVED_CALL_FROM_SANDBOX,
TIME_SYSTEM_SERVER_CALLED_APP,
TIME_SYSTEM_SERVER_RECEIVED_CALL_FROM_APP,
TIME_SYSTEM_SERVER_CALLED_SANDBOX);
loadSdk(SDK_NAME);
// 2. Call request package
FakeRequestSurfacePackageCallbackBinder surfacePackageCallback =
new FakeRequestSurfacePackageCallbackBinder();
mService.requestSurfacePackage(
TEST_PACKAGE,
SDK_NAME,
new Binder(),
0,
500,
500,
TIME_APP_CALLED_SYSTEM_SERVER,
new Bundle(),
surfacePackageCallback);
mSdkSandboxService.sendSurfacePackageReady(getFakedSandboxLatencies());
ExtendedMockito.verify(
() ->
SdkSandboxStatsLog.write(
SdkSandboxStatsLog.SANDBOX_API_CALLED,
SANDBOX_API_CALLED__METHOD__REQUEST_SURFACE_PACKAGE,
(int)
(TIME_SYSTEM_SERVER_CALLED_SANDBOX
- TIME_SYSTEM_SERVER_RECEIVED_CALL_FROM_APP),
/*success=*/ true,
SdkSandboxStatsLog
.SANDBOX_API_CALLED__STAGE__SYSTEM_SERVER_APP_TO_SANDBOX,
mClientAppUid));
ExtendedMockito.verify(
() ->
SdkSandboxStatsLog.write(
Mockito.eq(SdkSandboxStatsLog.SANDBOX_API_CALLED),
Mockito.eq(SANDBOX_API_CALLED__METHOD__REQUEST_SURFACE_PACKAGE),
Mockito.anyInt(),
Mockito.eq(/*success=*/ true),
Mockito.eq(SANDBOX_API_CALLED__STAGE__SYSTEM_SERVER_TO_SANDBOX),
Mockito.eq(mClientAppUid)));
ExtendedMockito.verify(
() ->
SdkSandboxStatsLog.write(
Mockito.eq(SdkSandboxStatsLog.SANDBOX_API_CALLED),
Mockito.eq(SANDBOX_API_CALLED__METHOD__REQUEST_SURFACE_PACKAGE),
Mockito.anyInt(),
Mockito.eq(/*success=*/ true),
Mockito.eq(SdkSandboxStatsLog.SANDBOX_API_CALLED__STAGE__SANDBOX),
Mockito.eq(mClientAppUid)));
ExtendedMockito.verify(
() ->
SdkSandboxStatsLog.write(
Mockito.eq(SdkSandboxStatsLog.SANDBOX_API_CALLED),
Mockito.eq(SANDBOX_API_CALLED__METHOD__REQUEST_SURFACE_PACKAGE),
Mockito.anyInt(),
Mockito.eq(/*success=*/ true),
Mockito.eq(SdkSandboxStatsLog.SANDBOX_API_CALLED__STAGE__SDK),
Mockito.eq(mClientAppUid)));
ExtendedMockito.verify(
() ->
SdkSandboxStatsLog.write(
Mockito.eq(SdkSandboxStatsLog.SANDBOX_API_CALLED),
Mockito.eq(SANDBOX_API_CALLED__METHOD__REQUEST_SURFACE_PACKAGE),
Mockito.anyInt(),
Mockito.eq(/*success=*/ true),
Mockito.eq(SANDBOX_API_CALLED__STAGE__SANDBOX_TO_SYSTEM_SERVER),
Mockito.eq(mClientAppUid)));
}
@Test
public void testLatencyMetrics_SystemServerSandboxToApp_RequestSurfacePackage()
throws RemoteException {
Mockito.when(mInjector.getCurrentTime())
.thenReturn(
TIME_SYSTEM_SERVER_RECEIVED_CALL_FROM_APP,
START_TIME_TO_LOAD_SANDBOX,
END_TIME_TO_LOAD_SANDBOX,
TIME_SYSTEM_SERVER_CALLED_SANDBOX,
TIME_SYSTEM_SERVER_RECEIVED_CALL_FROM_SANDBOX,
TIME_SYSTEM_SERVER_CALLED_APP,
TIME_SYSTEM_SERVER_RECEIVED_CALL_FROM_APP,
TIME_SYSTEM_SERVER_CALLED_SANDBOX,
TIME_SYSTEM_SERVER_RECEIVED_CALL_FROM_SANDBOX,
TIME_SYSTEM_SERVER_CALLED_APP);
loadSdk(SDK_NAME);
// 2. Call request package
FakeRequestSurfacePackageCallbackBinder surfacePackageCallback =
new FakeRequestSurfacePackageCallbackBinder();
mService.requestSurfacePackage(
TEST_PACKAGE,
SDK_NAME,
new Binder(),
0,
500,
500,
TIME_APP_CALLED_SYSTEM_SERVER,
new Bundle(),
surfacePackageCallback);
mSdkSandboxService.sendSurfacePackageReady(
new SandboxLatencyInfo(TIME_SYSTEM_SERVER_CALLED_SANDBOX));
ExtendedMockito.verify(
() ->
SdkSandboxStatsLog.write(
SdkSandboxStatsLog.SANDBOX_API_CALLED,
SANDBOX_API_CALLED__METHOD__REQUEST_SURFACE_PACKAGE,
(int)
(TIME_SYSTEM_SERVER_CALLED_APP
- TIME_SYSTEM_SERVER_RECEIVED_CALL_FROM_SANDBOX),
/*success=*/ true,
SANDBOX_API_CALLED__STAGE__SYSTEM_SERVER_SANDBOX_TO_APP,
mClientAppUid));
}
@Test
public void testLatencyMetrics_SystemServerAppToSandbox_GetSandboxedSdks() {
// TODO(b/242149555): Update tests to use fake for getting time series.
Mockito.when(mInjector.getCurrentTime())
.thenReturn(TIME_SYSTEM_SERVER_RECEIVED_CALL_FROM_APP, END_TIME_IN_SYSTEM_SERVER);
mService.getSandboxedSdks(TEST_PACKAGE, TIME_APP_CALLED_SYSTEM_SERVER);
ExtendedMockito.verify(
() ->
SdkSandboxStatsLog.write(
SdkSandboxStatsLog.SANDBOX_API_CALLED,
SdkSandboxStatsLog.SANDBOX_API_CALLED__METHOD__GET_SANDBOXED_SDKS,
(int)
(END_TIME_IN_SYSTEM_SERVER
- TIME_SYSTEM_SERVER_RECEIVED_CALL_FROM_APP),
/*success=*/ true,
SdkSandboxStatsLog
.SANDBOX_API_CALLED__STAGE__SYSTEM_SERVER_APP_TO_SANDBOX,
mClientAppUid));
}
@Test
public void testLatencyMetrics_IpcFromSystemServerToApp_RequestSurfacePackage() {
mService.logLatencyFromSystemServerToApp(
ISdkSandboxManager.REQUEST_SURFACE_PACKAGE, /*latency=*/ 1);
ExtendedMockito.verify(
() ->
SdkSandboxStatsLog.write(
SdkSandboxStatsLog.SANDBOX_API_CALLED,
SANDBOX_API_CALLED__METHOD__REQUEST_SURFACE_PACKAGE,
/*latency=*/ 1,
/*success=*/ true,
SdkSandboxStatsLog.SANDBOX_API_CALLED__STAGE__SYSTEM_SERVER_TO_APP,
mClientAppUid));
}
@Test
public void testIsDisabled() {
mService.forceEnableSandbox();
mSdkSandboxService.setIsDisabledResponse(false);
assertThat(mService.isSdkSandboxDisabled(mSdkSandboxService)).isFalse();
mService.clearSdkSandboxState();
mSdkSandboxService.setIsDisabledResponse(true);
assertThat(mService.isSdkSandboxDisabled(mSdkSandboxService)).isTrue();
mService.forceEnableSandbox();
}
@Test
public void testSdkSandboxDisabledCallback() {
SdkSandboxManagerService.SdkSandboxDisabledCallback callback =
new SdkSandboxManagerService.SdkSandboxDisabledCallback();
// In this case the callback has not been called, so a timeout will occur and sandbox
// will be disabled.
assertThat(callback.getIsDisabled()).isTrue();
callback.onResult(false);
assertThat(callback.getIsDisabled()).isFalse();
callback = new SdkSandboxManagerService.SdkSandboxDisabledCallback();
callback.onResult(true);
assertThat(callback.getIsDisabled()).isTrue();
}
@Test
public void testVisibilityPatchChecked() {
mService.clearSdkSandboxState();
// We should only check for the visibility patch on T devices.
boolean visibilityPatchCheckExpected = !SdkLevel.isAtLeastU();
mService.isSdkSandboxDisabled(mSdkSandboxService);
assertThat(mSdkSandboxService.wasVisibilityPatchChecked())
.isEqualTo(visibilityPatchCheckExpected);
}
@Test
public void testSdkSandboxEnabledForEmulator() {
// SDK sandbox is enabled for an emulator, even if the killswitch is turned on.
Mockito.when(mInjector.isEmulator()).thenReturn(true);
sSdkSandboxSettingsListener.setKillSwitchState(true);
assertThat(mService.isSdkSandboxDisabled(mSdkSandboxService)).isFalse();
// SDK sandbox is disabled when the killswitch is enabled if the device is not an emulator.
mService.clearSdkSandboxState();
Mockito.when(mInjector.isEmulator()).thenReturn(false);
sSdkSandboxSettingsListener.setKillSwitchState(true);
assertThat(mService.isSdkSandboxDisabled(mSdkSandboxService)).isTrue();
}
@Test
public void testSdkSandboxSettings() {
assertThat(sSdkSandboxSettingsListener.isKillSwitchEnabled()).isFalse();
sSdkSandboxSettingsListener.onPropertiesChanged(
new DeviceConfig.Properties(
DeviceConfig.NAMESPACE_ADSERVICES,
Map.of(PROPERTY_DISABLE_SANDBOX, "true")));
assertThat(sSdkSandboxSettingsListener.isKillSwitchEnabled()).isTrue();
sSdkSandboxSettingsListener.onPropertiesChanged(
new DeviceConfig.Properties(
DeviceConfig.NAMESPACE_ADSERVICES,
Map.of(PROPERTY_DISABLE_SANDBOX, "false")));
assertThat(sSdkSandboxSettingsListener.isKillSwitchEnabled()).isFalse();
}
@Test
public void testOtherPropertyChangeDoesNotAffectKillSwitch() {
assertThat(sSdkSandboxSettingsListener.isKillSwitchEnabled()).isFalse();
sSdkSandboxSettingsListener.onPropertiesChanged(
new DeviceConfig.Properties(
DeviceConfig.NAMESPACE_ADSERVICES, Map.of("other_property", "true")));
assertThat(sSdkSandboxSettingsListener.isKillSwitchEnabled()).isFalse();
}
@Test
public void testKillswitchStopsSandbox() throws Exception {
disableKillUid();
sSdkSandboxSettingsListener.onPropertiesChanged(
new DeviceConfig.Properties(
DeviceConfig.NAMESPACE_ADSERVICES,
Map.of(PROPERTY_DISABLE_SANDBOX, "false")));
sSdkSandboxSettingsListener.setKillSwitchState(false);
loadSdk(SDK_NAME);
sSdkSandboxSettingsListener.onPropertiesChanged(
new DeviceConfig.Properties(
DeviceConfig.NAMESPACE_ADSERVICES,
Map.of(PROPERTY_DISABLE_SANDBOX, "true")));
int callingUid = Binder.getCallingUid();
final CallingInfo callingInfo = new CallingInfo(callingUid, TEST_PACKAGE);
assertThat(sProvider.getSdkSandboxServiceForApp(callingInfo)).isEqualTo(null);
}
@Test
public void testLoadSdkFailsWhenSandboxDisabled() {
disableNetworkPermissionChecks();
disableForegroundCheck();
sSdkSandboxSettingsListener.setKillSwitchState(true);
sSdkSandboxSettingsListener.onPropertiesChanged(
new DeviceConfig.Properties(
DeviceConfig.NAMESPACE_ADSERVICES,
Map.of(PROPERTY_DISABLE_SANDBOX, "true")));
FakeLoadSdkCallbackBinder callback = new FakeLoadSdkCallbackBinder();
mService.loadSdk(
TEST_PACKAGE,
null,
SDK_NAME,
TIME_APP_CALLED_SYSTEM_SERVER,
new Bundle(),
callback);
callback.assertLoadSdkIsUnsuccessful();
assertThat(callback.getLoadSdkErrorCode())
.isEqualTo(SdkSandboxManager.LOAD_SDK_SDK_SANDBOX_DISABLED);
assertThat(callback.getLoadSdkErrorMsg()).isEqualTo("SDK sandbox is disabled");
}
@Test
public void testSdkSandboxSettings_canAccessContentProviderFromSdkSandbox_DefaultAccess()
throws Exception {
/** Ensuring that the property is not present in DeviceConfig */
DeviceConfig.deleteProperty(
DeviceConfig.NAMESPACE_ADSERVICES, PROPERTY_ENFORCE_CONTENT_PROVIDER_RESTRICTIONS);
ExtendedMockito.when(Process.isSdkSandboxUid(Mockito.anyInt())).thenReturn(true);
assertThat(
sSdkSandboxManagerLocal.canAccessContentProviderFromSdkSandbox(
new ProviderInfo()))
.isTrue();
}
@Test
public void testSdkSandboxSettings_canAccessContentProviderFromSdkSandbox_AccessNotAllowed() {
ExtendedMockito.when(Process.isSdkSandboxUid(Mockito.anyInt())).thenReturn(true);
sSdkSandboxSettingsListener.onPropertiesChanged(
new DeviceConfig.Properties(
DeviceConfig.NAMESPACE_ADSERVICES,
Map.of(PROPERTY_ENFORCE_CONTENT_PROVIDER_RESTRICTIONS, "true")));
assertThat(
sSdkSandboxManagerLocal.canAccessContentProviderFromSdkSandbox(
new ProviderInfo()))
.isFalse();
}
@Test
public void testSdkSandboxSettings_canAccessContentProviderFromSdkSandbox_AccessAllowed() {
ExtendedMockito.when(Process.isSdkSandboxUid(Mockito.anyInt())).thenReturn(true);
sSdkSandboxSettingsListener.onPropertiesChanged(
new DeviceConfig.Properties(
DeviceConfig.NAMESPACE_ADSERVICES,
Map.of(PROPERTY_ENFORCE_CONTENT_PROVIDER_RESTRICTIONS, "false")));
assertThat(
sSdkSandboxManagerLocal.canAccessContentProviderFromSdkSandbox(
new ProviderInfo()))
.isTrue();
}
@Test
public void
testLatencyMetrics_systemServerAppToSandbox_RequestSurfacePackage_sandboxNotLoaded() {
disableForegroundCheck();
Mockito.when(mInjector.getCurrentTime())
.thenReturn(TIME_SYSTEM_SERVER_RECEIVED_CALL_FROM_APP, TIME_FAILURE_HANDLED);
FakeRequestSurfacePackageCallbackBinder surfacePackageCallback =
new FakeRequestSurfacePackageCallbackBinder();
mService.requestSurfacePackage(
TEST_PACKAGE,
SDK_NAME,
new Binder(),
0,
500,
500,
TIME_APP_CALLED_SYSTEM_SERVER,
new Bundle(),
surfacePackageCallback);
ExtendedMockito.verify(
() ->
SdkSandboxStatsLog.write(
SdkSandboxStatsLog.SANDBOX_API_CALLED,
SANDBOX_API_CALLED__METHOD__REQUEST_SURFACE_PACKAGE,
(int)
(TIME_FAILURE_HANDLED
- TIME_SYSTEM_SERVER_RECEIVED_CALL_FROM_APP),
/*success=*/ false,
SdkSandboxStatsLog
.SANDBOX_API_CALLED__STAGE__SYSTEM_SERVER_APP_TO_SANDBOX,
mClientAppUid));
}
@Test
public void testLatencyMetrics_systemServer_unloadSdk() throws Exception {
disableKillUid();
Mockito.when(mInjector.getCurrentTime())
.thenReturn(
// for loadSdk
TIME_SYSTEM_SERVER_RECEIVED_CALL_FROM_APP,
START_TIME_TO_LOAD_SANDBOX,
END_TIME_TO_LOAD_SANDBOX,
TIME_SYSTEM_SERVER_CALLS_SANDBOX,
TIME_SYSTEM_SERVER_RECEIVED_CALL_FROM_SANDBOX,
TIME_SYSTEM_SERVER_CALLED_APP,
// for unloadSdk
TIME_SYSTEM_SERVER_RECEIVED_CALL_FROM_APP,
TIME_SYSTEM_SERVER_CALLED_SANDBOX,
TIME_SYSTEM_SERVER_RECEIVED_CALL_FROM_SANDBOX,
TIME_SYSTEM_SERVER_CALLED_APP);
loadSdk(SDK_NAME);
mService.unloadSdk(TEST_PACKAGE, SDK_NAME, TIME_APP_CALLED_SYSTEM_SERVER);
ExtendedMockito.verify(
() ->
SdkSandboxStatsLog.write(
SdkSandboxStatsLog.SANDBOX_API_CALLED,
SdkSandboxStatsLog.SANDBOX_API_CALLED__METHOD__UNLOAD_SDK,
(int)
(TIME_SYSTEM_SERVER_RECEIVED_CALL_FROM_APP
- TIME_APP_CALLED_SYSTEM_SERVER),
/*success=*/ true,
SdkSandboxStatsLog.SANDBOX_API_CALLED__STAGE__APP_TO_SYSTEM_SERVER,
mClientAppUid));
ExtendedMockito.verify(
() ->
SdkSandboxStatsLog.write(
SdkSandboxStatsLog.SANDBOX_API_CALLED,
SdkSandboxStatsLog.SANDBOX_API_CALLED__METHOD__UNLOAD_SDK,
(int)
(TIME_SYSTEM_SERVER_CALLED_SANDBOX
- TIME_SYSTEM_SERVER_RECEIVED_CALL_FROM_APP),
/*success=*/ true,
SdkSandboxStatsLog
.SANDBOX_API_CALLED__STAGE__SYSTEM_SERVER_APP_TO_SANDBOX,
mClientAppUid));
}
@Test
public void testLatencyMetrics_SystemServer_UnloadSdk_WithSandboxLatencies() throws Exception {
disableKillUid();
Mockito.when(mInjector.getCurrentTime())
.thenReturn(
// for loadSdk
TIME_SYSTEM_SERVER_RECEIVED_CALL_FROM_APP,
START_TIME_TO_LOAD_SANDBOX,
END_TIME_TO_LOAD_SANDBOX,
TIME_SYSTEM_SERVER_CALLS_SANDBOX,
TIME_SYSTEM_SERVER_RECEIVED_CALL_FROM_SANDBOX,
TIME_SYSTEM_SERVER_CALLED_APP,
// for unloadSdk
TIME_SYSTEM_SERVER_RECEIVED_CALL_FROM_APP,
TIME_SYSTEM_SERVER_CALLED_SANDBOX,
TIME_SYSTEM_SERVER_RECEIVED_CALL_FROM_SANDBOX,
TIME_SYSTEM_SERVER_CALLED_APP,
TIME_SANDBOX_CALLED_SYSTEM_SERVER);
loadSdk(SDK_NAME);
mService.unloadSdk(TEST_PACKAGE, SDK_NAME, TIME_APP_CALLED_SYSTEM_SERVER);
mSdkSandboxService.sendUnloadSdkSuccess();
ExtendedMockito.verify(
() ->
SdkSandboxStatsLog.write(
SdkSandboxStatsLog.SANDBOX_API_CALLED,
SdkSandboxStatsLog.SANDBOX_API_CALLED__METHOD__UNLOAD_SDK,
(int)
(TIME_SANDBOX_RECEIVED_CALL_FROM_SYSTEM_SERVER
- TIME_SYSTEM_SERVER_CALLED_SANDBOX),
/*success=*/ true,
SANDBOX_API_CALLED__STAGE__SYSTEM_SERVER_TO_SANDBOX,
mClientAppUid));
ExtendedMockito.verify(
() ->
SdkSandboxStatsLog.write(
SdkSandboxStatsLog.SANDBOX_API_CALLED,
SdkSandboxStatsLog.SANDBOX_API_CALLED__METHOD__UNLOAD_SDK,
(int)
(TIME_SANDBOX_CALLED_SYSTEM_SERVER
- TIME_SANDBOX_RECEIVED_CALL_FROM_SYSTEM_SERVER
- (TIME_SDK_CALL_COMPLETED
- TIME_SANDBOX_CALLED_SDK)),
/*success=*/ true,
SdkSandboxStatsLog.SANDBOX_API_CALLED__STAGE__SANDBOX,
mClientAppUid));
ExtendedMockito.verify(
() ->
SdkSandboxStatsLog.write(
SdkSandboxStatsLog.SANDBOX_API_CALLED,
SdkSandboxStatsLog.SANDBOX_API_CALLED__METHOD__UNLOAD_SDK,
(int) (TIME_SDK_CALL_COMPLETED - TIME_SANDBOX_CALLED_SDK),
/*success=*/ true,
SdkSandboxStatsLog.SANDBOX_API_CALLED__STAGE__SDK,
mClientAppUid));
ExtendedMockito.verify(
() ->
SdkSandboxStatsLog.write(
SdkSandboxStatsLog.SANDBOX_API_CALLED,
SdkSandboxStatsLog.SANDBOX_API_CALLED__METHOD__UNLOAD_SDK,
(int)
(TIME_SANDBOX_CALLED_SYSTEM_SERVER
- TIME_SANDBOX_CALLED_SYSTEM_SERVER),
/*success=*/ true,
SANDBOX_API_CALLED__STAGE__SANDBOX_TO_SYSTEM_SERVER,
mClientAppUid));
ExtendedMockito.verify(
() ->
SdkSandboxStatsLog.write(
SdkSandboxStatsLog.SANDBOX_API_CALLED,
SdkSandboxStatsLog.SANDBOX_API_CALLED__METHOD__UNLOAD_SDK,
(int)
(TIME_SYSTEM_SERVER_CALLED_APP
- TIME_SYSTEM_SERVER_RECEIVED_CALL_FROM_SANDBOX),
/*success=*/ true,
SANDBOX_API_CALLED__STAGE__SYSTEM_SERVER_SANDBOX_TO_APP,
mClientAppUid));
}
@Test
public void testLatencyMetrics_ipcFromAppToSystemServer_addSdkSandboxProcessDeathCallback()
throws Exception {
Mockito.when(mInjector.getCurrentTime())
.thenReturn(TIME_SYSTEM_SERVER_RECEIVED_CALL_FROM_APP);
// Register for sandbox death event
FakeSdkSandboxProcessDeathCallbackBinder lifecycleCallback =
new FakeSdkSandboxProcessDeathCallbackBinder();
mService.addSdkSandboxProcessDeathCallback(
TEST_PACKAGE, TIME_APP_CALLED_SYSTEM_SERVER, lifecycleCallback);
ExtendedMockito.verify(
() ->
SdkSandboxStatsLog.write(
SdkSandboxStatsLog.SANDBOX_API_CALLED,
SANDBOX_API_CALLED__METHOD__ADD_SDK_SANDBOX_LIFECYCLE_CALLBACK,
(int)
(TIME_SYSTEM_SERVER_RECEIVED_CALL_FROM_APP
- TIME_APP_CALLED_SYSTEM_SERVER),
/*success=*/ true,
SdkSandboxStatsLog.SANDBOX_API_CALLED__STAGE__APP_TO_SYSTEM_SERVER,
mClientAppUid));
}
@Test
public void testLatencyMetrics_systemServerAppToSandbox_addSdkSandboxProcessDeathCallback()
throws Exception {
Mockito.when(mInjector.getCurrentTime())
.thenReturn(
TIME_SYSTEM_SERVER_RECEIVED_CALL_FROM_APP,
TIME_SYSTEM_SERVER_COMPLETED_EXECUTION);
// Register for sandbox death event
FakeSdkSandboxProcessDeathCallbackBinder lifecycleCallback =
new FakeSdkSandboxProcessDeathCallbackBinder();
mService.addSdkSandboxProcessDeathCallback(
TEST_PACKAGE, TIME_APP_CALLED_SYSTEM_SERVER, lifecycleCallback);
ExtendedMockito.verify(
() ->
SdkSandboxStatsLog.write(
SdkSandboxStatsLog.SANDBOX_API_CALLED,
SANDBOX_API_CALLED__METHOD__ADD_SDK_SANDBOX_LIFECYCLE_CALLBACK,
(int)
(TIME_SYSTEM_SERVER_COMPLETED_EXECUTION
- TIME_SYSTEM_SERVER_RECEIVED_CALL_FROM_APP),
/*success=*/ true,
SdkSandboxStatsLog
.SANDBOX_API_CALLED__STAGE__SYSTEM_SERVER_APP_TO_SANDBOX,
mClientAppUid));
}
@Test
public void testLatencyMetrics_ipcFromAppToSystemServer_removeSdkSandboxProcessDeathCallback() {
Mockito.when(mInjector.getCurrentTime())
.thenReturn(
// for addSdkSandboxLifecycleCallback
TIME_SYSTEM_SERVER_RECEIVED_CALL_FROM_APP,
TIME_SYSTEM_SERVER_COMPLETED_EXECUTION,
// for removeSdkSandboxLifecycleCallback
TIME_SYSTEM_SERVER_RECEIVED_CALL_FROM_APP);
// Register for sandbox death event again
FakeSdkSandboxProcessDeathCallbackBinder lifecycleCallback =
new FakeSdkSandboxProcessDeathCallbackBinder();
mService.addSdkSandboxProcessDeathCallback(
TEST_PACKAGE, TIME_APP_CALLED_SYSTEM_SERVER, lifecycleCallback);
// Unregister one of the lifecycle callbacks
mService.removeSdkSandboxProcessDeathCallback(
TEST_PACKAGE, TIME_APP_CALLED_SYSTEM_SERVER, lifecycleCallback);
ExtendedMockito.verify(
() ->
SdkSandboxStatsLog.write(
SdkSandboxStatsLog.SANDBOX_API_CALLED,
SANDBOX_API_CALLED__METHOD__REMOVE_SDK_SANDBOX_LIFECYCLE_CALLBACK,
(int)
(TIME_SYSTEM_SERVER_RECEIVED_CALL_FROM_APP
- TIME_APP_CALLED_SYSTEM_SERVER),
/*success=*/ true,
SdkSandboxStatsLog.SANDBOX_API_CALLED__STAGE__APP_TO_SYSTEM_SERVER,
mClientAppUid));
}
@Test
public void testLatencyMetrics_systemServerAppToSandbox_removeSdkSandboxProcessDeathCallback()
throws Exception {
Mockito.when(mInjector.getCurrentTime())
.thenReturn(
// for addSdkSandboxLifecycleCallback
TIME_SYSTEM_SERVER_RECEIVED_CALL_FROM_APP,
TIME_SYSTEM_SERVER_COMPLETED_EXECUTION,
// for removeSdkSandboxLifecycleCallback
TIME_SYSTEM_SERVER_RECEIVED_CALL_FROM_APP,
TIME_SYSTEM_SERVER_COMPLETED_EXECUTION);
// Register for sandbox death event again
FakeSdkSandboxProcessDeathCallbackBinder lifecycleCallback =
new FakeSdkSandboxProcessDeathCallbackBinder();
mService.addSdkSandboxProcessDeathCallback(
TEST_PACKAGE, TIME_APP_CALLED_SYSTEM_SERVER, lifecycleCallback);
// Unregister one of the lifecycle callbacks
mService.removeSdkSandboxProcessDeathCallback(
TEST_PACKAGE, TIME_APP_CALLED_SYSTEM_SERVER, lifecycleCallback);
ExtendedMockito.verify(
() ->
SdkSandboxStatsLog.write(
SdkSandboxStatsLog.SANDBOX_API_CALLED,
SANDBOX_API_CALLED__METHOD__REMOVE_SDK_SANDBOX_LIFECYCLE_CALLBACK,
(int)
(TIME_SYSTEM_SERVER_COMPLETED_EXECUTION
- TIME_SYSTEM_SERVER_RECEIVED_CALL_FROM_APP),
/*success=*/ true,
SdkSandboxStatsLog
.SANDBOX_API_CALLED__STAGE__SYSTEM_SERVER_APP_TO_SANDBOX,
mClientAppUid));
}
@Test
public void testRemoveAppOwnedSdkSandboxInterfacesOnAppDeath() throws Exception {
IBinder iBinder = Mockito.mock(IBinder.class);
mService.registerAppOwnedSdkSandboxInterface(
TEST_PACKAGE,
new AppOwnedSdkSandboxInterface(
APP_OWNED_SDK_SANDBOX_INTERFACE_NAME,
/*version=*/ 0,
/*interfaceIBinder=*/ iBinder),
TIME_APP_CALLED_SYSTEM_SERVER);
ArgumentCaptor<IBinder.DeathRecipient> deathRecipient =
ArgumentCaptor.forClass(IBinder.DeathRecipient.class);
Mockito.verify(iBinder).linkToDeath(deathRecipient.capture(), ArgumentMatchers.eq(0));
// App Died
deathRecipient.getValue().binderDied();
assertThat(
mService.getAppOwnedSdkSandboxInterfaces(
TEST_PACKAGE, TIME_APP_CALLED_SYSTEM_SERVER))
.hasSize(0);
}
@Test
public void testUnloadSdkNotCalledOnAppDeath() throws Exception {
disableKillUid();
disableForegroundCheck();
disableNetworkPermissionChecks();
FakeLoadSdkCallbackBinder callback = Mockito.spy(new FakeLoadSdkCallbackBinder());
Mockito.doReturn(Mockito.mock(Binder.class)).when(callback).asBinder();
ArgumentCaptor<IBinder.DeathRecipient> deathRecipient =
ArgumentCaptor.forClass(IBinder.DeathRecipient.class);
mService.loadSdk(
TEST_PACKAGE,
null,
SDK_NAME,
TIME_APP_CALLED_SYSTEM_SERVER,
new Bundle(),
callback);
mSdkSandboxService.sendLoadCodeSuccessful();
callback.assertLoadSdkIsSuccessful();
Mockito.verify(callback.asBinder())
.linkToDeath(deathRecipient.capture(), ArgumentMatchers.eq(0));
// App Died
deathRecipient.getValue().binderDied();
Mockito.verify(mSdkSandboxService, Mockito.never())
.unloadSdk(
Mockito.anyString(),
Mockito.any(IUnloadSdkCallback.class),
Mockito.any(SandboxLatencyInfo.class));
}
@Test
public void testLoadSdk_computeSdkStorage() throws Exception {
final int callingUid = Process.myUid();
final CallingInfo callingInfo = new CallingInfo(callingUid, TEST_PACKAGE);
mSdkSandboxStorageManagerUtility.createSdkStorageForTest(
UserHandle.getUserId(callingUid),
TEST_PACKAGE,
Arrays.asList("sdk1", "sdk2"),
Arrays.asList(
SdkSandboxStorageManager.SubDirectories.SHARED_DIR,
SdkSandboxStorageManager.SubDirectories.SANDBOX_DIR));
loadSdk(SDK_NAME);
// Assume sdk storage information calculated and sent
mSdkSandboxService.sendStorageInfoToSystemServer();
final List<SdkSandboxStorageManager.StorageDirInfo> internalStorageDirInfo =
mSdkSandboxStorageManager.getInternalStorageDirInfo(callingInfo);
final List<SdkSandboxStorageManager.StorageDirInfo> sdkStorageDirInfo =
mSdkSandboxStorageManager.getSdkStorageDirInfo(callingInfo);
Mockito.verify(sSdkSandboxPulledAtoms, Mockito.timeout(5000))
.logStorage(mClientAppUid, /*sharedStorage=*/ 0, /*sdkStorage=*/ 0);
Mockito.verify(mSdkSandboxService, Mockito.times(1))
.computeSdkStorage(
Mockito.eq(mService.getListOfStoragePaths(internalStorageDirInfo)),
Mockito.eq(mService.getListOfStoragePaths(sdkStorageDirInfo)),
Mockito.any(IComputeSdkStorageCallback.class));
}
@Test
public void testLoadSdk_CustomizedApplicationInfoIsPopulatedProperly() throws Exception {
final int userId = UserHandle.getUserId(Process.myUid());
// Create fake storage directories
mSdkSandboxStorageManagerUtility.createSdkStorageForTest(
userId, TEST_PACKAGE, Arrays.asList(SDK_NAME), Collections.emptyList());
StorageDirInfo storageInfo =
mSdkSandboxStorageManagerUtility
.getSdkStorageDirInfoForTest(
null, userId, TEST_PACKAGE, Arrays.asList(SDK_NAME))
.get(0);
// Load SDK so that information is passed to sandbox service
loadSdk(SDK_NAME);
// Verify customized application info is overloaded with per-sdk storage paths
ApplicationInfo ai = mSdkSandboxService.getCustomizedInfo();
assertThat(ai.dataDir).isEqualTo(storageInfo.getCeDataDir());
assertThat(ai.credentialProtectedDataDir).isEqualTo(storageInfo.getCeDataDir());
assertThat(ai.deviceProtectedDataDir).isEqualTo(storageInfo.getDeDataDir());
}
@Test
public void testRegisterActivityInterceptorCallbackOnServiceStart() {
assumeTrue(SdkLevel.isAtLeastU());
// Build ActivityInterceptorInfo
int callingUid = 1000;
Intent intent = new Intent();
intent.setAction(SdkSandboxManager.ACTION_START_SANDBOXED_ACTIVITY);
intent.setPackage(getSandboxPackageName());
ActivityInfo activityInfo = new ActivityInfo();
ActivityInterceptorCallback.ActivityInterceptResult result =
interceptActivityLunch(intent, callingUid, activityInfo);
assertThat(result.getIntent()).isEqualTo(intent);
assertThat(result.getActivityOptions()).isNull();
assertThat(result.isActivityResolved()).isTrue();
assertThat(activityInfo.processName)
.isEqualTo(
mInjector
.getSdkSandboxServiceProvider()
.toSandboxProcessName(TEST_PACKAGE));
assertThat(activityInfo.applicationInfo.uid).isEqualTo(Process.toSdkSandboxUid(callingUid));
}
@Test
public void testRegisterActivityInterceptionWithRightComponentSuccess() {
assumeTrue(SdkLevel.isAtLeastU());
// Build ActivityInterceptorInfo
int callingUid = 1000;
Intent intent = new Intent();
intent.setAction(SdkSandboxManager.ACTION_START_SANDBOXED_ACTIVITY);
intent.setPackage(getSandboxPackageName());
intent.setComponent(new ComponentName(getSandboxPackageName(), ""));
ActivityInfo activityInfo = new ActivityInfo();
ActivityInterceptorCallback.ActivityInterceptResult result =
interceptActivityLunch(intent, callingUid, activityInfo);
assertThat(result.getIntent()).isEqualTo(intent);
assertThat(result.getActivityOptions()).isNull();
assertThat(result.isActivityResolved()).isTrue();
assertThat(activityInfo.processName)
.isEqualTo(
mInjector
.getSdkSandboxServiceProvider()
.toSandboxProcessName(TEST_PACKAGE));
assertThat(activityInfo.applicationInfo.uid).isEqualTo(Process.toSdkSandboxUid(callingUid));
}
@Test
public void testRegisterActivityInterceptionNotProceedForNullIntent() {
assumeTrue(SdkLevel.isAtLeastU());
ActivityInterceptorCallback.ActivityInterceptResult result = interceptActivityLunch(null);
assertThat(result).isNull();
}
@Test
public void testRegisterActivityInterceptionNotProceedForNullPackage() {
assumeTrue(SdkLevel.isAtLeastU());
Intent intent = new Intent();
ActivityInterceptorCallback.ActivityInterceptResult result = interceptActivityLunch(intent);
assertThat(result).isNull();
}
@Test
public void testRegisterActivityInterceptionNotProceedForWrongPackage() {
assumeTrue(SdkLevel.isAtLeastU());
Intent intent = new Intent();
intent.setPackage("com.random.package");
ActivityInterceptorCallback.ActivityInterceptResult result = interceptActivityLunch(intent);
assertThat(result).isNull();
}
@Test
public void testRegisterActivityInterceptionCallbackReturnNullForNullAction() {
assumeTrue(SdkLevel.isAtLeastU());
Intent intent = new Intent();
intent.setPackage("com.random.package");
ActivityInterceptorCallback.ActivityInterceptResult result = interceptActivityLunch(intent);
assertThat(result).isNull();
}
@Test
public void testRegisterActivityInterceptionCallbackReturnNullForWrongAction() {
assumeTrue(SdkLevel.isAtLeastU());
Intent intent = new Intent();
intent.setPackage("com.random.package");
intent.setAction(Intent.ACTION_VIEW);
ActivityInterceptorCallback.ActivityInterceptResult result = interceptActivityLunch(intent);
assertThat(result).isNull();
}
@Test
public void testRegisterActivityInterceptionCallbackReturnNullForWrongComponent() {
assumeTrue(SdkLevel.isAtLeastU());
Intent intent = new Intent();
intent.setAction(SdkSandboxManager.ACTION_START_SANDBOXED_ACTIVITY);
intent.setPackage(getSandboxPackageName());
intent.setComponent(new ComponentName("random", ""));
ActivityInterceptorCallback.ActivityInterceptResult result = interceptActivityLunch(intent);
assertThat(result).isNull();
}
private ActivityInterceptorCallback.ActivityInterceptResult interceptActivityLunch(
Intent intent) {
return interceptActivityLunch(intent, 1000, new ActivityInfo());
}
private ActivityInterceptorCallback.ActivityInterceptResult interceptActivityLunch(
Intent intent, int callingUid, ActivityInfo activityInfo) {
activityInfo.applicationInfo = new ApplicationInfo();
ActivityInterceptorCallback.ActivityInterceptorInfo info =
new ActivityInterceptorCallback.ActivityInterceptorInfo.Builder(
callingUid, 0, 0, 0, 0, intent, null, activityInfo)
.setCallingPackage(TEST_PACKAGE)
.build();
return mInterceptorCallbackArgumentCaptor.getValue().onInterceptActivityLaunch(info);
}
private SandboxLatencyInfo getFakedSandboxLatencies() {
final SandboxLatencyInfo sandboxLatencyInfo =
new SandboxLatencyInfo(TIME_SYSTEM_SERVER_CALLED_SANDBOX);
sandboxLatencyInfo.setTimeSandboxReceivedCallFromSystemServer(
TIME_SANDBOX_RECEIVED_CALL_FROM_SYSTEM_SERVER);
sandboxLatencyInfo.setTimeSandboxCalledSdk(TIME_SANDBOX_CALLED_SDK);
sandboxLatencyInfo.setTimeSdkCallCompleted(TIME_SDK_CALL_COMPLETED);
sandboxLatencyInfo.setTimeSandboxCalledSystemServer(TIME_SANDBOX_CALLED_SYSTEM_SERVER);
return sandboxLatencyInfo;
}
private void loadSdk(String sdkName) throws RemoteException {
disableNetworkPermissionChecks();
disableForegroundCheck();
FakeLoadSdkCallbackBinder callback = new FakeLoadSdkCallbackBinder();
mService.loadSdk(
TEST_PACKAGE, null, sdkName, TIME_APP_CALLED_SYSTEM_SERVER, new Bundle(), callback);
mSdkSandboxService.sendLoadCodeSuccessful();
callback.assertLoadSdkIsSuccessful();
}
private void killSandbox() throws Exception {
ArgumentCaptor<IBinder.DeathRecipient> deathRecipientCaptor =
ArgumentCaptor.forClass(IBinder.DeathRecipient.class);
Mockito.verify(mSdkSandboxService.asBinder(), Mockito.atLeastOnce())
.linkToDeath(deathRecipientCaptor.capture(), ArgumentMatchers.eq(0));
List<IBinder.DeathRecipient> deathRecipients = deathRecipientCaptor.getAllValues();
for (IBinder.DeathRecipient deathRecipient : deathRecipients) {
deathRecipient.binderDied();
}
}
// Restart sandbox which creates a new sandbox service binder.
private void restartAndSetSandboxService() throws Exception {
mSdkSandboxService = sProvider.restartSandbox();
}
private String getSandboxPackageName() {
return mSpyContext.getPackageManager().getSdkSandboxPackageName();
}
private void mockGrantedPermission(String permission) {
Log.d(TAG, "mockGrantedPermission(" + permission + ")");
Mockito.doNothing()
.when(mSpyContext)
.enforceCallingPermission(Mockito.eq(permission), Mockito.anyString());
}
private void requiresAtLeastU(boolean required) {
Log.d(
TAG,
"requireAtLeastU("
+ required
+ "): SdkLevel.isAtLeastU()="
+ SdkLevel.isAtLeastU());
// TODO(b/280677793): rather than assuming it's the given version, mock it:
// ExtendedMockito.doReturn(required).when(() -> SdkLevel.isAtLeastU());
if (required) {
assumeTrue("Device must be at least U", SdkLevel.isAtLeastU());
} else {
assumeFalse("Device must be less than U", SdkLevel.isAtLeastU());
}
}
/** Fake service provider that returns local instance of {@link SdkSandboxServiceProvider} */
private static class FakeSdkSandboxProvider implements SdkSandboxServiceProvider {
private FakeSdkSandboxService mSdkSandboxService;
private final ArrayMap<CallingInfo, ISdkSandboxService> mService = new ArrayMap<>();
// When set to true, this will fail the bindService call
private boolean mFailBinding = false;
private ServiceConnection mServiceConnection = null;
FakeSdkSandboxProvider(FakeSdkSandboxService service) {
mSdkSandboxService = service;
}
public void disableBinding() {
mFailBinding = true;
}
public FakeSdkSandboxService restartSandbox() {
mServiceConnection.onServiceDisconnected(null);
// Create a new sandbox service.
mSdkSandboxService = Mockito.spy(FakeSdkSandboxService.class);
// Call onServiceConnected() again with the new fake sandbox service.
mServiceConnection.onServiceConnected(null, mSdkSandboxService.asBinder());
return mSdkSandboxService;
}
@Override
public void bindService(CallingInfo callingInfo, ServiceConnection serviceConnection) {
if (mFailBinding) {
serviceConnection.onNullBinding(new ComponentName("random", "component"));
return;
}
if (mService.containsKey(callingInfo)) {
return;
}
mService.put(callingInfo, mSdkSandboxService);
serviceConnection.onServiceConnected(null, mSdkSandboxService.asBinder());
mServiceConnection = serviceConnection;
}
@Override
public void unbindService(CallingInfo callingInfo) {
mService.remove(callingInfo);
}
@Override
public void stopSandboxService(CallingInfo callingInfo) {
mService.remove(callingInfo);
}
@Nullable
@Override
public ISdkSandboxService getSdkSandboxServiceForApp(CallingInfo callingInfo) {
return mService.get(callingInfo);
}
@Override
public void onServiceConnected(
CallingInfo callingInfo, @NonNull ISdkSandboxService service) {
mService.put(callingInfo, service);
}
@Override
public void onServiceDisconnected(CallingInfo callingInfo) {
mService.put(callingInfo, null);
}
@Override
public void onAppDeath(CallingInfo callingInfo) {}
@Override
public void onSandboxDeath(CallingInfo callingInfo) {}
@Override
public int getSandboxStatusForApp(CallingInfo callingInfo) {
if (mService.containsKey(callingInfo)) {
return CREATED;
} else {
return NON_EXISTENT;
}
}
@Override
public void dump(PrintWriter writer) {
writer.println("FakeDump");
}
@NonNull
@Override
public String toSandboxProcessName(@NonNull String packageName) {
return TEST_PACKAGE + SANDBOX_PROCESS_NAME_SUFFIX;
}
}
private static Bundle getTestBundle() {
final Bundle data = new Bundle();
data.putString(TEST_KEY, TEST_VALUE);
return data;
}
public static class InjectorForTest extends SdkSandboxManagerService.Injector {
private SdkSandboxStorageManager mSdkSandboxStorageManager = null;
InjectorForTest(Context context, SdkSandboxStorageManager sdkSandboxStorageManager) {
super(context);
mSdkSandboxStorageManager = sdkSandboxStorageManager;
}
public InjectorForTest(Context spyContext) {
super(spyContext);
}
@Override
public long getCurrentTime() {
return TIME_SYSTEM_SERVER_RECEIVED_CALL_FROM_APP;
}
@Override
public SdkSandboxServiceProvider getSdkSandboxServiceProvider() {
return sProvider;
}
@Override
public SdkSandboxPulledAtoms getSdkSandboxPulledAtoms() {
return sSdkSandboxPulledAtoms;
}
@Override
public SdkSandboxStorageManager getSdkSandboxStorageManager() {
return mSdkSandboxStorageManager;
}
}
}