CTS test spoofed packageName can not bypass BG-FGS restrictions.
Add CTS test case ActivityManagerFgsBgStartTest.java#testSpoofPackageName
to verify framework can stop spoofed packageName from starting FGS
from the background.
Bug: 216695100
Bug: 215003903
Test: atest cts/tests/app/src/android/app/cts/ActivityManagerFgsBgStartTest.java#testSpoofPackageName
Change-Id: I579a3336f3cc9143775540b073558d25f6c08338
Merged-In: I579a3336f3cc9143775540b073558d25f6c08338
BYPASS_INCLUSIVE_LANGUAGE_REASON=legacy API
diff --git a/tests/app/app/src/android/app/stubs/CommandReceiver.java b/tests/app/app/src/android/app/stubs/CommandReceiver.java
index 9fcd4ef..224416d 100644
--- a/tests/app/app/src/android/app/stubs/CommandReceiver.java
+++ b/tests/app/app/src/android/app/stubs/CommandReceiver.java
@@ -17,6 +17,7 @@
package android.app.stubs;
import android.app.ActivityManager;
+import android.app.IActivityManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -24,8 +25,12 @@
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Binder;
+import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.IBinder;
+import android.os.Parcel;
+import android.os.RemoteException;
+import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
@@ -51,6 +56,7 @@
public static final int COMMAND_BIND_FOREGROUND_SERVICE = 14;
public static final int COMMAND_START_FGSL_SERVICE = 15;
public static final int COMMAND_STOP_FGSL_SERVICE = 16;
+ public static final int COMMAND_START_FOREGROUND_SERVICE_SPOOF_PACKAGE_NAME = 23;
public static final String EXTRA_COMMAND = "android.app.stubs.extra.COMMAND";
public static final String EXTRA_TARGET_PACKAGE = "android.app.stubs.extra.TARGET_PACKAGE";
@@ -132,6 +138,9 @@
case COMMAND_STOP_FGSL_SERVICE:
doStopService(context, intent, FG_LOCATION_SERVICE_NAME);
break;
+ case COMMAND_START_FOREGROUND_SERVICE_SPOOF_PACKAGE_NAME:
+ doStartForegroundServiceSpoofPackageName(context, intent);
+ break;
}
}
@@ -248,6 +257,53 @@
}
}
+ /**
+ * Directly call IActivityManager.startService() using a spoofed packageName which is known to
+ * be allowlisted by Android framework to be able to start foreground service
+ * from the background. Framework will disallow the foreground service to start from the
+ * background.
+ * @param context
+ * @param commandIntent
+ */
+ private void doStartForegroundServiceSpoofPackageName(Context context, Intent commandIntent) {
+ String targetPackage = getTargetPackage(commandIntent);
+ Intent fgsIntent = new Intent();
+ fgsIntent.putExtras(commandIntent); // include the fg service type if any.
+ fgsIntent.setComponent(new ComponentName(targetPackage, FG_LOCATION_SERVICE_NAME));
+ int command = LocalForegroundService.COMMAND_START_FOREGROUND_WITH_TYPE;
+ fgsIntent.putExtras(LocalForegroundService.newCommand(new Binder(), command));
+ try {
+ final PackageManager pm = context.getPackageManager();
+ String spoofPackageName = pm.getAttentionServicePackageName();
+ if (TextUtils.isEmpty(spoofPackageName)) {
+ Log.d(TAG, "getAttentionServicePackageName() returns empty");
+ spoofPackageName = pm.getSystemCaptionsServicePackageName();
+ }
+ if (TextUtils.isEmpty(spoofPackageName)) {
+ Log.d(TAG, "getSystemCaptionsServicePackageName() returns empty");
+ spoofPackageName = "android";
+ }
+ Log.d(TAG, "spoofPackageName: " + spoofPackageName);
+ final IBinder activityProxy = android.os.ServiceManager.getService("activity");
+ // Call IActivityManager.startService() directly using a spoofed packageName.
+ IActivityManager.Stub.asInterface(activityProxy).startService(
+ context.getIApplicationThread(),
+ fgsIntent,
+ null,
+ true,
+ spoofPackageName,
+ null,
+ android.os.Process.myUserHandle().getIdentifier()
+ );
+ } catch (LinkageError e) {
+ // IActivityManager.startService() is a hidden API, access hidden API could get
+ // LinkageError, consider the test as pass if we get LinkageError.
+ Log.d(TAG, "startForegroundService gets an LinkageError", e);
+ } catch (RemoteException e) {
+ Log.d(TAG, "startForegroundService gets an RemoteException", e);
+ }
+ }
+
private String getTargetPackage(Intent intent) {
return intent.getStringExtra(EXTRA_TARGET_PACKAGE);
}
diff --git a/tests/app/src/android/app/cts/ActivityManagerFgsBgStartTest.java b/tests/app/src/android/app/cts/ActivityManagerFgsBgStartTest.java
index 502f282..711d0aa 100644
--- a/tests/app/src/android/app/cts/ActivityManagerFgsBgStartTest.java
+++ b/tests/app/src/android/app/cts/ActivityManagerFgsBgStartTest.java
@@ -40,6 +40,10 @@
import android.platform.test.annotations.AsbSecurityTest;
import android.provider.DeviceConfig;
import android.test.InstrumentationTestCase;
+import android.provider.Settings;
+import android.server.wm.settings.SettingsSession;
+import android.support.test.uiautomator.UiDevice;
+import android.util.Log;
import com.android.compatibility.common.util.SystemUtil;
@@ -583,6 +587,58 @@
}
}
+ /**
+ * IActivityManager.startService() is called directly (does not go through
+ * {@link Context#startForegroundService(Intent)}, a spoofed packageName "com.google.android.as"
+ * is used as callingPackage. Although "com.google.android.as" is allowlisted to start
+ * foreground service from the background, but framework will detect this is a spoofed
+ * packageName and disallow foreground service start from the background.
+ * @throws Exception
+ */
+ @Test
+ public void testSpoofPackageName() throws Exception {
+ ApplicationInfo app1Info = mContext.getPackageManager().getApplicationInfo(
+ PACKAGE_NAME_APP1, 0);
+ WatchUidRunner uid1Watcher = new WatchUidRunner(mInstrumentation, app1Info.uid,
+ WAITFOR_MSEC);
+ // CommandReceiver.COMMAND_START_FOREGROUND_SERVICE_SPOOF_PACKAGE_NAME needs access
+ // to hidden API PackageManager.getAttentionServicePackageName() and
+ // PackageManager.getSystemCaptionsServicePackageName(), so we need to call
+ // hddenApiSettings.set("*") to exempt the hidden APIs.
+ SettingsSession<String> hiddenApiSettings = new SettingsSession<>(
+ Settings.Global.getUriFor(
+ Settings.Global.HIDDEN_API_BLACKLIST_EXEMPTIONS),
+ Settings.Global::getString, Settings.Global::putString);
+ hiddenApiSettings.set("*");
+ try {
+ Bundle bundle = new Bundle();
+ bundle.putInt(LocalForegroundServiceLocation.EXTRA_FOREGROUND_SERVICE_TYPE,
+ ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION);
+ // Package1 is in BG state, start FGSL in package1, it won't get location capability.
+ CommandReceiver.sendCommand(mContext,
+ CommandReceiver.COMMAND_START_FOREGROUND_SERVICE_SPOOF_PACKAGE_NAME,
+ PACKAGE_NAME_APP1, PACKAGE_NAME_APP1, 0, bundle);
+
+ // Package1 is in FGS state, but won't get location capability.
+ uid1Watcher.waitFor(WatchUidRunner.CMD_PROCSTATE,
+ WatchUidRunner.STATE_FG_SERVICE,
+ new Integer(PROCESS_CAPABILITY_NONE));
+
+ // stop FGSL
+ CommandReceiver.sendCommand(mContext,
+ CommandReceiver.COMMAND_STOP_FOREGROUND_SERVICE_LOCATION,
+ PACKAGE_NAME_APP1, PACKAGE_NAME_APP1, 0, null);
+ uid1Watcher.waitFor(WatchUidRunner.CMD_PROCSTATE,
+ WatchUidRunner.STATE_CACHED_EMPTY,
+ new Integer(PROCESS_CAPABILITY_NONE));
+ } finally {
+ uid1Watcher.finish();
+ if (hiddenApiSettings != null) {
+ hiddenApiSettings.close();
+ }
+ }
+ }
+
private void setFgsStartForegroundTimeout(int timeoutMs) throws Exception {
runWithShellPermissionIdentity(() -> {
DeviceConfig.setProperty("activity_manager",