diff options
23 files changed, 237 insertions, 162 deletions
diff --git a/api/test-current.txt b/api/test-current.txt index 1142fb631891..795d873ec69c 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -3323,6 +3323,7 @@ package android.provider { field public static final String NFC_PAYMENT_DEFAULT_COMPONENT = "nfc_payment_default_component"; field public static final String NOTIFICATION_BADGING = "notification_badging"; field public static final String POWER_MENU_LOCKED_SHOW_CONTENT = "power_menu_locked_show_content"; + field public static final String SHOW_IME_WITH_HARD_KEYBOARD = "show_ime_with_hard_keyboard"; field @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public static final String SYNC_PARENT_SOUNDS = "sync_parent_sounds"; field public static final String USER_SETUP_COMPLETE = "user_setup_complete"; field public static final String VOICE_INTERACTION_SERVICE = "voice_interaction_service"; diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 1f90e401dee5..322cac81d58b 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -4247,6 +4247,12 @@ public class DevicePolicyManager { * device. After this method is called, the device must be unlocked using strong authentication * (PIN, pattern, or password). This API is intended for use only by device admins. * <p> + * From version {@link android.os.Build.VERSION_CODES#R} onwards, the caller must either have + * the LOCK_DEVICE permission or the device must have the device admin feature; if neither is + * true, then the method will return without completing any action. Before version + * {@link android.os.Build.VERSION_CODES#R}, the device needed the device admin feature, + * regardless of the caller's permissions. + * <p> * The calling device admin must have requested {@link DeviceAdminInfo#USES_POLICY_FORCE_LOCK} * to be able to call this method; if it has not, a security exception will be thrown. * <p> @@ -4274,6 +4280,12 @@ public class DevicePolicyManager { * device. After this method is called, the device must be unlocked using strong authentication * (PIN, pattern, or password). This API is intended for use only by device admins. * <p> + * From version {@link android.os.Build.VERSION_CODES#R} onwards, the caller must either have + * the LOCK_DEVICE permission or the device must have the device admin feature; if neither is + * true, then the method will return without completing any action. Before version + * {@link android.os.Build.VERSION_CODES#R}, the device needed the device admin feature, + * regardless of the caller's permissions. + * <p> * The calling device admin must have requested {@link DeviceAdminInfo#USES_POLICY_FORCE_LOCK} * to be able to call this method; if it has not, a security exception will be thrown. * <p> diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index e10fceaa5bc7..4f0a9728fcf8 100755 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -24,6 +24,7 @@ import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; +import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.annotation.TestApi; import android.annotation.UserIdInt; @@ -6238,6 +6239,8 @@ public final class Settings { * determines if the IME should be shown when a hard keyboard is attached. * @hide */ + @TestApi + @SuppressLint("NoSettingsProvider") public static final String SHOW_IME_WITH_HARD_KEYBOARD = "show_ime_with_hard_keyboard"; /** diff --git a/core/java/android/service/controls/templates/ControlTemplate.java b/core/java/android/service/controls/templates/ControlTemplate.java index 1e16273c455b..e592fad394b8 100644 --- a/core/java/android/service/controls/templates/ControlTemplate.java +++ b/core/java/android/service/controls/templates/ControlTemplate.java @@ -214,10 +214,13 @@ public abstract class ControlTemplate { } /** - * Get a singleton {@link ControlTemplate} that has no features. + * Get a singleton {@link ControlTemplate}, which supports no direct user input. * - * This template has no distinctive field, not even an identifier. Used for a {@link Control} - * that accepts no type of input, or when there is no known state. + * Used by {@link Control.StatelessBuilder} when there is no known state. Can also be used + * in {@link Control.StatefulBuilder} for conveying information to a user about the + * {@link Control} but direct user interaction is not desired. Since this template has no + * corresponding {@link ControlAction}, any user interaction will launch the + * {@link Control#getAppIntent()}. * * @return a singleton {@link ControlTemplate} to indicate no specific template is used by * this {@link Control} diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/BlurUtils.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/BlurUtils.java new file mode 100644 index 000000000000..9f26d851f775 --- /dev/null +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/BlurUtils.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.systemui.shared.system; + +import android.app.ActivityManager; +import android.os.SystemProperties; + +public abstract class BlurUtils { + + private static boolean mBlurSupportedSysProp = SystemProperties + .getBoolean("ro.surface_flinger.supports_background_blur", false); + private static boolean mBlurDisabledSysProp = SystemProperties + .getBoolean("persist.sys.sf.disable_blurs", false); + + /** + * If this device can render blurs. + * + * @return {@code true} when supported. + */ + public static boolean supportsBlursOnWindows() { + return mBlurSupportedSysProp && !mBlurDisabledSysProp && ActivityManager.isHighEndGfx(); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt index ebdcdccead90..40c8c6bfa9f7 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt @@ -200,9 +200,9 @@ class ControlsControllerImpl @Inject constructor ( GlobalActionsDialog.PREFS_CONTROLS_FILE, Context.MODE_PRIVATE) val completedSeedingPackageSet = prefs.getStringSet( GlobalActionsDialog.PREFS_CONTROLS_SEEDING_COMPLETED, mutableSetOf<String>()) - val favoritePackageSet = favoriteComponentSet.map { it.packageName } + val servicePackageSet = serviceInfoSet.map { it.packageName } prefs.edit().putStringSet(GlobalActionsDialog.PREFS_CONTROLS_SEEDING_COMPLETED, - completedSeedingPackageSet.intersect(favoritePackageSet)).apply() + completedSeedingPackageSet.intersect(servicePackageSet)).apply() var changed = false favoriteComponentSet.subtract(serviceInfoSet).forEach { diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt index 353367ead7e6..ee02b85e4a00 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt @@ -89,6 +89,7 @@ class ControlViewHolder( return when { status != Control.STATUS_OK -> StatusBehavior::class deviceType == DeviceTypes.TYPE_CAMERA -> TouchBehavior::class + template == ControlTemplate.NO_TEMPLATE -> TouchBehavior::class template is ToggleTemplate -> ToggleBehavior::class template is StatelessTemplate -> TouchBehavior::class template is ToggleRangeTemplate -> ToggleRangeBehavior::class @@ -235,7 +236,10 @@ class ControlViewHolder( controlsController.action(cws.componentName, cws.ci, action) } - fun usePanel(): Boolean = deviceType in ControlViewHolder.FORCE_PANEL_DEVICES + fun usePanel(): Boolean { + return deviceType in ControlViewHolder.FORCE_PANEL_DEVICES || + controlTemplate == ControlTemplate.NO_TEMPLATE + } fun bindBehavior( existingBehavior: Behavior?, diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java index bc95a2514c0b..915092134cc5 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java @@ -247,7 +247,6 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, private final Executor mBackgroundExecutor; private List<ControlsServiceInfo> mControlsServiceInfos = new ArrayList<>(); private Optional<ControlsController> mControlsControllerOptional; - private SharedPreferences mControlsPreferences; private final RingerModeTracker mRingerModeTracker; private int mDialogPressDelay = DIALOG_PRESS_DELAY; // ms private Handler mMainHandler; @@ -405,12 +404,6 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, }); } - // Need to be user-specific with the context to make sure we read the correct prefs - Context userContext = context.createContextAsUser( - new UserHandle(mUserManager.getUserHandle()), 0); - mControlsPreferences = userContext.getSharedPreferences(PREFS_CONTROLS_FILE, - Context.MODE_PRIVATE); - // Listen for changes to show controls on the power menu while locked onPowerMenuLockScreenSettingsChanged(); mContext.getContentResolver().registerContentObserver( @@ -444,19 +437,22 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, Collections.emptySet()); List<ComponentName> componentsToSeed = new ArrayList<>(); - for (ControlsServiceInfo info : mControlsServiceInfos) { - String pkg = info.componentName.getPackageName(); - if (seededPackages.contains(pkg) - || mControlsControllerOptional.get().countFavoritesForComponent( - info.componentName) > 0) { - continue; - } - - for (int i = 0; i < Math.min(SEEDING_MAX, preferredControlsPackages.length); i++) { - if (pkg.equals(preferredControlsPackages[i])) { - componentsToSeed.add(info.componentName); + for (int i = 0; i < Math.min(SEEDING_MAX, preferredControlsPackages.length); i++) { + String pkg = preferredControlsPackages[i]; + for (ControlsServiceInfo info : mControlsServiceInfos) { + if (!pkg.equals(info.componentName.getPackageName())) continue; + if (seededPackages.contains(pkg)) { + break; + } else if (mControlsControllerOptional.get() + .countFavoritesForComponent(info.componentName) > 0) { + // When there are existing controls but no saved preference, assume it + // is out of sync, perhaps through a device restore, and update the + // preference + addPackageToSeededSet(prefs, pkg); break; } + componentsToSeed.add(info.componentName); + break; } } @@ -466,16 +462,20 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, componentsToSeed, (response) -> { Log.d(TAG, "Controls seeded: " + response); - Set<String> completedPkgs = prefs.getStringSet(PREFS_CONTROLS_SEEDING_COMPLETED, - new HashSet<String>()); if (response.getAccepted()) { - completedPkgs.add(response.getPackageName()); - prefs.edit().putStringSet(PREFS_CONTROLS_SEEDING_COMPLETED, - completedPkgs).apply(); + addPackageToSeededSet(prefs, response.getPackageName()); } }); } + private void addPackageToSeededSet(SharedPreferences prefs, String pkg) { + Set<String> seededPackages = prefs.getStringSet(PREFS_CONTROLS_SEEDING_COMPLETED, + Collections.emptySet()); + Set<String> updatedPkgs = new HashSet<>(seededPackages); + updatedPkgs.add(pkg); + prefs.edit().putStringSet(PREFS_CONTROLS_SEEDING_COMPLETED, updatedPkgs).apply(); + } + /** * Show the global actions dialog (creating if necessary) * diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java index 72d9d0ee8f8f..4d09071c6b5d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java @@ -104,6 +104,9 @@ public class NotificationListener extends NotificationListenerWithPlugins { listener.onNotificationPosted(sbn, completeMap); } } + for (NotificationHandler listener : mNotificationHandlers) { + listener.onNotificationsInitialized(); + } }); onSilentStatusBarIconsVisibilityChanged( mNotificationManager.shouldHideSilentStatusBarIcons()); @@ -224,5 +227,10 @@ public class NotificationListener extends NotificationListenerWithPlugins { void onNotificationRemoved(StatusBarNotification sbn, RankingMap rankingMap); void onNotificationRemoved(StatusBarNotification sbn, RankingMap rankingMap, int reason); void onNotificationRankingUpdate(RankingMap rankingMap); + + /** + * Called after the listener has connected to NoMan and posted any current notifications. + */ + void onNotificationsInitialized(); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java index adb51a5d959a..9abc66056452 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java @@ -385,6 +385,10 @@ public class NotificationEntryManager implements public void onNotificationRankingUpdate(RankingMap rankingMap) { updateNotificationRanking(rankingMap); } + + @Override + public void onNotificationsInitialized() { + } }; /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java index 45987b6eb21b..c1acfbadef45 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java @@ -47,7 +47,6 @@ import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.Notification; import android.os.RemoteException; -import android.os.SystemClock; import android.os.UserHandle; import android.service.notification.NotificationListenerService; import android.service.notification.NotificationListenerService.Ranking; @@ -82,6 +81,7 @@ import com.android.systemui.statusbar.notification.collection.notifcollection.No import com.android.systemui.statusbar.notification.collection.notifcollection.RankingAppliedEvent; import com.android.systemui.statusbar.notification.collection.notifcollection.RankingUpdatedEvent; import com.android.systemui.util.Assert; +import com.android.systemui.util.time.SystemClock; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -95,6 +95,7 @@ import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Queue; +import java.util.concurrent.TimeUnit; import javax.inject.Inject; import javax.inject.Singleton; @@ -125,6 +126,7 @@ import javax.inject.Singleton; @Singleton public class NotifCollection implements Dumpable { private final IStatusBarService mStatusBarService; + private final SystemClock mClock; private final FeatureFlags mFeatureFlags; private final NotifCollectionLogger mLogger; private final LogBufferEulogizer mEulogizer; @@ -142,20 +144,24 @@ public class NotifCollection implements Dumpable { private boolean mAttached = false; private boolean mAmDispatchingToOtherCode; + private long mInitializedTimestamp = 0; @Inject public NotifCollection( IStatusBarService statusBarService, - DumpManager dumpManager, + SystemClock clock, FeatureFlags featureFlags, NotifCollectionLogger logger, - LogBufferEulogizer logBufferEulogizer) { + LogBufferEulogizer logBufferEulogizer, + DumpManager dumpManager) { Assert.isMainThread(); mStatusBarService = statusBarService; + mClock = clock; + mFeatureFlags = featureFlags; mLogger = logger; mEulogizer = logBufferEulogizer; + dumpManager.registerDumpable(TAG, this); - mFeatureFlags = featureFlags; } /** Initializes the NotifCollection and registers it to receive notification events. */ @@ -376,9 +382,10 @@ public class NotifCollection implements Dumpable { final NotificationEntry entry = mNotificationSet.get(sbn.getKey()); if (entry == null) { - throw mEulogizer.record( + crashIfNotInitializing( new IllegalStateException("No notification to remove with key " + sbn.getKey())); + return; } entry.mCancellationReason = reason; @@ -394,6 +401,10 @@ public class NotifCollection implements Dumpable { dispatchEventsAndRebuildList(); } + private void onNotificationsInitialized() { + mInitializedTimestamp = mClock.uptimeMillis(); + } + private void postNotification( StatusBarNotification sbn, Ranking ranking) { @@ -401,7 +412,7 @@ public class NotifCollection implements Dumpable { if (entry == null) { // A new notification! - entry = new NotificationEntry(sbn, ranking, SystemClock.uptimeMillis()); + entry = new NotificationEntry(sbn, ranking, mClock.uptimeMillis()); mEventQueue.add(new InitEntryEvent(entry)); mEventQueue.add(new BindEntryEvent(entry, sbn)); mNotificationSet.put(sbn.getKey(), entry); @@ -628,6 +639,23 @@ public class NotifCollection implements Dumpable { } } + // While the NotificationListener is connecting to NotificationManager, there is a short period + // during which it's possible for us to receive events about notifications we don't yet know + // about (or that otherwise don't make sense). Until that race condition is fixed, we create a + // "forgiveness window" of five seconds during which we won't crash if we receive nonsensical + // messages from system server. + private void crashIfNotInitializing(RuntimeException exception) { + final boolean isRecentlyInitialized = mInitializedTimestamp == 0 + || mClock.uptimeMillis() - mInitializedTimestamp + < INITIALIZATION_FORGIVENESS_WINDOW; + + if (isRecentlyInitialized) { + mLogger.logIgnoredError(exception.getMessage()); + } else { + throw mEulogizer.record(exception); + } + } + private static Ranking requireRanking(RankingMap rankingMap, String key) { // TODO: Modify RankingMap so that we don't have to make a copy here Ranking ranking = new Ranking(); @@ -742,6 +770,11 @@ public class NotifCollection implements Dumpable { public void onNotificationRankingUpdate(RankingMap rankingMap) { NotifCollection.this.onNotificationRankingUpdate(rankingMap); } + + @Override + public void onNotificationsInitialized() { + NotifCollection.this.onNotificationsInitialized(); + } }; private static final String TAG = "NotifCollection"; @@ -773,4 +806,6 @@ public class NotifCollection implements Dumpable { static final int REASON_NOT_CANCELED = -1; public static final int REASON_UNKNOWN = 0; + + private static final long INITIALIZATION_FORGIVENESS_WINDOW = TimeUnit.SECONDS.toMillis(5); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescer.java index 596235cfb4d0..1710daa16735 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescer.java @@ -153,6 +153,11 @@ public class GroupCoalescer implements Dumpable { applyRanking(rankingMap); mHandler.onNotificationRankingUpdate(rankingMap); } + + @Override + public void onNotificationsInitialized() { + mHandler.onNotificationsInitialized(); + } }; private void maybeEmitBatch(StatusBarNotification sbn) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt index 0eb2d64e8682..76751eaaecb1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt @@ -20,6 +20,7 @@ import android.os.RemoteException import android.service.notification.NotificationListenerService.RankingMap import com.android.systemui.log.LogBuffer import com.android.systemui.log.LogLevel.DEBUG +import com.android.systemui.log.LogLevel.ERROR import com.android.systemui.log.LogLevel.INFO import com.android.systemui.log.LogLevel.WARNING import com.android.systemui.log.LogLevel.WTF @@ -167,6 +168,14 @@ class NotifCollectionLogger @Inject constructor( "LIFETIME EXTENSION ENDED for $str1 by '$str2'; $int1 remaining extensions" }) } + + fun logIgnoredError(message: String?) { + buffer.log(TAG, ERROR, { + str1 = message + }, { + "ERROR suppressed due to initialization forgiveness: $str1" + }) + } } private const val TAG = "NotifCollection" diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java index ca9cc299b36d..363fe95aae18 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java @@ -80,6 +80,7 @@ import com.android.systemui.statusbar.notification.collection.notifcollection.No import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionLogger; import com.android.systemui.statusbar.notification.collection.notifcollection.NotifDismissInterceptor; import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender; +import com.android.systemui.util.time.FakeSystemClock; import org.junit.Before; import org.junit.Test; @@ -131,6 +132,7 @@ public class NotifCollectionTest extends SysuiTestCase { private InOrder mListenerInOrder; private NoManSimulator mNoMan; + private FakeSystemClock mClock = new FakeSystemClock(); @Before public void setUp() { @@ -146,10 +148,11 @@ public class NotifCollectionTest extends SysuiTestCase { mCollection = new NotifCollection( mStatusBarService, - mock(DumpManager.class), + mClock, mFeatureFlags, mLogger, - mEulogizer); + mEulogizer, + mock(DumpManager.class)); mCollection.attach(mGroupCoalescer); mCollection.addCollectionListener(mCollectionListener); mCollection.setBuildListener(mBuildListener); @@ -161,6 +164,8 @@ public class NotifCollectionTest extends SysuiTestCase { mNoMan = new NoManSimulator(); mNoMan.addListener(mNotifHandler); + + mNotifHandler.onNotificationsInitialized(); } @Test @@ -1268,6 +1273,42 @@ public class NotifCollectionTest extends SysuiTestCase { verify(mInterceptor3, never()).shouldInterceptDismissal(clearable); } + @Test(expected = IllegalStateException.class) + public void testClearNotificationThrowsIfMissing() { + // GIVEN that enough time has passed that we're beyond the forgiveness window + mClock.advanceTime(5001); + + // WHEN we get a remove event for a notification we don't know about + final NotificationEntry container = new NotificationEntryBuilder() + .setPkg(TEST_PACKAGE) + .setId(47) + .build(); + mNotifHandler.onNotificationRemoved( + container.getSbn(), + new RankingMap(new Ranking[]{ container.getRanking() })); + + // THEN an exception is thrown + } + + @Test + public void testClearNotificationDoesntThrowIfInForgivenessWindow() { + // GIVEN that some time has passed but we're still within the initialization forgiveness + // window + mClock.advanceTime(4999); + + // WHEN we get a remove event for a notification we don't know about + final NotificationEntry container = new NotificationEntryBuilder() + .setPkg(TEST_PACKAGE) + .setId(47) + .build(); + mNotifHandler.onNotificationRemoved( + container.getSbn(), + new RankingMap(new Ranking[]{ container.getRanking() })); + + // THEN no exception is thrown, but no event is fired + verify(mCollectionListener, never()).onEntryRemoved(any(NotificationEntry.class), anyInt()); + } + private static NotificationEntryBuilder buildNotif(String pkg, int id, String tag) { return new NotificationEntryBuilder() .setPkg(pkg) diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java b/packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java index f14def6a3a02..fd6f171487a9 100644 --- a/packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java +++ b/packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java @@ -37,8 +37,8 @@ public final class TetheringConstants { private TetheringConstants() { } /** - * Extra used for communicating with the TetherService and TetherProvisioningActivity. - * Includes the type of tethering to enable if any. + * Extra used for communicating with the TetherService. Includes the type of tethering to + * enable if any. */ public static final String EXTRA_ADD_TETHER_TYPE = "extraAddTetherType"; /** @@ -56,38 +56,8 @@ public final class TetheringConstants { */ public static final String EXTRA_RUN_PROVISION = "extraRunProvision"; /** - * Extra used for communicating with the TetherService and TetherProvisioningActivity. - * Contains the {@link ResultReceiver} which will receive provisioning results. - * Can not be empty. + * Extra used for communicating with the TetherService. Contains the {@link ResultReceiver} + * which will receive provisioning results. Can be left empty. */ public static final String EXTRA_PROVISION_CALLBACK = "extraProvisionCallback"; - - /** - * Extra used for communicating with the TetherService and TetherProvisioningActivity. - * Contains the subId of current active cellular upstream. - * @hide - */ - public static final String EXTRA_TETHER_SUBID = "android.net.extra.TETHER_SUBID"; - - /** - * Extra used for telling TetherProvisioningActivity the entitlement package name and class - * name to start UI entitlement check. - * @hide - */ - public static final String EXTRA_TETHER_UI_PROVISIONING_APP_NAME = - "android.net.extra.TETHER_UI_PROVISIONING_APP_NAME"; - - /** - * Extra used for telling TetherService the intent action to start silent entitlement check. - * @hide - */ - public static final String EXTRA_TETHER_SILENT_PROVISIONING_ACTION = - "android.net.extra.TETHER_SILENT_PROVISIONING_ACTION"; - - /** - * Extra used for TetherService to receive the response of provisioning check. - * @hide - */ - public static final String EXTRA_TETHER_PROVISIONING_RESPONSE = - "android.net.extra.TETHER_PROVISIONING_RESPONSE"; } diff --git a/packages/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java b/packages/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java index 9dace709d734..3c6e8d88ed13 100644 --- a/packages/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java +++ b/packages/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java @@ -19,10 +19,6 @@ package com.android.networkstack.tethering; import static android.net.TetheringConstants.EXTRA_ADD_TETHER_TYPE; import static android.net.TetheringConstants.EXTRA_PROVISION_CALLBACK; import static android.net.TetheringConstants.EXTRA_RUN_PROVISION; -import static android.net.TetheringConstants.EXTRA_TETHER_PROVISIONING_RESPONSE; -import static android.net.TetheringConstants.EXTRA_TETHER_SILENT_PROVISIONING_ACTION; -import static android.net.TetheringConstants.EXTRA_TETHER_SUBID; -import static android.net.TetheringConstants.EXTRA_TETHER_UI_PROVISIONING_APP_NAME; import static android.net.TetheringManager.TETHERING_BLUETOOTH; import static android.net.TetheringManager.TETHERING_ETHERNET; import static android.net.TetheringManager.TETHERING_INVALID; @@ -73,6 +69,7 @@ public class EntitlementManager { protected static final String DISABLE_PROVISIONING_SYSPROP_KEY = "net.tethering.noprovisioning"; private static final String ACTION_PROVISIONING_ALARM = "com.android.networkstack.tethering.PROVISIONING_RECHECK_ALARM"; + private static final String EXTRA_SUBID = "subId"; private final ComponentName mSilentProvisioningService; private static final int MS_PER_HOUR = 60 * 60 * 1000; @@ -200,9 +197,9 @@ public class EntitlementManager { // till upstream change to cellular. if (mUsingCellularAsUpstream) { if (showProvisioningUi) { - runUiTetherProvisioning(downstreamType, config); + runUiTetherProvisioning(downstreamType, config.activeDataSubId); } else { - runSilentTetherProvisioning(downstreamType, config); + runSilentTetherProvisioning(downstreamType, config.activeDataSubId); } mNeedReRunProvisioningUi = false; } else { @@ -265,9 +262,9 @@ public class EntitlementManager { if (mCurrentEntitlementResults.indexOfKey(downstream) < 0) { if (mNeedReRunProvisioningUi) { mNeedReRunProvisioningUi = false; - runUiTetherProvisioning(downstream, config); + runUiTetherProvisioning(downstream, config.activeDataSubId); } else { - runSilentTetherProvisioning(downstream, config); + runSilentTetherProvisioning(downstream, config.activeDataSubId); } } } @@ -364,7 +361,7 @@ public class EntitlementManager { * @param subId default data subscription ID. */ @VisibleForTesting - protected Intent runSilentTetherProvisioning(int type, final TetheringConfiguration config) { + protected void runSilentTetherProvisioning(int type, int subId) { if (DBG) mLog.i("runSilentTetherProvisioning: " + type); // For silent provisioning, settings would stop tethering when entitlement fail. ResultReceiver receiver = buildProxyReceiver(type, false/* notifyFail */, null); @@ -372,20 +369,17 @@ public class EntitlementManager { Intent intent = new Intent(); intent.putExtra(EXTRA_ADD_TETHER_TYPE, type); intent.putExtra(EXTRA_RUN_PROVISION, true); - intent.putExtra(EXTRA_TETHER_SILENT_PROVISIONING_ACTION, config.provisioningAppNoUi); - intent.putExtra(EXTRA_TETHER_PROVISIONING_RESPONSE, config.provisioningResponse); intent.putExtra(EXTRA_PROVISION_CALLBACK, receiver); - intent.putExtra(EXTRA_TETHER_SUBID, config.activeDataSubId); + intent.putExtra(EXTRA_SUBID, subId); intent.setComponent(mSilentProvisioningService); // Only admin user can change tethering and SilentTetherProvisioning don't need to // show UI, it is fine to always start setting's background service as system user. mContext.startService(intent); - return intent; } - private void runUiTetherProvisioning(int type, final TetheringConfiguration config) { + private void runUiTetherProvisioning(int type, int subId) { ResultReceiver receiver = buildProxyReceiver(type, true/* notifyFail */, null); - runUiTetherProvisioning(type, config, receiver); + runUiTetherProvisioning(type, subId, receiver); } /** @@ -395,20 +389,17 @@ public class EntitlementManager { * @param receiver to receive entitlement check result. */ @VisibleForTesting - protected Intent runUiTetherProvisioning(int type, final TetheringConfiguration config, - ResultReceiver receiver) { + protected void runUiTetherProvisioning(int type, int subId, ResultReceiver receiver) { if (DBG) mLog.i("runUiTetherProvisioning: " + type); Intent intent = new Intent(Settings.ACTION_TETHER_PROVISIONING_UI); intent.putExtra(EXTRA_ADD_TETHER_TYPE, type); - intent.putExtra(EXTRA_TETHER_UI_PROVISIONING_APP_NAME, config.provisioningApp); intent.putExtra(EXTRA_PROVISION_CALLBACK, receiver); - intent.putExtra(EXTRA_TETHER_SUBID, config.activeDataSubId); + intent.putExtra(EXTRA_SUBID, subId); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // Only launch entitlement UI for system user. Entitlement UI should not appear for other // user because only admin user is allowed to change tethering. mContext.startActivity(intent); - return intent; } // Not needed to check if this don't run on the handler thread because it's private. @@ -640,7 +631,7 @@ public class EntitlementManager { receiver.send(cacheValue, null); } else { ResultReceiver proxy = buildProxyReceiver(downstream, false/* notifyFail */, receiver); - runUiTetherProvisioning(downstream, config, proxy); + runUiTetherProvisioning(downstream, config.activeDataSubId, proxy); } } } diff --git a/packages/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java b/packages/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java index 1d45f129b51b..48a600dfe6e1 100644 --- a/packages/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java +++ b/packages/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java @@ -107,7 +107,6 @@ public class TetheringConfiguration { public final String[] provisioningApp; public final String provisioningAppNoUi; public final int provisioningCheckPeriod; - public final String provisioningResponse; public final int activeDataSubId; @@ -142,13 +141,10 @@ public class TetheringConfiguration { enableLegacyDhcpServer = getEnableLegacyDhcpServer(res); provisioningApp = getResourceStringArray(res, R.array.config_mobile_hotspot_provision_app); - provisioningAppNoUi = getResourceString(res, - R.string.config_mobile_hotspot_provision_app_no_ui); + provisioningAppNoUi = getProvisioningAppNoUi(res); provisioningCheckPeriod = getResourceInteger(res, R.integer.config_mobile_hotspot_provision_check_period, 0 /* No periodic re-check */); - provisioningResponse = getResourceString(res, - R.string.config_mobile_hotspot_provision_response); mOffloadPollInterval = getResourceInteger(res, R.integer.config_tether_offload_poll_interval, @@ -341,9 +337,9 @@ public class TetheringConfiguration { return copy(LEGACY_DHCP_DEFAULT_RANGE); } - private static String getResourceString(Resources res, final int resId) { + private static String getProvisioningAppNoUi(Resources res) { try { - return res.getString(resId); + return res.getString(R.string.config_mobile_hotspot_provision_app_no_ui); } catch (Resources.NotFoundException e) { return ""; } diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java index 354e75356e9f..72fa916b9e42 100644 --- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java +++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java @@ -16,16 +16,8 @@ package com.android.networkstack.tethering; -import static android.net.TetheringConstants.EXTRA_ADD_TETHER_TYPE; -import static android.net.TetheringConstants.EXTRA_PROVISION_CALLBACK; -import static android.net.TetheringConstants.EXTRA_RUN_PROVISION; -import static android.net.TetheringConstants.EXTRA_TETHER_PROVISIONING_RESPONSE; -import static android.net.TetheringConstants.EXTRA_TETHER_SILENT_PROVISIONING_ACTION; -import static android.net.TetheringConstants.EXTRA_TETHER_SUBID; -import static android.net.TetheringConstants.EXTRA_TETHER_UI_PROVISIONING_APP_NAME; import static android.net.TetheringManager.TETHERING_BLUETOOTH; import static android.net.TetheringManager.TETHERING_ETHERNET; -import static android.net.TetheringManager.TETHERING_INVALID; import static android.net.TetheringManager.TETHERING_USB; import static android.net.TetheringManager.TETHERING_WIFI; import static android.net.TetheringManager.TETHERING_WIFI_P2P; @@ -52,7 +44,6 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.Context; -import android.content.Intent; import android.content.res.Resources; import android.net.util.SharedLog; import android.os.Bundle; @@ -62,7 +53,6 @@ import android.os.ResultReceiver; import android.os.SystemProperties; import android.os.test.TestLooper; import android.provider.DeviceConfig; -import android.provider.Settings; import android.telephony.CarrierConfigManager; import androidx.test.filters.SmallTest; @@ -86,7 +76,6 @@ public final class EntitlementManagerTest { private static final String[] PROVISIONING_APP_NAME = {"some", "app"}; private static final String PROVISIONING_NO_UI_APP_NAME = "no_ui_app"; - private static final String PROVISIONING_APP_RESPONSE = "app_response"; @Mock private CarrierConfigManager mCarrierConfigManager; @Mock private Context mContext; @@ -133,51 +122,15 @@ public final class EntitlementManagerTest { } @Override - protected Intent runUiTetherProvisioning(int type, - final TetheringConfiguration config, final ResultReceiver receiver) { - Intent intent = super.runUiTetherProvisioning(type, config, receiver); - assertUiTetherProvisioningIntent(type, config, receiver, intent); + protected void runUiTetherProvisioning(int type, int subId, ResultReceiver receiver) { uiProvisionCount++; receiver.send(fakeEntitlementResult, null); - return intent; - } - - private void assertUiTetherProvisioningIntent(int type, final TetheringConfiguration config, - final ResultReceiver receiver, final Intent intent) { - assertEquals(Settings.ACTION_TETHER_PROVISIONING_UI, intent.getAction()); - assertEquals(type, intent.getIntExtra(EXTRA_ADD_TETHER_TYPE, TETHERING_INVALID)); - final String[] appName = intent.getStringArrayExtra( - EXTRA_TETHER_UI_PROVISIONING_APP_NAME); - assertEquals(PROVISIONING_APP_NAME.length, appName.length); - for (int i = 0; i < PROVISIONING_APP_NAME.length; i++) { - assertEquals(PROVISIONING_APP_NAME[i], appName[i]); - } - assertEquals(receiver, intent.getParcelableExtra(EXTRA_PROVISION_CALLBACK)); - assertEquals(config.activeDataSubId, - intent.getIntExtra(EXTRA_TETHER_SUBID, INVALID_SUBSCRIPTION_ID)); } @Override - protected Intent runSilentTetherProvisioning(int type, - final TetheringConfiguration config) { - Intent intent = super.runSilentTetherProvisioning(type, config); - assertSilentTetherProvisioning(type, config, intent); + protected void runSilentTetherProvisioning(int type, int subId) { silentProvisionCount++; addDownstreamMapping(type, fakeEntitlementResult); - return intent; - } - - private void assertSilentTetherProvisioning(int type, final TetheringConfiguration config, - final Intent intent) { - assertEquals(type, intent.getIntExtra(EXTRA_ADD_TETHER_TYPE, TETHERING_INVALID)); - assertEquals(true, intent.getBooleanExtra(EXTRA_RUN_PROVISION, false)); - assertEquals(PROVISIONING_NO_UI_APP_NAME, - intent.getStringExtra(EXTRA_TETHER_SILENT_PROVISIONING_ACTION)); - assertEquals(PROVISIONING_APP_RESPONSE, - intent.getStringExtra(EXTRA_TETHER_PROVISIONING_RESPONSE)); - assertTrue(intent.hasExtra(EXTRA_PROVISION_CALLBACK)); - assertEquals(config.activeDataSubId, - intent.getIntExtra(EXTRA_TETHER_SUBID, INVALID_SUBSCRIPTION_ID)); } } @@ -234,8 +187,6 @@ public final class EntitlementManagerTest { .thenReturn(PROVISIONING_APP_NAME); when(mResources.getString(R.string.config_mobile_hotspot_provision_app_no_ui)) .thenReturn(PROVISIONING_NO_UI_APP_NAME); - when(mResources.getString(R.string.config_mobile_hotspot_provision_response)).thenReturn( - PROVISIONING_APP_RESPONSE); // Act like the CarrierConfigManager is present and ready unless told otherwise. when(mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE)) .thenReturn(mCarrierConfigManager); diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java index 312186391d5f..1999ad786ed4 100644 --- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java +++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java @@ -61,8 +61,6 @@ public class TetheringConfigurationTest { private final SharedLog mLog = new SharedLog("TetheringConfigurationTest"); private static final String[] PROVISIONING_APP_NAME = {"some", "app"}; - private static final String PROVISIONING_NO_UI_APP_NAME = "no_ui_app"; - private static final String PROVISIONING_APP_RESPONSE = "app_response"; @Mock private Context mContext; @Mock private TelephonyManager mTelephonyManager; @Mock private Resources mResources; @@ -390,8 +388,6 @@ public class TetheringConfigurationTest { new MockTetheringConfiguration(mMockContext, mLog, anyValidSubId); assertEquals(mockCfg.provisioningApp[0], PROVISIONING_APP_NAME[0]); assertEquals(mockCfg.provisioningApp[1], PROVISIONING_APP_NAME[1]); - assertEquals(mockCfg.provisioningAppNoUi, PROVISIONING_NO_UI_APP_NAME); - assertEquals(mockCfg.provisioningResponse, PROVISIONING_APP_RESPONSE); } private void setUpResourceForSubId() { @@ -407,10 +403,6 @@ public class TetheringConfigurationTest { new int[0]); when(mResourcesForSubId.getStringArray( R.array.config_mobile_hotspot_provision_app)).thenReturn(PROVISIONING_APP_NAME); - when(mResourcesForSubId.getString(R.string.config_mobile_hotspot_provision_app_no_ui)) - .thenReturn(PROVISIONING_NO_UI_APP_NAME); - when(mResourcesForSubId.getString( - R.string.config_mobile_hotspot_provision_response)).thenReturn( - PROVISIONING_APP_RESPONSE); } + } diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 6d45abaf0234..36272278e0e4 100755 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -5322,6 +5322,15 @@ public class AudioService extends IAudioService.Stub } private void setVolumeIndexInt(int index, int device, int flags) { + // Reflect mute state of corresponding stream by forcing index to 0 if muted + // Only set audio policy BT SCO stream volume to 0 when the stream is actually muted. + // This allows RX path muting by the audio HAL only when explicitly muted but not when + // index is just set to 0 to repect BT requirements + if (mStreamStates[mPublicStreamType].isFullyMuted()) { + index = 0; + } else if (mPublicStreamType == AudioSystem.STREAM_BLUETOOTH_SCO && index == 0) { + index = 1; + } // Set the volume index AudioSystem.setVolumeIndexForAttributes(mAudioAttributes, index, device); } diff --git a/services/core/java/com/android/server/location/SettingsHelper.java b/services/core/java/com/android/server/location/SettingsHelper.java index cbb06b86a291..8a35302d6fd5 100644 --- a/services/core/java/com/android/server/location/SettingsHelper.java +++ b/services/core/java/com/android/server/location/SettingsHelper.java @@ -168,7 +168,7 @@ public class SettingsHelper { * Remove a listener for changes to the location enabled setting. */ public void removeOnLocationEnabledChangedListener(UserSettingChangedListener listener) { - mLocationMode.addListener(listener); + mLocationMode.removeListener(listener); } /** diff --git a/services/core/jni/com_android_server_tv_TvInputHal.cpp b/services/core/jni/com_android_server_tv_TvInputHal.cpp index 098b2ef6439d..4e1a23416330 100644 --- a/services/core/jni/com_android_server_tv_TvInputHal.cpp +++ b/services/core/jni/com_android_server_tv_TvInputHal.cpp @@ -301,6 +301,7 @@ private: JTvInputHal(JNIEnv* env, jobject thiz, sp<ITvInput> tvInput, const sp<Looper>& looper); Mutex mLock; + Mutex mStreamLock; jweak mThiz; sp<Looper> mLooper; @@ -338,6 +339,7 @@ JTvInputHal* JTvInputHal::createInstance(JNIEnv* env, jobject thiz, const sp<Loo } int JTvInputHal::addOrUpdateStream(int deviceId, int streamId, const sp<Surface>& surface) { + Mutex::Autolock autoLock(&mStreamLock); KeyedVector<int, Connection>& connections = mConnections.editValueFor(deviceId); if (connections.indexOfKey(streamId) < 0) { connections.add(streamId, Connection()); @@ -412,6 +414,7 @@ int JTvInputHal::addOrUpdateStream(int deviceId, int streamId, const sp<Surface> } int JTvInputHal::removeStream(int deviceId, int streamId) { + Mutex::Autolock autoLock(&mStreamLock); KeyedVector<int, Connection>& connections = mConnections.editValueFor(deviceId); if (connections.indexOfKey(streamId) < 0) { return BAD_VALUE; diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 10ad07cff847..7b624cae8141 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -631,7 +631,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { /** * Whether or not device admin feature is supported. If it isn't return defaults for all - * public methods. + * public methods, unless the caller has the appropriate permission for a particular method. */ final boolean mHasFeature; @@ -6032,7 +6032,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public void lockNow(int flags, boolean parent) { - if (!mHasFeature) { + if (!mHasFeature && mContext.checkCallingPermission(android.Manifest.permission.LOCK_DEVICE) + != PackageManager.PERMISSION_GRANTED) { return; } |