summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/api/current.txt4
-rw-r--r--core/api/module-lib-current.txt36
-rw-r--r--core/api/test-current.txt21
-rw-r--r--core/java/android/app/ActivityOptions.java14
-rw-r--r--core/java/android/app/INotificationManager.aidl2
-rw-r--r--core/java/android/app/PropertyInvalidatedCache.java71
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java23
-rw-r--r--core/java/android/app/servertransaction/LaunchActivityItem.java7
-rw-r--r--core/java/android/app/servertransaction/StartActivityItem.java4
-rw-r--r--core/java/android/content/pm/ApplicationInfo.java2
-rw-r--r--core/java/android/inputmethodservice/IInputMethodWrapper.java36
-rw-r--r--core/java/android/inputmethodservice/InputMethodService.java17
-rw-r--r--core/java/android/inputmethodservice/NavigationBarController.java99
-rw-r--r--core/java/android/os/IpcDataCache.java364
-rw-r--r--core/java/android/service/dreams/OWNERS5
-rw-r--r--core/java/android/view/MotionEvent.java2
-rw-r--r--core/java/android/view/inputmethod/InputMethod.java19
-rw-r--r--core/java/com/android/internal/inputmethod/InputMethodNavButtonFlags.java49
-rw-r--r--core/java/com/android/internal/statusbar/IStatusBarService.aidl2
-rw-r--r--core/java/com/android/internal/view/IInputMethod.aidl8
-rw-r--r--core/jni/com_android_internal_os_ZygoteCommandBuffer.cpp4
-rw-r--r--core/res/AndroidManifest.xml23
-rw-r--r--core/res/res/values/strings.xml18
-rw-r--r--core/tests/coretests/src/android/os/IpcDataCacheTest.java312
-rw-r--r--core/tests/coretests/src/android/view/MotionEventTest.java24
-rw-r--r--graphics/java/android/graphics/RuntimeShader.java220
-rw-r--r--keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java6
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/common/CommonFoldingFeature.java (renamed from libs/WindowManager/Jetpack/src/androidx/window/common/CommonDisplayFeature.java)121
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java (renamed from libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerPostureProducer.java)30
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/common/DisplayFeature.java60
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/common/EmptyLifecycleCallbacksAdapter.java55
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/common/ResourceConfigDisplayFeatureProducer.java74
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/common/SettingsDevicePostureProducer.java96
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/common/SettingsDisplayFeatureProducer.java36
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java33
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java150
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java98
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/sidecar/StubSidecar.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BadgedImageView.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUI.java11
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java16
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduDialogLayout.java7
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduWindowManager.java7
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java40
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java8
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java30
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduDialogLayoutTest.java2
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduWindowManagerTest.java8
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java6
-rw-r--r--packages/CompanionDeviceManager/res/drawable/ic_apps.xml34
-rw-r--r--packages/CompanionDeviceManager/res/drawable/ic_notifications.xml25
-rw-r--r--packages/CompanionDeviceManager/res/drawable/ic_storage.xml25
-rw-r--r--packages/CompanionDeviceManager/res/layout/activity_confirmation.xml5
-rw-r--r--packages/CompanionDeviceManager/res/layout/list_item_permission.xml55
-rw-r--r--packages/CompanionDeviceManager/res/values/strings.xml18
-rw-r--r--packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java64
-rw-r--r--packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceListAdapter.java19
-rw-r--r--packages/CompanionDeviceManager/src/com/android/companiondevicemanager/PermissionListAdapter.java128
-rw-r--r--packages/CompanionDeviceManager/src/com/android/companiondevicemanager/Utils.java7
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt50
-rw-r--r--packages/SystemUI/res/values-sw720dp-land/dimens.xml2
-rw-r--r--packages/SystemUI/src/com/android/keyguard/EmergencyButton.java6
-rw-r--r--packages/SystemUI/src/com/android/keyguard/EmergencyButtonController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt17
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt163
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java25
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java75
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/Utils.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java14
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java22
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommonTest.kt12
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt13
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java11
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java10
-rw-r--r--services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java9
-rw-r--r--services/backup/backuplib/java/com/android/server/backup/TransportManager.java54
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java9
-rw-r--r--services/core/java/com/android/server/am/CachedAppOptimizer.java44
-rw-r--r--services/core/java/com/android/server/am/PendingIntentRecord.java3
-rw-r--r--services/core/java/com/android/server/clipboard/EmulatorClipboardMonitor.java37
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java17
-rw-r--r--services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java14
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodManagerService.java68
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodMenuController.java4
-rw-r--r--services/core/java/com/android/server/inputmethod/OverlayableSystemBooleanResourceWrapper.java159
-rwxr-xr-xservices/core/java/com/android/server/notification/NotificationManagerService.java25
-rw-r--r--services/core/java/com/android/server/notification/PermissionHelper.java29
-rw-r--r--services/core/java/com/android/server/notification/PreferencesHelper.java6
-rw-r--r--services/core/java/com/android/server/notification/ZenModeFiltering.java69
-rw-r--r--services/core/java/com/android/server/pm/IntentResolverInterceptor.java126
-rw-r--r--services/core/java/com/android/server/pm/LauncherAppsService.java12
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java10
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java7
-rw-r--r--services/core/java/com/android/server/policy/PermissionPolicyInternal.java17
-rw-r--r--services/core/java/com/android/server/policy/PermissionPolicyService.java72
-rw-r--r--services/core/java/com/android/server/vibrator/VibrationSettings.java12
-rw-r--r--services/core/java/com/android/server/wm/ActivityInterceptorCallback.java6
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java11
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java8
-rw-r--r--services/core/java/com/android/server/wm/AsyncRotationController.java4
-rw-r--r--services/core/java/com/android/server/wm/RootWindowContainer.java31
-rw-r--r--services/core/java/com/android/server/wm/Task.java24
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java13
-rw-r--r--services/core/java/com/android/server/wm/WindowStateAnimator.java46
-rw-r--r--services/robotests/backup/src/com/android/server/backup/TransportManagerTest.java84
-rw-r--r--services/robotests/src/com/android/server/testing/shadows/ShadowApplicationPackageManager.java20
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java22
-rw-r--r--services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java14
-rwxr-xr-xservices/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java14
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java20
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java34
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/ZenModeFilteringTest.java42
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java5
-rw-r--r--services/usb/java/com/android/server/usb/UsbDeviceManager.java2
-rw-r--r--services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java4
-rw-r--r--telephony/java/android/telephony/SubscriptionManager.java5
-rw-r--r--telephony/java/android/telephony/data/DataServiceCallback.java12
130 files changed, 3260 insertions, 1136 deletions
diff --git a/core/api/current.txt b/core/api/current.txt
index 51247d18045f..542221c3fcc9 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -8,6 +8,9 @@ package android {
public static final class Manifest.permission {
ctor public Manifest.permission();
field public static final String ACCEPT_HANDOVER = "android.permission.ACCEPT_HANDOVER";
+ field public static final String ACCESS_ADSERVICES_ATTRIBUTION = "android.permission.ACCESS_ADSERVICES_ATTRIBUTION";
+ field public static final String ACCESS_ADSERVICES_CUSTOM_AUDIENCES = "android.permission.ACCESS_ADSERVICES_CUSTOM_AUDIENCES";
+ field public static final String ACCESS_ADSERVICES_TOPICS = "android.permission.ACCESS_ADSERVICES_TOPICS";
field public static final String ACCESS_BACKGROUND_LOCATION = "android.permission.ACCESS_BACKGROUND_LOCATION";
field public static final String ACCESS_BLOBS_ACROSS_USERS = "android.permission.ACCESS_BLOBS_ACROSS_USERS";
field public static final String ACCESS_CHECKIN_PROPERTIES = "android.permission.ACCESS_CHECKIN_PROPERTIES";
@@ -17,7 +20,6 @@ package android {
field public static final String ACCESS_MEDIA_LOCATION = "android.permission.ACCESS_MEDIA_LOCATION";
field public static final String ACCESS_NETWORK_STATE = "android.permission.ACCESS_NETWORK_STATE";
field public static final String ACCESS_NOTIFICATION_POLICY = "android.permission.ACCESS_NOTIFICATION_POLICY";
- field public static final String ACCESS_SUPPLEMENTAL_APIS = "android.permission.ACCESS_SUPPLEMENTAL_APIS";
field public static final String ACCESS_WIFI_STATE = "android.permission.ACCESS_WIFI_STATE";
field public static final String ACCOUNT_MANAGER = "android.permission.ACCOUNT_MANAGER";
field public static final String ACTIVITY_RECOGNITION = "android.permission.ACTIVITY_RECOGNITION";
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 5bc5bbc08074..27ca6a2794b9 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -20,10 +20,6 @@ package android.app {
method @RequiresPermission(android.Manifest.permission.CHANGE_CONFIGURATION) public boolean updateMccMncConfiguration(@NonNull String, @NonNull String);
}
- public class ActivityOptions {
- method @NonNull public static android.app.ActivityOptions fromBundle(@NonNull android.os.Bundle);
- }
-
public class AppOpsManager {
field public static final String OPSTR_NO_ISOLATED_STORAGE = "android:no_isolated_storage";
}
@@ -54,22 +50,6 @@ package android.app {
method public void onCanceled(@NonNull android.app.PendingIntent);
}
- public class PropertyInvalidatedCache<Query, Result> {
- ctor public PropertyInvalidatedCache(int, @NonNull String, @NonNull String, @NonNull String, @NonNull android.app.PropertyInvalidatedCache.QueryHandler<Query,Result>);
- method public final void disableForCurrentProcess();
- method public final void invalidateCache();
- method public static void invalidateCache(@NonNull String, @NonNull String);
- method @Nullable public final Result query(@NonNull Query);
- field public static final String MODULE_BLUETOOTH = "bluetooth";
- field public static final String MODULE_TELEPHONY = "telephony";
- }
-
- public abstract static class PropertyInvalidatedCache.QueryHandler<Q, R> {
- ctor public PropertyInvalidatedCache.QueryHandler();
- method @Nullable public abstract R apply(@NonNull Q);
- method public boolean shouldBypassCache(@NonNull Q);
- }
-
public class StatusBarManager {
method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void setExpansionDisabledForSimNetworkLock(boolean);
}
@@ -358,6 +338,22 @@ package android.os {
field public static final int DEVICE_INITIAL_SDK_INT;
}
+ public class IpcDataCache<Query, Result> {
+ ctor public IpcDataCache(int, @NonNull String, @NonNull String, @NonNull String, @NonNull android.os.IpcDataCache.QueryHandler<Query,Result>);
+ method public void disableForCurrentProcess();
+ method public static void disableForCurrentProcess(@NonNull String);
+ method public void invalidateCache();
+ method public static void invalidateCache(@NonNull String, @NonNull String);
+ method @Nullable public Result query(@NonNull Query);
+ field public static final String MODULE_BLUETOOTH = "bluetooth";
+ }
+
+ public abstract static class IpcDataCache.QueryHandler<Q, R> {
+ ctor public IpcDataCache.QueryHandler();
+ method @Nullable public abstract R apply(@NonNull Q);
+ method public boolean shouldBypassCache(@NonNull Q);
+ }
+
public interface Parcelable {
method public default int getStability();
}
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 5c6f5f6bafb6..c5cce35d988e 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -154,7 +154,6 @@ package android.app {
}
public class ActivityOptions {
- method @NonNull public static android.app.ActivityOptions fromBundle(@NonNull android.os.Bundle);
method public boolean isEligibleForLegacyPermissionPrompt();
method @NonNull public static android.app.ActivityOptions makeCustomAnimation(@NonNull android.content.Context, int, int, int, @Nullable android.os.Handler, @Nullable android.app.ActivityOptions.OnAnimationStartedListener, @Nullable android.app.ActivityOptions.OnAnimationFinishedListener);
method @NonNull @RequiresPermission(android.Manifest.permission.START_TASKS_FROM_RECENTS) public static android.app.ActivityOptions makeCustomTaskAnimation(@NonNull android.content.Context, int, int, @Nullable android.os.Handler, @Nullable android.app.ActivityOptions.OnAnimationStartedListener, @Nullable android.app.ActivityOptions.OnAnimationFinishedListener);
@@ -374,16 +373,17 @@ package android.app {
public class PropertyInvalidatedCache<Query, Result> {
ctor public PropertyInvalidatedCache(int, @NonNull String, @NonNull String, @NonNull String, @NonNull android.app.PropertyInvalidatedCache.QueryHandler<Query,Result>);
method @NonNull public static String createPropertyName(@NonNull String, @NonNull String);
- method public final void disableForCurrentProcess();
+ method public void disableForCurrentProcess();
+ method public static void disableForCurrentProcess(@NonNull String);
method public static void disableForTestMode();
method public final void disableInstance();
method public final void disableSystemWide();
method public final void forgetDisableLocal();
method public boolean getDisabledState();
- method public final void invalidateCache();
+ method public void invalidateCache();
method public static void invalidateCache(@NonNull String, @NonNull String);
method public final boolean isDisabled();
- method @Nullable public final Result query(@NonNull Query);
+ method @Nullable public Result query(@NonNull Query);
method public static void setTestMode(boolean);
method public void testPropertyName();
field public static final String MODULE_BLUETOOTH = "bluetooth";
@@ -1727,6 +1727,19 @@ package android.os {
method @NonNull public static byte[] digest(@NonNull java.io.InputStream, @NonNull String) throws java.io.IOException, java.security.NoSuchAlgorithmException;
}
+ public class IpcDataCache<Query, Result> extends android.app.PropertyInvalidatedCache<Query,Result> {
+ ctor public IpcDataCache(int, @NonNull String, @NonNull String, @NonNull String, @NonNull android.os.IpcDataCache.QueryHandler<Query,Result>);
+ method public static void disableForCurrentProcess(@NonNull String);
+ method public static void invalidateCache(@NonNull String, @NonNull String);
+ field public static final String MODULE_BLUETOOTH = "bluetooth";
+ field public static final String MODULE_SYSTEM = "system_server";
+ field public static final String MODULE_TEST = "test";
+ }
+
+ public abstract static class IpcDataCache.QueryHandler<Q, R> extends android.app.PropertyInvalidatedCache.QueryHandler<Q,R> {
+ ctor public IpcDataCache.QueryHandler();
+ }
+
public final class MessageQueue {
method public int postSyncBarrier();
method public void removeSyncBarrier(int);
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index 87ac6cb1fe4c..0178fa143445 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -1421,17 +1421,9 @@ public class ActivityOptions extends ComponentOptions {
return mRemoteTransition;
}
- /**
- * Creates an ActivityOptions from the Bundle generated from {@link ActivityOptions#toBundle()}.
- * Returns an instance of ActivityOptions populated with options with known keys from the
- * provided Bundle, stripping out unknown entries.
- * @hide
- */
- @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
- @TestApi
- @NonNull
- public static ActivityOptions fromBundle(@NonNull Bundle bOptions) {
- return new ActivityOptions(bOptions);
+ /** @hide */
+ public static ActivityOptions fromBundle(Bundle bOptions) {
+ return bOptions != null ? new ActivityOptions(bOptions) : null;
}
/** @hide */
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index a82ecce2dc04..4fbe232556ed 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -116,7 +116,7 @@ interface INotificationManager
ParceledListSlice getNotificationChannelGroups(String pkg);
boolean onlyHasDefaultChannel(String pkg, int uid);
boolean areChannelsBypassingDnd();
- ParceledListSlice getNotificationChannelsBypassingDnd(String pkg, int userId);
+ ParceledListSlice getNotificationChannelsBypassingDnd(String pkg, int uid);
boolean isPackagePaused(String pkg);
void deleteNotificationHistoryItem(String pkg, int uid, long postedTime);
boolean isPermissionFixed(String pkg, int userId);
diff --git a/core/java/android/app/PropertyInvalidatedCache.java b/core/java/android/app/PropertyInvalidatedCache.java
index 2f202d95e0e3..df7bf7b94700 100644
--- a/core/java/android/app/PropertyInvalidatedCache.java
+++ b/core/java/android/app/PropertyInvalidatedCache.java
@@ -18,7 +18,6 @@ package android.app;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.os.Handler;
import android.os.Looper;
@@ -137,6 +136,26 @@ import java.util.concurrent.atomic.AtomicLong;
* With this cache, clients perform a binder call to birthdayd if asking for a user's birthday
* for the first time; on subsequent queries, we return the already-known Birthday object.
*
+ * The second parameter to the IpcDataCache constructor is a string that identifies the "module"
+ * that owns the cache. There are some well-known modules (such as {@code MODULE_SYSTEM} but any
+ * string is permitted. The third parameters is the name of the API being cached; this, too, can
+ * any value. The fourth is the name of the cache. The cache is usually named after th API.
+ * Some things you must know about the three strings:
+ * <list>
+ * <ul> The system property that controls the cache is named {@code cache_key.<module>.<api>}.
+ * Usually, the SELinux rules permit a process to write a system property (and therefore
+ * invalidate a cache) based on the wildcard {@code cache_key.<module>.*}. This means that
+ * although the cache can be constructed with any module string, whatever string is chosen must be
+ * consistent with the SELinux configuration.
+ * <ul> The API name can be any string of alphanumeric characters. All caches with the same API
+ * are invalidated at the same time. If a server supports several caches and all are invalidated
+ * in common, then it is most efficient to assign the same API string to every cache.
+ * <ul> The cache name can be any string. In debug output, the name is used to distiguish between
+ * caches with the same API name. The cache name is also used when disabling caches in the
+ * current process. So, invalidation is based on the module+api but disabling (which is generally
+ * a once-per-process operation) is based on the cache name.
+ * </list>
+ *
* User birthdays do occasionally change, so we have to modify the server to invalidate this
* cache when necessary. That invalidation code looks like this:
*
@@ -192,25 +211,23 @@ import java.util.concurrent.atomic.AtomicLong;
* <pre>
* public class ActivityThread {
* ...
- * private static final int BDAY_CACHE_MAX = 8; // Maximum birthdays to cache
- * private static final String BDAY_CACHE_KEY = "cache_key.birthdayd";
- * private final PropertyInvalidatedCache&lt;Integer, Birthday%&gt; mBirthdayCache = new
- * PropertyInvalidatedCache&lt;Integer, Birthday%&gt;(BDAY_CACHE_MAX, BDAY_CACHE_KEY) {
- * {@literal @}Override
- * protected Birthday recompute(Integer userId) {
- * return GetService("birthdayd").getUserBirthday(userId);
- * }
- * {@literal @}Override
- * protected boolean bypass(Integer userId) {
- * return userId == NEXT_BIRTHDAY;
- * }
- * };
+ * private final IpcDataCache.QueryHandler&lt;Integer, Birthday&gt; mBirthdayQuery =
+ * new IpcDataCache.QueryHandler&lt;Integer, Birthday&gt;() {
+ * {@literal @}Override
+ * public Birthday apply(Integer) {
+ * return GetService("birthdayd").getUserBirthday(userId);
+ * }
+ * {@literal @}Override
+ * public boolean shouldBypassQuery(Integer userId) {
+ * return userId == NEXT_BIRTHDAY;
+ * }
+ * };
* ...
* }
* </pre>
*
- * If the {@code bypass()} method returns true then the cache is not used for that
- * particular query. The {@code bypass()} method is not abstract and the default
+ * If the {@code shouldBypassQuery()} method returns true then the cache is not used for that
+ * particular query. The {@code shouldBypassQuery()} method is not abstract and the default
* implementation returns false.
*
* For security, there is a allowlist of processes that are allowed to invalidate a cache.
@@ -231,14 +248,12 @@ import java.util.concurrent.atomic.AtomicLong;
* @param <Result> The class holding cache entries; use a boxed primitive if possible
* @hide
*/
-@SystemApi(client=SystemApi.Client.MODULE_LIBRARIES)
@TestApi
public class PropertyInvalidatedCache<Query, Result> {
/**
* This is a configuration class that customizes a cache instance.
* @hide
*/
- @SystemApi(client=SystemApi.Client.MODULE_LIBRARIES)
@TestApi
public static abstract class QueryHandler<Q,R> {
/**
@@ -285,7 +300,6 @@ public class PropertyInvalidatedCache<Query, Result> {
* The module used for bluetooth caches.
* @hide
*/
- @SystemApi(client=SystemApi.Client.MODULE_LIBRARIES)
@TestApi
public static final String MODULE_BLUETOOTH = "bluetooth";
@@ -533,7 +547,6 @@ public class PropertyInvalidatedCache<Query, Result> {
* @param computer The code to compute values that are not in the cache.
* @hide
*/
- @SystemApi(client=SystemApi.Client.MODULE_LIBRARIES)
@TestApi
public PropertyInvalidatedCache(int maxEntries, @NonNull String module, @NonNull String api,
@NonNull String cacheName, @NonNull QueryHandler<Query, Result> computer) {
@@ -792,7 +805,7 @@ public class PropertyInvalidatedCache<Query, Result> {
* TODO(216112648) Remove this in favor of disableForCurrentProcess().
* @hide
*/
- public final void disableLocal() {
+ public void disableLocal() {
disableForCurrentProcess();
}
@@ -802,12 +815,17 @@ public class PropertyInvalidatedCache<Query, Result> {
* property.
* @hide
*/
- @SystemApi(client=SystemApi.Client.MODULE_LIBRARIES)
@TestApi
- public final void disableForCurrentProcess() {
+ public void disableForCurrentProcess() {
disableLocal(mCacheName);
}
+ /** @hide */
+ @TestApi
+ public static void disableForCurrentProcess(@NonNull String cacheName) {
+ disableLocal(cacheName);
+ }
+
/**
* Return whether a cache instance is disabled.
* @hide
@@ -821,9 +839,8 @@ public class PropertyInvalidatedCache<Query, Result> {
* Get a value from the cache or recompute it.
* @hide
*/
- @SystemApi(client=SystemApi.Client.MODULE_LIBRARIES)
@TestApi
- public final @Nullable Result query(@NonNull Query query) {
+ public @Nullable Result query(@NonNull Query query) {
// Let access to mDisabled race: it's atomic anyway.
long currentNonce = (!isDisabled()) ? getCurrentNonce() : NONCE_DISABLED;
if (bypass(query)) {
@@ -964,9 +981,8 @@ public class PropertyInvalidatedCache<Query, Result> {
* PropertyInvalidatedCache is keyed on a particular property value.
* @hide
*/
- @SystemApi(client=SystemApi.Client.MODULE_LIBRARIES)
@TestApi
- public final void invalidateCache() {
+ public void invalidateCache() {
invalidateCache(mPropertyName);
}
@@ -974,7 +990,6 @@ public class PropertyInvalidatedCache<Query, Result> {
* Invalidate caches in all processes that are keyed for the module and api.
* @hide
*/
- @SystemApi(client=SystemApi.Client.MODULE_LIBRARIES)
@TestApi
public static void invalidateCache(@NonNull String module, @NonNull String api) {
invalidateCache(createPropertyName(module, api));
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 9bfdbd459574..8d4e0d6c1f42 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -1413,6 +1413,9 @@ public class DevicePolicyManager {
* admin app when performing the admin-integrated provisioning flow as a result of the
* {@link #ACTION_GET_PROVISIONING_MODE} activity.
*
+ * <p>This extra may also be provided to the admin app via an intent extra for {@link
+ * #ACTION_GET_PROVISIONING_MODE}.
+ *
* @see #ACTION_GET_PROVISIONING_MODE
*/
public static final String EXTRA_PROVISIONING_SENSORS_PERMISSION_GRANT_OPT_OUT =
@@ -3062,6 +3065,8 @@ public class DevicePolicyManager {
* <li>{@link #EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE}</li>
* <li>{@link #EXTRA_PROVISIONING_IMEI}</li>
* <li>{@link #EXTRA_PROVISIONING_SERIAL_NUMBER}</li>
+ * <li>{@link #EXTRA_PROVISIONING_ALLOWED_PROVISIONING_MODES}</li>
+ * <li>{@link #EXTRA_PROVISIONING_SENSORS_PERMISSION_GRANT_OPT_OUT}</li>
* </ul>
*
* <p>The target activity should return one of the following values in
@@ -3085,8 +3090,22 @@ public class DevicePolicyManager {
* activity, along with the values of the {@link #EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE} extra
* that are already supplied to this activity.
*
- * @see #EXTRA_PROVISIONING_KEEP_ACCOUNT_ON_MIGRATION
- * @see #EXTRA_PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED
+ * <p>Other extras the target activity may include in the intent result:
+ * <ul>
+ * <li>{@link #EXTRA_PROVISIONING_DISCLAIMERS}</li>
+ * <li>{@link #EXTRA_PROVISIONING_SKIP_ENCRYPTION}</li>
+ * <li>{@link #EXTRA_PROVISIONING_KEEP_SCREEN_ON}</li>
+ * <li>{@link #EXTRA_PROVISIONING_KEEP_ACCOUNT_ON_MIGRATION} for work profile
+ * provisioning</li>
+ * <li>{@link #EXTRA_PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED} for work profile
+ * provisioning</li>
+ * <li>{@link #EXTRA_PROVISIONING_SENSORS_PERMISSION_GRANT_OPT_OUT} for fully-managed
+ * device provisioning</li>
+ * <li>{@link #EXTRA_PROVISIONING_LOCALE} for fully-managed device provisioning</li>
+ * <li>{@link #EXTRA_PROVISIONING_LOCAL_TIME} for fully-managed device provisioning</li>
+ * <li>{@link #EXTRA_PROVISIONING_TIME_ZONE} for fully-managed device provisioning</li>
+ * </ul>
+ *
* @see #ACTION_ADMIN_POLICY_COMPLIANCE
*/
public static final String ACTION_GET_PROVISIONING_MODE =
diff --git a/core/java/android/app/servertransaction/LaunchActivityItem.java b/core/java/android/app/servertransaction/LaunchActivityItem.java
index abf1058f45a2..d7e09519bfb7 100644
--- a/core/java/android/app/servertransaction/LaunchActivityItem.java
+++ b/core/java/android/app/servertransaction/LaunchActivityItem.java
@@ -179,7 +179,7 @@ public class LaunchActivityItem extends ClientTransactionItem {
in.readPersistableBundle(getClass().getClassLoader()),
in.createTypedArrayList(ResultInfo.CREATOR),
in.createTypedArrayList(ReferrerIntent.CREATOR),
- readActivityOptions(in), in.readBoolean(),
+ ActivityOptions.fromBundle(in.readBundle()), in.readBoolean(),
in.readTypedObject(ProfilerInfo.CREATOR),
in.readStrongBinder(),
IActivityClientController.Stub.asInterface(in.readStrongBinder()),
@@ -187,11 +187,6 @@ public class LaunchActivityItem extends ClientTransactionItem {
in.readBoolean());
}
- private static ActivityOptions readActivityOptions(Parcel in) {
- Bundle bundle = in.readBundle();
- return bundle != null ? ActivityOptions.fromBundle(bundle) : null;
- }
-
public static final @NonNull Creator<LaunchActivityItem> CREATOR =
new Creator<LaunchActivityItem>() {
public LaunchActivityItem createFromParcel(Parcel in) {
diff --git a/core/java/android/app/servertransaction/StartActivityItem.java b/core/java/android/app/servertransaction/StartActivityItem.java
index f267060d1be6..15f65f6d9d26 100644
--- a/core/java/android/app/servertransaction/StartActivityItem.java
+++ b/core/java/android/app/servertransaction/StartActivityItem.java
@@ -23,7 +23,6 @@ import android.annotation.Nullable;
import android.app.ActivityOptions;
import android.app.ActivityThread.ActivityClientRecord;
import android.app.ClientTransactionHandler;
-import android.os.Bundle;
import android.os.Parcel;
import android.os.Trace;
@@ -84,8 +83,7 @@ public class StartActivityItem extends ActivityLifecycleItem {
/** Read from Parcel. */
private StartActivityItem(Parcel in) {
- Bundle bundle = in.readBundle();
- mActivityOptions = bundle != null ? ActivityOptions.fromBundle(bundle) : null;
+ mActivityOptions = ActivityOptions.fromBundle(in.readBundle());
}
public static final @NonNull Creator<StartActivityItem> CREATOR =
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 673127e9f808..2961b5505794 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -2084,7 +2084,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
splitNames = source.createString8Array();
splitSourceDirs = source.createString8Array();
splitPublicSourceDirs = source.createString8Array();
- splitDependencies = source.readSparseArray(null);
+ splitDependencies = source.readSparseArray(null, int[].class);
nativeLibraryDir = source.readString8();
secondaryNativeLibraryDir = source.readString8();
nativeLibraryRootDir = source.readString8();
diff --git a/core/java/android/inputmethodservice/IInputMethodWrapper.java b/core/java/android/inputmethodservice/IInputMethodWrapper.java
index af57f793bf73..02302a20fe38 100644
--- a/core/java/android/inputmethodservice/IInputMethodWrapper.java
+++ b/core/java/android/inputmethodservice/IInputMethodWrapper.java
@@ -40,6 +40,7 @@ import android.view.inputmethod.InputMethodSubtype;
import com.android.internal.inputmethod.CancellationGroup;
import com.android.internal.inputmethod.IInputMethodPrivilegedOperations;
+import com.android.internal.inputmethod.InputMethodNavButtonFlags;
import com.android.internal.os.HandlerCaller;
import com.android.internal.os.SomeArgs;
import com.android.internal.view.IInlineSuggestionsRequestCallback;
@@ -70,7 +71,7 @@ class IInputMethodWrapper extends IInputMethod.Stub
private static final int DO_SET_INPUT_CONTEXT = 20;
private static final int DO_UNSET_INPUT_CONTEXT = 30;
private static final int DO_START_INPUT = 32;
- private static final int DO_ON_SHOULD_SHOW_IME_SWITCHER_WHEN_IME_IS_SHOWN_CHANGED = 35;
+ private static final int DO_ON_NAV_BUTTON_FLAGS_CHANGED = 35;
private static final int DO_CREATE_SESSION = 40;
private static final int DO_SET_SESSION_ENABLED = 45;
private static final int DO_SHOW_SOFT_INPUT = 60;
@@ -176,7 +177,7 @@ class IInputMethodWrapper extends IInputMethod.Stub
try {
inputMethod.initializeInternal((IBinder) args.arg1,
(IInputMethodPrivilegedOperations) args.arg2, msg.arg1,
- (boolean) args.arg3, msg.arg2 != 0);
+ (boolean) args.arg3, msg.arg2);
} finally {
args.recycle();
}
@@ -196,22 +197,20 @@ class IInputMethodWrapper extends IInputMethod.Stub
final EditorInfo info = (EditorInfo) args.arg3;
final CancellationGroup cancellationGroup = (CancellationGroup) args.arg4;
final boolean restarting = args.argi5 == 1;
- final boolean shouldShowImeSwitcherWhenImeIsShown = args.argi6 != 0;
+ @InputMethodNavButtonFlags
+ final int navButtonFlags = args.argi6;
final InputConnection ic = inputContext != null
? new RemoteInputConnection(mTarget, inputContext, cancellationGroup)
: null;
info.makeCompatible(mTargetSdkVersion);
inputMethod.dispatchStartInputWithToken(ic, info, restarting, startInputToken,
- shouldShowImeSwitcherWhenImeIsShown);
+ navButtonFlags);
args.recycle();
return;
}
- case DO_ON_SHOULD_SHOW_IME_SWITCHER_WHEN_IME_IS_SHOWN_CHANGED: {
- final boolean shouldShowImeSwitcherWhenImeIsShown = msg.arg1 != 0;
- inputMethod.onShouldShowImeSwitcherWhenImeIsShownChanged(
- shouldShowImeSwitcherWhenImeIsShown);
+ case DO_ON_NAV_BUTTON_FLAGS_CHANGED:
+ inputMethod.onNavButtonFlagsChanged(msg.arg1);
return;
- }
case DO_CREATE_SESSION: {
SomeArgs args = (SomeArgs)msg.obj;
inputMethod.createSession(new InputMethodSessionCallbackWrapper(
@@ -301,10 +300,9 @@ class IInputMethodWrapper extends IInputMethod.Stub
@Override
public void initializeInternal(IBinder token, IInputMethodPrivilegedOperations privOps,
int configChanges, boolean stylusHwSupported,
- boolean shouldShowImeSwitcherWhenImeIsShown) {
+ @InputMethodNavButtonFlags int navButtonFlags) {
mCaller.executeOrSendMessage(mCaller.obtainMessageIIOOO(DO_INITIALIZE_INTERNAL,
- configChanges, shouldShowImeSwitcherWhenImeIsShown ? 1 : 0, token, privOps,
- stylusHwSupported));
+ configChanges, navButtonFlags, token, privOps, stylusHwSupported));
}
@BinderThread
@@ -344,23 +342,21 @@ class IInputMethodWrapper extends IInputMethod.Stub
@BinderThread
@Override
public void startInput(IBinder startInputToken, IInputContext inputContext,
- EditorInfo attribute, boolean restarting, boolean shouldShowImeSwitcherWhenImeIsShown) {
+ EditorInfo attribute, boolean restarting,
+ @InputMethodNavButtonFlags int navButtonFlags) {
if (mCancellationGroup == null) {
Log.e(TAG, "startInput must be called after bindInput.");
mCancellationGroup = new CancellationGroup();
}
mCaller.executeOrSendMessage(mCaller.obtainMessageOOOOII(DO_START_INPUT, startInputToken,
- inputContext, attribute, mCancellationGroup, restarting ? 1 : 0,
- shouldShowImeSwitcherWhenImeIsShown ? 1 : 0));
+ inputContext, attribute, mCancellationGroup, restarting ? 1 : 0, navButtonFlags));
}
@BinderThread
@Override
- public void onShouldShowImeSwitcherWhenImeIsShownChanged(
- boolean shouldShowImeSwitcherWhenImeIsShown) {
- mCaller.executeOrSendMessage(mCaller.obtainMessageI(
- DO_ON_SHOULD_SHOW_IME_SWITCHER_WHEN_IME_IS_SHOWN_CHANGED,
- shouldShowImeSwitcherWhenImeIsShown ? 1 : 0));
+ public void onNavButtonFlagsChanged(@InputMethodNavButtonFlags int navButtonFlags) {
+ mCaller.executeOrSendMessage(
+ mCaller.obtainMessageI(DO_ON_NAV_BUTTON_FLAGS_CHANGED, navButtonFlags));
}
@BinderThread
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index fbc0732affe1..60cd4181e933 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -140,6 +140,7 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.inputmethod.IInputContentUriToken;
import com.android.internal.inputmethod.IInputMethodPrivilegedOperations;
import com.android.internal.inputmethod.ImeTracing;
+import com.android.internal.inputmethod.InputMethodNavButtonFlags;
import com.android.internal.inputmethod.InputMethodPrivilegedOperations;
import com.android.internal.inputmethod.InputMethodPrivilegedOperationsRegistry;
import com.android.internal.view.IInlineSuggestionsRequestCallback;
@@ -660,7 +661,7 @@ public class InputMethodService extends AbstractInputMethodService {
@Override
public final void initializeInternal(@NonNull IBinder token,
IInputMethodPrivilegedOperations privilegedOperations, int configChanges,
- boolean stylusHwSupported, boolean shouldShowImeSwitcherWhenImeIsShown) {
+ boolean stylusHwSupported, @InputMethodNavButtonFlags int navButtonFlags) {
if (mDestroyed) {
Log.i(TAG, "The InputMethodService has already onDestroyed()."
+ "Ignore the initialization.");
@@ -673,8 +674,7 @@ public class InputMethodService extends AbstractInputMethodService {
if (stylusHwSupported) {
mInkWindow = new InkWindow(mWindow.getContext());
}
- mNavigationBarController.setShouldShowImeSwitcherWhenImeIsShown(
- shouldShowImeSwitcherWhenImeIsShown);
+ mNavigationBarController.onNavButtonFlagsChanged(navButtonFlags);
attachToken(token);
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
@@ -784,10 +784,9 @@ public class InputMethodService extends AbstractInputMethodService {
@Override
public final void dispatchStartInputWithToken(@Nullable InputConnection inputConnection,
@NonNull EditorInfo editorInfo, boolean restarting,
- @NonNull IBinder startInputToken, boolean shouldShowImeSwitcherWhenImeIsShown) {
+ @NonNull IBinder startInputToken, @InputMethodNavButtonFlags int navButtonFlags) {
mPrivOps.reportStartInputAsync(startInputToken);
- mNavigationBarController.setShouldShowImeSwitcherWhenImeIsShown(
- shouldShowImeSwitcherWhenImeIsShown);
+ mNavigationBarController.onNavButtonFlagsChanged(navButtonFlags);
if (restarting) {
restartInput(inputConnection, editorInfo);
} else {
@@ -801,10 +800,8 @@ public class InputMethodService extends AbstractInputMethodService {
*/
@MainThread
@Override
- public void onShouldShowImeSwitcherWhenImeIsShownChanged(
- boolean shouldShowImeSwitcherWhenImeIsShown) {
- mNavigationBarController.setShouldShowImeSwitcherWhenImeIsShown(
- shouldShowImeSwitcherWhenImeIsShown);
+ public void onNavButtonFlagsChanged(@InputMethodNavButtonFlags int navButtonFlags) {
+ mNavigationBarController.onNavButtonFlagsChanged(navButtonFlags);
}
/**
diff --git a/core/java/android/inputmethodservice/NavigationBarController.java b/core/java/android/inputmethodservice/NavigationBarController.java
index 19fa01de4a5f..0f9075b498ae 100644
--- a/core/java/android/inputmethodservice/NavigationBarController.java
+++ b/core/java/android/inputmethodservice/NavigationBarController.java
@@ -16,7 +16,6 @@
package android.inputmethodservice;
-import static android.content.Intent.ACTION_OVERLAY_CHANGED;
import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS;
import android.animation.ValueAnimator;
@@ -24,18 +23,12 @@ import android.annotation.FloatRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.StatusBarManager;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.Insets;
import android.graphics.Rect;
import android.graphics.Region;
import android.inputmethodservice.navigationbar.NavigationBarFrame;
import android.inputmethodservice.navigationbar.NavigationBarView;
-import android.os.PatternMatcher;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
@@ -49,6 +42,8 @@ import android.view.animation.Interpolator;
import android.view.animation.PathInterpolator;
import android.widget.FrameLayout;
+import com.android.internal.inputmethod.InputMethodNavButtonFlags;
+
import java.util.Objects;
/**
@@ -77,8 +72,7 @@ final class NavigationBarController {
default void onDestroy() {
}
- default void setShouldShowImeSwitcherWhenImeIsShown(
- boolean shouldShowImeSwitcherWhenImeIsShown) {
+ default void onNavButtonFlagsChanged(@InputMethodNavButtonFlags int navButtonFlags) {
}
default String toDebugString() {
@@ -117,8 +111,8 @@ final class NavigationBarController {
mImpl.onDestroy();
}
- void setShouldShowImeSwitcherWhenImeIsShown(boolean shouldShowImeSwitcherWhenImeIsShown) {
- mImpl.setShouldShowImeSwitcherWhenImeIsShown(shouldShowImeSwitcherWhenImeIsShown);
+ void onNavButtonFlagsChanged(@InputMethodNavButtonFlags int navButtonFlags) {
+ mImpl.onNavButtonFlagsChanged(navButtonFlags);
}
String toDebugString() {
@@ -144,9 +138,6 @@ final class NavigationBarController {
@Nullable
Insets mLastInsets;
- @Nullable
- private BroadcastReceiver mSystemOverlayChangedReceiver;
-
private boolean mShouldShowImeSwitcherWhenImeIsShown;
@Appearance
@@ -359,14 +350,6 @@ final class NavigationBarController {
});
}
- private boolean imeDrawsImeNavBar() {
- final Resources resources = mService.getResources();
- if (resources == null) {
- return false;
- }
- return resources.getBoolean(com.android.internal.R.bool.config_imeDrawsImeNavBar);
- }
-
@Override
public void onSoftInputWindowCreated(@NonNull SoftInputWindow softInputWindow) {
final Window window = softInputWindow.getWindow();
@@ -379,27 +362,6 @@ final class NavigationBarController {
if (mDestroyed) {
return;
}
- mImeDrawsImeNavBar = imeDrawsImeNavBar();
- if (mSystemOverlayChangedReceiver == null) {
- final IntentFilter intentFilter = new IntentFilter(ACTION_OVERLAY_CHANGED);
- intentFilter.addDataScheme(IntentFilter.SCHEME_PACKAGE);
- intentFilter.addDataSchemeSpecificPart("android", PatternMatcher.PATTERN_LITERAL);
- mSystemOverlayChangedReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (mDestroyed) {
- return;
- }
- mImeDrawsImeNavBar = imeDrawsImeNavBar();
- if (mImeDrawsImeNavBar) {
- installNavigationBarFrameIfNecessary();
- } else {
- uninstallNavigationBarFrameIfNecessary();
- }
- }
- };
- mService.registerReceiver(mSystemOverlayChangedReceiver, intentFilter);
- }
installNavigationBarFrameIfNecessary();
}
@@ -412,10 +374,6 @@ final class NavigationBarController {
mTintAnimator.cancel();
mTintAnimator = null;
}
- if (mSystemOverlayChangedReceiver != null) {
- mService.unregisterReceiver(mSystemOverlayChangedReceiver);
- mSystemOverlayChangedReceiver = null;
- }
mDestroyed = true;
}
@@ -448,28 +406,43 @@ final class NavigationBarController {
}
@Override
- public void setShouldShowImeSwitcherWhenImeIsShown(
- boolean shouldShowImeSwitcherWhenImeIsShown) {
+ public void onNavButtonFlagsChanged(@InputMethodNavButtonFlags int navButtonFlags) {
if (mDestroyed) {
return;
}
- if (mShouldShowImeSwitcherWhenImeIsShown == shouldShowImeSwitcherWhenImeIsShown) {
- return;
- }
+
+ final boolean imeDrawsImeNavBar =
+ (navButtonFlags & InputMethodNavButtonFlags.IME_DRAWS_IME_NAV_BAR) != 0;
+ final boolean shouldShowImeSwitcherWhenImeIsShown =
+ (navButtonFlags & InputMethodNavButtonFlags.SHOW_IME_SWITCHER_WHEN_IME_IS_SHOWN)
+ != 0;
+
+ mImeDrawsImeNavBar = imeDrawsImeNavBar;
+ final boolean prevShouldShowImeSwitcherWhenImeIsShown =
+ mShouldShowImeSwitcherWhenImeIsShown;
mShouldShowImeSwitcherWhenImeIsShown = shouldShowImeSwitcherWhenImeIsShown;
- if (mNavigationBarFrame == null) {
- return;
- }
- final NavigationBarView navigationBarView =
- mNavigationBarFrame.findViewByPredicate(NavigationBarView.class::isInstance);
- if (navigationBarView == null) {
- return;
+ if (imeDrawsImeNavBar) {
+ installNavigationBarFrameIfNecessary();
+ if (mNavigationBarFrame == null) {
+ return;
+ }
+ if (mShouldShowImeSwitcherWhenImeIsShown
+ == prevShouldShowImeSwitcherWhenImeIsShown) {
+ return;
+ }
+ final NavigationBarView navigationBarView = mNavigationBarFrame.findViewByPredicate(
+ NavigationBarView.class::isInstance);
+ if (navigationBarView == null) {
+ return;
+ }
+ final int hints = StatusBarManager.NAVIGATION_HINT_BACK_ALT
+ | (shouldShowImeSwitcherWhenImeIsShown
+ ? StatusBarManager.NAVIGATION_HINT_IME_SWITCHER_SHOWN : 0);
+ navigationBarView.setNavigationIconHints(hints);
+ } else {
+ uninstallNavigationBarFrameIfNecessary();
}
- final int hints = StatusBarManager.NAVIGATION_HINT_BACK_ALT
- | (shouldShowImeSwitcherWhenImeIsShown
- ? StatusBarManager.NAVIGATION_HINT_IME_SWITCHER_SHOWN : 0);
- navigationBarView.setNavigationIconHints(hints);
}
@Override
diff --git a/core/java/android/os/IpcDataCache.java b/core/java/android/os/IpcDataCache.java
new file mode 100644
index 000000000000..00734c806b7e
--- /dev/null
+++ b/core/java/android/os/IpcDataCache.java
@@ -0,0 +1,364 @@
+/*
+ * Copyright (C) 2022 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 android.os;
+
+import android.app.PropertyInvalidatedCache;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.FastPrintWriter;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Random;
+import java.util.Set;
+import java.util.WeakHashMap;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * LRU cache that's invalidated when an opaque value in a property changes. Self-synchronizing,
+ * but doesn't hold a lock across data fetches on query misses.
+ *
+ * The intended use case is caching frequently-read, seldom-changed information normally retrieved
+ * across interprocess communication. Imagine that you've written a user birthday information
+ * daemon called "birthdayd" that exposes an {@code IUserBirthdayService} interface over
+ * binder. That binder interface looks something like this:
+ *
+ * <pre>
+ * parcelable Birthday {
+ * int month;
+ * int day;
+ * }
+ * interface IUserBirthdayService {
+ * Birthday getUserBirthday(int userId);
+ * }
+ * </pre>
+ *
+ * Suppose the service implementation itself looks like this...
+ *
+ * <pre>
+ * public class UserBirthdayServiceImpl implements IUserBirthdayService {
+ * private final HashMap&lt;Integer, Birthday%&gt; mUidToBirthday;
+ * {@literal @}Override
+ * public synchronized Birthday getUserBirthday(int userId) {
+ * return mUidToBirthday.get(userId);
+ * }
+ * private synchronized void updateBirthdays(Map&lt;Integer, Birthday%&gt; uidToBirthday) {
+ * mUidToBirthday.clear();
+ * mUidToBirthday.putAll(uidToBirthday);
+ * }
+ * }
+ * </pre>
+ *
+ * ... and we have a client in frameworks (loaded into every app process) that looks like this:
+ *
+ * <pre>
+ * public class ActivityThread {
+ * ...
+ * public Birthday getUserBirthday(int userId) {
+ * return GetService("birthdayd").getUserBirthday(userId);
+ * }
+ * ...
+ * }
+ * </pre>
+ *
+ * With this code, every time an app calls {@code getUserBirthday(uid)}, we make a binder call to
+ * the birthdayd process and consult its database of birthdays. If we query user birthdays
+ * frequently, we do a lot of work that we don't have to do, since user birthdays change
+ * infrequently.
+ *
+ * IpcDataCache is part of a pattern for optimizing this kind of information-querying code. Using
+ * {@code IpcDataCache}, you'd write the client this way:
+ *
+ * <pre>
+ * public class ActivityThread {
+ * ...
+ * private final IpcDataCache.QueryHandler&lt;Integer, Birthday&gt; mBirthdayQuery =
+ * new IpcDataCache.QueryHandler&lt;Integer, Birthday&gt;() {
+ * {@literal @}Override
+ * public Birthday apply(Integer) {
+ * return GetService("birthdayd").getUserBirthday(userId);
+ * }
+ * };
+ * private static final int BDAY_CACHE_MAX = 8; // Maximum birthdays to cache
+ * private static final String BDAY_API = "getUserBirthday";
+ * private final IpcDataCache&lt;Integer, Birthday%&gt; mBirthdayCache = new
+ * IpcDataCache&lt;Integer, Birthday%&gt;(
+ * BDAY_CACHE_MAX, MODULE_SYSTEM, BDAY_API, BDAY_API, mBirthdayQuery);
+ *
+ * public void disableUserBirthdayCache() {
+ * mBirthdayCache.disableForCurrentProcess();
+ * }
+ * public void invalidateUserBirthdayCache() {
+ * mBirthdayCache.invalidateCache();
+ * }
+ * public Birthday getUserBirthday(int userId) {
+ * return mBirthdayCache.query(userId);
+ * }
+ * ...
+ * }
+ * </pre>
+ *
+ * With this cache, clients perform a binder call to birthdayd if asking for a user's birthday
+ * for the first time; on subsequent queries, we return the already-known Birthday object.
+ *
+ * The second parameter to the IpcDataCache constructor is a string that identifies the "module"
+ * that owns the cache. There are some well-known modules (such as {@code MODULE_SYSTEM} but any
+ * string is permitted. The third parameters is the name of the API being cached; this, too, can
+ * any value. The fourth is the name of the cache. The cache is usually named after th API.
+ * Some things you must know about the three strings:
+ * <list>
+ * <ul> The system property that controls the cache is named {@code cache_key.<module>.<api>}.
+ * Usually, the SELinux rules permit a process to write a system property (and therefore
+ * invalidate a cache) based on the wildcard {@code cache_key.<module>.*}. This means that
+ * although the cache can be constructed with any module string, whatever string is chosen must be
+ * consistent with the SELinux configuration.
+ * <ul> The API name can be any string of alphanumeric characters. All caches with the same API
+ * are invalidated at the same time. If a server supports several caches and all are invalidated
+ * in common, then it is most efficient to assign the same API string to every cache.
+ * <ul> The cache name can be any string. In debug output, the name is used to distiguish between
+ * caches with the same API name. The cache name is also used when disabling caches in the
+ * current process. So, invalidation is based on the module+api but disabling (which is generally
+ * a once-per-process operation) is based on the cache name.
+ * </list>
+ *
+ * User birthdays do occasionally change, so we have to modify the server to invalidate this
+ * cache when necessary. That invalidation code looks like this:
+ *
+ * <pre>
+ * public class UserBirthdayServiceImpl {
+ * ...
+ * public UserBirthdayServiceImpl() {
+ * ...
+ * ActivityThread.currentActivityThread().disableUserBirthdayCache();
+ * ActivityThread.currentActivityThread().invalidateUserBirthdayCache();
+ * }
+ *
+ * private synchronized void updateBirthdays(Map&lt;Integer, Birthday%&gt; uidToBirthday) {
+ * mUidToBirthday.clear();
+ * mUidToBirthday.putAll(uidToBirthday);
+ * ActivityThread.currentActivityThread().invalidateUserBirthdayCache();
+ * }
+ * ...
+ * }
+ * </pre>
+ *
+ * The call to {@code IpcDataCache.invalidateCache()} guarantees that all clients will re-fetch
+ * birthdays from binder during consequent calls to
+ * {@code ActivityThread.getUserBirthday()}. Because the invalidate call happens with the lock
+ * held, we maintain consistency between different client views of the birthday state. The use of
+ * IpcDataCache in this idiomatic way introduces no new race conditions.
+ *
+ * IpcDataCache has a few other features for doing things like incremental enhancement of cached
+ * values and invalidation of multiple caches (that all share the same property key) at once.
+ *
+ * {@code BDAY_CACHE_KEY} is the name of a property that we set to an opaque unique value each
+ * time we update the cache. SELinux configuration must allow everyone to read this property
+ * and it must allow any process that needs to invalidate the cache (here, birthdayd) to write
+ * the property. (These properties conventionally begin with the "cache_key." prefix.)
+ *
+ * The {@code UserBirthdayServiceImpl} constructor calls {@code disableUserBirthdayCache()} so
+ * that calls to {@code getUserBirthday} from inside birthdayd don't go through the cache. In this
+ * local case, there's no IPC, so use of the cache is (depending on exact circumstance)
+ * unnecessary.
+ *
+ * There may be queries for which it is more efficient to bypass the cache than to cache the
+ * result. This would be true, for example, if some queries would require frequent cache
+ * invalidation while other queries require infrequent invalidation. To expand on the birthday
+ * example, suppose that there is a userId that signifies "the next birthday". When passed this
+ * userId, the server returns the next birthday among all users - this value changes as time
+ * advances. The userId value can be cached, but the cache must be invalidated whenever a
+ * birthday occurs, and this invalidates all birthdays. If there is a large number of users,
+ * invalidation will happen so often that the cache provides no value.
+ *
+ * The class provides a bypass mechanism to handle this situation.
+ * <pre>
+ * public class ActivityThread {
+ * ...
+ * private final IpcDataCache.QueryHandler&lt;Integer, Birthday&gt; mBirthdayQuery =
+ * new IpcDataCache.QueryHandler&lt;Integer, Birthday&gt;() {
+ * {@literal @}Override
+ * public Birthday apply(Integer) {
+ * return GetService("birthdayd").getUserBirthday(userId);
+ * }
+ * {@literal @}Override
+ * public boolean shouldBypassQuery(Integer userId) {
+ * return userId == NEXT_BIRTHDAY;
+ * }
+ * };
+ * ...
+ * }
+ * </pre>
+ *
+ * If the {@code shouldBypassQuery()} method returns true then the cache is not used for that
+ * particular query. The {@code shouldBypassQuery()} method is not abstract and the default
+ * implementation returns false.
+ *
+ * For security, there is a allowlist of processes that are allowed to invalidate a cache. The
+ * allowlist includes normal runtime processes but does not include test processes. Test
+ * processes must call {@code IpcDataCache.disableForTestMode()} to disable all cache activity in
+ * that process.
+ *
+ * Caching can be disabled completely by initializing {@code sEnabled} to false and rebuilding.
+ *
+ * To test a binder cache, create one or more tests that exercise the binder method. This should
+ * be done twice: once with production code and once with a special image that sets {@code DEBUG}
+ * and {@code VERIFY} true. In the latter case, verify that no cache inconsistencies are
+ * reported. If a cache inconsistency is reported, however, it might be a false positive. This
+ * happens if the server side data can be read and written non-atomically with respect to cache
+ * invalidation.
+ *
+ * @param <Query> The class used to index cache entries: must be hashable and comparable
+ * @param <Result> The class holding cache entries; use a boxed primitive if possible
+ * @hide
+ */
+@TestApi
+@SystemApi(client=SystemApi.Client.MODULE_LIBRARIES)
+public class IpcDataCache<Query, Result> extends PropertyInvalidatedCache<Query, Result> {
+ /**
+ * {@inheritDoc}
+ * @hide
+ */
+ @SystemApi(client=SystemApi.Client.MODULE_LIBRARIES)
+ @TestApi
+ public static abstract class QueryHandler<Q,R>
+ extends PropertyInvalidatedCache.QueryHandler<Q,R> {
+ /**
+ * Compute a result given a query. The semantics are those of Functor.
+ */
+ public abstract @Nullable R apply(@NonNull Q query);
+
+ /**
+ * Return true if a query should not use the cache. The default implementation
+ * always uses the cache.
+ */
+ public boolean shouldBypassCache(@NonNull Q query) {
+ return false;
+ }
+ };
+
+ /**
+ * The module used for unit tests and cts tests. It is expected that no process in
+ * the system has permissions to write properties with this module.
+ * @hide
+ */
+ @TestApi
+ public static final String MODULE_TEST = PropertyInvalidatedCache.MODULE_TEST;
+
+ /**
+ * The module used for system server/framework caches. This is not visible outside
+ * the system processes.
+ * @hide
+ */
+ @TestApi
+ public static final String MODULE_SYSTEM = PropertyInvalidatedCache.MODULE_SYSTEM;
+
+ /**
+ * The module used for bluetooth caches.
+ * @hide
+ */
+ @SystemApi(client=SystemApi.Client.MODULE_LIBRARIES)
+ @TestApi
+ public static final String MODULE_BLUETOOTH = PropertyInvalidatedCache.MODULE_BLUETOOTH;
+
+ /**
+ * Make a new property invalidated cache. The key is computed from the module and api
+ * parameters.
+ *
+ * @param maxEntries Maximum number of entries to cache; LRU discard
+ * @param module The module under which the cache key should be placed.
+ * @param api The api this cache front-ends. The api must be a Java identifier but
+ * need not be an actual api.
+ * @param cacheName Name of this cache in debug and dumpsys
+ * @param computer The code to compute values that are not in the cache.
+ * @hide
+ */
+ @SystemApi(client=SystemApi.Client.MODULE_LIBRARIES)
+ @TestApi
+ public IpcDataCache(int maxEntries, @NonNull String module, @NonNull String api,
+ @NonNull String cacheName, @NonNull QueryHandler<Query, Result> computer) {
+ super(maxEntries, module, api, cacheName, computer);
+ }
+
+ /**
+ * {@inheritDoc}
+ * @hide
+ */
+ @SystemApi(client=SystemApi.Client.MODULE_LIBRARIES)
+ @TestApi
+ @Override
+ public void disableForCurrentProcess() {
+ super.disableForCurrentProcess();
+ }
+
+ /**
+ * {@inheritDoc}
+ * @hide
+ */
+ @SystemApi(client=SystemApi.Client.MODULE_LIBRARIES)
+ @TestApi
+ public static void disableForCurrentProcess(@NonNull String cacheName) {
+ PropertyInvalidatedCache.disableForCurrentProcess(cacheName);
+ }
+
+ /**
+ * {@inheritDoc}
+ * @hide
+ */
+ @SystemApi(client=SystemApi.Client.MODULE_LIBRARIES)
+ @TestApi
+ @Override
+ public @Nullable Result query(@NonNull Query query) {
+ return super.query(query);
+ }
+
+ /**
+ * {@inheritDoc}
+ * @hide
+ */
+ @SystemApi(client=SystemApi.Client.MODULE_LIBRARIES)
+ @TestApi
+ @Override
+ public void invalidateCache() {
+ super.invalidateCache();
+ }
+
+ /**
+ * Invalidate caches in all processes that are keyed for the module and api.
+ * @hide
+ */
+ @SystemApi(client=SystemApi.Client.MODULE_LIBRARIES)
+ @TestApi
+ public static void invalidateCache(@NonNull String module, @NonNull String api) {
+ PropertyInvalidatedCache.invalidateCache(module, api);
+ }
+}
diff --git a/core/java/android/service/dreams/OWNERS b/core/java/android/service/dreams/OWNERS
index f8318054ddfc..489a5f62b49d 100644
--- a/core/java/android/service/dreams/OWNERS
+++ b/core/java/android/service/dreams/OWNERS
@@ -1,3 +1,8 @@
# Bug component: 78010
+brycelee@google.com
dsandler@google.com
+galinap@google.com
+jjaggi@google.com
+michaelwr@google.com
+santoscordon@google.com
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index 0ef585478346..cc93adc32541 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -1877,7 +1877,7 @@ public final class MotionEvent extends InputEvent implements Parcelable {
float x, float y, float pressure, float size, int metaState,
float xPrecision, float yPrecision, int deviceId, int edgeFlags) {
return obtain(downTime, eventTime, action, x, y, pressure, size, metaState,
- xPrecision, yPrecision, deviceId, edgeFlags, InputDevice.SOURCE_UNKNOWN,
+ xPrecision, yPrecision, deviceId, edgeFlags, InputDevice.SOURCE_CLASS_POINTER,
DEFAULT_DISPLAY);
}
diff --git a/core/java/android/view/inputmethod/InputMethod.java b/core/java/android/view/inputmethod/InputMethod.java
index 69ad739608b2..fd336a27bb67 100644
--- a/core/java/android/view/inputmethod/InputMethod.java
+++ b/core/java/android/view/inputmethod/InputMethod.java
@@ -31,6 +31,7 @@ import android.view.MotionEvent;
import android.view.View;
import com.android.internal.inputmethod.IInputMethodPrivilegedOperations;
+import com.android.internal.inputmethod.InputMethodNavButtonFlags;
import com.android.internal.view.IInlineSuggestionsRequestCallback;
import com.android.internal.view.InlineSuggestionsRequestInfo;
@@ -105,14 +106,13 @@ public interface InputMethod {
* current IME.
* @param configChanges {@link InputMethodInfo#getConfigChanges()} declared by IME.
* @param stylusHwSupported {@link InputMethodInfo#supportsStylusHandwriting()} declared by IME.
- * @param shouldShowImeSwitcherWhenImeIsShown {@code true} If the IME switcher is expected to be
- * shown while the IME is shown.
+ * @param navButtonFlags The initial state of {@link InputMethodNavButtonFlags}.
* @hide
*/
@MainThread
default void initializeInternal(IBinder token,
IInputMethodPrivilegedOperations privilegedOperations, int configChanges,
- boolean stylusHwSupported, boolean shouldShowImeSwitcherWhenImeIsShown) {
+ boolean stylusHwSupported, @InputMethodNavButtonFlags int navButtonFlags) {
attachToken(token);
}
@@ -231,8 +231,7 @@ public interface InputMethod {
* the next {@link #startInput(InputConnection, EditorInfo, IBinder)} as
* long as your implementation of {@link InputMethod} relies on such
* IPCs
- * @param shouldShowImeSwitcherWhenImeIsShown {@code true} If the IME switcher is expected to be
- * shown while the IME is shown.
+ * @param navButtonFlags {@link InputMethodNavButtonFlags} in the initial state of this session.
* @see #startInput(InputConnection, EditorInfo)
* @see #restartInput(InputConnection, EditorInfo)
* @see EditorInfo
@@ -241,7 +240,7 @@ public interface InputMethod {
@MainThread
default void dispatchStartInputWithToken(@Nullable InputConnection inputConnection,
@NonNull EditorInfo editorInfo, boolean restarting,
- @NonNull IBinder startInputToken, boolean shouldShowImeSwitcherWhenImeIsShown) {
+ @NonNull IBinder startInputToken, @InputMethodNavButtonFlags int navButtonFlags) {
if (restarting) {
restartInput(inputConnection, editorInfo);
} else {
@@ -250,15 +249,13 @@ public interface InputMethod {
}
/**
- * Notifies that whether the IME should show the IME switcher or not is being changed.
+ * Notifies that {@link InputMethodNavButtonFlags} have been updated.
*
- * @param shouldShowImeSwitcherWhenImeIsShown {@code true} If the IME switcher is expected to be
- * shown while the IME is shown.
+ * @param navButtonFlags The new {@link InputMethodNavButtonFlags}.
* @hide
*/
@MainThread
- default void onShouldShowImeSwitcherWhenImeIsShownChanged(
- boolean shouldShowImeSwitcherWhenImeIsShown) {
+ default void onNavButtonFlagsChanged(@InputMethodNavButtonFlags int navButtonFlags) {
}
/**
diff --git a/core/java/com/android/internal/inputmethod/InputMethodNavButtonFlags.java b/core/java/com/android/internal/inputmethod/InputMethodNavButtonFlags.java
new file mode 100644
index 000000000000..728a42907cc4
--- /dev/null
+++ b/core/java/com/android/internal/inputmethod/InputMethodNavButtonFlags.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2022 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.internal.inputmethod;
+
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+
+/**
+ * A set of flags notified from {@link com.android.server.inputmethod.InputMethodManagerService} to
+ * {@link android.inputmethodservice.InputMethodService} regarding how
+ * {@link android.inputmethodservice.NavigationBarController} should behave.
+ *
+ * <p>These flags will take effect when and only when
+ * {@link android.inputmethodservice.InputMethodService#canImeRenderGesturalNavButtons} returns
+ * {@code true}.</p>
+ */
+@Retention(SOURCE)
+@IntDef(flag = true, value = {
+ InputMethodNavButtonFlags.IME_DRAWS_IME_NAV_BAR,
+ InputMethodNavButtonFlags.SHOW_IME_SWITCHER_WHEN_IME_IS_SHOWN,
+})
+public @interface InputMethodNavButtonFlags {
+ /**
+ * When set, the IME process needs to render and handle the navigation bar buttons such as the
+ * back button and the IME switcher button.
+ */
+ int IME_DRAWS_IME_NAV_BAR = 1;
+ /**
+ * When set, the IME switcher icon needs to be shown on the navigation bar.
+ */
+ int SHOW_IME_SWITCHER_WHEN_IME_IS_SHOWN = 2;
+}
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index 9163b6d6215e..1d60c501a88b 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -70,7 +70,7 @@ interface IStatusBarService
void onPanelRevealed(boolean clearNotificationEffects, int numItems);
void onPanelHidden();
// Mark current notifications as "seen" and stop ringing, vibrating, blinking.
- void clearNotificationEffects();
+ oneway void clearNotificationEffects();
void onNotificationClick(String key, in NotificationVisibility nv);
void onNotificationActionClick(String key, int actionIndex, in Notification.Action action, in NotificationVisibility nv, boolean generatedByAssistant);
void onNotificationError(String pkg, String tag, int id,
diff --git a/core/java/com/android/internal/view/IInputMethod.aidl b/core/java/com/android/internal/view/IInputMethod.aidl
index d2bc3442ed36..273c5f170812 100644
--- a/core/java/com/android/internal/view/IInputMethod.aidl
+++ b/core/java/com/android/internal/view/IInputMethod.aidl
@@ -37,8 +37,7 @@ import com.android.internal.view.InlineSuggestionsRequestInfo;
*/
oneway interface IInputMethod {
void initializeInternal(IBinder token, IInputMethodPrivilegedOperations privOps,
- int configChanges, boolean stylusHwSupported,
- boolean shouldShowImeSwitcherWhenImeIsShown);
+ int configChanges, boolean stylusHwSupported, int navigationBarFlags);
void onCreateInlineSuggestionsRequest(in InlineSuggestionsRequestInfo requestInfo,
in IInlineSuggestionsRequestCallback cb);
@@ -48,10 +47,9 @@ oneway interface IInputMethod {
void unbindInput();
void startInput(in IBinder startInputToken, in IInputContext inputContext,
- in EditorInfo attribute, boolean restarting,
- boolean shouldShowImeSwitcherWhenImeIsShown);
+ in EditorInfo attribute, boolean restarting, int navigationBarFlags);
- void onShouldShowImeSwitcherWhenImeIsShownChanged(boolean shouldShowImeSwitcherWhenImeIsShown);
+ void onNavButtonFlagsChanged(int navButtonFlags);
void createSession(in InputChannel channel, IInputSessionCallback callback);
diff --git a/core/jni/com_android_internal_os_ZygoteCommandBuffer.cpp b/core/jni/com_android_internal_os_ZygoteCommandBuffer.cpp
index 248db76da71d..0c05da551c8f 100644
--- a/core/jni/com_android_internal_os_ZygoteCommandBuffer.cpp
+++ b/core/jni/com_android_internal_os_ZygoteCommandBuffer.cpp
@@ -45,7 +45,7 @@ using android::zygote::ZygoteFailure;
// WARNING: Knows a little about the wire protocol used to communicate with Zygote.
// TODO: Fix error handling.
-constexpr size_t MAX_COMMAND_BYTES = 12200;
+constexpr size_t MAX_COMMAND_BYTES = 32768;
constexpr size_t NICE_NAME_BYTES = 50;
// A buffer optionally bundled with a file descriptor from which we can fill it.
@@ -273,8 +273,6 @@ class NativeCommandBuffer {
char mBuffer[MAX_COMMAND_BYTES];
};
-static_assert(sizeof(NativeCommandBuffer) < 3 * 4096);
-
static int buffersAllocd(0);
// Get a new NativeCommandBuffer. Can only be called once between freeNativeBuffer calls,
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index a7f749c6bfbf..fa9eba34d30b 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -3718,15 +3718,26 @@
android:protectionLevel="signature|privileged" />
<!-- ========================================= -->
- <!-- Permissions for SupplementalApi -->
+ <!-- Permissions for AdServices -->
<!-- ========================================= -->
<eat-comment />
- <!-- TODO(b/213488783): Update with correct names. -->
- <!-- Allows an application to access SupplementalApis. -->
- <permission android:name="android.permission.ACCESS_SUPPLEMENTAL_APIS"
- android:label="@string/permlab_accessSupplementalApi"
- android:description="@string/permdesc_accessSupplementalApi"
+ <!-- Allows an application to access AdServices Topics API. -->
+ <permission android:name="android.permission.ACCESS_ADSERVICES_TOPICS"
+ android:label="@string/permlab_accessAdServicesTopics"
+ android:description="@string/permdesc_accessAdServicesTopics"
+ android:protectionLevel="normal" />
+
+ <!-- Allows an application to access AdServices Attribution APIs. -->
+ <permission android:name="android.permission.ACCESS_ADSERVICES_ATTRIBUTION"
+ android:label="@string/permlab_accessAdServicesAttribution"
+ android:description="@string/permdesc_accessAdServicesAttribution"
+ android:protectionLevel="normal" />
+
+ <!-- Allows an application to access AdServices Custom Audiences APIs. -->
+ <permission android:name="android.permission.ACCESS_ADSERVICES_CUSTOM_AUDIENCES"
+ android:label="@string/permlab_accessAdServicesCustomAudiences"
+ android:description="@string/permdesc_accessAdServicesCustomAudiences"
android:protectionLevel="normal" />
<!-- ==================================== -->
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index c8202c5989b5..05ebef9bf56f 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -4095,10 +4095,20 @@
<!-- Description of an application permission that lets it query all other packages. [CHAR LIMIT=NONE] -->
<string name="permdesc_queryAllPackages">Allows an app to see all installed packages.</string>
- <!-- Title of an application permission that lets it access SupplementalApis. [CHAR LIMIT=NONE] -->
- <string name="permlab_accessSupplementalApi">access SupplementalApis</string>
- <!-- Description of an application permission that lets it access SupplementalApis. [CHAR LIMIT=NONE]-->
- <string name="permdesc_accessSupplementalApi">Allows an application to access SupplementalApis.</string>
+ <!-- Title of an application permission that lets it access AdServices Topics API. [CHAR LIMIT=NONE] -->
+ <string name="permlab_accessAdServicesTopics">access AdServices Topics API</string>
+ <!-- Description of an application permission that lets it access AdServices Topics API. [CHAR LIMIT=NONE]-->
+ <string name="permdesc_accessAdServicesTopics">Allows an application to access AdServices Topics API.</string>
+
+ <!-- Title of an application permission that lets it access AdServices Attribution APIs. [CHAR LIMIT=NONE] -->
+ <string name="permlab_accessAdServicesAttribution">access AdServices Attribution APIs</string>
+ <!-- Description of an application permission that lets it access AdServices Attribution APIs. [CHAR LIMIT=NONE]-->
+ <string name="permdesc_accessAdServicesAttribution">Allows an application to access AdServices Attribution APIs.</string>
+
+ <!-- Title of an application permission that lets it access AdServices Custom Audiences API. [CHAR LIMIT=NONE] -->
+ <string name="permlab_accessAdServicesCustomAudiences">access AdServices Custom Audiences API</string>
+ <!-- Description of an application permission that lets it access AdServices Custom Audiences API. [CHAR LIMIT=NONE]-->
+ <string name="permdesc_accessAdServicesCustomAudiences">Allows an application to access AdServices Custom Audiences API.</string>
<!-- Shown in the tutorial for tap twice for zoom control. -->
<string name="tutorial_double_tap_to_zoom_message_short">Tap twice for zoom control</string>
diff --git a/core/tests/coretests/src/android/os/IpcDataCacheTest.java b/core/tests/coretests/src/android/os/IpcDataCacheTest.java
new file mode 100644
index 000000000000..fa7d7214d289
--- /dev/null
+++ b/core/tests/coretests/src/android/os/IpcDataCacheTest.java
@@ -0,0 +1,312 @@
+/*
+ * Copyright (C) 2022 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 android.os;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertSame;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.After;
+import org.junit.Test;
+
+/**
+ * Test for verifying the behavior of {@link IpcDataCache}. This test does
+ * not use any actual binder calls - it is entirely self-contained. This test also relies
+ * on the test mode of {@link IpcDataCache} because Android SELinux rules do
+ * not grant test processes the permission to set system properties.
+ * <p>
+ * Build/Install/Run:
+ * atest FrameworksCoreTests:IpcDataCacheTest
+ */
+@SmallTest
+public class IpcDataCacheTest {
+
+ // Configuration for creating caches
+ private static final String MODULE = IpcDataCache.MODULE_TEST;
+ private static final String API = "testApi";
+
+ // This class is a proxy for binder calls. It contains a counter that increments
+ // every time the class is queried.
+ private static class ServerProxy {
+ // The number of times this class was queried.
+ private int mCount = 0;
+
+ // A single query. The key behavior is that the query count is incremented.
+ boolean query(int x) {
+ mCount++;
+ return value(x);
+ }
+
+ // Return the expected value of an input, without incrementing the query count.
+ boolean value(int x) {
+ return x % 3 == 0;
+ }
+
+ // Verify the count.
+ void verify(int x) {
+ assertEquals(x, mCount);
+ }
+ }
+
+ // The functions for querying the server.
+ private static class ServerQuery
+ extends IpcDataCache.QueryHandler<Integer, Boolean> {
+ private final ServerProxy mServer;
+
+ ServerQuery(ServerProxy server) {
+ mServer = server;
+ }
+
+ @Override
+ public Boolean apply(Integer x) {
+ return mServer.query(x);
+ }
+ @Override
+ public boolean shouldBypassCache(Integer x) {
+ return x % 13 == 0;
+ }
+ }
+
+ // Clear the test mode after every test, in case this process is used for other
+ // tests. This also resets the test property map.
+ @After
+ public void tearDown() throws Exception {
+ IpcDataCache.setTestMode(false);
+ }
+
+ // This test is disabled pending an sepolicy change that allows any app to set the
+ // test property.
+ @Test
+ public void testBasicCache() {
+
+ // A stand-in for the binder. The test verifies that calls are passed through to
+ // this class properly.
+ ServerProxy tester = new ServerProxy();
+
+ // Create a cache that uses simple arithmetic to computer its values.
+ IpcDataCache<Integer, Boolean> testCache =
+ new IpcDataCache<>(4, MODULE, API, "testCache1",
+ new ServerQuery(tester));
+
+ IpcDataCache.setTestMode(true);
+ testCache.testPropertyName();
+
+ tester.verify(0);
+ assertEquals(tester.value(3), testCache.query(3));
+ tester.verify(1);
+ assertEquals(tester.value(3), testCache.query(3));
+ tester.verify(2);
+ testCache.invalidateCache();
+ assertEquals(tester.value(3), testCache.query(3));
+ tester.verify(3);
+ assertEquals(tester.value(5), testCache.query(5));
+ tester.verify(4);
+ assertEquals(tester.value(5), testCache.query(5));
+ tester.verify(4);
+ assertEquals(tester.value(3), testCache.query(3));
+ tester.verify(4);
+
+ // Invalidate the cache, and verify that the next read on 3 goes to the server.
+ testCache.invalidateCache();
+ assertEquals(tester.value(3), testCache.query(3));
+ tester.verify(5);
+
+ // Test bypass. The query for 13 always bypasses the cache.
+ assertEquals(tester.value(12), testCache.query(12));
+ assertEquals(tester.value(13), testCache.query(13));
+ assertEquals(tester.value(14), testCache.query(14));
+ tester.verify(8);
+ assertEquals(tester.value(12), testCache.query(12));
+ assertEquals(tester.value(13), testCache.query(13));
+ assertEquals(tester.value(14), testCache.query(14));
+ tester.verify(9);
+ }
+
+ @Test
+ public void testDisableCache() {
+
+ // A stand-in for the binder. The test verifies that calls are passed through to
+ // this class properly.
+ ServerProxy tester = new ServerProxy();
+
+ // Three caches, all using the same system property but one uses a different name.
+ IpcDataCache<Integer, Boolean> cache1 =
+ new IpcDataCache<>(4, MODULE, API, "cacheA",
+ new ServerQuery(tester));
+ IpcDataCache<Integer, Boolean> cache2 =
+ new IpcDataCache<>(4, MODULE, API, "cacheA",
+ new ServerQuery(tester));
+ IpcDataCache<Integer, Boolean> cache3 =
+ new IpcDataCache<>(4, MODULE, API, "cacheB",
+ new ServerQuery(tester));
+
+ // Caches are enabled upon creation.
+ assertEquals(false, cache1.getDisabledState());
+ assertEquals(false, cache2.getDisabledState());
+ assertEquals(false, cache3.getDisabledState());
+
+ // Disable the cache1 instance. Only cache1 is disabled
+ cache1.disableInstance();
+ assertEquals(true, cache1.getDisabledState());
+ assertEquals(false, cache2.getDisabledState());
+ assertEquals(false, cache3.getDisabledState());
+
+ // Disable cache1. This will disable cache1 and cache2 because they share the
+ // same name. cache3 has a different name and will not be disabled.
+ cache1.disableForCurrentProcess();
+ assertEquals(true, cache1.getDisabledState());
+ assertEquals(true, cache2.getDisabledState());
+ assertEquals(false, cache3.getDisabledState());
+
+ // Create a new cache1. Verify that the new instance is disabled.
+ cache1 = new IpcDataCache<>(4, MODULE, API, "cacheA",
+ new ServerQuery(tester));
+ assertEquals(true, cache1.getDisabledState());
+
+ // Remove the record of caches being locally disabled. This is a clean-up step.
+ cache1.forgetDisableLocal();
+ assertEquals(true, cache1.getDisabledState());
+ assertEquals(true, cache2.getDisabledState());
+ assertEquals(false, cache3.getDisabledState());
+
+ // Create a new cache1. Verify that the new instance is not disabled.
+ cache1 = new IpcDataCache<>(4, MODULE, API, "cacheA",
+ new ServerQuery(tester));
+ assertEquals(false, cache1.getDisabledState());
+ }
+
+ private static class TestQuery
+ extends IpcDataCache.QueryHandler<Integer, String> {
+
+ private int mRecomputeCount = 0;
+
+ @Override
+ public String apply(Integer qv) {
+ mRecomputeCount += 1;
+ return "foo" + qv.toString();
+ }
+
+ int getRecomputeCount() {
+ return mRecomputeCount;
+ }
+ }
+
+ private static class TestCache extends IpcDataCache<Integer, String> {
+ private final TestQuery mQuery;
+
+ TestCache() {
+ this(MODULE, API);
+ }
+
+ TestCache(String module, String api) {
+ this(module, api, new TestQuery());
+ }
+
+ TestCache(String module, String api, TestQuery query) {
+ super(4, module, api, "testCache7", query);
+ mQuery = query;
+ setTestMode(true);
+ testPropertyName();
+ }
+
+ int getRecomputeCount() {
+ return mQuery.getRecomputeCount();
+ }
+ }
+
+ @Test
+ public void testCacheRecompute() {
+ TestCache cache = new TestCache();
+ cache.invalidateCache();
+ assertEquals(cache.isDisabled(), false);
+ assertEquals("foo5", cache.query(5));
+ assertEquals(1, cache.getRecomputeCount());
+ assertEquals("foo5", cache.query(5));
+ assertEquals(1, cache.getRecomputeCount());
+ assertEquals("foo6", cache.query(6));
+ assertEquals(2, cache.getRecomputeCount());
+ cache.invalidateCache();
+ assertEquals("foo5", cache.query(5));
+ assertEquals("foo5", cache.query(5));
+ assertEquals(3, cache.getRecomputeCount());
+ // Invalidate the cache with a direct call to the property.
+ IpcDataCache.invalidateCache(MODULE, API);
+ assertEquals("foo5", cache.query(5));
+ assertEquals("foo5", cache.query(5));
+ assertEquals(4, cache.getRecomputeCount());
+ }
+
+ @Test
+ public void testCacheInitialState() {
+ TestCache cache = new TestCache();
+ assertEquals("foo5", cache.query(5));
+ assertEquals("foo5", cache.query(5));
+ assertEquals(2, cache.getRecomputeCount());
+ cache.invalidateCache();
+ assertEquals("foo5", cache.query(5));
+ assertEquals("foo5", cache.query(5));
+ assertEquals(3, cache.getRecomputeCount());
+ }
+
+ @Test
+ public void testCachePropertyUnset() {
+ final String UNSET_API = "otherApi";
+ TestCache cache = new TestCache(MODULE, UNSET_API);
+ assertEquals("foo5", cache.query(5));
+ assertEquals("foo5", cache.query(5));
+ assertEquals(2, cache.getRecomputeCount());
+ }
+
+ @Test
+ public void testCacheDisableState() {
+ TestCache cache = new TestCache();
+ assertEquals("foo5", cache.query(5));
+ assertEquals("foo5", cache.query(5));
+ assertEquals(2, cache.getRecomputeCount());
+ cache.invalidateCache();
+ assertEquals("foo5", cache.query(5));
+ assertEquals("foo5", cache.query(5));
+ assertEquals(3, cache.getRecomputeCount());
+ cache.disableSystemWide();
+ assertEquals("foo5", cache.query(5));
+ assertEquals("foo5", cache.query(5));
+ assertEquals(5, cache.getRecomputeCount());
+ cache.invalidateCache(); // Should not reenable
+ assertEquals("foo5", cache.query(5));
+ assertEquals("foo5", cache.query(5));
+ assertEquals(7, cache.getRecomputeCount());
+ }
+
+ @Test
+ public void testLocalProcessDisable() {
+ TestCache cache = new TestCache();
+ assertEquals(cache.isDisabled(), false);
+ cache.invalidateCache();
+ assertEquals("foo5", cache.query(5));
+ assertEquals(1, cache.getRecomputeCount());
+ assertEquals("foo5", cache.query(5));
+ assertEquals(1, cache.getRecomputeCount());
+ assertEquals(cache.isDisabled(), false);
+ cache.disableForCurrentProcess();
+ assertEquals(cache.isDisabled(), true);
+ assertEquals("foo5", cache.query(5));
+ assertEquals("foo5", cache.query(5));
+ assertEquals(3, cache.getRecomputeCount());
+ }
+}
diff --git a/core/tests/coretests/src/android/view/MotionEventTest.java b/core/tests/coretests/src/android/view/MotionEventTest.java
index 78a8f7b3f32e..c4c983d24af9 100644
--- a/core/tests/coretests/src/android/view/MotionEventTest.java
+++ b/core/tests/coretests/src/android/view/MotionEventTest.java
@@ -16,6 +16,7 @@
package android.view;
+import static android.view.InputDevice.SOURCE_CLASS_POINTER;
import static android.view.MotionEvent.ACTION_DOWN;
import static android.view.MotionEvent.ACTION_POINTER_DOWN;
import static android.view.MotionEvent.TOOL_TYPE_FINGER;
@@ -214,4 +215,27 @@ public class MotionEventTest {
rotInvalid.transform(mat);
assertEquals(-1, rotInvalid.getSurfaceRotation());
}
+
+ @Test
+ public void testUsesPointerSourceByDefault() {
+ final MotionEvent event = MotionEvent.obtain(0 /* downTime */, 0 /* eventTime */,
+ ACTION_DOWN, 0 /* x */, 0 /* y */, 0 /* metaState */);
+ assertTrue(event.isFromSource(SOURCE_CLASS_POINTER));
+ }
+
+ @Test
+ public void testLocationOffsetOnlyAppliedToNonPointerSources() {
+ final MotionEvent event = MotionEvent.obtain(0 /* downTime */, 0 /* eventTime */,
+ ACTION_DOWN, 10 /* x */, 20 /* y */, 0 /* metaState */);
+ event.offsetLocation(40, 50);
+
+ // The offset should be applied since a pointer source is used by default.
+ assertEquals(50, (int) event.getX());
+ assertEquals(70, (int) event.getY());
+
+ // The offset should not be applied if the source is changed to a non-pointer source.
+ event.setSource(InputDevice.SOURCE_JOYSTICK);
+ assertEquals(10, (int) event.getX());
+ assertEquals(20, (int) event.getY());
+ }
}
diff --git a/graphics/java/android/graphics/RuntimeShader.java b/graphics/java/android/graphics/RuntimeShader.java
index 2ff888b06dd8..6abe34b1d675 100644
--- a/graphics/java/android/graphics/RuntimeShader.java
+++ b/graphics/java/android/graphics/RuntimeShader.java
@@ -19,12 +19,228 @@ package android.graphics;
import android.annotation.ColorInt;
import android.annotation.ColorLong;
import android.annotation.NonNull;
+import android.view.Window;
import libcore.util.NativeAllocationRegistry;
/**
- * Shader that calculates per-pixel color via a user defined Android Graphics Shading Language
- * (AGSL) function.
+ * <p>A {@link RuntimeShader} calculates a per-pixel color based on the output of a user defined
+ * Android Graphics Shading Language (AGSL) function.</p>
+ *
+ * <h3>Android Graphics Shading Language</h3>
+ * <p>The AGSL syntax is very similar to OpenGL ES Shading Language, but there are some important
+ * differences that are highlighted here. Most of these differences are summed up in one basic fact:
+ * <b>With GPU shading languages, you are programming a stage of the GPU pipeline. With AGSL, you
+ * are programming a stage of the {@link Canvas} or {@link RenderNode} drawing pipeline.</b></p>
+ *
+ * <p>In particular, a GLSL fragment shader controls the entire behavior of the GPU between the
+ * rasterizer and the blending hardware. That shader does all of the work to compute a color, and
+ * the color it generates is exactly what is fed to the blending stage of the pipeline.</p>
+ *
+ * <p>In contrast, AGSL functions exist as part of a larger pipeline. When you issue a
+ * {@link Canvas} drawing operation, Android (generally) assembles a single GPU fragment shader to
+ * do all of the required work. This shader typically includes several pieces. For example, it might
+ * include:</p>
+ * <ul>
+ * <li>Evaluating whether a pixel falls inside or outside of the shape being drawn (or on the
+ * border, where it might apply antialiasing).</li>
+ * <li>Evaluating whether a pixel falls inside or outside of the clipping region (again, with
+ * possible antialiasing logic for border pixels).</li>
+ * <li>Logic for the {@link Shader}, {@link ColorFilter}, and {@link BlendMode} on the
+ * {@link Paint}.</li>
+ * <li>Color space conversion code, as part of Android’s color management.</li>
+ * </ul>
+ *
+ * <p>A {@link RuntimeShader}, like other {@link Shader} types, effectively contributes a function
+ * to the GPU’s fragment shader.</p>
+ *
+ * <h3>AGSL Shader Execution</h3>
+ * <p>Just like a GLSL shader, an AGSL shader begins execution in a main function. Unlike GLSL, the
+ * function receives as an input parameter the position of the pixel within the {@link Canvas} or
+ * {@link RenderNode} coordinate space (similar to gl_fragCoord) and returns the color to be shaded
+ * as a vec4 (similar to out vec4 color or gl_FragColor in GLSL).</p>
+ *
+ * <pre class="prettyprint">
+ * vec4 main(vec2 canvas_coordinates);
+ * </pre>
+ *
+ * <p>AGSL and GLSL use different coordinate spaces by default. In GLSL, the fragment coordinate
+ * (fragCoord) is relative to the lower left. AGSL matches the screen coordinate system of the
+ * Android {@link Canvas} which has its origin as the upper left corner. This means that the
+ * coordinates provided as a parameter in the main function are local to the canvas with the
+ * exception of any {@link Shader#getLocalMatrix(Matrix)} transformations applied to this shader.
+ * Additionally, if the shader is invoked by another using {@link #setInputShader(String, Shader)},
+ * then that parent shader may modify the input coordinates arbitrarily.</p>
+ *
+ * <h3>AGSL and Color Spaces</h3>
+ * <p>Android Graphics and by extension {@link RuntimeShader} are color managed. The working
+ * {@link ColorSpace} for an AGSL shader is defined to be the color space of the destination, which
+ * in most cases is determined by {@link Window#setColorMode(int)}.</p>
+ *
+ * <p>When authoring an AGSL shader, you won’t know what the working color space is. For many
+ * effects, this is fine because by default color inputs are automatically converted into the
+ * working color space. For certain effects, it may be important to do some math in a fixed, known
+ * color space. A common example is lighting – to get physically accurate lighting, math should be
+ * done in a linear color space. To help with this, AGSL provides two intrinsic functions that
+ * convert colors between the working color space and the
+ * {@link ColorSpace.Named#LINEAR_EXTENDED_SRGB} color space:
+ *
+ * <pre class="prettyprint">
+ * vec3 toLinearSrgb(vec3 color);
+ * vec3 fromLinearSrgb(vec3 color);</pre>
+ *
+ * <h3>AGSL and Premultiplied Alpha</h3>
+ * <p>When dealing with transparent colors, there are two (common) possible representations:
+ * straight (unassociated) alpha and premultiplied (associated) alpha. In ASGL the color returned
+ * by the main function is expected to be premultiplied. AGSL’s use of premultiplied alpha
+ * implies:
+ * </p>
+ *
+ * <ul>
+ * <li>If your AGSL shader will return transparent colors, be sure to multiply the RGB by A. The
+ * resulting color should be [R*A, G*A, B*A, A], not [R, G, B, A].</li>
+ * <li>For more complex shaders, you must understand which of your colors are premultiplied vs.
+ * straight. Many operations don’t make sense if you mix both kinds of color together.</li>
+ * </ul>
+ *
+ * <h3>Uniforms</h3>
+ * <p>AGSL, like GLSL, exposes the concept of uniforms. An AGSL uniform is defined as a read-only,
+ * global variable that is accessible by the AGSL code and is initialized by a number of setter
+ * methods on {@link RuntimeShader}. AGSL exposes two primitive uniform data types (float, int) and
+ * two specialized types (colors, shaders) that are outlined below.</p>
+ *
+ * <h4>Primitive Uniforms</h4>
+ * <p>There are two primitive uniform types supported by AGSL, float and int. For these types and
+ * uniforms representing a grouping of these types, like arrays and matrices, there are
+ * corresponding {@link RuntimeShader} methods to initialize them.
+ * <table border="2" width="85%" align="center" cellpadding="5">
+ * <thead>
+ * <tr><th>Java Type</th> <th>AGSL Type</th> <th>Method</th> </tr>
+ * </thead>
+ *
+ * <tbody>
+ * <tr>
+ * <td rowspan="4">Floats</td>
+ * <td>float</td>
+ * <td>{@link RuntimeShader#setFloatUniform(String, float)}</td>
+ * </tr>
+ * <tr>
+ * <td>vec2</td>
+ * <td>{@link RuntimeShader#setFloatUniform(String, float, float)}</td>
+ * </tr>
+ * <tr>
+ * <td>vec3</td>
+ * <td>{@link RuntimeShader#setFloatUniform(String, float, float, float)}</td>
+ * </tr>
+ * <tr>
+ * <td>vec4</td>
+ * <td>{@link RuntimeShader#setFloatUniform(String, float, float, float, float)}</td>
+ * </tr>
+ * <tr>
+ * <td rowspan="4">Integers</td>
+ * <td>int</td>
+ * <td>{@link RuntimeShader#setIntUniform(String, int)}</td>
+ * </tr>
+ * <tr>
+ * <td>ivec2</td>
+ * <td>{@link RuntimeShader#setIntUniform(String, int, int)}</td>
+ * </tr>
+ * <tr>
+ * <td>ivec3</td>
+ * <td>{@link RuntimeShader#setIntUniform(String, int, int, int)}</td>
+ * </tr>
+ * <tr>
+ * <td>ivec4</td>
+ * <td>{@link RuntimeShader#setIntUniform(String, int, int, int, int)}</td>
+ * </tr>
+ * <tr>
+ * <td rowspan="2">Matrices and Arrays</td>
+ * <td>mat2, mat3, and mat4, and float[]</td>
+ * <td>{@link RuntimeShader#setFloatUniform(String, float[])}</td>
+ * </tr>
+ * <tr>
+ * <td>int[]</td>
+ * <td>{@link RuntimeShader#setIntUniform(String, int[])}</td>
+ * </tr>
+ * </tbody>
+ * </table>
+ *
+ * For example, a simple AGSL shader making use of a float uniform to modulate the transparency
+ * of the output color would look like:</p>
+ *
+ * <pre class="prettyprint">
+ * uniform float alpha;
+ * vec4 main(vec2 canvas_coordinates) {
+ * vec3 red = vec3(1.0, 0.0, 0.0);
+ * return vec4(red * alpha, alpha);
+ * }</pre>
+ *
+ * <p>After creating a {@link RuntimeShader} with that program the uniform can then be initialized
+ * and updated per frame by calling {@link RuntimeShader#setFloatUniform(String, float)} with the
+ * value of alpha. The value of a primitive uniform defaults to 0 if it is declared in the AGSL
+ * shader but not initialized.</p>
+ *
+ * <h4>Color Uniforms</h4>
+ * <p>AGSL doesn't know if uniform variables contain colors, it won't automatically convert them to
+ * the working colorspace of the shader at runtime. However, you can label your vec4 uniform with
+ * the "layout(color)" qualifier which lets Android know that the uniform will be used as a color.
+ * Doing so allows AGSL to transform the uniform value to the working color space. In AGSL, declare
+ * the uniform like this:
+ *
+ * <pre class="prettyprint">
+ * layout(color) uniform vec4 inputColorA;
+ * layout(color) uniform vec4 inputColorB;
+ * vec4 main(vec2 canvas_coordinates) {
+ * // blend the two colors together and return the resulting color
+ * return mix(inputColorA, inputColorB, 0.5);
+ * }</pre>
+ *
+ * <p>After creating a {@link RuntimeShader} with that program the uniforms can
+ * then be initialized and updated per frame by calling
+ * {@link RuntimeShader#setColorUniform(String, int)},
+ * {@link RuntimeShader#setColorUniform(String, long)}, or
+ * {@link RuntimeShader#setColorUniform(String, Color)} with the desired colors. The value of a
+ * color uniform is undefined if it is declared in the AGSL shader but not initialized.</p>
+ *
+ * <h4>Shader Uniforms</h4>
+ * In GLSL, a fragment shader can sample a texture. For AGSL instead of sampling textures you can
+ * sample from any {@link Shader}, which includes but is not limited to {@link BitmapShader}. To
+ * make it clear that you are operating on an {@link Shader} object there is no "sample"
+ * method. Instead, the shader uniform has an "eval()" method. This distinction enables AGSL shaders
+ * to sample from existing bitmap and gradient shaders as well as other {@link RuntimeShader}
+ * objects. In AGSL, declare the uniform like this:
+ *
+ * <pre class="prettyprint">
+ * uniform shader myShader;
+ * vec4 main(vec2 canvas_coordinates) {
+ * // swap the red and blue color channels when sampling from myShader
+ * return myShader.sample(canvas_coordinates).bgra;
+ * }</pre>
+ *
+ * <p>After creating a {@link RuntimeShader} with that program the shader uniform can
+ * then be initialized and updated per frame by calling
+ * {@link RuntimeShader#setInputShader(String, Shader)} with the desired shader. The value of a
+ * shader uniform is undefined if it is declared in the AGSL shader but not initialized.</p>
+ *
+ * <p>Although most {@link BitmapShader}s contain colors that should be color managed, some contain
+ * data that isn’t actually colors. This includes bitmaps storing normals, material properties
+ * (e.g. roughness), heightmaps, or any other purely mathematical data that happens to be stored in
+ * a bitmap. When using these kinds of shaders in AGSL, you probably want to initialize them with
+ * {@link #setInputBuffer(String, BitmapShader)}. Shaders initialized this way work much like
+ * a regular {@link BitmapShader} (including filtering and tiling), with a few major differences:
+ * <ul>
+ * <li>No color space transformation is applied (the color space of the bitmap is ignored).</li>
+ * <li>Bitmaps that return false for {@link Bitmap#isPremultiplied()} are not automatically
+ * premultiplied.</li>
+ * </ul>
+ *
+ * <p>In addition, when sampling from a {@link BitmapShader} be aware that the shader does not use
+ * normalized coordinates (like a texture in GLSL). It uses (0, 0) in the upper-left corner, and
+ * (width, height) in the bottom-right corner. Normally, this is exactly what you want. If you’re
+ * evaluating the shader with coordinates based on the ones passed to your AGSL program, the scale
+ * is correct. However, if you want to adjust those coordinates (to do some kind of re-mapping of
+ * the bitmap), remember that the coordinates are local to the canvas.</p>
+ *
*/
public class RuntimeShader extends Shader {
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
index 358104fffbf6..e5d127609b2e 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
@@ -83,16 +83,10 @@ public class AndroidKeyStoreProvider extends Provider {
// java.security.KeyPairGenerator
put("KeyPairGenerator.EC", PACKAGE_NAME + ".AndroidKeyStoreKeyPairGeneratorSpi$EC");
put("KeyPairGenerator.RSA", PACKAGE_NAME + ".AndroidKeyStoreKeyPairGeneratorSpi$RSA");
- put("KeyPairGenerator." + X25519_ALIAS,
- PACKAGE_NAME + ".AndroidKeyStoreKeyPairGeneratorSpi$RSA");
- put("KeyPairGenerator." + ED25519_OID,
- PACKAGE_NAME + ".AndroidKeyStoreKeyPairGeneratorSpi$RSA");
// java.security.KeyFactory
putKeyFactoryImpl("EC");
putKeyFactoryImpl("RSA");
- putKeyFactoryImpl(X25519_ALIAS);
- putKeyFactoryImpl(ED25519_OID);
// javax.crypto.KeyGenerator
put("KeyGenerator.AES", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$AES");
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/common/CommonDisplayFeature.java b/libs/WindowManager/Jetpack/src/androidx/window/common/CommonFoldingFeature.java
index eb9429747b66..1c49881904e4 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/common/CommonDisplayFeature.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/common/CommonFoldingFeature.java
@@ -18,17 +18,73 @@ package androidx.window.common;
import static androidx.window.util.ExtensionHelper.isZero;
+import android.annotation.IntDef;
import android.annotation.Nullable;
import android.graphics.Rect;
+import android.util.Log;
import androidx.annotation.NonNull;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-/** Wrapper for both Extension and Sidecar versions of DisplayFeature. */
-final class CommonDisplayFeature implements DisplayFeature {
+/** A representation of a folding feature for both Extension and Sidecar.
+ * For Sidecar this is the same as combining {@link androidx.window.sidecar.SidecarDeviceState} and
+ * {@link androidx.window.sidecar.SidecarDisplayFeature}. For Extensions this is the mirror of
+ * {@link androidx.window.extensions.layout.FoldingFeature}.
+ */
+public final class CommonFoldingFeature {
+
+ private static final boolean DEBUG = false;
+
+ public static final String TAG = CommonFoldingFeature.class.getSimpleName();
+
+ /**
+ * A common type to represent a hinge where the screen is continuous.
+ */
+ public static final int COMMON_TYPE_FOLD = 1;
+
+ /**
+ * A common type to represent a hinge where there is a physical gap separating multiple
+ * displays.
+ */
+ public static final int COMMON_TYPE_HINGE = 2;
+
+ @IntDef({COMMON_TYPE_FOLD, COMMON_TYPE_HINGE})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Type {
+ }
+
+ /**
+ * A common state to represent when the state is not known. One example is if the device is
+ * closed. We do not emit this value for developers but is useful for implementation reasons.
+ */
+ public static final int COMMON_STATE_UNKNOWN = -1;
+
+ /**
+ * A common state to represent a FLAT hinge. This is needed because the definitions in Sidecar
+ * and Extensions do not match exactly.
+ */
+ public static final int COMMON_STATE_FLAT = 3;
+ /**
+ * A common state to represent a HALF_OPENED hinge. This is needed because the definitions in
+ * Sidecar and Extensions do not match exactly.
+ */
+ public static final int COMMON_STATE_HALF_OPENED = 2;
+
+ /**
+ * The possible states for a folding hinge.
+ */
+ @IntDef({COMMON_STATE_UNKNOWN, COMMON_STATE_FLAT, COMMON_STATE_HALF_OPENED})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface State {
+ }
+
private static final Pattern FEATURE_PATTERN =
Pattern.compile("([a-z]+)-\\[(\\d+),(\\d+),(\\d+),(\\d+)]-?(flat|half-opened)?");
@@ -38,17 +94,49 @@ final class CommonDisplayFeature implements DisplayFeature {
private static final String PATTERN_STATE_FLAT = "flat";
private static final String PATTERN_STATE_HALF_OPENED = "half-opened";
- // TODO(b/183049815): Support feature strings that include the state of the feature.
+ /**
+ * Parse a {@link List} of {@link CommonFoldingFeature} from a {@link String}.
+ * @param value a {@link String} representation of multiple {@link CommonFoldingFeature}
+ * separated by a ":".
+ * @param hingeState a global fallback value for a {@link CommonFoldingFeature} if one is not
+ * specified in the input.
+ * @throws IllegalArgumentException if the provided string is improperly formatted or could not
+ * otherwise be parsed.
+ * @see #FEATURE_PATTERN
+ * @return {@link List} of {@link CommonFoldingFeature}.
+ */
+ static List<CommonFoldingFeature> parseListFromString(@NonNull String value,
+ @State int hingeState) {
+ List<CommonFoldingFeature> features = new ArrayList<>();
+ String[] featureStrings = value.split(";");
+ for (String featureString : featureStrings) {
+ CommonFoldingFeature feature;
+ try {
+ feature = CommonFoldingFeature.parseFromString(featureString, hingeState);
+ } catch (IllegalArgumentException e) {
+ if (DEBUG) {
+ Log.w(TAG, "Failed to parse display feature: " + featureString, e);
+ }
+ continue;
+ }
+ features.add(feature);
+ }
+ return features;
+ }
/**
* Parses a display feature from a string.
*
+ * @param string A {@link String} representation of a {@link CommonFoldingFeature}.
+ * @param hingeState A fallback value for the {@link State} if it is not specified in the input.
* @throws IllegalArgumentException if the provided string is improperly formatted or could not
* otherwise be parsed.
+ * @return {@link CommonFoldingFeature} represented by the {@link String} value.
* @see #FEATURE_PATTERN
*/
@NonNull
- static CommonDisplayFeature parseFromString(@NonNull String string) {
+ private static CommonFoldingFeature parseFromString(@NonNull String string,
+ @State int hingeState) {
Matcher featureMatcher = FEATURE_PATTERN.matcher(string);
if (!featureMatcher.matches()) {
throw new IllegalArgumentException("Malformed feature description format: " + string);
@@ -59,10 +147,10 @@ final class CommonDisplayFeature implements DisplayFeature {
int type;
switch (featureType) {
case FEATURE_TYPE_FOLD:
- type = 1 /* TYPE_FOLD */;
+ type = COMMON_TYPE_FOLD;
break;
case FEATURE_TYPE_HINGE:
- type = 2 /* TYPE_HINGE */;
+ type = COMMON_TYPE_HINGE;
break;
default: {
throw new IllegalArgumentException("Malformed feature type: " + featureType);
@@ -79,7 +167,7 @@ final class CommonDisplayFeature implements DisplayFeature {
}
String stateString = featureMatcher.group(6);
stateString = stateString == null ? "" : stateString;
- Integer state;
+ final int state;
switch (stateString) {
case PATTERN_STATE_FLAT:
state = COMMON_STATE_FLAT;
@@ -88,10 +176,10 @@ final class CommonDisplayFeature implements DisplayFeature {
state = COMMON_STATE_HALF_OPENED;
break;
default:
- state = null;
+ state = hingeState;
break;
}
- return new CommonDisplayFeature(type, state, featureRect);
+ return new CommonFoldingFeature(type, state, featureRect);
} catch (NumberFormatException e) {
throw new IllegalArgumentException("Malformed feature description: " + string, e);
}
@@ -99,11 +187,11 @@ final class CommonDisplayFeature implements DisplayFeature {
private final int mType;
@Nullable
- private final Integer mState;
+ private final int mState;
@NonNull
private final Rect mRect;
- CommonDisplayFeature(int type, @Nullable Integer state, @NonNull Rect rect) {
+ CommonFoldingFeature(int type, int state, @NonNull Rect rect) {
assertValidState(state);
this.mType = type;
this.mState = state;
@@ -114,16 +202,19 @@ final class CommonDisplayFeature implements DisplayFeature {
this.mRect = rect;
}
+ /** Returns the type of the feature. */
+ @Type
public int getType() {
return mType;
}
- /** Returns the state of the feature, or {@code null} if the feature has no state. */
- @Nullable
- public Integer getState() {
+ /** Returns the state of the feature.*/
+ @State
+ public int getState() {
return mState;
}
+ /** Returns the bounds of the feature. */
@NonNull
public Rect getRect() {
return mRect;
@@ -133,7 +224,7 @@ final class CommonDisplayFeature implements DisplayFeature {
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
- CommonDisplayFeature that = (CommonDisplayFeature) o;
+ CommonFoldingFeature that = (CommonFoldingFeature) o;
return mType == that.mType
&& Objects.equals(mState, that.mState)
&& mRect.equals(that.mRect);
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerPostureProducer.java b/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java
index fa9a5a8b7a1b..6987401525b4 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerPostureProducer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java
@@ -18,11 +18,15 @@ package androidx.window.common;
import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE;
+import static androidx.window.common.CommonFoldingFeature.COMMON_STATE_UNKNOWN;
+import static androidx.window.common.CommonFoldingFeature.parseListFromString;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.hardware.devicestate.DeviceStateManager;
import android.hardware.devicestate.DeviceStateManager.DeviceStateCallback;
+import android.text.TextUtils;
import android.util.Log;
import android.util.SparseIntArray;
@@ -30,6 +34,7 @@ import androidx.window.util.BaseDataProducer;
import com.android.internal.R;
+import java.util.List;
import java.util.Optional;
/**
@@ -37,10 +42,13 @@ import java.util.Optional;
* by mapping the state returned from {@link DeviceStateManager} to values provided in the resources
* config at {@link R.array#config_device_state_postures}.
*/
-public final class DeviceStateManagerPostureProducer extends BaseDataProducer<Integer> {
- private static final String TAG = "ConfigDevicePostureProducer";
+public final class DeviceStateManagerFoldingFeatureProducer extends
+ BaseDataProducer<List<CommonFoldingFeature>> {
+ private static final String TAG =
+ DeviceStateManagerFoldingFeatureProducer.class.getSimpleName();
private static final boolean DEBUG = false;
+ private final Context mContext;
private final SparseIntArray mDeviceStateToPostureMap = new SparseIntArray();
private int mCurrentDeviceState = INVALID_DEVICE_STATE;
@@ -50,7 +58,8 @@ public final class DeviceStateManagerPostureProducer extends BaseDataProducer<In
notifyDataChanged();
};
- public DeviceStateManagerPostureProducer(@NonNull Context context) {
+ public DeviceStateManagerFoldingFeatureProducer(@NonNull Context context) {
+ mContext = context;
String[] deviceStatePosturePairs = context.getResources()
.getStringArray(R.array.config_device_state_postures);
for (String deviceStatePosturePair : deviceStatePosturePairs) {
@@ -86,8 +95,17 @@ public final class DeviceStateManagerPostureProducer extends BaseDataProducer<In
@Override
@Nullable
- public Optional<Integer> getData() {
- final int posture = mDeviceStateToPostureMap.get(mCurrentDeviceState, -1);
- return posture != -1 ? Optional.of(posture) : Optional.empty();
+ public Optional<List<CommonFoldingFeature>> getData() {
+ final int globalHingeState = globalHingeState();
+ String displayFeaturesString = mContext.getResources().getString(
+ R.string.config_display_features);
+ if (TextUtils.isEmpty(displayFeaturesString)) {
+ return Optional.empty();
+ }
+ return Optional.of(parseListFromString(displayFeaturesString, globalHingeState));
+ }
+
+ private int globalHingeState() {
+ return mDeviceStateToPostureMap.get(mCurrentDeviceState, COMMON_STATE_UNKNOWN);
}
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/common/DisplayFeature.java b/libs/WindowManager/Jetpack/src/androidx/window/common/DisplayFeature.java
deleted file mode 100644
index 573641857b99..000000000000
--- a/libs/WindowManager/Jetpack/src/androidx/window/common/DisplayFeature.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2021 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 androidx.window.common;
-
-import android.annotation.IntDef;
-import android.annotation.Nullable;
-import android.graphics.Rect;
-
-import androidx.annotation.NonNull;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/** Wrapper for both Extension and Sidecar versions of DisplayFeature. */
-public interface DisplayFeature {
- /** Returns the type of the feature. */
- int getType();
-
- /** Returns the state of the feature, or {@code null} if the feature has no state. */
- @Nullable
- @State
- Integer getState();
-
- /** Returns the bounds of the feature. */
- @NonNull
- Rect getRect();
-
- /**
- * A common state to represent a FLAT hinge. This is needed because the definitions in Sidecar
- * and Extensions do not match exactly.
- */
- int COMMON_STATE_FLAT = 3;
- /**
- * A common state to represent a HALF_OPENED hinge. This is needed because the definitions in
- * Sidecar and Extensions do not match exactly.
- */
- int COMMON_STATE_HALF_OPENED = 2;
-
- /**
- * The possible states for a folding hinge.
- */
- @IntDef({COMMON_STATE_FLAT, COMMON_STATE_HALF_OPENED})
- @Retention(RetentionPolicy.SOURCE)
- @interface State {}
-
-}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/common/EmptyLifecycleCallbacksAdapter.java b/libs/WindowManager/Jetpack/src/androidx/window/common/EmptyLifecycleCallbacksAdapter.java
new file mode 100644
index 000000000000..d923a46c3b5d
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/common/EmptyLifecycleCallbacksAdapter.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2022 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 androidx.window.common;
+
+import android.app.Activity;
+import android.app.Application;
+import android.os.Bundle;
+
+/**
+ * An empty implementation of {@link Application.ActivityLifecycleCallbacks} derived classes can
+ * implement the methods necessary.
+ */
+public class EmptyLifecycleCallbacksAdapter implements Application.ActivityLifecycleCallbacks {
+ @Override
+ public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
+ }
+
+ @Override
+ public void onActivityStarted(Activity activity) {
+ }
+
+ @Override
+ public void onActivityResumed(Activity activity) {
+ }
+
+ @Override
+ public void onActivityPaused(Activity activity) {
+ }
+
+ @Override
+ public void onActivityStopped(Activity activity) {
+ }
+
+ @Override
+ public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
+ }
+
+ @Override
+ public void onActivityDestroyed(Activity activity) {
+ }
+}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/common/ResourceConfigDisplayFeatureProducer.java b/libs/WindowManager/Jetpack/src/androidx/window/common/ResourceConfigDisplayFeatureProducer.java
deleted file mode 100644
index cd2cadc082e1..000000000000
--- a/libs/WindowManager/Jetpack/src/androidx/window/common/ResourceConfigDisplayFeatureProducer.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright (C) 2021 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 androidx.window.common;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.Context;
-import android.text.TextUtils;
-import android.util.Log;
-
-import androidx.window.util.BaseDataProducer;
-
-import com.android.internal.R;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Optional;
-
-/**
- * Implementation of {@link androidx.window.util.DataProducer} that produces
- * {@link CommonDisplayFeature} parsed from a string stored in the resources config at
- * {@link R.string#config_display_features}.
- */
-public final class ResourceConfigDisplayFeatureProducer extends
- BaseDataProducer<List<DisplayFeature>> {
- private static final boolean DEBUG = false;
- private static final String TAG = "ResourceConfigDisplayFeatureProducer";
-
- private final Context mContext;
-
- public ResourceConfigDisplayFeatureProducer(@NonNull Context context) {
- mContext = context;
- }
-
- @Override
- @Nullable
- public Optional<List<DisplayFeature>> getData() {
- String displayFeaturesString = mContext.getResources().getString(
- R.string.config_display_features);
- if (TextUtils.isEmpty(displayFeaturesString)) {
- return Optional.empty();
- }
-
- List<DisplayFeature> features = new ArrayList<>();
- String[] featureStrings = displayFeaturesString.split(";");
- for (String featureString : featureStrings) {
- CommonDisplayFeature feature;
- try {
- feature = CommonDisplayFeature.parseFromString(featureString);
- } catch (IllegalArgumentException e) {
- if (DEBUG) {
- Log.w(TAG, "Failed to parse display feature: " + featureString, e);
- }
- continue;
- }
- features.add(feature);
- }
- return Optional.of(features);
- }
-}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/common/SettingsDevicePostureProducer.java b/libs/WindowManager/Jetpack/src/androidx/window/common/SettingsDevicePostureProducer.java
deleted file mode 100644
index 2026df3fa979..000000000000
--- a/libs/WindowManager/Jetpack/src/androidx/window/common/SettingsDevicePostureProducer.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright (C) 2021 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 androidx.window.common;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.database.ContentObserver;
-import android.net.Uri;
-import android.os.Handler;
-import android.os.Looper;
-import android.provider.Settings;
-
-import androidx.window.util.BaseDataProducer;
-
-import java.util.Optional;
-
-/**
- * Implementation of {@link androidx.window.util.DataProducer} that provides the device posture
- * as an {@link Integer} from a value stored in {@link Settings}.
- */
-public final class SettingsDevicePostureProducer extends BaseDataProducer<Integer> {
- private static final String DEVICE_POSTURE = "device_posture";
-
- private final Uri mDevicePostureUri =
- Settings.Global.getUriFor(DEVICE_POSTURE);
-
- private final ContentResolver mResolver;
- private final ContentObserver mObserver;
- private boolean mRegisteredObservers;
-
- public SettingsDevicePostureProducer(@NonNull Context context) {
- mResolver = context.getContentResolver();
- mObserver = new SettingsObserver();
- }
-
- @Override
- @Nullable
- public Optional<Integer> getData() {
- int posture = Settings.Global.getInt(mResolver, DEVICE_POSTURE, -1);
- return posture == -1 ? Optional.empty() : Optional.of(posture);
- }
-
- /**
- * Registers settings observers, if needed. When settings observers are registered for this
- * producer callbacks for changes in data will be triggered.
- */
- public void registerObserversIfNeeded() {
- if (mRegisteredObservers) {
- return;
- }
- mRegisteredObservers = true;
- mResolver.registerContentObserver(mDevicePostureUri, false /* notifyForDescendants */,
- mObserver /* ContentObserver */);
- }
-
- /**
- * Unregisters settings observers, if needed. When settings observers are unregistered for this
- * producer callbacks for changes in data will not be triggered.
- */
- public void unregisterObserversIfNeeded() {
- if (!mRegisteredObservers) {
- return;
- }
- mRegisteredObservers = false;
- mResolver.unregisterContentObserver(mObserver);
- }
-
- private final class SettingsObserver extends ContentObserver {
- SettingsObserver() {
- super(new Handler(Looper.getMainLooper()));
- }
-
- @Override
- public void onChange(boolean selfChange, Uri uri) {
- if (mDevicePostureUri.equals(uri)) {
- notifyDataChanged();
- }
- }
- }
-}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/common/SettingsDisplayFeatureProducer.java b/libs/WindowManager/Jetpack/src/androidx/window/common/SettingsDisplayFeatureProducer.java
index 040662657a74..e9d213e06fa9 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/common/SettingsDisplayFeatureProducer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/common/SettingsDisplayFeatureProducer.java
@@ -16,8 +16,10 @@
package androidx.window.common;
+import static androidx.window.common.CommonFoldingFeature.COMMON_STATE_UNKNOWN;
+import static androidx.window.common.CommonFoldingFeature.parseListFromString;
+
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.content.ContentResolver;
import android.content.Context;
import android.database.ContentObserver;
@@ -26,22 +28,19 @@ import android.os.Handler;
import android.os.Looper;
import android.provider.Settings;
import android.text.TextUtils;
-import android.util.Log;
import androidx.window.util.BaseDataProducer;
-import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.Optional;
/**
* Implementation of {@link androidx.window.util.DataProducer} that produces
- * {@link CommonDisplayFeature} parsed from a string stored in {@link Settings}.
+ * {@link CommonFoldingFeature} parsed from a string stored in {@link Settings}.
*/
public final class SettingsDisplayFeatureProducer
- extends BaseDataProducer<List<DisplayFeature>> {
- private static final boolean DEBUG = false;
- private static final String TAG = "SettingsDisplayFeatureProducer";
+ extends BaseDataProducer<List<CommonFoldingFeature>> {
private static final String DISPLAY_FEATURES = "display_features";
private final Uri mDisplayFeaturesUri =
@@ -57,32 +56,17 @@ public final class SettingsDisplayFeatureProducer
}
@Override
- @Nullable
- public Optional<List<DisplayFeature>> getData() {
+ @NonNull
+ public Optional<List<CommonFoldingFeature>> getData() {
String displayFeaturesString = Settings.Global.getString(mResolver, DISPLAY_FEATURES);
if (displayFeaturesString == null) {
return Optional.empty();
}
- List<DisplayFeature> features = new ArrayList<>();
if (TextUtils.isEmpty(displayFeaturesString)) {
- return Optional.of(features);
- }
- String[] featureStrings = displayFeaturesString.split(";");
-
- for (String featureString : featureStrings) {
- CommonDisplayFeature feature;
- try {
- feature = CommonDisplayFeature.parseFromString(featureString);
- } catch (IllegalArgumentException e) {
- if (DEBUG) {
- Log.w(TAG, "Failed to parse display feature: " + featureString, e);
- }
- continue;
- }
- features.add(feature);
+ return Optional.of(Collections.emptyList());
}
- return Optional.of(features);
+ return Optional.of(parseListFromString(displayFeaturesString, COMMON_STATE_UNKNOWN));
}
/**
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
index 6d8383372461..1d2b9384f47d 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -28,7 +28,6 @@ import android.app.Activity;
import android.app.ActivityClient;
import android.app.ActivityOptions;
import android.app.ActivityThread;
-import android.app.Application.ActivityLifecycleCallbacks;
import android.app.Instrumentation;
import android.content.Context;
import android.content.Intent;
@@ -41,6 +40,8 @@ import android.os.Looper;
import android.window.TaskFragmentInfo;
import android.window.WindowContainerTransaction;
+import androidx.window.common.EmptyLifecycleCallbacksAdapter;
+
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
@@ -763,11 +764,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
return shouldRetainAssociatedContainer(finishingContainer, associatedContainer);
}
- private final class LifecycleCallbacks implements ActivityLifecycleCallbacks {
-
- @Override
- public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
- }
+ private final class LifecycleCallbacks extends EmptyLifecycleCallbacksAdapter {
@Override
public void onActivityPostCreated(Activity activity, Bundle savedInstanceState) {
@@ -779,30 +776,6 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
}
@Override
- public void onActivityStarted(Activity activity) {
- }
-
- @Override
- public void onActivityResumed(Activity activity) {
- }
-
- @Override
- public void onActivityPaused(Activity activity) {
- }
-
- @Override
- public void onActivityStopped(Activity activity) {
- }
-
- @Override
- public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
- }
-
- @Override
- public void onActivityDestroyed(Activity activity) {
- }
-
- @Override
public void onActivityConfigurationChanged(Activity activity) {
SplitController.this.onActivityConfigurationChanged(activity);
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java
index fe9ce971d4d9..a4fbdbc493f5 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java
@@ -18,28 +18,30 @@ package androidx.window.extensions.layout;
import static android.view.Display.DEFAULT_DISPLAY;
-import static androidx.window.common.DisplayFeature.COMMON_STATE_FLAT;
-import static androidx.window.common.DisplayFeature.COMMON_STATE_HALF_OPENED;
+import static androidx.window.common.CommonFoldingFeature.COMMON_STATE_FLAT;
+import static androidx.window.common.CommonFoldingFeature.COMMON_STATE_HALF_OPENED;
import static androidx.window.util.ExtensionHelper.rotateRectToDisplayRotation;
import static androidx.window.util.ExtensionHelper.transformToWindowSpaceRect;
import android.annotation.Nullable;
import android.app.Activity;
+import android.app.Application;
import android.content.Context;
import android.graphics.Rect;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.util.ArrayMap;
import android.util.Log;
import androidx.annotation.NonNull;
-import androidx.window.common.DeviceStateManagerPostureProducer;
-import androidx.window.common.DisplayFeature;
-import androidx.window.common.ResourceConfigDisplayFeatureProducer;
-import androidx.window.common.SettingsDevicePostureProducer;
+import androidx.window.common.CommonFoldingFeature;
+import androidx.window.common.DeviceStateManagerFoldingFeatureProducer;
+import androidx.window.common.EmptyLifecycleCallbacksAdapter;
import androidx.window.common.SettingsDisplayFeatureProducer;
import androidx.window.util.DataProducer;
import androidx.window.util.PriorityDataProducer;
import java.util.ArrayList;
-import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@@ -56,36 +58,27 @@ import java.util.function.Consumer;
*/
public class WindowLayoutComponentImpl implements WindowLayoutComponent {
private static final String TAG = "SampleExtension";
- private static WindowLayoutComponent sInstance;
private final Map<Activity, Consumer<WindowLayoutInfo>> mWindowLayoutChangeListeners =
- new HashMap<>();
-
- private final SettingsDevicePostureProducer mSettingsDevicePostureProducer;
- private final DataProducer<Integer> mDevicePostureProducer;
+ new ArrayMap<>();
private final SettingsDisplayFeatureProducer mSettingsDisplayFeatureProducer;
- private final DataProducer<List<DisplayFeature>> mDisplayFeatureProducer;
+ private final DataProducer<List<CommonFoldingFeature>> mFoldingFeatureProducer;
public WindowLayoutComponentImpl(Context context) {
- mSettingsDevicePostureProducer = new SettingsDevicePostureProducer(context);
- mDevicePostureProducer = new PriorityDataProducer<>(List.of(
- mSettingsDevicePostureProducer,
- new DeviceStateManagerPostureProducer(context)
- ));
-
+ ((Application) context.getApplicationContext())
+ .registerActivityLifecycleCallbacks(new NotifyOnConfigurationChanged());
mSettingsDisplayFeatureProducer = new SettingsDisplayFeatureProducer(context);
- mDisplayFeatureProducer = new PriorityDataProducer<>(List.of(
+ mFoldingFeatureProducer = new PriorityDataProducer<>(List.of(
mSettingsDisplayFeatureProducer,
- new ResourceConfigDisplayFeatureProducer(context)
+ new DeviceStateManagerFoldingFeatureProducer(context)
));
-
- mDevicePostureProducer.addDataChangedCallback(this::onDisplayFeaturesChanged);
- mDisplayFeatureProducer.addDataChangedCallback(this::onDisplayFeaturesChanged);
+ mFoldingFeatureProducer.addDataChangedCallback(this::onDisplayFeaturesChanged);
}
/**
* Adds a listener interested in receiving updates to {@link WindowLayoutInfo}
+ *
* @param activity hosting a {@link android.view.Window}
* @param consumer interested in receiving updates to {@link WindowLayoutInfo}
*/
@@ -97,6 +90,7 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
/**
* Removes a listener no longer interested in receiving updates.
+ *
* @param consumer no longer interested in receiving updates to {@link WindowLayoutInfo}
*/
public void removeWindowLayoutInfoListener(
@@ -118,43 +112,34 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
return mWindowLayoutChangeListeners.keySet();
}
- protected boolean hasListeners() {
- return !mWindowLayoutChangeListeners.isEmpty();
+ @NonNull
+ private boolean isListeningForLayoutChanges(IBinder token) {
+ for (Activity activity: getActivitiesListeningForLayoutChanges()) {
+ if (token.equals(activity.getWindow().getAttributes().token)) {
+ return true;
+ }
+ }
+ return false;
}
- /**
- * Calculate the {@link DisplayFeature.State} from the feature or the device posture producer.
- * If the given {@link DisplayFeature.State} is not valid then {@code null} will be returned.
- * The {@link FoldingFeature} should be ignored in the case of an invalid
- * {@link DisplayFeature.State}.
- *
- * @param feature a {@link DisplayFeature} to provide the feature state if present.
- * @return {@link DisplayFeature.State} of the hinge if present or the state from the posture
- * produce if present.
- */
- @Nullable
- private Integer getFeatureState(DisplayFeature feature) {
- Integer featureState = feature.getState();
- Optional<Integer> posture = mDevicePostureProducer.getData();
- Integer state = featureState == null ? posture.orElse(null) : featureState;
- return convertToExtensionState(state);
+ protected boolean hasListeners() {
+ return !mWindowLayoutChangeListeners.isEmpty();
}
/**
* A convenience method to translate from the common feature state to the extensions feature
- * state. More specifically, translates from {@link DisplayFeature.State} to
+ * state. More specifically, translates from {@link CommonFoldingFeature.State} to
* {@link FoldingFeature.STATE_FLAT} or {@link FoldingFeature.STATE_HALF_OPENED}. If it is not
* possible to translate, then we will return a {@code null} value.
*
- * @param state if it matches a value in {@link DisplayFeature.State}, {@code null} otherwise.
- * @return a {@link FoldingFeature.STATE_FLAT} or {@link FoldingFeature.STATE_HALF_OPENED} if
- * the given state matches a value in {@link DisplayFeature.State} and {@code null} otherwise.
+ * @param state if it matches a value in {@link CommonFoldingFeature.State}, {@code null}
+ * otherwise. @return a {@link FoldingFeature.STATE_FLAT} or
+ * {@link FoldingFeature.STATE_HALF_OPENED} if the given state matches a value in
+ * {@link CommonFoldingFeature.State} and {@code null} otherwise.
*/
@Nullable
- private Integer convertToExtensionState(@Nullable Integer state) {
- if (state == null) { // The null check avoids a NullPointerException.
- return null;
- } else if (state == COMMON_STATE_FLAT) {
+ private Integer convertToExtensionState(int state) {
+ if (state == COMMON_STATE_FLAT) {
return FoldingFeature.STATE_FLAT;
} else if (state == COMMON_STATE_HALF_OPENED) {
return FoldingFeature.STATE_HALF_OPENED;
@@ -172,33 +157,30 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
@NonNull
private WindowLayoutInfo getWindowLayoutInfo(@NonNull Activity activity) {
- List<androidx.window.extensions.layout.DisplayFeature> displayFeatures =
- getDisplayFeatures(activity);
+ List<DisplayFeature> displayFeatures = getDisplayFeatures(activity);
return new WindowLayoutInfo(displayFeatures);
}
/**
- * Translate from the {@link DisplayFeature} to
- * {@link androidx.window.extensions.layout.DisplayFeature} for a given {@link Activity}. If a
- * {@link DisplayFeature} is not valid then it will be omitted.
+ * Translate from the {@link CommonFoldingFeature} to
+ * {@link DisplayFeature} for a given {@link Activity}. If a
+ * {@link CommonFoldingFeature} is not valid then it will be omitted.
*
* For a {@link FoldingFeature} the bounds are localized into the {@link Activity} window
- * coordinate space and the state is calculated either from {@link DisplayFeature#getState()} or
- * {@link #mDisplayFeatureProducer}. The state from {@link #mDisplayFeatureProducer} may not be
- * valid since {@link #mDisplayFeatureProducer} is a general state controller. If the state is
- * not valid, the {@link FoldingFeature} is omitted from the {@link List} of
- * {@link androidx.window.extensions.layout.DisplayFeature}. If the bounds are not valid,
- * constructing a {@link FoldingFeature} will throw an {@link IllegalArgumentException} since
- * this can cause negative UI effects down stream.
+ * coordinate space and the state is calculated from {@link CommonFoldingFeature#getState()}.
+ * The state from {@link #mFoldingFeatureProducer} may not be valid since
+ * {@link #mFoldingFeatureProducer} is a general state controller. If the state is not valid,
+ * the {@link FoldingFeature} is omitted from the {@link List} of {@link DisplayFeature}. If the
+ * bounds are not valid, constructing a {@link FoldingFeature} will throw an
+ * {@link IllegalArgumentException} since this can cause negative UI effects down stream.
*
* @param activity a proxy for the {@link android.view.Window} that contains the
- * {@link androidx.window.extensions.layout.DisplayFeature}.
- * @return a {@link List} of valid {@link androidx.window.extensions.layout.DisplayFeature} that
+ * {@link DisplayFeature}.
+ * @return a {@link List} of valid {@link DisplayFeature} that
* are within the {@link android.view.Window} of the {@link Activity}
*/
- private List<androidx.window.extensions.layout.DisplayFeature> getDisplayFeatures(
- @NonNull Activity activity) {
- List<androidx.window.extensions.layout.DisplayFeature> features = new ArrayList<>();
+ private List<DisplayFeature> getDisplayFeatures(@NonNull Activity activity) {
+ List<DisplayFeature> features = new ArrayList<>();
int displayId = activity.getDisplay().getDisplayId();
if (displayId != DEFAULT_DISPLAY) {
Log.w(TAG, "This sample doesn't support display features on secondary displays");
@@ -211,11 +193,10 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
return features;
}
- Optional<List<DisplayFeature>> storedFeatures = mDisplayFeatureProducer.getData();
+ Optional<List<CommonFoldingFeature>> storedFeatures = mFoldingFeatureProducer.getData();
if (storedFeatures.isPresent()) {
-
- for (DisplayFeature baseFeature : storedFeatures.get()) {
- Integer state = getFeatureState(baseFeature);
+ for (CommonFoldingFeature baseFeature : storedFeatures.get()) {
+ Integer state = convertToExtensionState(baseFeature.getState());
if (state == null) {
continue;
}
@@ -223,8 +204,7 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
rotateRectToDisplayRotation(displayId, featureRect);
transformToWindowSpaceRect(activity, featureRect);
- features.add(new FoldingFeature(featureRect, baseFeature.getType(),
- getFeatureState(baseFeature)));
+ features.add(new FoldingFeature(featureRect, baseFeature.getType(), state));
}
}
return features;
@@ -232,13 +212,31 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
private void updateRegistrations() {
if (hasListeners()) {
- mSettingsDevicePostureProducer.registerObserversIfNeeded();
mSettingsDisplayFeatureProducer.registerObserversIfNeeded();
} else {
- mSettingsDevicePostureProducer.unregisterObserversIfNeeded();
mSettingsDisplayFeatureProducer.unregisterObserversIfNeeded();
}
-
onDisplayFeaturesChanged();
}
+
+ private final class NotifyOnConfigurationChanged extends EmptyLifecycleCallbacksAdapter {
+ @Override
+ public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
+ super.onActivityCreated(activity, savedInstanceState);
+ onDisplayFeaturesChangedIfListening(activity);
+ }
+
+ @Override
+ public void onActivityConfigurationChanged(Activity activity) {
+ super.onActivityConfigurationChanged(activity);
+ onDisplayFeaturesChangedIfListening(activity);
+ }
+
+ private void onDisplayFeaturesChangedIfListening(Activity activity) {
+ IBinder token = activity.getWindow().getAttributes().token;
+ if (token == null || isListeningForLayoutChanges(token)) {
+ onDisplayFeaturesChanged();
+ }
+ }
+ }
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java
index aa949f126154..c7b709347060 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java
@@ -23,16 +23,17 @@ import static androidx.window.util.ExtensionHelper.transformToWindowSpaceRect;
import android.app.Activity;
import android.app.ActivityThread;
+import android.app.Application;
import android.content.Context;
import android.graphics.Rect;
+import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;
import androidx.annotation.NonNull;
-import androidx.window.common.DeviceStateManagerPostureProducer;
-import androidx.window.common.DisplayFeature;
-import androidx.window.common.ResourceConfigDisplayFeatureProducer;
-import androidx.window.common.SettingsDevicePostureProducer;
+import androidx.window.common.CommonFoldingFeature;
+import androidx.window.common.DeviceStateManagerFoldingFeatureProducer;
+import androidx.window.common.EmptyLifecycleCallbacksAdapter;
import androidx.window.common.SettingsDisplayFeatureProducer;
import androidx.window.util.DataProducer;
import androidx.window.util.PriorityDataProducer;
@@ -48,36 +49,25 @@ import java.util.Optional;
*/
class SampleSidecarImpl extends StubSidecar {
private static final String TAG = "SampleSidecar";
- private static final boolean DEBUG = false;
- private final SettingsDevicePostureProducer mSettingsDevicePostureProducer;
- private final DataProducer<Integer> mDevicePostureProducer;
+ private final DataProducer<List<CommonFoldingFeature>> mFoldingFeatureProducer;
- private final SettingsDisplayFeatureProducer mSettingsDisplayFeatureProducer;
- private final DataProducer<List<DisplayFeature>> mDisplayFeatureProducer;
+ private final SettingsDisplayFeatureProducer mSettingsFoldingFeatureProducer;
SampleSidecarImpl(Context context) {
- mSettingsDevicePostureProducer = new SettingsDevicePostureProducer(context);
- mDevicePostureProducer = new PriorityDataProducer<>(List.of(
- mSettingsDevicePostureProducer,
- new DeviceStateManagerPostureProducer(context)
+ ((Application) context.getApplicationContext())
+ .registerActivityLifecycleCallbacks(new NotifyOnConfigurationChanged());
+ mSettingsFoldingFeatureProducer = new SettingsDisplayFeatureProducer(context);
+ mFoldingFeatureProducer = new PriorityDataProducer<>(List.of(
+ mSettingsFoldingFeatureProducer,
+ new DeviceStateManagerFoldingFeatureProducer(context)
));
- mSettingsDisplayFeatureProducer = new SettingsDisplayFeatureProducer(context);
- mDisplayFeatureProducer = new PriorityDataProducer<>(List.of(
- mSettingsDisplayFeatureProducer,
- new ResourceConfigDisplayFeatureProducer(context)
- ));
-
- mDevicePostureProducer.addDataChangedCallback(this::onDevicePostureChanged);
- mDisplayFeatureProducer.addDataChangedCallback(this::onDisplayFeaturesChanged);
- }
-
- private void onDevicePostureChanged() {
- updateDeviceState(getDeviceState());
+ mFoldingFeatureProducer.addDataChangedCallback(this::onDisplayFeaturesChanged);
}
private void onDisplayFeaturesChanged() {
+ updateDeviceState(getDeviceState());
for (IBinder windowToken : getWindowsListeningForLayoutChanges()) {
SidecarWindowLayoutInfo newLayout = getWindowLayoutInfo(windowToken);
updateWindowLayout(windowToken, newLayout);
@@ -87,27 +77,21 @@ class SampleSidecarImpl extends StubSidecar {
@NonNull
@Override
public SidecarDeviceState getDeviceState() {
- Optional<Integer> posture = mDevicePostureProducer.getData();
-
SidecarDeviceState deviceState = new SidecarDeviceState();
- deviceState.posture = posture.orElse(deviceStateFromFeature());
+ deviceState.posture = deviceStateFromFeature();
return deviceState;
}
private int deviceStateFromFeature() {
- List<DisplayFeature> storedFeatures = mDisplayFeatureProducer.getData()
+ List<CommonFoldingFeature> storedFeatures = mFoldingFeatureProducer.getData()
.orElse(Collections.emptyList());
for (int i = 0; i < storedFeatures.size(); i++) {
- DisplayFeature feature = storedFeatures.get(i);
- final int state = feature.getState() == null ? -1 : feature.getState();
- if (DEBUG && feature.getState() == null) {
- Log.d(TAG, "feature#getState was null for DisplayFeature: " + feature);
- }
-
+ CommonFoldingFeature feature = storedFeatures.get(i);
+ final int state = feature.getState();
switch (state) {
- case DisplayFeature.COMMON_STATE_FLAT:
+ case CommonFoldingFeature.COMMON_STATE_FLAT:
return SidecarDeviceState.POSTURE_OPENED;
- case DisplayFeature.COMMON_STATE_HALF_OPENED:
+ case CommonFoldingFeature.COMMON_STATE_HALF_OPENED:
return SidecarDeviceState.POSTURE_HALF_OPENED;
}
}
@@ -127,22 +111,22 @@ class SampleSidecarImpl extends StubSidecar {
}
private List<SidecarDisplayFeature> getDisplayFeatures(@NonNull Activity activity) {
- List<SidecarDisplayFeature> features = new ArrayList<SidecarDisplayFeature>();
int displayId = activity.getDisplay().getDisplayId();
if (displayId != DEFAULT_DISPLAY) {
Log.w(TAG, "This sample doesn't support display features on secondary displays");
- return features;
+ return Collections.emptyList();
}
if (activity.isInMultiWindowMode()) {
// It is recommended not to report any display features in multi-window mode, since it
// won't be possible to synchronize the display feature positions with window movement.
- return features;
+ return Collections.emptyList();
}
- Optional<List<DisplayFeature>> storedFeatures = mDisplayFeatureProducer.getData();
+ Optional<List<CommonFoldingFeature>> storedFeatures = mFoldingFeatureProducer.getData();
+ List<SidecarDisplayFeature> features = new ArrayList<>();
if (storedFeatures.isPresent()) {
- for (DisplayFeature baseFeature : storedFeatures.get()) {
+ for (CommonFoldingFeature baseFeature : storedFeatures.get()) {
SidecarDisplayFeature feature = new SidecarDisplayFeature();
Rect featureRect = baseFeature.getRect();
rotateRectToDisplayRotation(displayId, featureRect);
@@ -152,17 +136,37 @@ class SampleSidecarImpl extends StubSidecar {
features.add(feature);
}
}
- return features;
+ return Collections.unmodifiableList(features);
}
@Override
protected void onListenersChanged() {
if (hasListeners()) {
- mSettingsDevicePostureProducer.registerObserversIfNeeded();
- mSettingsDisplayFeatureProducer.registerObserversIfNeeded();
+ mSettingsFoldingFeatureProducer.registerObserversIfNeeded();
+ onDisplayFeaturesChanged();
} else {
- mSettingsDevicePostureProducer.unregisterObserversIfNeeded();
- mSettingsDisplayFeatureProducer.unregisterObserversIfNeeded();
+ mSettingsFoldingFeatureProducer.unregisterObserversIfNeeded();
+ }
+ }
+
+ private final class NotifyOnConfigurationChanged extends EmptyLifecycleCallbacksAdapter {
+ @Override
+ public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
+ super.onActivityCreated(activity, savedInstanceState);
+ onDisplayFeaturesChangedForActivity(activity);
+ }
+
+ @Override
+ public void onActivityConfigurationChanged(Activity activity) {
+ super.onActivityConfigurationChanged(activity);
+ onDisplayFeaturesChangedForActivity(activity);
+ }
+
+ private void onDisplayFeaturesChangedForActivity(@NonNull Activity activity) {
+ IBinder token = activity.getWindow().getAttributes().token;
+ if (token == null || mWindowLayoutChangeListenerTokens.contains(token)) {
+ onDisplayFeaturesChanged();
+ }
}
}
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/StubSidecar.java b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/StubSidecar.java
index 199c37315c07..b9c808a6569b 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/StubSidecar.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/StubSidecar.java
@@ -30,7 +30,7 @@ import java.util.Set;
abstract class StubSidecar implements SidecarInterface {
private SidecarCallback mSidecarCallback;
- private final Set<IBinder> mWindowLayoutChangeListenerTokens = new HashSet<>();
+ final Set<IBinder> mWindowLayoutChangeListenerTokens = new HashSet<>();
private boolean mDeviceStateChangeListenerRegistered;
StubSidecar() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BadgedImageView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BadgedImageView.java
index 79e624212f4b..3876533a922e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BadgedImageView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BadgedImageView.java
@@ -173,8 +173,8 @@ public class BadgedImageView extends ConstraintLayout {
}
@Override
- public void onDraw(Canvas canvas) {
- super.onDraw(canvas);
+ public void dispatchDraw(Canvas canvas) {
+ super.dispatchDraw(canvas);
if (!shouldDrawDot()) {
return;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUI.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUI.java
index 99dbfe01964c..b87cf47dd93f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUI.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUI.java
@@ -24,9 +24,12 @@ import com.android.wm.shell.common.annotations.ExternalThread;
@ExternalThread
public interface CompatUI {
/**
- * Called when the keyguard occluded state changes. Removes all compat UIs if the
- * keyguard is now occluded.
- * @param occluded indicates if the keyguard is now occluded.
+ * Called when the keyguard showing state changes. Removes all compat UIs if the
+ * keyguard is now showing.
+ *
+ * <p>Note that if the keyguard is occluded it will also be considered showing.
+ *
+ * @param showing indicates if the keyguard is now showing.
*/
- void onKeyguardOccludedChanged(boolean occluded);
+ void onKeyguardShowingChanged(boolean showing);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
index ee4d5ed018bf..b2bbafeb7bf5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
@@ -109,9 +109,9 @@ public class CompatUIController implements OnDisplaysChangedListener,
// Only show each hint once automatically in the process life.
private final CompatUIHintsState mCompatUIHintsState;
- // Indicates if the keyguard is currently occluded, in which case compat UIs shouldn't
+ // Indicates if the keyguard is currently showing, in which case compat UIs shouldn't
// be shown.
- private boolean mKeyguardOccluded;
+ private boolean mKeyguardShowing;
public CompatUIController(Context context,
DisplayController displayController,
@@ -218,14 +218,14 @@ public class CompatUIController implements OnDisplaysChangedListener,
}
@VisibleForTesting
- void onKeyguardOccludedChanged(boolean occluded) {
- mKeyguardOccluded = occluded;
- // Hide the compat UIs when keyguard is occluded.
+ void onKeyguardShowingChanged(boolean showing) {
+ mKeyguardShowing = showing;
+ // Hide the compat UIs when keyguard is showing.
forAllLayouts(layout -> layout.updateVisibility(showOnDisplay(layout.getDisplayId())));
}
private boolean showOnDisplay(int displayId) {
- return !mKeyguardOccluded && !isImeShowingOnDisplay(displayId);
+ return !mKeyguardShowing && !isImeShowingOnDisplay(displayId);
}
private boolean isImeShowingOnDisplay(int displayId) {
@@ -372,9 +372,9 @@ public class CompatUIController implements OnDisplaysChangedListener,
@ExternalThread
private class CompatUIImpl implements CompatUI {
@Override
- public void onKeyguardOccludedChanged(boolean occluded) {
+ public void onKeyguardShowingChanged(boolean showing) {
mMainExecutor.execute(() -> {
- CompatUIController.this.onKeyguardOccludedChanged(occluded);
+ CompatUIController.this.onKeyguardShowingChanged(showing);
});
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduDialogLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduDialogLayout.java
index 2da6a6bc70c0..8aa4d0ee99ab 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduDialogLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduDialogLayout.java
@@ -21,6 +21,7 @@ import android.content.Context;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.View;
+import android.widget.TextView;
import androidx.constraintlayout.widget.ConstraintLayout;
@@ -38,6 +39,7 @@ class LetterboxEduDialogLayout extends ConstraintLayout {
// 204 is simply 255 * 0.8.
static final int BACKGROUND_DIM_ALPHA = 204;
private View mDialogContainer;
+ private TextView mDialogTitle;
private Drawable mBackgroundDim;
public LetterboxEduDialogLayout(Context context) {
@@ -61,6 +63,10 @@ class LetterboxEduDialogLayout extends ConstraintLayout {
return mDialogContainer;
}
+ TextView getDialogTitle() {
+ return mDialogTitle;
+ }
+
Drawable getBackgroundDim() {
return mBackgroundDim;
}
@@ -84,6 +90,7 @@ class LetterboxEduDialogLayout extends ConstraintLayout {
protected void onFinishInflate() {
super.onFinishInflate();
mDialogContainer = findViewById(R.id.letterbox_education_dialog_container);
+ mDialogTitle = findViewById(R.id.letterbox_education_dialog_title);
mBackgroundDim = getBackground().mutate();
// Set the alpha of the background dim to 0 for enter animation.
mBackgroundDim.setAlpha(0);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduWindowManager.java
index 30b9f0838e10..dda72ffb432f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduWindowManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduWindowManager.java
@@ -28,6 +28,7 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup.MarginLayoutParams;
import android.view.WindowManager;
+import android.view.accessibility.AccessibilityEvent;
import com.android.internal.annotations.VisibleForTesting;
import com.android.wm.shell.R;
@@ -132,7 +133,7 @@ public class LetterboxEduWindowManager extends CompatUIWindowManagerAbstract {
updateDialogMargins();
mAnimationController.startEnterAnimation(mLayout, /* endCallback= */
- this::setDismissOnClickListener);
+ this::onDialogEnterAnimationEnded);
return mLayout;
}
@@ -157,11 +158,13 @@ public class LetterboxEduWindowManager extends CompatUIWindowManagerAbstract {
R.layout.letterbox_education_dialog_layout, null);
}
- private void setDismissOnClickListener() {
+ private void onDialogEnterAnimationEnded() {
if (mLayout == null) {
return;
}
mLayout.setDismissOnClickListener(this::onDismiss);
+ // Focus on the dialog title for accessibility.
+ mLayout.getDialogTitle().sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
}
private void onDismiss() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java
index 491dff08187f..1e934c5c6e22 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java
@@ -27,7 +27,6 @@ import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.SystemWindows;
import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.common.annotations.ShellMainThread;
-import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController;
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.pip.PipAnimationController;
import com.android.wm.shell.pip.PipMediaController;
@@ -163,15 +162,14 @@ public abstract class TvPipModule {
PipAnimationController pipAnimationController,
PipTransitionController pipTransitionController,
PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
- Optional<LegacySplitScreenController> splitScreenOptional,
- Optional<SplitScreenController> newSplitScreenOptional,
+ Optional<SplitScreenController> splitScreenControllerOptional,
DisplayController displayController,
PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer,
@ShellMainThread ShellExecutor mainExecutor) {
return new PipTaskOrganizer(context,
syncTransactionQueue, pipTransitionState, tvPipBoundsState, tvPipBoundsAlgorithm,
tvPipMenuController, pipAnimationController, pipSurfaceTransactionHelper,
- pipTransitionController, splitScreenOptional, newSplitScreenOptional,
+ pipTransitionController, splitScreenControllerOptional,
displayController, pipUiEventLogger, shellTaskOrganizer, mainExecutor);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index 7879e7a5bb00..73f393140cbc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -285,15 +285,14 @@ public class WMShellModule {
PipAnimationController pipAnimationController,
PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
PipTransitionController pipTransitionController,
- Optional<LegacySplitScreenController> splitScreenOptional,
- Optional<SplitScreenController> newSplitScreenOptional,
+ Optional<SplitScreenController> splitScreenControllerOptional,
DisplayController displayController,
PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer,
@ShellMainThread ShellExecutor mainExecutor) {
return new PipTaskOrganizer(context,
syncTransactionQueue, pipTransitionState, pipBoundsState, pipBoundsAlgorithm,
menuPhoneController, pipAnimationController, pipSurfaceTransactionHelper,
- pipTransitionController, splitScreenOptional, newSplitScreenOptional,
+ pipTransitionController, splitScreenControllerOptional,
displayController, pipUiEventLogger, shellTaskOrganizer, mainExecutor);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index bfa14f3a705d..b266189ec262 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -81,7 +81,6 @@ import com.android.wm.shell.common.ScreenshotUtils;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.annotations.ShellMainThread;
-import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController;
import com.android.wm.shell.pip.phone.PipMotionHelper;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.splitscreen.SplitScreenController;
@@ -134,7 +133,6 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
private final int mExitAnimationDuration;
private final int mCrossFadeAnimationDuration;
private final PipSurfaceTransactionHelper mSurfaceTransactionHelper;
- private final Optional<LegacySplitScreenController> mLegacySplitScreenOptional;
private final Optional<SplitScreenController> mSplitScreenOptional;
protected final ShellTaskOrganizer mTaskOrganizer;
protected final ShellExecutor mMainExecutor;
@@ -262,7 +260,6 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
@NonNull PipAnimationController pipAnimationController,
@NonNull PipSurfaceTransactionHelper surfaceTransactionHelper,
@NonNull PipTransitionController pipTransitionController,
- Optional<LegacySplitScreenController> legacySplitScreenOptional,
Optional<SplitScreenController> splitScreenOptional,
@NonNull DisplayController displayController,
@NonNull PipUiEventLogger pipUiEventLogger,
@@ -285,7 +282,6 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
mPipAnimationController = pipAnimationController;
mPipUiEventLoggerLogger = pipUiEventLogger;
mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new;
- mLegacySplitScreenOptional = legacySplitScreenOptional;
mSplitScreenOptional = splitScreenOptional;
mTaskOrganizer = shellTaskOrganizer;
mMainExecutor = mainExecutor;
@@ -505,11 +501,6 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
wct.setWindowingMode(mToken, getOutPipWindowingMode());
// Simply reset the activity mode set prior to the animation running.
wct.setActivityWindowingMode(mToken, WINDOWING_MODE_UNDEFINED);
- mLegacySplitScreenOptional.ifPresent(splitScreen -> {
- if (direction == TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN) {
- wct.reparent(mToken, splitScreen.getSecondaryRoot(), true /* onTop */);
- }
- });
}
/**
@@ -1512,36 +1503,21 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
}
/**
- * Sync with {@link LegacySplitScreenController} or {@link SplitScreenController} on destination
- * bounds if PiP is going to split screen.
+ * Sync with {@link SplitScreenController} on destination bounds if PiP is going to
+ * split screen.
*
* @param destinationBoundsOut contain the updated destination bounds if applicable
* @return {@code true} if destinationBounds is altered for split screen
*/
private boolean syncWithSplitScreenBounds(Rect destinationBoundsOut, boolean enterSplit) {
- if (enterSplit && mSplitScreenOptional.isPresent()) {
- final Rect topLeft = new Rect();
- final Rect bottomRight = new Rect();
- mSplitScreenOptional.get().getStageBounds(topLeft, bottomRight);
- final boolean isPipTopLeft = isPipTopLeft();
- destinationBoundsOut.set(isPipTopLeft ? topLeft : bottomRight);
- return true;
- }
-
- if (!mLegacySplitScreenOptional.isPresent()) {
- return false;
- }
-
- LegacySplitScreenController legacySplitScreen = mLegacySplitScreenOptional.get();
- if (!legacySplitScreen.isDividerVisible()) {
- // fail early if system is not in split screen mode
+ if (!enterSplit || !mSplitScreenOptional.isPresent()) {
return false;
}
-
- // PiP window will go to split-secondary mode instead of fullscreen, populates the
- // split screen bounds here.
- destinationBoundsOut.set(legacySplitScreen.getDividerView()
- .getNonMinimizedSplitScreenSecondaryBounds());
+ final Rect topLeft = new Rect();
+ final Rect bottomRight = new Rect();
+ mSplitScreenOptional.get().getStageBounds(topLeft, bottomRight);
+ final boolean isPipTopLeft = isPipTopLeft();
+ destinationBoundsOut.set(isPipTopLeft ? topLeft : bottomRight);
return true;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index efb52a5b4644..610d2cc39445 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -363,7 +363,9 @@ public class Transitions implements RemoteCallable<Transitions> {
return;
}
- // apply transfer starting window directly if there is no other task change.
+ // apply transfer starting window directly if there is no other task change. Since this
+ // is an activity->activity situation, we can detect it by selecting transitions with only
+ // 2 changes where neither are tasks and one is a starting-window recipient.
final int changeSize = info.getChanges().size();
if (changeSize == 2) {
boolean nonTaskChange = true;
@@ -380,7 +382,9 @@ public class Transitions implements RemoteCallable<Transitions> {
}
if (nonTaskChange && transferStartingWindow) {
t.apply();
- onFinish(transitionToken, null /* wct */, null /* wctCB */);
+ // Treat this as an abort since we are bypassing any merge logic and effectively
+ // finishing immediately.
+ onAbort(transitionToken);
return;
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
index 29e40be457d1..a31b28737552 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
@@ -325,17 +325,17 @@ public class CompatUIControllerTest extends ShellTestCase {
}
@Test
- public void testChangeLayoutsVisibilityOnKeyguardOccludedChanged() {
+ public void testChangeLayoutsVisibilityOnKeyguardShowingChanged() {
mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID,
/* hasSizeCompat= */ true, CAMERA_COMPAT_CONTROL_HIDDEN), mMockTaskListener);
- // Verify that the restart button is hidden after keyguard becomes occluded.
- mController.onKeyguardOccludedChanged(true);
+ // Verify that the restart button is hidden after keyguard becomes showing.
+ mController.onKeyguardShowingChanged(true);
verify(mMockCompatLayout).updateVisibility(false);
verify(mMockLetterboxEduLayout).updateVisibility(false);
- // Verify button remains hidden while keyguard is occluded.
+ // Verify button remains hidden while keyguard is showing.
TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, /* hasSizeCompat= */ true,
CAMERA_COMPAT_CONTROL_HIDDEN);
mController.onCompatInfoChanged(taskInfo, mMockTaskListener);
@@ -345,20 +345,20 @@ public class CompatUIControllerTest extends ShellTestCase {
verify(mMockLetterboxEduLayout).updateCompatInfo(taskInfo, mMockTaskListener, /* canShow= */
false);
- // Verify button is shown after keyguard becomes not occluded.
- mController.onKeyguardOccludedChanged(false);
+ // Verify button is shown after keyguard becomes not showing.
+ mController.onKeyguardShowingChanged(false);
verify(mMockCompatLayout).updateVisibility(true);
verify(mMockLetterboxEduLayout).updateVisibility(true);
}
@Test
- public void testLayoutsRemainHiddenOnKeyguardOccludedFalseWhenImeIsShowing() {
+ public void testLayoutsRemainHiddenOnKeyguardShowingFalseWhenImeIsShowing() {
mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID,
/* hasSizeCompat= */ true, CAMERA_COMPAT_CONTROL_HIDDEN), mMockTaskListener);
mController.onImeVisibilityChanged(DISPLAY_ID, /* isShowing= */ true);
- mController.onKeyguardOccludedChanged(true);
+ mController.onKeyguardShowingChanged(true);
verify(mMockCompatLayout, times(2)).updateVisibility(false);
verify(mMockLetterboxEduLayout, times(2)).updateVisibility(false);
@@ -366,8 +366,8 @@ public class CompatUIControllerTest extends ShellTestCase {
clearInvocations(mMockCompatLayout);
clearInvocations(mMockLetterboxEduLayout);
- // Verify button remains hidden after keyguard becomes not occluded since IME is showing.
- mController.onKeyguardOccludedChanged(false);
+ // Verify button remains hidden after keyguard becomes not showing since IME is showing.
+ mController.onKeyguardShowingChanged(false);
verify(mMockCompatLayout).updateVisibility(false);
verify(mMockLetterboxEduLayout).updateVisibility(false);
@@ -380,12 +380,12 @@ public class CompatUIControllerTest extends ShellTestCase {
}
@Test
- public void testLayoutsRemainHiddenOnImeHideWhenKeyguardIsOccluded() {
+ public void testLayoutsRemainHiddenOnImeHideWhenKeyguardIsShowing() {
mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID,
/* hasSizeCompat= */ true, CAMERA_COMPAT_CONTROL_HIDDEN), mMockTaskListener);
mController.onImeVisibilityChanged(DISPLAY_ID, /* isShowing= */ true);
- mController.onKeyguardOccludedChanged(true);
+ mController.onKeyguardShowingChanged(true);
verify(mMockCompatLayout, times(2)).updateVisibility(false);
verify(mMockLetterboxEduLayout, times(2)).updateVisibility(false);
@@ -393,14 +393,14 @@ public class CompatUIControllerTest extends ShellTestCase {
clearInvocations(mMockCompatLayout);
clearInvocations(mMockLetterboxEduLayout);
- // Verify button remains hidden after IME is hidden since keyguard is occluded.
+ // Verify button remains hidden after IME is hidden since keyguard is showing.
mController.onImeVisibilityChanged(DISPLAY_ID, /* isShowing= */ false);
verify(mMockCompatLayout).updateVisibility(false);
verify(mMockLetterboxEduLayout).updateVisibility(false);
- // Verify button is shown after keyguard becomes not occluded.
- mController.onKeyguardOccludedChanged(false);
+ // Verify button is shown after keyguard becomes not showing.
+ mController.onKeyguardShowingChanged(false);
verify(mMockCompatLayout).updateVisibility(true);
verify(mMockLetterboxEduLayout).updateVisibility(true);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduDialogLayoutTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduDialogLayoutTest.java
index 00e4938d866c..1dee88c43806 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduDialogLayoutTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduDialogLayoutTest.java
@@ -70,6 +70,8 @@ public class LetterboxEduDialogLayoutTest extends ShellTestCase {
public void testOnFinishInflate() {
assertEquals(mLayout.getDialogContainer(),
mLayout.findViewById(R.id.letterbox_education_dialog_container));
+ assertEquals(mLayout.getDialogTitle(),
+ mLayout.findViewById(R.id.letterbox_education_dialog_title));
assertEquals(mLayout.getBackgroundDim(), mLayout.getBackground());
assertEquals(mLayout.getBackground().getAlpha(), 0);
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduWindowManagerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduWindowManagerTest.java
index 0509dd38abe0..337b7385faec 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduWindowManagerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduWindowManagerTest.java
@@ -41,9 +41,11 @@ import android.testing.AndroidTestingRunner;
import android.view.DisplayCutout;
import android.view.DisplayInfo;
import android.view.SurfaceControlViewHost;
+import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.MarginLayoutParams;
import android.view.WindowManager;
+import android.view.accessibility.AccessibilityEvent;
import androidx.test.filters.SmallTest;
@@ -173,13 +175,19 @@ public class LetterboxEduWindowManagerTest extends ShellTestCase {
verifyLayout(layout, mWindowAttrsCaptor.getValue(), /* expectedWidth= */ TASK_WIDTH,
/* expectedHeight= */ TASK_HEIGHT, /* expectedExtraTopMargin= */ DISPLAY_CUTOUT_TOP,
/* expectedExtraBottomMargin= */ DISPLAY_CUTOUT_BOTTOM);
+ View dialogTitle = layout.getDialogTitle();
+ assertNotNull(dialogTitle);
+ spyOn(dialogTitle);
// Clicking the layout does nothing until enter animation is done.
layout.performClick();
verify(mAnimationController, never()).startExitAnimation(any(), any());
+ // The dialog title shouldn't be focused for Accessibility until enter animation is done.
+ verify(dialogTitle, never()).sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
verifyAndFinishEnterAnimation(layout);
+ verify(dialogTitle).sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
// Exit animation should start following a click on the layout.
layout.performClick();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
index 0172cf324eea..14d9fb9babc4 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
@@ -48,7 +48,6 @@ import com.android.wm.shell.TestShellExecutor;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.SyncTransactionQueue;
-import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController;
import com.android.wm.shell.pip.phone.PhonePipMenuController;
import com.android.wm.shell.splitscreen.SplitScreenController;
@@ -76,7 +75,6 @@ public class PipTaskOrganizerTest extends ShellTestCase {
@Mock private PipTransitionController mMockPipTransitionController;
@Mock private PipSurfaceTransactionHelper mMockPipSurfaceTransactionHelper;
@Mock private PipUiEventLogger mMockPipUiEventLogger;
- @Mock private Optional<LegacySplitScreenController> mMockOptionalLegacySplitScreen;
@Mock private Optional<SplitScreenController> mMockOptionalSplitScreen;
@Mock private ShellTaskOrganizer mMockShellTaskOrganizer;
private TestShellExecutor mMainExecutor;
@@ -101,8 +99,8 @@ public class PipTaskOrganizerTest extends ShellTestCase {
mMockSyncTransactionQueue, mPipTransitionState, mPipBoundsState,
mPipBoundsAlgorithm, mMockPhonePipMenuController,
mMockPipAnimationController, mMockPipSurfaceTransactionHelper,
- mMockPipTransitionController, mMockOptionalLegacySplitScreen,
- mMockOptionalSplitScreen, mMockDisplayController, mMockPipUiEventLogger,
+ mMockPipTransitionController, mMockOptionalSplitScreen,
+ mMockDisplayController, mMockPipUiEventLogger,
mMockShellTaskOrganizer, mMainExecutor));
mMainExecutor.flushAll();
preparePipTaskOrg();
diff --git a/packages/CompanionDeviceManager/res/drawable/ic_apps.xml b/packages/CompanionDeviceManager/res/drawable/ic_apps.xml
new file mode 100644
index 000000000000..f839b80d2e0c
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/drawable/ic_apps.xml
@@ -0,0 +1,34 @@
+<!--
+ ~ Copyright (C) 2022 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.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="108dp"
+ android:height="108dp"
+ android:viewportWidth="108"
+ android:viewportHeight="108">
+ <group android:scaleX="5.142857"
+ android:scaleY="5.142857"
+ android:translateY="2.5714285">
+ <path
+ android:pathData="M13.1235,1.01L5.1235,1C4.0235,1 3.1235,1.9 3.1235,3V17C3.1235,18.1 4.0235,19 5.1235,19H13.1235C14.2235,19 15.1235,18.1 15.1235,17V12.9765H13.1235V14H5.1235V6H13.1235V6.0034H15.1235V3C15.1235,1.9 14.2235,1.01 13.1235,1.01ZM13.1235,17H5.1235V16H13.1235V17ZM5.1235,4V3H13.1235V4H5.1235Z"
+ android:fillColor="#202124"
+ android:fillType="evenOdd"/>
+ <path
+ android:pathData="M10.6984,6.4016V13.3136L12.3596,12.3628H17.8731V6.4016H10.6984ZM16.3731,7.9016H12.1984V10.8628H16.3731V7.9016Z"
+ android:fillColor="#202124"
+ android:fillType="evenOdd"/>
+ </group>
+</vector> \ No newline at end of file
diff --git a/packages/CompanionDeviceManager/res/drawable/ic_notifications.xml b/packages/CompanionDeviceManager/res/drawable/ic_notifications.xml
new file mode 100644
index 000000000000..4ac4d04b184e
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/drawable/ic_notifications.xml
@@ -0,0 +1,25 @@
+<!--
+ ~ Copyright (C) 2022 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.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="?attr/colorControlNormal">
+ <path android:fillColor="@android:color/white"
+ android:pathData="M4,19V17H6V10Q6,7.925 7.25,6.312Q8.5,4.7 10.5,4.2V3.5Q10.5,2.875 10.938,2.438Q11.375,2 12,2Q12.625,2 13.062,2.438Q13.5,2.875 13.5,3.5V4.2Q15.5,4.7 16.75,6.312Q18,7.925 18,10V17H20V19ZM12,11.5Q12,11.5 12,11.5Q12,11.5 12,11.5Q12,11.5 12,11.5Q12,11.5 12,11.5ZM12,22Q11.175,22 10.588,21.413Q10,20.825 10,20H14Q14,20.825 13.413,21.413Q12.825,22 12,22ZM8,17H16V10Q16,8.35 14.825,7.175Q13.65,6 12,6Q10.35,6 9.175,7.175Q8,8.35 8,10Z"/>
+</vector> \ No newline at end of file
diff --git a/packages/CompanionDeviceManager/res/drawable/ic_storage.xml b/packages/CompanionDeviceManager/res/drawable/ic_storage.xml
new file mode 100644
index 000000000000..d8b7f59185c8
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/drawable/ic_storage.xml
@@ -0,0 +1,25 @@
+<!--
+ ~ Copyright (C) 2022 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.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="?attr/colorControlNormal">
+ <path android:fillColor="@android:color/white"
+ android:pathData="M6,17H18L14.25,12L11.25,16L9,13ZM5,21Q4.175,21 3.587,20.413Q3,19.825 3,19V5Q3,4.175 3.587,3.587Q4.175,3 5,3H19Q19.825,3 20.413,3.587Q21,4.175 21,5V19Q21,19.825 20.413,20.413Q19.825,21 19,21ZM5,19H19Q19,19 19,19Q19,19 19,19V5Q19,5 19,5Q19,5 19,5H5Q5,5 5,5Q5,5 5,5V19Q5,19 5,19Q5,19 5,19ZM5,5Q5,5 5,5Q5,5 5,5V19Q5,19 5,19Q5,19 5,19Q5,19 5,19Q5,19 5,19V5Q5,5 5,5Q5,5 5,5Z"/>
+</vector> \ No newline at end of file
diff --git a/packages/CompanionDeviceManager/res/layout/activity_confirmation.xml b/packages/CompanionDeviceManager/res/layout/activity_confirmation.xml
index f30dadffa788..82fe72bee4e4 100644
--- a/packages/CompanionDeviceManager/res/layout/activity_confirmation.xml
+++ b/packages/CompanionDeviceManager/res/layout/activity_confirmation.xml
@@ -51,6 +51,11 @@
android:scrollbars="vertical"
android:layout_height="200dp" />
+ <androidx.recyclerview.widget.RecyclerView
+ android:id="@+id/permission_list"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
</RelativeLayout>
<LinearLayout
diff --git a/packages/CompanionDeviceManager/res/layout/list_item_permission.xml b/packages/CompanionDeviceManager/res/layout/list_item_permission.xml
new file mode 100644
index 000000000000..b8a0f7938f0b
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/layout/list_item_permission.xml
@@ -0,0 +1,55 @@
+<!--
+ ~ Copyright (C) 2022 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.
+ -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/list_item_permission"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:padding="5dp">
+
+ <ImageView
+ android:id="@+id/permission_icon"
+ android:layout_width="24dp"
+ android:layout_height="24dp"
+ android:layout_marginTop="7dp"
+ android:layout_marginEnd="12dp"
+ android:contentDescription="Permission Icon"/>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:gravity="center_vertical"
+ android:padding="6dp">
+
+ <TextView
+ android:id="@+id/permission_name"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textSize="16sp"
+ android:textAppearance="?android:attr/textAppearanceListItemSmall"/>
+
+ <TextView
+ android:id="@+id/permission_summary"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textSize="14sp"
+ android:textColor="?android:attr/textColorSecondary"/>
+
+ </LinearLayout>
+
+</LinearLayout> \ No newline at end of file
diff --git a/packages/CompanionDeviceManager/res/values/strings.xml b/packages/CompanionDeviceManager/res/values/strings.xml
index 9626751679e1..c4ad432b092e 100644
--- a/packages/CompanionDeviceManager/res/values/strings.xml
+++ b/packages/CompanionDeviceManager/res/values/strings.xml
@@ -38,6 +38,12 @@
<!-- ================= DEVICE_PROFILE_APP_STREAMING ================= -->
+ <!-- Apps permission will be granted of APP_STREAMING profile [CHAR LIMIT=30] -->
+ <string name="permission_apps">Apps</string>
+
+ <!-- Description of apps permission of APP_STREAMING profile [CHAR LIMIT=NONE] -->
+ <string name="permission_apps_summary">Stream your phone\u2019s apps</string>
+
<!-- Confirmation for associating an application with a companion device of APP_STREAMING profile (type) [CHAR LIMIT=NONE] -->
<string name="title_app_streaming">Allow &lt;strong&gt;<xliff:g id="app_name" example="Exo">%1$s</xliff:g>&lt;/strong&gt; to access this information for your phone</string>
@@ -72,6 +78,18 @@
<!-- Description of the privileges the application will get if associated with the companion device of COMPUTER profile (type) [CHAR LIMIT=NONE] -->
<string name="summary_computer"></string>
+ <!-- Notification permission will be granted of COMPUTER profile [CHAR LIMIT=30] -->
+ <string name="permission_notification">Notifications</string>
+
+ <!-- Description of notification permission of COMPUTER profile [CHAR LIMIT=NONE] -->
+ <string name="permission_notification_summary">Can read all notifications, including information like contracts, messages, and photos</string>
+
+ <!-- Storage permission will be granted of COMPUTER profile [CHAR LIMIT=30] -->
+ <string name="permission_storage">Photos and media</string>
+
+ <!-- Description of storage permission of COMPUTER profile [CHAR LIMIT=NONE] -->
+ <string name="permission_storage_summary"></string>
+
<!-- Title of the helper dialog for COMPUTER profile [CHAR LIMIT=30]. -->
<string name="helper_title_computer">Google Play services</string>
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
index 0fba250b200a..1b626911fdde 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
@@ -24,6 +24,9 @@ import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTE
import static com.android.companiondevicemanager.CompanionDeviceDiscoveryService.DiscoveryState;
import static com.android.companiondevicemanager.CompanionDeviceDiscoveryService.DiscoveryState.FINISHED_TIMEOUT;
+import static com.android.companiondevicemanager.PermissionListAdapter.TYPE_APPS;
+import static com.android.companiondevicemanager.PermissionListAdapter.TYPE_NOTIFICATION;
+import static com.android.companiondevicemanager.PermissionListAdapter.TYPE_STORAGE;
import static com.android.companiondevicemanager.Utils.getApplicationLabel;
import static com.android.companiondevicemanager.Utils.getHtmlFromResources;
import static com.android.companiondevicemanager.Utils.getVendorHeaderIcon;
@@ -61,6 +64,7 @@ import androidx.fragment.app.FragmentManager;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
+import java.util.ArrayList;
import java.util.List;
/**
@@ -122,8 +126,12 @@ public class CompanionDeviceActivity extends FragmentActivity implements
// The recycler view is only shown for multiple-device regular association request, after
// at least one matching device is found.
- private @Nullable RecyclerView mRecyclerView;
- private @Nullable DeviceListAdapter mAdapter;
+ private @Nullable RecyclerView mDeviceListRecyclerView;
+ private @Nullable DeviceListAdapter mDeviceAdapter;
+
+ // The recycler view is only shown for selfManaged association request.
+ private @Nullable RecyclerView mPermissionListRecyclerView;
+ private @Nullable PermissionListAdapter mPermissionListAdapter;
// The flag used to prevent double taps, that may lead to sending several requests for creating
// an association to CDM.
@@ -133,6 +141,8 @@ public class CompanionDeviceActivity extends FragmentActivity implements
// onActivityResult() after the association is created.
private @Nullable DeviceFilterPair<?> mSelectedDevice;
+ private @Nullable List<Integer> mPermissionTypes;
+
@Override
public void onCreate(Bundle savedInstanceState) {
if (DEBUG) Log.d(TAG, "onCreate()");
@@ -242,8 +252,11 @@ public class CompanionDeviceActivity extends FragmentActivity implements
mVendorHeaderName = findViewById(R.id.vendor_header_name);
mVendorHeaderButton = findViewById(R.id.vendor_header_button);
- mRecyclerView = findViewById(R.id.device_list);
- mAdapter = new DeviceListAdapter(this, this::onListItemClick);
+ mDeviceListRecyclerView = findViewById(R.id.device_list);
+ mDeviceAdapter = new DeviceListAdapter(this, this::onListItemClick);
+
+ mPermissionListRecyclerView = findViewById(R.id.permission_list);
+ mPermissionListAdapter = new PermissionListAdapter(this);
mButtonAllow = findViewById(R.id.btn_positive);
mButtonNotAllow = findViewById(R.id.btn_negative);
@@ -359,7 +372,8 @@ public class CompanionDeviceActivity extends FragmentActivity implements
final Drawable vendorIcon;
final CharSequence vendorName;
final Spanned title;
- final Spanned summary;
+
+ mPermissionTypes = new ArrayList<>();
try {
vendorIcon = getVendorHeaderIcon(this, packageName, userId);
@@ -372,33 +386,36 @@ public class CompanionDeviceActivity extends FragmentActivity implements
switch (deviceProfile) {
case DEVICE_PROFILE_APP_STREAMING:
- title = getHtmlFromResources(this, R.string.title_app_streaming, appLabel);
- summary = getHtmlFromResources(
- this, R.string.summary_app_streaming, appLabel, deviceName);
+ title = getHtmlFromResources(this, R.string.title_app_streaming, deviceName);
+ mPermissionTypes.add(TYPE_APPS);
break;
case DEVICE_PROFILE_AUTOMOTIVE_PROJECTION:
- title = getHtmlFromResources(this, R.string.title_automotive_projection, appLabel);
- summary = getHtmlFromResources(
- this, R.string.summary_automotive_projection, appLabel, deviceName);
+ title = getHtmlFromResources(
+ this, R.string.title_automotive_projection, deviceName);
break;
case DEVICE_PROFILE_COMPUTER:
- title = getHtmlFromResources(this, R.string.title_computer, appLabel);
- summary = getHtmlFromResources(
- this, R.string.summary_computer, appLabel, deviceName);
+ title = getHtmlFromResources(this, R.string.title_computer, deviceName);
+ mPermissionTypes.add(TYPE_NOTIFICATION);
+ mPermissionTypes.add(TYPE_STORAGE);
break;
default:
throw new RuntimeException("Unsupported profile " + deviceProfile);
}
+ mSummary.setVisibility(View.GONE);
+
+ mPermissionListAdapter.setPermissionType(mPermissionTypes);
+ mPermissionListRecyclerView.setAdapter(mPermissionListAdapter);
+ mPermissionListRecyclerView.setLayoutManager(new LinearLayoutManager(this));
+
mTitle.setText(title);
- mSummary.setText(summary);
mVendorHeaderImage.setImageDrawable(vendorIcon);
mVendorHeaderName.setText(vendorName);
- mRecyclerView.setVisibility(View.GONE);
+ mDeviceListRecyclerView.setVisibility(View.GONE);
mVendorHeader.setVisibility(View.VISIBLE);
}
@@ -411,7 +428,8 @@ public class CompanionDeviceActivity extends FragmentActivity implements
deviceFilterPairs -> updateSingleDeviceUi(
deviceFilterPairs, deviceProfile, appLabel));
- mRecyclerView.setVisibility(View.GONE);
+ mPermissionListRecyclerView.setVisibility(View.GONE);
+ mDeviceListRecyclerView.setVisibility(View.GONE);
}
private void updateSingleDeviceUi(List<DeviceFilterPair<?>> deviceFilterPairs,
@@ -460,15 +478,15 @@ public class CompanionDeviceActivity extends FragmentActivity implements
mTitle.setText(title);
mSummary.setText(summary);
- mAdapter = new DeviceListAdapter(this, this::onListItemClick);
+ mDeviceAdapter = new DeviceListAdapter(this, this::onListItemClick);
// TODO: hide the list and show a spinner until a first device matching device is found.
- mRecyclerView.setAdapter(mAdapter);
- mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
+ mDeviceListRecyclerView.setAdapter(mDeviceAdapter);
+ mDeviceListRecyclerView.setLayoutManager(new LinearLayoutManager(this));
CompanionDeviceDiscoveryService.getScanResult().observe(
/* lifecycleOwner */ this,
- /* observer */ mAdapter);
+ /* observer */ mDeviceAdapter);
// "Remove" consent button: users would need to click on the list item.
mButtonAllow.setVisibility(View.GONE);
@@ -477,14 +495,14 @@ public class CompanionDeviceActivity extends FragmentActivity implements
private void onListItemClick(int position) {
if (DEBUG) Log.d(TAG, "onListItemClick() " + position);
- final DeviceFilterPair<?> selectedDevice = mAdapter.getItem(position);
+ final DeviceFilterPair<?> selectedDevice = mDeviceAdapter.getItem(position);
if (mSelectedDevice != null) {
if (DEBUG) Log.w(TAG, "Already selected.");
return;
}
// Notify the adapter to highlight the selected item.
- mAdapter.setSelectedPosition(position);
+ mDeviceAdapter.setSelectedPosition(position);
mSelectedDevice = requireNonNull(selectedDevice);
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceListAdapter.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceListAdapter.java
index e5513b074865..8babd3ade1eb 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceListAdapter.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceListAdapter.java
@@ -15,9 +15,10 @@
*/
package com.android.companiondevicemanager;
+
+import static com.android.companiondevicemanager.Utils.getIcon;
+
import android.content.Context;
-import android.graphics.Color;
-import android.graphics.drawable.Drawable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -60,11 +61,11 @@ class DeviceListAdapter extends RecyclerView.Adapter<DeviceListAdapter.ViewHolde
R.layout.list_item_device, parent, false);
ViewHolder viewHolder = new ViewHolder(view);
if (viewType == TYPE_WIFI) {
- viewHolder.mImageView.setImageDrawable(getIcon(
- com.android.internal.R.drawable.ic_wifi_signal_3));
+ viewHolder.mImageView.setImageDrawable(
+ getIcon(mContext, com.android.internal.R.drawable.ic_wifi_signal_3));
} else {
- viewHolder.mImageView.setImageDrawable(getIcon(
- android.R.drawable.stat_sys_data_bluetooth));
+ viewHolder.mImageView.setImageDrawable(
+ getIcon(mContext, android.R.drawable.stat_sys_data_bluetooth));
}
return viewHolder;
}
@@ -115,12 +116,6 @@ class DeviceListAdapter extends RecyclerView.Adapter<DeviceListAdapter.ViewHolde
return mDevices.get(position).getDevice() instanceof android.net.wifi.ScanResult;
}
- private Drawable getIcon(int resId) {
- Drawable icon = mContext.getResources().getDrawable(resId, null);
- icon.setTint(Color.DKGRAY);
- return icon;
- }
-
public interface OnItemClickListener {
void onItemClick(int position);
}
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/PermissionListAdapter.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/PermissionListAdapter.java
new file mode 100644
index 000000000000..895b729ea8c7
--- /dev/null
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/PermissionListAdapter.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2022 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.companiondevicemanager;
+
+import static com.android.companiondevicemanager.Utils.getHtmlFromResources;
+import static com.android.companiondevicemanager.Utils.getIcon;
+
+import static java.util.Collections.unmodifiableMap;
+
+import android.content.Context;
+import android.text.Spanned;
+import android.util.ArrayMap;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import androidx.recyclerview.widget.RecyclerView;
+
+import java.util.List;
+import java.util.Map;
+
+class PermissionListAdapter extends RecyclerView.Adapter<PermissionListAdapter.ViewHolder> {
+ private final Context mContext;
+
+ private List<Integer> mPermissions;
+
+ static final int TYPE_NOTIFICATION = 0;
+ static final int TYPE_STORAGE = 1;
+ static final int TYPE_APPS = 2;
+
+ private static final Map<Integer, Integer> sTitleMap;
+ static {
+ final Map<Integer, Integer> map = new ArrayMap<>();
+ map.put(TYPE_NOTIFICATION, R.string.permission_notification);
+ map.put(TYPE_STORAGE, R.string.permission_storage);
+ map.put(TYPE_APPS, R.string.permission_apps);
+ sTitleMap = unmodifiableMap(map);
+ }
+
+ private static final Map<Integer, Integer> sSummaryMap;
+ static {
+ final Map<Integer, Integer> map = new ArrayMap<>();
+ map.put(TYPE_NOTIFICATION, R.string.permission_notification_summary);
+ map.put(TYPE_STORAGE, R.string.permission_storage_summary);
+ map.put(TYPE_APPS, R.string.permission_apps_summary);
+ sSummaryMap = unmodifiableMap(map);
+ }
+
+ private static final Map<Integer, Integer> sIconMap;
+ static {
+ final Map<Integer, Integer> map = new ArrayMap<>();
+ map.put(TYPE_NOTIFICATION, R.drawable.ic_notifications);
+ map.put(TYPE_STORAGE, R.drawable.ic_storage);
+ map.put(TYPE_APPS, R.drawable.ic_apps);
+ sIconMap = unmodifiableMap(map);
+ }
+
+ PermissionListAdapter(Context context) {
+ mContext = context;
+ }
+
+ @Override
+ public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ View view = LayoutInflater.from(parent.getContext()).inflate(
+ R.layout.list_item_permission, parent, false);
+ ViewHolder viewHolder = new ViewHolder(view);
+ viewHolder.mPermissionIcon.setImageDrawable(getIcon(mContext, sIconMap.get(viewType)));
+
+ return viewHolder;
+ }
+
+ @Override
+ public void onBindViewHolder(ViewHolder holder, int position) {
+ int type = getItemViewType(position);
+ final Spanned title = getHtmlFromResources(mContext, sTitleMap.get(type));
+ final Spanned summary = getHtmlFromResources(mContext, sSummaryMap.get(type));
+
+ holder.mPermissionName.setText(title);
+ holder.mPermissionSummary.setText(summary);
+ }
+
+ @Override
+ public int getItemViewType(int position) {
+ return mPermissions.get(position);
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return position;
+ }
+
+ @Override
+ public int getItemCount() {
+ return mPermissions != null ? mPermissions.size() : 0;
+ }
+
+ static class ViewHolder extends RecyclerView.ViewHolder {
+ private final TextView mPermissionName;
+ private final TextView mPermissionSummary;
+ private final ImageView mPermissionIcon;
+ ViewHolder(View itemView) {
+ super(itemView);
+ mPermissionName = itemView.findViewById(R.id.permission_name);
+ mPermissionSummary = itemView.findViewById(R.id.permission_summary);
+ mPermissionIcon = itemView.findViewById(R.id.permission_icon);
+ }
+ }
+
+ void setPermissionType(List<Integer> permissions) {
+ mPermissions = permissions;
+ }
+}
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/Utils.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/Utils.java
index 76bbcfb79155..d5b2f0a748f1 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/Utils.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/Utils.java
@@ -22,6 +22,7 @@ import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.ApplicationInfoFlags;
+import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Handler;
@@ -121,6 +122,12 @@ class Utils {
return appInfo;
}
+ static @NonNull Drawable getIcon(@NonNull Context context, int resId) {
+ Drawable icon = context.getResources().getDrawable(resId, null);
+ icon.setTint(Color.DKGRAY);
+ return icon;
+ }
+
static void runOnMainThread(Runnable runnable) {
if (Thread.currentThread() == Looper.getMainLooper().getThread()) {
runnable.run();
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
index 183584227087..3f7e0f0fb527 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
@@ -33,6 +33,7 @@ import android.view.ViewGroup
import android.view.ViewGroupOverlay
import android.widget.FrameLayout
import com.android.internal.jank.InteractionJankMonitor
+import java.util.LinkedList
import kotlin.math.min
private const val TAG = "GhostedViewLaunchAnimatorController"
@@ -81,21 +82,51 @@ open class GhostedViewLaunchAnimatorController(
* [backgroundView].
*/
private var backgroundDrawable: WrappedDrawable? = null
- private val backgroundInsets by lazy { getBackground()?.opticalInsets ?: Insets.NONE }
+ private val backgroundInsets by lazy { background?.opticalInsets ?: Insets.NONE }
private var startBackgroundAlpha: Int = 0xFF
private val ghostedViewLocation = IntArray(2)
private val ghostedViewState = LaunchAnimator.State()
/**
- * Return the background of the [ghostedView]. This background will be used to draw the
- * background of the background view that is expanding up to the final animation position. This
- * is called at the start of the animation.
+ * The background of the [ghostedView]. This background will be used to draw the background of
+ * the background view that is expanding up to the final animation position.
*
* Note that during the animation, the alpha value value of this background will be set to 0,
* then set back to its initial value at the end of the animation.
*/
- protected open fun getBackground(): Drawable? = ghostedView.background
+ private val background: Drawable?
+
+ init {
+ /** Find the first view with a background in [view] and its children. */
+ fun findBackground(view: View): Drawable? {
+ if (view.background != null) {
+ return view.background
+ }
+
+ // Perform a BFS to find the largest View with background.
+ val views = LinkedList<View>().apply {
+ add(view)
+ }
+
+ while (views.isNotEmpty()) {
+ val v = views.removeFirst()
+ if (v.background != null) {
+ return v.background
+ }
+
+ if (v is ViewGroup) {
+ for (i in 0 until v.childCount) {
+ views.add(v.getChildAt(i))
+ }
+ }
+ }
+
+ return null
+ }
+
+ background = findBackground(ghostedView)
+ }
/**
* Set the corner radius of [background]. The background is the one that was returned by
@@ -113,7 +144,7 @@ open class GhostedViewLaunchAnimatorController(
/** Return the current top corner radius of the background. */
protected open fun getCurrentTopCornerRadius(): Float {
- val drawable = getBackground() ?: return 0f
+ val drawable = background ?: return 0f
val gradient = findGradientDrawable(drawable) ?: return 0f
// TODO(b/184121838): Support more than symmetric top & bottom radius.
@@ -122,7 +153,7 @@ open class GhostedViewLaunchAnimatorController(
/** Return the current bottom corner radius of the background. */
protected open fun getCurrentBottomCornerRadius(): Float {
- val drawable = getBackground() ?: return 0f
+ val drawable = background ?: return 0f
val gradient = findGradientDrawable(drawable) ?: return 0f
// TODO(b/184121838): Support more than symmetric top & bottom radius.
@@ -162,9 +193,8 @@ open class GhostedViewLaunchAnimatorController(
// We wrap the ghosted view background and use it to draw the expandable background. Its
// alpha will be set to 0 as soon as we start drawing the expanding background.
- val drawable = getBackground()
- startBackgroundAlpha = drawable?.alpha ?: 0xFF
- backgroundDrawable = WrappedDrawable(drawable)
+ startBackgroundAlpha = background?.alpha ?: 0xFF
+ backgroundDrawable = WrappedDrawable(background)
backgroundView?.background = backgroundDrawable
// Create a ghost of the view that will be moving and fading out. This allows to fade out
diff --git a/packages/SystemUI/res/values-sw720dp-land/dimens.xml b/packages/SystemUI/res/values-sw720dp-land/dimens.xml
index 71c195896051..139146181a47 100644
--- a/packages/SystemUI/res/values-sw720dp-land/dimens.xml
+++ b/packages/SystemUI/res/values-sw720dp-land/dimens.xml
@@ -24,4 +24,6 @@
<dimen name="keyguard_split_shade_top_margin">72dp</dimen>
<dimen name="notification_panel_margin_horizontal">24dp</dimen>
+
+ <dimen name="split_shade_header_height">56dp</dimen>
</resources>
diff --git a/packages/SystemUI/src/com/android/keyguard/EmergencyButton.java b/packages/SystemUI/src/com/android/keyguard/EmergencyButton.java
index c4b02f62f291..ffd15460ce91 100644
--- a/packages/SystemUI/src/com/android/keyguard/EmergencyButton.java
+++ b/packages/SystemUI/src/com/android/keyguard/EmergencyButton.java
@@ -107,10 +107,10 @@ public class EmergencyButton extends Button {
return super.performLongClick();
}
- void updateEmergencyCallButton(boolean isInCall, boolean isVoiceCapable, boolean simLocked) {
+ void updateEmergencyCallButton(boolean isInCall, boolean hasTelephonyRadio, boolean simLocked) {
boolean visible = false;
- if (isVoiceCapable) {
- // Emergency calling requires voice capability.
+ if (hasTelephonyRadio) {
+ // Emergency calling requires a telephony radio.
if (isInCall) {
visible = true; // always show "return to call" if phone is off-hook
} else {
diff --git a/packages/SystemUI/src/com/android/keyguard/EmergencyButtonController.java b/packages/SystemUI/src/com/android/keyguard/EmergencyButtonController.java
index e7215b8ebe49..f28910576073 100644
--- a/packages/SystemUI/src/com/android/keyguard/EmergencyButtonController.java
+++ b/packages/SystemUI/src/com/android/keyguard/EmergencyButtonController.java
@@ -21,6 +21,7 @@ import static com.android.systemui.DejankUtils.whitelistIpcs;
import android.app.ActivityOptions;
import android.app.ActivityTaskManager;
import android.content.Intent;
+import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.os.PowerManager;
import android.os.SystemClock;
@@ -116,7 +117,8 @@ public class EmergencyButtonController extends ViewController<EmergencyButton> {
if (mView != null) {
mView.updateEmergencyCallButton(
mTelecomManager != null && mTelecomManager.isInCall(),
- mTelephonyManager.isVoiceCapable(),
+ getContext().getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_TELEPHONY),
mKeyguardUpdateMonitor.isSimPinVoiceSecure());
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
index 50ca447090b5..7e1a02626dc9 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
@@ -1072,6 +1072,7 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
pw.println("WindowMagnificationController (displayId=" + mDisplayId + "):");
pw.println(" mOverlapWithGestureInsets:" + mOverlapWithGestureInsets);
pw.println(" mScale:" + mScale);
+ pw.println(" mWindowBounds:" + mWindowBounds);
pw.println(" mMirrorViewBounds:" + (isWindowVisible() ? mMirrorViewBounds : "empty"));
pw.println(" mMagnificationFrameBoundary:"
+ (isWindowVisible() ? mMagnificationFrameBoundary : "empty"));
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
index ebc766635733..dfbb0c7c1624 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
@@ -65,6 +65,9 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
// A reference to the {@link Window} used to hold the dream overlay.
private Window mWindow;
+ // True if the service has been destroyed.
+ private boolean mDestroyed;
+
private final Complication.Host mHost = new Complication.Host() {
@Override
public void requestExitDream() {
@@ -134,6 +137,7 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
mPreviewComplication.setDreamLabel(null);
mStateController.removeComplication(mPreviewComplication);
mStateController.setPreviewMode(false);
+ mDestroyed = true;
super.onDestroy();
}
@@ -141,6 +145,11 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
public void onStartDream(@NonNull WindowManager.LayoutParams layoutParams) {
setCurrentState(Lifecycle.State.STARTED);
mExecutor.execute(() -> {
+ if (mDestroyed) {
+ // The task could still be executed after the service has been destroyed. Bail if
+ // that is the case.
+ return;
+ }
mStateController.setShouldShowComplications(shouldShowComplications());
mStateController.setPreviewMode(isPreviewMode());
if (isPreviewMode()) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt
index 81934a6b6a73..71cacac7f4df 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt
@@ -22,6 +22,8 @@ import android.content.Context
import android.content.pm.PackageManager
import android.graphics.PixelFormat
import android.graphics.drawable.Drawable
+import android.os.PowerManager
+import android.os.SystemClock
import android.util.Log
import android.view.Gravity
import android.view.LayoutInflater
@@ -53,6 +55,7 @@ abstract class MediaTttChipControllerCommon<T : ChipInfoCommon>(
private val viewUtil: ViewUtil,
@Main private val mainExecutor: DelayableExecutor,
private val tapGestureDetector: TapGestureDetector,
+ private val powerManager: PowerManager,
@LayoutRes private val chipLayoutRes: Int
) {
/** The window layout parameters we'll use when attaching the view to a window. */
@@ -95,6 +98,12 @@ abstract class MediaTttChipControllerCommon<T : ChipInfoCommon>(
if (oldChipView == null) {
tapGestureDetector.addOnGestureDetectedCallback(TAG, this::onScreenTapped)
windowManager.addView(chipView, windowLayoutParams)
+ // Wake the screen so the user will see the chip
+ powerManager.wakeUp(
+ SystemClock.uptimeMillis(),
+ PowerManager.WAKE_REASON_APPLICATION,
+ "com.android.systemui:media_tap_to_transfer_activated"
+ )
}
// Cancel and re-set the chip timeout each time we get a new state.
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
index 3b94a1d9597e..44965d705802 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
@@ -22,6 +22,7 @@ import android.graphics.drawable.Drawable
import android.graphics.drawable.Icon
import android.media.MediaRoute2Info
import android.os.Handler
+import android.os.PowerManager
import android.util.Log
import android.view.ViewGroup
import android.view.WindowManager
@@ -52,6 +53,7 @@ class MediaTttChipControllerReceiver @Inject constructor(
viewUtil: ViewUtil,
mainExecutor: DelayableExecutor,
tapGestureDetector: TapGestureDetector,
+ powerManager: PowerManager,
@Main private val mainHandler: Handler,
private val uiEventLogger: MediaTttReceiverUiEventLogger,
) : MediaTttChipControllerCommon<ChipReceiverInfo>(
@@ -61,6 +63,7 @@ class MediaTttChipControllerReceiver @Inject constructor(
viewUtil,
mainExecutor,
tapGestureDetector,
+ powerManager,
R.layout.media_ttt_chip_receiver
) {
private val commandQueueCallbacks = object : CommandQueue.Callbacks {
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt
index 3e4cf9921bad..9f5ec7e1a330 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt
@@ -19,6 +19,7 @@ package com.android.systemui.media.taptotransfer.sender
import android.app.StatusBarManager
import android.content.Context
import android.media.MediaRoute2Info
+import android.os.PowerManager
import android.util.Log
import android.view.View
import android.view.ViewGroup
@@ -51,6 +52,7 @@ class MediaTttChipControllerSender @Inject constructor(
viewUtil: ViewUtil,
@Main mainExecutor: DelayableExecutor,
tapGestureDetector: TapGestureDetector,
+ powerManager: PowerManager,
private val uiEventLogger: MediaTttSenderUiEventLogger
) : MediaTttChipControllerCommon<ChipSenderInfo>(
context,
@@ -59,6 +61,7 @@ class MediaTttChipControllerSender @Inject constructor(
viewUtil,
mainExecutor,
tapGestureDetector,
+ powerManager,
R.layout.media_ttt_chip
) {
private var currentlyDisplayedChipState: ChipStateSender? = null
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
index e19483ae7845..b4e20fd7f32b 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
@@ -97,7 +97,7 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI {
"SHOWING_AUTO_SAVER_SUGGESTION",
};
- private static final String ACTION_SHOW_BATTERY_SETTINGS = "PNW.batterySettings";
+ private static final String ACTION_SHOW_BATTERY_SAVER_SETTINGS = "PNW.batterySaverSettings";
private static final String ACTION_START_SAVER = "PNW.startSaver";
private static final String ACTION_DISMISSED_WARNING = "PNW.dismissedWarning";
private static final String ACTION_CLICKED_TEMP_WARNING = "PNW.clickedTempWarning";
@@ -138,6 +138,8 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI {
private final Handler mHandler = new Handler(Looper.getMainLooper());
private final Receiver mReceiver = new Receiver();
private final Intent mOpenBatterySettings = settings(Intent.ACTION_POWER_USAGE_SUMMARY);
+ private final Intent mOpenBatterySaverSettings =
+ settings(Settings.ACTION_BATTERY_SAVER_SETTINGS);
private final boolean mUseSevereDialog;
private int mBatteryLevel;
@@ -287,7 +289,7 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI {
.setStyle(new Notification.BigTextStyle().bigText(contentText))
.setVisibility(Notification.VISIBILITY_PUBLIC);
if (hasBatterySettings()) {
- nb.setContentIntent(pendingBroadcast(ACTION_SHOW_BATTERY_SETTINGS));
+ nb.setContentIntent(pendingBroadcast(ACTION_SHOW_BATTERY_SAVER_SETTINGS));
}
// Make the notification red if the percentage goes below a certain amount or the time
// remaining estimate is disabled
@@ -748,7 +750,7 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI {
public void init() {
IntentFilter filter = new IntentFilter();
- filter.addAction(ACTION_SHOW_BATTERY_SETTINGS);
+ filter.addAction(ACTION_SHOW_BATTERY_SAVER_SETTINGS);
filter.addAction(ACTION_START_SAVER);
filter.addAction(ACTION_DISMISSED_WARNING);
filter.addAction(ACTION_CLICKED_TEMP_WARNING);
@@ -768,9 +770,9 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI {
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
Slog.i(TAG, "Received " + action);
- if (action.equals(ACTION_SHOW_BATTERY_SETTINGS)) {
+ if (action.equals(ACTION_SHOW_BATTERY_SAVER_SETTINGS)) {
dismissLowBatteryNotification();
- mContext.startActivityAsUser(mOpenBatterySettings, UserHandle.CURRENT);
+ mContext.startActivityAsUser(mOpenBatterySaverSettings, UserHandle.CURRENT);
} else if (action.equals(ACTION_START_SAVER)) {
setSaverMode(true, true);
dismissLowBatteryNotification();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt b/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt
index 5d9361d201c1..e0d158cd7db6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt
@@ -18,7 +18,10 @@ package com.android.systemui.qs
import android.app.IActivityManager
import android.app.IForegroundServiceObserver
+import android.content.BroadcastReceiver
import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
import android.content.pm.PackageManager
import android.graphics.drawable.Drawable
import android.os.IBinder
@@ -42,6 +45,7 @@ import com.android.internal.config.sysui.SystemUiDeviceConfigFlags.TASK_MANAGER_
import com.android.systemui.Dumpable
import com.android.systemui.R
import com.android.systemui.animation.DialogLaunchAnimator
+import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
@@ -67,6 +71,7 @@ class FgsManagerController @Inject constructor(
private val packageManager: PackageManager,
private val deviceConfigProxy: DeviceConfigProxy,
private val dialogLaunchAnimator: DialogLaunchAnimator,
+ private val broadcastDispatcher: BroadcastDispatcher,
private val dumpManager: DumpManager
) : IForegroundServiceObserver.Stub(), Dumpable {
@@ -125,6 +130,18 @@ class FgsManagerController @Inject constructor(
dumpManager.registerDumpable(this)
+ broadcastDispatcher.registerReceiver(
+ object : BroadcastReceiver() {
+ override fun onReceive(context: Context, intent: Intent) {
+ if (intent.action == Intent.ACTION_SHOW_FOREGROUND_SERVICE_MANAGER) {
+ showDialog(null)
+ }
+ }
+ },
+ IntentFilter(Intent.ACTION_SHOW_FOREGROUND_SERVICE_MANAGER),
+ executor = mainExecutor,
+ flags = Context.RECEIVER_NOT_EXPORTED)
+
initialized = true
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
index 00142799c541..865f09337fa3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
@@ -199,9 +199,7 @@ public class QSPanelController extends QSPanelControllerBase<QSPanel> {
/** */
public void setListening(boolean listening, boolean expanded) {
- // TODO(218268829): checking for split shade is workaround but when proper fix lands
- // "|| mShouldUseSplitNotificationShade" should be removed
- setListening(listening && (expanded || mShouldUseSplitNotificationShade));
+ setListening(listening && expanded);
if (mView.isListening()) {
refreshAllTiles();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt
index 41b070635d4f..0df2162d3338 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt
@@ -72,9 +72,9 @@ class HeadsUpCoordinator @Inject constructor(
private var mEndLifetimeExtension: OnEndLifetimeExtensionCallback? = null
private lateinit var mNotifPipeline: NotifPipeline
private var mNow: Long = -1
-
// notifs we've extended the lifetime for
private val mNotifsExtendingLifetime = ArraySet<NotificationEntry>()
+ private val mPostedEntries = LinkedHashMap<String, PostedEntry>()
override fun attach(pipeline: NotifPipeline) {
mNotifPipeline = pipeline
@@ -101,10 +101,12 @@ class HeadsUpCoordinator @Inject constructor(
return
}
// Process all non-group adds/updates
- mPostedEntries.values.toList().forEach { posted ->
- if (!posted.entry.sbn.isGroup) {
- handlePostedEntry(posted, "non-group")
- mPostedEntries.remove(posted.key)
+ mHeadsUpManager.modifyHuns { hunMutator ->
+ mPostedEntries.values.toList().forEach { posted ->
+ if (!posted.entry.sbn.isGroup) {
+ handlePostedEntry(posted, hunMutator, "non-group")
+ mPostedEntries.remove(posted.key)
+ }
}
}
}
@@ -114,10 +116,10 @@ class HeadsUpCoordinator @Inject constructor(
* we know that stability and [NotifPromoter]s have been applied, so we can use the location of
* notifications in this list to determine what kind of group alert behavior should happen.
*/
- fun onBeforeFinalizeFilter(list: List<ListEntry>) {
+ fun onBeforeFinalizeFilter(list: List<ListEntry>) = mHeadsUpManager.modifyHuns { hunMutator ->
// Nothing to do if there are no other adds/updates
if (mPostedEntries.isEmpty()) {
- return
+ return@modifyHuns
}
// Calculate a bunch of information about the logical group and the locations of group
// entries in the nearly-finalized shade list. These may be used in the per-group loop.
@@ -138,13 +140,17 @@ class HeadsUpCoordinator @Inject constructor(
// If there is no logical summary, then there is no alert to transfer
if (logicalSummary == null) {
- postedEntries.forEach { handlePostedEntry(it, "logical-summary-missing") }
+ postedEntries.forEach {
+ handlePostedEntry(it, hunMutator, scenario = "logical-summary-missing")
+ }
return@forEach
}
// If summary isn't wanted to be heads up, then there is no alert to transfer
if (!isGoingToShowHunStrict(logicalSummary)) {
- postedEntries.forEach { handlePostedEntry(it, "logical-summary-not-alerting") }
+ postedEntries.forEach {
+ handlePostedEntry(it, hunMutator, scenario = "logical-summary-not-alerting")
+ }
return@forEach
}
@@ -177,7 +183,9 @@ class HeadsUpCoordinator @Inject constructor(
// If there is no child to receive the parent alert, then just handle the posted entries
// and return.
if (childToReceiveParentAlert == null) {
- postedEntries.forEach { handlePostedEntry(it, "no-transfer-target") }
+ postedEntries.forEach {
+ handlePostedEntry(it, hunMutator, scenario = "no-transfer-target")
+ }
return@forEach
}
@@ -189,51 +197,66 @@ class HeadsUpCoordinator @Inject constructor(
if (!isSummaryAttached) {
val summaryUpdateForRemoval = summaryUpdate?.also {
it.shouldHeadsUpEver = false
- } ?: PostedEntry(logicalSummary,
- wasAdded = false,
- wasUpdated = false,
- shouldHeadsUpEver = false,
- shouldHeadsUpAgain = false,
- isAlerting = mHeadsUpManager.isAlerting(logicalSummary.key),
- isBinding = isEntryBinding(logicalSummary),
+ } ?: PostedEntry(
+ logicalSummary,
+ wasAdded = false,
+ wasUpdated = false,
+ shouldHeadsUpEver = false,
+ shouldHeadsUpAgain = false,
+ isAlerting = mHeadsUpManager.isAlerting(logicalSummary.key),
+ isBinding = isEntryBinding(logicalSummary),
)
// If we transfer the alert and the summary isn't even attached, that means we
// should ensure the summary is no longer alerting, so we remove it here.
- handlePostedEntry(summaryUpdateForRemoval, "detached-summary-remove-alert")
- } else if (summaryUpdate!=null) {
- mLogger.logPostedEntryWillNotEvaluate(summaryUpdate, "attached-summary-transferred")
+ handlePostedEntry(
+ summaryUpdateForRemoval,
+ hunMutator,
+ scenario = "detached-summary-remove-alert")
+ } else if (summaryUpdate != null) {
+ mLogger.logPostedEntryWillNotEvaluate(
+ summaryUpdate,
+ reason = "attached-summary-transferred")
}
// Handle all posted entries -- if the child receiving the parent's alert is in the
// list, then set its flags to ensure it alerts.
var didAlertChildToReceiveParentAlert = false
postedEntries.asSequence()
- .filter { it.key != logicalSummary.key }
- .forEach { postedEntry ->
- if (childToReceiveParentAlert.key == postedEntry.key) {
- // Update the child's posted update so that it
- postedEntry.shouldHeadsUpEver = true
- postedEntry.shouldHeadsUpAgain = true
- handlePostedEntry(postedEntry, "child-alert-transfer-target-$targetType")
- didAlertChildToReceiveParentAlert = true
- } else {
- handlePostedEntry(postedEntry, "child-alert-non-target")
+ .filter { it.key != logicalSummary.key }
+ .forEach { postedEntry ->
+ if (childToReceiveParentAlert.key == postedEntry.key) {
+ // Update the child's posted update so that it
+ postedEntry.shouldHeadsUpEver = true
+ postedEntry.shouldHeadsUpAgain = true
+ handlePostedEntry(
+ postedEntry,
+ hunMutator,
+ scenario = "child-alert-transfer-target-$targetType")
+ didAlertChildToReceiveParentAlert = true
+ } else {
+ handlePostedEntry(
+ postedEntry,
+ hunMutator,
+ scenario = "child-alert-non-target")
+ }
}
- }
// If the child receiving the alert was not updated on this tick (which can happen in a
// standard alert transfer scenario), then construct an update so that we can apply it.
if (!didAlertChildToReceiveParentAlert) {
val posted = PostedEntry(
- childToReceiveParentAlert,
- wasAdded = false,
- wasUpdated = false,
- shouldHeadsUpEver = true,
- shouldHeadsUpAgain = true,
- isAlerting = mHeadsUpManager.isAlerting(childToReceiveParentAlert.key),
- isBinding = isEntryBinding(childToReceiveParentAlert),
+ childToReceiveParentAlert,
+ wasAdded = false,
+ wasUpdated = false,
+ shouldHeadsUpEver = true,
+ shouldHeadsUpAgain = true,
+ isAlerting = mHeadsUpManager.isAlerting(childToReceiveParentAlert.key),
+ isBinding = isEntryBinding(childToReceiveParentAlert),
)
- handlePostedEntry(posted, "non-posted-child-alert-transfer-target-$targetType")
+ handlePostedEntry(
+ posted,
+ hunMutator,
+ scenario = "non-posted-child-alert-transfer-target-$targetType")
}
}
// After this method runs, all posted entries should have been handled (or skipped).
@@ -292,9 +315,7 @@ class HeadsUpCoordinator @Inject constructor(
}
}
- private val mPostedEntries = LinkedHashMap<String, PostedEntry>()
-
- fun handlePostedEntry(posted: PostedEntry, scenario: String) {
+ private fun handlePostedEntry(posted: PostedEntry, hunMutator: HunMutator, scenario: String) {
mLogger.logPostedEntryWillEvaluate(posted, scenario)
if (posted.wasAdded) {
if (posted.shouldHeadsUpEver) {
@@ -308,12 +329,12 @@ class HeadsUpCoordinator @Inject constructor(
// If alerting, we need to post an update. Otherwise we're still binding,
// and we can just let that finish.
if (posted.isAlerting) {
- mHeadsUpManager.updateNotification(posted.key, posted.shouldHeadsUpAgain)
+ hunMutator.updateNotification(posted.key, posted.shouldHeadsUpAgain)
}
} else {
if (posted.isAlerting) {
// We don't want this to be interrupting anymore, let's remove it
- mHeadsUpManager.removeNotification(posted.key, false /*removeImmediately*/)
+ hunMutator.removeNotification(posted.key, false /*removeImmediately*/)
} else {
// Don't let the bind finish
cancelHeadsUpBind(posted.entry)
@@ -366,7 +387,7 @@ class HeadsUpCoordinator @Inject constructor(
val shouldHeadsUpAgain = shouldHunAgain(entry)
val isAlerting = mHeadsUpManager.isAlerting(entry.key)
val isBinding = isEntryBinding(entry)
- mPostedEntries.compute(entry.key) { _, value ->
+ val posted = mPostedEntries.compute(entry.key) { _, value ->
value?.also { update ->
update.wasUpdated = true
update.shouldHeadsUpEver = update.shouldHeadsUpEver || shouldHeadsUpEver
@@ -383,6 +404,18 @@ class HeadsUpCoordinator @Inject constructor(
isBinding = isBinding,
)
}
+ // Handle cancelling alerts here, rather than in the OnBeforeFinalizeFilter, so that
+ // work can be done before the ShadeListBuilder is run. This prevents re-entrant
+ // behavior between this Coordinator, HeadsUpManager, and VisualStabilityManager.
+ if (posted?.shouldHeadsUpEver == false) {
+ if (posted.isAlerting) {
+ // We don't want this to be interrupting anymore, let's remove it
+ mHeadsUpManager.removeNotification(posted.key, false /*removeImmediately*/)
+ } else if (posted.isBinding) {
+ // Don't let the bind finish
+ cancelHeadsUpBind(posted.entry)
+ }
+ }
}
/**
@@ -543,3 +576,43 @@ private enum class GroupLocation { Detached, Isolated, Summary, Child }
private fun Map<String, GroupLocation>.getLocation(key: String): GroupLocation =
getOrDefault(key, GroupLocation.Detached)
+
+/**
+ * Invokes the given block with a [HunMutator] that defers all HUN removals. This ensures that the
+ * HeadsUpManager is notified of additions before removals, which prevents a glitch where the
+ * HeadsUpManager temporarily believes that nothing is alerting, causing bad re-entrant behavior.
+ */
+private fun <R> HeadsUpManager.modifyHuns(block: (HunMutator) -> R): R {
+ val mutator = HunMutatorImpl(this)
+ return block(mutator).also { mutator.commitModifications() }
+}
+
+/** Mutates the HeadsUp state of notifications. */
+private interface HunMutator {
+ fun updateNotification(key: String, alert: Boolean)
+ fun removeNotification(key: String, releaseImmediately: Boolean)
+}
+
+/**
+ * [HunMutator] implementation that defers removing notifications from the HeadsUpManager until
+ * after additions/updates.
+ */
+private class HunMutatorImpl(private val headsUpManager: HeadsUpManager) : HunMutator {
+ private val deferred = mutableListOf<Pair<String, Boolean>>()
+
+ override fun updateNotification(key: String, alert: Boolean) {
+ headsUpManager.updateNotification(key, alert)
+ }
+
+ override fun removeNotification(key: String, releaseImmediately: Boolean) {
+ val args = Pair(key, releaseImmediately)
+ deferred.add(args)
+ }
+
+ fun commitModifications() {
+ deferred.forEach { (key, releaseImmediately) ->
+ headsUpManager.removeNotification(key, releaseImmediately)
+ }
+ deferred.clear()
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 24f44e6aa613..5b9dbd0f3361 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -404,7 +404,12 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
*/
private float mBackgroundXFactor = 1f;
- private boolean mQsExpanded;
+ /**
+ * Indicates QS are full screen and pushing notifications out of the screen.
+ * It's different from QS just being expanded as in split shade QS can be expanded and
+ * still don't take full screen nor influence notifications.
+ */
+ private boolean mQsFullScreen;
private boolean mForwardScrollable;
private boolean mBackwardScrollable;
private NotificationShelf mShelf;
@@ -1130,7 +1135,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
@ShadeViewRefactor(RefactorComponent.LAYOUT_ALGORITHM)
private void updateAlgorithmLayoutMinHeight() {
- mAmbientState.setLayoutMinHeight(mQsExpanded || isHeadsUpTransition()
+ mAmbientState.setLayoutMinHeight(mQsFullScreen || isHeadsUpTransition()
? getLayoutMinHeight() : 0);
}
@@ -1361,7 +1366,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
translationY = 0;
if (mShouldShowShelfOnly) {
stackHeight = mTopPadding + mShelf.getIntrinsicHeight();
- } else if (mQsExpanded) {
+ } else if (mQsFullScreen) {
int stackStartPosition = mContentHeight - mTopPadding + mIntrinsicPadding;
int stackEndPosition = mMaxTopPadding + mShelf.getIntrinsicHeight();
if (stackStartPosition <= stackEndPosition) {
@@ -2318,7 +2323,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
private void updateScrollability() {
- boolean scrollable = !mQsExpanded && getScrollRange() > 0;
+ boolean scrollable = !mQsFullScreen && getScrollRange() > 0;
if (scrollable != mScrollable) {
mScrollable = scrollable;
setFocusable(scrollable);
@@ -4839,14 +4844,14 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
}
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
- public void setQsExpanded(boolean qsExpanded) {
- mQsExpanded = qsExpanded;
+ public void setQsFullScreen(boolean qsFullScreen) {
+ mQsFullScreen = qsFullScreen;
updateAlgorithmLayoutMinHeight();
updateScrollability();
}
- boolean isQsExpanded() {
- return mQsExpanded;
+ boolean isQsFullScreen() {
+ return mQsFullScreen;
}
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
@@ -5807,10 +5812,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
mSwipeHelper.resetExposedMenuView(animate, force);
}
- boolean isUsingSplitNotificationShade() {
- return mShouldUseSplitNotificationShade;
- }
-
static boolean matchesSelection(
ExpandableNotificationRow row,
@SelectedRows int selection) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index e64a0d6220e1..7df8e7df486e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -111,13 +111,13 @@ import com.android.systemui.statusbar.notification.row.ExpandableView;
import com.android.systemui.statusbar.notification.row.NotificationGuts;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.row.NotificationSnooze;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.phone.HeadsUpTouchHelper;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.phone.ShadeController;
-import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
@@ -1086,8 +1086,8 @@ public class NotificationStackScrollLayoutController {
}
}
- public void setQsExpanded(boolean expanded) {
- mView.setQsExpanded(expanded);
+ public void setQsFullScreen(boolean fullScreen) {
+ mView.setQsFullScreen(fullScreen);
updateShowEmptyShadeView();
}
@@ -1204,7 +1204,7 @@ public class NotificationStackScrollLayoutController {
public void updateShowEmptyShadeView() {
Trace.beginSection("NSSLC.updateShowEmptyShadeView");
mShowEmptyShadeView = mBarState != KEYGUARD
- && (!mView.isQsExpanded() || mView.isUsingSplitNotificationShade())
+ && !mView.isQsFullScreen()
&& getVisibleNotificationCount() == 0;
mView.updateEmptyShadeView(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index 22a47aad4e2f..cc8a70388ed0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -358,6 +358,12 @@ public class NotificationPanelViewController extends PanelViewController {
private boolean mConflictingQsExpansionGesture;
private boolean mPanelExpanded;
+
+ /**
+ * Indicates that QS is in expanded state which can happen by:
+ * - single pane shade: expanding shade and then expanding QS
+ * - split shade: just expanding shade (QS are expanded automatically)
+ */
private boolean mQsExpanded;
private boolean mQsExpandedWhenExpandingStarted;
private boolean mQsFullyExpanded;
@@ -401,7 +407,11 @@ public class NotificationPanelViewController extends PanelViewController {
private boolean mIsExpanding;
private boolean mBlockTouches;
- // Used for two finger gesture as well as accessibility shortcut to QS.
+
+ /**
+ * Determines if QS should be already expanded when expanding shade.
+ * Used for split shade, two finger gesture as well as accessibility shortcut to QS.
+ */
private boolean mQsExpandImmediate;
private boolean mTwoFingerQsExpandPossible;
private String mHeaderDebugInfo;
@@ -1467,6 +1477,11 @@ public class NotificationPanelViewController extends PanelViewController {
constraintSet.connect(R.id.keyguard_status_view, END, statusConstraint, END);
if (animate) {
ChangeBounds transition = new ChangeBounds();
+ if (mShouldUseSplitNotificationShade) {
+ // Excluding media from the transition on split-shade, as it doesn't transition
+ // horizontally properly.
+ transition.excludeTarget(R.id.status_view_media_container, true);
+ }
transition.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
transition.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
TransitionManager.beginDelayedTransition(mNotificationContainerParent, transition);
@@ -1684,11 +1699,16 @@ public class NotificationPanelViewController extends PanelViewController {
if (mQsExpanded) {
mQsExpandImmediate = true;
- mNotificationStackScrollLayoutController.setShouldShowShelfOnly(true);
+ setShowShelfOnly(true);
}
super.collapse(delayed, speedUpFactor);
}
+ private void setShowShelfOnly(boolean shelfOnly) {
+ mNotificationStackScrollLayoutController.setShouldShowShelfOnly(
+ shelfOnly && !mShouldUseSplitNotificationShade);
+ }
+
public void closeQs() {
cancelQsAnimation();
setQsExpansion(mQsMinExpansionHeight);
@@ -1725,7 +1745,7 @@ public class NotificationPanelViewController extends PanelViewController {
public void expandWithQs() {
if (isQsExpansionEnabled()) {
mQsExpandImmediate = true;
- mNotificationStackScrollLayoutController.setShouldShowShelfOnly(true);
+ setShowShelfOnly(true);
}
if (isFullyCollapsed()) {
expand(true /* animate */);
@@ -1924,7 +1944,15 @@ public class NotificationPanelViewController extends PanelViewController {
mFalsingManager.isFalseTouch(QS_COLLAPSE);
}
- flingSettings(vel, expandsQs && !isCancelMotionEvent ? FLING_EXPAND : FLING_COLLAPSE);
+ int flingType;
+ if (expandsQs && !isCancelMotionEvent) {
+ flingType = FLING_EXPAND;
+ } else if (mShouldUseSplitNotificationShade) {
+ flingType = FLING_HIDE;
+ } else {
+ flingType = FLING_COLLAPSE;
+ }
+ flingSettings(vel, flingType);
}
private void logQsSwipeDown(float y) {
@@ -1989,8 +2017,10 @@ public class NotificationPanelViewController extends PanelViewController {
return false;
}
final int action = event.getActionMasked();
- if (action == MotionEvent.ACTION_DOWN && getExpandedFraction() == 1f
- && mBarState != KEYGUARD && !mQsExpanded && isQsExpansionEnabled()) {
+ boolean collapsedQs = !mQsExpanded && !mShouldUseSplitNotificationShade;
+ boolean expandedShadeCollapsedQs = getExpandedFraction() == 1f && mBarState != KEYGUARD
+ && collapsedQs && isQsExpansionEnabled();
+ if (action == MotionEvent.ACTION_DOWN && expandedShadeCollapsedQs) {
// Down in the empty area while fully expanded - go to QS.
mQsTracking = true;
traceQsJank(true /* startTracing */, false /* wasCancelled */);
@@ -2005,7 +2035,7 @@ public class NotificationPanelViewController extends PanelViewController {
}
if (!mQsExpandImmediate && mQsTracking) {
onQsTouch(event);
- if (!mConflictingQsExpansionGesture) {
+ if (!mConflictingQsExpansionGesture && !mShouldUseSplitNotificationShade) {
return true;
}
}
@@ -2019,7 +2049,7 @@ public class NotificationPanelViewController extends PanelViewController {
< mStatusBarMinHeight) {
mMetricsLogger.count(COUNTER_PANEL_OPEN_QS, 1);
mQsExpandImmediate = true;
- mNotificationStackScrollLayoutController.setShouldShowShelfOnly(true);
+ setShowShelfOnly(true);
requestPanelHeightUpdate();
// Normally, we start listening when the panel is expanded, but here we need to start
@@ -2090,6 +2120,9 @@ public class NotificationPanelViewController extends PanelViewController {
if (!isFullyCollapsed()) {
return;
}
+ if (mShouldUseSplitNotificationShade) {
+ mQsExpandImmediate = true;
+ }
mExpectingSynthesizedDown = true;
onTrackingStarted();
updatePanelExpanded();
@@ -2296,12 +2329,10 @@ public class NotificationPanelViewController extends PanelViewController {
}
private void updateQsState() {
- mNotificationStackScrollLayoutController.setQsExpanded(mQsExpanded);
+ boolean qsFullScreen = mQsExpanded && !mShouldUseSplitNotificationShade;
+ mNotificationStackScrollLayoutController.setQsFullScreen(qsFullScreen);
mNotificationStackScrollLayoutController.setScrollingEnabled(
- mBarState != KEYGUARD
- && (!mQsExpanded
- || mQsExpansionFromOverscroll
- || mShouldUseSplitNotificationShade));
+ mBarState != KEYGUARD && (!qsFullScreen || mQsExpansionFromOverscroll));
if (mKeyguardUserSwitcherController != null && mQsExpanded
&& !mStackScrollerOverscrolling) {
@@ -2346,7 +2377,7 @@ public class NotificationPanelViewController extends PanelViewController {
private void updateQsExpansion() {
if (mQs == null) return;
final float squishiness;
- if (mQsExpandImmediate || mQsExpanded) {
+ if ((mQsExpandImmediate || mQsExpanded) && !mShouldUseSplitNotificationShade) {
squishiness = 1;
} else if (mLockscreenShadeTransitionController.getQSDragProgress() > 0) {
squishiness = mLockscreenShadeTransitionController.getQSDragProgress();
@@ -3194,7 +3225,7 @@ public class NotificationPanelViewController extends PanelViewController {
setListening(true);
}
mQsExpandImmediate = false;
- mNotificationStackScrollLayoutController.setShouldShowShelfOnly(false);
+ setShowShelfOnly(false);
mTwoFingerQsExpandPossible = false;
updateTrackingHeadsUp(null);
mExpandingFromHeadsUp = false;
@@ -3250,9 +3281,7 @@ public class NotificationPanelViewController extends PanelViewController {
mScrimController.onTrackingStarted();
if (mQsFullyExpanded) {
mQsExpandImmediate = true;
- if (!mShouldUseSplitNotificationShade) {
- mNotificationStackScrollLayoutController.setShouldShowShelfOnly(true);
- }
+ setShowShelfOnly(true);
}
if (mBarState == KEYGUARD || mBarState == StatusBarState.SHADE_LOCKED) {
mAffordanceHelper.animateHideLeftRightIcon();
@@ -3957,10 +3986,6 @@ public class NotificationPanelViewController extends PanelViewController {
mNotificationStackScrollLayoutController.runAfterAnimationFinished(r);
}
- public void setScrollingEnabled(boolean b) {
- mNotificationStackScrollLayoutController.setScrollingEnabled(b);
- }
-
private Runnable mHideExpandedRunnable;
private final Runnable mMaybeHideExpandedRunnable = new Runnable() {
@Override
@@ -4871,7 +4896,11 @@ public class NotificationPanelViewController extends PanelViewController {
private void updateQSMinHeight() {
float previousMin = mQsMinExpansionHeight;
- mQsMinExpansionHeight = mKeyguardShowing ? 0 : mQs.getQsMinExpansionHeight();
+ if (mKeyguardShowing || mShouldUseSplitNotificationShade) {
+ mQsMinExpansionHeight = 0;
+ } else {
+ mQsMinExpansionHeight = mQs.getQsMinExpansionHeight();
+ }
if (mQsExpansionHeight == previousMin) {
mQsExpansionHeight = mQsMinExpansionHeight;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
index d7c8a9160807..ddc907666f1c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -584,13 +584,6 @@ public class UserSwitcherController implements Dumpable {
.setPackage(mCreateSupervisedUserPackage)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- // TODO(b/209659998): [to-be-removed] fallback activity for supervised user creation.
- if (mContext.getPackageManager().resolveActivity(intent, 0) == null) {
- intent.setPackage(null)
- .setClassName("com.android.settings",
- "com.android.settings.users.AddSupervisedUserActivity");
- }
-
mContext.startActivity(intent);
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/Utils.java b/packages/SystemUI/src/com/android/systemui/util/Utils.java
index 71d8e3344937..7e3bce589f7e 100644
--- a/packages/SystemUI/src/com/android/systemui/util/Utils.java
+++ b/packages/SystemUI/src/com/android/systemui/util/Utils.java
@@ -199,10 +199,13 @@ public class Utils {
/**
* Gets the {@link R.dimen#split_shade_header_height}.
*
- * Currently, it's the same as {@link com.android.internal.R.dimen#quick_qs_offset_height}.
+ * It should be fine to not ignore cutouts as split shade might not want to react to them:
+ * for split shade header, which is only on bigger screens, either cutout won't be a problem
+ * (it's usually centered and in split shade that's likely empty area) or we probably want to
+ * handle it differently.
*/
public static int getSplitShadeStatusBarHeight(Context context) {
- return SystemBarUtils.getQuickQsOffsetHeight(context);
+ return context.getResources().getDimensionPixelSize(R.dimen.split_shade_header_height);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
index c3c3f90539fd..60567c49bfdf 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
@@ -51,6 +51,7 @@ import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.shared.tracing.ProtoTraceable;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.UserInfoController;
import com.android.systemui.tracing.ProtoTracer;
import com.android.systemui.tracing.nano.SystemUiTraceProto;
@@ -116,6 +117,7 @@ public final class WMShell extends CoreStartable
private final CommandQueue mCommandQueue;
private final ConfigurationController mConfigurationController;
+ private final KeyguardStateController mKeyguardStateController;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private final NavigationModeController mNavigationModeController;
private final ScreenLifecycle mScreenLifecycle;
@@ -129,7 +131,7 @@ public final class WMShell extends CoreStartable
private KeyguardUpdateMonitorCallback mSplitScreenKeyguardCallback;
private KeyguardUpdateMonitorCallback mPipKeyguardCallback;
private KeyguardUpdateMonitorCallback mOneHandedKeyguardCallback;
- private KeyguardUpdateMonitorCallback mCompatUIKeyguardCallback;
+ private KeyguardStateController.Callback mCompatUIKeyguardCallback;
private WakefulnessLifecycle.Observer mWakefulnessObserver;
@Inject
@@ -143,6 +145,7 @@ public final class WMShell extends CoreStartable
Optional<DragAndDrop> dragAndDropOptional,
CommandQueue commandQueue,
ConfigurationController configurationController,
+ KeyguardStateController keyguardStateController,
KeyguardUpdateMonitor keyguardUpdateMonitor,
NavigationModeController navigationModeController,
ScreenLifecycle screenLifecycle,
@@ -154,6 +157,7 @@ public final class WMShell extends CoreStartable
super(context);
mCommandQueue = commandQueue;
mConfigurationController = configurationController;
+ mKeyguardStateController = keyguardStateController;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mNavigationModeController = navigationModeController;
mScreenLifecycle = screenLifecycle;
@@ -362,13 +366,13 @@ public final class WMShell extends CoreStartable
@VisibleForTesting
void initCompatUi(CompatUI sizeCompatUI) {
- mCompatUIKeyguardCallback = new KeyguardUpdateMonitorCallback() {
+ mCompatUIKeyguardCallback = new KeyguardStateController.Callback() {
@Override
- public void onKeyguardOccludedChanged(boolean occluded) {
- sizeCompatUI.onKeyguardOccludedChanged(occluded);
+ public void onKeyguardShowingChanged() {
+ sizeCompatUI.onKeyguardShowingChanged(mKeyguardStateController.isShowing());
}
};
- mKeyguardUpdateMonitor.registerCallback(mCompatUIKeyguardCallback);
+ mKeyguardStateController.addCallback(mCompatUIKeyguardCallback);
}
void initDragAndDrop(DragAndDrop dragAndDrop) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
index 35fda1392512..7d7ccb462b3e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
@@ -19,6 +19,7 @@ package com.android.systemui.dreams;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -222,4 +223,25 @@ public class DreamOverlayServiceTest extends SysuiTestCase {
verify(mLifecycleRegistry).setCurrentState(Lifecycle.State.DESTROYED);
verify(mStateController).setOverlayActive(false);
}
+
+ @Test
+ public void testDecorViewNotAddedToWindowAfterDestroy() throws Exception {
+ when(mDreamOverlayContainerView.getParent())
+ .thenReturn(mDreamOverlayContainerViewParent)
+ .thenReturn(null);
+
+ final IBinder proxy = mService.onBind(new Intent());
+ final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy);
+
+ // Inform the overlay service of dream starting.
+ overlay.startDream(mWindowParams, mDreamOverlayCallback);
+
+ // Destroy the service.
+ mService.onDestroy();
+
+ // Run executor tasks.
+ mMainExecutor.runAllReady();
+
+ verify(mWindowManager, never()).addView(any(), any());
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommonTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommonTest.kt
index f99efe25c6eb..ccce5778c150 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommonTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommonTest.kt
@@ -20,7 +20,7 @@ import android.content.Context
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import android.graphics.drawable.Drawable
-import android.media.MediaRoute2Info
+import android.os.PowerManager
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
@@ -30,7 +30,6 @@ import androidx.test.filters.SmallTest
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.media.taptotransfer.receiver.ChipReceiverInfo
import com.android.systemui.statusbar.gesture.TapGestureDetector
import com.android.systemui.util.concurrency.DelayableExecutor
import com.android.systemui.util.concurrency.FakeExecutor
@@ -71,6 +70,8 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() {
private lateinit var viewUtil: ViewUtil
@Mock
private lateinit var tapGestureDetector: TapGestureDetector
+ @Mock
+ private lateinit var powerManager: PowerManager
@Before
fun setUp() {
@@ -88,16 +89,17 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() {
fakeExecutor = FakeExecutor(fakeClock)
controllerCommon = TestControllerCommon(
- context, logger, windowManager, viewUtil, fakeExecutor, tapGestureDetector
+ context, logger, windowManager, viewUtil, fakeExecutor, tapGestureDetector, powerManager
)
}
@Test
- fun displayChip_chipAddedAndGestureDetectionStarted() {
+ fun displayChip_chipAddedAndGestureDetectionStartedAndScreenOn() {
controllerCommon.displayChip(getState())
verify(windowManager).addView(any(), any())
verify(tapGestureDetector).addOnGestureDetectedCallback(any(), any())
+ verify(powerManager).wakeUp(any(), any(), any())
}
@Test
@@ -281,6 +283,7 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() {
viewUtil: ViewUtil,
@Main mainExecutor: DelayableExecutor,
tapGestureDetector: TapGestureDetector,
+ powerManager: PowerManager
) : MediaTttChipControllerCommon<ChipInfo>(
context,
logger,
@@ -288,6 +291,7 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() {
viewUtil,
mainExecutor,
tapGestureDetector,
+ powerManager,
R.layout.media_ttt_chip
) {
override fun updateChipView(chipInfo: ChipInfo, currentChipView: ViewGroup) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
index 36b5761ca690..355d3fe4b8d1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
@@ -22,6 +22,7 @@ import android.content.pm.PackageManager
import android.graphics.drawable.Drawable
import android.media.MediaRoute2Info
import android.os.Handler
+import android.os.PowerManager
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.View
@@ -64,6 +65,8 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() {
@Mock
private lateinit var logger: MediaTttLogger
@Mock
+ private lateinit var powerManager: PowerManager
+ @Mock
private lateinit var windowManager: WindowManager
@Mock
private lateinit var viewUtil: ViewUtil
@@ -97,6 +100,7 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() {
viewUtil,
FakeExecutor(FakeSystemClock()),
TapGestureDetector(context),
+ powerManager,
Handler.getMain(),
receiverUiEventLogger,
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt
index 4ac7709071c8..ef5315428a60 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt
@@ -21,6 +21,7 @@ import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import android.graphics.drawable.Drawable
import android.media.MediaRoute2Info
+import android.os.PowerManager
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.View
@@ -66,6 +67,8 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
@Mock
private lateinit var logger: MediaTttLogger
@Mock
+ private lateinit var powerManager: PowerManager
+ @Mock
private lateinit var windowManager: WindowManager
@Mock
private lateinit var viewUtil: ViewUtil
@@ -103,6 +106,7 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
viewUtil,
fakeExecutor,
TapGestureDetector(context),
+ powerManager,
senderUiEventLogger
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
index 25a5b900f15a..067caa98102b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
@@ -87,6 +87,8 @@ class LockscreenShadeTransitionControllerTest : SysuiTestCase() {
row = helper.createRow()
context.getOrCreateTestableResources()
.addOverride(R.bool.config_use_split_notification_shade, false)
+ context.getOrCreateTestableResources()
+ .addOverride(R.dimen.lockscreen_shade_depth_controller_transition_distance, 100)
transitionController = LockscreenShadeTransitionController(
statusBarStateController = statusbarStateController,
logger = logger,
@@ -247,6 +249,17 @@ class LockscreenShadeTransitionControllerTest : SysuiTestCase() {
}
@Test
+ fun testDragDownAmount_depthDistanceIsZero_doesNotSetProgress() {
+ context.getOrCreateTestableResources()
+ .addOverride(R.dimen.lockscreen_shade_depth_controller_transition_distance, 0)
+ configurationController.notifyConfigurationChanged()
+
+ transitionController.dragDownAmount = 10f
+
+ verify(depthController, never()).transitionToFullShadeProgress
+ }
+
+ @Test
fun setDragDownAmount_setsValueOnMediaHierarchyManager() {
transitionController.dragDownAmount = 10f
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
index 1561b5a5d22e..bf16e0ab39c6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
@@ -69,11 +69,11 @@ import com.android.systemui.statusbar.notification.collection.render.SectionHead
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController.NotificationPanelEvent;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.phone.ShadeController;
-import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.ZenModeController;
@@ -278,18 +278,17 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase {
mStateListenerArgumentCaptor.capture(), anyInt());
StatusBarStateController.StateListener stateListener =
mStateListenerArgumentCaptor.getValue();
- when(mNotificationStackScrollLayout.isUsingSplitNotificationShade()).thenReturn(true);
stateListener.onStateChanged(SHADE);
mController.getView().removeAllViews();
- mController.setQsExpanded(false);
+ mController.setQsFullScreen(false);
reset(mNotificationStackScrollLayout);
mController.updateShowEmptyShadeView();
verify(mNotificationStackScrollLayout).updateEmptyShadeView(
/* visible= */ true,
/* notifVisibleInShade= */ false);
- mController.setQsExpanded(true);
+ mController.setQsFullScreen(true);
reset(mNotificationStackScrollLayout);
mController.updateShowEmptyShadeView();
verify(mNotificationStackScrollLayout).updateEmptyShadeView(
@@ -411,11 +410,11 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase {
boolean toShow) {
if (toShow) {
statusBarStateListener.onStateChanged(SHADE);
- mController.setQsExpanded(false);
+ mController.setQsFullScreen(false);
mController.getView().removeAllViews();
} else {
statusBarStateListener.onStateChanged(KEYGUARD);
- mController.setQsExpanded(true);
+ mController.setQsFullScreen(true);
mController.getView().addContainerView(mock(ExpandableNotificationRow.class));
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
index 7726938db3b0..185942e6fbc8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
@@ -32,6 +32,7 @@ import com.android.systemui.model.SysUiState;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.UserInfoController;
import com.android.systemui.tracing.ProtoTracer;
import com.android.wm.shell.ShellCommandHandler;
@@ -66,6 +67,7 @@ public class WMShellTest extends SysuiTestCase {
@Mock CommandQueue mCommandQueue;
@Mock ConfigurationController mConfigurationController;
+ @Mock KeyguardStateController mKeyguardStateController;
@Mock KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@Mock NavigationModeController mNavigationModeController;
@Mock ScreenLifecycle mScreenLifecycle;
@@ -90,9 +92,9 @@ public class WMShellTest extends SysuiTestCase {
Optional.of(mSplitScreen), Optional.of(mOneHanded), Optional.of(mHideDisplayCutout),
Optional.of(mShellCommandHandler), Optional.of(mCompatUI),
Optional.of(mDragAndDrop),
- mCommandQueue, mConfigurationController, mKeyguardUpdateMonitor,
- mNavigationModeController, mScreenLifecycle, mSysUiState, mProtoTracer,
- mWakefulnessLifecycle, mUserInfoController, mSysUiMainExecutor);
+ mCommandQueue, mConfigurationController, mKeyguardStateController,
+ mKeyguardUpdateMonitor, mNavigationModeController, mScreenLifecycle, mSysUiState,
+ mProtoTracer, mWakefulnessLifecycle, mUserInfoController, mSysUiMainExecutor);
}
@Test
@@ -132,6 +134,6 @@ public class WMShellTest extends SysuiTestCase {
public void initCompatUI_registersCallbacks() {
mWMShell.initCompatUi(mCompatUI);
- verify(mKeyguardUpdateMonitor).registerCallback(any(KeyguardUpdateMonitorCallback.class));
+ verify(mKeyguardStateController).addCallback(any(KeyguardStateController.Callback.class));
}
}
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
index 86777a2f62fd..e20b15a3e807 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
@@ -1327,14 +1327,13 @@ public class TouchExplorer extends BaseEventStreamTransformation
if (mState.isServiceDetectingGestures() && mState.isTouchInteracting()) {
// Cancel without deleting events.
mHandler.removeCallbacks(mSendHoverEnterAndMoveDelayed);
- mSendHoverEnterAndMoveDelayed.run();
- mSendHoverEnterAndMoveDelayed.clear();
- final MotionEvent prototype = mState.getLastReceivedEvent();
- final MotionEvent rawEvent = mState.getLastReceivedRawEvent();
final int pointerId = mReceivedPointerTracker.getPrimaryPointerId();
final int pointerIdBits = (1 << pointerId);
final int policyFlags = mState.getLastReceivedPolicyFlags();
- mSendHoverExitDelayed.post(prototype, rawEvent, pointerIdBits, policyFlags);
+ mSendHoverEnterAndMoveDelayed.setPointerIdBits(pointerIdBits);
+ mSendHoverEnterAndMoveDelayed.setPolicyFlags(policyFlags);
+ mSendHoverEnterAndMoveDelayed.run();
+ mSendHoverEnterAndMoveDelayed.clear();
}
}
diff --git a/services/backup/backuplib/java/com/android/server/backup/TransportManager.java b/services/backup/backuplib/java/com/android/server/backup/TransportManager.java
index 21a22f44f3dd..930f49e4d117 100644
--- a/services/backup/backuplib/java/com/android/server/backup/TransportManager.java
+++ b/services/backup/backuplib/java/com/android/server/backup/TransportManager.java
@@ -16,6 +16,9 @@
package com.android.server.backup;
+import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
+import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
+
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.annotation.WorkerThread;
@@ -28,7 +31,6 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
-import android.os.Binder;
import android.os.Bundle;
import android.os.RemoteException;
import android.util.ArrayMap;
@@ -42,8 +44,8 @@ import com.android.internal.util.Preconditions;
import com.android.server.backup.transport.BackupTransportClient;
import com.android.server.backup.transport.OnTransportRegisteredListener;
import com.android.server.backup.transport.TransportConnection;
-import com.android.server.backup.transport.TransportConnectionManager;
import com.android.server.backup.transport.TransportConnectionListener;
+import com.android.server.backup.transport.TransportConnectionManager;
import com.android.server.backup.transport.TransportNotAvailableException;
import com.android.server.backup.transport.TransportNotRegisteredException;
import com.android.server.backup.transport.TransportStats;
@@ -58,6 +60,7 @@ import java.util.function.Predicate;
/** Handles in-memory bookkeeping of all BackupTransport objects. */
public class TransportManager {
private static final String TAG = "BackupTransportManager";
+ private static final boolean MORE_DEBUG = false;
@VisibleForTesting
public static final String SERVICE_ACTION_TRANSPORT_HOST = "android.backup.TRANSPORT_HOST";
@@ -130,14 +133,61 @@ public class TransportManager {
}
}
+ void onPackageEnabled(String packageName) {
+ onPackageAdded(packageName);
+ }
+
+ void onPackageDisabled(String packageName) {
+ onPackageRemoved(packageName);
+ }
+
@WorkerThread
void onPackageChanged(String packageName, String... components) {
+ // Determine if the overall package has changed and not just its
+ // components - see {@link EXTRA_CHANGED_COMPONENT_NAME_LIST}. When we
+ // know a package was enabled/disabled we'll handle that directly and
+ // not continue with onPackageChanged.
+ if (components.length == 1 && components[0].equals(packageName)) {
+ int enabled;
+ try {
+ enabled = mPackageManager.getApplicationEnabledSetting(packageName);
+ } catch (IllegalArgumentException ex) {
+ // packageName doesn't exist: likely due to a race with it being uninstalled.
+ if (MORE_DEBUG) {
+ Slog.d(TAG, "Package " + packageName + " was changed, but no longer exists.");
+ }
+ return;
+ }
+ switch (enabled) {
+ case COMPONENT_ENABLED_STATE_ENABLED: {
+ if (MORE_DEBUG) {
+ Slog.d(TAG, "Package " + packageName + " was enabled.");
+ }
+ onPackageEnabled(packageName);
+ return;
+ }
+ case COMPONENT_ENABLED_STATE_DISABLED: {
+ if (MORE_DEBUG) {
+ Slog.d(TAG, "Package " + packageName + " was disabled.");
+ }
+ onPackageDisabled(packageName);
+ return;
+ }
+ default: {
+ Slog.w(TAG, "Package " + packageName + " enabled setting: " + enabled);
+ return;
+ }
+ }
+ }
// Unfortunately this can't be atomic because we risk a deadlock if
// registerTransportsFromPackage() is put inside the synchronized block
Set<ComponentName> transportComponents = new ArraySet<>(components.length);
for (String componentName : components) {
transportComponents.add(new ComponentName(packageName, componentName));
}
+ if (transportComponents.isEmpty()) {
+ return;
+ }
synchronized (mTransportLock) {
mRegisteredTransportsDescriptionMap.keySet().removeIf(transportComponents::contains);
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 47f9fd535801..7054a8171572 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -16999,6 +16999,15 @@ public class ActivityManagerService extends IActivityManager.Stub
@Override
public void addPendingTopUid(int uid, int pid) {
mPendingStartActivityUids.add(uid, pid);
+
+ // If the next top activity is in cached and frozen mode, WM should raise its priority
+ // to unfreeze it. This is done by calling AMS.updateOomAdj that will lower its oom adj.
+ // However, WM cannot hold the AMS clock here so the updateOomAdj operation is performed
+ // in a separate thread asynchronously. Therefore WM can't guarantee AMS will unfreeze
+ // next top activity on time. This race will fail the following binder transactions WM
+ // sends to the activity. After this race issue between WM/ATMS and AMS is solved, this
+ // workaround can be removed. (b/213288355)
+ mOomAdjuster.mCachedAppOptimizer.unfreezeProcess(pid);
}
@Override
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index b18d390476f0..0c3b984aed73 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -183,6 +183,8 @@ public final class CachedAppOptimizer {
private final ActivityManagerGlobalLock mProcLock;
+ private final Object mFreezerLock = new Object();
+
private final OnPropertiesChangedListener mOnFlagsChangedListener =
new OnPropertiesChangedListener() {
@Override
@@ -949,8 +951,8 @@ public final class CachedAppOptimizer {
}
}
- @GuardedBy({"mAm", "mProcLock"})
- void unfreezeAppLSP(ProcessRecord app) {
+ @GuardedBy({"mAm", "mProcLock", "mFreezerLock"})
+ void unfreezeAppInternalLSP(ProcessRecord app) {
final int pid = app.getPid();
final ProcessCachedOptimizerRecord opt = app.mOptRecord;
if (opt.isPendingFreeze()) {
@@ -1035,6 +1037,42 @@ public final class CachedAppOptimizer {
}
}
+ @GuardedBy({"mAm", "mProcLock"})
+ void unfreezeAppLSP(ProcessRecord app) {
+ synchronized (mFreezerLock) {
+ unfreezeAppInternalLSP(app);
+ }
+ }
+
+ /**
+ * This quick function works around the race condition between WM/ATMS and AMS, allowing
+ * the former to directly unfreeze a frozen process before the latter runs updateOomAdj.
+ * After the race issue is solved, this workaround can be removed. (b/213288355)
+ * @param pid pid of the process to be unfrozen
+ */
+ @GuardedBy({"mFreezerLock"})
+ void unfreezeProcess(int pid) {
+ synchronized (mFreezerLock) {
+ ProcessRecord app = mFrozenProcesses.get(pid);
+ if (app == null) {
+ return;
+ }
+ Slog.i(TAG_AM, "quick sync unfreeze " + pid);
+ try {
+ freezeBinder(pid, false);
+ } catch (RuntimeException e) {
+ Slog.e(TAG_AM, "Unable to quick unfreeze binder for " + pid);
+ return;
+ }
+
+ try {
+ Process.setProcessFrozen(pid, app.uid, false);
+ } catch (Exception e) {
+ Slog.e(TAG_AM, "Unable to quick unfreeze " + pid);
+ }
+ }
+ }
+
/**
* To be called when the given app is killed.
*/
@@ -1464,8 +1502,6 @@ public final class CachedAppOptimizer {
return;
}
- Slog.d(TAG_AM, "froze " + pid + " " + name);
-
EventLog.writeEvent(EventLogTags.AM_FREEZE, pid, name);
// See above for why we're not taking mPhenotypeFlagLock here
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index b1c91ba4a79d..81a8680cdbf0 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -368,8 +368,7 @@ public final class PendingIntentRecord extends IIntentSender.Stub {
// Apply any launch flags from the ActivityOptions. This is to ensure that the caller
// can specify a consistent launch mode even if the PendingIntent is immutable
- final ActivityOptions opts = options != null ? ActivityOptions.fromBundle(options)
- : null;
+ final ActivityOptions opts = ActivityOptions.fromBundle(options);
if (opts != null) {
finalIntent.addFlags(opts.getPendingIntentLaunchFlags());
}
diff --git a/services/core/java/com/android/server/clipboard/EmulatorClipboardMonitor.java b/services/core/java/com/android/server/clipboard/EmulatorClipboardMonitor.java
index 11c451e01d4c..28c7cad3b184 100644
--- a/services/core/java/com/android/server/clipboard/EmulatorClipboardMonitor.java
+++ b/services/core/java/com/android/server/clipboard/EmulatorClipboardMonitor.java
@@ -54,8 +54,8 @@ class EmulatorClipboardMonitor implements Consumer<ClipData> {
return bits;
}
- private boolean isPipeOpened() {
- return mPipe != null;
+ private synchronized FileDescriptor getPipeFD() {
+ return mPipe;
}
private synchronized boolean openPipe() {
@@ -107,14 +107,16 @@ class EmulatorClipboardMonitor implements Consumer<ClipData> {
return msg;
}
- private void sendMessage(final byte[] msg) throws ErrnoException, InterruptedIOException {
+ private static void sendMessage(
+ final FileDescriptor fd,
+ final byte[] msg) throws ErrnoException, InterruptedIOException {
final byte[] lengthBits = new byte[4];
final ByteBuffer bb = ByteBuffer.wrap(lengthBits);
bb.order(ByteOrder.LITTLE_ENDIAN);
bb.putInt(msg.length);
- Os.write(mPipe, lengthBits, 0, lengthBits.length);
- Os.write(mPipe, msg, 0, msg.length);
+ Os.write(fd, lengthBits, 0, lengthBits.length);
+ Os.write(fd, msg, 0, msg.length);
}
EmulatorClipboardMonitor(final Consumer<ClipData> setAndroidClipboard) {
@@ -162,17 +164,22 @@ class EmulatorClipboardMonitor implements Consumer<ClipData> {
}
private void setHostClipboardImpl(final String value) {
- if (LOG_CLIBOARD_ACCESS) {
- Slog.i(TAG, "Setting the host clipboard to '" + value + "'");
- }
+ final FileDescriptor pipeFD = getPipeFD();
- try {
- if (isPipeOpened()) {
- sendMessage(value.getBytes());
- }
- } catch (ErrnoException | InterruptedIOException e) {
- Slog.e(TAG, "Failed to set host clipboard " + e.getMessage());
- } catch (IllegalArgumentException e) {
+ if (pipeFD != null) {
+ Thread t = new Thread(() -> {
+ if (LOG_CLIBOARD_ACCESS) {
+ Slog.i(TAG, "Setting the host clipboard to '" + value + "'");
+ }
+
+ try {
+ sendMessage(pipeFD, value.getBytes());
+ } catch (ErrnoException | InterruptedIOException e) {
+ Slog.e(TAG, "Failed to set host clipboard " + e.getMessage());
+ } catch (IllegalArgumentException e) {
+ }
+ });
+ t.start();
}
}
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index c0950bf13a1c..d249d578cf67 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -654,14 +654,17 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
protected int handleTextViewOn(HdmiCecMessage message) {
assertRunOnServiceThread();
- // Note that <Text View On> (and <Image View On>) command won't be handled here in
- // most cases. A dedicated microcontroller should be in charge while Android system
- // is in sleep mode, and the command need not be passed up to this service.
- // The only situation where the command reaches this handler is that sleep mode is
- // implemented in such a way that Android system is not really put to standby mode
- // but only the display is set to blank. Then the command leads to the effect of
+ // Note that if the device is in sleep mode, the <Text View On> (and <Image View On>)
+ // command won't be handled here in most cases. A dedicated microcontroller should be in
+ // charge while the Android system is in sleep mode, and the command doesn't need to be
+ // passed up to this service.
+ // The only situations where the command reaches this handler are
+ // 1. if sleep mode is implemented in such a way that Android system is not really put to
+ // standby mode but only the display is set to blank. Then the command leads to
// turning on the display by the invocation of PowerManager.wakeUp().
- if (mService.isPowerStandbyOrTransient() && getAutoWakeup()) {
+ // 2. if the device is in dream mode, not sleep mode. Then this command leads to
+ // waking up the device from dream mode by the invocation of PowerManager.wakeUp().
+ if (getAutoWakeup()) {
mService.wakeUp();
}
return Constants.HANDLED;
diff --git a/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java b/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java
index 6cb3b3b6740d..e6fd40902386 100644
--- a/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java
+++ b/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java
@@ -32,6 +32,7 @@ import android.view.inputmethod.InputBinding;
import android.view.inputmethod.InputMethodSubtype;
import com.android.internal.inputmethod.IInputMethodPrivilegedOperations;
+import com.android.internal.inputmethod.InputMethodNavButtonFlags;
import com.android.internal.view.IInlineSuggestionsRequestCallback;
import com.android.internal.view.IInputContext;
import com.android.internal.view.IInputMethod;
@@ -108,10 +109,10 @@ final class IInputMethodInvoker {
@AnyThread
void initializeInternal(IBinder token, IInputMethodPrivilegedOperations privOps,
int configChanges, boolean stylusHwSupported,
- boolean shouldShowImeSwitcherWhenImeIsShown) {
+ @InputMethodNavButtonFlags int navButtonFlags) {
try {
mTarget.initializeInternal(token, privOps, configChanges, stylusHwSupported,
- shouldShowImeSwitcherWhenImeIsShown);
+ navButtonFlags);
} catch (RemoteException e) {
logRemoteException(e);
}
@@ -147,20 +148,19 @@ final class IInputMethodInvoker {
@AnyThread
void startInput(IBinder startInputToken, IInputContext inputContext, EditorInfo attribute,
- boolean restarting, boolean shouldShowImeSwitcherWhenImeIsShown) {
+ boolean restarting, @InputMethodNavButtonFlags int navButtonFlags) {
try {
mTarget.startInput(startInputToken, inputContext, attribute, restarting,
- shouldShowImeSwitcherWhenImeIsShown);
+ navButtonFlags);
} catch (RemoteException e) {
logRemoteException(e);
}
}
@AnyThread
- void onShouldShowImeSwitcherWhenImeIsShownChanged(boolean shouldShowImeSwitcherWhenImeIsShown) {
+ void onNavButtonFlagsChanged(@InputMethodNavButtonFlags int navButtonFlags) {
try {
- mTarget.onShouldShowImeSwitcherWhenImeIsShownChanged(
- shouldShowImeSwitcherWhenImeIsShown);
+ mTarget.onNavButtonFlagsChanged(navButtonFlags);
} catch (RemoteException e) {
logRemoteException(e);
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 882e2e397d2c..1f9d08063714 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -156,6 +156,7 @@ import com.android.internal.inputmethod.IInputMethodPrivilegedOperations;
import com.android.internal.inputmethod.ImeTracing;
import com.android.internal.inputmethod.InputBindResult;
import com.android.internal.inputmethod.InputMethodDebug;
+import com.android.internal.inputmethod.InputMethodNavButtonFlags;
import com.android.internal.inputmethod.SoftInputShowHideReason;
import com.android.internal.inputmethod.StartInputFlags;
import com.android.internal.inputmethod.StartInputReason;
@@ -335,6 +336,10 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
@GuardedBy("ImfLock.class")
private final HandwritingModeController mHwController;
+ @GuardedBy("ImfLock.class")
+ @Nullable
+ private OverlayableSystemBooleanResourceWrapper mImeDrawsImeNavBarRes;
+
static class SessionState {
final ClientState client;
final IInputMethodInvoker method;
@@ -1723,11 +1728,43 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
}
@GuardedBy("ImfLock.class")
+ private void recreateImeDrawsImeNavBarResIfNecessary(@UserIdInt int targetUserId) {
+ // Currently, com.android.internal.R.bool.config_imeDrawsImeNavBar is overlaid only for the
+ // profile parent user.
+ // TODO(b/221443458): See if we can make OverlayManager be aware of profile groups.
+ final int profileParentUserId = mUserManagerInternal.getProfileParentId(targetUserId);
+ if (mImeDrawsImeNavBarRes != null
+ && mImeDrawsImeNavBarRes.getUserId() != profileParentUserId) {
+ mImeDrawsImeNavBarRes.close();
+ mImeDrawsImeNavBarRes = null;
+ }
+ if (mImeDrawsImeNavBarRes == null) {
+ final Context userContext;
+ if (mContext.getUserId() == profileParentUserId) {
+ userContext = mContext;
+ } else {
+ userContext = mContext.createContextAsUser(UserHandle.of(profileParentUserId),
+ 0 /* flags */);
+ }
+ mImeDrawsImeNavBarRes = OverlayableSystemBooleanResourceWrapper.create(userContext,
+ com.android.internal.R.bool.config_imeDrawsImeNavBar, mHandler, resource -> {
+ synchronized (ImfLock.class) {
+ if (resource == mImeDrawsImeNavBarRes) {
+ sendOnNavButtonFlagsChangedLocked();
+ }
+ }
+ });
+ }
+ }
+
+ @GuardedBy("ImfLock.class")
private void switchUserOnHandlerLocked(@UserIdInt int newUserId,
IInputMethodClient clientToBeReset) {
if (DEBUG) Slog.d(TAG, "Switching user stage 1/3. newUserId=" + newUserId
+ " currentUserId=" + mSettings.getCurrentUserId());
+ recreateImeDrawsImeNavBarResIfNecessary(newUserId);
+
// ContentObserver should be registered again when the user is changed
mSettingsObserver.registerContentObserverLocked(newUserId);
@@ -1832,6 +1869,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
});
}
+ recreateImeDrawsImeNavBarResIfNecessary(currentUserId);
+
mMyPackageMonitor.register(mContext, null, UserHandle.ALL, true);
mSettingsObserver.registerContentObserverLocked(currentUserId);
@@ -2412,12 +2451,12 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
true /* direct */);
}
- final boolean shouldShowImeSwitcherWhenImeIsShown =
- shouldShowImeSwitcherWhenImeIsShownLocked();
+ @InputMethodNavButtonFlags
+ final int navButtonFlags = getInputMethodNavButtonFlagsLocked();
final SessionState session = mCurClient.curSession;
setEnabledSessionLocked(session);
session.method.startInput(startInputToken, mCurInputContext, mCurAttribute, restarting,
- shouldShowImeSwitcherWhenImeIsShown);
+ navButtonFlags);
if (mShowRequested) {
if (DEBUG) Slog.v(TAG, "Attach new input asks to show input");
showCurrentInputLocked(mCurFocusedWindow, getAppShowFlagsLocked(), null,
@@ -2681,7 +2720,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
+ mCurTokenDisplayId);
}
inputMethod.initializeInternal(token, new InputMethodPrivilegedOperationsImpl(this, token),
- configChanges, supportStylusHw, shouldShowImeSwitcherWhenImeIsShownLocked());
+ configChanges, supportStylusHw, getInputMethodNavButtonFlagsLocked());
}
@AnyThread
@@ -2938,9 +2977,15 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
}
@GuardedBy("ImfLock.class")
- boolean shouldShowImeSwitcherWhenImeIsShownLocked() {
- return shouldShowImeSwitcherLocked(
+ @InputMethodNavButtonFlags
+ private int getInputMethodNavButtonFlagsLocked() {
+ final boolean canImeDrawsImeNavBar =
+ mImeDrawsImeNavBarRes != null && mImeDrawsImeNavBarRes.get();
+ final boolean shouldShowImeSwitcherWhenImeIsShown = shouldShowImeSwitcherLocked(
InputMethodService.IME_ACTIVE | InputMethodService.IME_VISIBLE);
+ return (canImeDrawsImeNavBar ? InputMethodNavButtonFlags.IME_DRAWS_IME_NAV_BAR : 0)
+ | (shouldShowImeSwitcherWhenImeIsShown
+ ? InputMethodNavButtonFlags.SHOW_IME_SWITCHER_WHEN_IME_IS_SHOWN : 0);
}
@GuardedBy("ImfLock.class")
@@ -3203,7 +3248,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
// the same enabled IMEs list.
mSwitchingController.resetCircularListLocked(mContext);
- sendShouldShowImeSwitcherWhenImeIsShownLocked();
+ sendOnNavButtonFlagsChangedLocked();
}
@GuardedBy("ImfLock.class")
@@ -4680,7 +4725,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
case MSG_HARD_KEYBOARD_SWITCH_CHANGED:
mMenuController.handleHardKeyboardStatusChange(msg.arg1 == 1);
synchronized (ImfLock.class) {
- sendShouldShowImeSwitcherWhenImeIsShownLocked();
+ sendOnNavButtonFlagsChangedLocked();
}
return true;
case MSG_SYSTEM_UNLOCK_USER: {
@@ -4950,7 +4995,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
// the same enabled IMEs list.
mSwitchingController.resetCircularListLocked(mContext);
- sendShouldShowImeSwitcherWhenImeIsShownLocked();
+ sendOnNavButtonFlagsChangedLocked();
// Notify InputMethodListListeners of the new installed InputMethods.
final List<InputMethodInfo> inputMethodList = new ArrayList<>(mMethodList);
@@ -4959,14 +5004,13 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
}
@GuardedBy("ImfLock.class")
- void sendShouldShowImeSwitcherWhenImeIsShownLocked() {
+ void sendOnNavButtonFlagsChangedLocked() {
final IInputMethodInvoker curMethod = mBindingController.getCurMethod();
if (curMethod == null) {
// No need to send the data if the IME is not yet bound.
return;
}
- curMethod.onShouldShowImeSwitcherWhenImeIsShownChanged(
- shouldShowImeSwitcherWhenImeIsShownLocked());
+ curMethod.onNavButtonFlagsChanged(getInputMethodNavButtonFlagsLocked());
}
@GuardedBy("ImfLock.class")
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java b/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java
index 98bde11ad517..c255fe14c03e 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java
@@ -203,7 +203,7 @@ final class InputMethodMenuController {
attrs.setTitle("Select input method");
w.setAttributes(attrs);
mService.updateSystemUiLocked();
- mService.sendShouldShowImeSwitcherWhenImeIsShownLocked();
+ mService.sendOnNavButtonFlagsChangedLocked();
mSwitchingDialog.show();
}
}
@@ -239,7 +239,7 @@ final class InputMethodMenuController {
mSwitchingDialogTitleView = null;
mService.updateSystemUiLocked();
- mService.sendShouldShowImeSwitcherWhenImeIsShownLocked();
+ mService.sendOnNavButtonFlagsChangedLocked();
mDialogBuilder = null;
mIms = null;
}
diff --git a/services/core/java/com/android/server/inputmethod/OverlayableSystemBooleanResourceWrapper.java b/services/core/java/com/android/server/inputmethod/OverlayableSystemBooleanResourceWrapper.java
new file mode 100644
index 000000000000..33e7a7621340
--- /dev/null
+++ b/services/core/java/com/android/server/inputmethod/OverlayableSystemBooleanResourceWrapper.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.inputmethod;
+
+import static android.content.Intent.ACTION_OVERLAY_CHANGED;
+
+import android.annotation.AnyThread;
+import android.annotation.BoolRes;
+import android.annotation.NonNull;
+import android.annotation.UserHandleAware;
+import android.annotation.UserIdInt;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.os.Handler;
+import android.os.PatternMatcher;
+import android.util.Slog;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Consumer;
+
+/**
+ * A wrapper object for any boolean resource defined in {@code "android"} package, in a way that is
+ * aware of per-user Runtime Resource Overlay (RRO).
+ */
+final class OverlayableSystemBooleanResourceWrapper implements AutoCloseable {
+ private static final String TAG = "OverlayableSystemBooleanResourceWrapper";
+
+ private static final String SYSTEM_PACKAGE_NAME = "android";
+
+ @UserIdInt
+ private final int mUserId;
+ @NonNull
+ private final AtomicBoolean mValueRef;
+ @NonNull
+ private final AtomicReference<Runnable> mCleanerRef;
+
+ /**
+ * Creates {@link OverlayableSystemBooleanResourceWrapper} for the given boolean resource ID
+ * with a value change callback for the user associated with the {@link Context}.
+ *
+ * @param userContext The {@link Context} to be used to access the resource. This needs to be
+ * associated with the right user because the Runtime Resource Overlay (RRO)
+ * is per-user configuration.
+ * @param boolResId The resource ID to be queried.
+ * @param handler {@link Handler} to be used to dispatch {@code callback}.
+ * @param callback The callback to be notified when the specified value might be updated.
+ * The callback needs to take care of spurious wakeup. The value returned from
+ * {@link #get()} may look to be exactly the same as the previously read value
+ * e.g. when the value is changed from {@code false} to {@code true} to
+ * {@code false} in a very short period of time, because {@link #get()} always
+ * does volatile-read.
+ * @return New {@link OverlayableSystemBooleanResourceWrapper}.
+ */
+ @NonNull
+ @UserHandleAware
+ static OverlayableSystemBooleanResourceWrapper create(@NonNull Context userContext,
+ @BoolRes int boolResId, @NonNull Handler handler,
+ @NonNull Consumer<OverlayableSystemBooleanResourceWrapper> callback) {
+
+ // Note that we cannot fully trust this initial value due to the dead time between obtaining
+ // the value here and setting up a broadcast receiver for change callback below.
+ // We will refresh the value again later after setting up the change callback anyway.
+ final AtomicBoolean valueRef = new AtomicBoolean(evaluate(userContext, boolResId));
+
+ final AtomicReference<Runnable> cleanerRef = new AtomicReference<>();
+
+ final OverlayableSystemBooleanResourceWrapper object =
+ new OverlayableSystemBooleanResourceWrapper(userContext.getUserId(), valueRef,
+ cleanerRef);
+
+ final IntentFilter intentFilter = new IntentFilter(ACTION_OVERLAY_CHANGED);
+ intentFilter.addDataScheme(IntentFilter.SCHEME_PACKAGE);
+ intentFilter.addDataSchemeSpecificPart(SYSTEM_PACKAGE_NAME, PatternMatcher.PATTERN_LITERAL);
+
+ final BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final boolean newValue = evaluate(userContext, boolResId);
+ if (newValue != valueRef.getAndSet(newValue)) {
+ callback.accept(object);
+ }
+ }
+ };
+ userContext.registerReceiver(broadcastReceiver, intentFilter,
+ null /* broadcastPermission */, handler,
+ Context.RECEIVER_NOT_EXPORTED);
+ cleanerRef.set(() -> userContext.unregisterReceiver(broadcastReceiver));
+
+ // Make sure that the initial observable value is obtained after the change callback is set.
+ valueRef.set(evaluate(userContext, boolResId));
+ return object;
+ }
+
+ private OverlayableSystemBooleanResourceWrapper(@UserIdInt int userId,
+ @NonNull AtomicBoolean valueRef, @NonNull AtomicReference<Runnable> cleanerRef) {
+ mUserId = userId;
+ mValueRef = valueRef;
+ mCleanerRef = cleanerRef;
+ }
+
+ /**
+ * @return The boolean resource value.
+ */
+ @AnyThread
+ boolean get() {
+ return mValueRef.get();
+ }
+
+ /**
+ * @return The user ID associated with this resource reader.
+ */
+ @AnyThread
+ @UserIdInt
+ int getUserId() {
+ return mUserId;
+ }
+
+ @AnyThread
+ private static boolean evaluate(@NonNull Context context, @BoolRes int boolResId) {
+ try {
+ return context.getPackageManager()
+ .getResourcesForApplication(SYSTEM_PACKAGE_NAME)
+ .getBoolean(boolResId);
+ } catch (PackageManager.NameNotFoundException e) {
+ Slog.e(TAG, "getResourcesForApplication(\"" + SYSTEM_PACKAGE_NAME + "\") failed", e);
+ return false;
+ }
+ }
+
+ /**
+ * Cleans up the callback.
+ */
+ @AnyThread
+ @Override
+ public void close() {
+ final Runnable cleaner = mCleanerRef.getAndSet(null);
+ if (cleaner != null) {
+ cleaner.run();
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index c4731aa7b522..d65dc13b72e3 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -3746,6 +3746,10 @@ public class NotificationManagerService extends SystemService {
if (!hadChannel && hasChannel && !hasRequestedNotificationPermission
&& startingTaskId != ActivityTaskManager.INVALID_TASK_ID) {
hasRequestedNotificationPermission = true;
+ if (mPermissionPolicyInternal == null) {
+ mPermissionPolicyInternal =
+ LocalServices.getService(PermissionPolicyInternal.class);
+ }
mHandler.post(new ShowNotificationPermissionPromptRunnable(pkg,
UserHandle.getUserId(uid), startingTaskId,
mPermissionPolicyInternal));
@@ -3764,19 +3768,7 @@ public class NotificationManagerService extends SystemService {
try {
int uid = mPackageManager.getPackageUid(pkg, 0,
UserHandle.getUserId(Binder.getCallingUid()));
- List<ActivityManager.AppTask> tasks = mAtm.getAppTasks(pkg, uid);
- for (int i = 0; i < tasks.size(); i++) {
- ActivityManager.RecentTaskInfo task = tasks.get(i).getTaskInfo();
- if (mPermissionPolicyInternal == null) {
- mPermissionPolicyInternal =
- LocalServices.getService(PermissionPolicyInternal.class);
- }
- if (mPermissionPolicyInternal != null
- && mPermissionPolicyInternal.canShowPermissionPromptForTask(task)) {
- taskId = task.taskId;
- break;
- }
- }
+ taskId = mAtm.getTaskToShowPermissionDialogOn(pkg, uid);
} catch (RemoteException e) {
// Do nothing
}
@@ -4068,13 +4060,12 @@ public class NotificationManagerService extends SystemService {
@Override
public ParceledListSlice<NotificationChannel> getNotificationChannelsBypassingDnd(
- String pkg, int userId) {
+ String pkg, int uid) {
checkCallerIsSystem();
- if (!areNotificationsEnabledForPackage(pkg,
- mPackageManagerInternal.getPackageUid(pkg, 0, userId))) {
+ if (!areNotificationsEnabledForPackage(pkg, uid)) {
return ParceledListSlice.emptyList();
}
- return mPreferencesHelper.getNotificationChannelsBypassingDnd(pkg, userId);
+ return mPreferencesHelper.getNotificationChannelsBypassingDnd(pkg, uid);
}
@Override
diff --git a/services/core/java/com/android/server/notification/PermissionHelper.java b/services/core/java/com/android/server/notification/PermissionHelper.java
index 6b9e374771ff..e551f1056b24 100644
--- a/services/core/java/com/android/server/notification/PermissionHelper.java
+++ b/services/core/java/com/android/server/notification/PermissionHelper.java
@@ -35,6 +35,7 @@ import android.util.ArrayMap;
import android.util.Pair;
import android.util.Slog;
+import com.android.internal.util.ArrayUtils;
import com.android.server.pm.permission.PermissionManagerServiceInternal;
import java.util.Collections;
@@ -178,13 +179,16 @@ public final class PermissionHelper {
boolean userSet, boolean reviewRequired) {
assertFlag();
final long callingId = Binder.clearCallingIdentity();
- // Do not change fixed permissions, and do not change non-user set permissions that are
- // granted by default, or granted by role.
- if (isPermissionFixed(packageName, userId)
- || (isPermissionGrantedByDefaultOrRole(packageName, userId) && !userSet)) {
- return;
- }
try {
+ // Do not change the permission if the package doesn't request it, do not change fixed
+ // permissions, and do not change non-user set permissions that are granted by default,
+ // or granted by role.
+ if (!packageRequestsNotificationPermission(packageName, userId)
+ || isPermissionFixed(packageName, userId)
+ || (isPermissionGrantedByDefaultOrRole(packageName, userId) && !userSet)) {
+ return;
+ }
+
boolean currentlyGranted = mPmi.checkPermission(packageName, NOTIFICATION_PERMISSION,
userId) != PackageManager.PERMISSION_DENIED;
if (grant && !reviewRequired && !currentlyGranted) {
@@ -278,6 +282,19 @@ public final class PermissionHelper {
}
}
+ private boolean packageRequestsNotificationPermission(String packageName,
+ @UserIdInt int userId) {
+ assertFlag();
+ try {
+ String[] permissions = mPackageManager.getPackageInfo(packageName, GET_PERMISSIONS,
+ userId).requestedPermissions;
+ return ArrayUtils.contains(permissions, NOTIFICATION_PERMISSION);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Could not reach system server", e);
+ }
+ return false;
+ }
+
private void assertFlag() {
if (!mMigrationEnabled) {
throw new IllegalStateException("Method called without checking flag value");
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 11858904a69a..1f7d65e6c604 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -1669,14 +1669,14 @@ public class PreferencesHelper implements RankingConfig {
}
/**
- * Gets all notification channels associated with the given pkg and userId that can bypass dnd
+ * Gets all notification channels associated with the given pkg and uid that can bypass dnd
*/
public ParceledListSlice<NotificationChannel> getNotificationChannelsBypassingDnd(String pkg,
- int userId) {
+ int uid) {
List<NotificationChannel> channels = new ArrayList<>();
synchronized (mPackagePreferences) {
final PackagePreferences r = mPackagePreferences.get(
- packagePreferencesKey(pkg, userId));
+ packagePreferencesKey(pkg, uid));
if (r != null) {
for (NotificationChannel channel : r.channels.values()) {
if (channelIsLiveLocked(r, channel) && channel.canBypassDnd()) {
diff --git a/services/core/java/com/android/server/notification/ZenModeFiltering.java b/services/core/java/com/android/server/notification/ZenModeFiltering.java
index 2a6dd8410acb..b0d40efed690 100644
--- a/services/core/java/com/android/server/notification/ZenModeFiltering.java
+++ b/services/core/java/com/android/server/notification/ZenModeFiltering.java
@@ -112,7 +112,7 @@ public class ZenModeFiltering {
}
if (zen == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS) {
if (consolidatedPolicy.allowRepeatCallers()
- && REPEAT_CALLERS.isRepeat(context, extras)) {
+ && REPEAT_CALLERS.isRepeat(context, extras, null)) {
ZenLog.traceMatchesCallFilter(true, "repeat caller");
return true;
}
@@ -229,7 +229,8 @@ public class ZenModeFiltering {
}
if (isCall(record)) {
if (policy.allowRepeatCallers()
- && REPEAT_CALLERS.isRepeat(mContext, extras(record))) {
+ && REPEAT_CALLERS.isRepeat(
+ mContext, extras(record), record.getPhoneNumbers())) {
ZenLog.traceNotIntercepted(record, "repeatCaller");
return false;
}
@@ -350,6 +351,9 @@ public class ZenModeFiltering {
private final ArrayMap<String, Long> mOtherCalls = new ArrayMap<>();
private int mThresholdMinutes;
+ // Record all people URIs in the extras bundle as well as the provided phoneNumbers set
+ // as callers. The phoneNumbers set is used to pass in any additional phone numbers
+ // associated with the people URIs as separately retrieved from contacts.
private synchronized void recordCall(Context context, Bundle extras,
ArraySet<String> phoneNumbers) {
setThresholdMinutes(context);
@@ -362,7 +366,13 @@ public class ZenModeFiltering {
recordCallers(extraPeople, phoneNumbers, now);
}
- private synchronized boolean isRepeat(Context context, Bundle extras) {
+ // Determine whether any people in the provided extras bundle or phone number set is
+ // a repeat caller. The extras bundle contains the people associated with a specific
+ // notification, and will suffice for most callers; the phoneNumbers array may be used
+ // to additionally check any specific phone numbers previously retrieved from contacts
+ // associated with the people in the extras bundle.
+ private synchronized boolean isRepeat(Context context, Bundle extras,
+ ArraySet<String> phoneNumbers) {
setThresholdMinutes(context);
if (mThresholdMinutes <= 0 || extras == null) return false;
final String[] extraPeople = ValidateNotificationPeople.getExtraPeople(extras);
@@ -370,7 +380,7 @@ public class ZenModeFiltering {
final long now = System.currentTimeMillis();
cleanUp(mTelCalls, now);
cleanUp(mOtherCalls, now);
- return checkCallers(context, extraPeople);
+ return checkCallers(context, extraPeople, phoneNumbers);
}
private synchronized void cleanUp(ArrayMap<String, Long> calls, long now) {
@@ -433,7 +443,31 @@ public class ZenModeFiltering {
}
}
- private synchronized boolean checkCallers(Context context, String[] people) {
+ // helper function to check mTelCalls array for a number, and also check its decoded
+ // version
+ private synchronized boolean checkForNumber(String number, String defaultCountryCode) {
+ if (mTelCalls.containsKey(number)) {
+ // check directly via map first
+ return true;
+ } else {
+ // see if a number that matches via areSameNumber exists
+ String numberToCheck = Uri.decode(number);
+ if (numberToCheck != null) {
+ for (String prev : mTelCalls.keySet()) {
+ if (PhoneNumberUtils.areSamePhoneNumber(
+ numberToCheck, prev, defaultCountryCode)) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ // Check whether anyone in the provided array of people URIs or phone number set matches a
+ // previously recorded phone call.
+ private synchronized boolean checkCallers(Context context, String[] people,
+ ArraySet<String> phoneNumbers) {
// get the default country code for checking telephone numbers
final String defaultCountryCode =
context.getSystemService(TelephonyManager.class).getNetworkCountryIso();
@@ -443,20 +477,8 @@ public class ZenModeFiltering {
final Uri uri = Uri.parse(person);
if ("tel".equals(uri.getScheme())) {
String number = uri.getSchemeSpecificPart();
- if (mTelCalls.containsKey(number)) {
- // check directly via map first
+ if (checkForNumber(number, defaultCountryCode)) {
return true;
- } else {
- // see if a number that matches via areSameNumber exists
- String numberToCheck = Uri.decode(number);
- if (numberToCheck != null) {
- for (String prev : mTelCalls.keySet()) {
- if (PhoneNumberUtils.areSamePhoneNumber(
- numberToCheck, prev, defaultCountryCode)) {
- return true;
- }
- }
- }
}
} else {
if (mOtherCalls.containsKey(person)) {
@@ -464,6 +486,17 @@ public class ZenModeFiltering {
}
}
}
+
+ // also check any passed-in phone numbers
+ if (phoneNumbers != null) {
+ for (String num : phoneNumbers) {
+ if (checkForNumber(num, defaultCountryCode)) {
+ return true;
+ }
+ }
+ }
+
+ // no matches
return false;
}
}
diff --git a/services/core/java/com/android/server/pm/IntentResolverInterceptor.java b/services/core/java/com/android/server/pm/IntentResolverInterceptor.java
new file mode 100644
index 000000000000..0ee07b650cf5
--- /dev/null
+++ b/services/core/java/com/android/server/pm/IntentResolverInterceptor.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import static com.android.server.wm.ActivityInterceptorCallback.INTENT_RESOLVER_ORDERED_ID;
+
+import android.Manifest;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.app.ActivityTaskManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.os.RemoteException;
+import android.provider.DeviceConfig;
+import android.util.Slog;
+
+import com.android.internal.R;
+import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
+import com.android.server.LocalServices;
+import com.android.server.wm.ActivityInterceptorCallback;
+import com.android.server.wm.ActivityInterceptorCallback.ActivityInterceptorInfo;
+import com.android.server.wm.ActivityTaskManagerInternal;
+
+/**
+ * Service to register an {@code ActivityInterceptorCallback} that modifies any {@code Intent}
+ * that's being used to launch a user-space {@code ChooserActivity}, by adding
+ * EXTRA_PERMISSION_TOKEN, a Binder representing a single-use-only permission to invoke the
+ * #startActivityAsCaller() API (which normally isn't available in user-space); and setting the
+ * destination component to the delegated component when appropriate.
+ */
+public final class IntentResolverInterceptor {
+ private static final String TAG = "IntentResolverIntercept";
+
+ private final Context mContext;
+ private boolean mUseDelegateChooser;
+
+ private final ActivityInterceptorCallback mActivityInterceptorCallback =
+ new ActivityInterceptorCallback() {
+ @Nullable
+ @Override
+ public ActivityInterceptResult intercept(ActivityInterceptorInfo info) {
+ if (mUseDelegateChooser && isChooserActivity(info)) {
+ return new ActivityInterceptResult(
+ modifyChooserIntent(info.intent),
+ info.checkedOptions);
+ }
+ return null;
+ }
+ };
+
+ public IntentResolverInterceptor(Context context) {
+ mContext = context;
+ }
+
+ /**
+ * Start listening for intents and USE_DELEGATE_CHOOSER property changes.
+ */
+ @RequiresPermission(Manifest.permission.READ_DEVICE_CONFIG)
+ public void registerListeners() {
+ LocalServices.getService(ActivityTaskManagerInternal.class)
+ .registerActivityStartInterceptor(INTENT_RESOLVER_ORDERED_ID,
+ mActivityInterceptorCallback);
+
+ DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI,
+ mContext.getMainExecutor(), properties -> updateUseDelegateChooser());
+ updateUseDelegateChooser();
+ }
+
+ @RequiresPermission(Manifest.permission.READ_DEVICE_CONFIG)
+ private void updateUseDelegateChooser() {
+ mUseDelegateChooser = DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_SYSTEMUI,
+ SystemUiDeviceConfigFlags.USE_DELEGATE_CHOOSER,
+ false);
+ }
+
+ private Intent modifyChooserIntent(Intent intent) {
+ intent.setComponent(getUnbundledChooserComponentName());
+ addStartActivityPermissionTokenToIntent(intent, getUnbundledChooserComponentName());
+ return intent;
+ }
+
+ private static boolean isChooserActivity(ActivityInterceptorInfo info) {
+ ComponentName targetComponent = new ComponentName(info.aInfo.packageName, info.aInfo.name);
+
+ return targetComponent.equals(getSystemChooserComponentName())
+ || targetComponent.equals(getUnbundledChooserComponentName());
+ }
+
+ private static Intent addStartActivityPermissionTokenToIntent(
+ Intent intent, ComponentName grantee) {
+ try {
+ intent.putExtra(
+ ActivityTaskManager.EXTRA_PERMISSION_TOKEN,
+ ActivityTaskManager.getService().requestStartActivityPermissionToken(grantee));
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to add permission token to chooser intent");
+ }
+ return intent;
+ }
+
+ private static ComponentName getSystemChooserComponentName() {
+ return new ComponentName("android", "com.android.internal.app.ChooserActivity");
+ }
+
+ private static ComponentName getUnbundledChooserComponentName() {
+ return ComponentName.unflattenFromString(
+ Resources.getSystem().getString(R.string.config_chooserActivity));
+ }
+}
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 6b3ce773fb63..e9896617d8e8 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -1109,13 +1109,11 @@ public class LauncherAppsService extends SystemService {
// Note the target activity doesn't have to be exported.
// Flag for bubble
- if (startActivityOptions != null) {
- ActivityOptions options = ActivityOptions.fromBundle(startActivityOptions);
- if (options.isApplyActivityFlagsForBubbles()) {
- // Flag for bubble to make behaviour match documentLaunchMode=always.
- intents[0].addFlags(FLAG_ACTIVITY_NEW_DOCUMENT);
- intents[0].addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
- }
+ ActivityOptions options = ActivityOptions.fromBundle(startActivityOptions);
+ if (options != null && options.isApplyActivityFlagsForBubbles()) {
+ // Flag for bubble to make behaviour match documentLaunchMode=always.
+ intents[0].addFlags(FLAG_ACTIVITY_NEW_DOCUMENT);
+ intents[0].addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
}
intents[0].addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index f07418f6007f..a70cff98ed93 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -958,6 +958,7 @@ public class PackageManagerService extends IPackageManager.Stub
private final ResolveIntentHelper mResolveIntentHelper;
private final DexOptHelper mDexOptHelper;
private final SuspendPackageHelper mSuspendPackageHelper;
+ private final IntentResolverInterceptor mIntentResolverInterceptor;
/**
* Invalidate the package info cache, which includes updating the cached computer.
@@ -1712,6 +1713,8 @@ public class PackageManagerService extends IPackageManager.Stub
mSharedLibraries.setDeletePackageHelper(mDeletePackageHelper);
+ mIntentResolverInterceptor = null;
+
registerObservers(false);
invalidatePackageInfoCache();
}
@@ -2240,6 +2243,8 @@ public class PackageManagerService extends IPackageManager.Stub
mServiceStartWithDelay = SystemClock.uptimeMillis() + (60 * 1000L);
+ mIntentResolverInterceptor = new IntentResolverInterceptor(mContext);
+
Slog.i(TAG, "Fix for b/169414761 is applied");
}
@@ -6397,6 +6402,11 @@ public class PackageManagerService extends IPackageManager.Stub
// Prune unused static shared libraries which have been cached a period of time
schedulePruneUnusedStaticSharedLibraries(false /* delay */);
+
+ // TODO(b/222706900): Remove this intent interceptor before T launch
+ if (mIntentResolverInterceptor != null) {
+ mIntentResolverInterceptor.registerListeners();
+ }
}
/**
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
index 9961ae51b92d..90842619c31e 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
@@ -1403,7 +1403,7 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt
}
} else {
if (ps.getUserStateOrDefault(userId).isInstantApp() && !bp.isInstant()) {
- throw new SecurityException("Cannot grant non-ephemeral permission" + permName
+ throw new SecurityException("Cannot grant non-ephemeral permission " + permName
+ " for package " + packageName);
}
@@ -3532,8 +3532,9 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt
final Boolean granted =
SystemConfig.getInstance().getOemPermissions(pkg.getPackageName()).get(permission);
if (granted == null) {
- throw new IllegalStateException("OEM permission" + permission + " requested by package "
- + pkg.getPackageName() + " must be explicitly declared granted or not");
+ throw new IllegalStateException("OEM permission " + permission
+ + " requested by package " + pkg.getPackageName()
+ + " must be explicitly declared granted or not");
}
return Boolean.TRUE == granted;
}
diff --git a/services/core/java/com/android/server/policy/PermissionPolicyInternal.java b/services/core/java/com/android/server/policy/PermissionPolicyInternal.java
index 20b7ccd39287..92b9944b74cf 100644
--- a/services/core/java/com/android/server/policy/PermissionPolicyInternal.java
+++ b/services/core/java/com/android/server/policy/PermissionPolicyInternal.java
@@ -57,6 +57,7 @@ public abstract class PermissionPolicyInternal {
* prompt should be shown if the app targets S-, is currently running in a visible, focused
* task, has the REVIEW_REQUIRED flag set on its implicit notification permission, and has
* created at least one notification channel (even if it has since been deleted).
+ *
* @param packageName The package whose permission is being checked
* @param userId The user for whom the package is being started
* @param taskId The task the notification prompt should be attached to
@@ -66,10 +67,22 @@ public abstract class PermissionPolicyInternal {
/**
* Determine if a particular task is in the proper state to show a system-triggered permission
- * prompt. A prompt can be shown if the task is focused, visible, and running.
+ * prompt. A prompt can be shown if the task is focused, visible, and running and
+ * 1. The intent is a launcher intent (action is ACTION_MAIN, category is LAUNCHER), or
+ * 2. The activity belongs to the same package as the one which launched the task originally,
+ * and the task was started with a launcher intent
+ *
* @param taskInfo The task to be checked
+ * @param currPkg The package of the current top visible activity
+ * @param intent The intent of the current top visible activity
+ */
+ public abstract boolean shouldShowNotificationDialogForTask(@Nullable TaskInfo taskInfo,
+ @Nullable String currPkg, @Nullable Intent intent);
+
+ /**
+ * @return true if an intent will resolve to a permission request dialog activity
*/
- public abstract boolean canShowPermissionPromptForTask(@Nullable TaskInfo taskInfo);
+ public abstract boolean isIntentToPermissionDialog(@NonNull Intent intent);
/**
* @return Whether the policy is initialized for a user.
diff --git a/services/core/java/com/android/server/policy/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java
index f2ce0d4c49d3..70ef3d364277 100644
--- a/services/core/java/com/android/server/policy/PermissionPolicyService.java
+++ b/services/core/java/com/android/server/policy/PermissionPolicyService.java
@@ -21,6 +21,8 @@ import static android.app.AppOpsManager.MODE_ALLOWED;
import static android.app.AppOpsManager.MODE_FOREGROUND;
import static android.app.AppOpsManager.MODE_IGNORED;
import static android.app.AppOpsManager.OP_NONE;
+import static android.content.pm.PackageManager.ACTION_REQUEST_PERMISSIONS;
+import static android.content.pm.PackageManager.ACTION_REQUEST_PERMISSIONS_FOR_OTHER;
import static android.content.pm.PackageManager.FLAG_PERMISSION_APPLY_RESTRICTION;
import static android.content.pm.PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED;
import static android.content.pm.PackageManager.FLAG_PERMISSION_REVOKED_COMPAT;
@@ -54,6 +56,8 @@ import android.content.pm.PackageManagerInternal.PackageListObserver;
import android.content.pm.PermissionInfo;
import android.os.Build;
import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -109,6 +113,7 @@ public final class PermissionPolicyService extends SystemService {
private static final String SYSTEM_PKG = "android";
private static final boolean DEBUG = false;
private static final long USER_SENSITIVE_UPDATE_DELAY_MS = 60000;
+ private static final long ACTIVITY_START_DELAY_MS = 200;
private final Object mLock = new Object();
@@ -149,6 +154,7 @@ public final class PermissionPolicyService extends SystemService {
private List<String> mAppOpPermissions;
private Context mContext;
+ private Handler mHandler;
private PackageManagerInternal mPackageManagerInternal;
private NotificationManagerInternal mNotificationManager;
private final KeyguardManager mKeyguardManager;
@@ -158,6 +164,7 @@ public final class PermissionPolicyService extends SystemService {
super(context);
mContext = context;
+ mHandler = new Handler(Looper.getMainLooper());
mPackageManager = context.getPackageManager();
mKeyguardManager = context.getSystemService(KeyguardManager.class);
LocalServices.addService(PermissionPolicyInternal.class, new Internal());
@@ -1016,7 +1023,7 @@ public final class PermissionPolicyService extends SystemService {
ActivityInterceptorInfo info) {
String action = info.intent.getAction();
ActivityInterceptResult result = null;
- if (!PackageManager.ACTION_REQUEST_PERMISSIONS_FOR_OTHER.equals(action)
+ if (!ACTION_REQUEST_PERMISSIONS_FOR_OTHER.equals(action)
&& !PackageManager.ACTION_REQUEST_PERMISSIONS.equals(action)) {
return null;
}
@@ -1033,7 +1040,7 @@ public final class PermissionPolicyService extends SystemService {
&& !mContinueNotifGrantMessageUids.contains(info.realCallingUid)) {
return result;
}
- if (PackageManager.ACTION_REQUEST_PERMISSIONS_FOR_OTHER.equals(action)) {
+ if (ACTION_REQUEST_PERMISSIONS_FOR_OTHER.equals(action)) {
String otherPkg = info.intent.getStringExtra(Intent.EXTRA_PACKAGE_NAME);
if (otherPkg == null || (mPackageManager.getPermissionFlags(
POST_NOTIFICATIONS, otherPkg, UserHandle.of(info.userId))
@@ -1052,8 +1059,8 @@ public final class PermissionPolicyService extends SystemService {
public void onActivityLaunched(TaskInfo taskInfo, ActivityInfo activityInfo,
ActivityInterceptorInfo info) {
super.onActivityLaunched(taskInfo, activityInfo, info);
- if (!shouldShowNotificationDialogOrClearFlags(info.intent,
- info.checkedOptions)) {
+ if (!shouldShowNotificationDialogOrClearFlags(taskInfo,
+ activityInfo.packageName, info.intent, info.checkedOptions, true)) {
return;
}
UserHandle user = UserHandle.of(taskInfo.userId);
@@ -1085,7 +1092,7 @@ public final class PermissionPolicyService extends SystemService {
return false;
}
- if (PackageManager.ACTION_REQUEST_PERMISSIONS_FOR_OTHER.equals(intent.getAction())
+ if (ACTION_REQUEST_PERMISSIONS_FOR_OTHER.equals(intent.getAction())
&& (callingUid != Process.SYSTEM_UID || !SYSTEM_PKG.equals(callingPackage))) {
return false;
}
@@ -1104,18 +1111,48 @@ public final class PermissionPolicyService extends SystemService {
launchNotificationPermissionRequestDialog(packageName, user, taskId);
}
+ @Override
+ public boolean isIntentToPermissionDialog(@NonNull Intent intent) {
+ return Objects.equals(intent.getPackage(),
+ mPackageManager.getPermissionControllerPackageName())
+ && (Objects.equals(intent.getAction(), ACTION_REQUEST_PERMISSIONS_FOR_OTHER)
+ || Objects.equals(intent.getAction(), ACTION_REQUEST_PERMISSIONS));
+ }
+
+ @Override
+ public boolean shouldShowNotificationDialogForTask(TaskInfo taskInfo, String currPkg,
+ Intent intent) {
+ return shouldShowNotificationDialogOrClearFlags(
+ taskInfo, currPkg, intent, null, false);
+ }
+
/**
- * Determine if we should show a notification dialog, or clear the REVIEW_REQUIRED flag,
- * from a particular package for a particular intent. Returns true if:
+ * Determine if a particular task is in the proper state to show a system-triggered
+ * permission prompt. A prompt can be shown if the task is just starting, or the task is
+ * currently focused, visible, and running, and,
* 1. The isEligibleForLegacyPermissionPrompt ActivityOption is set, or
- * 2. The intent is a launcher intent (action is ACTION_MAIN, category is LAUNCHER)
+ * 2. The intent is a launcher intent (action is ACTION_MAIN, category is LAUNCHER), or
+ * 3. The activity belongs to the same package as the one which launched the task
+ * originally, and the task was started with a launcher intent
+ * @param taskInfo The task to be checked
+ * @param currPkg The package of the current top visible activity
+ * @param intent The intent of the current top visible activity
*/
- private boolean shouldShowNotificationDialogOrClearFlags(Intent intent,
- ActivityOptions options) {
- if ((options != null && options.isEligibleForLegacyPermissionPrompt())) {
- return true;
+ private boolean shouldShowNotificationDialogOrClearFlags(TaskInfo taskInfo, String currPkg,
+ Intent intent, ActivityOptions options, boolean activityStart) {
+ if (intent == null || currPkg == null || taskInfo == null
+ || (!(taskInfo.isFocused && taskInfo.isVisible && taskInfo.isRunning)
+ && !activityStart)) {
+ return false;
}
+ return isLauncherIntent(intent)
+ || (options != null && options.isEligibleForLegacyPermissionPrompt())
+ || (currPkg.equals(taskInfo.baseActivity.getPackageName())
+ && isLauncherIntent(taskInfo.baseIntent));
+ }
+
+ private boolean isLauncherIntent(Intent intent) {
return Intent.ACTION_MAIN.equals(intent.getAction())
&& intent.getCategories() != null
&& (intent.getCategories().contains(Intent.CATEGORY_LAUNCHER)
@@ -1144,14 +1181,15 @@ public final class PermissionPolicyService extends SystemService {
Intent grantPermission = mPackageManager
.buildRequestPermissionsIntent(new String[] { POST_NOTIFICATIONS });
grantPermission.setAction(
- PackageManager.ACTION_REQUEST_PERMISSIONS_FOR_OTHER);
+ ACTION_REQUEST_PERMISSIONS_FOR_OTHER);
grantPermission.putExtra(Intent.EXTRA_PACKAGE_NAME, pkgName);
ActivityOptions options = new ActivityOptions(new Bundle());
options.setTaskOverlay(true, false);
options.setLaunchTaskId(taskId);
try {
- mContext.startActivityAsUser(grantPermission, options.toBundle(), user);
+ mHandler.postDelayed(() -> mContext.startActivityAsUser(
+ grantPermission, options.toBundle(), user), ACTIVITY_START_DELAY_MS);
} catch (Exception e) {
Log.e(LOG_TAG, "couldn't start grant permission dialog"
+ "for other package " + pkgName, e);
@@ -1170,12 +1208,6 @@ public final class PermissionPolicyService extends SystemService {
}
}
- @Override
- public boolean canShowPermissionPromptForTask(@Nullable TaskInfo taskInfo) {
- return taskInfo != null && taskInfo.isFocused && taskInfo.isVisible
- && taskInfo.isRunning;
- }
-
/**
* Check if the intent action is removed for the calling package (often based on target SDK
* version). If the action is removed, we'll silently cancel the activity launch.
diff --git a/services/core/java/com/android/server/vibrator/VibrationSettings.java b/services/core/java/com/android/server/vibrator/VibrationSettings.java
index b05b44bcb1d2..77da75118958 100644
--- a/services/core/java/com/android/server/vibrator/VibrationSettings.java
+++ b/services/core/java/com/android/server/vibrator/VibrationSettings.java
@@ -355,8 +355,10 @@ final class VibrationSettings {
}
}
- if (!shouldVibrateForRingerModeLocked(usage)) {
- return Vibration.Status.IGNORED_FOR_RINGER_MODE;
+ if (!attrs.isFlagSet(VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY)) {
+ if (!shouldVibrateForRingerModeLocked(usage)) {
+ return Vibration.Status.IGNORED_FOR_RINGER_MODE;
+ }
}
}
return null;
@@ -386,12 +388,12 @@ final class VibrationSettings {
* Return {@code true} if the device should vibrate for current ringer mode.
*
* <p>This checks the current {@link AudioManager#getRingerModeInternal()} against user settings
- * for ringtone usage only. All other usages are allowed by this method.
+ * for ringtone and notification usages. All other usages are allowed by this method.
*/
@GuardedBy("mLock")
private boolean shouldVibrateForRingerModeLocked(@VibrationAttributes.Usage int usageHint) {
- if (usageHint != USAGE_RINGTONE) {
- // Only ringtone vibrations are disabled when phone is on silent mode.
+ if ((usageHint != USAGE_RINGTONE) && (usageHint != USAGE_NOTIFICATION)) {
+ // Only ringtone and notification vibrations are disabled when phone is on silent mode.
return true;
}
// If audio manager was not loaded yet then assume most restrictive mode.
diff --git a/services/core/java/com/android/server/wm/ActivityInterceptorCallback.java b/services/core/java/com/android/server/wm/ActivityInterceptorCallback.java
index 06c58baee1f9..1d65cbb70ffe 100644
--- a/services/core/java/com/android/server/wm/ActivityInterceptorCallback.java
+++ b/services/core/java/com/android/server/wm/ActivityInterceptorCallback.java
@@ -58,6 +58,7 @@ public abstract class ActivityInterceptorCallback {
@IntDef(suffix = { "_ORDERED_ID" }, value = {
FIRST_ORDERED_ID,
PERMISSION_POLICY_ORDERED_ID,
+ INTENT_RESOLVER_ORDERED_ID,
VIRTUAL_DEVICE_SERVICE_ORDERED_ID,
LAST_ORDERED_ID // Update this when adding new ids
})
@@ -75,6 +76,11 @@ public abstract class ActivityInterceptorCallback {
public static final int PERMISSION_POLICY_ORDERED_ID = 1;
/**
+ * The identifier for {@link com.android.server.pm.IntentResolverInterceptor}.
+ */
+ public static final int INTENT_RESOLVER_ORDERED_ID = 2;
+
+ /**
* The identifier for {@link com.android.server.companion.virtual.VirtualDeviceManagerService}
* interceptor.
*/
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index cecfccd1f836..01dfb91d12be 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -680,4 +680,15 @@ public abstract class ActivityTaskManagerInternal {
/** Get the app tasks for a package */
public abstract List<ActivityManager.AppTask> getAppTasks(String pkgName, int uid);
+
+ /**
+ * Determine if there exists a task which meets the criteria set by the PermissionPolicyService
+ * to show a system-owned permission dialog over, for a given package
+ * @see PermissionPolicyInternal.shouldShowNotificationDialogForTask
+ *
+ * @param pkgName The package whose activity must be top
+ * @param uid The uid that must have a top activity
+ * @return a task ID if a valid task ID is found. Otherwise, return INVALID_TASK_ID
+ */
+ public abstract int getTaskToShowPermissionDialogOn(String pkgName, int uid);
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index bfccdf97c680..6d8b3b1d63dd 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -6754,5 +6754,13 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
return tasks;
}
+
+ @Override
+ public int getTaskToShowPermissionDialogOn(String pkgName, int uid) {
+ synchronized (ActivityTaskManagerService.this.mGlobalLock) {
+ return ActivityTaskManagerService.this.mRootWindowContainer
+ .getTaskToShowPermissionDialogOn(pkgName, uid);
+ }
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/AsyncRotationController.java b/services/core/java/com/android/server/wm/AsyncRotationController.java
index 9e889ad11b8e..220d9ec8febb 100644
--- a/services/core/java/com/android/server/wm/AsyncRotationController.java
+++ b/services/core/java/com/android/server/wm/AsyncRotationController.java
@@ -329,8 +329,12 @@ class AsyncRotationController extends FadeAnimationController implements Consume
void hideImmediately(WindowToken windowToken) {
final boolean original = mHideImmediately;
mHideImmediately = true;
+ final Operation op = new Operation(Operation.ACTION_FADE);
+ mTargetWindowTokens.put(windowToken, op);
fadeWindowToken(false /* show */, windowToken, ANIMATION_TYPE_FIXED_TRANSFORM);
+ op.mLeash = windowToken.getAnimationLeash();
mHideImmediately = original;
+ if (DEBUG) Slog.d(TAG, "hideImmediately " + windowToken.getTopChild());
}
/** Returns {@code true} if the window will rotate independently. */
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index f65b17db1e10..4b824483bffd 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -149,6 +149,7 @@ import com.android.server.LocalServices;
import com.android.server.am.ActivityManagerService;
import com.android.server.am.AppTimeTracker;
import com.android.server.am.UserState;
+import com.android.server.policy.PermissionPolicyInternal;
import com.android.server.policy.WindowManagerPolicy;
import java.io.FileDescriptor;
@@ -3332,6 +3333,36 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
}
/**
+ * Iterate over all task fragments, to see if there exists one that meets the
+ * PermissionPolicyService's criteria to show a permission dialog.
+ */
+ public int getTaskToShowPermissionDialogOn(String pkgName, int uid) {
+ PermissionPolicyInternal pPi = mService.getPermissionPolicyInternal();
+ if (pPi == null) {
+ return INVALID_TASK_ID;
+ }
+
+ final int[] validTaskId = {INVALID_TASK_ID};
+ forAllLeafTaskFragments(fragment -> {
+ ActivityRecord record = fragment.getActivity((r) -> {
+ // skip hidden (or about to hide) apps, or the permission dialog
+ return r.canBeTopRunning() && r.isVisibleRequested()
+ && !pPi.isIntentToPermissionDialog(r.intent);
+ });
+ if (record != null && record.isUid(uid)
+ && Objects.equals(pkgName, record.packageName)
+ && pPi.shouldShowNotificationDialogForTask(record.getTask().getTaskInfo(),
+ pkgName, record.intent)) {
+ validTaskId[0] = record.getTask().mTaskId;
+ return true;
+ }
+ return false;
+ });
+
+ return validTaskId[0];
+ }
+
+ /**
* Dumps the activities matching the given {@param name} in the either the focused root task
* or all visible root tasks if {@param dumpVisibleRootTasksOnly} is true.
*/
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 7fe34f47c53c..923ac41cec77 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -504,13 +504,6 @@ class Task extends TaskFragment {
*/
boolean mInRemoveTask;
- // When non-null, this is a transaction that will get applied on the next frame returned after
- // a relayout is requested from the client. While this is only valid on a leaf task; since the
- // transaction can effect an ancestor task, this also needs to keep track of the ancestor task
- // that this transaction manipulates because deferUntilFrame acts on individual surfaces.
- SurfaceControl.Transaction mMainWindowSizeChangeTransaction;
- Task mMainWindowSizeChangeTask;
-
private final AnimatingActivityRegistry mAnimatingActivityRegistry =
new AnimatingActivityRegistry();
@@ -4390,17 +4383,16 @@ class Task extends TaskFragment {
leaf.setMainWindowSizeChangeTransaction(t, origin);
return;
}
- mMainWindowSizeChangeTransaction = t;
- mMainWindowSizeChangeTask = t == null ? null : origin;
- }
-
- SurfaceControl.Transaction getMainWindowSizeChangeTransaction() {
- return mMainWindowSizeChangeTransaction;
+ final WindowState w = getTopVisibleAppMainWindow();
+ if (w != null) {
+ w.applyWithNextDraw((d) -> {
+ d.merge(t);
+ });
+ } else {
+ t.apply();
+ }
}
- Task getMainWindowSizeChangeTask() {
- return mMainWindowSizeChangeTask;
- }
void setActivityWindowingMode(int windowingMode) {
PooledConsumer c = PooledLambda.obtainConsumer(ActivityRecord::setWindowingMode,
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 3dad2ba40627..db687f627bfa 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -5988,15 +5988,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
finishDrawing(null);
mWmService.mH.removeMessages(WINDOW_STATE_BLAST_SYNC_TIMEOUT, this);
if (!useBLASTSync()) return;
-
- final Task task = getTask();
- if (task != null) {
- final SurfaceControl.Transaction t = task.getMainWindowSizeChangeTransaction();
- if (t != null) {
- mSyncTransaction.merge(t);
- }
- task.setMainWindowSizeChangeTransaction(null);
- }
}
@Override
@@ -6033,10 +6024,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
if (mRedrawForSyncReported) {
return false;
}
- final Task task = getTask();
- if (task != null && task.getMainWindowSizeChangeTransaction() != null) {
- return true;
- }
return useBLASTSync();
}
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index c17961e8a564..285a6d5bdf5f 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -442,50 +442,6 @@ class WindowStateAnimator {
return mService.useBLASTSync() && mWin.useBLASTSync();
}
- private boolean shouldConsumeMainWindowSizeTransaction() {
- // We only consume the transaction when the client is calling relayout
- // because this is the only time we know the frameNumber will be valid
- // due to the client renderer being paused. Put otherwise, only when
- // mInRelayout is true can we guarantee the next frame will contain
- // the most recent configuration.
- if (!mWin.mInRelayout) return false;
- // Since we can only do this for one window, we focus on the main application window
- if (mAttrType != TYPE_BASE_APPLICATION) return false;
- final Task task = mWin.getTask();
- if (task == null) return false;
- if (task.getMainWindowSizeChangeTransaction() == null) return false;
- // Likewise we only focus on the task root, since we can only use one window
- if (!mWin.mActivityRecord.isRootOfTask()) return false;
- return true;
- }
-
- void setSurfaceBoundariesLocked(SurfaceControl.Transaction t) {
- if (mSurfaceController == null) {
- return;
- }
-
- final WindowState w = mWin;
- final Task task = w.getTask();
- if (shouldConsumeMainWindowSizeTransaction()) {
- if (isInBlastSync()) {
- // If we're in a sync transaction, there's no need to call defer transaction.
- // The sync transaction will contain the buffer so the bounds change transaction
- // will only be applied with the buffer.
- t.merge(task.getMainWindowSizeChangeTransaction());
- task.setMainWindowSizeChangeTransaction(null);
- } else {
- mWin.applyWithNextDraw(finishedFrame -> {
- final SurfaceControl.Transaction sizeChangedTransaction =
- task.getMainWindowSizeChangeTransaction();
- if (sizeChangedTransaction != null) {
- finishedFrame.merge(sizeChangedTransaction);
- task.setMainWindowSizeChangeTransaction(null);
- }
- });
- }
- }
- }
-
void prepareSurfaceLocked(SurfaceControl.Transaction t) {
final WindowState w = mWin;
if (!hasSurface()) {
@@ -501,8 +457,6 @@ class WindowStateAnimator {
computeShownFrameLocked();
- setSurfaceBoundariesLocked(t);
-
if (w.isParentWindowHidden() || !w.isOnScreen()) {
hide(t, "prepareSurfaceLocked");
mWallpaperControllerLocked.hideWallpapers(w);
diff --git a/services/robotests/backup/src/com/android/server/backup/TransportManagerTest.java b/services/robotests/backup/src/com/android/server/backup/TransportManagerTest.java
index b7f8c00896d4..8a9845b1c2d2 100644
--- a/services/robotests/backup/src/com/android/server/backup/TransportManagerTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/TransportManagerTest.java
@@ -16,6 +16,10 @@
package com.android.server.backup;
+import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
+import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
+import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
+
import static com.android.server.backup.testing.TransportData.genericTransport;
import static com.android.server.backup.testing.TransportTestUtils.mockTransport;
import static com.android.server.backup.testing.TransportTestUtils.setUpTransportsForTransportManager;
@@ -312,6 +316,86 @@ public class TransportManagerTest {
}
@Test
+ public void testOnPackageChanged_whenPackageChanged_packageDisabledUnregistersTransport()
+ throws Exception {
+ TransportManager transportManager =
+ createTransportManagerWithRegisteredTransports(mTransportA1, mTransportB1);
+ reset(mListener);
+
+ mContext.getPackageManager()
+ .setApplicationEnabledSetting(
+ PACKAGE_A,
+ Integer.valueOf(COMPONENT_ENABLED_STATE_DISABLED),
+ 0 /*flags*/);
+ transportManager.onPackageChanged(PACKAGE_A, PACKAGE_A);
+
+ assertRegisteredTransports(transportManager, singletonList(mTransportB1));
+ verify(mListener, never()).onTransportRegistered(any(), any());
+ }
+
+ @Test
+ public void testOnPackageChanged_whenPackageChanged_packageEnabledRegistersTransport()
+ throws Exception {
+ TransportManager transportManager =
+ createTransportManagerWithRegisteredTransports(mTransportA1, mTransportB1);
+ reset(mListener);
+
+ mContext.getPackageManager()
+ .setApplicationEnabledSetting(
+ PACKAGE_A,
+ Integer.valueOf(COMPONENT_ENABLED_STATE_DISABLED),
+ 0 /*flags*/);
+ transportManager.onPackageChanged(PACKAGE_A, PACKAGE_A);
+
+ assertRegisteredTransports(transportManager, singletonList(mTransportB1));
+ verify(mListener, never()).onTransportRegistered(any(), any());
+
+ mContext.getPackageManager()
+ .setApplicationEnabledSetting(
+ PACKAGE_A,
+ Integer.valueOf(COMPONENT_ENABLED_STATE_ENABLED),
+ 0 /*flags*/);
+ transportManager.onPackageChanged(PACKAGE_A, PACKAGE_A);
+
+ assertRegisteredTransports(transportManager, asList(mTransportA1, mTransportB1));
+ verify(mListener)
+ .onTransportRegistered(mTransportA1.transportName, mTransportA1.transportDirName);
+ }
+
+ @Test
+ public void testOnPackageChanged_whenPackageChanged_unknownComponentStateIsIgnored()
+ throws Exception {
+ TransportManager transportManager =
+ createTransportManagerWithRegisteredTransports(mTransportA1, mTransportB1);
+ reset(mListener);
+
+ mContext.getPackageManager()
+ .setApplicationEnabledSetting(
+ PACKAGE_A,
+ Integer.valueOf(COMPONENT_ENABLED_STATE_DEFAULT),
+ 0 /*flags*/);
+ transportManager.onPackageChanged(PACKAGE_A, PACKAGE_A);
+
+ assertRegisteredTransports(transportManager, asList(mTransportA1, mTransportB1));
+ verify(mListener, never()).onTransportRegistered(any(), any());
+ }
+
+ @Test
+ public void testOnPackageChanged_whenPackageChanged_unknownPackageExceptionIsIgnored()
+ throws Exception {
+ TransportManager transportManager =
+ createTransportManagerWithRegisteredTransports(mTransportA1, mTransportB1);
+ reset(mListener);
+
+ // empty packageName triggers Robolectric ApplicationPackageManager to throw
+ // exception as if package does not exist.
+ transportManager.onPackageChanged("", "");
+
+ assertRegisteredTransports(transportManager, asList(mTransportA1, mTransportB1));
+ verify(mListener, never()).onTransportRegistered(any(), any());
+ }
+
+ @Test
public void testRegisterAndSelectTransport_whenTransportRegistered() throws Exception {
TransportManager transportManager =
createTransportManagerWithRegisteredTransports(null, mTransportA1);
diff --git a/services/robotests/src/com/android/server/testing/shadows/ShadowApplicationPackageManager.java b/services/robotests/src/com/android/server/testing/shadows/ShadowApplicationPackageManager.java
index aea36e555ad7..4a9948668bc4 100644
--- a/services/robotests/src/com/android/server/testing/shadows/ShadowApplicationPackageManager.java
+++ b/services/robotests/src/com/android/server/testing/shadows/ShadowApplicationPackageManager.java
@@ -16,6 +16,7 @@
package com.android.server.testing.shadows;
+import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
import static android.content.pm.PackageManager.NameNotFoundException;
import android.app.ApplicationPackageManager;
@@ -44,6 +45,7 @@ public class ShadowApplicationPackageManager
private static final List<PackageInfo> sInstalledPackages = new ArrayList<>();
private static final Map<String, Integer> sPackageUids = new ArrayMap<>();
private static final Map<Integer, Map<String, Integer>> sUserPackageUids = new ArrayMap<>();
+ private static final Map<String, Integer> sPackageAppEnabledStates = new ArrayMap<>();
/**
* Registers the package {@code packageName} to be returned when invoking {@link
@@ -53,6 +55,7 @@ public class ShadowApplicationPackageManager
public static void addInstalledPackage(String packageName, PackageInfo packageInfo) {
sPackageInfos.put(packageName, packageInfo);
sInstalledPackages.add(packageInfo);
+ sPackageAppEnabledStates.put(packageName, Integer.valueOf(COMPONENT_ENABLED_STATE_DEFAULT));
}
/**
@@ -77,6 +80,22 @@ public class ShadowApplicationPackageManager
}
@Override
+ protected int getApplicationEnabledSetting(String packageName) {
+ if (packageName.isEmpty()) {
+ throw new IllegalArgumentException("Robo: Package '' does not exist.");
+ }
+ if (!sPackageAppEnabledStates.containsKey(packageName)) {
+ return COMPONENT_ENABLED_STATE_DEFAULT;
+ }
+ return sPackageAppEnabledStates.get(packageName);
+ }
+
+ @Override
+ protected void setApplicationEnabledSetting(String packageName, int newState, int flags) {
+ sPackageAppEnabledStates.put(packageName, Integer.valueOf(newState)); // flags unused here.
+ }
+
+ @Override
protected PackageInfo getPackageInfoAsUser(String packageName, int flags, int userId)
throws NameNotFoundException {
if (!sPackageInfos.containsKey(packageName)) {
@@ -115,6 +134,7 @@ public class ShadowApplicationPackageManager
public static void reset() {
sPackageInfos.clear();
sInstalledPackages.clear();
+ sPackageAppEnabledStates.clear();
org.robolectric.shadows.ShadowApplicationPackageManager.reset();
}
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
index a9812ab9bb03..d104871f488a 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
@@ -89,6 +89,7 @@ public class HdmiCecLocalDeviceTvTest {
private ArrayList<HdmiCecLocalDevice> mLocalDevices = new ArrayList<>();
private int mTvPhysicalAddress;
private int mTvLogicalAddress;
+ private boolean mWokenUp;
@Mock
private AudioManager mAudioManager;
@@ -104,6 +105,11 @@ public class HdmiCecLocalDeviceTvTest {
new HdmiControlService(InstrumentationRegistry.getTargetContext(),
Collections.emptyList()) {
@Override
+ void wakeUp() {
+ mWokenUp = true;
+ super.wakeUp();
+ }
+ @Override
boolean isControlEnabled() {
return true;
}
@@ -308,6 +314,22 @@ public class HdmiCecLocalDeviceTvTest {
}
@Test
+ public void handleTextViewOn_Dreaming() {
+ mHdmiCecLocalDeviceTv.mService.getHdmiCecConfig().setIntValue(
+ HdmiControlManager.CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY,
+ HdmiControlManager.TV_WAKE_ON_ONE_TOUCH_PLAY_ENABLED);
+ mTestLooper.dispatchAll();
+ mPowerManager.setInteractive(true);
+ mWokenUp = false;
+ HdmiCecMessage textViewOn = HdmiCecMessageBuilder.buildTextViewOn(ADDR_PLAYBACK_1,
+ mTvLogicalAddress);
+ assertThat(mHdmiCecLocalDeviceTv.dispatchMessage(textViewOn)).isEqualTo(Constants.HANDLED);
+ mTestLooper.dispatchAll();
+ assertThat(mPowerManager.isInteractive()).isTrue();
+ assertThat(mWokenUp).isTrue();
+ }
+
+ @Test
public void tvSendStandbyOnSleep_Enabled() {
mHdmiCecLocalDeviceTv.mService.getHdmiCecConfig().setIntValue(
HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP,
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java
index 3cda2a554e48..ec16188bfc1d 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java
@@ -288,7 +288,7 @@ public class VibrationSettingsTest {
}
@Test
- public void shouldIgnoreVibration_withRingerModeSilent_ignoresRingtoneOnly() {
+ public void shouldIgnoreVibration_withRingerModeSilent_ignoresRingtoneAndNotification() {
// Vibrating settings on are overruled by ringer mode.
setUserSetting(Settings.System.HAPTIC_FEEDBACK_ENABLED, 1);
setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 1);
@@ -296,7 +296,7 @@ public class VibrationSettingsTest {
setRingerMode(AudioManager.RINGER_MODE_SILENT);
for (int usage : ALL_USAGES) {
- if (usage == USAGE_RINGTONE) {
+ if (usage == USAGE_RINGTONE || usage == USAGE_NOTIFICATION) {
assertVibrationIgnoredForUsage(usage, Vibration.Status.IGNORED_FOR_RINGER_MODE);
} else {
assertVibrationNotIgnoredForUsage(usage);
@@ -305,6 +305,16 @@ public class VibrationSettingsTest {
}
@Test
+ public void shouldIgnoreVibration_withRingerModeSilentAndBypassFlag_allowsAllVibrations() {
+ setRingerMode(AudioManager.RINGER_MODE_SILENT);
+
+ for (int usage : ALL_USAGES) {
+ assertVibrationNotIgnoredForUsageAndFlags(usage,
+ VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY);
+ }
+ }
+
+ @Test
public void shouldIgnoreVibration_withRingerModeVibrate_allowsAllVibrations() {
setRingerMode(AudioManager.RINGER_MODE_VIBRATE);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 71f8b8de032b..d752322f567a 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -18,6 +18,7 @@ package com.android.server.notification;
import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE;
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.AppOpsManager.MODE_ALLOWED;
import static android.app.AppOpsManager.MODE_IGNORED;
import static android.app.Notification.FLAG_AUTO_CANCEL;
@@ -432,8 +433,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
when(mUgmInternal.newUriPermissionOwner(anyString())).thenReturn(mPermOwner);
when(mPackageManager.getPackagesForUid(mUid)).thenReturn(new String[]{PKG});
when(mPackageManagerClient.getPackagesForUid(anyInt())).thenReturn(new String[]{PKG});
- when(mPermissionPolicyInternal.canShowPermissionPromptForTask(
- any(ActivityManager.RecentTaskInfo.class))).thenReturn(false);
+ when(mAtm.getTaskToShowPermissionDialogOn(anyString(), anyInt()))
+ .thenReturn(INVALID_TASK_ID);
mContext.addMockSystemService(AppOpsManager.class, mock(AppOpsManager.class));
when(mUm.getProfileIds(0, false)).thenReturn(new int[]{0});
@@ -971,8 +972,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
@Test
public void testCreateNotificationChannels_FirstChannelWithFgndTaskStartsPermDialog()
throws Exception {
- when(mPermissionPolicyInternal.canShowPermissionPromptForTask(any(
- ActivityManager.RecentTaskInfo.class))).thenReturn(true);
+ when(mAtm.getTaskToShowPermissionDialogOn(anyString(), anyInt())).thenReturn(TEST_TASK_ID);
final NotificationChannel channel =
new NotificationChannel("id", "name", IMPORTANCE_DEFAULT);
mBinderService.createNotificationChannels(PKG_NO_CHANNELS,
@@ -985,8 +985,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
@Test
public void testCreateNotificationChannels_SecondChannelWithFgndTaskDoesntStartPermDialog()
throws Exception {
- when(mPermissionPolicyInternal.canShowPermissionPromptForTask(any(
- ActivityManager.RecentTaskInfo.class))).thenReturn(true);
+ when(mAtm.getTaskToShowPermissionDialogOn(anyString(), anyInt())).thenReturn(TEST_TASK_ID);
assertTrue(mBinderService.getNumNotificationChannelsForPackage(PKG, mUid, true) > 0);
final NotificationChannel channel =
@@ -1001,8 +1000,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
public void testCreateNotificationChannels_FirstChannelWithBgndTaskDoesntStartPermDialog()
throws Exception {
reset(mPermissionPolicyInternal);
- when(mPermissionPolicyInternal.canShowPermissionPromptForTask(any(
- ActivityManager.RecentTaskInfo.class))).thenReturn(false);
+ when(mAtm.getTaskToShowPermissionDialogOn(anyString(), anyInt())).thenReturn(TEST_TASK_ID);
final NotificationChannel channel =
new NotificationChannel("id", "name", IMPORTANCE_DEFAULT);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java
index a24ba0d1e1c2..50151bfb7191 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java
@@ -89,6 +89,10 @@ public class PermissionHelperTest extends UiServiceTestCase {
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mPermissionHelper = new PermissionHelper(mPmi, mPackageManager, mPermManager, true);
+ PackageInfo testPkgInfo = new PackageInfo();
+ testPkgInfo.requestedPermissions = new String[]{ Manifest.permission.POST_NOTIFICATIONS };
+ when(mPackageManager.getPackageInfo(anyString(), anyLong(), anyInt()))
+ .thenReturn(testPkgInfo);
}
// TODO (b/194833441): Remove when the migration is enabled
@@ -384,6 +388,22 @@ public class PermissionHelperTest extends UiServiceTestCase {
}
@Test
+ public void testSetNotificationPermission_doesntRequestNotChanged() throws Exception {
+ when(mPmi.checkPermission(anyString(), anyString(), anyInt()))
+ .thenReturn(PERMISSION_GRANTED);
+ PackageInfo testPkgInfo = new PackageInfo();
+ testPkgInfo.requestedPermissions = new String[]{ Manifest.permission.RECORD_AUDIO };
+ when(mPackageManager.getPackageInfo(anyString(), anyLong(), anyInt()))
+ .thenReturn(testPkgInfo);
+ mPermissionHelper.setNotificationPermission("pkg", 10, false, false);
+
+ verify(mPmi, never()).checkPermission(
+ eq("pkg"), eq(Manifest.permission.POST_NOTIFICATIONS), eq(10));
+ verify(mPermManager, never()).revokeRuntimePermission(
+ eq("pkg"), eq(Manifest.permission.POST_NOTIFICATIONS), eq(10), anyString());
+ }
+
+ @Test
public void testIsPermissionFixed() throws Exception {
when(mPermManager.getPermissionFlags(anyString(),
eq(Manifest.permission.POST_NOTIFICATIONS),
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index 8f1eed89647c..7d5a0d0bf84d 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -1870,46 +1870,46 @@ public class PreferencesHelperTest extends UiServiceTestCase {
@Test
public void testGetChannelsBypassingDndCount_noChannelsBypassing() throws Exception {
assertEquals(0, mHelper.getNotificationChannelsBypassingDnd(PKG_N_MR1,
- USER.getIdentifier()).getList().size());
+ UID_N_MR1).getList().size());
}
@Test
- public void testGetChannelsBypassingDnd_noChannelsForUserIdBypassing()
+ public void testGetChannelsBypassingDnd_noChannelsForUidBypassing()
throws Exception {
- int user = 9;
+ int uid = 222;
NotificationChannel channel = new NotificationChannel("id", "name",
NotificationManager.IMPORTANCE_MAX);
channel.setBypassDnd(true);
mHelper.createNotificationChannel(PKG_N_MR1, 111, channel, true, true);
assertEquals(0, mHelper.getNotificationChannelsBypassingDnd(PKG_N_MR1,
- user).getList().size());
+ uid).getList().size());
}
@Test
public void testGetChannelsBypassingDndCount_oneChannelBypassing_groupBlocked() {
- int user = USER.getIdentifier();
+ int uid = UID_N_MR1;
NotificationChannelGroup ncg = new NotificationChannelGroup("group1", "name1");
NotificationChannel channel1 = new NotificationChannel("id1", "name1",
NotificationManager.IMPORTANCE_MAX);
channel1.setBypassDnd(true);
channel1.setGroup(ncg.getId());
- mHelper.createNotificationChannelGroup(PKG_N_MR1, user, ncg, /* fromTargetApp */ true);
- mHelper.createNotificationChannel(PKG_N_MR1, user, channel1, true, /*has DND access*/ true);
+ mHelper.createNotificationChannelGroup(PKG_N_MR1, uid, ncg, /* fromTargetApp */ true);
+ mHelper.createNotificationChannel(PKG_N_MR1, uid, channel1, true, /*has DND access*/ true);
assertEquals(1, mHelper.getNotificationChannelsBypassingDnd(PKG_N_MR1,
- user).getList().size());
+ uid).getList().size());
// disable group
ncg.setBlocked(true);
- mHelper.createNotificationChannelGroup(PKG_N_MR1, user, ncg, /* fromTargetApp */ false);
+ mHelper.createNotificationChannelGroup(PKG_N_MR1, uid, ncg, /* fromTargetApp */ false);
assertEquals(0, mHelper.getNotificationChannelsBypassingDnd(PKG_N_MR1,
- user).getList().size());
+ uid).getList().size());
}
@Test
public void testGetChannelsBypassingDndCount_multipleChannelsBypassing() {
- int user = USER.getIdentifier();
+ int uid = UID_N_MR1;
NotificationChannel channel1 = new NotificationChannel("id1", "name1",
NotificationManager.IMPORTANCE_MAX);
NotificationChannel channel2 = new NotificationChannel("id2", "name2",
@@ -1920,22 +1920,22 @@ public class PreferencesHelperTest extends UiServiceTestCase {
channel2.setBypassDnd(true);
channel3.setBypassDnd(true);
// has DND access, so can set bypassDnd attribute
- mHelper.createNotificationChannel(PKG_N_MR1, user, channel1, true, /*has DND access*/ true);
- mHelper.createNotificationChannel(PKG_N_MR1, user, channel2, true, true);
- mHelper.createNotificationChannel(PKG_N_MR1, user, channel3, true, true);
+ mHelper.createNotificationChannel(PKG_N_MR1, uid, channel1, true, /*has DND access*/ true);
+ mHelper.createNotificationChannel(PKG_N_MR1, uid, channel2, true, true);
+ mHelper.createNotificationChannel(PKG_N_MR1, uid, channel3, true, true);
assertEquals(3, mHelper.getNotificationChannelsBypassingDnd(PKG_N_MR1,
- user).getList().size());
+ uid).getList().size());
// setBypassDnd false for some channels
channel1.setBypassDnd(false);
channel2.setBypassDnd(false);
assertEquals(1, mHelper.getNotificationChannelsBypassingDnd(PKG_N_MR1,
- user).getList().size());
+ uid).getList().size());
// setBypassDnd false for rest of the channels
channel3.setBypassDnd(false);
assertEquals(0, mHelper.getNotificationChannelsBypassingDnd(PKG_N_MR1,
- user).getList().size());
+ uid).getList().size());
}
@Test
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeFilteringTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeFilteringTest.java
index abcc8c1e99cb..8ac729e29424 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeFilteringTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeFilteringTest.java
@@ -121,7 +121,7 @@ public class ZenModeFilteringTest extends UiServiceTestCase {
// Create a notification record with the people String array as the
// bundled extras, and the numbers ArraySet as additional phone numbers.
- private NotificationRecord getRecordWithPeopleInfo(String[] people,
+ private NotificationRecord getCallRecordWithPeopleInfo(String[] people,
ArraySet<String> numbers) {
// set up notification record
NotificationRecord r = mock(NotificationRecord.class);
@@ -131,6 +131,8 @@ public class ZenModeFilteringTest extends UiServiceTestCase {
when(sbn.getNotification()).thenReturn(notification);
when(r.getSbn()).thenReturn(sbn);
when(r.getPhoneNumbers()).thenReturn(numbers);
+ when(r.getCriticality()).thenReturn(CriticalNotificationExtractor.NORMAL);
+ when(r.isCategory(CATEGORY_CALL)).thenReturn(true);
return r;
}
@@ -350,11 +352,41 @@ public class ZenModeFilteringTest extends UiServiceTestCase {
}
@Test
+ public void testRepeatCallers_checksPhoneNumbers() {
+ // set up telephony manager behavior
+ when(mTelephonyManager.getNetworkCountryIso()).thenReturn("us");
+
+ // first, record a phone call from a telephone number
+ String[] callNumber = new String[]{"tel:12345678910"};
+ mZenModeFiltering.recordCall(getCallRecordWithPeopleInfo(callNumber, null));
+
+ // set up policy to only allow repeat callers
+ Policy policy = new Policy(
+ PRIORITY_CATEGORY_REPEAT_CALLERS, 0, 0, 0, CONVERSATION_SENDERS_NONE);
+
+ // make sure that a record with the phone number in extras is correctly allowed through
+ NotificationRecord r = getCallRecordWithPeopleInfo(callNumber, null);
+ assertFalse(mZenModeFiltering.shouldIntercept(ZEN_MODE_IMPORTANT_INTERRUPTIONS, policy, r));
+
+ // make sure that a record with the phone number in the phone numbers array is also
+ // allowed through
+ NotificationRecord r2 = getCallRecordWithPeopleInfo(new String[]{"some_contact_uri"},
+ new ArraySet<>(new String[]{"12345678910"}));
+ assertFalse(mZenModeFiltering.shouldIntercept(
+ ZEN_MODE_IMPORTANT_INTERRUPTIONS, policy, r2));
+
+ // A record with the phone number in neither of the above should be intercepted
+ NotificationRecord r3 = getCallRecordWithPeopleInfo(new String[]{"tel:10987654321"},
+ new ArraySet<>(new String[]{"15555555555"}));
+ assertTrue(mZenModeFiltering.shouldIntercept(ZEN_MODE_IMPORTANT_INTERRUPTIONS, policy, r3));
+ }
+
+ @Test
public void testMatchesCallFilter_repeatCallers_directMatch() {
// after calls given an email with an exact string match, make sure that
// matchesCallFilter returns the right thing
String[] mailSource = new String[]{"mailto:hello.world"};
- mZenModeFiltering.recordCall(getRecordWithPeopleInfo(mailSource, null));
+ mZenModeFiltering.recordCall(getCallRecordWithPeopleInfo(mailSource, null));
// set up policy to only allow repeat callers
Policy policy = new Policy(
@@ -377,7 +409,7 @@ public class ZenModeFilteringTest extends UiServiceTestCase {
when(mTelephonyManager.getNetworkCountryIso()).thenReturn("us");
String[] telSource = new String[]{"tel:+1-617-555-1212"};
- mZenModeFiltering.recordCall(getRecordWithPeopleInfo(telSource, null));
+ mZenModeFiltering.recordCall(getCallRecordWithPeopleInfo(telSource, null));
// set up policy to only allow repeat callers
Policy policy = new Policy(
@@ -421,7 +453,7 @@ public class ZenModeFilteringTest extends UiServiceTestCase {
when(mTelephonyManager.getNetworkCountryIso()).thenReturn("us");
String[] telSource = new String[]{"tel:%2B16175551212"};
- mZenModeFiltering.recordCall(getRecordWithPeopleInfo(telSource, null));
+ mZenModeFiltering.recordCall(getCallRecordWithPeopleInfo(telSource, null));
// set up policy to only allow repeat callers
Policy policy = new Policy(
@@ -468,7 +500,7 @@ public class ZenModeFilteringTest extends UiServiceTestCase {
String[] contactSource = new String[]{"content://contacts/lookup/uri-here"};
ArraySet<String> contactNumbers = new ArraySet<>(
new String[]{"1-617-555-1212", "1-617-555-3434"});
- NotificationRecord record = getRecordWithPeopleInfo(contactSource, contactNumbers);
+ NotificationRecord record = getCallRecordWithPeopleInfo(contactSource, contactNumbers);
record.mergePhoneNumbers(contactNumbers);
mZenModeFiltering.recordCall(record);
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 12f987dfcd8d..3d95ec599b1b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -1370,7 +1370,9 @@ public class DisplayContentTests extends WindowTestsBase {
ROTATION_0 /* oldRotation */, ROTATION_90 /* newRotation */,
false /* forceUpdate */));
- assertNotNull(mDisplayContent.getAsyncRotationController());
+ final AsyncRotationController asyncRotationController =
+ mDisplayContent.getAsyncRotationController();
+ assertNotNull(asyncRotationController);
assertTrue(mStatusBarWindow.isAnimating(PARENTS, ANIMATION_TYPE_FIXED_TRANSFORM));
assertTrue(mNavBarWindow.isAnimating(PARENTS, ANIMATION_TYPE_FIXED_TRANSFORM));
// Notification shade may have its own view animation in real case so do not fade out it.
@@ -1443,6 +1445,7 @@ public class DisplayContentTests extends WindowTestsBase {
mDisplayContent.setImeLayeringTarget(mAppWindow);
LocalServices.getService(WindowManagerInternal.class).onToggleImeRequested(true /* show */,
app.token, app.token, mDisplayContent.mDisplayId);
+ assertTrue(asyncRotationController.isTargetToken(mImeWindow.mToken));
assertTrue(mImeWindow.mToken.hasFixedRotationTransform());
assertTrue(mImeWindow.isAnimating(PARENTS, ANIMATION_TYPE_FIXED_TRANSFORM));
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index 1999cfc706b4..b290669e103e 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -640,7 +640,7 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser
Slog.e(TAG, "unknown state " + state);
return;
}
- removeMessages(MSG_UPDATE_STATE);
+ if (configured == 0) removeMessages(MSG_UPDATE_STATE);
if (connected == 1) removeMessages(MSG_FUNCTION_SWITCH_TIMEOUT);
Message msg = Message.obtain(this, MSG_UPDATE_STATE);
msg.arg1 = connected;
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
index 84bd98347dab..a597fc69a340 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
@@ -97,6 +97,7 @@ final class HotwordDetectionConnection {
private static final Duration MAX_UPDATE_TIMEOUT_DURATION =
Duration.ofMillis(MAX_UPDATE_TIMEOUT_MILLIS);
private static final long RESET_DEBUG_HOTWORD_LOGGING_TIMEOUT_MILLIS = 60 * 60 * 1000; // 1 hour
+ private static final int MAX_ISOLATED_PROCESS_NUMBER = 10;
private final Executor mAudioCopyExecutor = Executors.newCachedThreadPool();
// TODO: This may need to be a Handler(looper)
@@ -772,7 +773,8 @@ final class HotwordDetectionConnection {
ServiceConnection createLocked() {
ServiceConnection connection =
new ServiceConnection(mContext, mIntent, mBindingFlags, mUser,
- IHotwordDetectionService.Stub::asInterface, ++mRestartCount);
+ IHotwordDetectionService.Stub::asInterface,
+ mRestartCount++ % MAX_ISOLATED_PROCESS_NUMBER);
connection.connect();
updateAudioFlinger(connection, mAudioFlinger);
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 0aaafef492bf..b6cacaf9f289 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -1976,7 +1976,7 @@ public class SubscriptionManager {
*/
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public void addSubscriptionInfoRecord(@NonNull String uniqueId, @Nullable String displayName,
- int slotIndex, int subscriptionType) {
+ int slotIndex, @SubscriptionType int subscriptionType) {
if (VDBG) {
logd("[addSubscriptionInfoRecord]+ uniqueId:" + uniqueId
+ ", displayName:" + displayName + ", slotIndex:" + slotIndex
@@ -2012,7 +2012,8 @@ public class SubscriptionManager {
* @hide
*/
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
- public void removeSubscriptionInfoRecord(@NonNull String uniqueId, int subscriptionType) {
+ public void removeSubscriptionInfoRecord(@NonNull String uniqueId,
+ @SubscriptionType int subscriptionType) {
if (VDBG) {
logd("[removeSubscriptionInfoRecord]+ uniqueId:" + uniqueId
+ ", subscriptionType: " + subscriptionType);
diff --git a/telephony/java/android/telephony/data/DataServiceCallback.java b/telephony/java/android/telephony/data/DataServiceCallback.java
index ec734716f6e4..b8c5b4e3628a 100644
--- a/telephony/java/android/telephony/data/DataServiceCallback.java
+++ b/telephony/java/android/telephony/data/DataServiceCallback.java
@@ -50,12 +50,13 @@ public class DataServiceCallback {
*/
@Retention(RetentionPolicy.SOURCE)
@IntDef({RESULT_SUCCESS, RESULT_ERROR_UNSUPPORTED, RESULT_ERROR_INVALID_ARG, RESULT_ERROR_BUSY,
- RESULT_ERROR_ILLEGAL_STATE})
+ RESULT_ERROR_ILLEGAL_STATE, RESULT_ERROR_TEMPORARILY_UNAVAILABLE,
+ RESULT_ERROR_RADIO_NOT_AVAILABLE})
public @interface ResultCode {}
/** Request is completed successfully */
public static final int RESULT_SUCCESS = 0;
- /** Request is not support */
+ /** Request is not supported */
public static final int RESULT_ERROR_UNSUPPORTED = 1;
/** Request contains invalid arguments */
public static final int RESULT_ERROR_INVALID_ARG = 2;
@@ -68,6 +69,11 @@ public class DataServiceCallback {
* @hide
*/
public static final int RESULT_ERROR_TEMPORARILY_UNAVAILABLE = 5;
+ /**
+ * Request failed to complete due to radio not available.
+ * @hide
+ */
+ public static final int RESULT_ERROR_RADIO_NOT_AVAILABLE = 6;
private final IDataServiceCallback mCallback;
@@ -255,6 +261,8 @@ public class DataServiceCallback {
return "RESULT_ERROR_ILLEGAL_STATE";
case RESULT_ERROR_TEMPORARILY_UNAVAILABLE:
return "RESULT_ERROR_TEMPORARILY_UNAVAILABLE";
+ case RESULT_ERROR_RADIO_NOT_AVAILABLE:
+ return "RESULT_ERROR_RADIO_NOT_AVAILABLE";
default:
return "Unknown(" + resultCode + ")";
}