blob: e36639aed2ca7c25bc02133d87ac40c30a05e47d [file] [log] [blame]
/*
* Copyright (C) 2017 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 android.app.cts;
import android.Manifest;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.Instrumentation;
import android.app.cts.android.app.cts.tools.ServiceConnectionHandler;
import android.app.cts.android.app.cts.tools.ServiceProcessController;
import android.app.cts.android.app.cts.tools.SyncOrderedBroadcast;
import android.app.cts.android.app.cts.tools.UidImportanceListener;
import android.app.cts.android.app.cts.tools.WaitForBroadcast;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.os.IBinder;
import android.os.Parcel;
import android.os.RemoteException;
import android.test.InstrumentationTestCase;
import com.android.compatibility.common.util.SystemUtil;
public class ActivityManagerProcessStateTest extends InstrumentationTestCase {
private static final String STUB_PACKAGE_NAME = "android.app.stubs";
private static final int WAIT_TIME = 2000;
// A secondary test activity from another APK.
static final String SIMPLE_PACKAGE_NAME = "com.android.cts.launcherapps.simpleapp";
static final String SIMPLE_SERVICE = ".SimpleService";
static final String SIMPLE_SERVICE2 = ".SimpleService2";
static final String SIMPLE_RECEIVER_START_SERVICE = ".SimpleReceiverStartService";
static final String SIMPLE_ACTIVITY_START_SERVICE = ".SimpleActivityStartService";
public static String ACTION_SIMPLE_ACTIVITY_START_SERVICE_RESULT =
"com.android.cts.launcherapps.simpleapp.SimpleActivityStartService.RESULT";
private Context mContext;
private Instrumentation mInstrumentation;
private Intent mServiceIntent;
private Intent mService2Intent;
private Intent mAllProcesses[];
@Override
protected void setUp() throws Exception {
super.setUp();
mInstrumentation = getInstrumentation();
mContext = mInstrumentation.getContext();
mServiceIntent = new Intent();
mServiceIntent.setClassName(SIMPLE_PACKAGE_NAME, SIMPLE_PACKAGE_NAME + SIMPLE_SERVICE);
mService2Intent = new Intent();
mService2Intent.setClassName(SIMPLE_PACKAGE_NAME, SIMPLE_PACKAGE_NAME + SIMPLE_SERVICE2);
mAllProcesses = new Intent[2];
mAllProcesses[0] = mServiceIntent;
mAllProcesses[1] = mService2Intent;
mContext.stopService(mServiceIntent);
mContext.stopService(mService2Intent);
}
@Override
protected void tearDown() throws Exception {
super.tearDown();
}
public void testUidImportanceListener() throws Exception {
final Parcel data = Parcel.obtain();
ServiceConnectionHandler conn = new ServiceConnectionHandler(mContext, mServiceIntent);
ServiceConnectionHandler conn2 = new ServiceConnectionHandler(mContext, mService2Intent);
ActivityManager am = mContext.getSystemService(ActivityManager.class);
ApplicationInfo appInfo = mContext.getPackageManager().getApplicationInfo(
SIMPLE_PACKAGE_NAME, 0);
UidImportanceListener uidForegroundListener = new UidImportanceListener(appInfo.uid);
String cmd = "pm revoke " + STUB_PACKAGE_NAME + " "
+ Manifest.permission.PACKAGE_USAGE_STATS;
String result = SystemUtil.runShellCommand(getInstrumentation(), cmd);
/*
Log.d("XXXX", "Invoke: " + cmd);
Log.d("XXXX", "Result: " + result);
*/
boolean gotException = false;
try {
am.addOnUidImportanceListener(uidForegroundListener,
ActivityManager.RunningAppProcessInfo.IMPORTANCE_SERVICE);
} catch (SecurityException e) {
gotException = true;
}
assertTrue("Expected SecurityException thrown", gotException);
cmd = "pm grant " + STUB_PACKAGE_NAME + " "
+ Manifest.permission.PACKAGE_USAGE_STATS;
result = SystemUtil.runShellCommand(getInstrumentation(), cmd);
/*
Log.d("XXXX", "Invoke: " + cmd);
Log.d("XXXX", "Result: " + result);
Log.d("XXXX", SystemUtil.runShellCommand(getInstrumentation(), "dumpsys package "
+ STUB_PACKAGE_NAME));
*/
am.addOnUidImportanceListener(uidForegroundListener,
ActivityManager.RunningAppProcessInfo.IMPORTANCE_SERVICE);
UidImportanceListener uidGoneListener = new UidImportanceListener(appInfo.uid);
am.addOnUidImportanceListener(uidGoneListener,
ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED);
// First kill the processes to start out in a stable state.
conn.bind(WAIT_TIME);
conn2.bind(WAIT_TIME);
try {
conn.getServiceIBinder().transact(IBinder.FIRST_CALL_TRANSACTION, data, null, 0);
} catch (RemoteException e) {
}
try {
conn2.getServiceIBinder().transact(IBinder.FIRST_CALL_TRANSACTION, data, null, 0);
} catch (RemoteException e) {
}
conn.unbind(WAIT_TIME);
conn2.unbind(WAIT_TIME);
// Wait for uid's processes to go away.
uidGoneListener.waitForValue(ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE,
ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE, WAIT_TIME);
assertEquals(ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE,
am.getPackageImportance(SIMPLE_PACKAGE_NAME));
// Now bind and see if we get told about the uid coming in to the foreground.
conn.bind(WAIT_TIME);
uidForegroundListener.waitForValue(ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND,
ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE, WAIT_TIME);
assertEquals(ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE,
am.getPackageImportance(SIMPLE_PACKAGE_NAME));
// Pull out the service IBinder for a kludy hack...
IBinder service = conn.getServiceIBinder();
// Now unbind and see if we get told about it going to the background.
conn.unbind(WAIT_TIME);
uidForegroundListener.waitForValue(ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED,
ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED, WAIT_TIME);
assertEquals(ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED,
am.getPackageImportance(SIMPLE_PACKAGE_NAME));
// Now kill the process and see if we are told about it being gone.
try {
service.transact(IBinder.FIRST_CALL_TRANSACTION, data, null, 0);
} catch (RemoteException e) {
// It is okay if it is already gone for some reason.
}
uidGoneListener.waitForValue(ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE,
ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE, WAIT_TIME);
assertEquals(ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE,
am.getPackageImportance(SIMPLE_PACKAGE_NAME));
// Now we are going to try different combinations of binding to two processes to
// see if they are correctly combined together for the app.
// Bring up both services.
conn.bind(WAIT_TIME);
conn2.bind(WAIT_TIME);
uidForegroundListener.waitForValue(ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND,
ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE, WAIT_TIME);
assertEquals(ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE,
am.getPackageImportance(SIMPLE_PACKAGE_NAME));
// Bring down one service, app state should remain foreground.
conn2.unbind(WAIT_TIME);
assertEquals(ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE,
am.getPackageImportance(SIMPLE_PACKAGE_NAME));
// Bring down other service, app state should now be cached. (If the processes both
// actually get killed immediately, this is also not a correctly behaving system.)
conn.unbind(WAIT_TIME);
uidGoneListener.waitForValue(ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED,
ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED, WAIT_TIME);
assertEquals(ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED,
am.getPackageImportance(SIMPLE_PACKAGE_NAME));
// Bring up one service, this should be sufficient to become foreground.
conn2.bind(WAIT_TIME);
uidForegroundListener.waitForValue(ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND,
ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE, WAIT_TIME);
assertEquals(ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE,
am.getPackageImportance(SIMPLE_PACKAGE_NAME));
// Bring up other service, should remain foreground.
conn.bind(WAIT_TIME);
assertEquals(ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE,
am.getPackageImportance(SIMPLE_PACKAGE_NAME));
// Bring down one service, should remain foreground.
conn.unbind(WAIT_TIME);
assertEquals(ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE,
am.getPackageImportance(SIMPLE_PACKAGE_NAME));
// And bringing down other service should put us back to cached.
conn2.unbind(WAIT_TIME);
uidGoneListener.waitForValue(ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED,
ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED, WAIT_TIME);
assertEquals(ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED,
am.getPackageImportance(SIMPLE_PACKAGE_NAME));
data.recycle();
am.removeOnUidImportanceListener(uidForegroundListener);
am.removeOnUidImportanceListener(uidGoneListener);
}
public void testBackgroundCheckService() throws Exception {
final Parcel data = Parcel.obtain();
Intent serviceIntent = new Intent();
serviceIntent.setClassName(SIMPLE_PACKAGE_NAME,
SIMPLE_PACKAGE_NAME + SIMPLE_SERVICE);
ServiceConnectionHandler conn = new ServiceConnectionHandler(mContext, serviceIntent);
ActivityManager am = mContext.getSystemService(ActivityManager.class);
String cmd = "pm grant " + STUB_PACKAGE_NAME + " "
+ Manifest.permission.PACKAGE_USAGE_STATS;
String result = SystemUtil.runShellCommand(getInstrumentation(), cmd);
/*
Log.d("XXXX", "Invoke: " + cmd);
Log.d("XXXX", "Result: " + result);
Log.d("XXXX", SystemUtil.runShellCommand(getInstrumentation(), "dumpsys package "
+ STUB_PACKAGE_NAME));
*/
ApplicationInfo appInfo = mContext.getPackageManager().getApplicationInfo(
SIMPLE_PACKAGE_NAME, 0);
UidImportanceListener uidForegroundListener = new UidImportanceListener(appInfo.uid);
am.addOnUidImportanceListener(uidForegroundListener,
ActivityManager.RunningAppProcessInfo.IMPORTANCE_SERVICE);
UidImportanceListener uidGoneListener = new UidImportanceListener(appInfo.uid);
am.addOnUidImportanceListener(uidGoneListener,
ActivityManager.RunningAppProcessInfo.IMPORTANCE_EMPTY);
// First kill the process to start out in a stable state.
mContext.stopService(serviceIntent);
conn.bind(WAIT_TIME);
try {
conn.getServiceIBinder().transact(IBinder.FIRST_CALL_TRANSACTION, data, null, 0);
} catch (RemoteException e) {
}
conn.unbind(WAIT_TIME);
// Wait for uid's process to go away.
uidGoneListener.waitForValue(ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE,
ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE, WAIT_TIME);
assertEquals(ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE,
am.getPackageImportance(SIMPLE_PACKAGE_NAME));
cmd = "appops set " + SIMPLE_PACKAGE_NAME + " RUN_IN_BACKGROUND deny";
result = SystemUtil.runShellCommand(getInstrumentation(), cmd);
// We don't want to wait for the uid to actually go idle, we can force it now.
cmd = "am make-uid-idle " + SIMPLE_PACKAGE_NAME;
result = SystemUtil.runShellCommand(getInstrumentation(), cmd);
// Make sure app is not yet on whitelist
cmd = "cmd deviceidle whitelist -" + SIMPLE_PACKAGE_NAME;
result = SystemUtil.runShellCommand(getInstrumentation(), cmd);
// We will use this to monitor when the service is running.
conn.startMonitoring();
try {
// Try starting the service. Should fail!
boolean failed = false;
try {
mContext.startService(serviceIntent);
} catch (IllegalStateException e) {
failed = true;
}
if (!failed) {
fail("Service was allowed to start while in the background");
}
// Put app on temporary whitelist to see if this allows the service start.
cmd = "cmd deviceidle tempwhitelist -d 2000 " + SIMPLE_PACKAGE_NAME;
result = SystemUtil.runShellCommand(getInstrumentation(), cmd);
// Try starting the service now that the app is whitelisted... should work!
mContext.startService(serviceIntent);
conn.waitForConnect(WAIT_TIME);
// Good, now stop the service and give enough time to get off the temp whitelist.
mContext.stopService(serviceIntent);
conn.waitForDisconnect(WAIT_TIME);
Thread.sleep(3000);
// We don't want to wait for the uid to actually go idle, we can force it now.
cmd = "am make-uid-idle " + SIMPLE_PACKAGE_NAME;
result = SystemUtil.runShellCommand(getInstrumentation(), cmd);
// Now that we should be off the temp whitelist, make sure we again can't start.
failed = false;
try {
mContext.startService(serviceIntent);
} catch (IllegalStateException e) {
failed = true;
}
if (!failed) {
fail("Service was allowed to start while in the background");
}
// Now put app on whitelist, should allow service to run.
cmd = "cmd deviceidle whitelist +" + SIMPLE_PACKAGE_NAME;
result = SystemUtil.runShellCommand(getInstrumentation(), cmd);
// Try starting the service now that the app is whitelisted... should work!
mContext.startService(serviceIntent);
conn.waitForConnect(WAIT_TIME);
// Okay, bring down the service.
mContext.stopService(serviceIntent);
conn.waitForDisconnect(WAIT_TIME);
} finally {
mContext.stopService(serviceIntent);
conn.stopMonitoring();
cmd = "appops set " + SIMPLE_PACKAGE_NAME + " RUN_IN_BACKGROUND allow";
result = SystemUtil.runShellCommand(getInstrumentation(), cmd);
cmd = "cmd deviceidle whitelist -" + SIMPLE_PACKAGE_NAME;
result = SystemUtil.runShellCommand(getInstrumentation(), cmd);
am.removeOnUidImportanceListener(uidGoneListener);
am.removeOnUidImportanceListener(uidForegroundListener);
data.recycle();
}
}
public void testBackgroundCheckBroadcastService() throws Exception {
final Intent broadcastIntent = new Intent();
broadcastIntent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
broadcastIntent.setClassName(SIMPLE_PACKAGE_NAME,
SIMPLE_PACKAGE_NAME + SIMPLE_RECEIVER_START_SERVICE);
final ServiceProcessController controller = new ServiceProcessController(mContext,
getInstrumentation(), STUB_PACKAGE_NAME, mAllProcesses);
final ServiceConnectionHandler conn = new ServiceConnectionHandler(mContext,
mServiceIntent);
// First kill the process to start out in a stable state.
controller.ensureProcessGone(WAIT_TIME);
String cmd = "appops set " + SIMPLE_PACKAGE_NAME + " RUN_IN_BACKGROUND deny";
String result = SystemUtil.runShellCommand(getInstrumentation(), cmd);
// We don't want to wait for the uid to actually go idle, we can force it now.
cmd = "am make-uid-idle " + SIMPLE_PACKAGE_NAME;
result = SystemUtil.runShellCommand(getInstrumentation(), cmd);
// Make sure app is not yet on whitelist
cmd = "cmd deviceidle whitelist -" + SIMPLE_PACKAGE_NAME;
result = SystemUtil.runShellCommand(getInstrumentation(), cmd);
// We will use this to monitor when the service is running.
conn.startMonitoring();
try {
// Try sending broadcast to start the service. Should fail!
SyncOrderedBroadcast br = new SyncOrderedBroadcast();
broadcastIntent.putExtra("service", mServiceIntent);
br.sendAndWait(mContext, broadcastIntent, Activity.RESULT_OK, null, null, WAIT_TIME);
int brCode = br.getReceivedCode();
if (brCode != Activity.RESULT_CANCELED) {
fail("Didn't fail starting service, result=" + brCode);
}
// Put app on temporary whitelist to see if this allows the service start.
cmd = "cmd deviceidle tempwhitelist -d 2000 " + SIMPLE_PACKAGE_NAME;
result = SystemUtil.runShellCommand(getInstrumentation(), cmd);
// Try starting the service now that the app is whitelisted... should work!
br.sendAndWait(mContext, broadcastIntent, Activity.RESULT_OK, null, null, WAIT_TIME);
brCode = br.getReceivedCode();
if (brCode != Activity.RESULT_FIRST_USER) {
fail("Failed starting service, result=" + brCode);
}
conn.waitForConnect(WAIT_TIME);
// Good, now stop the service and give enough time to get off the temp whitelist.
mContext.stopService(mServiceIntent);
conn.waitForDisconnect(WAIT_TIME);
Thread.sleep(3000);
// Make sure the process is gone so we start over fresh.
controller.ensureProcessGone(WAIT_TIME);
// We don't want to wait for the uid to actually go idle, we can force it now.
cmd = "am make-uid-idle " + SIMPLE_PACKAGE_NAME;
result = SystemUtil.runShellCommand(getInstrumentation(), cmd);
// Now that we should be off the temp whitelist, make sure we again can't start.
br.sendAndWait(mContext, broadcastIntent, Activity.RESULT_OK, null, null, WAIT_TIME);
brCode = br.getReceivedCode();
if (brCode != Activity.RESULT_CANCELED) {
fail("Didn't fail starting service, result=" + brCode);
}
// Now put app on whitelist, should allow service to run.
cmd = "cmd deviceidle whitelist +" + SIMPLE_PACKAGE_NAME;
result = SystemUtil.runShellCommand(getInstrumentation(), cmd);
// Try starting the service now that the app is whitelisted... should work!
br.sendAndWait(mContext, broadcastIntent, Activity.RESULT_OK, null, null, WAIT_TIME);
brCode = br.getReceivedCode();
if (brCode != Activity.RESULT_FIRST_USER) {
fail("Failed starting service, result=" + brCode);
}
conn.waitForConnect(WAIT_TIME);
// Okay, bring down the service.
mContext.stopService(mServiceIntent);
conn.waitForDisconnect(WAIT_TIME);
} finally {
mContext.stopService(mServiceIntent);
conn.stopMonitoring();
cmd = "appops set " + SIMPLE_PACKAGE_NAME + " RUN_IN_BACKGROUND allow";
result = SystemUtil.runShellCommand(getInstrumentation(), cmd);
cmd = "cmd deviceidle whitelist -" + SIMPLE_PACKAGE_NAME;
result = SystemUtil.runShellCommand(getInstrumentation(), cmd);
controller.cleanup();
}
}
public void testBackgroundCheckActivityService() throws Exception {
final Intent activityIntent = new Intent();
activityIntent.setClassName(SIMPLE_PACKAGE_NAME,
SIMPLE_PACKAGE_NAME + SIMPLE_ACTIVITY_START_SERVICE);
final ServiceProcessController controller = new ServiceProcessController(mContext,
getInstrumentation(), STUB_PACKAGE_NAME, mAllProcesses);
final ServiceConnectionHandler conn = new ServiceConnectionHandler(mContext,
mServiceIntent);
// First kill the process to start out in a stable state.
controller.ensureProcessGone(WAIT_TIME);
String cmd = "appops set " + SIMPLE_PACKAGE_NAME + " RUN_IN_BACKGROUND deny";
String result = SystemUtil.runShellCommand(getInstrumentation(), cmd);
// We don't want to wait for the uid to actually go idle, we can force it now.
cmd = "am make-uid-idle " + SIMPLE_PACKAGE_NAME;
result = SystemUtil.runShellCommand(getInstrumentation(), cmd);
// Make sure app is not yet on whitelist
cmd = "cmd deviceidle whitelist -" + SIMPLE_PACKAGE_NAME;
result = SystemUtil.runShellCommand(getInstrumentation(), cmd);
// We will use this to monitor when the service is running.
conn.startMonitoring();
try {
// Try starting activity that will start the service. This should be okay.
WaitForBroadcast waiter = new WaitForBroadcast(mInstrumentation.getTargetContext());
waiter.prepare(ACTION_SIMPLE_ACTIVITY_START_SERVICE_RESULT);
activityIntent.putExtra("service", mServiceIntent);
mContext.startActivity(activityIntent);
Intent resultIntent = waiter.doWait(WAIT_TIME);
int brCode = resultIntent.getIntExtra("result", Activity.RESULT_CANCELED);
if (brCode != Activity.RESULT_FIRST_USER) {
fail("Failed starting service, result=" + brCode);
}
conn.waitForConnect(WAIT_TIME);
// Okay, bring down the service.
mContext.stopService(mServiceIntent);
conn.waitForDisconnect(WAIT_TIME);
} finally {
mContext.stopService(mServiceIntent);
conn.stopMonitoring();
cmd = "appops set " + SIMPLE_PACKAGE_NAME + " RUN_IN_BACKGROUND allow";
result = SystemUtil.runShellCommand(getInstrumentation(), cmd);
cmd = "cmd deviceidle whitelist -" + SIMPLE_PACKAGE_NAME;
result = SystemUtil.runShellCommand(getInstrumentation(), cmd);
controller.cleanup();
}
}
}