diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2023-04-06 00:36:53 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2023-04-06 00:36:53 +0000 |
commit | e8eea65c85851a29eb6a1e660069414c9e04be07 (patch) | |
tree | 586d5dbf15a128f1702f27093637f28ca12836cd | |
parent | 196c3799a484dad5563c6ff63782b5e53570fb22 (diff) | |
parent | eb90a1ba3f05f1f6b19ada653836c90de07dc810 (diff) | |
download | base-android-security-11.0.0_r68.tar.gz |
Merge cherrypicks of ['googleplex-android-review.googlesource.com/21564420', 'googleplex-android-review.googlesource.com/21535256', 'googleplex-android-review.googlesource.com/21609150', 'googleplex-android-review.googlesource.com/22101922', 'googleplex-android-review.googlesource.com/21614639', 'googleplex-android-review.googlesource.com/20026095', 'googleplex-android-review.googlesource.com/22290985', 'googleplex-android-review.googlesource.com/22301867', 'googleplex-android-review.googlesource.com/22312942'] into security-aosp-rvc-release.android-security-11.0.0_r68
Change-Id: I6add43f5842300a918f1e728d175b186d2286135
28 files changed, 532 insertions, 49 deletions
diff --git a/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java b/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java index e15f0f37fc62..0e98bf0e8e00 100644 --- a/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java +++ b/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java @@ -146,6 +146,8 @@ public interface AppStandbyInternal { void setActiveAdminApps(Set<String> adminPkgs, int userId); + void setAdminProtectedPackages(Set<String> packageNames, int userId); + void onAdminDataAvailable(); void clearCarrierPrivilegedApps(); diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobStore.java b/apex/jobscheduler/service/java/com/android/server/job/JobStore.java index 2f5f555817ec..146d68fdd653 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobStore.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobStore.java @@ -687,6 +687,10 @@ public final class JobStore { } } catch (XmlPullParserException | IOException e) { Slog.wtf(TAG, "Error jobstore xml.", e); + } catch (Exception e) { + // Crashing at this point would result in a boot loop, so live with a general + // Exception for system stability's sake. + Slog.wtf(TAG, "Unexpected exception", e); } finally { if (mPersistInfo.countAllJobsLoaded < 0) { // Only set them once. mPersistInfo.countAllJobsLoaded = numJobs; @@ -817,6 +821,15 @@ public final class JobStore { } catch (NumberFormatException e) { Slog.d(TAG, "Error reading constraints, skipping."); return null; + } catch (XmlPullParserException e) { + Slog.d(TAG, "Error Parser Exception.", e); + return null; + } catch (IOException e) { + Slog.d(TAG, "Error I/O Exception.", e); + return null; + } catch (IllegalArgumentException e) { + Slog.e(TAG, "Constraints contained invalid data", e); + return null; } parser.next(); // Consume </constraints> @@ -912,8 +925,14 @@ public final class JobStore { return null; } - PersistableBundle extras = PersistableBundle.restoreFromXml(parser); - jobBuilder.setExtras(extras); + final PersistableBundle extras; + try { + extras = PersistableBundle.restoreFromXml(parser); + jobBuilder.setExtras(extras); + } catch (IllegalArgumentException e) { + Slog.e(TAG, "Persisted extras contained invalid data", e); + return null; + } parser.nextTag(); // Consume </extras> final JobInfo builtJob; @@ -959,7 +978,8 @@ public final class JobStore { return new JobInfo.Builder(jobId, cname); } - private void buildConstraintsFromXml(JobInfo.Builder jobBuilder, XmlPullParser parser) { + private void buildConstraintsFromXml(JobInfo.Builder jobBuilder, XmlPullParser parser) + throws XmlPullParserException, IOException { String val; final String netCapabilities = parser.getAttributeValue(null, "net-capabilities"); diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java index 0cadbfd23dba..c9e026d73894 100644 --- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java +++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java @@ -233,6 +233,10 @@ public class AppStandbyController implements AppStandbyInternal { @GuardedBy("mActiveAdminApps") private final SparseArray<Set<String>> mActiveAdminApps = new SparseArray<>(); + /** List of admin protected packages. Can contain {@link android.os.UserHandle#USER_ALL}. */ + @GuardedBy("mAdminProtectedPackages") + private final SparseArray<Set<String>> mAdminProtectedPackages = new SparseArray<>(); + /** * Set of system apps that are headless (don't have any declared activities, enabled or * disabled). Presence in this map indicates that the app is a headless system app. @@ -1020,6 +1024,9 @@ public class AppStandbyController implements AppStandbyInternal { synchronized (mActiveAdminApps) { mActiveAdminApps.remove(userId); } + synchronized (mAdminProtectedPackages) { + mAdminProtectedPackages.remove(userId); + } } } @@ -1109,6 +1116,10 @@ public class AppStandbyController implements AppStandbyInternal { return STANDBY_BUCKET_EXEMPTED; } + if (isAdminProtectedPackages(packageName, userId)) { + return STANDBY_BUCKET_EXEMPTED; + } + if (isActiveNetworkScorer(packageName)) { return STANDBY_BUCKET_EXEMPTED; } @@ -1507,6 +1518,17 @@ public class AppStandbyController implements AppStandbyInternal { } } + private boolean isAdminProtectedPackages(String packageName, int userId) { + synchronized (mAdminProtectedPackages) { + if (mAdminProtectedPackages.contains(UserHandle.USER_ALL) + && mAdminProtectedPackages.get(UserHandle.USER_ALL).contains(packageName)) { + return true; + } + return mAdminProtectedPackages.contains(userId) + && mAdminProtectedPackages.get(userId).contains(packageName); + } + } + @Override public void addActiveDeviceAdmin(String adminPkg, int userId) { synchronized (mActiveAdminApps) { @@ -1531,6 +1553,17 @@ public class AppStandbyController implements AppStandbyInternal { } @Override + public void setAdminProtectedPackages(Set<String> packageNames, int userId) { + synchronized (mAdminProtectedPackages) { + if (packageNames == null || packageNames.isEmpty()) { + mAdminProtectedPackages.remove(userId); + } else { + mAdminProtectedPackages.put(userId, packageNames); + } + } + } + + @Override public void onAdminDataAvailable() { mAdminDataAvailableLatch.countDown(); } @@ -1552,6 +1585,13 @@ public class AppStandbyController implements AppStandbyInternal { } } + @VisibleForTesting + Set<String> getAdminProtectedPackagesForTest(int userId) { + synchronized (mAdminProtectedPackages) { + return mAdminProtectedPackages.get(userId); + } + } + /** * Returns {@code true} if the supplied package is the device provisioning app. Otherwise, * returns {@code false}. diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index c72234f8fea4..3768b0ce1991 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -11990,7 +11990,8 @@ public class DevicePolicyManager { /** * Called by Device owner to disable user control over apps. User will not be able to clear - * app data or force-stop packages. + * app data or force-stop packages. Packages with user control disabled are exempted from + * App Standby Buckets. * * @param admin which {@link DeviceAdminReceiver} this request is associated with * @param packages The package names for the apps. diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java index 3fef92b203b6..e2351ee89c42 100644 --- a/core/java/android/appwidget/AppWidgetHostView.java +++ b/core/java/android/appwidget/AppWidgetHostView.java @@ -28,6 +28,7 @@ import android.content.pm.LauncherActivityInfo; import android.content.pm.LauncherApps; import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.Resources; +import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Rect; import android.os.Bundle; @@ -250,20 +251,27 @@ public class AppWidgetHostView extends FrameLayout { super.onLayout(changed, left, top, right, bottom); } catch (final RuntimeException e) { Log.e(TAG, "Remote provider threw runtime exception, using error view instead.", e); - removeViewInLayout(mView); - View child = getErrorView(); - prepareView(child); - addViewInLayout(child, 0, child.getLayoutParams()); - measureChild(child, MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY), - MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY)); - child.layout(0, 0, child.getMeasuredWidth() + mPaddingLeft + mPaddingRight, - child.getMeasuredHeight() + mPaddingTop + mPaddingBottom); - mView = child; - mViewMode = VIEW_MODE_ERROR; + handleViewError(); } } /** + * Remove bad view and replace with error message view + */ + private void handleViewError() { + removeViewInLayout(mView); + View child = getErrorView(); + prepareView(child); + addViewInLayout(child, 0, child.getLayoutParams()); + measureChild(child, MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY)); + child.layout(0, 0, child.getMeasuredWidth() + mPaddingLeft + mPaddingRight, + child.getMeasuredHeight() + mPaddingTop + mPaddingBottom); + mView = child; + mViewMode = VIEW_MODE_ERROR; + } + + /** * Provide guidance about the size of this widget to the AppWidgetManager. The widths and * heights should correspond to the full area the AppWidgetHostView is given. Padding added by * the framework will be accounted for automatically. This information gets embedded into the @@ -725,4 +733,15 @@ public class AppWidgetHostView extends FrameLayout { } }; } + + @Override + protected void dispatchDraw(Canvas canvas) { + try { + super.dispatchDraw(canvas); + } catch (Exception e) { + // Catch draw exceptions that may be caused by RemoteViews + Log.e(TAG, "Drawing view failed: " + e); + post(this::handleViewError); + } + } } diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index 14cf258f18ab..5048185565c7 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -16,6 +16,8 @@ package com.android.internal.app; +import static android.content.ContentProvider.getUserIdFromUri; + import static java.lang.annotation.RetentionPolicy.SOURCE; import android.animation.Animator; @@ -148,6 +150,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.stream.Collectors; /** * The Chooser Activity handles intent resolution specifically for sharing intents - @@ -1301,7 +1304,7 @@ public class ChooserActivity extends ResolverActivity implements ImageView previewThumbnailView = contentPreviewLayout.findViewById( R.id.content_preview_thumbnail); - if (previewThumbnail == null) { + if (!validForContentPreview(previewThumbnail)) { previewThumbnailView.setVisibility(View.GONE); } else { mPreviewCoord = new ContentPreviewCoordinator(contentPreviewLayout, false); @@ -1327,6 +1330,10 @@ public class ChooserActivity extends ResolverActivity implements String action = targetIntent.getAction(); if (Intent.ACTION_SEND.equals(action)) { Uri uri = targetIntent.getParcelableExtra(Intent.EXTRA_STREAM); + if (!validForContentPreview(uri)) { + contentPreviewLayout.setVisibility(View.GONE); + return contentPreviewLayout; + } mPreviewCoord.loadUriIntoView(R.id.content_preview_image_1_large, uri, 0); } else { ContentResolver resolver = getContentResolver(); @@ -1334,7 +1341,7 @@ public class ChooserActivity extends ResolverActivity implements List<Uri> uris = targetIntent.getParcelableArrayListExtra(Intent.EXTRA_STREAM); List<Uri> imageUris = new ArrayList<>(); for (Uri uri : uris) { - if (isImageType(resolver.getType(uri))) { + if (validForContentPreview(uri) && isImageType(resolver.getType(uri))) { imageUris.add(uri); } } @@ -1441,9 +1448,16 @@ public class ChooserActivity extends ResolverActivity implements String action = targetIntent.getAction(); if (Intent.ACTION_SEND.equals(action)) { Uri uri = targetIntent.getParcelableExtra(Intent.EXTRA_STREAM); + if (!validForContentPreview(uri)) { + contentPreviewLayout.setVisibility(View.GONE); + return contentPreviewLayout; + } loadFileUriIntoView(uri, contentPreviewLayout); } else { List<Uri> uris = targetIntent.getParcelableArrayListExtra(Intent.EXTRA_STREAM); + uris = uris.stream() + .filter(ChooserActivity::validForContentPreview) + .collect(Collectors.toList()); int uriCount = uris.size(); if (uriCount == 0) { @@ -1497,6 +1511,24 @@ public class ChooserActivity extends ResolverActivity implements } } + /** + * Indicate if the incoming content URI should be allowed. + * + * @param uri the uri to test + * @return true if the URI is allowed for content preview + */ + private static boolean validForContentPreview(Uri uri) throws SecurityException { + if (uri == null) { + return false; + } + int userId = getUserIdFromUri(uri, UserHandle.USER_CURRENT); + if (userId != UserHandle.USER_CURRENT && userId != UserHandle.myUserId()) { + Log.e(TAG, "dropped invalid content URI belonging to user " + userId); + return false; + } + return true; + } + @VisibleForTesting protected boolean isImageType(String mimeType) { return mimeType != null && mimeType.startsWith("image/"); diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/ServiceListing.java b/packages/SettingsLib/src/com/android/settingslib/applications/ServiceListing.java index bd9e760acfda..c8bcabff1094 100644 --- a/packages/SettingsLib/src/com/android/settingslib/applications/ServiceListing.java +++ b/packages/SettingsLib/src/com/android/settingslib/applications/ServiceListing.java @@ -35,6 +35,7 @@ import android.util.Slog; import java.util.ArrayList; import java.util.HashSet; import java.util.List; +import java.util.function.Predicate; /** * Class for managing services matching a given intent and requesting a given permission. @@ -51,12 +52,13 @@ public class ServiceListing { private final HashSet<ComponentName> mEnabledServices = new HashSet<>(); private final List<ServiceInfo> mServices = new ArrayList<>(); private final List<Callback> mCallbacks = new ArrayList<>(); + private final Predicate mValidator; private boolean mListening; private ServiceListing(Context context, String tag, String setting, String intentAction, String permission, String noun, - boolean addDeviceLockedFlags) { + boolean addDeviceLockedFlags, Predicate validator) { mContentResolver = context.getContentResolver(); mContext = context; mTag = tag; @@ -65,6 +67,7 @@ public class ServiceListing { mPermission = permission; mNoun = noun; mAddDeviceLockedFlags = addDeviceLockedFlags; + mValidator = validator; } public void addCallback(Callback callback) { @@ -137,7 +140,6 @@ public class ServiceListing { final PackageManager pmWrapper = mContext.getPackageManager(); List<ResolveInfo> installedServices = pmWrapper.queryIntentServicesAsUser( new Intent(mIntentAction), flags, user); - for (ResolveInfo resolveInfo : installedServices) { ServiceInfo info = resolveInfo.serviceInfo; @@ -148,6 +150,9 @@ public class ServiceListing { + mPermission); continue; } + if (mValidator != null && !mValidator.test(info)) { + continue; + } mServices.add(info); } for (Callback callback : mCallbacks) { @@ -194,6 +199,7 @@ public class ServiceListing { private String mPermission; private String mNoun; private boolean mAddDeviceLockedFlags = false; + private Predicate mValidator; public Builder(Context context) { mContext = context; @@ -224,6 +230,11 @@ public class ServiceListing { return this; } + public Builder setValidator(Predicate<ServiceInfo> validator) { + mValidator = validator; + return this; + } + /** * Set to true to add support for both MATCH_DIRECT_BOOT_AWARE and * MATCH_DIRECT_BOOT_UNAWARE flags when querying PackageManager. Required to get results @@ -236,7 +247,7 @@ public class ServiceListing { public ServiceListing build() { return new ServiceListing(mContext, mTag, mSetting, mIntentAction, mPermission, mNoun, - mAddDeviceLockedFlags); + mAddDeviceLockedFlags, mValidator); } } } diff --git a/packages/SettingsLib/tests/robotests/Android.bp b/packages/SettingsLib/tests/robotests/Android.bp index 3756c3bf7704..723160d68771 100644 --- a/packages/SettingsLib/tests/robotests/Android.bp +++ b/packages/SettingsLib/tests/robotests/Android.bp @@ -34,6 +34,7 @@ android_robolectric_test { srcs: ["src/**/*.java"], static_libs: [ "SettingsLib-robo-testutils", + "androidx.test.core", ], java_resource_dirs: ["config"], instrumentation_for: "SettingsLibShell", diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ServiceListingTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ServiceListingTest.java index f7fd25b9fb7d..7ff0988c494d 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ServiceListingTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ServiceListingTest.java @@ -18,20 +18,35 @@ package com.android.settingslib.applications; import static com.google.common.truth.Truth.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyList; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import android.content.ComponentName; +import android.content.Context; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.content.pm.ServiceInfo; import android.provider.Settings; +import androidx.test.core.app.ApplicationProvider; + +import com.google.common.collect.ImmutableList; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; +import java.util.List; + @RunWith(RobolectricTestRunner.class) public class ServiceListingTest { @@ -39,16 +54,97 @@ public class ServiceListingTest { private static final String TEST_INTENT = "com.example.intent"; private ServiceListing mServiceListing; + private Context mContext; + private PackageManager mPm; @Before public void setUp() { - mServiceListing = new ServiceListing.Builder(RuntimeEnvironment.application) + mPm = mock(PackageManager.class); + mContext = spy(ApplicationProvider.getApplicationContext()); + when(mContext.getPackageManager()).thenReturn(mPm); + + mServiceListing = new ServiceListing.Builder(mContext) + .setTag("testTag") + .setSetting(TEST_SETTING) + .setNoun("testNoun") + .setIntentAction(TEST_INTENT) + .setPermission("testPermission") + .build(); + } + + @Test + public void testValidator() { + ServiceInfo s1 = new ServiceInfo(); + s1.permission = "testPermission"; + s1.packageName = "pkg"; + ServiceInfo s2 = new ServiceInfo(); + s2.permission = "testPermission"; + s2.packageName = "pkg2"; + ResolveInfo r1 = new ResolveInfo(); + r1.serviceInfo = s1; + ResolveInfo r2 = new ResolveInfo(); + r2.serviceInfo = s2; + + when(mPm.queryIntentServicesAsUser(any(), anyInt(), anyInt())).thenReturn( + ImmutableList.of(r1, r2)); + + mServiceListing = new ServiceListing.Builder(mContext) + .setTag("testTag") + .setSetting(TEST_SETTING) + .setNoun("testNoun") + .setIntentAction(TEST_INTENT) + .setValidator(info -> { + if (info.packageName.equals("pkg")) { + return true; + } + return false; + }) + .setPermission("testPermission") + .build(); + ServiceListing.Callback callback = mock(ServiceListing.Callback.class); + mServiceListing.addCallback(callback); + mServiceListing.reload(); + + verify(mPm).queryIntentServicesAsUser(any(), anyInt(), anyInt()); + ArgumentCaptor<List<ServiceInfo>> captor = ArgumentCaptor.forClass(List.class); + verify(callback, times(1)).onServicesReloaded(captor.capture()); + + assertThat(captor.getValue().size()).isEqualTo(1); + assertThat(captor.getValue().get(0)).isEqualTo(s1); + } + + @Test + public void testNoValidator() { + ServiceInfo s1 = new ServiceInfo(); + s1.permission = "testPermission"; + s1.packageName = "pkg"; + ServiceInfo s2 = new ServiceInfo(); + s2.permission = "testPermission"; + s2.packageName = "pkg2"; + ResolveInfo r1 = new ResolveInfo(); + r1.serviceInfo = s1; + ResolveInfo r2 = new ResolveInfo(); + r2.serviceInfo = s2; + + when(mPm.queryIntentServicesAsUser(any(), anyInt(), anyInt())).thenReturn( + ImmutableList.of(r1, r2)); + + mServiceListing = new ServiceListing.Builder(mContext) .setTag("testTag") .setSetting(TEST_SETTING) .setNoun("testNoun") .setIntentAction(TEST_INTENT) .setPermission("testPermission") .build(); + ServiceListing.Callback callback = mock(ServiceListing.Callback.class); + mServiceListing.addCallback(callback); + mServiceListing.reload(); + + verify(mPm).queryIntentServicesAsUser(any(), anyInt(), anyInt()); + ArgumentCaptor<List<ServiceInfo>> captor = ArgumentCaptor.forClass(List.class); + verify(callback, times(1)).onServicesReloaded(captor.capture()); + + assertThat(captor.getValue().size()).isEqualTo(2); } @Test diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java index 93adb7d84663..8374c370afe5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java @@ -18,6 +18,7 @@ package com.android.systemui.statusbar.notification.interruption; import static com.android.systemui.statusbar.StatusBarState.SHADE; +import android.app.Notification; import android.app.NotificationManager; import android.content.ContentResolver; import android.database.ContentObserver; @@ -209,6 +210,20 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter return false; } + // If the notification has suppressive BubbleMetadata, block FSI and warn. + Notification.BubbleMetadata bubbleMetadata = sbn.getNotification().getBubbleMetadata(); + if (bubbleMetadata != null && bubbleMetadata.isNotificationSuppressed()) { + // b/274759612: Detect and report an event when a notification has both an FSI and a + // suppressive BubbleMetadata, and now correctly block the FSI from firing. + final int uid = entry.getSbn().getUid(); + android.util.EventLog.writeEvent(0x534e4554, "274759612", uid, "bubbleMetadata"); + if (DEBUG) { + Log.w(TAG, "No FullScreenIntent: WARNING: BubbleMetadata may prevent HUN: " + + entry.getKey()); + } + return false; + } + // If the screen is off, then launch the FullScreenIntent if (!mPowerManager.isInteractive()) { if (DEBUG) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java index 94e12e82f850..5809dc4b9c70 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java @@ -65,6 +65,7 @@ import android.widget.ImageView; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; +import com.android.internal.statusbar.IStatusBarService; import com.android.internal.util.ContrastColorUtil; import com.android.internal.widget.CachingIconView; import com.android.systemui.Dependency; @@ -1612,7 +1613,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView OnAppOpsClickListener onAppOpsClickListener, FalsingManager falsingManager, StatusBarStateController statusBarStateController, - PeopleNotificationIdentifier peopleNotificationIdentifier) { + PeopleNotificationIdentifier peopleNotificationIdentifier, + IStatusBarService statusBarService) { mAppName = appName; if (mMenuRow == null) { mMenuRow = new NotificationMenuRow(mContext, peopleNotificationIdentifier); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java index 7a6109d2ce78..8c92e730cd66 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java @@ -22,6 +22,8 @@ import static com.android.systemui.statusbar.NotificationRemoteInputManager.ENAB import android.view.View; import android.view.ViewGroup; +import androidx.annotation.NonNull; +import com.android.internal.statusbar.IStatusBarService; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; import com.android.systemui.plugins.statusbar.StatusBarStateController; @@ -69,6 +71,7 @@ public class ExpandableNotificationRowController { private final FalsingManager mFalsingManager; private final boolean mAllowLongPress; private final PeopleNotificationIdentifier mPeopleNotificationIdentifier; + private final IStatusBarService mStatusBarService; @Inject public ExpandableNotificationRowController(ExpandableNotificationRow view, @@ -84,7 +87,8 @@ public class ExpandableNotificationRowController { NotificationGutsManager notificationGutsManager, @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) boolean allowLongPress, @DismissRunnable Runnable onDismissRunnable, FalsingManager falsingManager, - PeopleNotificationIdentifier peopleNotificationIdentifier) { + PeopleNotificationIdentifier peopleNotificationIdentifier, + IStatusBarService statusBarService) { mView = view; mActivatableNotificationViewController = activatableNotificationViewController; mMediaManager = mediaManager; @@ -105,6 +109,7 @@ public class ExpandableNotificationRowController { mAllowLongPress = allowLongPress; mFalsingManager = falsingManager; mPeopleNotificationIdentifier = peopleNotificationIdentifier; + mStatusBarService = statusBarService; } /** @@ -125,7 +130,8 @@ public class ExpandableNotificationRowController { mOnAppOpsClickListener, mFalsingManager, mStatusBarStateController, - mPeopleNotificationIdentifier + mPeopleNotificationIdentifier, + mStatusBarService ); mView.setOnDismissRunnable(mOnDismissRunnable); mView.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java index 582e3e5b6c34..208cf466d0dc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java @@ -429,6 +429,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder CancellationSignal cancellationSignal = new CancellationSignal(); cancellationSignal.setOnCancelListener( () -> runningInflations.values().forEach(CancellationSignal::cancel)); + return cancellationSignal; } @@ -694,6 +695,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder public static class AsyncInflationTask extends AsyncTask<Void, Void, InflationProgress> implements InflationCallback, InflationTask { + private static final long IMG_PRELOAD_TIMEOUT_MS = 1000L; private final NotificationEntry mEntry; private final Context mContext; private final boolean mInflateSynchronously; @@ -776,10 +778,16 @@ public class NotificationContentInflater implements NotificationRowContentBinder InflationProgress inflationProgress = createRemoteViews(mReInflateFlags, recoveredBuilder, mIsLowPriority, mUsesIncreasedHeight, mUsesIncreasedHeadsUpHeight, packageContext); - return inflateSmartReplyViews(inflationProgress, mReInflateFlags, mEntry, + + InflationProgress result = inflateSmartReplyViews(inflationProgress, mReInflateFlags, mEntry, mRow.getContext(), packageContext, mRow.getHeadsUpManager(), mSmartReplyConstants, mSmartReplyController, mRow.getExistingSmartRepliesAndActions()); + + // wait for image resolver to finish preloading + mRow.getImageResolver().waitForPreloadedImages(IMG_PRELOAD_TIMEOUT_MS); + + return result; } catch (Exception e) { mError = e; return null; @@ -814,6 +822,9 @@ public class NotificationContentInflater implements NotificationRowContentBinder mCallback.handleInflationException(mRow.getEntry(), new InflationException("Couldn't inflate contentViews" + e)); } + + // Cancel any image loading tasks, not useful any more + mRow.getImageResolver().cancelRunningTasks(); } @Override @@ -840,6 +851,9 @@ public class NotificationContentInflater implements NotificationRowContentBinder // Notify the resolver that the inflation task has finished, // try to purge unnecessary cached entries. mRow.getImageResolver().purgeCache(); + + // Cancel any image loading tasks that have not completed at this point + mRow.getImageResolver().cancelRunningTasks(); } private class RtlEnabledContext extends ContextWrapper { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java index 1f5b063b0aa2..411c02e3be1d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java @@ -24,9 +24,11 @@ import android.annotation.Nullable; import android.app.Notification; import android.app.PendingIntent; import android.content.Context; +import android.graphics.Canvas; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.os.Build; +import android.os.RemoteException; import android.provider.Settings; import android.service.notification.StatusBarNotification; import android.util.ArrayMap; @@ -45,6 +47,7 @@ import android.widget.LinearLayout; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ContrastColorUtil; import com.android.systemui.Dependency; +import com.android.internal.statusbar.IStatusBarService; import com.android.systemui.R; import com.android.systemui.statusbar.MediaTransferManager; import com.android.systemui.statusbar.RemoteInputController; @@ -126,6 +129,8 @@ public class NotificationContentView extends FrameLayout { private RemoteInputController mRemoteInputController; private Runnable mExpandedVisibleListener; private PeopleNotificationIdentifier mPeopleIdentifier; + private IStatusBarService mStatusBarService; + /** * List of listeners for when content views become inactive (i.e. not the showing view). */ @@ -182,6 +187,7 @@ public class NotificationContentView extends FrameLayout { mMediaTransferManager = new MediaTransferManager(getContext()); mSmartReplyConstants = Dependency.get(SmartReplyConstants.class); mSmartReplyController = Dependency.get(SmartReplyController.class); + mStatusBarService = Dependency.get(IStatusBarService.class); initView(); } @@ -1965,4 +1971,35 @@ public class NotificationContentView extends FrameLayout { } return Notification.COLOR_INVALID; } + + @Override + protected void dispatchDraw(Canvas canvas) { + try { + super.dispatchDraw(canvas); + } catch (Exception e) { + // Catch draw exceptions that may be caused by RemoteViews + Log.e(TAG, "Drawing view failed: " + e); + cancelNotification(e); + } + } + + private void cancelNotification(Exception exception) { + try { + setVisibility(GONE); + if (mStatusBarService != null) { + // report notification inflation errors back up + // to notification delegates + mStatusBarService.onNotificationError( + mStatusBarNotification.getPackageName(), + mStatusBarNotification.getTag(), + mStatusBarNotification.getId(), + mStatusBarNotification.getUid(), + mStatusBarNotification.getInitialPid(), + exception.getMessage(), + mStatusBarNotification.getUser().getIdentifier()); + } + } catch (RemoteException ex) { + Log.e(TAG, "cancelNotification failed: " + ex); + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageCache.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageCache.java index 4b0e2ffd5d7f..6fdc8a3dce0b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageCache.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageCache.java @@ -21,10 +21,12 @@ import android.net.Uri; import android.os.AsyncTask; import android.util.Log; -import java.io.IOException; import java.util.Set; +import java.util.concurrent.CancellationException; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; /** * A cache for inline images of image messages. @@ -57,12 +59,13 @@ public class NotificationInlineImageCache implements NotificationInlineImageReso } @Override - public Drawable get(Uri uri) { + public Drawable get(Uri uri, long timeoutMs) { Drawable result = null; try { - result = mCache.get(uri).get(); - } catch (InterruptedException | ExecutionException ex) { - Log.d(TAG, "get: Failed get image from " + uri); + result = mCache.get(uri).get(timeoutMs, TimeUnit.MILLISECONDS); + } catch (InterruptedException | ExecutionException + | TimeoutException | CancellationException ex) { + Log.d(TAG, "get: Failed get image from " + uri + " " + ex); } return result; } @@ -73,6 +76,15 @@ public class NotificationInlineImageCache implements NotificationInlineImageReso mCache.entrySet().removeIf(entry -> !wantedSet.contains(entry.getKey())); } + @Override + public void cancelRunningTasks() { + mCache.forEach((key, value) -> { + if (value.getStatus() != AsyncTask.Status.FINISHED) { + value.cancel(true); + } + }); + } + private static class PreloadImageTask extends AsyncTask<Uri, Void, Drawable> { private final NotificationInlineImageResolver mResolver; @@ -87,7 +99,7 @@ public class NotificationInlineImageCache implements NotificationInlineImageReso try { drawable = mResolver.resolveImage(target); - } catch (IOException | SecurityException ex) { + } catch (Exception ex) { Log.d(TAG, "PreloadImageTask: Resolve failed from " + target, ex); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java index 7bd192d850c1..b240855fe6e9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java @@ -26,6 +26,7 @@ import android.graphics.drawable.Icon; import android.net.Uri; import android.os.Bundle; import android.os.Parcelable; +import android.os.SystemClock; import android.util.Log; import com.android.internal.R; @@ -49,6 +50,9 @@ import java.util.Set; public class NotificationInlineImageResolver implements ImageResolver { private static final String TAG = NotificationInlineImageResolver.class.getSimpleName(); + // Timeout for loading images from ImageCache when calling from UI thread + private static final long MAX_UI_THREAD_TIMEOUT_MS = 100L; + private final Context mContext; private final ImageCache mImageCache; private Set<Uri> mWantedUriSet; @@ -132,16 +136,20 @@ public class NotificationInlineImageResolver implements ImageResolver { return image; } + /** + * Loads an image from the Uri. + * This method is synchronous and is usually called from the Main thread. + * It will time-out after MAX_UI_THREAD_TIMEOUT_MS. + * + * @param uri Uri of the target image. + * @return drawable of the image, null if loading failed/timeout + */ @Override public Drawable loadImage(Uri uri) { Drawable result = null; try { if (hasCache()) { - // if the uri isn't currently cached, try caching it first - if (!mImageCache.hasEntry(uri)) { - mImageCache.preload((uri)); - } - result = mImageCache.get(uri); + result = loadImageFromCache(uri, MAX_UI_THREAD_TIMEOUT_MS); } else { result = resolveImage(uri); } @@ -151,6 +159,14 @@ public class NotificationInlineImageResolver implements ImageResolver { return result; } + private Drawable loadImageFromCache(Uri uri, long timeoutMs) { + // if the uri isn't currently cached, try caching it first + if (!mImageCache.hasEntry(uri)) { + mImageCache.preload((uri)); + } + return mImageCache.get(uri, timeoutMs); + } + /** * Resolve the message list from specified notification and * refresh internal cache according to the result. @@ -223,6 +239,30 @@ public class NotificationInlineImageResolver implements ImageResolver { } /** + * Wait for a maximum timeout for images to finish preloading + * @param timeoutMs total timeout time + */ + void waitForPreloadedImages(long timeoutMs) { + if (!hasCache()) { + return; + } + Set<Uri> preloadedUris = getWantedUriSet(); + if (preloadedUris != null) { + // Decrement remaining timeout after each image check + long endTimeMs = SystemClock.elapsedRealtime() + timeoutMs; + preloadedUris.forEach( + uri -> loadImageFromCache(uri, endTimeMs - SystemClock.elapsedRealtime())); + } + } + + void cancelRunningTasks() { + if (!hasCache()) { + return; + } + mImageCache.cancelRunningTasks(); + } + + /** * A interface for internal cache implementation of this resolver. */ interface ImageCache { @@ -231,7 +271,7 @@ public class NotificationInlineImageResolver implements ImageResolver { * @param uri The uri of the image. * @return Drawable of the image. */ - Drawable get(Uri uri); + Drawable get(Uri uri, long timeoutMs); /** * Set the image resolver that actually resolves image from specified uri. @@ -256,6 +296,11 @@ public class NotificationInlineImageResolver implements ImageResolver { * Purge unnecessary entries in the cache. */ void purge(); + + /** + * Cancel all unfinished image loading tasks + */ + void cancelRunningTasks(); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java index f4455ddb25bc..fb945f3ac3ca 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java @@ -435,9 +435,27 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase { .isFalse(); } + + @Test + public void testShouldNotFullScreen_isSuppressedByBubbleMetadata() throws RemoteException { + NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false); + Notification.BubbleMetadata bubbleMetadata = new Notification.BubbleMetadata.Builder("foo") + .setSuppressNotification(true).build(); + entry.getSbn().getNotification().setBubbleMetadata(bubbleMetadata); + when(mPowerManager.isInteractive()).thenReturn(false); + when(mDreamManager.isDreaming()).thenReturn(true); + when(mStatusBarStateController.getState()).thenReturn(KEYGUARD); + + assertThat(mNotifInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry)) + .isFalse(); + } + @Test public void testShouldFullScreen_notInteractive() throws RemoteException { NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false); + Notification.BubbleMetadata bubbleMetadata = new Notification.BubbleMetadata.Builder("foo") + .setSuppressNotification(false).build(); + entry.getSbn().getNotification().setBubbleMetadata(bubbleMetadata); when(mPowerManager.isInteractive()).thenReturn(false); when(mDreamManager.isDreaming()).thenReturn(false); when(mStatusBarStateController.getState()).thenReturn(SHADE); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java index 7dfead7575a9..fada48d8735c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java @@ -40,6 +40,7 @@ import android.testing.TestableLooper; import androidx.asynclayoutinflater.view.AsyncLayoutInflater; import androidx.test.filters.SmallTest; +import com.android.internal.statusbar.IStatusBarService; import com.android.internal.util.NotificationMessagingUtil; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; @@ -244,7 +245,8 @@ public class NotificationEntryManagerInflationTest extends SysuiTestCase { true, null, mFalsingManager, - mPeopleNotificationIdentifier + mPeopleNotificationIdentifier, + mock(IStatusBarService.class) )); when(mNotificationRowComponentBuilder.activatableNotificationView(any())) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java index b9eb4d1e29c2..de2e50727039 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java @@ -43,6 +43,7 @@ import android.text.TextUtils; import android.view.LayoutInflater; import android.widget.RemoteViews; +import com.android.internal.statusbar.IStatusBarService; import com.android.systemui.TestableDependency; import com.android.systemui.bubbles.BubbleController; import com.android.systemui.bubbles.BubblesTestActivity; @@ -422,7 +423,9 @@ public class NotificationTestHelper { mock(ExpandableNotificationRow.OnAppOpsClickListener.class), mock(FalsingManager.class), mStatusBarStateController, - mPeopleNotificationIdentifier); + mPeopleNotificationIdentifier, + mock(IStatusBarService.class)); + row.setAboveShelfChangedListener(aboveShelf -> { }); mBindStage.getStageParams(entry).requireContentViews(extraInflationFlags); inflateAndWait(entry); diff --git a/services/core/java/android/app/usage/UsageStatsManagerInternal.java b/services/core/java/android/app/usage/UsageStatsManagerInternal.java index fa84427ac064..920fe1b62417 100644 --- a/services/core/java/android/app/usage/UsageStatsManagerInternal.java +++ b/services/core/java/android/app/usage/UsageStatsManagerInternal.java @@ -199,6 +199,16 @@ public abstract class UsageStatsManagerInternal { public abstract void setActiveAdminApps(Set<String> adminApps, int userId); /** + * Called by DevicePolicyManagerService to inform about the protected packages for a user. + * User control will be disabled for protected packages. + * + * @param packageNames the set of protected packages for {@code userId}. + * @param userId the userId to which the protected packages belong. + */ + public abstract void setAdminProtectedPackages(@Nullable Set<String> packageNames, + @UserIdInt int userId); + + /** * Called by DevicePolicyManagerService during boot to inform that admin data is loaded and * pushed to UsageStatsService. */ diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java index 7255afbbfbba..ef8670209c44 100644 --- a/services/core/java/com/android/server/accounts/AccountManagerService.java +++ b/services/core/java/com/android/server/accounts/AccountManagerService.java @@ -4797,10 +4797,6 @@ public class AccountManagerService if (intent.getClipData() == null) { intent.setClipData(ClipData.newPlainText(null, null)); } - intent.setFlags(intent.getFlags() & ~(Intent.FLAG_GRANT_READ_URI_PERMISSION - | Intent.FLAG_GRANT_WRITE_URI_PERMISSION - | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION - | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION)); long bid = Binder.clearCallingIdentity(); try { PackageManager pm = mContext.getPackageManager(); @@ -4847,7 +4843,19 @@ public class AccountManagerService if (intent == null) { return (simulateIntent == null); } - return intent.filterEquals(simulateIntent); + if (!intent.filterEquals(simulateIntent)) { + return false; + } + + if (intent.getSelector() != simulateIntent.getSelector()) { + return false; + } + + int prohibitedFlags = Intent.FLAG_GRANT_READ_URI_PERMISSION + | Intent.FLAG_GRANT_WRITE_URI_PERMISSION + | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION + | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION; + return (simulateIntent.getFlags() & prohibitedFlags) == 0; } private boolean isExportedSystemActivity(ActivityInfo activityInfo) { diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 7d6804274e63..01aa92efc765 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -1200,6 +1200,12 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } ensureActivityConfiguration(0 /* globalChanges */, PRESERVE_WINDOWS, true /* ignoreVisibility */); + if (inPictureInPictureMode && findMainWindow() == null) { + // Prevent malicious app entering PiP without valid WindowState, which can in turn + // result a non-touchable PiP window since the InputConsumer for PiP requires it. + EventLog.writeEvent(0x534e4554, "265293293", -1, ""); + removeImmediately(); + } } } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 68bc1418829f..beb1fcdb9672 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -4013,7 +4013,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { updateMaximumTimeToLockLocked(userHandle); updateLockTaskPackagesLocked(policy.mLockTaskPackages, userHandle); updateLockTaskFeaturesLocked(policy.mLockTaskFeatures, userHandle); - updateUserControlDisabledPackagesLocked(policy.mUserControlDisabledPackages); + if (userHandle == UserHandle.USER_SYSTEM) { + updateUserControlDisabledPackagesLocked(policy.mUserControlDisabledPackages); + } if (policy.mStatusBarDisabled) { setStatusBarDisabledInternal(policy.mStatusBarDisabled, userHandle); } @@ -4041,6 +4043,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { private void updateUserControlDisabledPackagesLocked(List<String> packages) { mInjector.getPackageManagerInternal().setDeviceOwnerProtectedPackages(packages); + mUsageStatsManagerInternal.setAdminProtectedPackages( + new ArraySet(packages), UserHandle.USER_ALL); } private void updateLockTaskFeaturesLocked(int flags, int userId) { diff --git a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java index ac305a93bfb8..f7b7a5801b42 100644 --- a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java @@ -18,6 +18,7 @@ package com.android.server.accounts; import static android.database.sqlite.SQLiteDatabase.deleteDatabase; +import static org.mockito.ArgumentMatchers.contains; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyBoolean; import static org.mockito.Matchers.anyInt; @@ -692,6 +693,41 @@ public class AccountManagerServiceTest extends AndroidTestCase { } @SmallTest + public void testStartAddAccountSessionWhereAuthenticatorReturnsIntentWithProhibitedFlags() + throws Exception { + unlockSystemUser(); + ResolveInfo resolveInfo = new ResolveInfo(); + resolveInfo.activityInfo = new ActivityInfo(); + resolveInfo.activityInfo.applicationInfo = new ApplicationInfo(); + when(mMockPackageManager.resolveActivityAsUser( + any(Intent.class), anyInt(), anyInt())).thenReturn(resolveInfo); + when(mMockPackageManager.checkSignatures( + anyInt(), anyInt())).thenReturn(PackageManager.SIGNATURE_MATCH); + + final CountDownLatch latch = new CountDownLatch(1); + Response response = new Response(latch, mMockAccountManagerResponse); + Bundle options = createOptionsWithAccountName( + AccountManagerServiceTestFixtures.ACCOUNT_NAME_INTERVENE); + int prohibitedFlags = Intent.FLAG_GRANT_READ_URI_PERMISSION + | Intent.FLAG_GRANT_WRITE_URI_PERMISSION + | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION + | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION; + options.putInt(AccountManagerServiceTestFixtures.KEY_INTENT_FLAGS, prohibitedFlags); + + mAms.startAddAccountSession( + response, // response + AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1, // accountType + "authTokenType", + null, // requiredFeatures + true, // expectActivityLaunch + options); // optionsIn + waitForLatch(latch); + + verify(mMockAccountManagerResponse).onError( + eq(AccountManager.ERROR_CODE_INVALID_RESPONSE), contains("invalid intent")); + } + + @SmallTest public void testStartAddAccountSessionError() throws Exception { unlockSystemUser(); Bundle options = createOptionsWithAccountName( diff --git a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTestFixtures.java b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTestFixtures.java index 73f30d9f9e79..b98a6a891d55 100644 --- a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTestFixtures.java +++ b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTestFixtures.java @@ -17,9 +17,6 @@ package com.android.server.accounts; import android.accounts.Account; -import java.util.ArrayList; -import java.util.List; - /** * Constants shared between test AccountAuthenticators and AccountManagerServiceTest. */ @@ -31,6 +28,8 @@ public final class AccountManagerServiceTestFixtures { "account_manager_service_test:account_status_token_key"; public static final String KEY_ACCOUNT_PASSWORD = "account_manager_service_test:account_password_key"; + public static final String KEY_INTENT_FLAGS = + "account_manager_service_test:intent_flags_key"; public static final String KEY_OPTIONS_BUNDLE = "account_manager_service_test:option_bundle_key"; public static final String ACCOUNT_NAME_SUCCESS = "success_on_return@fixture.com"; diff --git a/services/tests/servicestests/src/com/android/server/accounts/TestAccountType1Authenticator.java b/services/tests/servicestests/src/com/android/server/accounts/TestAccountType1Authenticator.java index 8106364477d9..924443e9d5cf 100644 --- a/services/tests/servicestests/src/com/android/server/accounts/TestAccountType1Authenticator.java +++ b/services/tests/servicestests/src/com/android/server/accounts/TestAccountType1Authenticator.java @@ -24,8 +24,6 @@ import android.content.Context; import android.content.Intent; import android.os.Bundle; -import com.android.frameworks.servicestests.R; - import java.util.concurrent.atomic.AtomicInteger; /** @@ -270,11 +268,13 @@ public class TestAccountType1Authenticator extends AbstractAccountAuthenticator String accountName = null; Bundle sessionBundle = null; String password = null; + int intentFlags = 0; if (options != null) { accountName = options.getString(AccountManagerServiceTestFixtures.KEY_ACCOUNT_NAME); sessionBundle = options.getBundle( AccountManagerServiceTestFixtures.KEY_ACCOUNT_SESSION_BUNDLE); password = options.getString(AccountManagerServiceTestFixtures.KEY_ACCOUNT_PASSWORD); + intentFlags = options.getInt(AccountManagerServiceTestFixtures.KEY_INTENT_FLAGS, 0); } Bundle result = new Bundle(); @@ -302,6 +302,7 @@ public class TestAccountType1Authenticator extends AbstractAccountAuthenticator intent.putExtra(AccountManagerServiceTestFixtures.KEY_RESULT, eventualActivityResultData); intent.putExtra(AccountManagerServiceTestFixtures.KEY_CALLBACK, response); + intent.setFlags(intentFlags); result.putParcelable(AccountManager.KEY_INTENT, intent); } else { diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java index 4dec7a1a0ab9..b6ce12ded4a0 100644 --- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java +++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java @@ -123,6 +123,9 @@ public class AppStandbyControllerTests { private static final String ADMIN_PKG2 = "com.android.admin2"; private static final String ADMIN_PKG3 = "com.android.admin3"; + private static final String ADMIN_PROTECTED_PKG = "com.android.admin.protected"; + private static final String ADMIN_PROTECTED_PKG2 = "com.android.admin.protected2"; + private static final long MINUTE_MS = 60 * 1000; private static final long HOUR_MS = 60 * MINUTE_MS; private static final long DAY_MS = 24 * HOUR_MS; @@ -1353,6 +1356,19 @@ public class AppStandbyControllerTests { } @Test + public void testSetAdminProtectedPackages() { + assertAdminProtectedPackagesForTest(USER_ID, (String[]) null); + assertAdminProtectedPackagesForTest(USER_ID2, (String[]) null); + + setAdminProtectedPackages(USER_ID, ADMIN_PROTECTED_PKG, ADMIN_PROTECTED_PKG2); + assertAdminProtectedPackagesForTest(USER_ID, ADMIN_PROTECTED_PKG, ADMIN_PROTECTED_PKG2); + assertAdminProtectedPackagesForTest(USER_ID2, (String[]) null); + + setAdminProtectedPackages(USER_ID, (String[]) null); + assertAdminProtectedPackagesForTest(USER_ID, (String[]) null); + } + + @Test public void testUserInteraction_CrossProfile() throws Exception { mInjector.mRunningUsers = new int[] {USER_ID, USER_ID2, USER_ID3}; mInjector.mCrossProfileTargets = Arrays.asList(USER_HANDLE_USER2); @@ -1557,6 +1573,28 @@ public class AppStandbyControllerTests { mController.setActiveAdminApps(new ArraySet<>(Arrays.asList(admins)), userId); } + private void setAdminProtectedPackages(int userId, String... packageNames) { + Set<String> adminProtectedPackages = packageNames != null ? new ArraySet<>( + Arrays.asList(packageNames)) : null; + mController.setAdminProtectedPackages(adminProtectedPackages, userId); + } + + private void assertAdminProtectedPackagesForTest(int userId, String... packageNames) { + final Set<String> actualAdminProtectedPackages = + mController.getAdminProtectedPackagesForTest(userId); + if (packageNames == null) { + if (actualAdminProtectedPackages != null && !actualAdminProtectedPackages.isEmpty()) { + fail("Admin protected packages should be null; " + getAdminAppsStr(userId, + actualAdminProtectedPackages)); + } + return; + } + assertEquals(packageNames.length, actualAdminProtectedPackages.size()); + for (String adminProtectedPackage : packageNames) { + assertTrue(actualAdminProtectedPackages.contains(adminProtectedPackage)); + } + } + private void setAndAssertBucket(String pkg, int user, int bucket, int reason) throws Exception { mStateChangedLatch = new CountDownLatch(1); mController.setAppStandbyBucket(pkg, user, bucket, reason); diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java index fd68ec334d8b..131ca52ee618 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UsageStatsService.java @@ -2288,6 +2288,11 @@ public class UsageStatsService extends SystemService implements } @Override + public void setAdminProtectedPackages(Set<String> packageNames, int userId) { + mAppStandby.setAdminProtectedPackages(packageNames, userId); + } + + @Override public void onAdminDataAvailable() { mAppStandby.onAdminDataAvailable(); } |