Avoid killing the sandbox in hostside tests
After running runDeviceTests() in a hostside test, the app
instrumentation and sandbox are no longer alive, so there is no sandbox
to be killed. A new permission and a test API stopSdkSandbox is added to
allow an app to kill its own sandbox. Hostside tests that were
previously trying to kill a sandbox are rewritten.
Additionally, register a sandbox death recipient for cleaning up the
state of SdkSandboxManagerService each time onServiceConnected() is
called as the system can restart a new sandbox after the death of an old
one.
Bug: 239690000
Bug: 239553253
Bug: 240164606
Test: atest
SdkSandboxManagerTest#testReloadingSdkAfterSandboxDeathIsSuccessful
Test: atest SdkSandboxManagerServiceUnitTest
Change-Id: I74b625767da8f49f1a07eba438ec54e14a2536dd
Change-Id: If5b90e8285882902f8d56df0c43b3b1a6b36de6d
diff --git a/sdksandbox/SdkSandbox/AndroidManifest.xml b/sdksandbox/SdkSandbox/AndroidManifest.xml
index 3e65cbc..7d09b20 100644
--- a/sdksandbox/SdkSandbox/AndroidManifest.xml
+++ b/sdksandbox/SdkSandbox/AndroidManifest.xml
@@ -26,6 +26,10 @@
android:minSdkVersion="33"
android:targetSdkVersion="33" />
+ <!-- @hide @TestApi -->
+ <permission android:name="com.android.app.sdksandbox.permission.STOP_SDK_SANDBOX"
+ android:protectionLevel="normal" />
+
<!-- Allows SdkSandbox to start activities. -->
<!-- TODO(b/209599396): temporary workaround, remove once proper solution is implemented. -->
<uses-permission android:name="android.permission.START_ACTIVITIES_FROM_BACKGROUND"/>
diff --git a/sdksandbox/TEST_MAPPING b/sdksandbox/TEST_MAPPING
index cb73de2..33a00ff 100644
--- a/sdksandbox/TEST_MAPPING
+++ b/sdksandbox/TEST_MAPPING
@@ -17,9 +17,6 @@
"name": "SdkSandboxManagerTests[com.google.android.adservices.apex]"
},
{
- "name": "SdkSandboxHostTest[com.google.android.adservices.apex]"
- },
- {
"name": "SdkSandboxLifecycleHostTest[com.google.android.adservices.apex]"
},
{
@@ -60,9 +57,6 @@
"name": "SdkSandboxManagerTests"
},
{
- "name": "SdkSandboxHostTest"
- },
- {
"name": "SdkSandboxLifecycleHostTest"
},
{
diff --git a/sdksandbox/framework/java/android/app/sdksandbox/ISdkSandboxManager.aidl b/sdksandbox/framework/java/android/app/sdksandbox/ISdkSandboxManager.aidl
index 12bb23a..30fdf05 100644
--- a/sdksandbox/framework/java/android/app/sdksandbox/ISdkSandboxManager.aidl
+++ b/sdksandbox/framework/java/android/app/sdksandbox/ISdkSandboxManager.aidl
@@ -32,4 +32,5 @@
void sendData(in String callingPackageName, in String sdkName, in Bundle data, in ISendDataCallback callback);
List<SharedLibraryInfo> getLoadedSdkLibrariesInfo(in String callingPackageName);
void syncDataFromClient(in String callingPackageName, in Bundle data);
+ void stopSdkSandbox(in String callingPackageName);
}
diff --git a/sdksandbox/framework/java/android/app/sdksandbox/SdkSandboxManager.java b/sdksandbox/framework/java/android/app/sdksandbox/SdkSandboxManager.java
index a71bab9..e31ebd2 100644
--- a/sdksandbox/framework/java/android/app/sdksandbox/SdkSandboxManager.java
+++ b/sdksandbox/framework/java/android/app/sdksandbox/SdkSandboxManager.java
@@ -21,7 +21,9 @@
import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
import android.annotation.SystemService;
+import android.annotation.TestApi;
import android.content.Context;
import android.content.pm.SharedLibraryInfo;
import android.os.Binder;
@@ -163,6 +165,21 @@
}
/**
+ * Stop the SDK sandbox process corresponding to the app.
+ *
+ * @hide
+ */
+ @TestApi
+ @RequiresPermission("com.android.app.sdksandbox.permission.STOP_SDK_SANDBOX")
+ public void stopSdkSandbox() {
+ try {
+ mService.stopSdkSandbox(mContext.getPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Load SDK in a SDK sandbox java process.
*
* <p>It loads SDK library with {@code sdkName} to a sandbox process asynchronously, caller
diff --git a/sdksandbox/service/java/com/android/server/sdksandbox/SdkSandboxManagerService.java b/sdksandbox/service/java/com/android/server/sdksandbox/SdkSandboxManagerService.java
index 3bcfd66..065ca21 100644
--- a/sdksandbox/service/java/com/android/server/sdksandbox/SdkSandboxManagerService.java
+++ b/sdksandbox/service/java/com/android/server/sdksandbox/SdkSandboxManagerService.java
@@ -86,6 +86,8 @@
private static final String TAG = "SdkSandboxManager";
private static final String PROPERTY_SDK_PROVIDER_CLASS_NAME =
"android.sdksandbox.PROPERTY_SDK_PROVIDER_CLASS_NAME";
+ private static final String STOP_SDK_SANDBOX_PERMISSION =
+ "com.android.app.sdksandbox.permission.STOP_SDK_SANDBOX";
private final Context mContext;
private final SdkTokenManager mSdkTokenManager = new SdkTokenManager();
@@ -420,24 +422,24 @@
}
}
- static class SandboxServiceConnection implements ServiceConnection {
+ interface SandboxBindingCallback {
+ void onBindingSuccessful(ISdkSandboxService service);
- interface Callback {
- void onBindingSuccessful(ISdkSandboxService service);
+ void onBindingFailed();
+ }
- void onBindingFailed();
- }
+ class SandboxServiceConnection implements ServiceConnection {
private final SdkSandboxServiceProvider mServiceProvider;
private final CallingInfo mCallingInfo;
private boolean mServiceBound = false;
- private final Callback mCallback;
+ private final SandboxBindingCallback mCallback;
SandboxServiceConnection(
SdkSandboxServiceProvider serviceProvider,
CallingInfo callingInfo,
- Callback callback) {
+ SandboxBindingCallback callback) {
mServiceProvider = serviceProvider;
mCallingInfo = callingInfo;
mCallback = callback;
@@ -454,6 +456,13 @@
mCallingInfo.getPackageName(), mCallingInfo.getUid()));
mServiceProvider.setBoundServiceForApp(mCallingInfo, mService);
+ try {
+ service.linkToDeath(() -> removeAllSdkTokensAndLinks(mCallingInfo), 0);
+ } catch (RemoteException re) {
+ // Sandbox had already died, cleanup sdk tokens and links.
+ removeAllSdkTokensAndLinks(mCallingInfo);
+ }
+
if (!mServiceBound) {
mCallback.onBindingSuccessful(mService);
mServiceBound = true;
@@ -481,7 +490,7 @@
}
}
- void startSdkSandbox(CallingInfo callingInfo, SandboxServiceConnection.Callback callback) {
+ void startSdkSandbox(CallingInfo callingInfo, SandboxBindingCallback callback) {
mServiceProvider.bindService(
callingInfo, new SandboxServiceConnection(mServiceProvider, callingInfo, callback));
}
@@ -497,17 +506,9 @@
startSdkSandbox(
callingInfo,
- new SandboxServiceConnection.Callback() {
+ new SandboxBindingCallback() {
@Override
public void onBindingSuccessful(ISdkSandboxService service) {
- try {
- service.asBinder()
- .linkToDeath(() -> removeAllSdkTokensAndLinks(callingInfo), 0);
- } catch (RemoteException re) {
- // Sandbox had already died, cleanup sdk tokens and links.
- removeAllSdkTokensAndLinks(callingInfo);
- }
-
loadSdkForService(callingInfo, sdkToken, info, params, link, service);
}
@@ -521,7 +522,29 @@
});
}
+ @Override
+ public void stopSdkSandbox(String callingPackageName) {
+ final int callingUid = Binder.getCallingUid();
+ final CallingInfo callingInfo = new CallingInfo(callingUid, callingPackageName);
+ enforceCallingPackageBelongsToUid(callingInfo);
+
+ mContext.enforceCallingPermission(
+ STOP_SDK_SANDBOX_PERMISSION,
+ callingPackageName + " does not have permission to stop their sandbox");
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ stopSdkSandboxService(callingInfo, "App requesting sandbox kill");
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
void stopSdkSandboxService(CallingInfo callingInfo, String reason) {
+ if (!isSdkSandboxServiceRunning(callingInfo)) {
+ return;
+ }
+
mServiceProvider.unbindService(callingInfo);
final int sdkSandboxUid = Process.toSdkSandboxUid(callingInfo.getUid());
Log.i(TAG, "Killing sdk sandbox/s with uid " + sdkSandboxUid);
@@ -589,6 +612,7 @@
synchronized (mLock) {
ISdkSandboxService boundSandbox = mServiceProvider.getBoundServiceForApp(callingInfo);
boolean shouldStopSandbox = true;
+ ArrayList<IBinder> linksToDelete = new ArrayList<>();
for (int i = 0; i < mAppAndRemoteSdkLinks.size(); i++) {
AppAndRemoteSdkLink link = mAppAndRemoteSdkLinks.valueAt(i);
if (link.mCallingInfo.equals(callingInfo)) {
@@ -602,14 +626,19 @@
Log.w(TAG, "Failed to unload SDK: ", e);
}
}
- mSdkTokenManager.destroy(sdkToken);
- mAppAndRemoteSdkLinks.remove(sdkToken);
+ linksToDelete.add(sdkToken);
} else {
shouldStopSandbox = false;
}
}
}
+ for (int i = 0; i < linksToDelete.size(); i++) {
+ IBinder sdkToken = linksToDelete.get(i);
+ mSdkTokenManager.destroy(sdkToken);
+ mAppAndRemoteSdkLinks.remove(sdkToken);
+ }
+
return shouldStopSandbox;
}
}
diff --git a/sdksandbox/service/java/com/android/server/sdksandbox/SdkSandboxShellCommand.java b/sdksandbox/service/java/com/android/server/sdksandbox/SdkSandboxShellCommand.java
index d2e27d5..83c5bf9 100644
--- a/sdksandbox/service/java/com/android/server/sdksandbox/SdkSandboxShellCommand.java
+++ b/sdksandbox/service/java/com/android/server/sdksandbox/SdkSandboxShellCommand.java
@@ -120,7 +120,7 @@
/** Callback for binding sandbox. Provides blocking interface {@link #isSuccessful()}. */
private class LatchSandboxServiceConnectionCallback
- implements SdkSandboxManagerService.SandboxServiceConnection.Callback {
+ implements SdkSandboxManagerService.SandboxBindingCallback {
private final CountDownLatch mLatch = new CountDownLatch(1);
private boolean mSuccess = false;
diff --git a/sdksandbox/tests/cts/endtoendtests/Android.bp b/sdksandbox/tests/cts/endtoendtests/Android.bp
index 447d1ee..1e00ec6 100644
--- a/sdksandbox/tests/cts/endtoendtests/Android.bp
+++ b/sdksandbox/tests/cts/endtoendtests/Android.bp
@@ -34,7 +34,7 @@
data: [
":LoadSdkSuccessfullySdkProvider",
":GetLoadedSdkLibInfoSuccessfully",
- ":RetryLoadSameSdkShouldFailSdkProvider",
+ ":LoadSdkSuccessfullySdkProviderTwo",
":LoadSdkWithInternalErrorSdkProvider",
":RequestSurfacePackageSuccessfullySdkProvider",
":RequestSurfacePackageWithInternalErrorSdkProvider",
diff --git a/sdksandbox/tests/cts/endtoendtests/AndroidManifest.xml b/sdksandbox/tests/cts/endtoendtests/AndroidManifest.xml
index 949e465..f6c2920 100644
--- a/sdksandbox/tests/cts/endtoendtests/AndroidManifest.xml
+++ b/sdksandbox/tests/cts/endtoendtests/AndroidManifest.xml
@@ -20,6 +20,7 @@
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+ <uses-permission android:name="com.android.app.sdksandbox.permission.STOP_SDK_SANDBOX" />
<application android:debuggable="true">
<uses-library android:name="android.test.runner" />
<uses-sdk-library android:name="com.android.loadSdkSuccessfullySdkProvider"
@@ -28,7 +29,7 @@
<uses-sdk-library android:name="com.android.getLoadedSdkLibInfoSuccessfully"
android:versionMajor="1"
android:certDigest="0B:44:2D:88:FA:A7:B3:AD:23:8D:DE:29:8A:A1:9B:D5:62:03:92:0B:BF:D8:D3:EB:C8:99:33:2C:8E:E1:15:99" />
- <uses-sdk-library android:name="com.android.retryLoadSameSdkShouldFailSdkProvider"
+ <uses-sdk-library android:name="com.android.loadSdkSuccessfullySdkProviderTwo"
android:versionMajor="1"
android:certDigest="0B:44:2D:88:FA:A7:B3:AD:23:8D:DE:29:8A:A1:9B:D5:62:03:92:0B:BF:D8:D3:EB:C8:99:33:2C:8E:E1:15:99" />
<uses-sdk-library android:name="com.android.loadSdkWithInternalErrorSdkProvider"
diff --git a/sdksandbox/tests/cts/endtoendtests/AndroidTest.xml b/sdksandbox/tests/cts/endtoendtests/AndroidTest.xml
index d2e805d..24e83c8 100644
--- a/sdksandbox/tests/cts/endtoendtests/AndroidTest.xml
+++ b/sdksandbox/tests/cts/endtoendtests/AndroidTest.xml
@@ -24,7 +24,7 @@
<option name="cleanup-apks" value="true"/>
<option name="test-file-name" value="LoadSdkSuccessfullySdkProvider.apk"/>
<option name="test-file-name" value="GetLoadedSdkLibInfoSuccessfully.apk"/>
- <option name="test-file-name" value="RetryLoadSameSdkShouldFailSdkProvider.apk"/>
+ <option name="test-file-name" value="LoadSdkSuccessfullySdkProviderTwo.apk"/>
<option name="test-file-name" value="LoadSdkWithInternalErrorSdkProvider.apk"/>
<option name="test-file-name" value="RequestSurfacePackageSuccessfullySdkProvider.apk"/>
<option name="test-file-name" value="RequestSurfacePackageWithInternalErrorSdkProvider.apk"/>
diff --git a/sdksandbox/tests/cts/endtoendtests/providers/retryLoadSameSdkShouldFail/Android.bp b/sdksandbox/tests/cts/endtoendtests/providers/loadSdkSuccessfullyTwo/Android.bp
similarity index 94%
rename from sdksandbox/tests/cts/endtoendtests/providers/retryLoadSameSdkShouldFail/Android.bp
rename to sdksandbox/tests/cts/endtoendtests/providers/loadSdkSuccessfullyTwo/Android.bp
index 6c16bb6..95b2785 100644
--- a/sdksandbox/tests/cts/endtoendtests/providers/retryLoadSameSdkShouldFail/Android.bp
+++ b/sdksandbox/tests/cts/endtoendtests/providers/loadSdkSuccessfullyTwo/Android.bp
@@ -17,7 +17,7 @@
}
android_test_helper_app {
- name: "RetryLoadSameSdkShouldFailSdkProvider",
+ name: "LoadSdkSuccessfullySdkProviderTwo",
defaults: ["platform_app_defaults"],
certificate: ":sdksandbox-test",
srcs: [
diff --git a/sdksandbox/tests/cts/endtoendtests/providers/retryLoadSameSdkShouldFail/AndroidManifest.xml b/sdksandbox/tests/cts/endtoendtests/providers/loadSdkSuccessfullyTwo/AndroidManifest.xml
similarity index 84%
rename from sdksandbox/tests/cts/endtoendtests/providers/retryLoadSameSdkShouldFail/AndroidManifest.xml
rename to sdksandbox/tests/cts/endtoendtests/providers/loadSdkSuccessfullyTwo/AndroidManifest.xml
index 302fbec..90229b2 100644
--- a/sdksandbox/tests/cts/endtoendtests/providers/retryLoadSameSdkShouldFail/AndroidManifest.xml
+++ b/sdksandbox/tests/cts/endtoendtests/providers/loadSdkSuccessfullyTwo/AndroidManifest.xml
@@ -18,9 +18,9 @@
package="com.android.retryloadsamesdkshouldfailsdkprovider">
<application>
- <sdk-library android:name="com.android.retryLoadSameSdkShouldFailSdkProvider"
+ <sdk-library android:name="com.android.loadSdkSuccessfullySdkProviderTwo"
android:versionMajor="1" />
<property android:name="android.sdksandbox.PROPERTY_SDK_PROVIDER_CLASS_NAME"
- android:value="com.android.retryloadsamesdkshouldfailsdkprovider.SdkProvider" />
+ android:value="com.android.loadsdksuccessfullysdkprovidertwo.SdkProvider" />
</application>
</manifest>
diff --git a/sdksandbox/tests/cts/endtoendtests/providers/retryLoadSameSdkShouldFail/src/com/android/retryloadsamesdkshouldfailsdkprovider/SdkProvider.java b/sdksandbox/tests/cts/endtoendtests/providers/loadSdkSuccessfullyTwo/src/com/android/loadsdksuccessfullysdkprovidertwo/SdkProvider.java
similarity index 95%
rename from sdksandbox/tests/cts/endtoendtests/providers/retryLoadSameSdkShouldFail/src/com/android/retryloadsamesdkshouldfailsdkprovider/SdkProvider.java
rename to sdksandbox/tests/cts/endtoendtests/providers/loadSdkSuccessfullyTwo/src/com/android/loadsdksuccessfullysdkprovidertwo/SdkProvider.java
index 3185670..f06845c 100644
--- a/sdksandbox/tests/cts/endtoendtests/providers/retryLoadSameSdkShouldFail/src/com/android/retryloadsamesdkshouldfailsdkprovider/SdkProvider.java
+++ b/sdksandbox/tests/cts/endtoendtests/providers/loadSdkSuccessfullyTwo/src/com/android/loadsdksuccessfullysdkprovidertwo/SdkProvider.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.retryloadsamesdkshouldfailsdkprovider;
+package com.android.loadsdksuccessfullysdkprovidertwo;
import android.app.sdksandbox.SandboxedSdkProvider;
import android.content.Context;
diff --git a/sdksandbox/tests/cts/endtoendtests/src/com/android/tests/sdksandbox/endtoend/SdkSandboxManagerTest.java b/sdksandbox/tests/cts/endtoendtests/src/com/android/tests/sdksandbox/endtoend/SdkSandboxManagerTest.java
index 2657763..c7cc7ce 100644
--- a/sdksandbox/tests/cts/endtoendtests/src/com/android/tests/sdksandbox/endtoend/SdkSandboxManagerTest.java
+++ b/sdksandbox/tests/cts/endtoendtests/src/com/android/tests/sdksandbox/endtoend/SdkSandboxManagerTest.java
@@ -64,7 +64,7 @@
@Test
public void retryLoadSameSdkShouldFail() {
- final String sdkName = "com.android.retryLoadSameSdkShouldFailSdkProvider";
+ final String sdkName = "com.android.loadSdkSuccessfullySdkProviderTwo";
FakeLoadSdkCallback callback = new FakeLoadSdkCallback();
mSdkSandboxManager.loadSdk(sdkName, new Bundle(), Runnable::run, callback);
@@ -163,6 +163,20 @@
}
@Test
+ public void testReloadingSdkAfterKillingSandboxIsSuccessful() throws Exception {
+ // Killing the sandbox and loading the same SDKs again multiple times should work
+ for (int i = 0; i < 3; ++i) {
+ // Kill the sandbox if it already exists from previous tests/loop
+ killSandboxAndWaitForDeath();
+ // The same SDKs should be able to be loaded again after sandbox death
+ loadMultipleSdks();
+ }
+
+ // Clean up before running other tests
+ killSandboxAndWaitForDeath();
+ }
+
+ @Test
public void getLoadedSdkLibrariesInfoSuccessfully() {
final String sdkName = "com.android.getLoadedSdkLibInfoSuccessfully";
final FakeLoadSdkCallback callback = new FakeLoadSdkCallback();
@@ -262,5 +276,24 @@
mSdkSandboxManager.loadSdk(sdkName, new Bundle(), Runnable::run, callback);
assertThat(callback.isLoadSdkSuccessful()).isTrue();
}
+
+ private void loadMultipleSdks() {
+ FakeLoadSdkCallback callback = new FakeLoadSdkCallback();
+ final String sdk1 = "com.android.loadSdkSuccessfullySdkProvider";
+ mSdkSandboxManager.loadSdk(sdk1, new Bundle(), Runnable::run, callback);
+ assertThat(callback.isLoadSdkSuccessful()).isTrue();
+
+ FakeLoadSdkCallback callback2 = new FakeLoadSdkCallback();
+ final String sdk2 = "com.android.loadSdkSuccessfullySdkProviderTwo";
+ mSdkSandboxManager.loadSdk(sdk2, new Bundle(), Runnable::run, callback2);
+ assertThat(callback2.isLoadSdkSuccessful()).isTrue();
+ }
+
+ private void killSandboxAndWaitForDeath() throws Exception {
+ // TODO(b/241542162): Avoid using reflection as a workaround once test apis can be run
+ // without issue.
+ mSdkSandboxManager.getClass().getMethod("stopSdkSandbox").invoke(mSdkSandboxManager);
+ Thread.sleep(1000);
+ }
}
diff --git a/sdksandbox/tests/hostsidetests/Android.bp b/sdksandbox/tests/hostsidetests/Android.bp
index a651d38..2f41daf 100644
--- a/sdksandbox/tests/hostsidetests/Android.bp
+++ b/sdksandbox/tests/hostsidetests/Android.bp
@@ -17,18 +17,6 @@
}
java_test_host {
- name: "SdkSandboxHostTest",
- srcs: ["src/**/SdkSandboxHostTest.java"],
- libs: ["tradefed"],
- test_suites: ["general-tests"],
- data: [
- ":TestCodeProvider",
- ":TestCodeProvider2",
- ":SdkSandboxTestApp",
- ],
-}
-
-java_test_host {
name: "SdkSandboxLifecycleHostTest",
srcs: [
"src/**/SdkSandboxLifecycleHostTest.java",
@@ -71,14 +59,12 @@
platform_apis: true,
srcs: [
"app/src/**/SdkSandboxTestActivity.java",
- "app/src/**/SdkSandboxTestApp.java",
":framework-sdksandbox-sources",
":sdksandbox_aidl",
":sdksandbox-sources",
],
static_libs: [
"androidx.core_core",
- "compatibility-device-util-axt",
"SdkSandboxTestUtils",
],
}
diff --git a/sdksandbox/tests/hostsidetests/AndroidTest.xml b/sdksandbox/tests/hostsidetests/AndroidTest.xml
deleted file mode 100644
index 705f953..0000000
--- a/sdksandbox/tests/hostsidetests/AndroidTest.xml
+++ /dev/null
@@ -1,47 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2021 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.
- -->
-<configuration description="Runs sdk sandbox host tests">
- <option name="test-suite-tag" value="SdkSandboxHostTest" />
-
- <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
- <option name="cleanup-apks" value="true" />
- <option name="test-file-name" value="TestCodeProvider.apk" />
- </target_preparer>
-
- <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
- <option name="cleanup-apks" value="true" />
- <option name="test-file-name" value="TestCodeProvider2.apk" />
- </target_preparer>
-
- <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
- <option name="cleanup-apks" value="true" />
- <option name="test-file-name" value="SdkSandboxTestApp.apk" />
- </target_preparer>
-
- <test class="com.android.tradefed.testtype.HostTest" >
- <option name="class"
- value="com.android.tests.sdksandbox.host.SdkSandboxHostTest" />
- </test>
-
- <object type="module_controller"
- class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController" >
- <option name="mainline-module-package-name" value="com.google.android.adservices" />
- </object>
-
- <option name="config-descriptor:metadata" key="mainline-param"
- value="com.google.android.adservices.apex" />
-</configuration>
\ No newline at end of file
diff --git a/sdksandbox/tests/hostsidetests/app/SdkSandboxTestAppManifest.xml b/sdksandbox/tests/hostsidetests/app/SdkSandboxTestAppManifest.xml
index 82eff93..e2ef2ac 100644
--- a/sdksandbox/tests/hostsidetests/app/SdkSandboxTestAppManifest.xml
+++ b/sdksandbox/tests/hostsidetests/app/SdkSandboxTestAppManifest.xml
@@ -40,7 +40,4 @@
/>
</application>
- <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
- android:targetPackage="com.android.sdksandbox.app"
- android:label="SdkSandboxtestApp"/>
</manifest>
diff --git a/sdksandbox/tests/hostsidetests/app/src/com/android/sdksandbox/app/SdkSandboxTestApp.java b/sdksandbox/tests/hostsidetests/app/src/com/android/sdksandbox/app/SdkSandboxTestApp.java
deleted file mode 100644
index 9c79a72..0000000
--- a/sdksandbox/tests/hostsidetests/app/src/com/android/sdksandbox/app/SdkSandboxTestApp.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * 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.sdksandbox.app;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.app.sdksandbox.SdkSandboxManager;
-import android.app.sdksandbox.testutils.FakeLoadSdkCallback;
-import android.os.Bundle;
-
-import androidx.test.core.app.ApplicationProvider;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-@RunWith(JUnit4.class)
-public class SdkSandboxTestApp {
-
- private static final String SDK_NAME = "com.android.testcode";
- private static final String SDK_2_NAME = "com.android.testcode2";
-
- private SdkSandboxManager mSdkSandboxManager;
-
- @Before
- public void setup() {
- mSdkSandboxManager =
- ApplicationProvider.getApplicationContext()
- .getSystemService(SdkSandboxManager.class);
- assertThat(mSdkSandboxManager).isNotNull();
- }
-
- @Test
- public void testLoadMultipleSdks() throws Exception {
- FakeLoadSdkCallback callback = new FakeLoadSdkCallback();
- mSdkSandboxManager.loadSdk(SDK_NAME, new Bundle(), Runnable::run, callback);
- assertThat(callback.isLoadSdkSuccessful()).isTrue();
-
- FakeLoadSdkCallback callback2 = new FakeLoadSdkCallback();
- mSdkSandboxManager.loadSdk(SDK_2_NAME, new Bundle(), Runnable::run, callback2);
- assertThat(callback2.isLoadSdkSuccessful()).isTrue();
- }
-}
diff --git a/sdksandbox/tests/hostsidetests/src/com/android/tests/sdksandbox/host/SdkSandboxHostTest.java b/sdksandbox/tests/hostsidetests/src/com/android/tests/sdksandbox/host/SdkSandboxHostTest.java
deleted file mode 100644
index a2bfc99..0000000
--- a/sdksandbox/tests/hostsidetests/src/com/android/tests/sdksandbox/host/SdkSandboxHostTest.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.tests.sdksandbox.host;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
-import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@RunWith(DeviceJUnit4ClassRunner.class)
-public final class SdkSandboxHostTest extends BaseHostJUnit4Test {
-
- private static final String APP_PACKAGE = "com.android.sdksandbox.app";
- private static final String APP_TEST_CLASS = APP_PACKAGE + ".SdkSandboxTestApp";
-
- private static final String SDK_APK = "TestCodeProvider.apk";
-
- /**
- * Runs the given phase of a test by calling into the device. Throws an exception if the test
- * phase fails.
- *
- * <p>For example, <code>runPhase("testExample");</code>
- */
- private void runPhase(String phase) throws Exception {
- assertThat(runDeviceTests(APP_PACKAGE, APP_TEST_CLASS, phase)).isTrue();
- }
-
- @Before
- public void setUp() throws Exception {
- assertThat(getBuild()).isNotNull();
- assertThat(getDevice()).isNotNull();
-
- // Ensure the app is not currently running
- getDevice().executeShellCommand(String.format("pm clear %s", APP_PACKAGE));
-
- // Workaround for autoTeardown which removes packages installed in test
- if (!isPackageInstalled(SDK_APK)) {
- installPackage(SDK_APK, "-d");
- }
- }
-
- @Test
- public void testReloadingSdkAfterKillingSandboxIsSuccessful() throws Exception {
- // Have the app load multiple SDKs and bring up the sandbox
- runPhase("testLoadMultipleSdks");
-
- final String sandboxProcessName = APP_PACKAGE + "_sdk_sandbox";
- final String sandboxPid =
- getDevice().executeShellCommand(String.format("pidof -s %s", sandboxProcessName));
- // Kill the sandbox
- getDevice().executeShellCommand(String.format("kill -9 %s", sandboxPid));
- String processDump = getDevice().executeAdbCommand("shell", "ps", "-A");
- assertThat(processDump).doesNotContain(sandboxProcessName);
-
- // Loading both SDKs again should succeed
- runPhase("testLoadMultipleSdks");
- }
-}
diff --git a/sdksandbox/tests/testutils/src/android/app/sdksandbox/testutils/StubSdkSandboxManagerService.java b/sdksandbox/tests/testutils/src/android/app/sdksandbox/testutils/StubSdkSandboxManagerService.java
index 26e2cdf..2a84dad 100644
--- a/sdksandbox/tests/testutils/src/android/app/sdksandbox/testutils/StubSdkSandboxManagerService.java
+++ b/sdksandbox/tests/testutils/src/android/app/sdksandbox/testutils/StubSdkSandboxManagerService.java
@@ -23,6 +23,7 @@
import android.content.pm.SharedLibraryInfo;
import android.os.Bundle;
import android.os.IBinder;
+import android.os.RemoteException;
import java.util.Collections;
import java.util.List;
@@ -62,5 +63,8 @@
}
@Override
+ public void stopSdkSandbox(String callingPackageName) throws RemoteException {}
+
+ @Override
public void syncDataFromClient(String callingPackageName, Bundle data) {}
}
diff --git a/sdksandbox/tests/unittest/src/com/android/server/sdksandbox/SdkSandboxManagerServiceUnitTest.java b/sdksandbox/tests/unittest/src/com/android/server/sdksandbox/SdkSandboxManagerServiceUnitTest.java
index bcd2b54..4e86d9f 100644
--- a/sdksandbox/tests/unittest/src/com/android/server/sdksandbox/SdkSandboxManagerServiceUnitTest.java
+++ b/sdksandbox/tests/unittest/src/com/android/server/sdksandbox/SdkSandboxManagerServiceUnitTest.java
@@ -683,6 +683,33 @@
assertThat(mSdkSandboxService.getLastUpdate()).isSameInstanceAs(data);
}
+ @Test
+ public void testStopSdkSandbox() throws Exception {
+ disableKillUid();
+ disableNetworkPermissionChecks();
+
+ FakeLoadSdkCallbackBinder callback = new FakeLoadSdkCallbackBinder();
+ mService.loadSdk(TEST_PACKAGE, SDK_NAME, new Bundle(), callback);
+ // Assume sandbox loads successfully
+ mSdkSandboxService.sendLoadCodeSuccessful();
+ assertThat(callback.isLoadSdkSuccessful()).isTrue();
+
+ 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(mProvider.getBoundServiceForApp(callingInfo)).isEqualTo(null);
+ }
+
+ @Test(expected = SecurityException.class)
+ public void testStopSdkSandbox_WithoutPermission() {
+ mService.stopSdkSandbox(TEST_PACKAGE);
+ }
+
/**
* Fake service provider that returns local instance of {@link SdkSandboxServiceProvider}
*/