summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Sarup Dalwani <sarup@google.com> 2024-02-12 04:46:04 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2024-02-12 04:46:04 +0000
commitaaa6244b08193d43f23dd414ebc37498c29e9bea (patch)
tree7c2e6d54a49917e8f1fe8b3de439c27a99711e7e
parentace52242eb6fcbe7dc6e67b2262497300469d140 (diff)
parent3e5c69374d03791ad964538e64377a6126b66ecc (diff)
Merge "Avoiding overlay activities when usb device is attached." into main
-rw-r--r--services/usb/Android.bp12
-rw-r--r--services/usb/java/com/android/server/usb/UsbHandlerManager.java2
-rw-r--r--services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java83
-rw-r--r--services/usb/java/com/android/server/usb/UsbSettingsManager.java4
-rw-r--r--services/usb/java/com/android/server/usb/UsbUserSettingsManager.java4
-rw-r--r--services/usb/java/com/android/server/usb/flags/usb_flags.aconfig8
-rw-r--r--tests/UsbManagerTests/Android.bp9
-rw-r--r--tests/UsbManagerTests/src/com/android/server/usbtest/UsbProfileGroupSettingsManagerTest.java195
8 files changed, 307 insertions, 10 deletions
diff --git a/services/usb/Android.bp b/services/usb/Android.bp
index 3a0a6abb2307..e8ffe541d641 100644
--- a/services/usb/Android.bp
+++ b/services/usb/Android.bp
@@ -34,8 +34,20 @@ java_library_static {
"android.hardware.usb-V1.2-java",
"android.hardware.usb-V1.3-java",
"android.hardware.usb-V3-java",
+ "usb_flags_lib",
],
lint: {
baseline_filename: "lint-baseline.xml",
},
}
+
+aconfig_declarations {
+ name: "usb_flags",
+ package: "com.android.server.usb.flags",
+ srcs: ["**/usb_flags.aconfig"],
+}
+
+java_aconfig_library {
+ name: "usb_flags_lib",
+ aconfig_declarations: "usb_flags",
+}
diff --git a/services/usb/java/com/android/server/usb/UsbHandlerManager.java b/services/usb/java/com/android/server/usb/UsbHandlerManager.java
index f3112743bcf2..d83ff1fbc381 100644
--- a/services/usb/java/com/android/server/usb/UsbHandlerManager.java
+++ b/services/usb/java/com/android/server/usb/UsbHandlerManager.java
@@ -37,7 +37,7 @@ import java.util.ArrayList;
*
* @hide
*/
-class UsbHandlerManager {
+public class UsbHandlerManager {
private static final String LOG_TAG = UsbHandlerManager.class.getSimpleName();
private final Context mContext;
diff --git a/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java b/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java
index f91666081e82..2ff21ad40558 100644
--- a/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java
+++ b/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java
@@ -18,6 +18,7 @@ package com.android.server.usb;
import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_MANAGED_PROFILE;
+import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
@@ -62,6 +63,7 @@ import com.android.internal.util.XmlUtils;
import com.android.internal.util.dump.DualDumpOutputStream;
import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;
+import com.android.server.usb.flags.Flags;
import com.android.server.utils.EventLogger;
import libcore.io.IoUtils;
@@ -80,8 +82,20 @@ import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.stream.Collectors;
-class UsbProfileGroupSettingsManager {
+public class UsbProfileGroupSettingsManager {
+ /**
+ * &lt;application&gt; level property that an app can specify to restrict any overlaying of
+ * activities when usb device is attached.
+ *
+ *
+ * <p>This should only be set by privileged apps having {@link Manifest.permission#MANAGE_USB}
+ * permission.
+ * @hide
+ */
+ public static final String PROPERTY_RESTRICT_USB_OVERLAY_ACTIVITIES =
+ "android.app.PROPERTY_RESTRICT_USB_OVERLAY_ACTIVITIES";
private static final String TAG = UsbProfileGroupSettingsManager.class.getSimpleName();
private static final boolean DEBUG = false;
@@ -101,6 +115,8 @@ class UsbProfileGroupSettingsManager {
private final PackageManager mPackageManager;
+ private final ActivityManager mActivityManager;
+
private final UserManager mUserManager;
private final @NonNull UsbSettingsManager mSettingsManager;
@@ -224,7 +240,7 @@ class UsbProfileGroupSettingsManager {
* @param settingsManager The settings manager of the service
* @param usbResolveActivityManager The resovle activity manager of the service
*/
- UsbProfileGroupSettingsManager(@NonNull Context context, @NonNull UserHandle user,
+ public UsbProfileGroupSettingsManager(@NonNull Context context, @NonNull UserHandle user,
@NonNull UsbSettingsManager settingsManager,
@NonNull UsbHandlerManager usbResolveActivityManager) {
if (DEBUG) Slog.v(TAG, "Creating settings for " + user);
@@ -238,6 +254,7 @@ class UsbProfileGroupSettingsManager {
mContext = context;
mPackageManager = context.getPackageManager();
+ mActivityManager = context.getSystemService(ActivityManager.class);
mSettingsManager = settingsManager;
mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
@@ -895,7 +912,10 @@ class UsbProfileGroupSettingsManager {
// Send broadcast to running activities with registered intent
mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
- resolveActivity(intent, device, true /* showMtpNotification */);
+ //resolving activities only if there is no foreground activity restricting it.
+ if (!shouldRestrictOverlayActivities()) {
+ resolveActivity(intent, device, true /* showMtpNotification */);
+ }
}
private void resolveActivity(Intent intent, UsbDevice device, boolean showMtpNotification) {
@@ -918,6 +938,63 @@ class UsbProfileGroupSettingsManager {
resolveActivity(intent, matches, defaultActivity, device, null);
}
+ /**
+ * @return true if any application in foreground have set restrict_usb_overlay_activities as
+ * true in manifest file. The application needs to have MANAGE_USB permission.
+ */
+ private boolean shouldRestrictOverlayActivities() {
+
+ if (!Flags.allowRestrictionOfOverlayActivities()) return false;
+
+ List<ActivityManager.RunningAppProcessInfo> appProcessInfos = mActivityManager
+ .getRunningAppProcesses();
+
+ List<String> filteredAppProcessInfos = new ArrayList<>();
+ boolean shouldRestrictOverlayActivities;
+
+ //filtering out applications in foreground.
+ for (ActivityManager.RunningAppProcessInfo processInfo : appProcessInfos) {
+ if (processInfo.importance <= ActivityManager
+ .RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
+ filteredAppProcessInfos.addAll(List.of(processInfo.pkgList));
+ }
+ }
+
+ if (DEBUG) Slog.d(TAG, "packages in foreground : " + filteredAppProcessInfos);
+
+ List<String> packagesHoldingManageUsbPermission =
+ mPackageManager.getPackagesHoldingPermissions(
+ new String[]{android.Manifest.permission.MANAGE_USB},
+ PackageManager.MATCH_SYSTEM_ONLY).stream()
+ .map(packageInfo -> packageInfo.packageName).collect(Collectors.toList());
+
+ //retaining only packages that hold the required permission
+ filteredAppProcessInfos.retainAll(packagesHoldingManageUsbPermission);
+
+ if (DEBUG) {
+ Slog.d(TAG, "packages in foreground with required permission : "
+ + filteredAppProcessInfos);
+ }
+
+ shouldRestrictOverlayActivities = filteredAppProcessInfos.stream().anyMatch(pkg -> {
+ try {
+ return mPackageManager.getProperty(PROPERTY_RESTRICT_USB_OVERLAY_ACTIVITIES, pkg)
+ .getBoolean();
+ } catch (NameNotFoundException e) {
+ if (DEBUG) {
+ Slog.d(TAG, "property PROPERTY_RESTRICT_USB_OVERLAY_ACTIVITIES "
+ + "not present for " + pkg);
+ }
+ return false;
+ }
+ });
+
+ if (shouldRestrictOverlayActivities) {
+ Slog.d(TAG, "restricting starting of usb overlay activities");
+ }
+ return shouldRestrictOverlayActivities;
+ }
+
public void deviceAttachedForFixedHandler(UsbDevice device, ComponentName component) {
final Intent intent = createDeviceAttachedIntent(device);
diff --git a/services/usb/java/com/android/server/usb/UsbSettingsManager.java b/services/usb/java/com/android/server/usb/UsbSettingsManager.java
index 8e53ff412f0a..0b854a8440a5 100644
--- a/services/usb/java/com/android/server/usb/UsbSettingsManager.java
+++ b/services/usb/java/com/android/server/usb/UsbSettingsManager.java
@@ -33,7 +33,7 @@ import java.util.List;
/**
* Maintains all {@link UsbUserSettingsManager} for all users.
*/
-class UsbSettingsManager {
+public class UsbSettingsManager {
private static final String LOG_TAG = UsbSettingsManager.class.getSimpleName();
private static final boolean DEBUG = false;
@@ -70,7 +70,7 @@ class UsbSettingsManager {
*
* @return The settings for the user
*/
- @NonNull UsbUserSettingsManager getSettingsForUser(@UserIdInt int userId) {
+ public @NonNull UsbUserSettingsManager getSettingsForUser(@UserIdInt int userId) {
synchronized (mSettingsByUser) {
UsbUserSettingsManager settings = mSettingsByUser.get(userId);
if (settings == null) {
diff --git a/services/usb/java/com/android/server/usb/UsbUserSettingsManager.java b/services/usb/java/com/android/server/usb/UsbUserSettingsManager.java
index c2b8d0109e68..be729c562f64 100644
--- a/services/usb/java/com/android/server/usb/UsbUserSettingsManager.java
+++ b/services/usb/java/com/android/server/usb/UsbUserSettingsManager.java
@@ -49,7 +49,7 @@ import org.xmlpull.v1.XmlPullParser;
import java.util.ArrayList;
import java.util.List;
-class UsbUserSettingsManager {
+public class UsbUserSettingsManager {
private static final String TAG = UsbUserSettingsManager.class.getSimpleName();
private static final boolean DEBUG = false;
@@ -81,7 +81,7 @@ class UsbUserSettingsManager {
*
* @return The resolve infos of the activities that can handle the intent
*/
- List<ResolveInfo> queryIntentActivities(@NonNull Intent intent) {
+ public List<ResolveInfo> queryIntentActivities(@NonNull Intent intent) {
return mPackageManager.queryIntentActivitiesAsUser(intent, PackageManager.GET_META_DATA,
mUser.getIdentifier());
}
diff --git a/services/usb/java/com/android/server/usb/flags/usb_flags.aconfig b/services/usb/java/com/android/server/usb/flags/usb_flags.aconfig
new file mode 100644
index 000000000000..ea6e50221cd0
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/flags/usb_flags.aconfig
@@ -0,0 +1,8 @@
+package: "com.android.server.usb.flags"
+
+flag {
+ name: "allow_restriction_of_overlay_activities"
+ namespace: "usb"
+ description: "This flag controls the restriction of usb overlay activities"
+ bug: "307231174"
+}
diff --git a/tests/UsbManagerTests/Android.bp b/tests/UsbManagerTests/Android.bp
index c02d8e96abb0..70c7dad2fd55 100644
--- a/tests/UsbManagerTests/Android.bp
+++ b/tests/UsbManagerTests/Android.bp
@@ -29,12 +29,17 @@ android_test {
static_libs: [
"frameworks-base-testutils",
"androidx.test.rules",
- "mockito-target-inline-minus-junit4",
+ "mockito-target-extended-minus-junit4",
"platform-test-annotations",
"truth",
"UsbManagerTestLib",
],
- jni_libs: ["libdexmakerjvmtiagent"],
+ jni_libs: [
+ // Required for ExtendedMockito
+ "libdexmakerjvmtiagent",
+ "libmultiplejvmtiagentsinterferenceagent",
+ "libstaticjvmtiagent",
+ ],
certificate: "platform",
platform_apis: true,
test_suites: ["device-tests"],
diff --git a/tests/UsbManagerTests/src/com/android/server/usbtest/UsbProfileGroupSettingsManagerTest.java b/tests/UsbManagerTests/src/com/android/server/usbtest/UsbProfileGroupSettingsManagerTest.java
new file mode 100644
index 000000000000..4780d8a610e8
--- /dev/null
+++ b/tests/UsbManagerTests/src/com/android/server/usbtest/UsbProfileGroupSettingsManagerTest.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2024 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.usbtest;
+
+import static com.android.server.usb.UsbProfileGroupSettingsManager.PROPERTY_RESTRICT_USB_OVERLAY_ACTIVITIES;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.ActivityManager;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.Property;
+import android.content.pm.UserInfo;
+import android.content.res.Resources;
+import android.hardware.usb.UsbDevice;
+import android.os.UserHandle;
+import android.os.UserManager;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.dx.mockito.inline.extended.ExtendedMockito;
+import com.android.server.usb.UsbHandlerManager;
+import com.android.server.usb.UsbProfileGroupSettingsManager;
+import com.android.server.usb.UsbSettingsManager;
+import com.android.server.usb.UsbUserSettingsManager;
+import com.android.server.usb.flags.Flags;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Unit tests for {@link com.android.server.usb.UsbProfileGroupSettingsManager}.
+ * Note: MUST claim MANAGE_USB permission in Manifest
+ */
+@RunWith(AndroidJUnit4.class)
+public class UsbProfileGroupSettingsManagerTest {
+
+ private static final String TEST_PACKAGE_NAME = "testPkg";
+ @Mock
+ private Context mContext;
+ @Mock
+ private PackageManager mPackageManager;
+ @Mock
+ private ActivityManager mActivityManager;
+ @Mock
+ private UserHandle mUserHandle;
+ @Mock
+ private UsbSettingsManager mUsbSettingsManager;
+ @Mock
+ private UsbHandlerManager mUsbHandlerManager;
+ @Mock
+ private UserManager mUserManager;
+ @Mock
+ private UsbUserSettingsManager mUsbUserSettingsManager;
+ @Mock private Property mProperty;
+ private ActivityManager.RunningAppProcessInfo mRunningAppProcessInfo;
+ private PackageInfo mPackageInfo;
+ private UsbProfileGroupSettingsManager mUsbProfileGroupSettingsManager;
+ private MockitoSession mStaticMockSession;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ mRunningAppProcessInfo = new ActivityManager.RunningAppProcessInfo();
+ mRunningAppProcessInfo.pkgList = new String[]{TEST_PACKAGE_NAME};
+ mPackageInfo = new PackageInfo();
+ mPackageInfo.packageName = TEST_PACKAGE_NAME;
+ mPackageInfo.applicationInfo = Mockito.mock(ApplicationInfo.class);
+
+ when(mContext.getPackageManager()).thenReturn(mPackageManager);
+ when(mContext.getSystemService(ActivityManager.class)).thenReturn(mActivityManager);
+ when(mContext.getResources()).thenReturn(Mockito.mock(Resources.class));
+ when(mContext.createPackageContextAsUser(anyString(), anyInt(), any(UserHandle.class)))
+ .thenReturn(mContext);
+ when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
+
+ mUsbProfileGroupSettingsManager = new UsbProfileGroupSettingsManager(mContext, mUserHandle,
+ mUsbSettingsManager, mUsbHandlerManager);
+
+ mStaticMockSession = ExtendedMockito.mockitoSession()
+ .mockStatic(Flags.class)
+ .strictness(Strictness.WARN)
+ .startMocking();
+
+ when(mPackageManager.getPackageInfo(TEST_PACKAGE_NAME, 0)).thenReturn(mPackageInfo);
+ when(mPackageManager.getProperty(eq(PROPERTY_RESTRICT_USB_OVERLAY_ACTIVITIES),
+ eq(TEST_PACKAGE_NAME))).thenReturn(mProperty);
+ when(mUserManager.getEnabledProfiles(anyInt()))
+ .thenReturn(List.of(Mockito.mock(UserInfo.class)));
+ when(mUsbSettingsManager.getSettingsForUser(anyInt())).thenReturn(mUsbUserSettingsManager);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mStaticMockSession.finishMocking();
+ }
+
+ @Test
+ public void testDeviceAttached_flagTrueWithoutForegroundActivity_resolveActivityCalled() {
+ when(Flags.allowRestrictionOfOverlayActivities()).thenReturn(true);
+ when(mActivityManager.getRunningAppProcesses()).thenReturn(new ArrayList<>());
+ when(mPackageManager.getPackagesHoldingPermissions(
+ new String[]{android.Manifest.permission.MANAGE_USB},
+ PackageManager.MATCH_SYSTEM_ONLY)).thenReturn(List.of(mPackageInfo));
+ UsbDevice device = Mockito.mock(UsbDevice.class);
+ mUsbProfileGroupSettingsManager.deviceAttached(device);
+ verify(mUsbUserSettingsManager).queryIntentActivities(any(Intent.class));
+ }
+
+ @Test
+ public void testDeviceAttached_noForegroundActivityWithUsbPermission_resolveActivityCalled() {
+ when(Flags.allowRestrictionOfOverlayActivities()).thenReturn(true);
+ when(mActivityManager.getRunningAppProcesses()).thenReturn(List.of(mRunningAppProcessInfo));
+ when(mPackageManager.getPackagesHoldingPermissions(
+ new String[]{android.Manifest.permission.MANAGE_USB},
+ PackageManager.MATCH_SYSTEM_ONLY)).thenReturn(new ArrayList<>());
+ UsbDevice device = Mockito.mock(UsbDevice.class);
+ mUsbProfileGroupSettingsManager.deviceAttached(device);
+ verify(mUsbUserSettingsManager).queryIntentActivities(any(Intent.class));
+ }
+
+ @Test
+ public void testDeviceAttached_foregroundActivityWithManifestField_resolveActivityNotCalled() {
+ when(Flags.allowRestrictionOfOverlayActivities()).thenReturn(true);
+ when(mProperty.getBoolean()).thenReturn(true);
+ when(mActivityManager.getRunningAppProcesses()).thenReturn(List.of(mRunningAppProcessInfo));
+ when(mPackageManager.getPackagesHoldingPermissions(
+ new String[]{android.Manifest.permission.MANAGE_USB},
+ PackageManager.MATCH_SYSTEM_ONLY)).thenReturn(List.of(mPackageInfo));
+ UsbDevice device = Mockito.mock(UsbDevice.class);
+ mUsbProfileGroupSettingsManager.deviceAttached(device);
+ verify(mUsbUserSettingsManager, times(0))
+ .queryIntentActivities(any(Intent.class));
+ }
+
+ @Test
+ public void testDeviceAttached_foregroundActivityWithoutManifestField_resolveActivityCalled() {
+ when(Flags.allowRestrictionOfOverlayActivities()).thenReturn(true);
+ when(mProperty.getBoolean()).thenReturn(false);
+ when(mActivityManager.getRunningAppProcesses()).thenReturn(List.of(mRunningAppProcessInfo));
+ when(mPackageManager.getPackagesHoldingPermissions(
+ new String[]{android.Manifest.permission.MANAGE_USB},
+ PackageManager.MATCH_SYSTEM_ONLY)).thenReturn(List.of(mPackageInfo));
+ UsbDevice device = Mockito.mock(UsbDevice.class);
+ mUsbProfileGroupSettingsManager.deviceAttached(device);
+ verify(mUsbUserSettingsManager).queryIntentActivities(any(Intent.class));
+ }
+
+ @Test
+ public void testDeviceAttached_flagFalseForegroundActivity_resolveActivityCalled() {
+ when(Flags.allowRestrictionOfOverlayActivities()).thenReturn(false);
+ when(mProperty.getBoolean()).thenReturn(true);
+ when(mActivityManager.getRunningAppProcesses()).thenReturn(List.of(mRunningAppProcessInfo));
+ when(mPackageManager.getPackagesHoldingPermissions(
+ new String[]{android.Manifest.permission.MANAGE_USB},
+ PackageManager.MATCH_SYSTEM_ONLY)).thenReturn(List.of(mPackageInfo));
+ UsbDevice device = Mockito.mock(UsbDevice.class);
+ mUsbProfileGroupSettingsManager.deviceAttached(device);
+ verify(mUsbUserSettingsManager).queryIntentActivities(any(Intent.class));
+ }
+}