From 6e5f5da9c10ef5307bc505bef51f4216e89e8e8d Mon Sep 17 00:00:00 2001 From: Faye Yan Date: Mon, 22 Apr 2024 21:53:07 +0000 Subject: Security fix for VPN app killable via lockscreen. Do not show the active apps dialog when the screen is locked. Instead prompt the user to unlock directly if clicked and only open the dialog on successsful unlock. Flag: NONE Bug: 304772709 Test: manually, locked and unlocked Test: atest com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModelTest (cherry picked from commit d7628d5621c912399cefcddd9977199d62df320c) (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:598d7a18601a04b9904f0e170cc7c1777a3389ff) Merged-In: I384699d478e5abcee3a165afc45211b9ed96334a Change-Id: I384699d478e5abcee3a165afc45211b9ed96334a --- .../qs/footer/ui/viewmodel/FooterActionsViewModel.kt | 13 ++++++++++++- .../android/systemui/qs/footer/FooterActionsTestUtils.kt | 4 +++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt index b3596a254b7d..b9258ac1497a 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt @@ -30,6 +30,7 @@ import com.android.systemui.common.shared.model.Icon import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.globalactions.GlobalActionsDialogLite +import com.android.systemui.plugins.ActivityStarter import com.android.systemui.plugins.FalsingManager import com.android.systemui.qs.dagger.QSFlagsModule.PM_LITE_ENABLED import com.android.systemui.qs.footer.data.model.UserSwitcherStatusModel @@ -54,6 +55,7 @@ class FooterActionsViewModel( private val footerActionsInteractor: FooterActionsInteractor, private val falsingManager: FalsingManager, private val globalActionsDialogLite: GlobalActionsDialogLite, + private val activityStarter: ActivityStarter, showPowerButton: Boolean, ) { /** The context themed with the Quick Settings colors. */ @@ -222,7 +224,14 @@ class FooterActionsViewModel( return } - footerActionsInteractor.showForegroundServicesDialog(expandable) + activityStarter.dismissKeyguardThenExecute( + { + footerActionsInteractor.showForegroundServicesDialog(expandable) + false /* if the dismiss should be deferred */ + }, + null /* cancelAction */, + true /* afterKeyguardGone */ + ) } private fun onUserSwitcherClicked(expandable: Expandable) { @@ -283,6 +292,7 @@ class FooterActionsViewModel( private val falsingManager: FalsingManager, private val footerActionsInteractor: FooterActionsInteractor, private val globalActionsDialogLiteProvider: Provider, + private val activityStarter: ActivityStarter, @Named(PM_LITE_ENABLED) private val showPowerButton: Boolean, ) { /** Create a [FooterActionsViewModel] bound to the lifecycle of [lifecycleOwner]. */ @@ -308,6 +318,7 @@ class FooterActionsViewModel( footerActionsInteractor, falsingManager, globalActionsDialogLite, + activityStarter, showPowerButton, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/footer/FooterActionsTestUtils.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/footer/FooterActionsTestUtils.kt index 1a893f8c523c..a94acf9c8106 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/footer/FooterActionsTestUtils.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/footer/FooterActionsTestUtils.kt @@ -68,6 +68,7 @@ class FooterActionsTestUtils( private val testableLooper: TestableLooper, private val scheduler: TestCoroutineScheduler, ) { + private val mockActivityStarter: ActivityStarter = mock() /** Enable or disable the user switcher in the settings. */ fun setUserSwitcherEnabled(settings: GlobalSettings, enabled: Boolean, userId: Int) { settings.putBoolForUser(Settings.Global.USER_SWITCHER_ENABLED, enabled, userId) @@ -90,13 +91,14 @@ class FooterActionsTestUtils( footerActionsInteractor, falsingManager, globalActionsDialogLite, + mockActivityStarter, showPowerButton, ) } /** Create a [FooterActionsInteractor] to be used in tests. */ fun footerActionsInteractor( - activityStarter: ActivityStarter = mock(), + activityStarter: ActivityStarter = mockActivityStarter, metricsLogger: MetricsLogger = FakeMetricsLogger(), uiEventLogger: UiEventLogger = UiEventLoggerFake(), deviceProvisionedController: DeviceProvisionedController = mock(), -- cgit v1.2.3 From b1b6c7d609cb490bdecb00adec31443fc14eab02 Mon Sep 17 00:00:00 2001 From: Hongwei Wang Date: Tue, 1 Aug 2023 16:00:31 -0700 Subject: Rate limiting PiP aspect ratio change request Using CountQuotaTrack to limit how frequent an app can request aspect ratio change via PictureInPictureParams, which could result flood of PiP resizing requests and freeze the PiP window. Note that CountQuotaTrack is initialized out of the WM lock to avoid dead lock with the AM one. Bug: 283103220 Test: Manually, using the POC app Test: Manually, switching YT PiP video functions at a regular rate Test: atest WindowOrganizerTests ActivityThreadTest (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:8d814cc3b2fc94c8c47861abbcb3cec72aceb07e) Merged-In: Icb7dd17bbf7df573a9bb28f3dc56e90e78384f4f Change-Id: Icb7dd17bbf7df573a9bb28f3dc56e90e78384f4f --- .../server/wm/ActivityClientController.java | 39 ++++++++++++++++++++++ .../android/server/wm/WindowOrganizerTests.java | 6 ++++ 2 files changed, 45 insertions(+) diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java index aafff2c70b8b..f2567561ed8c 100644 --- a/services/core/java/com/android/server/wm/ActivityClientController.java +++ b/services/core/java/com/android/server/wm/ActivityClientController.java @@ -96,6 +96,7 @@ import android.view.RemoteAnimationDefinition; import android.window.SizeConfigurationBuckets; import android.window.TransitionInfo; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.AssistUtils; import com.android.internal.policy.IKeyguardDismissCallback; import com.android.internal.protolog.common.ProtoLog; @@ -104,6 +105,9 @@ import com.android.server.Watchdog; import com.android.server.pm.KnownPackages; import com.android.server.pm.pkg.AndroidPackage; import com.android.server.uri.NeededUriGrants; +import com.android.server.utils.quota.Categorizer; +import com.android.server.utils.quota.Category; +import com.android.server.utils.quota.CountQuotaTracker; import com.android.server.vr.VrManagerInternal; /** @@ -119,6 +123,13 @@ class ActivityClientController extends IActivityClientController.Stub { private final ActivityTaskSupervisor mTaskSupervisor; private final Context mContext; + // Prevent malicious app abusing the Activity#setPictureInPictureParams API + @VisibleForTesting CountQuotaTracker mSetPipAspectRatioQuotaTracker; + // Limit to 60 times / minute + private static final int SET_PIP_ASPECT_RATIO_LIMIT = 60; + // The timeWindowMs here can not be smaller than QuotaTracker#MIN_WINDOW_SIZE_MS + private static final long SET_PIP_ASPECT_RATIO_TIME_WINDOW_MS = 60_000; + /** Wrapper around VoiceInteractionServiceManager. */ private AssistUtils mAssistUtils; @@ -869,6 +880,7 @@ class ActivityClientController extends IActivityClientController.Stub { public boolean enterPictureInPictureMode(IBinder token, final PictureInPictureParams params) { final long origId = Binder.clearCallingIdentity(); try { + ensureSetPipAspectRatioQuotaTracker(); synchronized (mGlobalLock) { final ActivityRecord r = ensureValidPictureInPictureActivityParams( "enterPictureInPictureMode", token, params); @@ -883,6 +895,7 @@ class ActivityClientController extends IActivityClientController.Stub { public void setPictureInPictureParams(IBinder token, final PictureInPictureParams params) { final long origId = Binder.clearCallingIdentity(); try { + ensureSetPipAspectRatioQuotaTracker(); synchronized (mGlobalLock) { final ActivityRecord r = ensureValidPictureInPictureActivityParams( "setPictureInPictureParams", token, params); @@ -934,6 +947,19 @@ class ActivityClientController extends IActivityClientController.Stub { } } + /** + * Initialize the {@link #mSetPipAspectRatioQuotaTracker} if applicable, which should happen + * out of {@link #mGlobalLock} to avoid deadlock (AM lock is used in QuotaTrack ctor). + */ + private void ensureSetPipAspectRatioQuotaTracker() { + if (mSetPipAspectRatioQuotaTracker == null) { + mSetPipAspectRatioQuotaTracker = new CountQuotaTracker(mContext, + Categorizer.SINGLE_CATEGORIZER); + mSetPipAspectRatioQuotaTracker.setCountLimit(Category.SINGLE_CATEGORY, + SET_PIP_ASPECT_RATIO_LIMIT, SET_PIP_ASPECT_RATIO_TIME_WINDOW_MS); + } + } + /** * Checks the state of the system and the activity associated with the given {@param token} to * verify that picture-in-picture is supported for that activity. @@ -958,6 +984,19 @@ class ActivityClientController extends IActivityClientController.Stub { + ": Current activity does not support picture-in-picture."); } + // Rate limit how frequent an app can request aspect ratio change via + // Activity#setPictureInPictureParams + final int userId = UserHandle.getCallingUserId(); + if (r.pictureInPictureArgs.hasSetAspectRatio() + && params.hasSetAspectRatio() + && !r.pictureInPictureArgs.getAspectRatio().equals( + params.getAspectRatio()) + && !mSetPipAspectRatioQuotaTracker.noteEvent( + userId, r.packageName, "setPipAspectRatio")) { + throw new IllegalStateException(caller + + ": Too many PiP aspect ratio change requests from " + r.packageName); + } + final float minAspectRatio = mContext.getResources().getFloat( com.android.internal.R.dimen.config_pictureInPictureMinAspectRatio); final float maxAspectRatio = mContext.getResources().getFloat( diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java index 600681fb332c..92135e4a0206 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java @@ -1104,6 +1104,12 @@ public class WindowOrganizerTests extends WindowTestsBase { assertNotNull(o.mInfo); assertNotNull(o.mInfo.pictureInPictureParams); + // Bypass the quota check, which causes NPE in current test setup. + if (mWm.mAtmService.mActivityClientController.mSetPipAspectRatioQuotaTracker != null) { + mWm.mAtmService.mActivityClientController.mSetPipAspectRatioQuotaTracker + .setEnabled(false); + } + final PictureInPictureParams p2 = new PictureInPictureParams.Builder() .setAspectRatio(new Rational(3, 4)).build(); mWm.mAtmService.mActivityClientController.setPictureInPictureParams(record.token, p2); -- cgit v1.2.3 From c86a26c21dba024c8ac98fd70d543a39e2a15dbb Mon Sep 17 00:00:00 2001 From: Kiran S Date: Mon, 13 May 2024 05:49:06 +0000 Subject: Restrict USB poups while setup is in progress Test: Cherry pick of http://ag/27094197 Bug: 294105066 (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:2ce2e54a040342373e401f9c2e70035ede4e63ad) (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:71042ac086b3470f4086c5c76fc2b6c4e3dff263) Merged-In: I7d54534696fd73f3b94c5b4250142eed9341c5d8 Change-Id: I7d54534696fd73f3b94c5b4250142eed9341c5d8 --- .../server/usb/UsbProfileGroupSettingsManager.java | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java b/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java index f91666081e82..47f565606a1d 100644 --- a/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java +++ b/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java @@ -16,6 +16,8 @@ package com.android.server.usb; +import static android.provider.Settings.Secure.USER_SETUP_COMPLETE; + import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_MANAGED_PROFILE; import android.annotation.NonNull; @@ -42,6 +44,7 @@ import android.os.AsyncTask; import android.os.Environment; import android.os.UserHandle; import android.os.UserManager; +import android.provider.Settings; import android.service.usb.UsbProfileGroupSettingsManagerProto; import android.service.usb.UsbSettingsAccessoryPreferenceProto; import android.service.usb.UsbSettingsDevicePreferenceProto; @@ -914,10 +917,28 @@ class UsbProfileGroupSettingsManager { return; } + if (shouldRestrictOverlayActivities()) { + return; + } + // Start activity with registered intent resolveActivity(intent, matches, defaultActivity, device, null); } + private boolean shouldRestrictOverlayActivities() { + if (Settings.Secure.getIntForUser( + mContext.getContentResolver(), + USER_SETUP_COMPLETE, + /* defaultValue= */ 1, + UserHandle.CURRENT.getIdentifier()) + == 0) { + Slog.d(TAG, "restricting usb overlay activities as setup is not complete"); + return true; + } + + return false; + } + public void deviceAttachedForFixedHandler(UsbDevice device, ComponentName component) { final Intent intent = createDeviceAttachedIntent(device); -- cgit v1.2.3 From 125f5335cd0962e8fecf56d69e76fd18dffcc98c Mon Sep 17 00:00:00 2001 From: lpeter Date: Tue, 21 May 2024 07:58:43 +0000 Subject: Add unit test to test data overflow when using BinaryXmlSerializer Add the unit tests to test data overflow when calling: 1.BinaryXmlSerializer#attributeBytesHex 2.BinaryXmlSerializer#attributeBytesBase64 Bug: 307288067 Test: atest BinaryXmlTest (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:eebe3b8baf112082c3178ba7d17b5318c53b3b5f) Merged-In: I4e3f4881742f0e865eaefabb8ee134c67c6b53d9 Change-Id: I4e3f4881742f0e865eaefabb8ee134c67c6b53d9 --- .../coretests/src/android/util/BinaryXmlTest.java | 50 ++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/core/tests/coretests/src/android/util/BinaryXmlTest.java b/core/tests/coretests/src/android/util/BinaryXmlTest.java index 025e8314f5ed..da29828383b6 100644 --- a/core/tests/coretests/src/android/util/BinaryXmlTest.java +++ b/core/tests/coretests/src/android/util/BinaryXmlTest.java @@ -24,6 +24,8 @@ import static android.util.XmlTest.doVerifyRead; import static android.util.XmlTest.doVerifyWrite; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; +import static org.junit.Assert.fail; import static org.xmlpull.v1.XmlPullParser.START_TAG; import android.os.PersistableBundle; @@ -41,12 +43,15 @@ import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; +import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.charset.StandardCharsets; @RunWith(AndroidJUnit4.class) public class BinaryXmlTest { + private static final int MAX_UNSIGNED_SHORT = 65_535; + /** * Verify that we can write and read large numbers of interned * {@link String} values. @@ -170,4 +175,49 @@ public class BinaryXmlTest { } } } + + @Test + public void testAttributeBytes_BinaryDataOverflow() throws Exception { + final TypedXmlSerializer out = Xml.newBinarySerializer(); + final ByteArrayOutputStream os = new ByteArrayOutputStream(); + out.setOutput(os, StandardCharsets.UTF_8.name()); + + final byte[] testBytes = new byte[MAX_UNSIGNED_SHORT + 1]; + assertThrows(IOException.class, + () -> out.attributeBytesHex(/* namespace */ null, /* name */ "attributeBytesHex", + testBytes)); + + assertThrows(IOException.class, + () -> out.attributeBytesBase64(/* namespace */ null, /* name */ + "attributeBytesBase64", testBytes)); + } + + @Test + public void testAttributeBytesHex_MaximumBinaryData() throws Exception { + final TypedXmlSerializer out = Xml.newBinarySerializer(); + final ByteArrayOutputStream os = new ByteArrayOutputStream(); + out.setOutput(os, StandardCharsets.UTF_8.name()); + + final byte[] testBytes = new byte[MAX_UNSIGNED_SHORT]; + try { + out.attributeBytesHex(/* namespace */ null, /* name */ "attributeBytesHex", testBytes); + } catch (Exception e) { + fail("testAttributeBytesHex fails with exception: " + e.toString()); + } + } + + @Test + public void testAttributeBytesBase64_MaximumBinaryData() throws Exception { + final TypedXmlSerializer out = Xml.newBinarySerializer(); + final ByteArrayOutputStream os = new ByteArrayOutputStream(); + out.setOutput(os, StandardCharsets.UTF_8.name()); + + final byte[] testBytes = new byte[MAX_UNSIGNED_SHORT]; + try { + out.attributeBytesBase64(/* namespace */ null, /* name */ "attributeBytesBase64", + testBytes); + } catch (Exception e) { + fail("testAttributeBytesBase64 fails with exception: " + e.toString()); + } + } } -- cgit v1.2.3 From 62d324e0e5382388b284e9b1d1ced228f2077fc4 Mon Sep 17 00:00:00 2001 From: Linus Tufvesson Date: Mon, 29 Apr 2024 16:32:15 +0200 Subject: Hide SAW subwindows .. when top window is hidden through Window#setHideOverlayWindows Bug: 318683640 Test: atest CtsWindowManagerDeviceWindow:HideOverlayWindowsTest Flag: EXEMPT securityfix (cherry picked from commit c37bc9147086f497ac7b1595083836014f524d5f) (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:55d02153259003b7552e7eef70b9e4f3f0dcd45c) Merged-In: If19240f5aec2e048de80d75cbbdc00be47622d7f Change-Id: If19240f5aec2e048de80d75cbbdc00be47622d7f --- services/core/java/com/android/server/wm/WindowState.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 3dfcf1ff55a8..cdb3aa4c110d 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -3121,12 +3121,13 @@ class WindowState extends WindowContainer implements WindowManagerP } void setForceHideNonSystemOverlayWindowIfNeeded(boolean forceHide) { + final int baseType = getBaseType(); if (mSession.mCanAddInternalSystemWindow - || (!isSystemAlertWindowType(mAttrs.type) && mAttrs.type != TYPE_TOAST)) { + || (!isSystemAlertWindowType(baseType) && baseType != TYPE_TOAST)) { return; } - if (mAttrs.type == TYPE_APPLICATION_OVERLAY && mAttrs.isSystemApplicationOverlay() + if (baseType == TYPE_APPLICATION_OVERLAY && mAttrs.isSystemApplicationOverlay() && mSession.mCanCreateSystemApplicationOverlay) { return; } -- cgit v1.2.3 From 2cdc66edfaed6849dc911ee106c0709c1ad3eb28 Mon Sep 17 00:00:00 2001 From: Pavel Grafov Date: Tue, 16 Apr 2024 18:28:16 +0100 Subject: Ensure device_owners2.xml is always written. Bug: 335232744 Test: Manual, upgrading from T-QPR3 (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:3abc07421d5bed187589d6deb48da07e4c407203) Merged-In: I7a7dba56f2951e7e3699b19d2517d198dc8f9d35 Change-Id: I7a7dba56f2951e7e3699b19d2517d198dc8f9d35 --- .../devicepolicy/java/com/android/server/devicepolicy/OwnersData.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/OwnersData.java b/services/devicepolicy/java/com/android/server/devicepolicy/OwnersData.java index 608ae140450e..2ced224091a0 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/OwnersData.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/OwnersData.java @@ -354,8 +354,7 @@ class OwnersData { @Override boolean shouldWrite() { - return (mDeviceOwner != null) || (mSystemUpdatePolicy != null) - || (mSystemUpdateInfo != null); + return true; } @Override -- cgit v1.2.3 From 10e30ea1140ee0b76bf4ef652fdbb013987fcf20 Mon Sep 17 00:00:00 2001 From: Hao Ke Date: Mon, 22 Apr 2024 15:13:58 +0000 Subject: Fix READ/WRITE operation access issues on Restricted appOps. Problems were identified around read and write access to the restricted appOps, this change includes: - Filter out restricted appOps status for unprivileged readers. - Allow additional privileged appOps permission holder reading restricted appOps status. Bug: 336273802 Bug: 336323279 Test: Local test see b/336273802#comment3 Test: atest AppOpsTest#testRestrictedSettingsOpsRead (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:e31c33ea3586531ca99dd4c6d68a34ce07c1cebb) Merged-In: I09008b365e36b2c20c9a1fe5a1d52699ddb17d35 Change-Id: I09008b365e36b2c20c9a1fe5a1d52699ddb17d35 --- core/java/android/app/AppOpInfo.java | 2 +- core/java/android/app/AppOpsManager.java | 2 +- .../com/android/server/appop/AppOpsService.java | 31 ++++++++++++++++++---- 3 files changed, 28 insertions(+), 7 deletions(-) diff --git a/core/java/android/app/AppOpInfo.java b/core/java/android/app/AppOpInfo.java index 5268ec42e21c..a0f0ccaec58c 100644 --- a/core/java/android/app/AppOpInfo.java +++ b/core/java/android/app/AppOpInfo.java @@ -88,7 +88,7 @@ class AppOpInfo { /** * This specifies whether each option is only allowed to be read - * by apps with manage appops permission. + * by apps with privileged appops permission. */ public final boolean restrictRead; diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index ccd83f756730..2ec54535cdb9 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -2985,7 +2985,7 @@ public class AppOpsManager { } /** - * Retrieve whether the op can be read by apps with manage appops permission. + * Retrieve whether the op can be read by apps with privileged appops permission. * @hide */ public static boolean opRestrictsRead(int op) { diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java index 33655f748230..e2388e2918ab 100644 --- a/services/core/java/com/android/server/appop/AppOpsService.java +++ b/services/core/java/com/android/server/appop/AppOpsService.java @@ -1430,16 +1430,26 @@ public class AppOpsService extends IAppOpsService.Stub { private ArrayList collectOps(Ops pkgOps, int[] ops) { ArrayList resOps = null; + boolean shouldReturnRestrictedAppOps = mContext.checkPermission( + Manifest.permission.GET_APP_OPS_STATS, + Binder.getCallingPid(), Binder.getCallingUid()) + == PackageManager.PERMISSION_GRANTED; if (ops == null) { resOps = new ArrayList<>(); - for (int j=0; j(); } @@ -3615,10 +3625,21 @@ public class AppOpsService extends IAppOpsService.Stub { private void verifyIncomingOp(int op) { if (op >= 0 && op < AppOpsManager._NUM_OP) { - // Enforce manage appops permission if it's a restricted read op. + // Enforce privileged appops permission if it's a restricted read op. if (opRestrictsRead(op)) { - mContext.enforcePermission(Manifest.permission.MANAGE_APPOPS, - Binder.getCallingPid(), Binder.getCallingUid(), "verifyIncomingOp"); + if (!(mContext.checkPermission(Manifest.permission.MANAGE_APPOPS, + Binder.getCallingPid(), Binder.getCallingUid()) + == PackageManager.PERMISSION_GRANTED || mContext.checkPermission( + Manifest.permission.GET_APP_OPS_STATS, + Binder.getCallingPid(), Binder.getCallingUid()) + == PackageManager.PERMISSION_GRANTED || mContext.checkPermission( + Manifest.permission.MANAGE_APP_OPS_MODES, + Binder.getCallingPid(), Binder.getCallingUid()) + == PackageManager.PERMISSION_GRANTED)) { + throw new SecurityException("verifyIncomingOp: uid " + Binder.getCallingUid() + + " does not have any of {MANAGE_APPOPS, GET_APP_OPS_STATS, " + + "MANAGE_APP_OPS_MODES}"); + } } return; } -- cgit v1.2.3