blob: 10f4c05eb6d8b15247480e9725acab3d98c9a104 [file] [log] [blame]
/*
* 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.
*/
package com.android.server.am;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import android.app.ActivityManager;
import android.app.ActivityManager.OnUidImportanceListener;
import android.app.ActivityManager.RunningAppProcessInfo;
import android.app.Instrumentation;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.os.SystemClock;
import android.test.suitebuilder.annotation.LargeTest;
import android.util.Log;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
import com.android.compatibility.common.util.SystemUtil;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
/**
* Build/Install/Run:
* atest FrameworksServicesTests:ServiceRestarterTest
*/
@RunWith(AndroidJUnit4.class)
public final class ServiceRestarterTest {
private static final String TAG = "ServiceRestarterTest";
private static final String TEST_PACKAGE1_NAME =
"com.android.servicestests.apps.simpleservicetestapp1";
private static final String TEST_PACKAGE2_NAME =
"com.android.servicestests.apps.simpleservicetestapp2";
private static final String TEST_PACKAGE3_NAME =
"com.android.servicestests.apps.simpleservicetestapp3";
private static final String TEST_SERVICE_NAME =
"com.android.servicestests.apps.simpleservicetestapp.SimpleService";
private static final long WAIT_MS = 5 * 1000;
private static final long WAIT_LONG_MS = 30 * 1000;
private static final int ACTION_START = 1;
private static final int ACTION_KILL = 2;
private static final int ACTION_WAIT = 4;
private static final int ACTION_STOPPKG = 8;
private static final int ACTION_ALL = ACTION_START | ACTION_KILL | ACTION_WAIT | ACTION_STOPPKG;
private Context mContext;
private Instrumentation mInstrumentation;
private int mTestPackage1Uid;
private int mTestPackage2Uid;
private int mTestPackage3Uid;
@Before
public void setUp() throws Exception {
mInstrumentation = InstrumentationRegistry.getInstrumentation();
mContext = InstrumentationRegistry.getContext();
ApplicationInfo ai = mContext.getPackageManager().getApplicationInfo(TEST_PACKAGE1_NAME, 0);
mTestPackage1Uid = ai.uid;
ai = mContext.getPackageManager().getApplicationInfo(TEST_PACKAGE2_NAME, 0);
mTestPackage2Uid = ai.uid;
ai = mContext.getPackageManager().getApplicationInfo(TEST_PACKAGE3_NAME, 0);
mTestPackage3Uid = ai.uid;
}
@LargeTest
@Test
public void testDisableServiceRestartBackoff() throws Exception {
final ActivityManager am = mContext.getSystemService(ActivityManager.class);
final MyUidImportanceListener uid1Listener1 = new MyUidImportanceListener(mTestPackage1Uid);
final MyUidImportanceListener uid1Listener2 = new MyUidImportanceListener(mTestPackage1Uid);
final MyUidImportanceListener uid2Listener1 = new MyUidImportanceListener(mTestPackage2Uid);
final MyUidImportanceListener uid2Listener2 = new MyUidImportanceListener(mTestPackage2Uid);
final MyUidImportanceListener uid3Listener1 = new MyUidImportanceListener(mTestPackage3Uid);
final MyUidImportanceListener uid3Listener2 = new MyUidImportanceListener(mTestPackage3Uid);
try {
am.addOnUidImportanceListener(uid1Listener1, RunningAppProcessInfo.IMPORTANCE_SERVICE);
am.addOnUidImportanceListener(uid1Listener2, RunningAppProcessInfo.IMPORTANCE_GONE);
am.addOnUidImportanceListener(uid2Listener1, RunningAppProcessInfo.IMPORTANCE_SERVICE);
am.addOnUidImportanceListener(uid2Listener2, RunningAppProcessInfo.IMPORTANCE_GONE);
am.addOnUidImportanceListener(uid3Listener1, RunningAppProcessInfo.IMPORTANCE_SERVICE);
am.addOnUidImportanceListener(uid3Listener2, RunningAppProcessInfo.IMPORTANCE_GONE);
executeShellCmd("cmd deviceidle whitelist +" + TEST_PACKAGE1_NAME);
executeShellCmd("cmd deviceidle whitelist +" + TEST_PACKAGE2_NAME);
executeShellCmd("cmd deviceidle whitelist +" + TEST_PACKAGE3_NAME);
// Issue the command to enable service backoff policy for app2.
executeShellCmd("am service-restart-backoff enable " + TEST_PACKAGE2_NAME);
// Test restarts in normal case
final long[] ts1 = startKillAndRestart(am, ACTION_START | ACTION_KILL | ACTION_WAIT,
uid1Listener1, uid1Listener2, uid2Listener1, uid2Listener2,
uid3Listener1, uid3Listener2, Long.MAX_VALUE);
assertTrue("app1 restart should be before app2", ts1[1] < ts1[2]);
assertTrue("app2 restart should be before app3", ts1[2] < ts1[3]);
// Issue the command to disable service backoff policy for app2.
executeShellCmd("am service-restart-backoff disable " + TEST_PACKAGE2_NAME);
// Test restarts again.
final long[] ts2 = startKillAndRestart(am, ACTION_KILL | ACTION_WAIT | ACTION_STOPPKG,
uid1Listener1, uid1Listener2, uid2Listener1,
uid2Listener2, uid3Listener1, uid3Listener2, Long.MAX_VALUE);
assertTrue("app2 restart should be before app1", ts2[2] < ts2[1]);
assertTrue("app1 restart should be before app3", ts2[1] < ts2[3]);
assertTrue("app2 should be restart in a very short moment", ts2[2] - ts2[0] < WAIT_MS);
// Issue the command to enable service backoff policy for app2.
executeShellCmd("am service-restart-backoff enable " + TEST_PACKAGE2_NAME);
// Test restarts again.
final long[] ts3 = startKillAndRestart(am, ACTION_ALL, uid1Listener1, uid1Listener2,
uid2Listener1, uid2Listener2, uid3Listener1, uid3Listener2, Long.MAX_VALUE);
assertTrue("app1 restart should be before app2", ts3[1] < ts3[2]);
assertTrue("app2 restart should be before app3", ts3[2] < ts3[3]);
} finally {
executeShellCmd("cmd deviceidle whitelist -" + TEST_PACKAGE1_NAME);
executeShellCmd("cmd deviceidle whitelist -" + TEST_PACKAGE2_NAME);
executeShellCmd("cmd deviceidle whitelist -" + TEST_PACKAGE3_NAME);
executeShellCmd("am service-restart-backoff enable " + TEST_PACKAGE2_NAME);
am.removeOnUidImportanceListener(uid1Listener1);
am.removeOnUidImportanceListener(uid1Listener2);
am.removeOnUidImportanceListener(uid2Listener1);
am.removeOnUidImportanceListener(uid2Listener2);
am.removeOnUidImportanceListener(uid3Listener1);
am.removeOnUidImportanceListener(uid3Listener2);
am.forceStopPackage(TEST_PACKAGE1_NAME);
am.forceStopPackage(TEST_PACKAGE2_NAME);
am.forceStopPackage(TEST_PACKAGE3_NAME);
}
}
private long[] startKillAndRestart(ActivityManager am, int action,
MyUidImportanceListener uid1Listener1, MyUidImportanceListener uid1Listener2,
MyUidImportanceListener uid2Listener1, MyUidImportanceListener uid2Listener2,
MyUidImportanceListener uid3Listener1, MyUidImportanceListener uid3Listener2,
long waitDuration) throws Exception {
final long[] res = new long[4];
// Test restarts in normal condition.
if ((action & ACTION_START) != 0) {
startServiceAndWait(TEST_PACKAGE1_NAME, uid1Listener1, WAIT_MS);
startServiceAndWait(TEST_PACKAGE2_NAME, uid2Listener1, WAIT_MS);
startServiceAndWait(TEST_PACKAGE3_NAME, uid3Listener1, WAIT_MS);
}
if ((action & ACTION_KILL) != 0) {
final long now = res[0] = SystemClock.uptimeMillis();
killUidAndWait(am, mTestPackage1Uid, uid1Listener2, WAIT_MS);
killUidAndWait(am, mTestPackage2Uid, uid2Listener2, WAIT_MS);
killUidAndWait(am, mTestPackage3Uid, uid3Listener2, WAIT_MS);
}
if ((action & ACTION_WAIT) != 0) {
assertTrue("Timed out to restart " + TEST_PACKAGE1_NAME, uid1Listener1.waitFor(
RunningAppProcessInfo.IMPORTANCE_SERVICE, waitDuration));
assertTrue("Timed out to restart " + TEST_PACKAGE2_NAME, uid2Listener1.waitFor(
RunningAppProcessInfo.IMPORTANCE_SERVICE, waitDuration));
assertTrue("Timed out to restart " + TEST_PACKAGE3_NAME, uid3Listener1.waitFor(
RunningAppProcessInfo.IMPORTANCE_SERVICE, waitDuration));
res[1] = uid1Listener1.mCurrentTimestamp;
res[2] = uid2Listener1.mCurrentTimestamp;
res[3] = uid3Listener1.mCurrentTimestamp;
}
if ((action & ACTION_STOPPKG) != 0) {
// Force stop these packages to reset the backoff delays.
am.forceStopPackage(TEST_PACKAGE1_NAME);
am.forceStopPackage(TEST_PACKAGE2_NAME);
am.forceStopPackage(TEST_PACKAGE3_NAME);
assertTrue("Timed out to force-stop " + mTestPackage1Uid,
uid1Listener2.waitFor(RunningAppProcessInfo.IMPORTANCE_GONE, WAIT_MS));
assertTrue("Timed out to force-stop " + mTestPackage2Uid,
uid2Listener2.waitFor(RunningAppProcessInfo.IMPORTANCE_GONE, WAIT_MS));
assertTrue("Timed out to force-stop " + mTestPackage3Uid,
uid3Listener2.waitFor(RunningAppProcessInfo.IMPORTANCE_GONE, WAIT_MS));
}
return res;
}
private void startServiceAndWait(String pkgName, MyUidImportanceListener uidListener,
long timeout) throws Exception {
final Intent intent = new Intent();
final ComponentName cn = ComponentName.unflattenFromString(
pkgName + "/" + TEST_SERVICE_NAME);
intent.setComponent(cn);
Log.i(TAG, "Starting service " + cn);
assertNotNull(mContext.startService(intent));
assertTrue("Timed out to start service " + cn,
uidListener.waitFor(RunningAppProcessInfo.IMPORTANCE_SERVICE, timeout));
}
private void killUidAndWait(ActivityManager am, int uid, MyUidImportanceListener uidListener,
long timeout) throws Exception {
am.killUid(uid, "test service restart");
assertTrue("Timed out to kill " + uid,
uidListener.waitFor(RunningAppProcessInfo.IMPORTANCE_GONE, timeout));
}
private String executeShellCmd(String cmd) throws Exception {
final String result = SystemUtil.runShellCommand(mInstrumentation, cmd);
Log.d(TAG, String.format("Output for '%s': %s", cmd, result));
return result;
}
private static class MyUidImportanceListener implements OnUidImportanceListener {
final CountDownLatch[] mLatchHolder = new CountDownLatch[1];
private final int mExpectedUid;
private int mExpectedImportance;
private int mCurrentImportance = RunningAppProcessInfo.IMPORTANCE_GONE;
long mCurrentTimestamp;
MyUidImportanceListener(int uid) {
mExpectedUid = uid;
}
@Override
public void onUidImportance(int uid, int importance) {
if (uid == mExpectedUid) {
mCurrentTimestamp = SystemClock.uptimeMillis();
synchronized (this) {
if (importance == mExpectedImportance && mLatchHolder[0] != null) {
mLatchHolder[0].countDown();
}
mCurrentImportance = importance;
}
Log.i(TAG, "uid " + uid + " importance: " + importance);
}
}
boolean waitFor(int expectedImportance, long timeout) throws Exception {
synchronized (this) {
mExpectedImportance = expectedImportance;
if (mCurrentImportance == expectedImportance) {
return true;
}
mLatchHolder[0] = new CountDownLatch(1);
}
return mLatchHolder[0].await(timeout, TimeUnit.MILLISECONDS);
}
}
}