Loading services/core/java/com/android/server/os/BugreportManagerServiceImpl.java +19 −4 Original line number Diff line number Diff line Loading @@ -22,6 +22,7 @@ import android.annotation.RequiresPermission; import android.app.ActivityManager; import android.app.AppOpsManager; import android.app.admin.DevicePolicyManager; import android.app.role.RoleManager; import android.content.Context; import android.content.pm.PackageManager; import android.content.pm.UserInfo; Loading Loading @@ -64,6 +65,8 @@ class BugreportManagerServiceImpl extends IDumpstate.Stub { private static final int LOCAL_LOG_SIZE = 20; private static final String TAG = "BugreportManagerService"; private static final boolean DEBUG = false; private static final String ROLE_SYSTEM_AUTOMOTIVE_PROJECTION = "android.app.role.SYSTEM_AUTOMOTIVE_PROJECTION"; private static final String BUGREPORT_SERVICE = "bugreportd"; private static final long DEFAULT_BUGREPORT_SERVICE_TIMEOUT_MILLIS = 30 * 1000; Loading Loading @@ -326,11 +329,22 @@ class BugreportManagerServiceImpl extends IDumpstate.Stub { // To gain access through the DUMP permission, the OEM has to allow this package explicitly // via sysconfig and privileged permissions. if (mBugreportAllowlistedPackages.contains(callingPackage) && mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) == PackageManager.PERMISSION_GRANTED) { boolean allowlisted = mBugreportAllowlistedPackages.contains(callingPackage); if (!allowlisted) { final long token = Binder.clearCallingIdentity(); try { allowlisted = mContext.getSystemService(RoleManager.class).getRoleHolders( ROLE_SYSTEM_AUTOMOTIVE_PROJECTION).contains(callingPackage); } finally { Binder.restoreCallingIdentity(token); } } if (allowlisted && mContext.checkCallingOrSelfPermission( android.Manifest.permission.DUMP) == PackageManager.PERMISSION_GRANTED) { return; } // For carrier privileges, this can include user-installed apps. This is essentially a // function of the current active SIM(s) in the device to let carrier apps through. final long token = Binder.clearCallingIdentity(); Loading @@ -346,7 +360,8 @@ class BugreportManagerServiceImpl extends IDumpstate.Stub { String message = callingPackage + " does not hold the DUMP permission or is not bugreport-whitelisted " + " does not hold the DUMP permission or is not bugreport-whitelisted or " + "does not have an allowed role " + (checkCarrierPrivileges ? "and does not have carrier privileges " : "") + "to request a bugreport"; Slog.w(TAG, message); Loading services/tests/servicestests/AndroidManifest.xml +1 −0 Original line number Diff line number Diff line Loading @@ -110,6 +110,7 @@ <uses-permission android:name="android.permission.ACCESS_CONTEXT_HUB" /> <uses-permission android:name="android.permission.USE_BIOMETRIC_INTERNAL" /> <uses-permission android:name="android.permission.MANAGE_MEDIA_PROJECTION" /> <uses-permission android:name="android.permission.MANAGE_ROLE_HOLDERS" /> <queries> <package android:name="com.android.servicestests.apps.suspendtestapp" /> Loading services/tests/servicestests/src/com/android/server/os/BugreportManagerServiceImplTest.java +50 −7 Original line number Diff line number Diff line Loading @@ -16,15 +16,18 @@ package com.android.server.os; import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertThrows; import android.app.role.RoleManager; import android.content.Context; import android.os.Binder; import android.os.BugreportManager.BugreportCallback; import android.os.IBinder; import android.os.IDumpstateListener; import android.os.Process; import android.os.RemoteException; import android.util.ArraySet; import android.util.Pair; Loading @@ -37,21 +40,23 @@ import org.junit.Test; import org.junit.runner.RunWith; import java.io.FileDescriptor; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; @RunWith(AndroidJUnit4.class) public class BugreportManagerServiceImplTest { Context mContext; BugreportManagerServiceImpl mService; BugreportManagerServiceImpl.BugreportFileManager mBugreportFileManager; private Context mContext; private BugreportManagerServiceImpl mService; private BugreportManagerServiceImpl.BugreportFileManager mBugreportFileManager; int mCallingUid = 1234; String mCallingPackage = "test.package"; private int mCallingUid = 1234; private String mCallingPackage = "test.package"; String mBugreportFile = "bugreport-file.zip"; String mBugreportFile2 = "bugreport-file2.zip"; private String mBugreportFile = "bugreport-file.zip"; private String mBugreportFile2 = "bugreport-file2.zip"; @Before public void setUp() { Loading Loading @@ -109,6 +114,36 @@ public class BugreportManagerServiceImplTest { BugreportCallback.BUGREPORT_ERROR_NO_BUGREPORT_TO_RETRIEVE); } @Test public void testCancelBugreportWithoutRole() throws Exception { // Clear out allowlisted packages. mService = new BugreportManagerServiceImpl( new BugreportManagerServiceImpl.Injector(mContext, new ArraySet<>())); assertThrows(SecurityException.class, () -> mService.cancelBugreport( Binder.getCallingUid(), mContext.getPackageName())); } @Test public void testCancelBugreportWithRole() throws Exception { // Clear out allowlisted packages. mService = new BugreportManagerServiceImpl( new BugreportManagerServiceImpl.Injector(mContext, new ArraySet<>())); RoleManager roleManager = mContext.getSystemService(RoleManager.class); CallbackFuture future = new CallbackFuture(); runWithShellPermissionIdentity(() -> roleManager.setBypassingRoleQualification(true)); runWithShellPermissionIdentity(() -> roleManager.addRoleHolderAsUser( "android.app.role.SYSTEM_AUTOMOTIVE_PROJECTION", mContext.getPackageName(), /* flags= */ 0, Process.myUserHandle(), mContext.getMainExecutor(), future)); assertThat(future.get()).isEqualTo(true); mService.cancelBugreport(Binder.getCallingUid(), mContext.getPackageName()); } private static class Listener implements IDumpstateListener { CountDownLatch mLatch; int mErrorCode; Loading Loading @@ -149,4 +184,12 @@ public class BugreportManagerServiceImplTest { return mErrorCode; } } private static class CallbackFuture extends CompletableFuture<Boolean> implements Consumer<Boolean> { @Override public void accept(Boolean successful) { complete(successful); } } } Loading
services/core/java/com/android/server/os/BugreportManagerServiceImpl.java +19 −4 Original line number Diff line number Diff line Loading @@ -22,6 +22,7 @@ import android.annotation.RequiresPermission; import android.app.ActivityManager; import android.app.AppOpsManager; import android.app.admin.DevicePolicyManager; import android.app.role.RoleManager; import android.content.Context; import android.content.pm.PackageManager; import android.content.pm.UserInfo; Loading Loading @@ -64,6 +65,8 @@ class BugreportManagerServiceImpl extends IDumpstate.Stub { private static final int LOCAL_LOG_SIZE = 20; private static final String TAG = "BugreportManagerService"; private static final boolean DEBUG = false; private static final String ROLE_SYSTEM_AUTOMOTIVE_PROJECTION = "android.app.role.SYSTEM_AUTOMOTIVE_PROJECTION"; private static final String BUGREPORT_SERVICE = "bugreportd"; private static final long DEFAULT_BUGREPORT_SERVICE_TIMEOUT_MILLIS = 30 * 1000; Loading Loading @@ -326,11 +329,22 @@ class BugreportManagerServiceImpl extends IDumpstate.Stub { // To gain access through the DUMP permission, the OEM has to allow this package explicitly // via sysconfig and privileged permissions. if (mBugreportAllowlistedPackages.contains(callingPackage) && mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) == PackageManager.PERMISSION_GRANTED) { boolean allowlisted = mBugreportAllowlistedPackages.contains(callingPackage); if (!allowlisted) { final long token = Binder.clearCallingIdentity(); try { allowlisted = mContext.getSystemService(RoleManager.class).getRoleHolders( ROLE_SYSTEM_AUTOMOTIVE_PROJECTION).contains(callingPackage); } finally { Binder.restoreCallingIdentity(token); } } if (allowlisted && mContext.checkCallingOrSelfPermission( android.Manifest.permission.DUMP) == PackageManager.PERMISSION_GRANTED) { return; } // For carrier privileges, this can include user-installed apps. This is essentially a // function of the current active SIM(s) in the device to let carrier apps through. final long token = Binder.clearCallingIdentity(); Loading @@ -346,7 +360,8 @@ class BugreportManagerServiceImpl extends IDumpstate.Stub { String message = callingPackage + " does not hold the DUMP permission or is not bugreport-whitelisted " + " does not hold the DUMP permission or is not bugreport-whitelisted or " + "does not have an allowed role " + (checkCarrierPrivileges ? "and does not have carrier privileges " : "") + "to request a bugreport"; Slog.w(TAG, message); Loading
services/tests/servicestests/AndroidManifest.xml +1 −0 Original line number Diff line number Diff line Loading @@ -110,6 +110,7 @@ <uses-permission android:name="android.permission.ACCESS_CONTEXT_HUB" /> <uses-permission android:name="android.permission.USE_BIOMETRIC_INTERNAL" /> <uses-permission android:name="android.permission.MANAGE_MEDIA_PROJECTION" /> <uses-permission android:name="android.permission.MANAGE_ROLE_HOLDERS" /> <queries> <package android:name="com.android.servicestests.apps.suspendtestapp" /> Loading
services/tests/servicestests/src/com/android/server/os/BugreportManagerServiceImplTest.java +50 −7 Original line number Diff line number Diff line Loading @@ -16,15 +16,18 @@ package com.android.server.os; import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertThrows; import android.app.role.RoleManager; import android.content.Context; import android.os.Binder; import android.os.BugreportManager.BugreportCallback; import android.os.IBinder; import android.os.IDumpstateListener; import android.os.Process; import android.os.RemoteException; import android.util.ArraySet; import android.util.Pair; Loading @@ -37,21 +40,23 @@ import org.junit.Test; import org.junit.runner.RunWith; import java.io.FileDescriptor; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; @RunWith(AndroidJUnit4.class) public class BugreportManagerServiceImplTest { Context mContext; BugreportManagerServiceImpl mService; BugreportManagerServiceImpl.BugreportFileManager mBugreportFileManager; private Context mContext; private BugreportManagerServiceImpl mService; private BugreportManagerServiceImpl.BugreportFileManager mBugreportFileManager; int mCallingUid = 1234; String mCallingPackage = "test.package"; private int mCallingUid = 1234; private String mCallingPackage = "test.package"; String mBugreportFile = "bugreport-file.zip"; String mBugreportFile2 = "bugreport-file2.zip"; private String mBugreportFile = "bugreport-file.zip"; private String mBugreportFile2 = "bugreport-file2.zip"; @Before public void setUp() { Loading Loading @@ -109,6 +114,36 @@ public class BugreportManagerServiceImplTest { BugreportCallback.BUGREPORT_ERROR_NO_BUGREPORT_TO_RETRIEVE); } @Test public void testCancelBugreportWithoutRole() throws Exception { // Clear out allowlisted packages. mService = new BugreportManagerServiceImpl( new BugreportManagerServiceImpl.Injector(mContext, new ArraySet<>())); assertThrows(SecurityException.class, () -> mService.cancelBugreport( Binder.getCallingUid(), mContext.getPackageName())); } @Test public void testCancelBugreportWithRole() throws Exception { // Clear out allowlisted packages. mService = new BugreportManagerServiceImpl( new BugreportManagerServiceImpl.Injector(mContext, new ArraySet<>())); RoleManager roleManager = mContext.getSystemService(RoleManager.class); CallbackFuture future = new CallbackFuture(); runWithShellPermissionIdentity(() -> roleManager.setBypassingRoleQualification(true)); runWithShellPermissionIdentity(() -> roleManager.addRoleHolderAsUser( "android.app.role.SYSTEM_AUTOMOTIVE_PROJECTION", mContext.getPackageName(), /* flags= */ 0, Process.myUserHandle(), mContext.getMainExecutor(), future)); assertThat(future.get()).isEqualTo(true); mService.cancelBugreport(Binder.getCallingUid(), mContext.getPackageName()); } private static class Listener implements IDumpstateListener { CountDownLatch mLatch; int mErrorCode; Loading Loading @@ -149,4 +184,12 @@ public class BugreportManagerServiceImplTest { return mErrorCode; } } private static class CallbackFuture extends CompletableFuture<Boolean> implements Consumer<Boolean> { @Override public void accept(Boolean successful) { complete(successful); } } }