summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--services/tests/servicestests/AndroidTest.xml1
-rw-r--r--services/tests/servicestests/src/com/android/server/am/ActivityManagerTest.java132
-rw-r--r--services/tests/servicestests/test-apps/SimpleServiceTestApp/Android.bp30
-rw-r--r--services/tests/servicestests/test-apps/SimpleServiceTestApp/AndroidManifest.xml25
-rw-r--r--services/tests/servicestests/test-apps/SimpleServiceTestApp/src/com/android/servicestests/apps/simpleservicetestapp/SimpleService.java37
5 files changed, 225 insertions, 0 deletions
diff --git a/services/tests/servicestests/AndroidTest.xml b/services/tests/servicestests/AndroidTest.xml
index 4d653b99439c..d34f78353e71 100644
--- a/services/tests/servicestests/AndroidTest.xml
+++ b/services/tests/servicestests/AndroidTest.xml
@@ -23,6 +23,7 @@
<option name="test-file-name" value="JobTestApp.apk" />
<option name="test-file-name" value="ConnTestApp.apk" />
<option name="test-file-name" value="SuspendTestApp.apk" />
+ <option name="test-file-name" value="SimpleServiceTestApp.apk" />
</target_preparer>
<option name="test-tag" value="FrameworksServicesTests" />
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityManagerTest.java b/services/tests/servicestests/src/com/android/server/am/ActivityManagerTest.java
index 8965152257c6..129d2633ae92 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityManagerTest.java
@@ -19,20 +19,34 @@ package com.android.server.am;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import android.app.ActivityManager;
import android.app.ActivityManager.RecentTaskInfo;
import android.app.IActivityManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.pm.PackageManager;
+import android.os.IBinder;
+import android.os.IRemoteCallback;
import android.os.RemoteException;
import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
+import android.support.test.uiautomator.UiDevice;
+import androidx.test.InstrumentationRegistry;
import androidx.test.filters.FlakyTest;
import org.junit.Before;
import org.junit.Test;
+import java.io.IOException;
import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
/**
* Tests for {@link ActivityManager}.
@@ -43,12 +57,22 @@ import java.util.List;
@FlakyTest(detail = "Promote to presubmit if stable")
@Presubmit
public class ActivityManagerTest {
+ private static final String TAG = "ActivityManagerTest";
+
+ private static final String TEST_APP = "com.android.servicestests.apps.simpleservicetestapp";
+ private static final String TEST_CLASS = TEST_APP + ".SimpleService";
+ private static final int TEST_LOOPS = 100;
+ private static final long AWAIT_TIMEOUT = 2000;
+ private static final long CHECK_INTERVAL = 100;
private IActivityManager mService;
+ private IRemoteCallback mCallback;
+ private Context mContext;
@Before
public void setUp() throws Exception {
mService = ActivityManager.getService();
+ mContext = InstrumentationRegistry.getTargetContext();
}
@Test
@@ -72,4 +96,112 @@ public class ActivityManagerTest {
}
}
}
+
+ @Test
+ public void testServiceUnbindAndKilling() {
+ for (int i = TEST_LOOPS; i > 0; i--) {
+ runOnce(i);
+ }
+ }
+
+ private void runOnce(long yieldDuration) {
+ final PackageManager pm = mContext.getPackageManager();
+ int uid = 0;
+ try {
+ uid = pm.getPackageUid(TEST_APP, 0);
+ } catch (PackageManager.NameNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+
+ Intent intent = new Intent();
+ intent.setClassName(TEST_APP, TEST_CLASS);
+
+ // Create a service connection with auto creation.
+ CountDownLatch latch = new CountDownLatch(1);
+ final MyServiceConnection autoConnection = new MyServiceConnection(latch);
+ mContext.bindService(intent, autoConnection, Context.BIND_AUTO_CREATE);
+ try {
+ assertTrue("Timeout to bind to service " + intent.getComponent(),
+ latch.await(AWAIT_TIMEOUT, TimeUnit.MILLISECONDS));
+ } catch (InterruptedException e) {
+ fail("Unable to bind to service " + intent.getComponent());
+ }
+
+ // Create a service connection without any flags.
+ intent = new Intent();
+ intent.setClassName(TEST_APP, TEST_CLASS);
+ latch = new CountDownLatch(1);
+ MyServiceConnection otherConnection = new MyServiceConnection(latch);
+ mContext.bindService(intent, otherConnection, 0);
+ try {
+ assertTrue("Timeout to bind to service " + intent.getComponent(),
+ latch.await(AWAIT_TIMEOUT, TimeUnit.MILLISECONDS));
+ } catch (InterruptedException e) {
+ fail("Unable to bind to service " + intent.getComponent());
+ }
+
+ // Inform the remote process to kill itself
+ try {
+ mCallback.sendResult(null);
+ // It's basically a test for race condition, we expect the bringDownServiceLocked()
+ // would find out the hosting process is dead - to do this, technically we should
+ // do killing and unbinding simultaneously; but in reality, the killing would take
+ // a little while, before the signal really kills it; so we do it in the same thread,
+ // and even wait a while after sending killing signal.
+ Thread.sleep(yieldDuration);
+ } catch (RemoteException | InterruptedException e) {
+ fail("Unable to kill the process");
+ }
+ // Now unbind that auto connection, this should be equivalent to stopService
+ mContext.unbindService(autoConnection);
+
+ // Now we don't expect the system_server crashes.
+
+ // Wait for the target process dies
+ long total = 0;
+ for (; total < AWAIT_TIMEOUT; total += CHECK_INTERVAL) {
+ try {
+ if (!targetPackageIsRunning(mContext, uid)) {
+ break;
+ }
+ Thread.sleep(CHECK_INTERVAL);
+ } catch (InterruptedException e) {
+ }
+ }
+ assertTrue("Timeout to wait for the target package dies", total < AWAIT_TIMEOUT);
+ mCallback = null;
+ }
+
+ private boolean targetPackageIsRunning(Context context, int uid) {
+ final String result = runShellCommand(
+ String.format("cmd activity get-uid-state %d", uid));
+ return !result.contains("(NONEXISTENT)");
+ }
+
+ private static String runShellCommand(String cmd) {
+ try {
+ return UiDevice.getInstance(
+ InstrumentationRegistry.getInstrumentation()).executeShellCommand(cmd);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private class MyServiceConnection implements ServiceConnection {
+ private CountDownLatch mLatch;
+
+ MyServiceConnection(CountDownLatch latch) {
+ this.mLatch = latch;
+ }
+
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ mCallback = IRemoteCallback.Stub.asInterface(service);
+ mLatch.countDown();
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ }
+ }
}
diff --git a/services/tests/servicestests/test-apps/SimpleServiceTestApp/Android.bp b/services/tests/servicestests/test-apps/SimpleServiceTestApp/Android.bp
new file mode 100644
index 000000000000..5cbd39c39434
--- /dev/null
+++ b/services/tests/servicestests/test-apps/SimpleServiceTestApp/Android.bp
@@ -0,0 +1,30 @@
+// Copyright (C) 2019 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.
+
+android_test_helper_app {
+ name: "SimpleServiceTestApp",
+
+ test_suites: ["device-tests"],
+
+ srcs: ["**/*.java"],
+
+ platform_apis: true,
+ certificate: "platform",
+ dex_preopt: {
+ enabled: false,
+ },
+ optimize: {
+ enabled: false,
+ },
+}
diff --git a/services/tests/servicestests/test-apps/SimpleServiceTestApp/AndroidManifest.xml b/services/tests/servicestests/test-apps/SimpleServiceTestApp/AndroidManifest.xml
new file mode 100644
index 000000000000..8789992280d0
--- /dev/null
+++ b/services/tests/servicestests/test-apps/SimpleServiceTestApp/AndroidManifest.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.servicestests.apps.simpleservicetestapp">
+
+ <application>
+ <service android:name=".SimpleService"
+ android:exported="true" />
+ </application>
+
+</manifest>
diff --git a/services/tests/servicestests/test-apps/SimpleServiceTestApp/src/com/android/servicestests/apps/simpleservicetestapp/SimpleService.java b/services/tests/servicestests/test-apps/SimpleServiceTestApp/src/com/android/servicestests/apps/simpleservicetestapp/SimpleService.java
new file mode 100644
index 000000000000..75f71d609a13
--- /dev/null
+++ b/services/tests/servicestests/test-apps/SimpleServiceTestApp/src/com/android/servicestests/apps/simpleservicetestapp/SimpleService.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2019 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.servicestests.apps.simpleservicetestapp;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.IRemoteCallback;
+import android.os.Process;
+
+public class SimpleService extends Service {
+ private final IRemoteCallback.Stub mBinder = new IRemoteCallback.Stub() {
+ @Override
+ public void sendResult(Bundle bundle) {
+ Process.killProcess(Process.myPid());
+ }
+ };
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return mBinder;
+ }
+}