summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/api/current.txt51
-rw-r--r--core/api/system-current.txt4
-rw-r--r--core/api/test-current.txt4
-rw-r--r--core/java/android/app/ActivityManagerInternal.java47
-rw-r--r--core/java/android/app/ForegroundServiceTypePolicy.java260
-rw-r--r--core/java/android/hardware/camera2/params/StreamConfigurationMap.java3
-rw-r--r--core/java/android/view/ViewRootImpl.java22
-rw-r--r--core/java/android/window/SurfaceSyncGroup.java59
-rw-r--r--core/res/AndroidManifest.xml10
-rw-r--r--core/res/res/values/config.xml12
-rw-r--r--core/res/res/values/symbols.xml6
-rw-r--r--core/tests/coretests/src/android/provider/NameValueCacheTest.java54
-rw-r--r--data/etc/services.core.protolog.json6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java129
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java10
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizer.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java56
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskPositioner.java24
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/TaskPositionerTest.kt92
-rw-r--r--media/java/android/media/AudioManager.java5
-rw-r--r--media/java/android/media/tv/ITvInputClient.aidl2
-rw-r--r--media/java/android/media/tv/ITvInputManager.aidl4
-rw-r--r--media/java/android/media/tv/ITvInputSession.aidl4
-rw-r--r--media/java/android/media/tv/ITvInputSessionCallback.aidl2
-rw-r--r--media/java/android/media/tv/ITvInputSessionWrapper.java8
-rw-r--r--media/java/android/media/tv/TableResponse.java147
-rw-r--r--media/java/android/media/tv/TvInputManager.java22
-rw-r--r--media/java/android/media/tv/TvInputService.java17
-rw-r--r--media/java/android/media/tv/TvRecordingInfo.java7
-rw-r--r--media/java/android/media/tv/TvView.java14
-rw-r--r--media/java/android/media/tv/interactive/ITvInteractiveAppClient.aidl2
-rw-r--r--media/java/android/media/tv/interactive/ITvInteractiveAppManager.aidl2
-rw-r--r--media/java/android/media/tv/interactive/ITvInteractiveAppSession.aidl2
-rw-r--r--media/java/android/media/tv/interactive/ITvInteractiveAppSessionCallback.aidl2
-rw-r--r--media/java/android/media/tv/interactive/ITvInteractiveAppSessionWrapper.java6
-rwxr-xr-xmedia/java/android/media/tv/interactive/TvInteractiveAppManager.java10
-rwxr-xr-xmedia/java/android/media/tv/interactive/TvInteractiveAppService.java32
-rwxr-xr-xmedia/java/android/media/tv/interactive/TvInteractiveAppView.java35
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt1
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/GenerationRegistry.java32
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java105
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java6
-rw-r--r--packages/SettingsProvider/test/src/com/android/providers/settings/GenerationRegistryTest.java20
-rw-r--r--packages/Shell/AndroidManifest.xml2
-rw-r--r--packages/SystemUI/AndroidManifest.xml2
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/Flags.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt18
-rw-r--r--packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInfo.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInfoResolver.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt61
-rw-r--r--packages/SystemUI/src/com/android/systemui/notetask/shortcut/LaunchNoteTaskActivity.kt9
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt44
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt114
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInfoTest.kt32
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt130
-rw-r--r--services/core/java/com/android/server/am/ActiveServices.java34
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerConstants.java7
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java69
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerShellCommand.java1
-rw-r--r--services/core/java/com/android/server/am/DropboxRateLimiter.java72
-rw-r--r--services/core/java/com/android/server/appop/AppOpsCheckingServiceTracingDecorator.java2
-rw-r--r--services/core/java/com/android/server/connectivity/Vpn.java107
-rw-r--r--services/core/java/com/android/server/display/mode/SkinThermalStatusObserver.java8
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodDeviceConfigs.java7
-rw-r--r--services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java9
-rw-r--r--services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java6
-rw-r--r--services/core/java/com/android/server/tv/TvInputManagerService.java8
-rw-r--r--services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java8
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java67
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java3
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java26
-rw-r--r--services/core/java/com/android/server/wm/EventLogTags.logtags4
-rw-r--r--services/core/java/com/android/server/wm/KeyguardController.java13
-rw-r--r--services/core/java/com/android/server/wm/LetterboxConfiguration.java15
-rw-r--r--services/core/java/com/android/server/wm/LetterboxConfigurationDeviceConfig.java20
-rw-r--r--services/core/java/com/android/server/wm/LetterboxUiController.java4
-rw-r--r--services/core/java/com/android/server/wm/RemoteAnimationController.java14
-rw-r--r--services/core/java/com/android/server/wm/Task.java3
-rw-r--r--services/core/java/com/android/server/wm/WindowContainer.java23
-rw-r--r--services/credentials/java/com/android/server/credentials/CredentialDescriptionRegistry.java38
-rw-r--r--services/credentials/java/com/android/server/credentials/CredentialManagerService.java58
-rw-r--r--services/credentials/java/com/android/server/credentials/MetricUtilities.java4
-rw-r--r--services/credentials/java/com/android/server/credentials/ProviderClearSession.java5
-rw-r--r--services/credentials/java/com/android/server/credentials/ProviderCreateSession.java4
-rw-r--r--services/credentials/java/com/android/server/credentials/ProviderGetSession.java5
-rw-r--r--services/credentials/java/com/android/server/credentials/ProviderRegistryGetSession.java9
-rw-r--r--services/credentials/java/com/android/server/credentials/ProviderSession.java13
-rw-r--r--services/credentials/java/com/android/server/credentials/RequestSession.java4
-rw-r--r--services/credentials/java/com/android/server/credentials/metrics/CandidatePhaseMetric.java246
-rw-r--r--services/credentials/java/com/android/server/credentials/metrics/CandidateProviderMetric.java89
-rw-r--r--services/credentials/java/com/android/server/credentials/metrics/ChosenProviderMetric.java8
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java2
-rw-r--r--services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java11
-rw-r--r--services/tests/servicestests/src/com/android/server/am/DropboxRateLimiterTest.java58
-rw-r--r--services/tests/servicestests/src/com/android/server/credentials/CredentialDescriptionRegistryTest.java18
-rw-r--r--services/tests/servicestests/src/com/android/server/credentials/ProviderRegistryGetSessionTest.java12
-rw-r--r--services/tests/servicestests/src/com/android/server/display/mode/SkinThermalStatusObserverTest.java38
-rwxr-xr-xservices/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java47
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SurfaceSyncGroupTest.java52
-rw-r--r--telephony/common/com/android/internal/telephony/util/TelephonyUtils.java9
-rw-r--r--wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java4
-rw-r--r--wifi/java/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityService.java28
-rw-r--r--wifi/tests/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityServiceTest.java47
107 files changed, 2259 insertions, 857 deletions
diff --git a/core/api/current.txt b/core/api/current.txt
index 5b9970b6c15d..6078712fa7d1 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -26782,9 +26782,7 @@ package android.media.tv {
}
public final class TableResponse extends android.media.tv.BroadcastInfoResponse implements android.os.Parcelable {
- ctor public TableResponse(int, int, int, @Nullable android.net.Uri, int, int);
- ctor public TableResponse(int, int, int, @NonNull byte[], int, int);
- ctor public TableResponse(int, int, int, @NonNull android.os.SharedMemory, int, int);
+ ctor @Deprecated public TableResponse(int, int, int, @Nullable android.net.Uri, int, int);
method public int getSize();
method @Nullable public byte[] getTableByteArray();
method @Nullable public android.os.SharedMemory getTableSharedMemory();
@@ -26793,6 +26791,14 @@ package android.media.tv {
field @NonNull public static final android.os.Parcelable.Creator<android.media.tv.TableResponse> CREATOR;
}
+ public static final class TableResponse.Builder {
+ ctor public TableResponse.Builder(int, int, int, int, int);
+ method @NonNull public android.media.tv.TableResponse build();
+ method @NonNull public android.media.tv.TableResponse.Builder setTableByteArray(@NonNull byte[]);
+ method @NonNull public android.media.tv.TableResponse.Builder setTableSharedMemory(@NonNull android.os.SharedMemory);
+ method @NonNull public android.media.tv.TableResponse.Builder setTableUri(@NonNull android.net.Uri);
+ }
+
public final class TimelineRequest extends android.media.tv.BroadcastInfoRequest implements android.os.Parcelable {
ctor public TimelineRequest(int, int, int);
ctor public TimelineRequest(int, int, int, @NonNull String);
@@ -27337,8 +27343,9 @@ package android.media.tv {
field public static final int TIME_SHIFT_STATUS_UNKNOWN = 0; // 0x0
field public static final int TIME_SHIFT_STATUS_UNSUPPORTED = 1; // 0x1
field public static final String TV_MESSAGE_KEY_STREAM_ID = "android.media.tv.TvInputManager.stream_id";
- field public static final String TV_MESSAGE_TYPE_CLOSED_CAPTION = "CC";
- field public static final String TV_MESSAGE_TYPE_WATERMARK = "Watermark";
+ field public static final int TV_MESSAGE_TYPE_CLOSED_CAPTION = 2; // 0x2
+ field public static final int TV_MESSAGE_TYPE_OTHER = 1000; // 0x3e8
+ field public static final int TV_MESSAGE_TYPE_WATERMARK = 1; // 0x1
field public static final int VIDEO_UNAVAILABLE_REASON_AUDIO_ONLY = 4; // 0x4
field public static final int VIDEO_UNAVAILABLE_REASON_BUFFERING = 3; // 0x3
field public static final int VIDEO_UNAVAILABLE_REASON_CAS_BLACKOUT = 16; // 0x10
@@ -27429,10 +27436,10 @@ package android.media.tv {
method public void notifyTrackSelected(int, String);
method public void notifyTracksChanged(java.util.List<android.media.tv.TvTrackInfo>);
method public void notifyTuned(@NonNull android.net.Uri);
- method public void notifyTvMessage(@NonNull String, @NonNull android.os.Bundle);
+ method public void notifyTvMessage(int, @NonNull android.os.Bundle);
method public void notifyVideoAvailable();
method public void notifyVideoUnavailable(int);
- method public void onAdBuffer(@NonNull android.media.tv.AdBuffer);
+ method public void onAdBufferReady(@NonNull android.media.tv.AdBuffer);
method public void onAppPrivateCommand(@NonNull String, android.os.Bundle);
method public android.view.View onCreateOverlayView();
method public boolean onGenericMotionEvent(android.view.MotionEvent);
@@ -27451,7 +27458,7 @@ package android.media.tv {
method public void onSetInteractiveAppNotificationEnabled(boolean);
method public abstract void onSetStreamVolume(@FloatRange(from=0.0, to=1.0) float);
method public abstract boolean onSetSurface(@Nullable android.view.Surface);
- method public void onSetTvMessageEnabled(@NonNull String, boolean);
+ method public void onSetTvMessageEnabled(int, boolean);
method public void onSurfaceChanged(int, int, int);
method public long onTimeShiftGetCurrentPosition();
method public long onTimeShiftGetStartPosition();
@@ -27501,17 +27508,17 @@ package android.media.tv {
method @NonNull public android.net.Uri getChannelUri();
method @NonNull public java.util.List<android.media.tv.TvContentRating> getContentRatings();
method @NonNull public String getDescription();
- method @NonNull public long getEndPaddingMillis();
+ method public long getEndPaddingMillis();
method @NonNull public String getName();
method @Nullable public android.net.Uri getProgramUri();
- method @IntRange(from=0xffffffff) @NonNull public long getRecordingDurationMillis();
+ method @IntRange(from=0xffffffff) public long getRecordingDurationMillis();
method @NonNull public String getRecordingId();
- method @IntRange(from=0xffffffff) @NonNull public long getRecordingStartTimeMillis();
+ method @IntRange(from=0xffffffff) public long getRecordingStartTimeMillis();
method @Nullable public android.net.Uri getRecordingUri();
- method @NonNull public int getRepeatDays();
- method @IntRange(from=0) @NonNull public long getScheduledDurationMillis();
- method @IntRange(from=0) @NonNull public long getScheduledStartTimeMillis();
- method @NonNull public long getStartPaddingMillis();
+ method public int getRepeatDays();
+ method @IntRange(from=0) public long getScheduledDurationMillis();
+ method @IntRange(from=0) public long getScheduledStartTimeMillis();
+ method public long getStartPaddingMillis();
method public void setDescription(@NonNull String);
method public void setName(@NonNull String);
method public void writeToParcel(@NonNull android.os.Parcel, int);
@@ -27594,7 +27601,7 @@ package android.media.tv {
method public void setOnUnhandledInputEventListener(android.media.tv.TvView.OnUnhandledInputEventListener);
method public void setStreamVolume(@FloatRange(from=0.0, to=1.0) float);
method public void setTimeShiftPositionCallback(@Nullable android.media.tv.TvView.TimeShiftPositionCallback);
- method public void setTvMessageEnabled(@NonNull String, boolean);
+ method public void setTvMessageEnabled(int, boolean);
method public void setZOrderMediaOverlay(boolean);
method public void setZOrderOnTop(boolean);
method public void timeShiftPause();
@@ -27635,7 +27642,7 @@ package android.media.tv {
method public void onTrackSelected(String, int, String);
method public void onTracksChanged(String, java.util.List<android.media.tv.TvTrackInfo>);
method public void onTuned(@NonNull String, @NonNull android.net.Uri);
- method public void onTvMessage(@NonNull String, @NonNull String, @NonNull android.os.Bundle);
+ method public void onTvMessage(@NonNull String, int, @NonNull android.os.Bundle);
method public void onVideoAvailable(String);
method public void onVideoSizeChanged(String, int, int);
method public void onVideoUnavailable(String, int);
@@ -27743,7 +27750,7 @@ package android.media.tv.interactive {
ctor public TvInteractiveAppService.Session(@NonNull android.content.Context);
method public boolean isMediaViewEnabled();
method @CallSuper public void layoutSurface(int, int, int, int);
- method @CallSuper public void notifyAdBuffer(@NonNull android.media.tv.AdBuffer);
+ method @CallSuper public void notifyAdBufferReady(@NonNull android.media.tv.AdBuffer);
method @CallSuper public final void notifyBiInteractiveAppCreated(@NonNull android.net.Uri, @Nullable String);
method @CallSuper public void notifySessionStateChanged(int, int);
method @CallSuper public final void notifyTeletextAppStateChanged(int);
@@ -27795,7 +27802,7 @@ package android.media.tv.interactive {
method public boolean onTrackballEvent(@NonNull android.view.MotionEvent);
method public void onTracksChanged(@NonNull java.util.List<android.media.tv.TvTrackInfo>);
method public void onTuned(@NonNull android.net.Uri);
- method public void onTvMessage(@NonNull String, @NonNull android.os.Bundle);
+ method public void onTvMessage(int, @NonNull android.os.Bundle);
method public void onTvRecordingInfo(@Nullable android.media.tv.TvRecordingInfo);
method public void onTvRecordingInfoList(@NonNull java.util.List<android.media.tv.TvRecordingInfo>);
method public void onVideoAvailable();
@@ -27817,7 +27824,7 @@ package android.media.tv.interactive {
method @CallSuper public void requestTimeShiftMode();
method @CallSuper public void requestTrackInfoList();
method @CallSuper public void requestTvRecordingInfo(@NonNull String);
- method @CallSuper public void requestTvRecordingInfoList(@NonNull int);
+ method @CallSuper public void requestTvRecordingInfoList(int);
method @CallSuper public void sendPlaybackCommandRequest(@NonNull String, @Nullable android.os.Bundle);
method @CallSuper public void sendTimeShiftCommandRequest(@NonNull String, @Nullable android.os.Bundle);
method @CallSuper public void setMediaViewEnabled(boolean);
@@ -27859,7 +27866,7 @@ package android.media.tv.interactive {
method public void notifyTimeShiftPlaybackParams(@NonNull android.media.PlaybackParams);
method public void notifyTimeShiftStartPositionChanged(@NonNull String, long);
method public void notifyTimeShiftStatusChanged(@NonNull String, int);
- method public void notifyTvMessage(@NonNull String, @NonNull android.os.Bundle);
+ method public void notifyTvMessage(@NonNull int, @NonNull android.os.Bundle);
method public void onAttachedToWindow();
method public void onDetachedFromWindow();
method public void onLayout(boolean, int, int, int, int);
@@ -27916,7 +27923,7 @@ package android.media.tv.interactive {
method public void onRequestTimeShiftMode(@NonNull String);
method public void onRequestTrackInfoList(@NonNull String);
method public void onRequestTvRecordingInfo(@NonNull String, @NonNull String);
- method public void onRequestTvRecordingInfoList(@NonNull String, @NonNull int);
+ method public void onRequestTvRecordingInfoList(@NonNull String, int);
method public void onSetTvRecordingInfo(@NonNull String, @NonNull String, @NonNull android.media.tv.TvRecordingInfo);
method public void onSetVideoBounds(@NonNull String, @NonNull android.graphics.Rect);
method public void onStateChanged(@NonNull String, int, int);
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index fb5ee8d94524..cd96a1ea210a 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -33,6 +33,7 @@ package android {
field public static final String ADD_TRUSTED_DISPLAY = "android.permission.ADD_TRUSTED_DISPLAY";
field public static final String ADJUST_RUNTIME_PERMISSIONS_POLICY = "android.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY";
field public static final String ALLOCATE_AGGRESSIVE = "android.permission.ALLOCATE_AGGRESSIVE";
+ field public static final String ALLOWLISTED_WRITE_DEVICE_CONFIG = "android.permission.ALLOWLISTED_WRITE_DEVICE_CONFIG";
field public static final String ALLOW_ANY_CODEC_FOR_PLAYBACK = "android.permission.ALLOW_ANY_CODEC_FOR_PLAYBACK";
field public static final String ALLOW_PLACE_IN_MULTI_PANE_SETTINGS = "android.permission.ALLOW_PLACE_IN_MULTI_PANE_SETTINGS";
field public static final String ALLOW_SLIPPERY_TOUCHES = "android.permission.ALLOW_SLIPPERY_TOUCHES";
@@ -287,6 +288,7 @@ package android {
field public static final String READ_SYSTEM_UPDATE_INFO = "android.permission.READ_SYSTEM_UPDATE_INFO";
field public static final String READ_WALLPAPER_INTERNAL = "android.permission.READ_WALLPAPER_INTERNAL";
field public static final String READ_WIFI_CREDENTIAL = "android.permission.READ_WIFI_CREDENTIAL";
+ field public static final String READ_WRITE_SYNC_DISABLED_MODE_CONFIG = "android.permission.READ_WRITE_SYNC_DISABLED_MODE_CONFIG";
field public static final String REAL_GET_TASKS = "android.permission.REAL_GET_TASKS";
field public static final String RECEIVE_BLUETOOTH_MAP = "android.permission.RECEIVE_BLUETOOTH_MAP";
field public static final String RECEIVE_DATA_ACTIVITY_CHANGE = "android.permission.RECEIVE_DATA_ACTIVITY_CHANGE";
@@ -10158,6 +10160,8 @@ package android.net.wifi.sharedconnectivity.service {
public abstract class SharedConnectivityService extends android.app.Service {
ctor public SharedConnectivityService();
+ method public static boolean areHotspotNetworksEnabledForService(@NonNull android.content.Context);
+ method public static boolean areKnownNetworksEnabledForService(@NonNull android.content.Context);
method @Nullable public final android.os.IBinder onBind(@NonNull android.content.Intent);
method public abstract void onConnectHotspotNetwork(@NonNull android.net.wifi.sharedconnectivity.app.HotspotNetwork);
method public abstract void onConnectKnownNetwork(@NonNull android.net.wifi.sharedconnectivity.app.KnownNetwork);
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 46651a9f5409..14c53d100d4f 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -5,6 +5,7 @@ package android {
field public static final String ACCESS_NOTIFICATIONS = "android.permission.ACCESS_NOTIFICATIONS";
field public static final String ACTIVITY_EMBEDDING = "android.permission.ACTIVITY_EMBEDDING";
field public static final String ADJUST_RUNTIME_PERMISSIONS_POLICY = "android.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY";
+ field public static final String ALLOWLISTED_WRITE_DEVICE_CONFIG = "android.permission.ALLOWLISTED_WRITE_DEVICE_CONFIG";
field public static final String APPROVE_INCIDENT_REPORTS = "android.permission.APPROVE_INCIDENT_REPORTS";
field public static final String BACKGROUND_CAMERA = "android.permission.BACKGROUND_CAMERA";
field public static final String BIND_CELL_BROADCAST_SERVICE = "android.permission.BIND_CELL_BROADCAST_SERVICE";
@@ -39,6 +40,7 @@ package android {
field public static final String QUERY_AUDIO_STATE = "android.permission.QUERY_AUDIO_STATE";
field public static final String READ_CELL_BROADCASTS = "android.permission.READ_CELL_BROADCASTS";
field public static final String READ_PRIVILEGED_PHONE_STATE = "android.permission.READ_PRIVILEGED_PHONE_STATE";
+ field public static final String READ_WRITE_SYNC_DISABLED_MODE_CONFIG = "android.permission.READ_WRITE_SYNC_DISABLED_MODE_CONFIG";
field public static final String RECORD_BACKGROUND_AUDIO = "android.permission.RECORD_BACKGROUND_AUDIO";
field public static final String REMAP_MODIFIER_KEYS = "android.permission.REMAP_MODIFIER_KEYS";
field public static final String REMOVE_TASKS = "android.permission.REMOVE_TASKS";
@@ -1957,7 +1959,7 @@ package android.media.tv {
}
public class TvView extends android.view.ViewGroup {
- method public void notifyTvMessage(@NonNull String, @NonNull android.os.Bundle);
+ method public void notifyTvMessage(int, @NonNull android.os.Bundle);
}
}
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index d810f055e96e..90427cb51c26 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -18,6 +18,7 @@ package android.app;
import static android.app.ActivityManager.StopUserOnSwitch;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.PermissionMethod;
@@ -47,6 +48,8 @@ import android.util.Pair;
import com.android.internal.os.TimeoutRecord;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@@ -991,4 +994,48 @@ public abstract class ActivityManagerInternal {
*/
public abstract void logFgsApiEnd(int apiType, int uid, int pid);
+ /**
+ * The list of the events about the {@link android.media.projection.IMediaProjection} itself.
+ *
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ MEDIA_PROJECTION_TOKEN_EVENT_CREATED,
+ MEDIA_PROJECTION_TOKEN_EVENT_DESTROYED,
+ })
+ public @interface MediaProjectionTokenEvent{};
+
+ /**
+ * An instance of {@link android.media.projection.IMediaProjection} has been created
+ * by the system.
+ *
+ * @hide
+ */
+ public static final @MediaProjectionTokenEvent int MEDIA_PROJECTION_TOKEN_EVENT_CREATED = 0;
+
+ /**
+ * An instance of {@link android.media.projection.IMediaProjection} has been destroyed
+ * by the system.
+ *
+ * @hide
+ */
+ public static final @MediaProjectionTokenEvent int MEDIA_PROJECTION_TOKEN_EVENT_DESTROYED = 1;
+
+ /**
+ * Called after the system created/destroyed a media projection for an app, if the user
+ * has granted the permission to start a media projection from this app.
+ *
+ * <p>This API is specifically for the use case of enforcing the FGS type
+ * {@code android.content.pm.ServiceInfo#FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION},
+ * where the app who is starting this type of FGS must have been granted with the permission
+ * to start the projection via the {@link android.media.projection.MediaProjection} APIs.
+ *
+ * @param uid The uid of the app which the system created/destroyed a media projection for.
+ * @param projectionToken The {@link android.media.projection.IMediaProjection} token that
+ * the system created/destroyed.
+ * @param event The actual event happening to the given {@code projectionToken}.
+ */
+ public abstract void notifyMediaProjectionEvent(int uid, @NonNull IBinder projectionToken,
+ @MediaProjectionTokenEvent int event);
}
diff --git a/core/java/android/app/ForegroundServiceTypePolicy.java b/core/java/android/app/ForegroundServiceTypePolicy.java
index a7824a850e59..be012cf8e202 100644
--- a/core/java/android/app/ForegroundServiceTypePolicy.java
+++ b/core/java/android/app/ForegroundServiceTypePolicy.java
@@ -59,6 +59,9 @@ import android.hardware.usb.UsbManager;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.permission.PermissionCheckerManager;
+import android.provider.DeviceConfig;
+import android.text.TextUtils;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.SparseArray;
@@ -147,6 +150,108 @@ public abstract class ForegroundServiceTypePolicy {
public static final long FGS_TYPE_PERMISSION_CHANGE_ID = 254662522L;
/**
+ * The prefix for the feature flags of the permission enforcement.
+ */
+ private static final String FGS_TYPE_PERM_ENFORCEMENT_FLAG_PREFIX =
+ "fgs_type_perm_enforcement_flag_";
+
+ /**
+ * The feature flag of the permission enforcement for
+ * {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_DATA_SYNC},
+ * in the namespace of {@link DeviceConfig#NAMESPACE_ACTIVITY_MANAGER}.
+ */
+ private static final String FGS_TYPE_PERM_ENFORCEMENT_FLAG_DATA_SYNC =
+ FGS_TYPE_PERM_ENFORCEMENT_FLAG_PREFIX + "data_sync";
+
+ /**
+ * The feature flag of the permission enforcement for
+ * {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK},
+ * in the namespace of {@link DeviceConfig#NAMESPACE_ACTIVITY_MANAGER}.
+ */
+ private static final String FGS_TYPE_PERM_ENFORCEMENT_FLAG_MEDIA_PLAYBACK =
+ FGS_TYPE_PERM_ENFORCEMENT_FLAG_PREFIX + "media_playback";
+
+ /**
+ * The feature flag of the permission enforcement for
+ * {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_PHONE_CALL},
+ * in the namespace of {@link DeviceConfig#NAMESPACE_ACTIVITY_MANAGER}.
+ */
+ private static final String FGS_TYPE_PERM_ENFORCEMENT_FLAG_PHONE_CALL =
+ FGS_TYPE_PERM_ENFORCEMENT_FLAG_PREFIX + "phone_call";
+
+ /**
+ * The feature flag of the permission enforcement for
+ * {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_LOCATION},
+ * in the namespace of {@link DeviceConfig#NAMESPACE_ACTIVITY_MANAGER}.
+ */
+ private static final String FGS_TYPE_PERM_ENFORCEMENT_FLAG_LOCATION =
+ FGS_TYPE_PERM_ENFORCEMENT_FLAG_PREFIX + "location";
+
+ /**
+ * The feature flag of the permission enforcement for
+ * {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE},
+ * in the namespace of {@link DeviceConfig#NAMESPACE_ACTIVITY_MANAGER}.
+ */
+ private static final String FGS_TYPE_PERM_ENFORCEMENT_FLAG_CONNECTED_DEVICE =
+ FGS_TYPE_PERM_ENFORCEMENT_FLAG_PREFIX + "connected_device";
+
+ /**
+ * The feature flag of the permission enforcement for
+ * {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION},
+ * in the namespace of {@link DeviceConfig#NAMESPACE_ACTIVITY_MANAGER}.
+ */
+ private static final String FGS_TYPE_PERM_ENFORCEMENT_FLAG_MEDIA_PROJECTION =
+ FGS_TYPE_PERM_ENFORCEMENT_FLAG_PREFIX + "media_projection";
+
+ /**
+ * The feature flag of the permission enforcement for
+ * {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_CAMERA},
+ * in the namespace of {@link DeviceConfig#NAMESPACE_ACTIVITY_MANAGER}.
+ */
+ private static final String FGS_TYPE_PERM_ENFORCEMENT_FLAG_CAMERA =
+ FGS_TYPE_PERM_ENFORCEMENT_FLAG_PREFIX + "camera";
+
+ /**
+ * The feature flag of the permission enforcement for
+ * {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_MICROPHONE},
+ * in the namespace of {@link DeviceConfig#NAMESPACE_ACTIVITY_MANAGER}.
+ */
+ private static final String FGS_TYPE_PERM_ENFORCEMENT_FLAG_MICROPHONE =
+ FGS_TYPE_PERM_ENFORCEMENT_FLAG_PREFIX + "microphone";
+
+ /**
+ * The feature flag of the permission enforcement for
+ * {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_HEALTH},
+ * in the namespace of {@link DeviceConfig#NAMESPACE_ACTIVITY_MANAGER}.
+ */
+ private static final String FGS_TYPE_PERM_ENFORCEMENT_FLAG_HEALTH =
+ FGS_TYPE_PERM_ENFORCEMENT_FLAG_PREFIX + "health";
+
+ /**
+ * The feature flag of the permission enforcement for
+ * {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_REMOTE_MESSAGING},
+ * in the namespace of {@link DeviceConfig#NAMESPACE_ACTIVITY_MANAGER}.
+ */
+ private static final String FGS_TYPE_PERM_ENFORCEMENT_FLAG_REMOTE_MESSAGING =
+ FGS_TYPE_PERM_ENFORCEMENT_FLAG_PREFIX + "remote_messaging";
+
+ /**
+ * The feature flag of the permission enforcement for
+ * {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_SYSTEM_EXEMPTED},
+ * in the namespace of {@link DeviceConfig#NAMESPACE_ACTIVITY_MANAGER}.
+ */
+ private static final String FGS_TYPE_PERM_ENFORCEMENT_FLAG_SYSTEM_EXEMPTED =
+ FGS_TYPE_PERM_ENFORCEMENT_FLAG_PREFIX + "system_exempted";
+
+ /**
+ * The feature flag of the permission enforcement for
+ * {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_SPECIAL_USE},
+ * in the namespace of {@link DeviceConfig#NAMESPACE_ACTIVITY_MANAGER}.
+ */
+ private static final String FGS_TYPE_PERM_ENFORCEMENT_FLAG_SPECIAL_USE =
+ FGS_TYPE_PERM_ENFORCEMENT_FLAG_PREFIX + "special_use";
+
+ /**
* The policy for the {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_MANIFEST}.
*
* @hide
@@ -156,8 +261,10 @@ public abstract class ForegroundServiceTypePolicy {
FOREGROUND_SERVICE_TYPE_MANIFEST,
FGS_TYPE_NONE_DEPRECATION_CHANGE_ID,
FGS_TYPE_NONE_DISABLED_CHANGE_ID,
- null,
- null
+ null /* allOfPermissions */,
+ null /* anyOfPermissions */,
+ null /* permissionEnforcementFlag */,
+ false /* permissionEnforcementFlagDefaultValue */
);
/**
@@ -170,8 +277,10 @@ public abstract class ForegroundServiceTypePolicy {
FOREGROUND_SERVICE_TYPE_NONE,
FGS_TYPE_NONE_DEPRECATION_CHANGE_ID,
FGS_TYPE_NONE_DISABLED_CHANGE_ID,
- null,
- null
+ null /* allOfPermissions */,
+ null /* anyOfPermissions */,
+ null /* permissionEnforcementFlag */,
+ false /* permissionEnforcementFlagDefaultValue */
);
/**
@@ -187,7 +296,9 @@ public abstract class ForegroundServiceTypePolicy {
new ForegroundServiceTypePermissions(new ForegroundServiceTypePermission[] {
new RegularPermission(Manifest.permission.FOREGROUND_SERVICE_DATA_SYNC)
}, true),
- null
+ null /* anyOfPermissions */,
+ FGS_TYPE_PERM_ENFORCEMENT_FLAG_DATA_SYNC /* permissionEnforcementFlag */,
+ true /* permissionEnforcementFlagDefaultValue */
);
/**
@@ -203,7 +314,9 @@ public abstract class ForegroundServiceTypePolicy {
new ForegroundServiceTypePermissions(new ForegroundServiceTypePermission[] {
new RegularPermission(Manifest.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK)
}, true),
- null
+ null /* anyOfPermissions */,
+ FGS_TYPE_PERM_ENFORCEMENT_FLAG_MEDIA_PLAYBACK /* permissionEnforcementFlag */,
+ true /* permissionEnforcementFlagDefaultValue */
);
/**
@@ -221,7 +334,9 @@ public abstract class ForegroundServiceTypePolicy {
}, true),
new ForegroundServiceTypePermissions(new ForegroundServiceTypePermission[] {
new RegularPermission(Manifest.permission.MANAGE_OWN_CALLS)
- }, false)
+ }, false),
+ FGS_TYPE_PERM_ENFORCEMENT_FLAG_PHONE_CALL /* permissionEnforcementFlag */,
+ true /* permissionEnforcementFlagDefaultValue */
);
/**
@@ -240,7 +355,9 @@ public abstract class ForegroundServiceTypePolicy {
new ForegroundServiceTypePermissions(new ForegroundServiceTypePermission[] {
new RegularPermission(Manifest.permission.ACCESS_COARSE_LOCATION),
new RegularPermission(Manifest.permission.ACCESS_FINE_LOCATION),
- }, false)
+ }, false),
+ FGS_TYPE_PERM_ENFORCEMENT_FLAG_LOCATION /* permissionEnforcementFlag */,
+ true /* permissionEnforcementFlagDefaultValue */
);
/**
@@ -268,7 +385,9 @@ public abstract class ForegroundServiceTypePolicy {
new RegularPermission(Manifest.permission.UWB_RANGING),
new UsbDevicePermission(),
new UsbAccessoryPermission(),
- }, false)
+ }, false),
+ FGS_TYPE_PERM_ENFORCEMENT_FLAG_CONNECTED_DEVICE /* permissionEnforcementFlag */,
+ true /* permissionEnforcementFlagDefaultValue */
);
/**
@@ -284,7 +403,12 @@ public abstract class ForegroundServiceTypePolicy {
new ForegroundServiceTypePermissions(new ForegroundServiceTypePermission[] {
new RegularPermission(Manifest.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION)
}, true),
- null
+ new ForegroundServiceTypePermissions(new ForegroundServiceTypePermission[] {
+ new RegularPermission(Manifest.permission.CAPTURE_VIDEO_OUTPUT),
+ new AppOpPermission(AppOpsManager.OP_PROJECT_MEDIA)
+ }, false),
+ FGS_TYPE_PERM_ENFORCEMENT_FLAG_MEDIA_PROJECTION /* permissionEnforcementFlag */,
+ true /* permissionEnforcementFlagDefaultValue */
);
/**
@@ -303,7 +427,9 @@ public abstract class ForegroundServiceTypePolicy {
new ForegroundServiceTypePermissions(new ForegroundServiceTypePermission[] {
new RegularPermission(Manifest.permission.CAMERA),
new RegularPermission(Manifest.permission.SYSTEM_CAMERA),
- }, false)
+ }, false),
+ FGS_TYPE_PERM_ENFORCEMENT_FLAG_CAMERA /* permissionEnforcementFlag */,
+ true /* permissionEnforcementFlagDefaultValue */
);
/**
@@ -326,7 +452,9 @@ public abstract class ForegroundServiceTypePolicy {
new RegularPermission(Manifest.permission.CAPTURE_TUNER_AUDIO_INPUT),
new RegularPermission(Manifest.permission.CAPTURE_VOICE_COMMUNICATION_OUTPUT),
new RegularPermission(Manifest.permission.RECORD_AUDIO),
- }, false)
+ }, false),
+ FGS_TYPE_PERM_ENFORCEMENT_FLAG_MICROPHONE /* permissionEnforcementFlag */,
+ true /* permissionEnforcementFlagDefaultValue */
);
/**
@@ -347,7 +475,9 @@ public abstract class ForegroundServiceTypePolicy {
new RegularPermission(Manifest.permission.BODY_SENSORS),
new RegularPermission(Manifest.permission.BODY_SENSORS_WRIST_TEMPERATURE),
new RegularPermission(Manifest.permission.HIGH_SAMPLING_RATE_SENSORS),
- }, false)
+ }, false),
+ FGS_TYPE_PERM_ENFORCEMENT_FLAG_HEALTH /* permissionEnforcementFlag */,
+ true /* permissionEnforcementFlagDefaultValue */
);
/**
@@ -363,7 +493,9 @@ public abstract class ForegroundServiceTypePolicy {
new ForegroundServiceTypePermissions(new ForegroundServiceTypePermission[] {
new RegularPermission(Manifest.permission.FOREGROUND_SERVICE_REMOTE_MESSAGING)
}, true),
- null
+ null /* anyOfPermissions */,
+ FGS_TYPE_PERM_ENFORCEMENT_FLAG_REMOTE_MESSAGING /* permissionEnforcementFlag */,
+ true /* permissionEnforcementFlagDefaultValue */
);
/**
@@ -383,7 +515,9 @@ public abstract class ForegroundServiceTypePolicy {
new RegularPermission(Manifest.permission.SCHEDULE_EXACT_ALARM),
new RegularPermission(Manifest.permission.USE_EXACT_ALARM),
new AppOpPermission(AppOpsManager.OP_ACTIVATE_VPN),
- }, false)
+ }, false),
+ FGS_TYPE_PERM_ENFORCEMENT_FLAG_SYSTEM_EXEMPTED /* permissionEnforcementFlag */,
+ true /* permissionEnforcementFlagDefaultValue */
);
/**
@@ -396,7 +530,10 @@ public abstract class ForegroundServiceTypePolicy {
FOREGROUND_SERVICE_TYPE_SHORT_SERVICE,
ForegroundServiceTypePolicyInfo.INVALID_CHANGE_ID,
ForegroundServiceTypePolicyInfo.INVALID_CHANGE_ID,
- null /* no type specific permissions */, null /* no type specific permissions */
+ null /* allOfPermissions */,
+ null /* anyOfPermissions */,
+ null /* permissionEnforcementFlag */,
+ false /* permissionEnforcementFlagDefaultValue */
);
/**
@@ -412,7 +549,9 @@ public abstract class ForegroundServiceTypePolicy {
new ForegroundServiceTypePermissions(new ForegroundServiceTypePermission[] {
new RegularPermission(Manifest.permission.FOREGROUND_SERVICE_FILE_MANAGEMENT)
}, true),
- null
+ null /* anyOfPermissions */,
+ null /* permissionEnforcementFlag */,
+ false /* permissionEnforcementFlagDefaultValue */
);
/**
@@ -428,7 +567,9 @@ public abstract class ForegroundServiceTypePolicy {
new ForegroundServiceTypePermissions(new ForegroundServiceTypePermission[] {
new RegularPermission(Manifest.permission.FOREGROUND_SERVICE_SPECIAL_USE)
}, true),
- null
+ null /* anyOfPermissions */,
+ FGS_TYPE_PERM_ENFORCEMENT_FLAG_SPECIAL_USE /* permissionEnforcementFlag */,
+ true /* permissionEnforcementFlagDefaultValue */
);
/**
@@ -512,6 +653,14 @@ public abstract class ForegroundServiceTypePolicy {
@NonNull String packageName, int callerUid, int callerPid, boolean allowWhileInUse,
@NonNull ForegroundServiceTypePolicyInfo policy);
+ /**
+ * Run the given {@code policyFunctor} on the matching policy, if the flag is known
+ * to the policy.
+ *
+ * @hide
+ */
+ public abstract void updatePermissionEnforcementFlagIfNecessary(@NonNull String flag);
+
@GuardedBy("sLock")
private static ForegroundServiceTypePolicy sDefaultForegroundServiceTypePolicy = null;
@@ -575,11 +724,31 @@ public abstract class ForegroundServiceTypePolicy {
final @Nullable ForegroundServiceTypePermissions mAnyOfPermissions;
/**
+ * A permission enforcement flag, unlike the {@link #FGS_TYPE_PERMISSION_CHANGE_ID},
+ * here it applies to all apps using this FGS type.
+ */
+ final @Nullable String mPermissionEnforcementFlag;
+
+ /**
+ * The default value to {@link #mPermissionEnforcementFlag}.
+ */
+ final boolean mPermissionEnforcementFlagDefaultValue;
+
+ /**
* A customized check for the permissions.
*/
@Nullable ForegroundServiceTypePermission mCustomPermission;
/**
+ * The value of the permission enforcement flag, will be updated by the system.
+ * If the value is {@code false}, the FGS permission check will be ignored.
+ *
+ * <p>This value could be updated via the DeviceConfig flag specified
+ * in the {@link #mPermissionEnforcementFlag}.</p>
+ */
+ volatile boolean mPermissionEnforcementFlagValue;
+
+ /**
* Not a real change id, but a place holder.
*/
private static final long INVALID_CHANGE_ID = 0L;
@@ -599,12 +768,17 @@ public abstract class ForegroundServiceTypePolicy {
public ForegroundServiceTypePolicyInfo(@ForegroundServiceType int type,
long deprecationChangeId, long disabledChangeId,
@Nullable ForegroundServiceTypePermissions allOfPermissions,
- @Nullable ForegroundServiceTypePermissions anyOfPermissions) {
+ @Nullable ForegroundServiceTypePermissions anyOfPermissions,
+ @Nullable String permissionEnforcementFlag,
+ boolean permissionEnforcementFlagDefaultValue) {
mType = type;
mDeprecationChangeId = deprecationChangeId;
mDisabledChangeId = disabledChangeId;
mAllOfPermissions = allOfPermissions;
mAnyOfPermissions = anyOfPermissions;
+ mPermissionEnforcementFlag = permissionEnforcementFlag;
+ mPermissionEnforcementFlagDefaultValue = permissionEnforcementFlagDefaultValue;
+ mPermissionEnforcementFlagValue = permissionEnforcementFlagDefaultValue;
}
/**
@@ -650,6 +824,17 @@ public abstract class ForegroundServiceTypePolicy {
return sb;
}
+ private void updatePermissionEnforcementFlagIfNecessary(@NonNull String flagName) {
+ if (mPermissionEnforcementFlag == null
+ || !TextUtils.equals(flagName, mPermissionEnforcementFlag)) {
+ return;
+ }
+ mPermissionEnforcementFlagValue = DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ mPermissionEnforcementFlag,
+ mPermissionEnforcementFlagDefaultValue);
+ }
+
/**
* @hide
*/
@@ -745,6 +930,15 @@ public abstract class ForegroundServiceTypePolicy {
ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE));
platformCompat.clearOverrideForTest(changeId, packageName);
}
+
+ /**
+ * For test only.
+ *
+ * @return The permission enforcement flag.
+ */
+ public @Nullable String getPermissionEnforcementFlagForTest() {
+ return mPermissionEnforcementFlag;
+ }
}
/**
@@ -991,6 +1185,12 @@ public abstract class ForegroundServiceTypePolicy {
new SparseArray<>();
/**
+ * The map between permission enforcement flag to its permission policy info.
+ */
+ private final ArrayMap<String, ForegroundServiceTypePolicyInfo>
+ mPermissionEnforcementToPolicyInfoMap = new ArrayMap<>();
+
+ /**
* Constructor
*/
public DefaultForegroundServiceTypePolicy() {
@@ -1022,10 +1222,15 @@ public abstract class ForegroundServiceTypePolicy {
FGS_TYPE_POLICY_SYSTEM_EXEMPTED);
mForegroundServiceTypePolicies.put(FOREGROUND_SERVICE_TYPE_SHORT_SERVICE,
FGS_TYPE_POLICY_SHORT_SERVICE);
- mForegroundServiceTypePolicies.put(FOREGROUND_SERVICE_TYPE_FILE_MANAGEMENT,
- FGS_TYPE_POLICY_FILE_MANAGEMENT);
+ // TODO (b/271950506): revisit it in the next release.
+ // Hide the file management type for now. If anyone uses it, will default to "none".
mForegroundServiceTypePolicies.put(FOREGROUND_SERVICE_TYPE_SPECIAL_USE,
FGS_TYPE_POLICY_SPECIAL_USE);
+ for (int i = 0, size = mForegroundServiceTypePolicies.size(); i < size; i++) {
+ final ForegroundServiceTypePolicyInfo info =
+ mForegroundServiceTypePolicies.valueAt(i);
+ mPermissionEnforcementToPolicyInfoMap.put(info.mPermissionEnforcementFlag, info);
+ }
}
@Override
@@ -1078,8 +1283,8 @@ public abstract class ForegroundServiceTypePolicy {
}
}
if (permissionResult != PERMISSION_GRANTED) {
- return (CompatChanges.isChangeEnabled(
- FGS_TYPE_PERMISSION_CHANGE_ID, callerUid))
+ return policy.mPermissionEnforcementFlagValue
+ && (CompatChanges.isChangeEnabled(FGS_TYPE_PERMISSION_CHANGE_ID, callerUid))
? FGS_TYPE_POLICY_CHECK_PERMISSION_DENIED_ENFORCED
: FGS_TYPE_POLICY_CHECK_PERMISSION_DENIED_PERMISSIVE;
}
@@ -1089,5 +1294,14 @@ public abstract class ForegroundServiceTypePolicy {
}
return FGS_TYPE_POLICY_CHECK_OK;
}
+
+ @Override
+ public void updatePermissionEnforcementFlagIfNecessary(@NonNull String flagName) {
+ final ForegroundServiceTypePolicyInfo info =
+ mPermissionEnforcementToPolicyInfoMap.get(flagName);
+ if (info != null) {
+ info.updatePermissionEnforcementFlagIfNecessary(flagName);
+ }
+ }
}
}
diff --git a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
index cb678b98a998..5a48176d29e7 100644
--- a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
+++ b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
@@ -26,7 +26,6 @@ import android.hardware.camera2.CameraMetadata;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.utils.HashCodeHelpers;
import android.hardware.camera2.utils.SurfaceUtils;
-import android.util.Log;
import android.util.Range;
import android.util.Size;
import android.util.SparseIntArray;
@@ -2019,7 +2018,7 @@ public final class StreamConfigurationMap {
/**
* @hide
*/
- public static final int HAL_DATASPACE_HEIF = 0x1003;
+ public static final int HAL_DATASPACE_HEIF = 0x1004;
/**
* @hide
*/
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index a8b4da14c1c5..24dcb69f8342 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -5718,7 +5718,7 @@ public final class ViewRootImpl implements ViewParent,
private static final int MSG_WINDOW_TOUCH_MODE_CHANGED = 34;
private static final int MSG_KEEP_CLEAR_RECTS_CHANGED = 35;
private static final int MSG_REPORT_KEEP_CLEAR_RECTS = 36;
-
+ private static final int MSG_PAUSED_FOR_SYNC_TIMEOUT = 37;
final class ViewRootHandler extends Handler {
@Override
@@ -6006,6 +6006,11 @@ public final class ViewRootImpl implements ViewParent,
case MSG_REQUEST_SCROLL_CAPTURE:
handleScrollCaptureRequest((IScrollCaptureResponseListener) msg.obj);
break;
+ case MSG_PAUSED_FOR_SYNC_TIMEOUT:
+ Log.e(mTag, "Timedout waiting to unpause for sync");
+ mNumPausedForSync = 0;
+ scheduleTraversals();
+ break;
}
}
}
@@ -11584,9 +11589,16 @@ public final class ViewRootImpl implements ViewParent,
mActiveSurfaceSyncGroup = new SurfaceSyncGroup(mTag);
mActiveSurfaceSyncGroup.setAddedToSyncListener(() -> {
Runnable runnable = () -> {
- mNumPausedForSync--;
- if (!mIsInTraversal && mNumPausedForSync == 0) {
- scheduleTraversals();
+ // Check if it's already 0 because the timeout could have reset the count to
+ // 0 and we don't want to go negative.
+ if (mNumPausedForSync > 0) {
+ mNumPausedForSync--;
+ }
+ if (mNumPausedForSync == 0) {
+ mHandler.removeMessages(MSG_PAUSED_FOR_SYNC_TIMEOUT);
+ if (!mIsInTraversal) {
+ scheduleTraversals();
+ }
}
};
@@ -11613,6 +11625,8 @@ public final class ViewRootImpl implements ViewParent,
}
mNumPausedForSync++;
+ mHandler.removeMessages(MSG_PAUSED_FOR_SYNC_TIMEOUT);
+ mHandler.sendEmptyMessageDelayed(MSG_PAUSED_FOR_SYNC_TIMEOUT, 1000);
return mActiveSurfaceSyncGroup;
};
diff --git a/core/java/android/window/SurfaceSyncGroup.java b/core/java/android/window/SurfaceSyncGroup.java
index 78de9544b59b..0672d6318212 100644
--- a/core/java/android/window/SurfaceSyncGroup.java
+++ b/core/java/android/window/SurfaceSyncGroup.java
@@ -22,6 +22,8 @@ import android.annotation.UiThread;
import android.os.Binder;
import android.os.BinderProxy;
import android.os.Debug;
+import android.os.Handler;
+import android.os.HandlerThread;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.Trace;
@@ -60,6 +62,7 @@ public final class SurfaceSyncGroup {
private static final int MAX_COUNT = 100;
private static final AtomicInteger sCounter = new AtomicInteger(0);
+ private static final int TRANSACTION_READY_TIMEOUT = 1000;
private static Supplier<Transaction> sTransactionFactory = Transaction::new;
@@ -111,6 +114,13 @@ public final class SurfaceSyncGroup {
*/
private final Binder mToken = new Binder();
+ private static final Object sHandlerThreadLock = new Object();
+ @GuardedBy("sHandlerThreadLock")
+ private static HandlerThread sHandlerThread;
+ private Handler mHandler;
+
+ private boolean mTimeoutAdded;
+
private static boolean isLocalBinder(IBinder binder) {
return !(binder instanceof BinderProxy);
}
@@ -538,6 +548,11 @@ public final class SurfaceSyncGroup {
+ transactionReadyCallback.hashCode());
}
+ // Start the timeout when this SurfaceSyncGroup has been added to a parent SurfaceSyncGroup.
+ // This is because if the other SurfaceSyncGroup has bugs and doesn't complete, this SSG
+ // will get stuck. It's better to complete this SSG even if the parent SSG is broken.
+ addTimeout();
+
boolean finished = false;
Runnable addedToSyncListener = null;
synchronized (mLock) {
@@ -641,6 +656,9 @@ public final class SurfaceSyncGroup {
}
mTransactionReadyConsumer.accept(mTransaction);
mFinished = true;
+ if (mTimeoutAdded) {
+ mHandler.removeCallbacksAndMessages(this);
+ }
}
/**
@@ -701,6 +719,12 @@ public final class SurfaceSyncGroup {
}
}
+ // Start the timeout when another SSG has been added to this SurfaceSyncGroup. This is
+ // because if the other SurfaceSyncGroup has bugs and doesn't complete, it will affect this
+ // SSGs. So it's better to just add a timeout in case the other SSG doesn't invoke the
+ // callback and complete this SSG.
+ addTimeout();
+
return transactionReadyCallback;
}
@@ -731,6 +755,41 @@ public final class SurfaceSyncGroup {
}
}
+ private void addTimeout() {
+ synchronized (sHandlerThreadLock) {
+ if (sHandlerThread == null) {
+ sHandlerThread = new HandlerThread("SurfaceSyncGroupTimer");
+ sHandlerThread.start();
+ }
+ }
+
+ synchronized (mLock) {
+ if (mTimeoutAdded) {
+ // We only need one timeout for the entire SurfaceSyncGroup since we just want to
+ // ensure it doesn't stay stuck forever.
+ return;
+ }
+
+ if (mHandler == null) {
+ mHandler = new Handler(sHandlerThread.getLooper());
+ }
+
+ mTimeoutAdded = true;
+ }
+
+ Runnable runnable = () -> {
+ Log.e(TAG, "Failed to receive transaction ready in " + TRANSACTION_READY_TIMEOUT
+ + "ms. Marking SurfaceSyncGroup as ready " + mName);
+ // Clear out any pending syncs in case the other syncs can't complete or timeout due to
+ // a crash.
+ synchronized (mLock) {
+ mPendingSyncs.clear();
+ }
+ markSyncReady();
+ };
+ mHandler.postDelayed(runnable, this, TRANSACTION_READY_TIMEOUT);
+ }
+
/**
* A frame callback that is used to synchronize SurfaceViews. The owner of the SurfaceView must
* implement onFrameStarted when trying to sync the SurfaceView. This is to ensure the sync
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 8fdefab8ff2c..5be18fb482b5 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -4203,6 +4203,16 @@
<permission android:name="android.permission.WRITE_DEVICE_CONFIG"
android:protectionLevel="signature|verifier|configurator"/>
+ <!-- @SystemApi @TestApi @hide Allows an application to read/write sync disabled mode config.
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.READ_WRITE_SYNC_DISABLED_MODE_CONFIG"
+ android:protectionLevel="signature|verifier|configurator"/>
+
+ <!-- @SystemApi @TestApi @hide Allows an application to modify only allowlisted settings.
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.ALLOWLISTED_WRITE_DEVICE_CONFIG"
+ android:protectionLevel="signature|verifier|configurator"/>
+
<!-- @SystemApi @hide Allows an application to read config settings.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.READ_DEVICE_CONFIG"
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 16511a678760..27c477f3f241 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3020,10 +3020,18 @@
>com.android.systemui/com.android.systemui.wifi.WifiDebuggingSecondaryUserActivity</string>
<!-- Package name of the system service that implements the shared connectivity service -->
- <string translatable="false" name="shared_connectivity_service_package"></string>
+ <string translatable="false" name="config_sharedConnectivityServicePackage"></string>
<!-- Intent action used when binding to the shared connectivity service -->
- <string translatable="false" name="shared_connectivity_service_intent_action"></string>
+ <string translatable="false" name="config_sharedConnectivityServiceIntentAction"></string>
+
+ <!-- The system and settings UI can support all the features of instant tether. If set to false,
+ instant tether will run in notifications mode -->
+ <bool name="config_hotspotNetworksEnabledForService">false</bool>
+
+ <!-- The system and settings UI can support all the features of known networks. If set to false,
+ known networks will run in notifications mode -->
+ <bool name="config_knownNetworksEnabledForService">false</bool>
<!-- Component name of the activity that shows the usb containment status. -->
<string name="config_usbContaminantActivity" translatable="false"
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index c34d31cca7ba..a15833d36870 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -4960,8 +4960,10 @@
<java-symbol type="bool" name="config_stopSystemPackagesByDefault"/>
<java-symbol type="string" name="config_wearServiceComponent" />
- <java-symbol type="string" name="shared_connectivity_service_package" />
- <java-symbol type="string" name="shared_connectivity_service_intent_action" />
+ <java-symbol type="string" name="config_sharedConnectivityServicePackage" />
+ <java-symbol type="string" name="config_sharedConnectivityServiceIntentAction" />
+ <java-symbol type="bool" name="config_hotspotNetworksEnabledForService"/>
+ <java-symbol type="bool" name="config_knownNetworksEnabledForService"/>
<!-- Whether to show weather on the lockscreen by default. -->
<java-symbol type="bool" name="config_lockscreenWeatherEnabledByDefault" />
diff --git a/core/tests/coretests/src/android/provider/NameValueCacheTest.java b/core/tests/coretests/src/android/provider/NameValueCacheTest.java
index ccf8085f87ff..b6fc137471a4 100644
--- a/core/tests/coretests/src/android/provider/NameValueCacheTest.java
+++ b/core/tests/coretests/src/android/provider/NameValueCacheTest.java
@@ -91,7 +91,7 @@ public class NameValueCacheTest {
mConfigsCacheGenerationStore = new MemoryIntArray(2);
mConfigsCacheGenerationStore.set(0, 123);
mConfigsCacheGenerationStore.set(1, 456);
- mSettingsCacheGenerationStore = new MemoryIntArray(3);
+ mSettingsCacheGenerationStore = new MemoryIntArray(2);
mSettingsCacheGenerationStore.set(0, 234);
mSettingsCacheGenerationStore.set(1, 567);
mConfigsStorage = new HashMap<>();
@@ -163,10 +163,6 @@ public class NameValueCacheTest {
Bundle incomingBundle = invocationOnMock.getArgument(4);
String key = invocationOnMock.getArgument(3);
String value = incomingBundle.getString(Settings.NameValueTable.VALUE);
- boolean newSetting = false;
- if (!mSettingsStorage.containsKey(key)) {
- newSetting = true;
- }
mSettingsStorage.put(key, value);
int currentGeneration;
// Different settings have different generation codes
@@ -177,18 +173,12 @@ public class NameValueCacheTest {
currentGeneration = mSettingsCacheGenerationStore.get(1);
mSettingsCacheGenerationStore.set(1, ++currentGeneration);
}
- if (newSetting) {
- // Tracking the generation of all unset settings.
- // Increment when a new setting is inserted
- currentGeneration = mSettingsCacheGenerationStore.get(2);
- mSettingsCacheGenerationStore.set(2, ++currentGeneration);
- }
return null;
});
// Returns the value corresponding to a setting, or null if the setting
- // doesn't have a value stored for it. Returns the generation key
- // if the caller asked for the generation key.
+ // doesn't have a value stored for it. Returns the generation key if the value isn't null
+ // and the caller asked for the generation key.
when(mMockIContentProvider.call(any(), eq(Settings.Secure.CONTENT_URI.getAuthority()),
eq(Settings.CALL_METHOD_GET_SECURE), any(), any(Bundle.class))).thenAnswer(
invocationOnMock -> {
@@ -199,15 +189,9 @@ public class NameValueCacheTest {
Bundle bundle = new Bundle();
bundle.putSerializable(Settings.NameValueTable.VALUE, value);
- if (incomingBundle.containsKey(
+ if (value != null && incomingBundle.containsKey(
Settings.CALL_METHOD_TRACK_GENERATION_KEY)) {
- int index;
- if (value != null) {
- index = key.equals(SETTING) ? 0 : 1;
- } else {
- // special index for unset settings
- index = 2;
- }
+ int index = key.equals(SETTING) ? 0 : 1;
bundle.putParcelable(Settings.CALL_METHOD_TRACK_GENERATION_KEY,
mSettingsCacheGenerationStore);
bundle.putInt(Settings.CALL_METHOD_GENERATION_INDEX_KEY, index);
@@ -377,38 +361,16 @@ public class NameValueCacheTest {
}
@Test
- public void testCaching_unsetSetting() throws Exception {
+ public void testCaching_nullSetting() throws Exception {
String returnedValue = Settings.Secure.getString(mMockContentResolver, SETTING);
verify(mMockIContentProvider, times(1)).call(any(), any(),
eq(Settings.CALL_METHOD_GET_SECURE), any(), any(Bundle.class));
assertThat(returnedValue).isNull();
String cachedValue = Settings.Secure.getString(mMockContentResolver, SETTING);
- // The first unset setting's generation number is cached
- verifyNoMoreInteractions(mMockIContentProvider);
- assertThat(cachedValue).isNull();
-
- String returnedValue2 = Settings.Secure.getString(mMockContentResolver, SETTING2);
+ // Empty list won't be cached
verify(mMockIContentProvider, times(2)).call(any(), any(),
eq(Settings.CALL_METHOD_GET_SECURE), any(), any(Bundle.class));
- assertThat(returnedValue2).isNull();
-
- String cachedValue2 = Settings.Secure.getString(mMockContentResolver, SETTING);
- // The second unset setting's generation number is cached
- verifyNoMoreInteractions(mMockIContentProvider);
- assertThat(cachedValue2).isNull();
-
- Settings.Secure.putString(mMockContentResolver, SETTING, "a");
- // The generation for unset settings should have changed
- returnedValue2 = Settings.Secure.getString(mMockContentResolver, SETTING2);
- verify(mMockIContentProvider, times(3)).call(any(), any(),
- eq(Settings.CALL_METHOD_GET_SECURE), any(), any(Bundle.class));
- assertThat(returnedValue2).isNull();
-
- // The generation tracker for the first setting should have change because it's set now
- returnedValue = Settings.Secure.getString(mMockContentResolver, SETTING);
- verify(mMockIContentProvider, times(4)).call(any(), any(),
- eq(Settings.CALL_METHOD_GET_SECURE), any(), any(Bundle.class));
- assertThat(returnedValue).isEqualTo("a");
+ assertThat(cachedValue).isNull();
}
}
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 913eaf2391f6..ffc5ff226607 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -1099,6 +1099,12 @@
"group": "WM_DEBUG_ORIENTATION",
"at": "com\/android\/server\/wm\/WindowContainer.java"
},
+ "-1104347731": {
+ "message": "Setting requested orientation %s for %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ORIENTATION",
+ "at": "com\/android\/server\/wm\/ActivityRecord.java"
+ },
"-1103716954": {
"message": "Not removing %s due to exit animation",
"level": "VERBOSE",
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index 852fae695046..ef53839cb2ea 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -139,6 +139,30 @@ public class BubbleController implements ConfigurationChangeListener {
private static final boolean BUBBLE_BAR_ENABLED =
SystemProperties.getBoolean("persist.wm.debug.bubble_bar", false);
+
+ /**
+ * Common interface to send updates to bubble views.
+ */
+ public interface BubbleViewCallback {
+ /** Called when the provided bubble should be removed. */
+ void removeBubble(Bubble removedBubble);
+ /** Called when the provided bubble should be added. */
+ void addBubble(Bubble addedBubble);
+ /** Called when the provided bubble should be updated. */
+ void updateBubble(Bubble updatedBubble);
+ /** Called when the provided bubble should be selected. */
+ void selectionChanged(BubbleViewProvider selectedBubble);
+ /** Called when the provided bubble's suppression state has changed. */
+ void suppressionChanged(Bubble bubble, boolean isSuppressed);
+ /** Called when the expansion state of bubbles has changed. */
+ void expansionChanged(boolean isExpanded);
+ /**
+ * Called when the order of the bubble list has changed. Depending on the expanded state
+ * the pointer might need to be updated.
+ */
+ void bubbleOrderChanged(List<Bubble> bubbleOrder, boolean updatePointer);
+ }
+
private final Context mContext;
private final BubblesImpl mImpl = new BubblesImpl();
private Bubbles.BubbleExpandListener mExpandListener;
@@ -162,7 +186,6 @@ public class BubbleController implements ConfigurationChangeListener {
// Used to post to main UI thread
private final ShellExecutor mMainExecutor;
private final Handler mMainHandler;
-
private final ShellExecutor mBackgroundExecutor;
private BubbleLogger mLogger;
@@ -1320,6 +1343,58 @@ public class BubbleController implements ConfigurationChangeListener {
});
}
+ private final BubbleViewCallback mBubbleViewCallback = new BubbleViewCallback() {
+ @Override
+ public void removeBubble(Bubble removedBubble) {
+ if (mStackView != null) {
+ mStackView.removeBubble(removedBubble);
+ }
+ }
+
+ @Override
+ public void addBubble(Bubble addedBubble) {
+ if (mStackView != null) {
+ mStackView.addBubble(addedBubble);
+ }
+ }
+
+ @Override
+ public void updateBubble(Bubble updatedBubble) {
+ if (mStackView != null) {
+ mStackView.updateBubble(updatedBubble);
+ }
+ }
+
+ @Override
+ public void bubbleOrderChanged(List<Bubble> bubbleOrder, boolean updatePointer) {
+ if (mStackView != null) {
+ mStackView.updateBubbleOrder(bubbleOrder, updatePointer);
+ }
+ }
+
+ @Override
+ public void suppressionChanged(Bubble bubble, boolean isSuppressed) {
+ if (mStackView != null) {
+ mStackView.setBubbleSuppressed(bubble, isSuppressed);
+ }
+ }
+
+ @Override
+ public void expansionChanged(boolean isExpanded) {
+ if (mStackView != null) {
+ mStackView.setExpanded(isExpanded);
+ }
+ }
+
+ @Override
+ public void selectionChanged(BubbleViewProvider selectedBubble) {
+ if (mStackView != null) {
+ mStackView.setSelectedBubble(selectedBubble);
+ }
+
+ }
+ };
+
@SuppressWarnings("FieldCanBeLocal")
private final BubbleData.Listener mBubbleDataListener = new BubbleData.Listener() {
@@ -1342,7 +1417,8 @@ public class BubbleController implements ConfigurationChangeListener {
// Lazy load overflow bubbles from disk
loadOverflowBubblesFromDisk();
- mStackView.updateOverflowButtonDot();
+ // If bubbles in the overflow have a dot, make sure the overflow shows a dot
+ updateOverflowButtonDot();
// Update bubbles in overflow.
if (mOverflowListener != null) {
@@ -1357,9 +1433,7 @@ public class BubbleController implements ConfigurationChangeListener {
final Bubble bubble = removed.first;
@Bubbles.DismissReason final int reason = removed.second;
- if (mStackView != null) {
- mStackView.removeBubble(bubble);
- }
+ mBubbleViewCallback.removeBubble(bubble);
// Leave the notification in place if we're dismissing due to user switching, or
// because DND is suppressing the bubble. In both of those cases, we need to be able
@@ -1389,49 +1463,47 @@ public class BubbleController implements ConfigurationChangeListener {
}
mDataRepository.removeBubbles(mCurrentUserId, bubblesToBeRemovedFromRepository);
- if (update.addedBubble != null && mStackView != null) {
+ if (update.addedBubble != null) {
mDataRepository.addBubble(mCurrentUserId, update.addedBubble);
- mStackView.addBubble(update.addedBubble);
+ mBubbleViewCallback.addBubble(update.addedBubble);
}
- if (update.updatedBubble != null && mStackView != null) {
- mStackView.updateBubble(update.updatedBubble);
+ if (update.updatedBubble != null) {
+ mBubbleViewCallback.updateBubble(update.updatedBubble);
}
- if (update.suppressedBubble != null && mStackView != null) {
- mStackView.setBubbleSuppressed(update.suppressedBubble, true);
+ if (update.suppressedBubble != null) {
+ mBubbleViewCallback.suppressionChanged(update.suppressedBubble, true);
}
- if (update.unsuppressedBubble != null && mStackView != null) {
- mStackView.setBubbleSuppressed(update.unsuppressedBubble, false);
+ if (update.unsuppressedBubble != null) {
+ mBubbleViewCallback.suppressionChanged(update.unsuppressedBubble, false);
}
boolean collapseStack = update.expandedChanged && !update.expanded;
// At this point, the correct bubbles are inflated in the stack.
// Make sure the order in bubble data is reflected in bubble row.
- if (update.orderChanged && mStackView != null) {
+ if (update.orderChanged) {
mDataRepository.addBubbles(mCurrentUserId, update.bubbles);
// if the stack is going to be collapsed, do not update pointer position
// after reordering
- mStackView.updateBubbleOrder(update.bubbles, !collapseStack);
+ mBubbleViewCallback.bubbleOrderChanged(update.bubbles, !collapseStack);
}
if (collapseStack) {
- mStackView.setExpanded(false);
+ mBubbleViewCallback.expansionChanged(/* expanded= */ false);
mSysuiProxy.requestNotificationShadeTopUi(false, TAG);
}
- if (update.selectionChanged && mStackView != null) {
- mStackView.setSelectedBubble(update.selectedBubble);
+ if (update.selectionChanged) {
+ mBubbleViewCallback.selectionChanged(update.selectedBubble);
}
// Expanding? Apply this last.
if (update.expandedChanged && update.expanded) {
- if (mStackView != null) {
- mStackView.setExpanded(true);
- mSysuiProxy.requestNotificationShadeTopUi(true, TAG);
- }
+ mBubbleViewCallback.expansionChanged(/* expanded= */ true);
+ mSysuiProxy.requestNotificationShadeTopUi(true, TAG);
}
mSysuiProxy.notifyInvalidateNotifications("BubbleData.Listener.applyUpdate");
@@ -1442,6 +1514,19 @@ public class BubbleController implements ConfigurationChangeListener {
}
};
+ private void updateOverflowButtonDot() {
+ BubbleOverflow overflow = mBubbleData.getOverflow();
+ if (overflow == null) return;
+
+ for (Bubble b : mBubbleData.getOverflowBubbles()) {
+ if (b.showDot()) {
+ overflow.setShowDot(true);
+ return;
+ }
+ }
+ overflow.setShowDot(false);
+ }
+
private boolean handleDismissalInterception(BubbleEntry entry,
@Nullable List<BubbleEntry> children, IntConsumer removeCallback) {
if (isSummaryOfBubbles(entry)) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index 7d71089ef4fa..0b947c8b9b08 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -1359,16 +1359,6 @@ public class BubbleStackView extends FrameLayout
updateOverflowVisibility();
}
- void updateOverflowButtonDot() {
- for (Bubble b : mBubbleData.getOverflowBubbles()) {
- if (b.showDot()) {
- mBubbleOverflow.setShowDot(true);
- return;
- }
- }
- mBubbleOverflow.setShowDot(false);
- }
-
/**
* Handle theme changes.
*/
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizer.java
index 9796e4c29352..2d84d211e30a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizer.java
@@ -267,6 +267,11 @@ public class KidsModeTaskOrganizer extends ShellTaskOrganizer {
mLaunchRootTask = taskInfo;
}
+ if (mHomeTask != null && mHomeTask.taskId == taskInfo.taskId
+ && !taskInfo.equals(mHomeTask)) {
+ mHomeTask = taskInfo;
+ }
+
super.onTaskInfoChanged(taskInfo);
}
@@ -376,6 +381,7 @@ public class KidsModeTaskOrganizer extends ShellTaskOrganizer {
final WindowContainerTransaction wct = getWindowContainerTransaction();
final Rect taskBounds = calculateBounds();
wct.setBounds(mLaunchRootTask.token, taskBounds);
+ wct.setBounds(mHomeTask.token, new Rect(0, 0, mDisplayWidth, mDisplayHeight));
mSyncQueue.queue(wct);
final SurfaceControl finalLeash = mLaunchRootLeash;
mSyncQueue.runInSync(t -> {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 18a3849ea17d..a5546e554422 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -207,6 +207,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
private boolean mIsDividerRemoteAnimating;
private boolean mIsDropEntering;
private boolean mIsExiting;
+ private boolean mIsRootTranslucent;
private DefaultMixedHandler mMixedHandler;
private final Toast mSplitUnsupportedToast;
@@ -422,6 +423,11 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
}
}
+ if (!isSplitActive()) {
+ // prevent the fling divider to center transitioni if split screen didn't active.
+ mIsDropEntering = true;
+ }
+
setSideStagePosition(sideStagePosition, wct);
final WindowContainerTransaction evictWct = new WindowContainerTransaction();
targetStage.evictAllChildren(evictWct);
@@ -436,28 +442,13 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
// reparent the task to an invisible split root will make the activity invisible. Reorder
// the root task to front to make the entering transition from pip to split smooth.
wct.reorder(mRootTaskInfo.token, true);
- wct.setForceTranslucent(mRootTaskInfo.token, true);
wct.reorder(targetStage.mRootTaskInfo.token, true);
- wct.setForceTranslucent(targetStage.mRootTaskInfo.token, true);
- // prevent the fling divider to center transition
- mIsDropEntering = true;
-
targetStage.addTask(task, wct);
- if (ENABLE_SHELL_TRANSITIONS) {
- prepareEnterSplitScreen(wct);
- mSplitTransitions.startEnterTransition(TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE, wct,
- null, this, null /* consumedCallback */, (finishWct, finishT) -> {
- if (!evictWct.isEmpty()) {
- finishWct.merge(evictWct, true);
- }
- } /* finishedCallback */);
- } else {
- if (!evictWct.isEmpty()) {
- wct.merge(evictWct, true /* transfer */);
- }
- mTaskOrganizer.applyTransaction(wct);
+ if (!evictWct.isEmpty()) {
+ wct.merge(evictWct, true /* transfer */);
}
+ mTaskOrganizer.applyTransaction(wct);
return true;
}
@@ -716,7 +707,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mSplitLayout.setDivideRatio(splitRatio);
updateWindowBounds(mSplitLayout, wct);
wct.reorder(mRootTaskInfo.token, true);
- wct.setForceTranslucent(mRootTaskInfo.token, false);
+ setRootForceTranslucent(false, wct);
// Make sure the launch options will put tasks in the corresponding split roots
mainOptions = mainOptions != null ? mainOptions : new Bundle();
@@ -923,7 +914,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
updateWindowBounds(mSplitLayout, wct);
wct.reorder(mRootTaskInfo.token, true);
- wct.setForceTranslucent(mRootTaskInfo.token, false);
+ setRootForceTranslucent(false, wct);
// TODO(b/268008375): Merge APIs to start a split pair into one.
if (mainTaskId != INVALID_TASK_ID) {
@@ -1351,7 +1342,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mSideStage.removeAllTasks(wct, false /* toTop */);
mMainStage.deactivate(wct, false /* toTop */);
wct.reorder(mRootTaskInfo.token, false /* onTop */);
- wct.setForceTranslucent(mRootTaskInfo.token, true);
+ setRootForceTranslucent(true, wct);
wct.setBounds(mSideStage.mRootTaskInfo.token, mTempRect1);
onTransitionAnimationComplete();
} else {
@@ -1383,7 +1374,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mMainStage.deactivate(finishedWCT, childrenToTop == mMainStage /* toTop */);
mSideStage.removeAllTasks(finishedWCT, childrenToTop == mSideStage /* toTop */);
finishedWCT.reorder(mRootTaskInfo.token, false /* toTop */);
- finishedWCT.setForceTranslucent(mRootTaskInfo.token, true);
+ setRootForceTranslucent(true, wct);
finishedWCT.setBounds(mSideStage.mRootTaskInfo.token, mTempRect1);
mSyncQueue.queue(finishedWCT);
mSyncQueue.runInSync(at -> {
@@ -1505,7 +1496,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mMainStage.activate(wct, true /* includingTopTask */);
updateWindowBounds(mSplitLayout, wct);
wct.reorder(mRootTaskInfo.token, true);
- wct.setForceTranslucent(mRootTaskInfo.token, false);
+ setRootForceTranslucent(false, wct);
}
void finishEnterSplitScreen(SurfaceControl.Transaction t) {
@@ -1709,6 +1700,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mRootTaskInfo = null;
mRootTaskLeash = null;
+ mIsRootTranslucent = false;
}
@@ -1727,7 +1719,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
// Make the stages adjacent to each other so they occlude what's behind them.
wct.setAdjacentRoots(mMainStage.mRootTaskInfo.token, mSideStage.mRootTaskInfo.token);
wct.setLaunchAdjacentFlagRoot(mSideStage.mRootTaskInfo.token);
- wct.setForceTranslucent(mRootTaskInfo.token, true);
+ setRootForceTranslucent(true, wct);
mSplitLayout.getInvisibleBounds(mTempRect1);
wct.setBounds(mSideStage.mRootTaskInfo.token, mTempRect1);
mSyncQueue.queue(wct);
@@ -1751,7 +1743,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mSideStage.evictOtherChildren(wct, taskId);
updateWindowBounds(mSplitLayout, wct);
wct.reorder(mRootTaskInfo.token, true);
- wct.setForceTranslucent(mRootTaskInfo.token, false);
+ setRootForceTranslucent(false, wct);
mSyncQueue.queue(wct);
mSyncQueue.runInSync(t -> {
@@ -1775,6 +1767,13 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mDisplayInsetsController.removeInsetsChangedListener(mDisplayId, mSplitLayout);
}
+ private void setRootForceTranslucent(boolean translucent, WindowContainerTransaction wct) {
+ if (mIsRootTranslucent == translucent) return;
+
+ mIsRootTranslucent = translucent;
+ wct.setForceTranslucent(mRootTaskInfo.token, translucent);
+ }
+
private void onStageVisibilityChanged(StageListenerImpl stageListener) {
// If split didn't active, just ignore this callback because we should already did these
// on #applyExitSplitScreen.
@@ -1801,12 +1800,11 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
// Split entering background.
wct.setReparentLeafTaskIfRelaunch(mRootTaskInfo.token,
true /* setReparentLeafTaskIfRelaunch */);
- if (!mMainStage.mRootTaskInfo.isSleeping && !mSideStage.mRootTaskInfo.isSleeping) {
- wct.setForceTranslucent(mRootTaskInfo.token, true);
- }
+ setRootForceTranslucent(true, wct);
} else {
wct.setReparentLeafTaskIfRelaunch(mRootTaskInfo.token,
false /* setReparentLeafTaskIfRelaunch */);
+ setRootForceTranslucent(false, wct);
}
mSyncQueue.queue(wct);
@@ -1938,7 +1936,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mMainStage.activate(wct, true /* includingTopTask */);
updateWindowBounds(mSplitLayout, wct);
wct.reorder(mRootTaskInfo.token, true);
- wct.setForceTranslucent(mRootTaskInfo.token, false);
+ setRootForceTranslucent(false, wct);
}
mSyncQueue.queue(wct);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskPositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskPositioner.java
index a3d364a0068e..0bce3acecb3c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskPositioner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskPositioner.java
@@ -40,6 +40,7 @@ class TaskPositioner implements DragPositioningCallback {
private final DisplayController mDisplayController;
private final WindowDecoration mWindowDecoration;
+ private final Rect mTempBounds = new Rect();
private final Rect mTaskBoundsAtDragStart = new Rect();
private final PointF mRepositionStartPoint = new PointF();
private final Rect mRepositionTaskBounds = new Rect();
@@ -117,17 +118,32 @@ class TaskPositioner implements DragPositioningCallback {
final float deltaX = x - mRepositionStartPoint.x;
final float deltaY = y - mRepositionStartPoint.y;
mRepositionTaskBounds.set(mTaskBoundsAtDragStart);
+
+ final Rect stableBounds = mTempBounds;
+ // Make sure the new resizing destination in any direction falls within the stable bounds.
+ // If not, set the bounds back to the old location that was valid to avoid conflicts with
+ // some regions such as the gesture area.
+ mDisplayController.getDisplayLayout(mWindowDecoration.mDisplay.getDisplayId())
+ .getStableBounds(stableBounds);
if ((mCtrlType & CTRL_TYPE_LEFT) != 0) {
- mRepositionTaskBounds.left += deltaX;
+ final int candidateLeft = mRepositionTaskBounds.left + (int) deltaX;
+ mRepositionTaskBounds.left = (candidateLeft > stableBounds.left)
+ ? candidateLeft : oldLeft;
}
if ((mCtrlType & CTRL_TYPE_RIGHT) != 0) {
- mRepositionTaskBounds.right += deltaX;
+ final int candidateRight = mRepositionTaskBounds.right + (int) deltaX;
+ mRepositionTaskBounds.right = (candidateRight < stableBounds.right)
+ ? candidateRight : oldRight;
}
if ((mCtrlType & CTRL_TYPE_TOP) != 0) {
- mRepositionTaskBounds.top += deltaY;
+ final int candidateTop = mRepositionTaskBounds.top + (int) deltaY;
+ mRepositionTaskBounds.top = (candidateTop > stableBounds.top)
+ ? candidateTop : oldTop;
}
if ((mCtrlType & CTRL_TYPE_BOTTOM) != 0) {
- mRepositionTaskBounds.bottom += deltaY;
+ final int candidateBottom = mRepositionTaskBounds.bottom + (int) deltaY;
+ mRepositionTaskBounds.bottom = (candidateBottom < stableBounds.bottom)
+ ? candidateBottom : oldBottom;
}
if (mCtrlType == CTRL_TYPE_UNDEFINED) {
mRepositionTaskBounds.offset((int) deltaX, (int) deltaY);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/TaskPositionerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/TaskPositionerTest.kt
index 8f66f4e7e47b..94c064bda763 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/TaskPositionerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/TaskPositionerTest.kt
@@ -5,13 +5,16 @@ import android.app.WindowConfiguration
import android.graphics.Rect
import android.os.IBinder
import android.testing.AndroidTestingRunner
+import android.view.Display
import android.window.WindowContainerToken
+import android.window.WindowContainerTransaction
import android.window.WindowContainerTransaction.Change.CHANGE_DRAG_RESIZING
import androidx.test.filters.SmallTest
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.common.DisplayLayout
import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.windowdecor.TaskPositioner.CTRL_TYPE_BOTTOM
import com.android.wm.shell.windowdecor.TaskPositioner.CTRL_TYPE_RIGHT
import com.android.wm.shell.windowdecor.TaskPositioner.CTRL_TYPE_TOP
import com.android.wm.shell.windowdecor.TaskPositioner.CTRL_TYPE_UNDEFINED
@@ -19,10 +22,11 @@ import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
+import org.mockito.Mockito.`when`
+import org.mockito.Mockito.any
import org.mockito.Mockito.argThat
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
-import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
/**
@@ -51,6 +55,8 @@ class TaskPositionerTest : ShellTestCase() {
private lateinit var mockDisplayController: DisplayController
@Mock
private lateinit var mockDisplayLayout: DisplayLayout
+ @Mock
+ private lateinit var mockDisplay: Display
private lateinit var taskPositioner: TaskPositioner
@@ -68,6 +74,9 @@ class TaskPositionerTest : ShellTestCase() {
`when`(taskToken.asBinder()).thenReturn(taskBinder)
`when`(mockDisplayController.getDisplayLayout(DISPLAY_ID)).thenReturn(mockDisplayLayout)
`when`(mockDisplayLayout.densityDpi()).thenReturn(DENSITY_DPI)
+ `when`(mockDisplayLayout.getStableBounds(any())).thenAnswer { i ->
+ (i.arguments.first() as Rect).set(STABLE_BOUNDS)
+ }
mockWindowDecoration.mTaskInfo = ActivityManager.RunningTaskInfo().apply {
taskId = TASK_ID
@@ -78,6 +87,8 @@ class TaskPositionerTest : ShellTestCase() {
displayId = DISPLAY_ID
configuration.windowConfiguration.bounds = STARTING_BOUNDS
}
+ mockWindowDecoration.mDisplay = mockDisplay
+ `when`(mockDisplay.displayId).thenAnswer { DISPLAY_ID }
}
@Test
@@ -451,6 +462,72 @@ class TaskPositionerTest : ShellTestCase() {
})
}
+ fun testDragResize_toDisallowedBounds_freezesAtLimit() {
+ taskPositioner.onDragPositioningStart(
+ CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM, // Resize right-bottom corner
+ STARTING_BOUNDS.right.toFloat(),
+ STARTING_BOUNDS.bottom.toFloat()
+ )
+
+ // Resize the task by 10px to the right and bottom, a valid destination
+ val newBounds = Rect(
+ STARTING_BOUNDS.left,
+ STARTING_BOUNDS.top,
+ STARTING_BOUNDS.right + 10,
+ STARTING_BOUNDS.bottom + 10)
+ taskPositioner.onDragPositioningMove(
+ newBounds.right.toFloat(),
+ newBounds.bottom.toFloat()
+ )
+
+ // Resize the task by another 10px to the right (allowed) and to just in the disallowed
+ // area of the Y coordinate.
+ val newBounds2 = Rect(
+ newBounds.left,
+ newBounds.top,
+ newBounds.right + 10,
+ DISALLOWED_RESIZE_AREA.top
+ )
+ taskPositioner.onDragPositioningMove(
+ newBounds2.right.toFloat(),
+ newBounds2.bottom.toFloat()
+ )
+
+ taskPositioner.onDragPositioningEnd(newBounds2.right.toFloat(), newBounds2.bottom.toFloat())
+
+ // The first resize falls in the allowed area, verify there's a change for it.
+ verify(mockShellTaskOrganizer).applyTransaction(argThat { wct ->
+ return@argThat wct.changes.any { (token, change) ->
+ token == taskBinder && change.ofBounds(newBounds)
+ }
+ })
+ // The second resize falls in the disallowed area, verify there's no change for it.
+ verify(mockShellTaskOrganizer, never()).applyTransaction(argThat { wct ->
+ return@argThat wct.changes.any { (token, change) ->
+ token == taskBinder && change.ofBounds(newBounds2)
+ }
+ })
+ // Instead, there should be a change for its allowed portion (the X movement) with the Y
+ // staying frozen in the last valid resize position.
+ verify(mockShellTaskOrganizer).applyTransaction(argThat { wct ->
+ return@argThat wct.changes.any { (token, change) ->
+ token == taskBinder && change.ofBounds(
+ Rect(
+ newBounds2.left,
+ newBounds2.top,
+ newBounds2.right,
+ newBounds.bottom // Stayed at the first resize destination.
+ )
+ )
+ }
+ })
+ }
+
+ private fun WindowContainerTransaction.Change.ofBounds(bounds: Rect): Boolean {
+ return ((windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0) &&
+ bounds == configuration.windowConfiguration.bounds
+ }
+
companion object {
private const val TASK_ID = 5
private const val MIN_WIDTH = 10
@@ -458,6 +535,19 @@ class TaskPositionerTest : ShellTestCase() {
private const val DENSITY_DPI = 20
private const val DEFAULT_MIN = 40
private const val DISPLAY_ID = 1
+ private const val NAVBAR_HEIGHT = 50
+ private val DISPLAY_BOUNDS = Rect(0, 0, 2400, 1600)
private val STARTING_BOUNDS = Rect(0, 0, 100, 100)
+ private val DISALLOWED_RESIZE_AREA = Rect(
+ DISPLAY_BOUNDS.left,
+ DISPLAY_BOUNDS.bottom - NAVBAR_HEIGHT,
+ DISPLAY_BOUNDS.right,
+ DISPLAY_BOUNDS.bottom)
+ private val STABLE_BOUNDS = Rect(
+ DISPLAY_BOUNDS.left,
+ DISPLAY_BOUNDS.top,
+ DISPLAY_BOUNDS.right,
+ DISPLAY_BOUNDS.bottom - NAVBAR_HEIGHT
+ )
}
}
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 4da4c7fc56de..f9b6ce0a199c 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -9564,7 +9564,10 @@ public class AudioManager {
* A stream type is considered independent when the volume changes of that type do not
* affect any other independent volume control stream type.
* An independent stream type is its own alias when using {@link #getStreamTypeAlias(int)}.
- * @return list of independent stream types.
+ * @return list of independent stream types, where each value can be one of
+ * {@link #STREAM_VOICE_CALL}, {@link #STREAM_SYSTEM}, {@link #STREAM_RING},
+ * {@link #STREAM_MUSIC}, {@link #STREAM_ALARM}, {@link #STREAM_NOTIFICATION},
+ * {@link #STREAM_DTMF} and {@link #STREAM_ACCESSIBILITY}.
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED)
diff --git a/media/java/android/media/tv/ITvInputClient.aidl b/media/java/android/media/tv/ITvInputClient.aidl
index c52cd59311c5..0f8a00a3c7d9 100644
--- a/media/java/android/media/tv/ITvInputClient.aidl
+++ b/media/java/android/media/tv/ITvInputClient.aidl
@@ -55,7 +55,7 @@ oneway interface ITvInputClient {
void onCueingMessageAvailability(boolean available, int seq);
void onTimeShiftMode(int mode, int seq);
void onAvailableSpeeds(in float[] speeds, int seq);
- void onTvMessage(in String type, in Bundle data, int seq);
+ void onTvMessage(int type, in Bundle data, int seq);
void onTuned(in Uri channelUri, int seq);
// For the recording session
diff --git a/media/java/android/media/tv/ITvInputManager.aidl b/media/java/android/media/tv/ITvInputManager.aidl
index 113c85802fa2..14c020e94c15 100644
--- a/media/java/android/media/tv/ITvInputManager.aidl
+++ b/media/java/android/media/tv/ITvInputManager.aidl
@@ -116,10 +116,10 @@ interface ITvInputManager {
// For ad request
void requestAd(in IBinder sessionToken, in AdRequest request, int userId);
- void notifyAdBuffer(in IBinder sessionToken, in AdBuffer buffer, int userId);
+ void notifyAdBufferReady(in IBinder sessionToken, in AdBuffer buffer, int userId);
// For TV Message
- void notifyTvMessage(in IBinder sessionToken, in String type, in Bundle data, int userId);
+ void notifyTvMessage(in IBinder sessionToken, int type, in Bundle data, int userId);
// For TV input hardware binding
List<TvInputHardwareInfo> getHardwareList();
diff --git a/media/java/android/media/tv/ITvInputSession.aidl b/media/java/android/media/tv/ITvInputSession.aidl
index 165a9ddc26e9..a7bd8d313ac3 100644
--- a/media/java/android/media/tv/ITvInputSession.aidl
+++ b/media/java/android/media/tv/ITvInputSession.aidl
@@ -75,8 +75,8 @@ oneway interface ITvInputSession {
// For ad request
void requestAd(in AdRequest request);
- void notifyAdBuffer(in AdBuffer buffer);
+ void notifyAdBufferReady(in AdBuffer buffer);
// For TV messages
- void notifyTvMessage(in String type, in Bundle data);
+ void notifyTvMessage(int type, in Bundle data);
}
diff --git a/media/java/android/media/tv/ITvInputSessionCallback.aidl b/media/java/android/media/tv/ITvInputSessionCallback.aidl
index 449c2d67067e..a52e9a572dca 100644
--- a/media/java/android/media/tv/ITvInputSessionCallback.aidl
+++ b/media/java/android/media/tv/ITvInputSessionCallback.aidl
@@ -66,5 +66,5 @@ oneway interface ITvInputSessionCallback {
void onAdBufferConsumed(in AdBuffer buffer);
// For messages sent from the TV input
- void onTvMessage(in String type, in Bundle data);
+ void onTvMessage(int type, in Bundle data);
}
diff --git a/media/java/android/media/tv/ITvInputSessionWrapper.java b/media/java/android/media/tv/ITvInputSessionWrapper.java
index 8389706b501c..7946baee200a 100644
--- a/media/java/android/media/tv/ITvInputSessionWrapper.java
+++ b/media/java/android/media/tv/ITvInputSessionWrapper.java
@@ -267,7 +267,7 @@ public class ITvInputSessionWrapper extends ITvInputSession.Stub implements Hand
}
case DO_SET_TV_MESSAGE_ENABLED: {
SomeArgs args = (SomeArgs) msg.obj;
- mTvInputSessionImpl.setTvMessageEnabled((String) args.arg1, (Boolean) args.arg2);
+ mTvInputSessionImpl.setTvMessageEnabled((Integer) args.arg1, (Boolean) args.arg2);
break;
}
case DO_REQUEST_AD: {
@@ -275,7 +275,7 @@ public class ITvInputSessionWrapper extends ITvInputSession.Stub implements Hand
break;
}
case DO_NOTIFY_AD_BUFFER: {
- mTvInputSessionImpl.notifyAdBuffer((AdBuffer) msg.obj);
+ mTvInputSessionImpl.notifyAdBufferReady((AdBuffer) msg.obj);
break;
}
case DO_NOTIFY_TV_MESSAGE: {
@@ -465,12 +465,12 @@ public class ITvInputSessionWrapper extends ITvInputSession.Stub implements Hand
}
@Override
- public void notifyAdBuffer(AdBuffer buffer) {
+ public void notifyAdBufferReady(AdBuffer buffer) {
mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_NOTIFY_AD_BUFFER, buffer));
}
@Override
- public void notifyTvMessage(String type, Bundle data) {
+ public void notifyTvMessage(int type, Bundle data) {
mCaller.executeOrSendMessage(mCaller.obtainMessageOO(DO_NOTIFY_TV_MESSAGE, type, data));
}
diff --git a/media/java/android/media/tv/TableResponse.java b/media/java/android/media/tv/TableResponse.java
index fb4e99cb1098..c4fc26ef1932 100644
--- a/media/java/android/media/tv/TableResponse.java
+++ b/media/java/android/media/tv/TableResponse.java
@@ -64,7 +64,10 @@ public final class TableResponse extends BroadcastInfoResponse implements Parcel
* @param tableUri The URI of the table in the database.
* @param version The version number of requested table.
* @param size The Size number of table in bytes.
+ *
+ * @deprecated use {@link Builder} instead.
*/
+ @Deprecated
public TableResponse(int requestId, int sequence, @ResponseResult int responseResult,
@Nullable Uri tableUri, int version, int size) {
super(RESPONSE_TYPE, requestId, sequence, responseResult);
@@ -76,30 +79,7 @@ public final class TableResponse extends BroadcastInfoResponse implements Parcel
}
/**
- * Constructs a TableResponse with a table URI.
- *
- * @param requestId The ID is used to associate the response with the request.
- * @param sequence The sequence number which indicates the order of related responses.
- * @param responseResult The result for the response. It's one of {@link #RESPONSE_RESULT_OK},
- * {@link #RESPONSE_RESULT_CANCEL}, {@link #RESPONSE_RESULT_ERROR}.
- * @param tableByteArray The byte array which stores the table in bytes. The structure and
- * syntax of the table depends on the table name in
- * {@link TableRequest#getTableName()} and the corresponding standard.
- * @param version The version number of requested table.
- * @param size The Size number of table in bytes.
- */
- public TableResponse(int requestId, int sequence, @ResponseResult int responseResult,
- @NonNull byte[] tableByteArray, int version, int size) {
- super(RESPONSE_TYPE, requestId, sequence, responseResult);
- mVersion = version;
- mSize = size;
- mTableUri = null;
- mTableByteArray = tableByteArray;
- mTableSharedMemory = null;
- }
-
- /**
- * Constructs a TableResponse with a table URI.
+ * Constructs a TableResponse.
*
* @param requestId The ID is used to associate the response with the request.
* @param sequence The sequence number which indicates the order of related responses.
@@ -112,17 +92,128 @@ public final class TableResponse extends BroadcastInfoResponse implements Parcel
* {@link TableRequest#getTableName()} and the corresponding standard.
* @param version The version number of requested table.
* @param size The Size number of table in bytes.
+ * @param tableUri The URI of the table in the database.
+ * @param tableByteArray The byte array which stores the table in bytes. The structure and
+ * syntax of the table depends on the table name in
+ * @param tableSharedMemory The shared memory which stores the table. The table size can be
+ * large so using a shared memory optimizes the data
+ * communication between the table data source and the receiver. The
+ * structure syntax of the table depends on the table name in
+ * {@link TableRequest#getTableName()} and the corresponding standard.
*/
- public TableResponse(int requestId, int sequence, @ResponseResult int responseResult,
- @NonNull SharedMemory tableSharedMemory, int version, int size) {
+ private TableResponse(int requestId, int sequence, @ResponseResult int responseResult,
+ int version, int size, Uri tableUri, byte[] tableByteArray,
+ SharedMemory tableSharedMemory) {
super(RESPONSE_TYPE, requestId, sequence, responseResult);
mVersion = version;
mSize = size;
- mTableUri = null;
- mTableByteArray = null;
+ mTableUri = tableUri;
+ mTableByteArray = tableByteArray;
mTableSharedMemory = tableSharedMemory;
}
+ /**
+ * Builder for {@link TableResponse}.
+ */
+ public static final class Builder {
+ private final int mRequestId;
+ private final int mSequence;
+ @ResponseResult
+ private final int mResponseResult;
+ private final int mVersion;
+ private final int mSize;
+ private Uri mTableUri;
+ private byte[] mTableByteArray;
+ private SharedMemory mTableSharedMemory;
+
+ /**
+ * Constructs a Builder object of {@link TableResponse}.
+ *
+ * @param requestId The ID is used to associate the response with the request.
+ * @param sequence The sequence number which indicates the order of related responses.
+ * @param responseResult The result for the response. It's one of
+ * {@link #RESPONSE_RESULT_OK}, {@link #RESPONSE_RESULT_CANCEL},
+ * {@link #RESPONSE_RESULT_ERROR}.
+ * @param version The version number of requested table.
+ * @param size The Size number of table in bytes.
+ */
+ public Builder(int requestId, int sequence, @ResponseResult int responseResult, int version,
+ int size) {
+ mRequestId = requestId;
+ mSequence = sequence;
+ mResponseResult = responseResult;
+ mVersion = version;
+ mSize = size;
+ }
+
+ /**
+ * Sets table URI.
+ *
+ * <p>For a single builder instance, at most one of table URI, table byte array, and table
+ * shared memory can be set. If more than one are set, only the last call takes precedence
+ * and others are reset to {@code null}.
+ *
+ * @param uri The URI of the table.
+ */
+ @NonNull
+ public Builder setTableUri(@NonNull Uri uri) {
+ mTableUri = uri;
+ mTableByteArray = null;
+ mTableSharedMemory = null;
+ return this;
+ }
+
+ /**
+ * Sets table byte array.
+ *
+ * <p>For a single builder instance, at most one of table URI, table byte array, and table
+ * shared memory can be set. If more than one are set, only the last call takes precedence
+ * and others are reset to {@code null}.
+ *
+ * @param bytes The byte array which stores the table in bytes. The structure and
+ * syntax of the table depends on the table name in
+ * {@link TableRequest#getTableName()} and the corresponding standard.
+ */
+ @NonNull
+ public Builder setTableByteArray(@NonNull byte[] bytes) {
+ mTableByteArray = bytes;
+ mTableUri = null;
+ mTableSharedMemory = null;
+ return this;
+ }
+
+
+ /**
+ * Sets table shared memory.
+ *
+ * <p>For a single builder instance, at most one of table URI, table byte array, and table
+ * shared memory can be set. If more than one are set, only the last call takes precedence
+ * and others are reset to {@code null}.
+ *
+ * @param sharedMemory The shared memory which stores the table. The table size can be
+ * large so using a shared memory optimizes the data
+ * communication between the table data source and the receiver. The
+ * structure syntax of the table depends on the table name in
+ * {@link TableRequest#getTableName()} and the corresponding standard.
+ */
+ @NonNull
+ public Builder setTableSharedMemory(@NonNull SharedMemory sharedMemory) {
+ mTableSharedMemory = sharedMemory;
+ mTableUri = null;
+ mTableByteArray = null;
+ return this;
+ }
+
+ /**
+ * Builds a {@link TableResponse} object.
+ */
+ @NonNull
+ public TableResponse build() {
+ return new TableResponse(mRequestId, mSequence, mResponseResult, mVersion, mSize,
+ mTableUri, mTableByteArray, mTableSharedMemory);
+ }
+ }
+
TableResponse(Parcel source) {
super(RESPONSE_TYPE, source);
String uriString = source.readString();
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index 55a753f663c8..7d6a8b4c2e97 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -22,7 +22,6 @@ import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
-import android.annotation.StringDef;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
@@ -134,14 +133,17 @@ public final class TvInputManager {
public @interface VideoUnavailableReason {}
/** Indicates that this TV message contains watermarking data */
- public static final String TV_MESSAGE_TYPE_WATERMARK = "Watermark";
+ public static final int TV_MESSAGE_TYPE_WATERMARK = 1;
/** Indicates that this TV message contains Closed Captioning data */
- public static final String TV_MESSAGE_TYPE_CLOSED_CAPTION = "CC";
+ public static final int TV_MESSAGE_TYPE_CLOSED_CAPTION = 2;
+
+ /** Indicates that this TV message contains other data */
+ public static final int TV_MESSAGE_TYPE_OTHER = 1000;
/** @hide */
@Retention(RetentionPolicy.SOURCE)
- @StringDef({TV_MESSAGE_TYPE_WATERMARK, TV_MESSAGE_TYPE_CLOSED_CAPTION})
+ @IntDef({TV_MESSAGE_TYPE_WATERMARK, TV_MESSAGE_TYPE_CLOSED_CAPTION, TV_MESSAGE_TYPE_OTHER})
public @interface TvMessageType {}
/**
@@ -802,7 +804,7 @@ public final class TvInputManager {
* @param type The type of message received, such as {@link #TV_MESSAGE_TYPE_WATERMARK}
* @param data The raw data of the message
*/
- public void onTvMessage(Session session, @TvInputManager.TvMessageType String type,
+ public void onTvMessage(Session session, @TvInputManager.TvMessageType int type,
Bundle data) {
}
@@ -1081,7 +1083,7 @@ public final class TvInputManager {
});
}
- void postTvMessage(String type, Bundle data) {
+ void postTvMessage(int type, Bundle data) {
mHandler.post(new Runnable() {
@Override
public void run() {
@@ -1620,7 +1622,7 @@ public final class TvInputManager {
}
@Override
- public void onTvMessage(String type, Bundle data, int seq) {
+ public void onTvMessage(int type, Bundle data, int seq) {
synchronized (mSessionCallbackRecordMap) {
SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
if (record == null) {
@@ -3230,7 +3232,7 @@ public final class TvInputManager {
/**
* Sends TV messages to the service for testing purposes
*/
- public void notifyTvMessage(@NonNull @TvMessageType String type, @NonNull Bundle data) {
+ public void notifyTvMessage(@NonNull @TvMessageType int type, @NonNull Bundle data) {
try {
mService.notifyTvMessage(mToken, type, data, mUserId);
} catch (RemoteException e) {
@@ -3642,13 +3644,13 @@ public final class TvInputManager {
/**
* Notifies when the advertisement buffer is filled and ready to be read.
*/
- public void notifyAdBuffer(AdBuffer buffer) {
+ public void notifyAdBufferReady(AdBuffer buffer) {
if (mToken == null) {
Log.w(TAG, "The session has been already released");
return;
}
try {
- mService.notifyAdBuffer(mToken, buffer, mUserId);
+ mService.notifyAdBufferReady(mToken, buffer, mUserId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
index 9f40d704f7ac..3c6ed91d2100 100644
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -1032,7 +1032,7 @@ public abstract class TvInputService extends Service {
* {@link TvInputManager#TV_MESSAGE_TYPE_WATERMARK}
* @param data The data sent with the message.
*/
- public void notifyTvMessage(@NonNull @TvInputManager.TvMessageType String type,
+ public void notifyTvMessage(@TvInputManager.TvMessageType int type,
@NonNull Bundle data) {
executeOrPostRunnableOnMainThread(new Runnable() {
@MainThread
@@ -1351,7 +1351,7 @@ public abstract class TvInputService extends Service {
*
* @param buffer The {@link AdBuffer} that became ready for playback.
*/
- public void onAdBuffer(@NonNull AdBuffer buffer) {
+ public void onAdBufferReady(@NonNull AdBuffer buffer) {
}
/**
@@ -1486,11 +1486,12 @@ public abstract class TvInputService extends Service {
/**
* Called when the application enables or disables the detection of the specified message
* type.
- * @param type The {@link TvInputManager.TvMessageType} of message that was sent.
+ * @param type The type of message received, such as
+ * {@link TvInputManager#TV_MESSAGE_TYPE_WATERMARK}
* @param enabled {@code true} if TV message detection is enabled,
* {@code false} otherwise.
*/
- public void onSetTvMessageEnabled(@NonNull @TvInputManager.TvMessageType String type,
+ public void onSetTvMessageEnabled(@TvInputManager.TvMessageType int type,
boolean enabled) {
}
@@ -1856,9 +1857,9 @@ public abstract class TvInputService extends Service {
}
/**
- * Calls {@link #onSetTvMessageEnabled(String, boolean)}.
+ * Calls {@link #onSetTvMessageEnabled(int, boolean)}.
*/
- void setTvMessageEnabled(String type, boolean enabled) {
+ void setTvMessageEnabled(int type, boolean enabled) {
onSetTvMessageEnabled(type, enabled);
}
@@ -2050,8 +2051,8 @@ public abstract class TvInputService extends Service {
onRequestAd(request);
}
- void notifyAdBuffer(AdBuffer buffer) {
- onAdBuffer(buffer);
+ void notifyAdBufferReady(AdBuffer buffer) {
+ onAdBufferReady(buffer);
}
void onTvMessageReceived(String type, Bundle data) {
diff --git a/media/java/android/media/tv/TvRecordingInfo.java b/media/java/android/media/tv/TvRecordingInfo.java
index 60ceb8394159..59915fc078c0 100644
--- a/media/java/android/media/tv/TvRecordingInfo.java
+++ b/media/java/android/media/tv/TvRecordingInfo.java
@@ -131,7 +131,6 @@ public final class TvRecordingInfo implements Parcelable {
* cause the recording to start later than the specified time. This should cause the actual
* duration of the recording to decrease.
*/
- @NonNull
public long getStartPaddingMillis() {
return mStartPaddingMillis;
}
@@ -144,7 +143,6 @@ public final class TvRecordingInfo implements Parcelable {
* cause the recording to end earlier than the specified time. This should cause the actual
* duration of the recording to decrease.
*/
- @NonNull
public long getEndPaddingMillis() {
return mEndPaddingMillis;
}
@@ -176,7 +174,6 @@ public final class TvRecordingInfo implements Parcelable {
* https://www.oipf.tv/docs/OIPF-T1-R2_Specification-Volume-5-Declarative-Application-Environment-v2_3-2014-01-24.pdf
* ">Open IPTV Forum Release 2 Specification</a>. It is described in Volume 5, section 7.10.1.1.
*/
- @NonNull
@DaysOfWeek
public int getRepeatDays() {
return mRepeatDays;
@@ -228,7 +225,6 @@ public final class TvRecordingInfo implements Parcelable {
* Returns the scheduled start time of the recording in milliseconds since the epoch.
*/
@IntRange(from = 0)
- @NonNull
public long getScheduledStartTimeMillis() {
return mScheduledStartTimeMillis;
}
@@ -237,7 +233,6 @@ public final class TvRecordingInfo implements Parcelable {
* Returns the scheduled duration of the recording in milliseconds since the epoch.
*/
@IntRange(from = 0)
- @NonNull
public long getScheduledDurationMillis() {
return mScheduledDurationMillis;
}
@@ -292,7 +287,6 @@ public final class TvRecordingInfo implements Parcelable {
* <p> Returns -1 for recordings that have not yet started.
*/
@IntRange(from = -1)
- @NonNull
public long getRecordingStartTimeMillis() {
return mRecordingStartTimeMillis;
}
@@ -306,7 +300,6 @@ public final class TvRecordingInfo implements Parcelable {
* <p> Returns -1 for recordings that have not yet started.
*/
@IntRange(from = -1)
- @NonNull
public long getRecordingDurationMillis() {
return mRecordingDurationMillis;
}
diff --git a/media/java/android/media/tv/TvView.java b/media/java/android/media/tv/TvView.java
index 5aeed1fbbaf5..19a2e5d8d157 100644
--- a/media/java/android/media/tv/TvView.java
+++ b/media/java/android/media/tv/TvView.java
@@ -648,7 +648,7 @@ public class TvView extends ViewGroup {
* @hide
*/
@TestApi
- public void notifyTvMessage(@TvInputManager.TvMessageType @NonNull String type,
+ public void notifyTvMessage(@TvInputManager.TvMessageType int type,
@NonNull Bundle data) {
if (mSession != null) {
mSession.notifyTvMessage(type, data);
@@ -738,11 +738,12 @@ public class TvView extends ViewGroup {
/**
* Enables or disables TV message detection in the stream of the bound TV input.
*
- * @param type The type of {@link android.media.tv.TvInputManager.TvMessageType}
+ * @param type The type of message received, such as
+ * {@link TvInputManager#TV_MESSAGE_TYPE_WATERMARK}
* @param enabled {@code true} if you want to enable TV message detection
* {@code false} otherwise.
*/
- public void setTvMessageEnabled(@NonNull @TvInputManager.TvMessageType String type,
+ public void setTvMessageEnabled(@TvInputManager.TvMessageType int type,
boolean enabled) {
}
@@ -1251,11 +1252,12 @@ public class TvView extends ViewGroup {
* This is called when a new TV Message has been received.
*
* @param inputId The ID of the TV input bound to this view.
- * @param type The type of {@link android.media.tv.TvInputManager.TvMessageType}
+ * @param type The type of message received, such as
+ * {@link TvInputManager#TV_MESSAGE_TYPE_WATERMARK}
* @param data The raw data of the message
*/
public void onTvMessage(@NonNull String inputId,
- @NonNull @TvInputManager.TvMessageType String type, @NonNull Bundle data) {
+ @TvInputManager.TvMessageType int type, @NonNull Bundle data) {
}
}
@@ -1670,7 +1672,7 @@ public class TvView extends ViewGroup {
}
@Override
- public void onTvMessage(Session session, String type, Bundle data) {
+ public void onTvMessage(Session session, int type, Bundle data) {
if (DEBUG) {
Log.d(TAG, "onTvMessage(type=" + type + ", data=" + data + ")");
}
diff --git a/media/java/android/media/tv/interactive/ITvInteractiveAppClient.aidl b/media/java/android/media/tv/interactive/ITvInteractiveAppClient.aidl
index 36954ad00f5f..77391841c6fe 100644
--- a/media/java/android/media/tv/interactive/ITvInteractiveAppClient.aidl
+++ b/media/java/android/media/tv/interactive/ITvInteractiveAppClient.aidl
@@ -39,7 +39,7 @@ oneway interface ITvInteractiveAppClient {
void onSessionStateChanged(int state, int err, int seq);
void onBiInteractiveAppCreated(in Uri biIAppUri, in String biIAppId, int seq);
void onTeletextAppStateChanged(int state, int seq);
- void onAdBuffer(in AdBuffer buffer, int seq);
+ void onAdBufferReady(in AdBuffer buffer, int seq);
void onCommandRequest(in String cmdType, in Bundle parameters, int seq);
void onTimeShiftCommandRequest(in String cmdType, in Bundle parameters, int seq);
void onSetVideoBounds(in Rect rect, int seq);
diff --git a/media/java/android/media/tv/interactive/ITvInteractiveAppManager.aidl b/media/java/android/media/tv/interactive/ITvInteractiveAppManager.aidl
index 89847a73ccde..41cbe4ae02d0 100644
--- a/media/java/android/media/tv/interactive/ITvInteractiveAppManager.aidl
+++ b/media/java/android/media/tv/interactive/ITvInteractiveAppManager.aidl
@@ -94,7 +94,7 @@ interface ITvInteractiveAppManager {
void notifyRecordingStarted(in IBinder sessionToken, in String recordingId, String requestId,
int userId);
void notifyRecordingStopped(in IBinder sessionToken, in String recordingId, int userId);
- void notifyTvMessage(in IBinder sessionToken, in String type, in Bundle data, int userId);
+ void notifyTvMessage(in IBinder sessionToken, in int type, in Bundle data, int userId);
void setSurface(in IBinder sessionToken, in Surface surface, int userId);
void dispatchSurfaceChanged(in IBinder sessionToken, int format, int width, int height,
int userId);
diff --git a/media/java/android/media/tv/interactive/ITvInteractiveAppSession.aidl b/media/java/android/media/tv/interactive/ITvInteractiveAppSession.aidl
index f17d1b73994e..052bc3d5adce 100644
--- a/media/java/android/media/tv/interactive/ITvInteractiveAppSession.aidl
+++ b/media/java/android/media/tv/interactive/ITvInteractiveAppSession.aidl
@@ -72,7 +72,7 @@ oneway interface ITvInteractiveAppSession {
void notifySignalStrength(int strength);
void notifyRecordingStarted(in String recordingId, in String requestId);
void notifyRecordingStopped(in String recordingId);
- void notifyTvMessage(in String type, in Bundle data);
+ void notifyTvMessage(int type, in Bundle data);
void setSurface(in Surface surface);
void dispatchSurfaceChanged(int format, int width, int height);
void notifyBroadcastInfoResponse(in BroadcastInfoResponse response);
diff --git a/media/java/android/media/tv/interactive/ITvInteractiveAppSessionCallback.aidl b/media/java/android/media/tv/interactive/ITvInteractiveAppSessionCallback.aidl
index 7db860489e8a..9e43e79144fd 100644
--- a/media/java/android/media/tv/interactive/ITvInteractiveAppSessionCallback.aidl
+++ b/media/java/android/media/tv/interactive/ITvInteractiveAppSessionCallback.aidl
@@ -38,7 +38,7 @@ oneway interface ITvInteractiveAppSessionCallback {
void onSessionStateChanged(int state, int err);
void onBiInteractiveAppCreated(in Uri biIAppUri, in String biIAppId);
void onTeletextAppStateChanged(int state);
- void onAdBuffer(in AdBuffer buffer);
+ void onAdBufferReady(in AdBuffer buffer);
void onCommandRequest(in String cmdType, in Bundle parameters);
void onTimeShiftCommandRequest(in String cmdType, in Bundle parameters);
void onSetVideoBounds(in Rect rect);
diff --git a/media/java/android/media/tv/interactive/ITvInteractiveAppSessionWrapper.java b/media/java/android/media/tv/interactive/ITvInteractiveAppSessionWrapper.java
index 6eed4832847f..d3f598a7c009 100644
--- a/media/java/android/media/tv/interactive/ITvInteractiveAppSessionWrapper.java
+++ b/media/java/android/media/tv/interactive/ITvInteractiveAppSessionWrapper.java
@@ -243,7 +243,7 @@ public class ITvInteractiveAppSessionWrapper
}
case DO_NOTIFY_TV_MESSAGE: {
SomeArgs args = (SomeArgs) msg.obj;
- mSessionImpl.notifyTvMessage((String) args.arg1, (Bundle) args.arg2);
+ mSessionImpl.notifyTvMessage((Integer) args.arg1, (Bundle) args.arg2);
args.recycle();
break;
}
@@ -520,7 +520,7 @@ public class ITvInteractiveAppSessionWrapper
}
@Override
- public void notifyTvMessage(String type, Bundle data) {
+ public void notifyTvMessage(int type, Bundle data) {
mCaller.executeOrSendMessage(
mCaller.obtainMessageOO(DO_NOTIFY_TV_MESSAGE, type, data));
}
@@ -558,7 +558,7 @@ public class ITvInteractiveAppSessionWrapper
@Override
public void notifyRecordingStarted(String recordingId, String requestId) {
mCaller.executeOrSendMessage(mCaller.obtainMessageOO(
- DO_NOTIFY_RECORDING_STARTED, recordingId, recordingId));
+ DO_NOTIFY_RECORDING_STARTED, recordingId, requestId));
}
@Override
diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppManager.java b/media/java/android/media/tv/interactive/TvInteractiveAppManager.java
index 3e31bce30baa..fc8fe5c30967 100755
--- a/media/java/android/media/tv/interactive/TvInteractiveAppManager.java
+++ b/media/java/android/media/tv/interactive/TvInteractiveAppManager.java
@@ -681,14 +681,14 @@ public final class TvInteractiveAppManager {
}
@Override
- public void onAdBuffer(AdBuffer buffer, int seq) {
+ public void onAdBufferReady(AdBuffer buffer, int seq) {
synchronized (mSessionCallbackRecordMap) {
SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
if (record == null) {
Log.e(TAG, "Callback not found for seq " + seq);
return;
}
- record.postAdBuffer(buffer);
+ record.postAdBufferReady(buffer);
}
}
};
@@ -1751,7 +1751,7 @@ public final class TvInteractiveAppManager {
/**
* Notifies Interactive APP session when a new TV message is received.
*/
- public void notifyTvMessage(String type, Bundle data) {
+ public void notifyTvMessage(int type, Bundle data) {
if (mToken == null) {
Log.w(TAG, "The session has been already released");
return;
@@ -2245,12 +2245,12 @@ public final class TvInteractiveAppManager {
});
}
- void postAdBuffer(AdBuffer buffer) {
+ void postAdBufferReady(AdBuffer buffer) {
mHandler.post(new Runnable() {
@Override
public void run() {
if (mSession.getInputSession() != null) {
- mSession.getInputSession().notifyAdBuffer(buffer);
+ mSession.getInputSession().notifyAdBufferReady(buffer);
}
}
});
diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppService.java b/media/java/android/media/tv/interactive/TvInteractiveAppService.java
index 1ae82f43b9dd..8a58abf1914b 100755
--- a/media/java/android/media/tv/interactive/TvInteractiveAppService.java
+++ b/media/java/android/media/tv/interactive/TvInteractiveAppService.java
@@ -630,6 +630,8 @@ public abstract class TvInteractiveAppService extends Service {
* {@link #requestStartRecording(String, Uri)} is called.
* {@code null} if the recording is not triggered by a
* {@link #requestStartRecording(String, Uri)} request.
+ * This ID should be created by the {@link TvInteractiveAppService} and
+ * can be any string.
* @see #onRecordingStopped(String)
*/
public void onRecordingStarted(@NonNull String recordingId, @Nullable String requestId) {
@@ -708,6 +710,8 @@ public abstract class TvInteractiveAppService extends Service {
* @param requestId The ID of the request when
* {@link #requestScheduleRecording} is called.
* {@code null} if the recording is not triggered by a request.
+ * This ID should be created by the {@link TvInteractiveAppService} and
+ * can be any string.
*/
public void onRecordingScheduled(@NonNull String recordingId, @Nullable String requestId) {
}
@@ -916,7 +920,7 @@ public abstract class TvInteractiveAppService extends Service {
* {@link TvInputManager#TV_MESSAGE_TYPE_WATERMARK}
* @param data The raw data of the message
*/
- public void onTvMessage(@NonNull @TvInputManager.TvMessageType String type,
+ public void onTvMessage(@TvInputManager.TvMessageType int type,
@NonNull Bundle data) {
}
@@ -1338,7 +1342,10 @@ public abstract class TvInteractiveAppService extends Service {
* @param requestId The ID of this request which is used to match the corresponding
* response. The request ID in
* {@link #onRecordingStarted(String, String)} for this request is the
- * same as the ID sent here.
+ * same as the ID sent here. This should be defined by the
+ * {@link TvInteractiveAppService} and can be any string.
+ * Should this API be called with the same requestId twice, both
+ * requests should be handled regardless by the TV application.
* @param programUri The URI for the TV program to record.
* @see android.media.tv.TvRecordingClient#startRecording(Uri)
*/
@@ -1390,7 +1397,10 @@ public abstract class TvInteractiveAppService extends Service {
* @param requestId The ID of this request which is used to match the corresponding
* response. The request ID in
* {@link #onRecordingScheduled(String, String)} for this request is the
- * same as the ID sent here.
+ * same as the ID sent here. This should be defined by the
+ * {@link TvInteractiveAppService} and can be any string.
+ * Should this API be called with the same requestId twice, both requests
+ * should be handled regardless by the TV application.
* @param inputId The ID of the TV input for the given channel.
* @param channelUri The URI of a channel to be recorded.
* @param programUri The URI of the TV program to be recorded.
@@ -1424,7 +1434,10 @@ public abstract class TvInteractiveAppService extends Service {
* @param requestId The ID of this request which is used to match the corresponding
* response. The request ID in
* {@link #onRecordingScheduled(String, String)} for this request is the
- * same as the ID sent here.
+ * same as the ID sent here. This should be defined by the
+ * {@link TvInteractiveAppService} and can be any string. Should this API
+ * be called with the same requestId twice, both requests should be handled
+ * regardless by the TV application.
* @param inputId The ID of the TV input for the given channel.
* @param channelUri The URI of a channel to be recorded.
* @param startTime The start time of the recording in milliseconds since epoch.
@@ -1507,8 +1520,7 @@ public abstract class TvInteractiveAppService extends Service {
* @param type The type of recording to retrieve.
*/
@CallSuper
- public void requestTvRecordingInfoList(@NonNull @TvRecordingInfo.TvRecordingListType
- int type) {
+ public void requestTvRecordingInfoList(@TvRecordingInfo.TvRecordingListType int type) {
executeOrPostRunnableOnMainThread(() -> {
try {
if (DEBUG) {
@@ -1753,7 +1765,7 @@ public abstract class TvInteractiveAppService extends Service {
onAdResponse(response);
}
- void notifyTvMessage(String type, Bundle data) {
+ void notifyTvMessage(int type, Bundle data) {
if (DEBUG) {
Log.d(TAG, "notifyTvMessage (type=" + type + ", data= " + data + ")");
}
@@ -1942,7 +1954,7 @@ public abstract class TvInteractiveAppService extends Service {
* @param buffer The {@link AdBuffer} to be received
*/
@CallSuper
- public void notifyAdBuffer(@NonNull AdBuffer buffer) {
+ public void notifyAdBufferReady(@NonNull AdBuffer buffer) {
executeOrPostRunnableOnMainThread(new Runnable() {
@MainThread
@Override
@@ -1950,10 +1962,10 @@ public abstract class TvInteractiveAppService extends Service {
try {
if (DEBUG) {
Log.d(TAG,
- "notifyAdBuffer(buffer=" + buffer + ")");
+ "notifyAdBufferReady(buffer=" + buffer + ")");
}
if (mSessionCallback != null) {
- mSessionCallback.onAdBuffer(buffer);
+ mSessionCallback.onAdBufferReady(buffer);
}
} catch (RemoteException e) {
Log.w(TAG, "error in notifyAdBuffer", e);
diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppView.java b/media/java/android/media/tv/interactive/TvInteractiveAppView.java
index 0a8de127b6c9..1a0319bddc2d 100755
--- a/media/java/android/media/tv/interactive/TvInteractiveAppView.java
+++ b/media/java/android/media/tv/interactive/TvInteractiveAppView.java
@@ -675,6 +675,8 @@ public class TvInteractiveAppView extends ViewGroup {
* @param requestId The ID of the request when
* {@link TvInteractiveAppService.Session#requestStartRecording(String, Uri)}
* is called. {@code null} if the recording is not triggered by a request.
+ * This ID should be created by the {@link TvInteractiveAppService} and
+ * can be any string.
* @see TvInteractiveAppView#notifyRecordingStopped(String)
*/
public void notifyRecordingStarted(@NonNull String recordingId, @Nullable String requestId) {
@@ -682,7 +684,7 @@ public class TvInteractiveAppView extends ViewGroup {
Log.d(TAG, "notifyRecordingStarted");
}
if (mSession != null) {
- mSession.notifyRecordingStarted(recordingId, recordingId);
+ mSession.notifyRecordingStarted(recordingId, requestId);
}
}
@@ -922,6 +924,8 @@ public class TvInteractiveAppView extends ViewGroup {
* @param requestId The ID of the request when
* {@link TvInteractiveAppService.Session#requestScheduleRecording} is called.
* {@code null} if the recording is not triggered by a request.
+ * This ID should be created by the {@link TvInteractiveAppService} and
+ * can be any string.
*/
public void notifyRecordingScheduled(
@NonNull String recordingId, @Nullable String requestId) {
@@ -942,7 +946,7 @@ public class TvInteractiveAppView extends ViewGroup {
* {@link TvInputManager#TV_MESSAGE_TYPE_WATERMARK}
* @param data The raw data of the message
*/
- public void notifyTvMessage(@NonNull @TvInputManager.TvMessageType String type,
+ public void notifyTvMessage(@NonNull @TvInputManager.TvMessageType int type,
@NonNull Bundle data) {
if (DEBUG) {
Log.d(TAG, "notifyTvMessage type=" + type
@@ -1214,14 +1218,17 @@ public class TvInteractiveAppView extends ViewGroup {
}
/**
- * This is called when {@link TvInteractiveAppService.Session#requestStartRecording(Uri)}
- * is called.
+ * This is called when
+ * {@link TvInteractiveAppService.Session#requestStartRecording(String, Uri)} is called.
*
* @param iAppServiceId The ID of the TV interactive app service bound to this view.
* @param requestId The ID of this request which is used to match the corresponding
* response. The request ID in
- * {@link #notifyRecordingStarted(String, String)}
- * for this request should be the same as the ID received here.
+ * {@link #notifyRecordingStarted(String, String)} for this request is the
+ * same as the ID sent here. This should be defined by the
+ * TIAS and can be any string. Should this API be called with the
+ * same requestId twice, both requests should be handled regardless
+ * by the TV application.
* @param programUri The URI of the program to record
*
*/
@@ -1252,8 +1259,11 @@ public class TvInteractiveAppView extends ViewGroup {
* @param iAppServiceId The ID of the TV interactive app service bound to this view.
* @param requestId The ID of this request which is used to match the corresponding
* response. The request ID in
- * {@link #notifyRecordingScheduled(String, String)} for this request
- * should be the same as the ID received here.
+ * {@link #notifyRecordingScheduled(String, String)} for this request is
+ * the same as the ID sent here. This should be defined by the
+ * TIAS and can be any string. Should this API be called with the
+ * same requestId twice, both requests should be handled regardless
+ * by the TV application.
* @param inputId The ID of the TV input for the given channel.
* @param channelUri The URI of a channel to be recorded.
* @param programUri The URI of the TV program to be recorded.
@@ -1276,8 +1286,11 @@ public class TvInteractiveAppView extends ViewGroup {
* @param iAppServiceId The ID of the TV interactive app service bound to this view.
* @param requestId The ID of this request which is used to match the corresponding
* response. The request ID in
- * {@link #notifyRecordingScheduled(String, String)} for this request
- * should be the same as the ID received here.
+ * {@link #notifyRecordingScheduled(String, String)} for this request is
+ * the same as the ID sent here. This should be defined by the
+ * TIAS and can be any string. Should this API be called with the
+ * same requestId twice, both requests should be handled regardless
+ * by the TV application.
* @param inputId The ID of the TV input for the given channel.
* @param channelUri The URI of a channel to be recorded.
* @param startTime The start time of the recording in milliseconds since epoch.
@@ -1350,7 +1363,7 @@ public class TvInteractiveAppView extends ViewGroup {
*/
public void onRequestTvRecordingInfoList(
@NonNull String iAppServiceId,
- @NonNull @TvRecordingInfo.TvRecordingListType int type) {
+ @TvRecordingInfo.TvRecordingListType int type) {
}
}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
index 15f5684a1b82..6fd579b02a4f 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
@@ -446,6 +446,7 @@ fun MoreOptionsRowIntroCard(
BodyMediumText(text = stringResource(
R.string.use_provider_for_all_description, entryInfo.userProviderDisplayName))
}
+ item { Divider(thickness = 24.dp, color = Color.Transparent) }
item {
CtaButtonRow(
leftButton = {
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/GenerationRegistry.java b/packages/SettingsProvider/src/com/android/providers/settings/GenerationRegistry.java
index db6cc1a39ef1..7f3b0ff8c838 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/GenerationRegistry.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/GenerationRegistry.java
@@ -16,7 +16,6 @@
package com.android.providers.settings;
-import android.annotation.NonNull;
import android.os.Bundle;
import android.provider.Settings;
import android.util.ArrayMap;
@@ -60,10 +59,6 @@ final class GenerationRegistry {
// Maximum size of an individual backing store
static final int MAX_BACKING_STORE_SIZE = MemoryIntArray.getMaxSize();
- // Use an empty string to track the generation number of all non-predefined, unset settings
- // The generation number is only increased when a new non-predefined setting is inserted
- private static final String DEFAULT_MAP_KEY_FOR_UNSET_SETTINGS = "";
-
public GenerationRegistry(Object lock) {
mLock = lock;
}
@@ -77,10 +72,6 @@ final class GenerationRegistry {
(SettingsState.getTypeFromKey(key) == SettingsState.SETTINGS_TYPE_CONFIG);
// Only store the prefix if the mutated setting is a config
final String indexMapKey = isConfig ? (name.split("/")[0] + "/") : name;
- incrementGenerationInternal(key, indexMapKey);
- }
-
- private void incrementGenerationInternal(int key, @NonNull String indexMapKey) {
synchronized (mLock) {
final MemoryIntArray backingStore = getBackingStoreLocked(key,
/* createIfNotExist= */ false);
@@ -96,8 +87,7 @@ final class GenerationRegistry {
final int generation = backingStore.get(index) + 1;
backingStore.set(index, generation);
if (DEBUG) {
- Slog.i(LOG_TAG, "Incremented generation for "
- + (indexMapKey.isEmpty() ? "unset settings" : "setting:" + indexMapKey)
+ Slog.i(LOG_TAG, "Incremented generation for setting:" + indexMapKey
+ " key:" + SettingsState.keyToString(key)
+ " at index:" + index);
}
@@ -108,18 +98,6 @@ final class GenerationRegistry {
}
}
- // A new, non-predefined setting has been inserted, increment the tracking number for all unset
- // settings
- public void incrementGenerationForUnsetSettings(int key) {
- final boolean isConfig =
- (SettingsState.getTypeFromKey(key) == SettingsState.SETTINGS_TYPE_CONFIG);
- if (isConfig) {
- // No need to track new settings for configs
- return;
- }
- incrementGenerationInternal(key, DEFAULT_MAP_KEY_FOR_UNSET_SETTINGS);
- }
-
/**
* Return the backing store's reference, the index and the current generation number
* of a cached setting. If it was not in the backing store, first create the entry in it before
@@ -146,8 +124,8 @@ final class GenerationRegistry {
bundle.putInt(Settings.CALL_METHOD_GENERATION_KEY,
backingStore.get(index));
if (DEBUG) {
- Slog.i(LOG_TAG, "Exported index:" + index + " for "
- + (indexMapKey.isEmpty() ? "unset settings" : "setting:" + indexMapKey)
+ Slog.i(LOG_TAG, "Exported index:" + index
+ + " for setting:" + indexMapKey
+ " key:" + SettingsState.keyToString(key));
}
} catch (IOException e) {
@@ -157,10 +135,6 @@ final class GenerationRegistry {
}
}
- public void addGenerationDataForUnsetSettings(Bundle bundle, int key) {
- addGenerationData(bundle, key, /* indexMapKey= */ DEFAULT_MAP_KEY_FOR_UNSET_SETTINGS);
- }
-
public void onUserRemoved(int userId) {
final int secureKey = SettingsState.makeKey(
SettingsState.SETTINGS_TYPE_SECURE, userId);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 27c8cdfe98f3..2f4ad9fd4ccc 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -1173,7 +1173,7 @@ public class SettingsProvider extends ContentProvider {
Slog.v(LOG_TAG, "setAllConfigSettings for prefix: " + prefix);
}
- enforceWritePermission(Manifest.permission.WRITE_DEVICE_CONFIG);
+ enforceDeviceConfigWritePermission(getContext(), keyValues.keySet());
final String callingPackage = resolveCallingPackage();
synchronized (mLock) {
@@ -1192,7 +1192,8 @@ public class SettingsProvider extends ContentProvider {
Slog.v(LOG_TAG, "setSyncDisabledModeConfig(" + syncDisabledMode + ")");
}
- enforceWritePermission(Manifest.permission.WRITE_DEVICE_CONFIG);
+ enforceHasAtLeastOnePermission(Manifest.permission.WRITE_DEVICE_CONFIG,
+ Manifest.permission.READ_WRITE_SYNC_DISABLED_MODE_CONFIG);
synchronized (mLock) {
setSyncDisabledModeConfigLocked(syncDisabledMode);
@@ -1204,7 +1205,8 @@ public class SettingsProvider extends ContentProvider {
Slog.v(LOG_TAG, "getSyncDisabledModeConfig");
}
- enforceWritePermission(Manifest.permission.WRITE_DEVICE_CONFIG);
+ enforceHasAtLeastOnePermission(Manifest.permission.WRITE_DEVICE_CONFIG,
+ Manifest.permission.READ_WRITE_SYNC_DISABLED_MODE_CONFIG);
synchronized (mLock) {
return getSyncDisabledModeConfigLocked();
@@ -1289,13 +1291,13 @@ public class SettingsProvider extends ContentProvider {
private boolean mutateConfigSetting(String name, String value, String prefix,
boolean makeDefault, int operation, int mode) {
- enforceWritePermission(Manifest.permission.WRITE_DEVICE_CONFIG);
final String callingPackage = resolveCallingPackage();
// Perform the mutation.
synchronized (mLock) {
switch (operation) {
case MUTATION_OPERATION_INSERT: {
+ enforceDeviceConfigWritePermission(getContext(), Collections.singleton(name));
return mSettingsRegistry.insertSettingLocked(SETTINGS_TYPE_CONFIG,
UserHandle.USER_SYSTEM, name, value, null, makeDefault, true,
callingPackage, false, null,
@@ -1303,11 +1305,14 @@ public class SettingsProvider extends ContentProvider {
}
case MUTATION_OPERATION_DELETE: {
+ enforceDeviceConfigWritePermission(getContext(), Collections.singleton(name));
return mSettingsRegistry.deleteSettingLocked(SETTINGS_TYPE_CONFIG,
UserHandle.USER_SYSTEM, name, false, null);
}
case MUTATION_OPERATION_RESET: {
+ enforceDeviceConfigWritePermission(getContext(),
+ getAllConfigFlags(prefix).keySet());
mSettingsRegistry.resetSettingsLocked(SETTINGS_TYPE_CONFIG,
UserHandle.USER_SYSTEM, callingPackage, mode, null, prefix);
} return true;
@@ -1464,7 +1469,7 @@ public class SettingsProvider extends ContentProvider {
boolean makeDefault, int requestingUserId, int operation, boolean forceNotify,
int mode, boolean overrideableByRestore) {
// Make sure the caller can change the settings - treated as secure.
- enforceWritePermission(Manifest.permission.WRITE_SECURE_SETTINGS);
+ enforceHasAtLeastOnePermission(Manifest.permission.WRITE_SECURE_SETTINGS);
// Resolve the userId on whose behalf the call is made.
final int callingUserId = resolveCallingUserIdEnforcingPermissionsLocked(requestingUserId);
@@ -1752,7 +1757,7 @@ public class SettingsProvider extends ContentProvider {
boolean makeDefault, int requestingUserId, int operation, boolean forceNotify,
int mode, boolean overrideableByRestore) {
// Make sure the caller can change the settings.
- enforceWritePermission(Manifest.permission.WRITE_SECURE_SETTINGS);
+ enforceHasAtLeastOnePermission(Manifest.permission.WRITE_SECURE_SETTINGS);
// Resolve the userId on whose behalf the call is made.
final int callingUserId = resolveCallingUserIdEnforcingPermissionsLocked(requestingUserId);
@@ -2277,11 +2282,57 @@ public class SettingsProvider extends ContentProvider {
}
}
- private void enforceWritePermission(String permission) {
- if (getContext().checkCallingOrSelfPermission(permission)
- != PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException("Permission denial: writing to settings requires:"
- + permission);
+ private void enforceHasAtLeastOnePermission(String ...permissions) {
+ for (String permission : permissions) {
+ if (getContext().checkCallingOrSelfPermission(permission)
+ == PackageManager.PERMISSION_GRANTED) {
+ return;
+ }
+ }
+ throw new SecurityException("Permission denial, must have one of: "
+ + Arrays.toString(permissions));
+ }
+
+ /**
+ * Throws an exception if write permissions are not granted for {@code flags}.
+ * <p>
+ * Write permissions are granted if the calling UID is root, or the
+ * WRITE_DEVICE_CONFIG permission is granted, or the WRITE_DEVICE_CONFIG_ALLOWLIST
+ * permission is granted and each flag in {@code flags} is allowlisted in {@code
+ * WRITABLE_FLAG_ALLOWLIST_FLAG}.
+ *
+ * @param context the {@link Context} this is called in
+ * @param flags a list of flags to check, each one of the form 'namespace/flagName'
+ *
+ * @throws SecurityException if the above criteria are not met.
+ * @hide
+ */
+ private void enforceDeviceConfigWritePermission(
+ @NonNull Context context,
+ @NonNull Set<String> flags) {
+ boolean hasAllowlistPermission =
+ context.checkCallingOrSelfPermission(
+ Manifest.permission.ALLOWLISTED_WRITE_DEVICE_CONFIG)
+ == PackageManager.PERMISSION_GRANTED;
+ boolean hasWritePermission =
+ context.checkCallingOrSelfPermission(
+ Manifest.permission.WRITE_DEVICE_CONFIG)
+ == PackageManager.PERMISSION_GRANTED;
+ boolean isRoot = Binder.getCallingUid() == Process.ROOT_UID;
+
+ if (isRoot || hasWritePermission) {
+ return;
+ } else if (hasAllowlistPermission) {
+ for (String flag : flags) {
+ if (!DeviceConfig.getAdbWritableFlags().contains(flag)) {
+ throw new SecurityException("Permission denial for flag '"
+ + flag
+ + "'; allowlist permission granted, but must add flag to the allowlist.");
+ }
+ }
+ } else {
+ throw new SecurityException("Permission denial to mutate flag, must have root, "
+ + "WRITE_DEVICE_CONFIG, or ALLOWLISTED_WRITE_DEVICE_CONFIG");
}
}
@@ -2327,15 +2378,11 @@ public class SettingsProvider extends ContentProvider {
result.putString(Settings.NameValueTable.VALUE,
(setting != null && !setting.isNull()) ? setting.getValue() : null);
- synchronized (mLock) {
- if ((setting != null && !setting.isNull()) || isSettingPreDefined(name, type)) {
- // Individual generation tracking for predefined settings even if they are unset
+ if ((setting != null && !setting.isNull()) || isSettingPreDefined(name, type)) {
+ // Don't track generation for non-existent settings unless the name is predefined
+ synchronized (mLock) {
mSettingsRegistry.mGenerationRegistry.addGenerationData(result,
SettingsState.makeKey(type, userId), name);
- } else {
- // All non-predefined, unset settings are tracked using the same generation number
- mSettingsRegistry.mGenerationRegistry.addGenerationDataForUnsetSettings(result,
- SettingsState.makeKey(type, userId));
}
}
return result;
@@ -2349,8 +2396,7 @@ public class SettingsProvider extends ContentProvider {
} else if (type == SETTINGS_TYPE_SYSTEM) {
return sAllSystemSettings.contains(name);
} else {
- // Consider all config settings predefined because they are used by system apps only
- return type == SETTINGS_TYPE_CONFIG;
+ return false;
}
}
@@ -2359,13 +2405,14 @@ public class SettingsProvider extends ContentProvider {
Bundle result = new Bundle();
result.putSerializable(Settings.NameValueTable.VALUE, keyValues);
if (trackingGeneration) {
+ // Track generation even if the namespace is empty because this is for system apps
synchronized (mLock) {
- // Track generation even if namespace is empty because this is for system apps only
mSettingsRegistry.mGenerationRegistry.addGenerationData(result,
- SettingsState.makeKey(SETTINGS_TYPE_CONFIG, UserHandle.USER_SYSTEM),
- prefix);
+ mSettingsRegistry.getSettingsLocked(SETTINGS_TYPE_CONFIG,
+ UserHandle.USER_SYSTEM).mKey, prefix);
}
}
+
return result;
}
@@ -3056,15 +3103,10 @@ public class SettingsProvider extends ContentProvider {
final int key = makeKey(type, userId);
boolean success = false;
- boolean wasUnsetNonPredefinedSetting = false;
SettingsState settingsState = peekSettingsStateLocked(key);
if (settingsState != null) {
- if (!isSettingPreDefined(name, type) && !settingsState.hasSetting(name)) {
- wasUnsetNonPredefinedSetting = true;
- }
success = settingsState.insertSettingLocked(name, value,
- tag, makeDefault, forceNonSystemPackage, packageName,
- overrideableByRestore);
+ tag, makeDefault, forceNonSystemPackage, packageName, overrideableByRestore);
}
if (success && criticalSettings != null && criticalSettings.contains(name)) {
@@ -3073,11 +3115,6 @@ public class SettingsProvider extends ContentProvider {
if (forceNotify || success) {
notifyForSettingsChange(key, name);
- if (wasUnsetNonPredefinedSetting) {
- // Increment the generation number for all non-predefined, unset settings,
- // because a new non-predefined setting has been inserted
- mGenerationRegistry.incrementGenerationForUnsetSettings(key);
- }
}
if (success) {
logSettingChanged(userId, name, type, CHANGE_TYPE_INSERT);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index 4d8705f135af..c3888268a61d 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -759,12 +759,6 @@ final class SettingsState {
mPackageToMemoryUsage.put(packageName, newSize);
}
- public boolean hasSetting(String name) {
- synchronized (mLock) {
- return hasSettingLocked(name);
- }
- }
-
@GuardedBy("mLock")
private boolean hasSettingLocked(String name) {
return mSettings.indexOfKey(name) >= 0;
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/GenerationRegistryTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/GenerationRegistryTest.java
index 6ec8146baee0..d34fe6943153 100644
--- a/packages/SettingsProvider/test/src/com/android/providers/settings/GenerationRegistryTest.java
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/GenerationRegistryTest.java
@@ -151,26 +151,6 @@ public class GenerationRegistryTest {
checkBundle(b, 0, 1, false);
}
- @Test
- public void testUnsetSettings() throws IOException {
- final GenerationRegistry generationRegistry = new GenerationRegistry(new Object());
- final int secureKey = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_SECURE, 0);
- final String testSecureSetting = "test_secure_setting";
- Bundle b = new Bundle();
- generationRegistry.addGenerationData(b, secureKey, testSecureSetting);
- checkBundle(b, 0, 1, false);
- generationRegistry.addGenerationDataForUnsetSettings(b, secureKey);
- checkBundle(b, 1, 1, false);
- generationRegistry.addGenerationDataForUnsetSettings(b, secureKey);
- // Test that unset settings always have the same index
- checkBundle(b, 1, 1, false);
- generationRegistry.incrementGenerationForUnsetSettings(secureKey);
- // Test that the generation number of the unset settings have increased
- generationRegistry.addGenerationDataForUnsetSettings(b, secureKey);
- checkBundle(b, 1, 2, false);
- }
-
-
private void checkBundle(Bundle b, int expectedIndex, int expectedGeneration, boolean isNull)
throws IOException {
final MemoryIntArray array = getArray(b);
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index d02e5693bbf3..8cbf5f8d4c30 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -148,6 +148,8 @@
<uses-permission android:name="android.permission.LOCATION_BYPASS" />
<uses-permission android:name="android.permission.READ_DEVICE_CONFIG" />
<uses-permission android:name="android.permission.WRITE_DEVICE_CONFIG" />
+ <uses-permission android:name="android.permission.ALLOWLISTED_WRITE_DEVICE_CONFIG" />
+ <uses-permission android:name="android.permission.READ_WRITE_SYNC_DISABLED_MODE_CONFIG" />
<uses-permission android:name="android.permission.MONITOR_DEVICE_CONFIG_ACCESS" />
<uses-permission android:name="android.permission.BROADCAST_STICKY" />
<uses-permission android:name="android.permission.MANAGE_ACCESSIBILITY" />
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 6176c61d363a..dc24dce76d78 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -967,6 +967,7 @@
android:enabled="false"
android:exported="true"
android:excludeFromRecents="true"
+ android:resizeableActivity="false"
android:theme="@android:style/Theme.NoDisplay"
android:label="@string/note_task_button_label"
android:icon="@drawable/ic_note_task_shortcut_widget">
@@ -981,6 +982,7 @@
android:name=".notetask.shortcut.LaunchNoteTaskActivity"
android:exported="true"
android:excludeFromRecents="true"
+ android:resizeableActivity="false"
android:theme="@android:style/Theme.NoDisplay" />
<!-- endregion -->
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 6209c0ba47de..19ec61d9354a 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -137,7 +137,7 @@ object Flags {
* the digits when the clock moves.
*/
@JvmField
- val STEP_CLOCK_ANIMATION = unreleasedFlag(212, "step_clock_animation", teamfood = true)
+ val STEP_CLOCK_ANIMATION = releasedFlag(212, "step_clock_animation")
/**
* Migration from the legacy isDozing/dozeAmount paths to the new KeyguardTransitionRepository
@@ -670,5 +670,5 @@ object Flags {
// TODO(b/272036292): Tracking Bug
@JvmField
val LARGE_SHADE_GRANULAR_ALPHA_INTERPOLATION =
- unreleasedFlag(2602, "large_shade_granular_alpha_interpolation")
+ unreleasedFlag(2602, "large_shade_granular_alpha_interpolation", teamfood = true)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
index 911861ddde47..28cc69758308 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
@@ -64,7 +64,11 @@ constructor(
.sample(keyguardTransitionInteractor.startedKeyguardTransitionStep, ::Pair)
.collect { pair ->
val (isAbleToDream, lastStartedTransition) = pair
- if (isAbleToDream && lastStartedTransition.to == KeyguardState.LOCKSCREEN) {
+ if (
+ isAbleToDream &&
+ lastStartedTransition.to == KeyguardState.LOCKSCREEN &&
+ lastStartedTransition.from != KeyguardState.AOD
+ ) {
keyguardTransitionRepository.startTransition(
TransitionInfo(
name,
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index e4351d2c412c..df70f6bd31da 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -839,6 +839,7 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
}
mFrame = null;
mOrientationHandle = null;
+ notifyNavigationBarSurface();
}
// TODO: Remove this when we update nav bar recreation
@@ -1003,7 +1004,8 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
private void notifyNavigationBarSurface() {
ViewRootImpl viewRoot = mView.getViewRootImpl();
- SurfaceControl surface = viewRoot != null
+ SurfaceControl surface = mView.getParent() != null
+ && viewRoot != null
&& viewRoot.getSurfaceControl() != null
&& viewRoot.getSurfaceControl().isValid()
? viewRoot.getSurfaceControl()
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
index 6cd04c861155..ac22b7ce8b6b 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
@@ -89,22 +89,16 @@ constructor(
/**
* Shows a note task. How the task is shown will depend on when the method is invoked.
*
- * If in multi-window mode, notes will open as a full screen experience. That is particularly
- * important for Large screen devices. These devices may support a taskbar that let users to
- * drag and drop a shortcut into multi-window mode, and notes should comply with this behaviour.
- *
* If the keyguard is locked, notes will open as a full screen experience. A locked device has
* no contextual information which let us use the whole screen space available.
*
- * If not in multi-window or the keyguard is unlocked, notes will open as a bubble OR it will be
- * collapsed if the notes bubble is already opened.
+ * If the keyguard is unlocked, notes will open as a bubble OR it will be collapsed if the notes
+ * bubble is already opened.
*
* That will let users open other apps in full screen, and take contextual notes.
*/
- @JvmOverloads
fun showNoteTask(
entryPoint: NoteTaskEntryPoint,
- isInMultiWindowMode: Boolean = false,
) {
if (!isEnabled) return
@@ -125,13 +119,7 @@ constructor(
return
}
- val info =
- resolver.resolveInfo(
- entryPoint = entryPoint,
- isInMultiWindowMode = isInMultiWindowMode,
- isKeyguardLocked = isKeyguardLocked,
- )
- ?: return
+ val info = resolver.resolveInfo(entryPoint, isKeyguardLocked) ?: return
infoReference.set(info)
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInfo.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInfo.kt
index 28d76474efba..2b9f0af046ff 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInfo.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInfo.kt
@@ -20,12 +20,11 @@ data class NoteTaskInfo(
val packageName: String,
val uid: Int,
val entryPoint: NoteTaskEntryPoint? = null,
- val isInMultiWindowMode: Boolean = false,
val isKeyguardLocked: Boolean = false,
) {
val launchMode: NoteTaskLaunchMode =
- if (isInMultiWindowMode || isKeyguardLocked) {
+ if (isKeyguardLocked) {
NoteTaskLaunchMode.Activity
} else {
NoteTaskLaunchMode.AppBubble
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInfoResolver.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInfoResolver.kt
index b98a0fd75331..8ecf08192e29 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInfoResolver.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInfoResolver.kt
@@ -34,7 +34,6 @@ constructor(
fun resolveInfo(
entryPoint: NoteTaskEntryPoint? = null,
- isInMultiWindowMode: Boolean = false,
isKeyguardLocked: Boolean = false,
): NoteTaskInfo? {
// TODO(b/267634412): Select UserHandle depending on where the user initiated note-taking.
@@ -48,7 +47,6 @@ constructor(
packageName = packageName,
uid = packageManager.getUidOf(packageName, user),
entryPoint = entryPoint,
- isInMultiWindowMode = isInMultiWindowMode,
isKeyguardLocked = isKeyguardLocked,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt
index 30660c492baa..8aed9958a7a2 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt
@@ -17,6 +17,7 @@
package com.android.systemui.notetask.quickaffordance
import android.content.Context
+import android.hardware.input.InputSettings
import com.android.systemui.R
import com.android.systemui.animation.Expandable
import com.android.systemui.common.shared.model.ContentDescription
@@ -26,36 +27,52 @@ import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanc
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig.LockScreenState
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig.OnTriggeredResult
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig.PickerScreenState
+import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository
import com.android.systemui.notetask.NoteTaskController
import com.android.systemui.notetask.NoteTaskEnabledKey
import com.android.systemui.notetask.NoteTaskEntryPoint
+import com.android.systemui.stylus.StylusManager
+import dagger.Lazy
import javax.inject.Inject
-import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.channels.trySendBlocking
+import kotlinx.coroutines.flow.callbackFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.map
class NoteTaskQuickAffordanceConfig
@Inject
constructor(
context: Context,
- private val noteTaskController: NoteTaskController,
+ private val controller: NoteTaskController,
+ private val stylusManager: StylusManager,
+ private val lazyRepository: Lazy<KeyguardQuickAffordanceRepository>,
@NoteTaskEnabledKey private val isEnabled: Boolean,
) : KeyguardQuickAffordanceConfig {
override val key = BuiltInKeyguardQuickAffordanceKeys.CREATE_NOTE
- override val pickerName: String = context.getString(R.string.note_task_button_label)
+ private val pickerNameResourceId = R.string.note_task_button_label
- override val pickerIconResourceId = R.drawable.ic_note_task_shortcut_keyguard
+ override val pickerName: String = context.getString(pickerNameResourceId)
- override val lockScreenState = flowOf(getLockScreenState())
+ override val pickerIconResourceId = R.drawable.ic_note_task_shortcut_keyguard
- // TODO(b/265949213)
- private fun getLockScreenState() =
- if (isEnabled) {
- val icon = Icon.Resource(pickerIconResourceId, ContentDescription.Loaded(pickerName))
- LockScreenState.Visible(icon)
- } else {
- LockScreenState.Hidden
+ // Due to a dependency cycle with KeyguardQuickAffordanceRepository, we need to lazily access
+ // the repository when lockScreenState is accessed for the first time.
+ override val lockScreenState by lazy {
+ val stylusEverUsedFlow = createStylusEverUsedFlow(context, stylusManager)
+ val configSelectedFlow = createConfigSelectedFlow(lazyRepository.get(), key)
+ combine(configSelectedFlow, stylusEverUsedFlow) { isSelected, isStylusEverUsed ->
+ if (isEnabled && (isSelected || isStylusEverUsed)) {
+ val contentDescription = ContentDescription.Resource(pickerNameResourceId)
+ val icon = Icon.Resource(pickerIconResourceId, contentDescription)
+ LockScreenState.Visible(icon)
+ } else {
+ LockScreenState.Hidden
+ }
}
+ }
override suspend fun getPickerScreenState() =
if (isEnabled) {
@@ -65,9 +82,27 @@ constructor(
}
override fun onTriggered(expandable: Expandable?): OnTriggeredResult {
- noteTaskController.showNoteTask(
+ controller.showNoteTask(
entryPoint = NoteTaskEntryPoint.QUICK_AFFORDANCE,
)
return OnTriggeredResult.Handled
}
}
+
+private fun createStylusEverUsedFlow(context: Context, stylusManager: StylusManager) =
+ callbackFlow {
+ trySendBlocking(InputSettings.isStylusEverUsed(context))
+ val callback =
+ object : StylusManager.StylusCallback {
+ override fun onStylusFirstUsed() {
+ trySendBlocking(InputSettings.isStylusEverUsed(context))
+ }
+ }
+ stylusManager.registerCallback(callback)
+ awaitClose { stylusManager.unregisterCallback(callback) }
+ }
+
+private fun createConfigSelectedFlow(repository: KeyguardQuickAffordanceRepository, key: String) =
+ repository.selections.map { selected ->
+ selected.values.flatten().any { selectedConfig -> selectedConfig.key == key }
+ }
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/shortcut/LaunchNoteTaskActivity.kt b/packages/SystemUI/src/com/android/systemui/notetask/shortcut/LaunchNoteTaskActivity.kt
index 80fce6ae288b..14b0779ab162 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/shortcut/LaunchNoteTaskActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/shortcut/LaunchNoteTaskActivity.kt
@@ -28,17 +28,12 @@ import javax.inject.Inject
class LaunchNoteTaskActivity
@Inject
constructor(
- private val noteTaskController: NoteTaskController,
+ private val controller: NoteTaskController,
) : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
-
- noteTaskController.showNoteTask(
- entryPoint = NoteTaskEntryPoint.WIDGET_PICKER_SHORTCUT,
- isInMultiWindowMode = isInMultiWindowMode,
- )
-
+ controller.showNoteTask(entryPoint = NoteTaskEntryPoint.WIDGET_PICKER_SHORTCUT)
finish()
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
index fc3a6383cd88..5cd24e675a54 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
@@ -358,6 +358,50 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
}
@Test
+ fun `LOCKSCREEN to DREAMING`() =
+ testScope.runTest {
+ // GIVEN a device that is not dreaming or dozing
+ keyguardRepository.setDreamingWithOverlay(false)
+ keyguardRepository.setWakefulnessModel(startingToWake())
+ keyguardRepository.setDozeTransitionModel(
+ DozeTransitionModel(from = DozeStateModel.DOZE, to = DozeStateModel.FINISH)
+ )
+ runCurrent()
+
+ // GIVEN a prior transition has run to LOCKSCREEN
+ runner.startTransition(
+ testScope,
+ TransitionInfo(
+ ownerName = "",
+ from = KeyguardState.GONE,
+ to = KeyguardState.LOCKSCREEN,
+ animator =
+ ValueAnimator().apply {
+ duration = 10
+ interpolator = Interpolators.LINEAR
+ },
+ )
+ )
+ reset(mockTransitionRepository)
+
+ // WHEN the device begins to dream
+ keyguardRepository.setDreamingWithOverlay(true)
+ advanceUntilIdle()
+
+ val info =
+ withArgCaptor<TransitionInfo> {
+ verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
+ }
+ // THEN a transition to DREAMING should occur
+ assertThat(info.ownerName).isEqualTo("FromLockscreenTransitionInteractor")
+ assertThat(info.from).isEqualTo(KeyguardState.LOCKSCREEN)
+ assertThat(info.to).isEqualTo(KeyguardState.DREAMING)
+ assertThat(info.animator).isNotNull()
+
+ coroutineContext.cancelChildren()
+ }
+
+ @Test
fun `LOCKSCREEN to DOZING`() =
testScope.runTest {
// GIVEN a device with AOD not available
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
index 608d809e7e56..3f940d64f236 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
@@ -72,7 +72,7 @@ internal class NoteTaskControllerTest : SysuiTestCase() {
MockitoAnnotations.initMocks(this)
whenever(context.packageManager).thenReturn(packageManager)
- whenever(resolver.resolveInfo(any(), any(), any())).thenReturn(noteTaskInfo)
+ whenever(resolver.resolveInfo(any(), any())).thenReturn(noteTaskInfo)
whenever(userManager.isUserUnlocked).thenReturn(true)
whenever(
devicePolicyManager.getKeyguardDisabledFeatures(
@@ -102,7 +102,7 @@ internal class NoteTaskControllerTest : SysuiTestCase() {
// region onBubbleExpandChanged
@Test
fun onBubbleExpandChanged_expanding_logNoteTaskOpened() {
- val expectedInfo = noteTaskInfo.copy(isKeyguardLocked = false, isInMultiWindowMode = false)
+ val expectedInfo = noteTaskInfo.copy(isKeyguardLocked = false)
createNoteTaskController()
.apply { infoReference.set(expectedInfo) }
@@ -117,7 +117,7 @@ internal class NoteTaskControllerTest : SysuiTestCase() {
@Test
fun onBubbleExpandChanged_collapsing_logNoteTaskClosed() {
- val expectedInfo = noteTaskInfo.copy(isKeyguardLocked = false, isInMultiWindowMode = false)
+ val expectedInfo = noteTaskInfo.copy(isKeyguardLocked = false)
createNoteTaskController()
.apply { infoReference.set(expectedInfo) }
@@ -132,7 +132,7 @@ internal class NoteTaskControllerTest : SysuiTestCase() {
@Test
fun onBubbleExpandChanged_expandingAndKeyguardLocked_doNothing() {
- val expectedInfo = noteTaskInfo.copy(isKeyguardLocked = true, isInMultiWindowMode = false)
+ val expectedInfo = noteTaskInfo.copy(isKeyguardLocked = true)
createNoteTaskController()
.apply { infoReference.set(expectedInfo) }
@@ -146,35 +146,7 @@ internal class NoteTaskControllerTest : SysuiTestCase() {
@Test
fun onBubbleExpandChanged_notExpandingAndKeyguardLocked_doNothing() {
- val expectedInfo = noteTaskInfo.copy(isKeyguardLocked = true, isInMultiWindowMode = false)
-
- createNoteTaskController()
- .apply { infoReference.set(expectedInfo) }
- .onBubbleExpandChanged(
- isExpanding = false,
- key = Bubble.KEY_APP_BUBBLE,
- )
-
- verifyZeroInteractions(context, bubbles, keyguardManager, userManager, eventLogger)
- }
-
- @Test
- fun onBubbleExpandChanged_expandingAndInMultiWindowMode_doNothing() {
- val expectedInfo = noteTaskInfo.copy(isKeyguardLocked = false, isInMultiWindowMode = true)
-
- createNoteTaskController()
- .apply { infoReference.set(expectedInfo) }
- .onBubbleExpandChanged(
- isExpanding = true,
- key = Bubble.KEY_APP_BUBBLE,
- )
-
- verifyZeroInteractions(context, bubbles, keyguardManager, userManager)
- }
-
- @Test
- fun onBubbleExpandChanged_notExpandingAndInMultiWindowMode_doNothing() {
- val expectedInfo = noteTaskInfo.copy(isKeyguardLocked = false, isInMultiWindowMode = true)
+ val expectedInfo = noteTaskInfo.copy(isKeyguardLocked = true)
createNoteTaskController()
.apply { infoReference.set(expectedInfo) }
@@ -215,16 +187,14 @@ internal class NoteTaskControllerTest : SysuiTestCase() {
val expectedInfo =
noteTaskInfo.copy(
entryPoint = NoteTaskEntryPoint.TAIL_BUTTON,
- isInMultiWindowMode = false,
isKeyguardLocked = true,
)
whenever(keyguardManager.isKeyguardLocked).thenReturn(expectedInfo.isKeyguardLocked)
- whenever(resolver.resolveInfo(any(), any(), any())).thenReturn(expectedInfo)
+ whenever(resolver.resolveInfo(any(), any())).thenReturn(expectedInfo)
createNoteTaskController()
.showNoteTask(
entryPoint = expectedInfo.entryPoint!!,
- isInMultiWindowMode = expectedInfo.isInMultiWindowMode,
)
val intentCaptor = argumentCaptor<Intent>()
@@ -250,16 +220,14 @@ internal class NoteTaskControllerTest : SysuiTestCase() {
val expectedInfo =
noteTaskInfo.copy(
entryPoint = NoteTaskEntryPoint.TAIL_BUTTON,
- isInMultiWindowMode = false,
isKeyguardLocked = false,
)
- whenever(resolver.resolveInfo(any(), any(), any())).thenReturn(expectedInfo)
+ whenever(resolver.resolveInfo(any(), any())).thenReturn(expectedInfo)
whenever(keyguardManager.isKeyguardLocked).thenReturn(expectedInfo.isKeyguardLocked)
createNoteTaskController()
.showNoteTask(
entryPoint = expectedInfo.entryPoint!!,
- isInMultiWindowMode = expectedInfo.isInMultiWindowMode,
)
verifyZeroInteractions(context)
@@ -275,49 +243,10 @@ internal class NoteTaskControllerTest : SysuiTestCase() {
}
@Test
- fun showNoteTask_isInMultiWindowMode_shouldStartActivityAndLogUiEvent() {
- val expectedInfo =
- noteTaskInfo.copy(
- entryPoint = NoteTaskEntryPoint.WIDGET_PICKER_SHORTCUT,
- isInMultiWindowMode = true,
- isKeyguardLocked = false,
- )
- whenever(resolver.resolveInfo(any(), any(), any())).thenReturn(expectedInfo)
- whenever(keyguardManager.isKeyguardLocked).thenReturn(expectedInfo.isKeyguardLocked)
-
- createNoteTaskController()
- .showNoteTask(
- entryPoint = expectedInfo.entryPoint!!,
- isInMultiWindowMode = expectedInfo.isInMultiWindowMode,
- )
-
- val intentCaptor = argumentCaptor<Intent>()
- val userCaptor = argumentCaptor<UserHandle>()
- verify(context).startActivityAsUser(capture(intentCaptor), capture(userCaptor))
-
- (intentCaptor.value.flags and FLAG_ACTIVITY_NEW_TASK) == FLAG_ACTIVITY_NEW_TASK
-
- intentCaptor.value.let { intent ->
- assertThat(intent.action).isEqualTo(Intent.ACTION_CREATE_NOTE)
- assertThat(intent.`package`).isEqualTo(NOTES_PACKAGE_NAME)
- assertThat(intent.flags and FLAG_ACTIVITY_NEW_TASK).isEqualTo(FLAG_ACTIVITY_NEW_TASK)
- assertThat(intent.flags and FLAG_ACTIVITY_MULTIPLE_TASK)
- .isEqualTo(FLAG_ACTIVITY_MULTIPLE_TASK)
- assertThat(intent.flags and FLAG_ACTIVITY_NEW_DOCUMENT)
- .isEqualTo(FLAG_ACTIVITY_NEW_DOCUMENT)
- assertThat(intent.getBooleanExtra(Intent.EXTRA_USE_STYLUS_MODE, false)).isTrue()
- }
- assertThat(userCaptor.value).isEqualTo(userTracker.userHandle)
- verify(eventLogger).logNoteTaskOpened(expectedInfo)
- verifyZeroInteractions(bubbles)
- }
-
- @Test
fun showNoteTask_bubblesIsNull_shouldDoNothing() {
createNoteTaskController(bubbles = null)
.showNoteTask(
entryPoint = NoteTaskEntryPoint.TAIL_BUTTON,
- isInMultiWindowMode = false,
)
verifyZeroInteractions(context, bubbles, eventLogger)
@@ -325,12 +254,11 @@ internal class NoteTaskControllerTest : SysuiTestCase() {
@Test
fun showNoteTask_intentResolverReturnsNull_shouldDoNothing() {
- whenever(resolver.resolveInfo(any(), any(), any())).thenReturn(null)
+ whenever(resolver.resolveInfo(any(), any())).thenReturn(null)
createNoteTaskController()
.showNoteTask(
entryPoint = NoteTaskEntryPoint.TAIL_BUTTON,
- isInMultiWindowMode = false,
)
verifyZeroInteractions(context, bubbles, eventLogger)
@@ -341,7 +269,6 @@ internal class NoteTaskControllerTest : SysuiTestCase() {
createNoteTaskController(isEnabled = false)
.showNoteTask(
entryPoint = NoteTaskEntryPoint.TAIL_BUTTON,
- isInMultiWindowMode = false,
)
verifyZeroInteractions(context, bubbles, eventLogger)
@@ -354,7 +281,6 @@ internal class NoteTaskControllerTest : SysuiTestCase() {
createNoteTaskController()
.showNoteTask(
entryPoint = NoteTaskEntryPoint.TAIL_BUTTON,
- isInMultiWindowMode = false,
)
verifyZeroInteractions(context, bubbles, eventLogger)
@@ -405,11 +331,7 @@ internal class NoteTaskControllerTest : SysuiTestCase() {
)
.thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_SHORTCUTS_ALL)
- createNoteTaskController()
- .showNoteTask(
- isInMultiWindowMode = false,
- entryPoint = NoteTaskEntryPoint.QUICK_AFFORDANCE
- )
+ createNoteTaskController().showNoteTask(entryPoint = NoteTaskEntryPoint.QUICK_AFFORDANCE)
verifyZeroInteractions(context, bubbles, eventLogger)
}
@@ -425,11 +347,7 @@ internal class NoteTaskControllerTest : SysuiTestCase() {
)
.thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_ALL)
- createNoteTaskController()
- .showNoteTask(
- isInMultiWindowMode = false,
- entryPoint = NoteTaskEntryPoint.QUICK_AFFORDANCE
- )
+ createNoteTaskController().showNoteTask(entryPoint = NoteTaskEntryPoint.QUICK_AFFORDANCE)
verifyZeroInteractions(context, bubbles, eventLogger)
}
@@ -445,11 +363,7 @@ internal class NoteTaskControllerTest : SysuiTestCase() {
)
.thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_SHORTCUTS_ALL)
- createNoteTaskController()
- .showNoteTask(
- isInMultiWindowMode = false,
- entryPoint = NoteTaskEntryPoint.QUICK_AFFORDANCE
- )
+ createNoteTaskController().showNoteTask(entryPoint = NoteTaskEntryPoint.QUICK_AFFORDANCE)
val intentCaptor = argumentCaptor<Intent>()
verify(bubbles).showOrHideAppBubble(capture(intentCaptor))
@@ -472,11 +386,7 @@ internal class NoteTaskControllerTest : SysuiTestCase() {
)
.thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_ALL)
- createNoteTaskController()
- .showNoteTask(
- isInMultiWindowMode = false,
- entryPoint = NoteTaskEntryPoint.QUICK_AFFORDANCE
- )
+ createNoteTaskController().showNoteTask(entryPoint = NoteTaskEntryPoint.QUICK_AFFORDANCE)
val intentCaptor = argumentCaptor<Intent>()
verify(bubbles).showOrHideAppBubble(capture(intentCaptor))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInfoTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInfoTest.kt
index 7e975b6732a5..91cd6ae5d988 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInfoTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInfoTest.kt
@@ -31,39 +31,17 @@ internal class NoteTaskInfoTest : SysuiTestCase() {
NoteTaskInfo(packageName = NOTES_PACKAGE_NAME, uid = NOTES_UID)
@Test
- fun launchMode_notInMultiWindowModeAndKeyguardUnlocked_launchModeAppBubble() {
- val underTest =
- createNoteTaskInfo()
- .copy(
- isKeyguardLocked = false,
- isInMultiWindowMode = false,
- )
-
- assertThat(underTest.launchMode).isEqualTo(NoteTaskLaunchMode.AppBubble)
- }
-
- @Test
- fun launchMode_inMultiWindowMode_launchModeActivity() {
- val underTest =
- createNoteTaskInfo()
- .copy(
- isKeyguardLocked = false,
- isInMultiWindowMode = true,
- )
+ fun launchMode_keyguardLocked_launchModeActivity() {
+ val underTest = createNoteTaskInfo().copy(isKeyguardLocked = true)
assertThat(underTest.launchMode).isEqualTo(NoteTaskLaunchMode.Activity)
}
@Test
- fun launchMode_keyguardLocked_launchModeActivity() {
- val underTest =
- createNoteTaskInfo()
- .copy(
- isKeyguardLocked = true,
- isInMultiWindowMode = false,
- )
+ fun launchMode_keyguardUnlocked_launchModeActivity() {
+ val underTest = createNoteTaskInfo().copy(isKeyguardLocked = false)
- assertThat(underTest.launchMode).isEqualTo(NoteTaskLaunchMode.Activity)
+ assertThat(underTest.launchMode).isEqualTo(NoteTaskLaunchMode.AppBubble)
}
private companion object {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt
index e57d0d943aab..d44012f31fa5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt
@@ -18,75 +18,139 @@
package com.android.systemui.notetask.quickaffordance
+import android.hardware.input.InputSettings
import android.test.suitebuilder.annotation.SmallTest
-import androidx.test.runner.AndroidJUnit4
+import android.testing.AndroidTestingRunner
+import com.android.dx.mockito.inline.extended.ExtendedMockito
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig.LockScreenState
+import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository
import com.android.systemui.notetask.NoteTaskController
import com.android.systemui.notetask.NoteTaskEntryPoint
+import com.android.systemui.stylus.StylusManager
+import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.test.runTest
+import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.verify
-import org.mockito.MockitoAnnotations
+import org.mockito.MockitoSession
+import org.mockito.quality.Strictness
-/**
- * Tests for [NoteTaskQuickAffordanceConfig].
- *
- * Build/Install/Run:
- * - atest SystemUITests:NoteTaskQuickAffordanceConfigTest
- */
+/** atest SystemUITests:NoteTaskQuickAffordanceConfigTest */
@SmallTest
-@RunWith(AndroidJUnit4::class)
+@RunWith(AndroidTestingRunner::class)
internal class NoteTaskQuickAffordanceConfigTest : SysuiTestCase() {
- @Mock lateinit var noteTaskController: NoteTaskController
+ @Mock lateinit var controller: NoteTaskController
+ @Mock lateinit var stylusManager: StylusManager
+ @Mock lateinit var repository: KeyguardQuickAffordanceRepository
+
+ private lateinit var mockitoSession: MockitoSession
@Before
fun setUp() {
- MockitoAnnotations.initMocks(this)
+ mockitoSession =
+ ExtendedMockito.mockitoSession()
+ .initMocks(this)
+ .mockStatic(InputSettings::class.java)
+ .strictness(Strictness.LENIENT)
+ .startMocking()
+
+ whenever(InputSettings.isStylusEverUsed(mContext)).then { true }
+ whenever(repository.selections).then {
+ val map = mapOf("" to listOf(createUnderTest()))
+ MutableStateFlow(map)
+ }
}
- private fun createUnderTest(isEnabled: Boolean) =
+ @After
+ fun tearDown() {
+ mockitoSession.finishMocking()
+ }
+
+ private fun createUnderTest(isEnabled: Boolean = true): KeyguardQuickAffordanceConfig =
NoteTaskQuickAffordanceConfig(
context = context,
- noteTaskController = noteTaskController,
+ controller = controller,
+ stylusManager = stylusManager,
+ lazyRepository = { repository },
isEnabled = isEnabled,
)
+ private fun createLockScreenStateVisible(): LockScreenState =
+ LockScreenState.Visible(
+ icon =
+ Icon.Resource(
+ res = R.drawable.ic_note_task_shortcut_keyguard,
+ contentDescription =
+ ContentDescription.Resource(R.string.note_task_button_label),
+ )
+ )
+
@Test
- fun lockScreenState_isNotEnabled_shouldEmitHidden() = runTest {
- val underTest = createUnderTest(isEnabled = false)
+ fun lockScreenState_stylusUsed_noCustomShortcutSelected_shouldEmitVisible() = runTest {
+ val underTest = createUnderTest()
+
+ val actual by collectLastValue(underTest.lockScreenState)
+
+ assertThat(actual).isEqualTo(createLockScreenStateVisible())
+ }
+
+ @Test
+ fun lockScreenState_noStylusEverUsed_noCustomShortcutSelected_shouldEmitVisible() = runTest {
+ whenever(InputSettings.isStylusEverUsed(mContext)).then { false }
+ val underTest = createUnderTest()
- val actual = collectLastValue(underTest.lockScreenState)
+ val actual by collectLastValue(underTest.lockScreenState)
- assertThat(actual()).isEqualTo(LockScreenState.Hidden)
+ assertThat(actual).isEqualTo(createLockScreenStateVisible())
}
@Test
- fun lockScreenState_isEnabled_shouldEmitVisible() = runTest {
- val stringResult = "Notetaking"
- val underTest = createUnderTest(isEnabled = true)
-
- val actual = collectLastValue(underTest.lockScreenState)
-
- val expected =
- LockScreenState.Visible(
- icon =
- Icon.Resource(
- res = R.drawable.ic_note_task_shortcut_keyguard,
- contentDescription = ContentDescription.Loaded(stringResult),
- )
- )
- assertThat(actual()).isEqualTo(expected)
+ fun lockScreenState_stylusUsed_customShortcutSelected_shouldEmitVisible() = runTest {
+ whenever(repository.selections).then {
+ val map = mapOf<String, List<KeyguardQuickAffordanceConfig>>()
+ MutableStateFlow(map)
+ }
+ val underTest = createUnderTest()
+
+ val actual by collectLastValue(underTest.lockScreenState)
+
+ assertThat(actual).isEqualTo(createLockScreenStateVisible())
+ }
+
+ @Test
+ fun lockScreenState_noIsStylusEverUsed_noCustomShortcutSelected_shouldEmitHidden() = runTest {
+ whenever(InputSettings.isStylusEverUsed(mContext)).then { false }
+ whenever(repository.selections).then {
+ val map = mapOf<String, List<KeyguardQuickAffordanceConfig>>()
+ MutableStateFlow(map)
+ }
+ val underTest = createUnderTest()
+
+ val actual by collectLastValue(underTest.lockScreenState)
+
+ assertThat(actual).isEqualTo(LockScreenState.Hidden)
+ }
+
+ @Test
+ fun lockScreenState_isNotEnabled_shouldEmitHidden() = runTest {
+ val underTest = createUnderTest(isEnabled = false)
+
+ val actual by collectLastValue(underTest.lockScreenState)
+
+ assertThat(actual).isEqualTo(LockScreenState.Hidden)
}
@Test
@@ -95,6 +159,6 @@ internal class NoteTaskQuickAffordanceConfigTest : SysuiTestCase() {
underTest.onTriggered(expandable = null)
- verify(noteTaskController).showNoteTask(entryPoint = NoteTaskEntryPoint.QUICK_AFFORDANCE)
+ verify(controller).showNoteTask(entryPoint = NoteTaskEntryPoint.QUICK_AFFORDANCE)
}
}
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 24c9e0f55ab9..0b22f36958d1 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -640,6 +640,7 @@ public final class ActiveServices {
mAppWidgetManagerInternal = LocalServices.getService(AppWidgetManagerInternal.class);
setAllowListWhileInUsePermissionInFgs();
initSystemExemptedFgsTypePermission();
+ initMediaProjectFgsTypeCustomPermission();
}
private AppStateTracker getAppStateTracker() {
@@ -2580,6 +2581,39 @@ public final class ActiveServices {
}
}
+ /**
+ * A custom permission checker for the "mediaProjection" FGS type:
+ * if the app has been granted the permission to start a media projection via
+ * the {@link android.media.project.MediaProjectionManager#createScreenCaptureIntent()},
+ * it'll get the permission to start a foreground service with type "mediaProjection".
+ */
+ private class MediaProjectionFgsTypeCustomPermission extends ForegroundServiceTypePermission {
+ MediaProjectionFgsTypeCustomPermission() {
+ super("Media projection screen capture permission");
+ }
+
+ @Override
+ public int checkPermission(@NonNull Context context, int callerUid, int callerPid,
+ @NonNull String packageName, boolean allowWhileInUse) {
+ return mAm.isAllowedMediaProjectionNoOpCheck(callerUid)
+ ? PERMISSION_GRANTED : PERMISSION_DENIED;
+ }
+ }
+
+ /**
+ * Set a custom permission checker for the "mediaProjection" FGS type.
+ */
+ private void initMediaProjectFgsTypeCustomPermission() {
+ final ForegroundServiceTypePolicy policy = ForegroundServiceTypePolicy.getDefaultPolicy();
+ final ForegroundServiceTypePolicyInfo policyInfo =
+ policy.getForegroundServiceTypePolicyInfo(
+ ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION,
+ ServiceInfo.FOREGROUND_SERVICE_TYPE_NONE);
+ if (policyInfo != null) {
+ policyInfo.setCustomPermission(new MediaProjectionFgsTypeCustomPermission());
+ }
+ }
+
ServiceNotificationPolicy applyForegroundServiceNotificationLocked(Notification notification,
final String tag, final int id, final String pkg, final int userId) {
// By nature of the FGS API, all FGS notifications have a null tag
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index 9e95e5fb0a40..569600482873 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -25,6 +25,7 @@ import static com.android.server.am.BroadcastConstants.DEFER_BOOT_COMPLETED_BROA
import android.annotation.NonNull;
import android.app.ActivityThread;
+import android.app.ForegroundServiceTypePolicy;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
@@ -1196,6 +1197,7 @@ final class ActivityManagerConstants extends ContentObserver {
updateUseTieredCachedAdj();
break;
default:
+ updateFGSPermissionEnforcementFlagsIfNecessary(name);
break;
}
}
@@ -1951,6 +1953,11 @@ final class ActivityManagerConstants extends ContentObserver {
DEFAULT_TIERED_CACHED_ADJ_DECAY_TIME);
}
+ private void updateFGSPermissionEnforcementFlagsIfNecessary(@NonNull String name) {
+ ForegroundServiceTypePolicy.getDefaultPolicy()
+ .updatePermissionEnforcementFlagIfNecessary(name);
+ }
+
@NeverCompile // Avoid size overhead of debugging code.
void dump(PrintWriter pw) {
pw.println("ACTIVITY MANAGER SETTINGS (dumpsys activity settings) "
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 37f744f67eba..fdd55a8406ce 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -38,6 +38,8 @@ import static android.app.ActivityManager.PROCESS_STATE_TOP;
import static android.app.ActivityManager.StopUserOnSwitch;
import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY;
import static android.app.ActivityManagerInternal.ALLOW_NON_FULL;
+import static android.app.ActivityManagerInternal.MEDIA_PROJECTION_TOKEN_EVENT_CREATED;
+import static android.app.ActivityManagerInternal.MEDIA_PROJECTION_TOKEN_EVENT_DESTROYED;
import static android.app.AppOpsManager.MODE_ALLOWED;
import static android.app.AppOpsManager.OP_NONE;
import static android.content.pm.ApplicationInfo.HIDDEN_API_ENFORCEMENT_DEFAULT;
@@ -178,6 +180,7 @@ import android.app.ActivityManagerInternal;
import android.app.ActivityManagerInternal.BindServiceEventListener;
import android.app.ActivityManagerInternal.BroadcastEventListener;
import android.app.ActivityManagerInternal.ForegroundServiceStateListener;
+import android.app.ActivityManagerInternal.MediaProjectionTokenEvent;
import android.app.ActivityTaskManager.RootTaskInfo;
import android.app.ActivityThread;
import android.app.AnrController;
@@ -1508,6 +1511,17 @@ public class ActivityManagerService extends IActivityManager.Stub
final AppRestrictionController mAppRestrictionController;
+ /**
+ * The collection of the MediaProjection tokens per UID, for the apps that are allowed to
+ * start FGS with the type "mediaProjection"; this permission is granted via the request over
+ * the call to {@link android.media.project.MediaProjectionManager#createScreenCaptureIntent()}.
+ *
+ * <p>Note, the "token" here is actually an instance of
+ * {@link android.media.projection.IMediaProjection}.</p>
+ */
+ @GuardedBy("mMediaProjectionTokenMap")
+ private final SparseArray<ArraySet<IBinder>> mMediaProjectionTokenMap = new SparseArray();
+
private final class AppDeathRecipient implements IBinder.DeathRecipient {
final ProcessRecord mApp;
final int mPid;
@@ -9866,7 +9880,6 @@ public class ActivityManagerService extends IActivityManager.Stub
boolean dumpNormalPriority = false;
boolean dumpVisibleStacksOnly = false;
boolean dumpFocusedStackOnly = false;
- boolean dumpVerbose = false;
int dumpDisplayId = INVALID_DISPLAY;
String dumpPackage = null;
int dumpUserId = UserHandle.USER_ALL;
@@ -9925,8 +9938,6 @@ public class ActivityManagerService extends IActivityManager.Stub
return;
}
dumpClient = true;
- } else if ("--verbose".equals(opt)) {
- dumpVerbose = true;
} else if ("-h".equals(opt)) {
ActivityManagerShellCommand.dumpHelp(pw, true);
return;
@@ -10213,8 +10224,7 @@ public class ActivityManagerService extends IActivityManager.Stub
} else {
// Dumping a single activity?
if (!mAtmInternal.dumpActivity(fd, pw, cmd, args, opti, dumpAll,
- dumpVisibleStacksOnly, dumpFocusedStackOnly, dumpVerbose, dumpDisplayId,
- dumpUserId)) {
+ dumpVisibleStacksOnly, dumpFocusedStackOnly, dumpDisplayId, dumpUserId)) {
ActivityManagerShellCommand shell = new ActivityManagerShellCommand(this, true);
int res = shell.exec(this, null, fd, null, args, null,
new ResultReceiver(null));
@@ -18141,7 +18151,7 @@ public class ActivityManagerService extends IActivityManager.Stub
| Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
final Bundle configChangedOptions = new BroadcastOptions()
.setDeliveryGroupPolicy(BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT)
- .setDeferUntilActive(true)
+ .setDeferralPolicy(BroadcastOptions.DEFERRAL_POLICY_UNTIL_ACTIVE)
.toBundle();
broadcastIntentLocked(null, null, null, intent, null, null, 0, null, null, null,
null, null, OP_NONE, configChangedOptions, false, false, MY_PID, SYSTEM_UID,
@@ -18160,7 +18170,7 @@ public class ActivityManagerService extends IActivityManager.Stub
PowerExemptionManager.REASON_LOCALE_CHANGED, "");
bOptions.setDeliveryGroupPolicy(
BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT);
- bOptions.setDeferUntilActive(true);
+ bOptions.setDeferralPolicy(BroadcastOptions.DEFERRAL_POLICY_UNTIL_ACTIVE);
broadcastIntentLocked(null, null, null, intent, null, null, 0, null, null, null,
null, null, OP_NONE, bOptions.toBundle(), false, false, MY_PID,
SYSTEM_UID, Binder.getCallingUid(), Binder.getCallingPid(),
@@ -18205,7 +18215,7 @@ public class ActivityManagerService extends IActivityManager.Stub
final BroadcastOptions options = new BroadcastOptions()
.setDeliveryGroupPolicy(BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT)
- .setDeferUntilActive(true);
+ .setDeferralPolicy(BroadcastOptions.DEFERRAL_POLICY_UNTIL_ACTIVE);
if (reason != null) {
options.setDeliveryGroupMatchingKey(Intent.ACTION_CLOSE_SYSTEM_DIALOGS, reason);
}
@@ -18741,6 +18751,12 @@ public class ActivityManagerService extends IActivityManager.Stub
int uid, int pid) {
ActivityManagerService.this.logFgsApiEnd(apiType, uid, pid);
}
+
+ @Override
+ public void notifyMediaProjectionEvent(int uid, @NonNull IBinder projectionToken,
+ @MediaProjectionTokenEvent int event) {
+ ActivityManagerService.this.notifyMediaProjectionEvent(uid, projectionToken, event);
+ }
}
long inputDispatchingTimedOut(int pid, final boolean aboveSystem, TimeoutRecord timeoutRecord) {
@@ -19974,4 +19990,41 @@ public class ActivityManagerService extends IActivityManager.Stub
return invalidValue;
}
}
+
+ private void notifyMediaProjectionEvent(int uid, @NonNull IBinder projectionToken,
+ @MediaProjectionTokenEvent int event) {
+ synchronized (mMediaProjectionTokenMap) {
+ final int index = mMediaProjectionTokenMap.indexOfKey(uid);
+ ArraySet<IBinder> tokens;
+ if (event == MEDIA_PROJECTION_TOKEN_EVENT_CREATED) {
+ if (index < 0) {
+ tokens = new ArraySet();
+ mMediaProjectionTokenMap.put(uid, tokens);
+ } else {
+ tokens = mMediaProjectionTokenMap.valueAt(index);
+ }
+ tokens.add(projectionToken);
+ } else if (event == MEDIA_PROJECTION_TOKEN_EVENT_DESTROYED && index >= 0) {
+ tokens = mMediaProjectionTokenMap.valueAt(index);
+ tokens.remove(projectionToken);
+ if (tokens.isEmpty()) {
+ mMediaProjectionTokenMap.removeAt(index);
+ }
+ }
+ }
+ }
+
+ /**
+ * @return {@code true} if the MediaProjectionManagerService has created a media projection
+ * for the given {@code uid} because the user has granted the permission;
+ * it doesn't necessarily mean it has started the projection.
+ *
+ * <p>It doesn't check the {@link AppOpsManager#OP_PROJECT_MEDIA}.</p>
+ */
+ boolean isAllowedMediaProjectionNoOpCheck(int uid) {
+ synchronized (mMediaProjectionTokenMap) {
+ final int index = mMediaProjectionTokenMap.indexOfKey(uid);
+ return index >= 0 && !mMediaProjectionTokenMap.valueAt(index).isEmpty();
+ }
+ }
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 01bb549630b8..809f3f7ff857 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -3957,7 +3957,6 @@ final class ActivityManagerShellCommand extends ShellCommand {
pw.println(" --checkin: output checkin format, resetting data.");
pw.println(" --C: output checkin format, not resetting data.");
pw.println(" --proto: output dump in protocol buffer format.");
- pw.println(" --verbose: dumps extra information.");
pw.printf(" %s: dump just the DUMPABLE-related state of an activity. Use the %s "
+ "option to list the supported DUMPABLEs\n", Activity.DUMP_ARG_DUMP_DUMPABLE,
Activity.DUMP_ARG_LIST_DUMPABLES);
diff --git a/services/core/java/com/android/server/am/DropboxRateLimiter.java b/services/core/java/com/android/server/am/DropboxRateLimiter.java
index 37926259ffb3..9ff2cd0649d4 100644
--- a/services/core/java/com/android/server/am/DropboxRateLimiter.java
+++ b/services/core/java/com/android/server/am/DropboxRateLimiter.java
@@ -31,11 +31,17 @@ public class DropboxRateLimiter {
// process/eventType) further entries will be rejected until RATE_LIMIT_BUFFER_DURATION has
// elapsed, after which the current count for this breakdown will be reset.
private static final long RATE_LIMIT_BUFFER_DURATION = 10 * DateUtils.MINUTE_IN_MILLIS;
- // The time duration after which the rate limit buffer will be cleared.
- private static final long RATE_LIMIT_BUFFER_EXPIRY = 3 * RATE_LIMIT_BUFFER_DURATION;
+ // Indicated how many buffer durations to wait before the rate limit buffer will be cleared.
+ // E.g. if set to 3 will wait 3xRATE_LIMIT_BUFFER_DURATION before clearing the buffer.
+ private static final long RATE_LIMIT_BUFFER_EXPIRY_FACTOR = 3;
// The number of entries to keep per breakdown of process/eventType.
private static final int RATE_LIMIT_ALLOWED_ENTRIES = 6;
+ // If a process is rate limited twice in a row we consider it crash-looping and rate limit it
+ // more aggressively.
+ private static final int STRICT_RATE_LIMIT_ALLOWED_ENTRIES = 1;
+ private static final long STRICT_RATE_LIMIT_BUFFER_DURATION = 60 * DateUtils.MINUTE_IN_MILLIS;
+
@GuardedBy("mErrorClusterRecords")
private final ArrayMap<String, ErrorRecord> mErrorClusterRecords = new ArrayMap<>();
private final Clock mClock;
@@ -71,15 +77,27 @@ public class DropboxRateLimiter {
return new RateLimitResult(false, 0);
}
- if (now - errRecord.getStartTime() > RATE_LIMIT_BUFFER_DURATION) {
+ final long timeSinceFirstError = now - errRecord.getStartTime();
+ if (timeSinceFirstError > errRecord.getBufferDuration()) {
final int errCount = recentlyDroppedCount(errRecord);
errRecord.setStartTime(now);
errRecord.setCount(1);
+
+ // If this error happened exactly the next "rate limiting cycle" after the last
+ // error and the previous cycle was rate limiting then increment the successive
+ // rate limiting cycle counter. If a full "cycle" has passed since the last error
+ // then this is no longer a continuous occurrence and will be rate limited normally.
+ if (errCount > 0 && timeSinceFirstError < 2 * errRecord.getBufferDuration()) {
+ errRecord.incrementSuccessiveRateLimitCycles();
+ } else {
+ errRecord.setSuccessiveRateLimitCycles(0);
+ }
+
return new RateLimitResult(false, errCount);
}
errRecord.incrementCount();
- if (errRecord.getCount() > RATE_LIMIT_ALLOWED_ENTRIES) {
+ if (errRecord.getCount() > errRecord.getAllowedEntries()) {
return new RateLimitResult(true, recentlyDroppedCount(errRecord));
}
}
@@ -91,16 +109,19 @@ public class DropboxRateLimiter {
* dropped. Resets every RATE_LIMIT_BUFFER_DURATION if events are still actively created or
* RATE_LIMIT_BUFFER_EXPIRY if not. */
private int recentlyDroppedCount(ErrorRecord errRecord) {
- if (errRecord == null || errRecord.getCount() < RATE_LIMIT_ALLOWED_ENTRIES) return 0;
- return errRecord.getCount() - RATE_LIMIT_ALLOWED_ENTRIES;
+ if (errRecord == null || errRecord.getCount() < errRecord.getAllowedEntries()) return 0;
+ return errRecord.getCount() - errRecord.getAllowedEntries();
}
- private void maybeRemoveExpiredRecords(long now) {
- if (now - mLastMapCleanUp <= RATE_LIMIT_BUFFER_EXPIRY) return;
+ private void maybeRemoveExpiredRecords(long currentTime) {
+ if (currentTime - mLastMapCleanUp
+ <= RATE_LIMIT_BUFFER_EXPIRY_FACTOR * RATE_LIMIT_BUFFER_DURATION) {
+ return;
+ }
for (int i = mErrorClusterRecords.size() - 1; i >= 0; i--) {
- if (now - mErrorClusterRecords.valueAt(i).getStartTime() > RATE_LIMIT_BUFFER_EXPIRY) {
+ if (mErrorClusterRecords.valueAt(i).hasExpired(currentTime)) {
Counter.logIncrement(
"stability_errors.value_dropbox_buffer_expired_count",
mErrorClusterRecords.valueAt(i).getCount());
@@ -108,7 +129,7 @@ public class DropboxRateLimiter {
}
}
- mLastMapCleanUp = now;
+ mLastMapCleanUp = currentTime;
}
/** Resets the rate limiter memory. */
@@ -153,10 +174,12 @@ public class DropboxRateLimiter {
private class ErrorRecord {
long mStartTime;
int mCount;
+ int mSuccessiveRateLimitCycles;
ErrorRecord(long startTime, int count) {
mStartTime = startTime;
mCount = count;
+ mSuccessiveRateLimitCycles = 0;
}
public void setStartTime(long startTime) {
@@ -171,6 +194,14 @@ public class DropboxRateLimiter {
mCount++;
}
+ public void setSuccessiveRateLimitCycles(int successiveRateLimitCycles) {
+ mSuccessiveRateLimitCycles = successiveRateLimitCycles;
+ }
+
+ public void incrementSuccessiveRateLimitCycles() {
+ mSuccessiveRateLimitCycles++;
+ }
+
public long getStartTime() {
return mStartTime;
}
@@ -178,6 +209,27 @@ public class DropboxRateLimiter {
public int getCount() {
return mCount;
}
+
+ public int getSuccessiveRateLimitCycles() {
+ return mSuccessiveRateLimitCycles;
+ }
+
+ public boolean isRepeated() {
+ return mSuccessiveRateLimitCycles >= 2;
+ }
+
+ public int getAllowedEntries() {
+ return isRepeated() ? STRICT_RATE_LIMIT_ALLOWED_ENTRIES : RATE_LIMIT_ALLOWED_ENTRIES;
+ }
+
+ public long getBufferDuration() {
+ return isRepeated() ? STRICT_RATE_LIMIT_BUFFER_DURATION : RATE_LIMIT_BUFFER_DURATION;
+ }
+
+ public boolean hasExpired(long currentTime) {
+ long bufferExpiry = RATE_LIMIT_BUFFER_EXPIRY_FACTOR * getBufferDuration();
+ return currentTime - mStartTime > bufferExpiry;
+ }
}
private static class DefaultClock implements Clock {
diff --git a/services/core/java/com/android/server/appop/AppOpsCheckingServiceTracingDecorator.java b/services/core/java/com/android/server/appop/AppOpsCheckingServiceTracingDecorator.java
index dd06464c4699..a028ae16da2f 100644
--- a/services/core/java/com/android/server/appop/AppOpsCheckingServiceTracingDecorator.java
+++ b/services/core/java/com/android/server/appop/AppOpsCheckingServiceTracingDecorator.java
@@ -32,7 +32,7 @@ import java.io.PrintWriter;
* Trace.traceEnd. These traces are used for performance testing.
*/
public class AppOpsCheckingServiceTracingDecorator implements AppOpsCheckingServiceInterface {
- private static final long TRACE_TAG = Trace.TRACE_TAG_SYSTEM_SERVER;
+ private static final long TRACE_TAG = Trace.TRACE_TAG_ACTIVITY_MANAGER;
private final AppOpsCheckingServiceInterface mService;
AppOpsCheckingServiceTracingDecorator(
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 1ce917cb7841..6d3f8fd77232 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -21,6 +21,7 @@ import static android.Manifest.permission.CONTROL_VPN;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_VPN;
import static android.net.RouteInfo.RTN_THROW;
import static android.net.RouteInfo.RTN_UNREACHABLE;
@@ -29,6 +30,7 @@ import static android.net.ipsec.ike.IkeSessionParams.ESP_ENCAP_TYPE_AUTO;
import static android.net.ipsec.ike.IkeSessionParams.ESP_IP_VERSION_AUTO;
import static android.os.PowerWhitelistManager.REASON_VPN;
import static android.os.UserHandle.PER_USER_RANGE;
+import static android.telephony.CarrierConfigManager.KEY_MIN_UDP_PORT_4500_NAT_TIMEOUT_SEC_INT;
import static com.android.net.module.util.NetworkStackConstants.IPV6_MIN_MTU;
import static com.android.server.vcn.util.PersistableBundleUtils.STRING_DESERIALIZER;
@@ -79,7 +81,9 @@ import android.net.NetworkInfo.DetailedState;
import android.net.NetworkProvider;
import android.net.NetworkRequest;
import android.net.NetworkScore;
+import android.net.NetworkSpecifier;
import android.net.RouteInfo;
+import android.net.TelephonyNetworkSpecifier;
import android.net.UidRangeParcel;
import android.net.UnderlyingNetworkInfo;
import android.net.Uri;
@@ -127,12 +131,16 @@ import android.security.keystore.KeyProperties;
import android.system.keystore2.Domain;
import android.system.keystore2.KeyDescriptor;
import android.system.keystore2.KeyPermission;
+import android.telephony.CarrierConfigManager;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.IndentingPrintWriter;
import android.util.LocalLog;
import android.util.Log;
import android.util.Range;
+import android.util.SparseArray;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
@@ -268,6 +276,10 @@ public class Vpn {
private final ConnectivityManager mConnectivityManager;
private final AppOpsManager mAppOpsManager;
private final ConnectivityDiagnosticsManager mConnectivityDiagnosticsManager;
+ private final TelephonyManager mTelephonyManager;
+ private final CarrierConfigManager mCarrierConfigManager;
+ private final SubscriptionManager mSubscriptionManager;
+
// The context is for specific user which is created from mUserId
private final Context mUserIdContext;
@VisibleForTesting final Dependencies mDeps;
@@ -314,6 +326,14 @@ public class Vpn {
private final LocalLog mVpnManagerEvents = new LocalLog(MAX_EVENTS_LOGS);
/**
+ * Cached Map of <subscription ID, keepalive delay ms> since retrieving the PersistableBundle
+ * and the target value from CarrierConfigManager is somewhat expensive as it has hundreds of
+ * fields. This cache is cleared when the carrier config changes to ensure data freshness.
+ */
+ @GuardedBy("this")
+ private final SparseArray<Integer> mCachedKeepalivePerSubId = new SparseArray<>();
+
+ /**
* Whether to keep the connection active after rebooting, or upgrading or reinstalling. This
* only applies to {@link VpnService} connections.
*/
@@ -626,6 +646,10 @@ public class Vpn {
mUserIdContext = context.createContextAsUser(UserHandle.of(userId), 0 /* flags */);
mConnectivityDiagnosticsManager =
mContext.getSystemService(ConnectivityDiagnosticsManager.class);
+ mCarrierConfigManager = mContext.getSystemService(CarrierConfigManager.class);
+ mTelephonyManager = mContext.getSystemService(TelephonyManager.class);
+ mSubscriptionManager = mContext.getSystemService(SubscriptionManager.class);
+
mDeps = deps;
mNms = netService;
mNetd = netd;
@@ -2893,6 +2917,24 @@ public class Vpn {
*/
private int mRetryCount = 0;
+ private CarrierConfigManager.CarrierConfigChangeListener mCarrierConfigChangeListener =
+ new CarrierConfigManager.CarrierConfigChangeListener() {
+ @Override
+ public void onCarrierConfigChanged(int slotIndex, int subId, int carrierId,
+ int specificCarrierId) {
+ synchronized (Vpn.this) {
+ mCachedKeepalivePerSubId.remove(subId);
+
+ // Ignore stale runner.
+ if (mVpnRunner != Vpn.IkeV2VpnRunner.this) return;
+
+ maybeMigrateIkeSession(mActiveNetwork);
+ }
+ // TODO: update the longLivedTcpConnectionsExpensive value in the
+ // networkcapabilities of the VPN network.
+ }
+ };
+
IkeV2VpnRunner(
@NonNull Ikev2VpnProfile profile, @NonNull ScheduledThreadPoolExecutor executor) {
super(TAG);
@@ -2918,6 +2960,9 @@ public class Vpn {
setVpnNetworkPreference(mSessionKey,
createUserAndRestrictedProfilesRanges(mUserId,
mConfig.allowedApplications, mConfig.disallowedApplications));
+
+ mCarrierConfigManager.registerCarrierConfigChangeListener(mExecutor,
+ mCarrierConfigChangeListener);
}
@Override
@@ -3257,8 +3302,6 @@ public class Vpn {
mUnderlyingLinkProperties = null;
mUnderlyingNetworkCapabilities = null;
mRetryCount = 0;
-
- startOrMigrateIkeSession(network);
}
@NonNull
@@ -3356,9 +3399,36 @@ public class Vpn {
}
private int guessNattKeepaliveTimerForNetwork() {
- // TODO : guess the keepalive delay based on carrier if auto keepalive timer is
- // enabled
- return AUTOMATIC_KEEPALIVE_DELAY_SECONDS;
+ final int subId = getCellSubIdForNetworkCapabilities(mUnderlyingNetworkCapabilities);
+ if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+ Log.d(TAG, "Underlying network is not a cellular network");
+ return AUTOMATIC_KEEPALIVE_DELAY_SECONDS;
+ }
+
+ synchronized (Vpn.this) {
+ if (mCachedKeepalivePerSubId.contains(subId)) {
+ Log.d(TAG, "Get cached keepalive config");
+ return mCachedKeepalivePerSubId.get(subId);
+ }
+
+ final TelephonyManager perSubTm = mTelephonyManager.createForSubscriptionId(subId);
+ if (perSubTm.getSimApplicationState() != TelephonyManager.SIM_STATE_LOADED) {
+ Log.d(TAG, "SIM card is not ready on sub " + subId);
+ return AUTOMATIC_KEEPALIVE_DELAY_SECONDS;
+ }
+
+ final PersistableBundle carrierConfig =
+ mCarrierConfigManager.getConfigForSubId(subId);
+ if (!CarrierConfigManager.isConfigForIdentifiedCarrier(carrierConfig)) {
+ return AUTOMATIC_KEEPALIVE_DELAY_SECONDS;
+ }
+
+ final int natKeepalive =
+ carrierConfig.getInt(KEY_MIN_UDP_PORT_4500_NAT_TIMEOUT_SEC_INT);
+ mCachedKeepalivePerSubId.put(subId, natKeepalive);
+ Log.d(TAG, "Get customized keepalive=" + natKeepalive);
+ return natKeepalive;
+ }
}
boolean maybeMigrateIkeSession(@NonNull Network underlyingNetwork) {
@@ -3456,7 +3526,15 @@ public class Vpn {
/** Called when the NetworkCapabilities of underlying network is changed */
public void onDefaultNetworkCapabilitiesChanged(@NonNull NetworkCapabilities nc) {
+ final NetworkCapabilities oldNc = mUnderlyingNetworkCapabilities;
mUnderlyingNetworkCapabilities = nc;
+ if (oldNc == null) {
+ // A new default network is available.
+ startOrMigrateIkeSession(mActiveNetwork);
+ } else if (!nc.getSubscriptionIds().equals(oldNc.getSubscriptionIds())) {
+ // Renew carrierConfig values.
+ maybeMigrateIkeSession(mActiveNetwork);
+ }
}
/** Called when the LinkProperties of underlying network is changed */
@@ -3812,6 +3890,8 @@ public class Vpn {
resetIkeState();
+ mCarrierConfigManager.unregisterCarrierConfigChangeListener(
+ mCarrierConfigChangeListener);
mConnectivityManager.unregisterNetworkCallback(mNetworkCallback);
mConnectivityDiagnosticsManager.unregisterConnectivityDiagnosticsCallback(
mDiagnosticsCallback);
@@ -4804,6 +4884,8 @@ public class Vpn {
pw.println("Reset session scheduled");
}
}
+ pw.println("mCachedKeepalivePerSubId=" + mCachedKeepalivePerSubId);
+
pw.println("mUnderlyNetworkChanges (most recent first):");
pw.increaseIndent();
mUnderlyNetworkChanges.reverseDump(pw);
@@ -4815,4 +4897,19 @@ public class Vpn {
pw.decreaseIndent();
}
}
+
+ private static int getCellSubIdForNetworkCapabilities(@Nullable NetworkCapabilities nc) {
+ if (nc == null) return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+
+ if (!nc.hasTransport(TRANSPORT_CELLULAR)) {
+ return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ }
+
+ final NetworkSpecifier specifier = nc.getNetworkSpecifier();
+ if (specifier instanceof TelephonyNetworkSpecifier) {
+ return ((TelephonyNetworkSpecifier) specifier).getSubscriptionId();
+ }
+
+ return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ }
}
diff --git a/services/core/java/com/android/server/display/mode/SkinThermalStatusObserver.java b/services/core/java/com/android/server/display/mode/SkinThermalStatusObserver.java
index 1bb34abe9025..f93d9ee55d30 100644
--- a/services/core/java/com/android/server/display/mode/SkinThermalStatusObserver.java
+++ b/services/core/java/com/android/server/display/mode/SkinThermalStatusObserver.java
@@ -84,7 +84,11 @@ final class SkinThermalStatusObserver extends IThermalEventListener.Stub impleme
@Override
public void notifyThrottling(Temperature temp) {
@Temperature.ThrottlingStatus int currentStatus = temp.getStatus();
+
synchronized (mThermalObserverLock) {
+ if (mStatus == currentStatus) {
+ return; // status not changed, skip update
+ }
mStatus = currentStatus;
mHandler.post(this::updateVotes);
}
@@ -188,6 +192,10 @@ final class SkinThermalStatusObserver extends IThermalEventListener.Stub impleme
localStatus = mStatus;
localMap = mThermalThrottlingByDisplay.get(displayId);
}
+ if (localMap == null) {
+ Slog.d(TAG, "Updating votes, display already removed, display=" + displayId);
+ return;
+ }
if (mLoggingEnabled) {
Slog.d(TAG, "Updating votes for status=" + localStatus + ", display =" + displayId
+ ", map=" + localMap);
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodDeviceConfigs.java b/services/core/java/com/android/server/inputmethod/InputMethodDeviceConfigs.java
index dc2799e8e434..6cd2493cfdff 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodDeviceConfigs.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodDeviceConfigs.java
@@ -21,6 +21,8 @@ import static android.provider.InputMethodManagerDeviceConfig.KEY_HIDE_IME_WHEN_
import android.app.ActivityThread;
import android.provider.DeviceConfig;
+import com.android.internal.annotations.VisibleForTesting;
+
/**
* Class for the device-level configuration related to the input method manager
* platform features in {@link DeviceConfig}.
@@ -56,4 +58,9 @@ final class InputMethodDeviceConfigs {
public boolean shouldHideImeWhenNoEditorFocus() {
return mHideImeWhenNoEditorFocus;
}
+
+ @VisibleForTesting
+ void destroy() {
+ DeviceConfig.removeOnPropertiesChangedListener(mDeviceConfigChangedListener);
+ }
}
diff --git a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
index 77b9abed0d41..48326188e215 100644
--- a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
+++ b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
@@ -16,6 +16,9 @@
package com.android.server.media.projection;
+import static android.app.ActivityManagerInternal.MEDIA_PROJECTION_TOKEN_EVENT_CREATED;
+import static android.app.ActivityManagerInternal.MEDIA_PROJECTION_TOKEN_EVENT_DESTROYED;
+
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -484,6 +487,9 @@ public final class MediaProjectionManagerService extends SystemService
userHandle = new UserHandle(UserHandle.getUserId(uid));
mTargetSdkVersion = targetSdkVersion;
mIsPrivileged = isPrivileged;
+ // TODO(b/267740338): Add unit test.
+ mActivityManagerInternal.notifyMediaProjectionEvent(uid, asBinder(),
+ MEDIA_PROJECTION_TOKEN_EVENT_CREATED);
}
@Override // Binder call
@@ -630,6 +636,9 @@ public final class MediaProjectionManagerService extends SystemService
mToken = null;
unregisterCallback(mCallback);
mCallback = null;
+ // TODO(b/267740338): Add unit test.
+ mActivityManagerInternal.notifyMediaProjectionEvent(uid, asBinder(),
+ MEDIA_PROJECTION_TOKEN_EVENT_DESTROYED);
}
}
diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
index 85f13572db68..646dc4e98c39 100644
--- a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
+++ b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
@@ -29,6 +29,7 @@ import com.android.internal.policy.IKeyguardExitCallback;
import com.android.internal.policy.IKeyguardService;
import com.android.server.UiThread;
import com.android.server.policy.WindowManagerPolicy.OnKeyguardExitResult;
+import com.android.server.wm.EventLogTags;
import java.io.PrintWriter;
@@ -255,6 +256,11 @@ public class KeyguardServiceDelegate {
public void setOccluded(boolean isOccluded, boolean notify) {
if (mKeyguardService != null && notify) {
if (DEBUG) Log.v(TAG, "setOccluded(" + isOccluded + ")");
+ EventLogTags.writeWmSetKeyguardOccluded(
+ isOccluded ? 1 : 0,
+ 0 /* animate */,
+ 0 /* transit */,
+ "setOccluded");
mKeyguardService.setOccluded(isOccluded, false /* animate */);
}
mKeyguardState.occluded = isOccluded;
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index c6684dfdb6e0..5442b6dc4b09 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -2125,7 +2125,7 @@ public final class TvInputManagerService extends SystemService {
}
@Override
- public void notifyTvMessage(IBinder sessionToken, String type, Bundle data, int userId) {
+ public void notifyTvMessage(IBinder sessionToken, int type, Bundle data, int userId) {
final int callingUid = Binder.getCallingUid();
final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
userId, "timeShiftEnablePositionTracking");
@@ -2590,7 +2590,7 @@ public final class TvInputManagerService extends SystemService {
}
@Override
- public void notifyAdBuffer(
+ public void notifyAdBufferReady(
IBinder sessionToken, AdBuffer buffer, int userId) {
final int callingUid = Binder.getCallingUid();
final int callingPid = Binder.getCallingPid();
@@ -2602,7 +2602,7 @@ public final class TvInputManagerService extends SystemService {
try {
SessionState sessionState = getSessionStateLocked(sessionToken, callingUid,
resolvedUserId);
- getSessionLocked(sessionState).notifyAdBuffer(buffer);
+ getSessionLocked(sessionState).notifyAdBufferReady(buffer);
} catch (RemoteException | SessionNotFoundException e) {
Slog.e(TAG, "error in notifyAdBuffer", e);
}
@@ -3765,7 +3765,7 @@ public final class TvInputManagerService extends SystemService {
}
@Override
- public void onTvMessage(String type, Bundle data) {
+ public void onTvMessage(int type, Bundle data) {
synchronized (mLock) {
if (DEBUG) {
Slog.d(TAG, "onTvMessage(type=" + type + ", data=" + data + ")");
diff --git a/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java b/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java
index 61c137ed1c5b..0d4a76ee3add 100644
--- a/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java
+++ b/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java
@@ -1117,7 +1117,7 @@ public class TvInteractiveAppManagerService extends SystemService {
}
@Override
- public void notifyTvMessage(IBinder sessionToken, String type, Bundle data, int userId) {
+ public void notifyTvMessage(IBinder sessionToken, int type, Bundle data, int userId) {
final int callingUid = Binder.getCallingUid();
final int callingPid = Binder.getCallingPid();
final int resolvedUserId = resolveCallingUserId(callingPid, callingUid, userId,
@@ -3047,16 +3047,16 @@ public class TvInteractiveAppManagerService extends SystemService {
}
@Override
- public void onAdBuffer(AdBuffer buffer) {
+ public void onAdBufferReady(AdBuffer buffer) {
synchronized (mLock) {
if (DEBUG) {
- Slogf.d(TAG, "onAdBuffer(buffer=" + buffer + ")");
+ Slogf.d(TAG, "onAdBufferReady(buffer=" + buffer + ")");
}
if (mSessionState.mSession == null || mSessionState.mClient == null) {
return;
}
try {
- mSessionState.mClient.onAdBuffer(buffer, mSessionState.mSeq);
+ mSessionState.mClient.onAdBufferReady(buffer, mSessionState.mSeq);
} catch (RemoteException e) {
Slogf.e(TAG, "error in onAdBuffer", e);
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 28b974c7330a..c7754194d3b1 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -1734,7 +1734,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
: null;
}
- private void clearLastParentBeforePip() {
+ void clearLastParentBeforePip() {
if (mLastParentBeforePip != null) {
mLastParentBeforePip.mChildPipActivity = null;
mLastParentBeforePip = null;
@@ -7824,27 +7824,38 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
/**
* Returns the requested {@link Configuration.Orientation} for the current activity.
+ */
+ @Configuration.Orientation
+ @Override
+ int getRequestedConfigurationOrientation(boolean forDisplay) {
+ return getRequestedConfigurationOrientation(forDisplay, getOverrideOrientation());
+ }
+
+ /**
+ * Returns the requested {@link Configuration.Orientation} for the requested
+ * {@link ActivityInfo.ScreenOrientation}.
*
- * <p>When The current orientation is set to {@link SCREEN_ORIENTATION_BEHIND} it returns the
- * requested orientation for the activity below which is the first activity with an explicit
+ * <p>When the current screen orientation is set to {@link SCREEN_ORIENTATION_BEHIND} it returns
+ * the requested orientation for the activity below which is the first activity with an explicit
* (different from {@link SCREEN_ORIENTATION_UNSET}) orientation which is not {@link
* SCREEN_ORIENTATION_BEHIND}.
*/
@Configuration.Orientation
- @Override
- int getRequestedConfigurationOrientation(boolean forDisplay) {
+ int getRequestedConfigurationOrientation(boolean forDisplay,
+ @ActivityInfo.ScreenOrientation int requestedOrientation) {
if (mLetterboxUiController.hasInheritedOrientation()) {
final RootDisplayArea root = getRootDisplayArea();
if (forDisplay && root != null && root.isOrientationDifferentFromDisplay()) {
- return ActivityInfo.reverseOrientation(
+ return reverseConfigurationOrientation(
mLetterboxUiController.getInheritedOrientation());
} else {
return mLetterboxUiController.getInheritedOrientation();
}
}
- if (task != null && getOverrideOrientation() == SCREEN_ORIENTATION_BEHIND) {
+ if (task != null && requestedOrientation == SCREEN_ORIENTATION_BEHIND) {
// We use Task here because we want to be consistent with what happens in
// multi-window mode where other tasks orientations are ignored.
+ android.util.Log.d("orientation", "We are here");
final ActivityRecord belowCandidate = task.getActivity(
a -> a.canDefineOrientationForActivitiesAbove() /* callback */,
this /* boundary */, false /* includeBoundary */,
@@ -7853,7 +7864,23 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
return belowCandidate.getRequestedConfigurationOrientation(forDisplay);
}
}
- return super.getRequestedConfigurationOrientation(forDisplay);
+ return super.getRequestedConfigurationOrientation(forDisplay, requestedOrientation);
+ }
+
+ /**
+ * Returns the reversed configuration orientation.
+ * @hide
+ */
+ @Configuration.Orientation
+ public static int reverseConfigurationOrientation(@Configuration.Orientation int orientation) {
+ switch (orientation) {
+ case ORIENTATION_LANDSCAPE:
+ return ORIENTATION_PORTRAIT;
+ case ORIENTATION_PORTRAIT:
+ return ORIENTATION_LANDSCAPE;
+ default:
+ return orientation;
+ }
}
/**
@@ -7899,6 +7926,19 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
if (mLetterboxUiController.shouldIgnoreRequestedOrientation(requestedOrientation)) {
return;
}
+ // This is necessary in order to avoid going into size compat mode when the orientation
+ // change request comes from the app
+ if (mWmService.mLetterboxConfiguration
+ .isSizeCompatModeDisabledAfterOrientationChangeFromApp()
+ && getRequestedConfigurationOrientation(false, requestedOrientation)
+ != getRequestedConfigurationOrientation(false /*forDisplay */)) {
+ // Do not change the requested configuration now, because this will be done when setting
+ // the orientation below with the new mCompatDisplayInsets
+ clearSizeCompatModeAttributes();
+ }
+ ProtoLog.v(WM_DEBUG_ORIENTATION,
+ "Setting requested orientation %s for %s",
+ ActivityInfo.screenOrientationToString(requestedOrientation), this);
setOrientation(requestedOrientation, this);
// Push the new configuration to the requested app in case where it's not pushed, e.g. when
@@ -8139,17 +8179,20 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
mDisplayContent, this, mLetterboxBoundsForFixedOrientationAndAspectRatio);
}
- @VisibleForTesting
- void clearSizeCompatMode() {
- final float lastSizeCompatScale = mSizeCompatScale;
+ private void clearSizeCompatModeAttributes() {
mInSizeCompatModeForBounds = false;
mSizeCompatScale = 1f;
mSizeCompatBounds = null;
mCompatDisplayInsets = null;
+ }
+
+ @VisibleForTesting
+ void clearSizeCompatMode() {
+ final float lastSizeCompatScale = mSizeCompatScale;
+ clearSizeCompatModeAttributes();
if (mSizeCompatScale != lastSizeCompatScale) {
forAllWindows(WindowState::updateGlobalScale, false /* traverseTopToBottom */);
}
-
// Clear config override in #updateCompatDisplayInsets().
final int activityType = getActivityType();
final Configuration overrideConfig = getRequestedOverrideConfiguration();
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index 5ea28542207e..bfb735de2d0a 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -490,8 +490,7 @@ public abstract class ActivityTaskManagerInternal {
/** Dump the current activities state. */
public abstract boolean dumpActivity(FileDescriptor fd, PrintWriter pw, String name,
String[] args, int opti, boolean dumpAll, boolean dumpVisibleRootTasksOnly,
- boolean dumpFocusedRootTaskOnly, boolean dumpVerbose, int displayIdFilter,
- @UserIdInt int userId);
+ boolean dumpFocusedRootTaskOnly, int displayIdFilter, @UserIdInt int userId);
/** Dump the current state for inclusion in oom dump. */
public abstract void dumpForOom(PrintWriter pw);
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 898b47785a7b..992743ab8593 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -4205,8 +4205,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
*/
protected boolean dumpActivity(FileDescriptor fd, PrintWriter pw, String name, String[] args,
int opti, boolean dumpAll, boolean dumpVisibleRootTasksOnly,
- boolean dumpFocusedRootTaskOnly, boolean dumpVerbose, int displayIdFilter,
- @UserIdInt int userId) {
+ boolean dumpFocusedRootTaskOnly, int displayIdFilter, @UserIdInt int userId) {
ArrayList<ActivityRecord> activities;
synchronized (mGlobalLock) {
@@ -4251,7 +4250,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
}
}
- dumpActivity(" ", fd, pw, activities.get(i), newArgs, dumpAll, dumpVerbose);
+ dumpActivity(" ", fd, pw, activities.get(i), newArgs, dumpAll);
}
if (!printedAnything) {
// Typically happpens when no task matches displayIdFilter
@@ -4265,7 +4264,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
* there is a thread associated with the activity.
*/
private void dumpActivity(String prefix, FileDescriptor fd, PrintWriter pw,
- ActivityRecord r, String[] args, boolean dumpAll, boolean dumpVerbose) {
+ ActivityRecord r, String[] args, boolean dumpAll) {
String innerPrefix = prefix + " ";
IApplicationThread appThread = null;
synchronized (mGlobalLock) {
@@ -4281,15 +4280,11 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
} else {
pw.print("(not running)");
}
- if (dumpVerbose) {
- pw.print(" userId=");
- pw.print(r.mUserId);
- pw.print(" uid=");
- pw.print(r.getUid());
- printDisplayInfoAndNewLine(pw, r);
- } else {
- pw.println();
- }
+ pw.print(" userId=");
+ pw.print(r.mUserId);
+ pw.print(" uid=");
+ pw.print(r.getUid());
+ printDisplayInfoAndNewLine(pw, r);
if (dumpAll) {
r.dump(pw, innerPrefix, /* dumpAll= */ true);
}
@@ -6618,11 +6613,10 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
@Override
public boolean dumpActivity(FileDescriptor fd, PrintWriter pw, String name,
String[] args, int opti, boolean dumpAll, boolean dumpVisibleRootTasksOnly,
- boolean dumpFocusedRootTaskOnly, boolean dumpVerbose, int displayIdFilter,
+ boolean dumpFocusedRootTaskOnly, int displayIdFilter,
@UserIdInt int userId) {
return ActivityTaskManagerService.this.dumpActivity(fd, pw, name, args, opti, dumpAll,
- dumpVisibleRootTasksOnly, dumpFocusedRootTaskOnly, dumpVerbose, displayIdFilter,
- userId);
+ dumpVisibleRootTasksOnly, dumpFocusedRootTaskOnly, displayIdFilter, userId);
}
@Override
diff --git a/services/core/java/com/android/server/wm/EventLogTags.logtags b/services/core/java/com/android/server/wm/EventLogTags.logtags
index 594929bea77a..244656ca68e6 100644
--- a/services/core/java/com/android/server/wm/EventLogTags.logtags
+++ b/services/core/java/com/android/server/wm/EventLogTags.logtags
@@ -49,7 +49,7 @@ option java_package com.android.server.wm
30066 wm_add_to_stopping (User|1|5),(Token|1|5),(Component Name|3),(Reason|3)
# Keyguard status changed
-30067 wm_set_keyguard_shown (Display Id|1|5),(keyguardShowing|1),(aodShowing|1),(keyguardGoingAway|1),(Reason|3)
+30067 wm_set_keyguard_shown (Display Id|1|5),(keyguardShowing|1),(aodShowing|1),(keyguardGoingAway|1),(occluded|1),(Reason|3)
# Out of memory for surfaces.
31000 wm_no_surface_memory (Window|3),(PID|1|5),(Operation|3)
@@ -65,6 +65,8 @@ option java_package com.android.server.wm
# bootanim finished:
31007 wm_boot_animation_done (time|2|3)
+# Notify keyguard occlude statuc change to SysUI.
+31008 wm_set_keyguard_occluded (occluded|1),(animate|1),(transit|1),(Channel|3)
# Back navigation.
31100 wm_back_navi_canceled (Reason|3)
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index 32b3ccf85c94..08a6358afbc7 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -188,6 +188,7 @@ class KeyguardController {
keyguardShowing ? 1 : 0,
aodShowing ? 1 : 0,
state.mKeyguardGoingAway ? 1 : 0,
+ state.mOccluded ? 1 : 0,
"setKeyguardShown");
// Update the task snapshot if the screen will not be turned off. To make sure that the
@@ -253,9 +254,10 @@ class KeyguardController {
try {
EventLogTags.writeWmSetKeyguardShown(
displayId,
- 1 /* keyguardShowing */,
+ state.mKeyguardShowing ? 1 : 0,
state.mAodShowing ? 1 : 0,
1 /* keyguardGoingAway */,
+ state.mOccluded ? 1 : 0,
"keyguardGoingAway");
final int transitFlags = convertTransitFlags(flags);
final DisplayContent dc = mRootWindowContainer.getDefaultDisplay();
@@ -669,6 +671,15 @@ class KeyguardController {
boolean hasChange = false;
if (lastOccluded != mOccluded) {
+ if (mDisplayId == DEFAULT_DISPLAY) {
+ EventLogTags.writeWmSetKeyguardShown(
+ mDisplayId,
+ mKeyguardShowing ? 1 : 0,
+ mAodShowing ? 1 : 0,
+ mKeyguardGoingAway ? 1 : 0,
+ mOccluded ? 1 : 0,
+ "updateVisibility");
+ }
controller.handleOccludedChanged(mDisplayId, mTopOccludesActivity);
hasChange = true;
} else if (!lastKeyguardGoingAway && mKeyguardGoingAway) {
diff --git a/services/core/java/com/android/server/wm/LetterboxConfiguration.java b/services/core/java/com/android/server/wm/LetterboxConfiguration.java
index fa49a6ba6c2b..37cf5bc95a23 100644
--- a/services/core/java/com/android/server/wm/LetterboxConfiguration.java
+++ b/services/core/java/com/android/server/wm/LetterboxConfiguration.java
@@ -19,6 +19,7 @@ package com.android.server.wm;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.LetterboxConfigurationDeviceConfig.KEY_ALLOW_IGNORE_ORIENTATION_REQUEST;
+import static com.android.server.wm.LetterboxConfigurationDeviceConfig.KEY_DISABLE_SIZE_COMPAT_MODE_AFTER_ORIENTATION_CHANGE_FROM_APP;
import static com.android.server.wm.LetterboxConfigurationDeviceConfig.KEY_ENABLE_CAMERA_COMPAT_TREATMENT;
import static com.android.server.wm.LetterboxConfigurationDeviceConfig.KEY_ENABLE_COMPAT_FAKE_FOCUS;
import static com.android.server.wm.LetterboxConfigurationDeviceConfig.KEY_ENABLE_DISPLAY_ROTATION_IMMERSIVE_APP_COMPAT_POLICY;
@@ -314,6 +315,10 @@ final class LetterboxConfiguration {
mDeviceConfig.updateFlagActiveStatus(
/* isActive */ mTranslucentLetterboxingEnabled,
/* key */ KEY_ENABLE_LETTERBOX_TRANSLUCENT_ACTIVITY);
+ mDeviceConfig.updateFlagActiveStatus(
+ /* isActive */ true,
+ /* key */ KEY_DISABLE_SIZE_COMPAT_MODE_AFTER_ORIENTATION_CHANGE_FROM_APP);
+
mLetterboxConfigurationPersister = letterboxConfigurationPersister;
mLetterboxConfigurationPersister.start();
}
@@ -327,6 +332,16 @@ final class LetterboxConfiguration {
}
/**
+ * Whether size compat mode is disabled after an orientation change request comes from the app.
+ * This value is controlled via {@link android.provider.DeviceConfig}.
+ */
+ // TODO(b/270356567) Clean up this flag
+ boolean isSizeCompatModeDisabledAfterOrientationChangeFromApp() {
+ return mDeviceConfig.getFlag(
+ KEY_DISABLE_SIZE_COMPAT_MODE_AFTER_ORIENTATION_CHANGE_FROM_APP);
+ }
+
+ /**
* Overrides the aspect ratio of letterbox for fixed orientation. If given value is <= {@link
* #MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO}, both it and a value of {@link
* com.android.internal.R.dimen.config_fixedOrientationLetterboxAspectRatio} will be ignored and
diff --git a/services/core/java/com/android/server/wm/LetterboxConfigurationDeviceConfig.java b/services/core/java/com/android/server/wm/LetterboxConfigurationDeviceConfig.java
index df3c8f0fdccc..1651af328b1f 100644
--- a/services/core/java/com/android/server/wm/LetterboxConfigurationDeviceConfig.java
+++ b/services/core/java/com/android/server/wm/LetterboxConfigurationDeviceConfig.java
@@ -20,7 +20,6 @@ import android.annotation.NonNull;
import android.provider.DeviceConfig;
import android.util.ArraySet;
-
import com.android.internal.annotations.VisibleForTesting;
import java.util.Map;
@@ -53,6 +52,11 @@ final class LetterboxConfigurationDeviceConfig
private static final boolean DEFAULT_VALUE_ENABLE_LETTERBOX_TRANSLUCENT_ACTIVITY = true;
+ static final String KEY_DISABLE_SIZE_COMPAT_MODE_AFTER_ORIENTATION_CHANGE_FROM_APP =
+ "disable_size_compat_mode_after_orientation_change_from_app";
+ private static final boolean
+ DEFAULT_VALUE_DISABLE_SIZE_COMPAT_MODE_AFTER_ORIENTATION_CHANGE_FROM_APP = true;
+
@VisibleForTesting
static final Map<String, Boolean> sKeyToDefaultValueMap = Map.of(
KEY_ENABLE_CAMERA_COMPAT_TREATMENT,
@@ -64,7 +68,9 @@ final class LetterboxConfigurationDeviceConfig
KEY_ENABLE_COMPAT_FAKE_FOCUS,
DEFAULT_VALUE_ENABLE_COMPAT_FAKE_FOCUS,
KEY_ENABLE_LETTERBOX_TRANSLUCENT_ACTIVITY,
- DEFAULT_VALUE_ENABLE_LETTERBOX_TRANSLUCENT_ACTIVITY
+ DEFAULT_VALUE_ENABLE_LETTERBOX_TRANSLUCENT_ACTIVITY,
+ KEY_DISABLE_SIZE_COMPAT_MODE_AFTER_ORIENTATION_CHANGE_FROM_APP,
+ DEFAULT_VALUE_DISABLE_SIZE_COMPAT_MODE_AFTER_ORIENTATION_CHANGE_FROM_APP
);
// Whether camera compatibility treatment is enabled.
@@ -93,6 +99,10 @@ final class LetterboxConfigurationDeviceConfig
private boolean mIsTranslucentLetterboxingAllowed =
DEFAULT_VALUE_ENABLE_LETTERBOX_TRANSLUCENT_ACTIVITY;
+ // Whether size compat mode is disabled after an orientation change request comes from the app
+ private boolean mIsSizeCompatModeDisabledAfterOrientationChangeFromApp =
+ DEFAULT_VALUE_DISABLE_SIZE_COMPAT_MODE_AFTER_ORIENTATION_CHANGE_FROM_APP;
+
// Set of active device configs that need to be updated in
// DeviceConfig.OnPropertiesChangedListener#onPropertiesChanged.
private final ArraySet<String> mActiveDeviceConfigsSet = new ArraySet<>();
@@ -142,6 +152,8 @@ final class LetterboxConfigurationDeviceConfig
return mIsCompatFakeFocusAllowed;
case KEY_ENABLE_LETTERBOX_TRANSLUCENT_ACTIVITY:
return mIsTranslucentLetterboxingAllowed;
+ case KEY_DISABLE_SIZE_COMPAT_MODE_AFTER_ORIENTATION_CHANGE_FROM_APP:
+ return mIsSizeCompatModeDisabledAfterOrientationChangeFromApp;
default:
throw new AssertionError("Unexpected flag name: " + key);
}
@@ -169,6 +181,10 @@ final class LetterboxConfigurationDeviceConfig
case KEY_ENABLE_LETTERBOX_TRANSLUCENT_ACTIVITY:
mIsTranslucentLetterboxingAllowed = getDeviceConfig(key, defaultValue);
break;
+ case KEY_DISABLE_SIZE_COMPAT_MODE_AFTER_ORIENTATION_CHANGE_FROM_APP:
+ mIsSizeCompatModeDisabledAfterOrientationChangeFromApp =
+ getDeviceConfig(key, defaultValue);
+ break;
default:
throw new AssertionError("Unexpected flag name: " + key);
}
diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java
index 7208934efd51..f355f088b608 100644
--- a/services/core/java/com/android/server/wm/LetterboxUiController.java
+++ b/services/core/java/com/android/server/wm/LetterboxUiController.java
@@ -1395,9 +1395,9 @@ final class LetterboxUiController {
FIRST_OPAQUE_NOT_FINISHING_ACTIVITY_PREDICATE /* callback */,
mActivityRecord /* boundary */, false /* includeBoundary */,
true /* traverseTopToBottom */);
- if (firstOpaqueActivityBeneath == null) {
+ if (firstOpaqueActivityBeneath == null || firstOpaqueActivityBeneath.isEmbedded()) {
// We skip letterboxing if the translucent activity doesn't have any opaque
- // activities beneath
+ // activities beneath or the activity below is embedded which never has letterbox.
return;
}
inheritConfiguration(firstOpaqueActivityBeneath);
diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java
index 3635ebbad018..b879d1a8ec56 100644
--- a/services/core/java/com/android/server/wm/RemoteAnimationController.java
+++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java
@@ -16,6 +16,8 @@
package com.android.server.wm;
+import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_UNOCCLUDE;
+
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_REMOTE_ANIMATIONS;
import static com.android.server.wm.AnimationAdapterProto.REMOTE;
import static com.android.server.wm.RemoteAnimationAdapterWrapperProto.TARGET;
@@ -195,6 +197,13 @@ class RemoteAnimationController implements DeathRecipient {
+ " transit=%s, apps=%d, wallpapers=%d, nonApps=%d",
AppTransition.appTransitionOldToString(transit), appTargets.length,
wallpaperTargets.length, nonAppTargets.length);
+ if (AppTransition.isKeyguardOccludeTransitOld(transit)) {
+ EventLogTags.writeWmSetKeyguardOccluded(
+ transit == TRANSIT_OLD_KEYGUARD_UNOCCLUDE ? 0 : 1,
+ 1 /* animate */,
+ transit,
+ "onAnimationStart");
+ }
mRemoteAnimationAdapter.getRunner().onAnimationStart(transit, appTargets,
wallpaperTargets, nonAppTargets, mFinishedCallback);
} catch (RemoteException e) {
@@ -356,6 +365,11 @@ class RemoteAnimationController implements DeathRecipient {
final boolean isKeyguardOccluded = mDisplayContent.isKeyguardOccluded();
try {
+ EventLogTags.writeWmSetKeyguardOccluded(
+ isKeyguardOccluded ? 1 : 0,
+ 0 /* animate */,
+ 0 /* transit */,
+ "onAnimationCancelled");
mRemoteAnimationAdapter.getRunner().onAnimationCancelled(isKeyguardOccluded);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to notify cancel", e);
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 3680e6dff9fe..254c911a52da 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -2584,6 +2584,9 @@ class Task extends TaskFragment {
EventLogTags.writeWmTaskRemoved(mTaskId, getRootTaskId(), getDisplayId(), reason);
clearPinnedTaskIfNeed();
+ if (mChildPipActivity != null) {
+ mChildPipActivity.clearLastParentBeforePip();
+ }
// If applicable let the TaskOrganizer know the Task is vanishing.
setTaskOrganizer(null);
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 132f5a72aadb..d42a62971395 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -1493,7 +1493,26 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
*/
@Configuration.Orientation
int getRequestedConfigurationOrientation(boolean forDisplay) {
- int requestedOrientation = getOverrideOrientation();
+ return getRequestedConfigurationOrientation(forDisplay, getOverrideOrientation());
+ }
+
+ /**
+ * Gets the configuration orientation by the requested screen orientation
+ *
+ * @param forDisplay whether it is the requested config orientation for display.
+ * If {@code true}, we may reverse the requested orientation if the root is
+ * different from the display, so that when the display rotates to the
+ * reversed orientation, the requested app will be in the requested
+ * orientation.
+ * @param requestedOrientation the screen orientation({@link ScreenOrientation}) that is
+ * requested
+ * @return orientation in ({@link Configuration#ORIENTATION_LANDSCAPE},
+ * {@link Configuration#ORIENTATION_PORTRAIT},
+ * {@link Configuration#ORIENTATION_UNDEFINED}).
+ */
+ @Configuration.Orientation
+ int getRequestedConfigurationOrientation(boolean forDisplay,
+ @ScreenOrientation int requestedOrientation) {
final RootDisplayArea root = getRootDisplayArea();
if (forDisplay && root != null && root.isOrientationDifferentFromDisplay()) {
// Reverse the requested orientation if the orientation of its root is different from
@@ -1503,7 +1522,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
// (portrait).
// When an app below the DAG is requesting landscape, it should actually request the
// display to be portrait, so that the DAG and the app will be in landscape.
- requestedOrientation = reverseOrientation(getOverrideOrientation());
+ requestedOrientation = reverseOrientation(requestedOrientation);
}
if (requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_NOSENSOR) {
diff --git a/services/credentials/java/com/android/server/credentials/CredentialDescriptionRegistry.java b/services/credentials/java/com/android/server/credentials/CredentialDescriptionRegistry.java
index 14c49b312f34..3b92cc9647f7 100644
--- a/services/credentials/java/com/android/server/credentials/CredentialDescriptionRegistry.java
+++ b/services/credentials/java/com/android/server/credentials/CredentialDescriptionRegistry.java
@@ -25,17 +25,19 @@ import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
-
+import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.locks.ReentrantLock;
+import java.util.stream.Collectors;
/** Contains information on what CredentialProvider has what provisioned Credential. */
public class CredentialDescriptionRegistry {
+ private static final String FLAT_STRING_SPLIT_REGEX = ";";
private static final int MAX_ALLOWED_CREDENTIAL_DESCRIPTIONS = 128;
private static final int MAX_ALLOWED_ENTRIES_PER_PROVIDER = 16;
@GuardedBy("sLock")
@@ -164,14 +166,16 @@ public class CredentialDescriptionRegistry {
/** Returns package names and entries of a CredentialProviders that can satisfy a given
* {@link CredentialDescription}. */
public Set<FilterResult> getFilteredResultForProvider(String packageName,
- String flatRequestStrings) {
+ String flatRequestString) {
Set<FilterResult> result = new HashSet<>();
if (!mCredentialDescriptions.containsKey(packageName)) {
return result;
}
Set<CredentialDescription> currentSet = mCredentialDescriptions.get(packageName);
+ Set<String> unflattenedRequestString = flatStringToSet(flatRequestString);
for (CredentialDescription containedDescription: currentSet) {
- if (flatRequestStrings.equals(containedDescription.getFlattenedRequestString())) {
+ if (checkForMatch(flatStringToSet(containedDescription.getFlattenedRequestString()),
+ unflattenedRequestString)) {
result.add(new FilterResult(packageName,
containedDescription.getFlattenedRequestString(), containedDescription
.getCredentialEntries()));
@@ -182,12 +186,16 @@ public class CredentialDescriptionRegistry {
/** Returns package names of CredentialProviders that can satisfy a given
* {@link CredentialDescription}. */
- public Set<FilterResult> getMatchingProviders(Set<String> flatRequestString) {
+ public Set<FilterResult> getMatchingProviders(Set<String> flatRequestStrings) {
Set<FilterResult> result = new HashSet<>();
+ Set<Set<String>> unflattenedRequestStrings = flatRequestStrings.stream().map(
+ CredentialDescriptionRegistry::flatStringToSet).collect(Collectors.toSet());
for (String packageName: mCredentialDescriptions.keySet()) {
Set<CredentialDescription> currentSet = mCredentialDescriptions.get(packageName);
for (CredentialDescription containedDescription : currentSet) {
- if (flatRequestString.contains(containedDescription.getFlattenedRequestString())) {
+ if (canProviderSatisfyAny(flatStringToSet(containedDescription
+ .getFlattenedRequestString()),
+ unflattenedRequestStrings)) {
result.add(new FilterResult(packageName,
containedDescription.getFlattenedRequestString(), containedDescription
.getCredentialEntries()));
@@ -203,4 +211,24 @@ public class CredentialDescriptionRegistry {
}
}
+ private static boolean canProviderSatisfyAny(Set<String> registeredUnflattenedStrings,
+ Set<Set<String>> requestedUnflattenedStrings) {
+ for (Set<String> requestedUnflattenedString : requestedUnflattenedStrings) {
+ if (registeredUnflattenedStrings.containsAll(requestedUnflattenedString)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static boolean checkForMatch(Set<String> registeredUnflattenedStrings,
+ Set<String> requestedUnflattenedString) {
+ return registeredUnflattenedStrings.containsAll(requestedUnflattenedString);
+ }
+
+ private static Set<String> flatStringToSet(String flatString) {
+ return new HashSet<>(Arrays
+ .asList(flatString.split(FLAT_STRING_SPLIT_REGEX)));
+ }
+
}
diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
index 6e998c4ef195..55973fa41ab6 100644
--- a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
+++ b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
@@ -33,7 +33,6 @@ import android.content.pm.ServiceInfo;
import android.credentials.ClearCredentialStateRequest;
import android.credentials.CreateCredentialException;
import android.credentials.CreateCredentialRequest;
-import android.credentials.CredentialDescription;
import android.credentials.CredentialManager;
import android.credentials.CredentialOption;
import android.credentials.CredentialProviderInfo;
@@ -296,11 +295,6 @@ public final class CredentialManagerService
mContext,
UserHandle.getCallingUserId(),
session,
- CredentialProviderInfoFactory.getCredentialProviderFromPackageName(
- mContext, UserHandle.getCallingUserId() ,
- result.second.mPackageName,
- CredentialManager.PROVIDER_FILTER_ALL_PROVIDERS,
- new HashSet<>()),
session.mClientAppInfo,
result.second.mPackageName,
result.first));
@@ -752,44 +746,6 @@ public final class CredentialManagerService
enforceCallingPackage(callingPackage, Binder.getCallingUid());
- List<CredentialProviderInfo> services =
- getServicesForCredentialDescription(UserHandle.getCallingUserId());
-
- List<String> providers =
- services.stream()
- .map(
- credentialProviderInfo ->
- credentialProviderInfo.getServiceInfo().packageName)
- .toList();
-
- if (!providers.contains(callingPackage)) {
- throw new NonCredentialProviderCallerException(callingPackage);
- }
-
- List<CredentialProviderInfo> matchingService =
- services.stream()
- .filter(
- credentialProviderInfo ->
- credentialProviderInfo
- .getServiceInfo()
- .packageName
- .equals(callingPackage))
- .toList();
-
- CredentialProviderInfo credentialProviderInfo = matchingService.get(0);
-
- Set<String> supportedTypes =
- request.getCredentialDescriptions().stream()
- .map(CredentialDescription::getType)
- .filter(credentialProviderInfo::hasCapability)
- .collect(Collectors.toSet());
-
- if (supportedTypes.size() != request.getCredentialDescriptions().size()) {
- throw new IllegalArgumentException(
- "CredentialProvider does not support one or more"
- + "of the registered types. Check your XML entry.");
- }
-
CredentialDescriptionRegistry session =
CredentialDescriptionRegistry.forUser(UserHandle.getCallingUserId());
@@ -808,20 +764,6 @@ public final class CredentialManagerService
enforceCallingPackage(callingPackage, Binder.getCallingUid());
- List<CredentialProviderInfo> services =
- getServicesForCredentialDescription(UserHandle.getCallingUserId());
-
- List<String> providers =
- services.stream()
- .map(
- credentialProviderInfo ->
- credentialProviderInfo.getServiceInfo().packageName)
- .toList();
-
- if (!providers.contains(callingPackage)) {
- throw new NonCredentialProviderCallerException(callingPackage);
- }
-
CredentialDescriptionRegistry session =
CredentialDescriptionRegistry.forUser(UserHandle.getCallingUserId());
diff --git a/services/credentials/java/com/android/server/credentials/MetricUtilities.java b/services/credentials/java/com/android/server/credentials/MetricUtilities.java
index 255b2f80ff44..2dc930d42d62 100644
--- a/services/credentials/java/com/android/server/credentials/MetricUtilities.java
+++ b/services/credentials/java/com/android/server/credentials/MetricUtilities.java
@@ -24,7 +24,7 @@ import android.util.Log;
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.credentials.metrics.ApiName;
import com.android.server.credentials.metrics.ApiStatus;
-import com.android.server.credentials.metrics.CandidateProviderMetric;
+import com.android.server.credentials.metrics.CandidatePhaseMetric;
import com.android.server.credentials.metrics.ChosenProviderMetric;
import java.util.Map;
@@ -98,7 +98,7 @@ public class MetricUtilities {
int[] candidateStatusList = new int[providerSize];
int index = 0;
for (var session : providerSessions) {
- CandidateProviderMetric metric = session.mCandidateProviderMetric;
+ CandidatePhaseMetric metric = session.mCandidateProviderMetric;
candidateUidList[index] = metric.getCandidateUid();
candidateQueryRoundTripTimeList[index] = metric.getQueryLatencyMicroseconds();
candidateStatusList[index] = metric.getProviderQueryStatus();
diff --git a/services/credentials/java/com/android/server/credentials/ProviderClearSession.java b/services/credentials/java/com/android/server/credentials/ProviderClearSession.java
index b7a4cd581a1a..ab29acc72a53 100644
--- a/services/credentials/java/com/android/server/credentials/ProviderClearSession.java
+++ b/services/credentials/java/com/android/server/credentials/ProviderClearSession.java
@@ -72,7 +72,8 @@ public final class ProviderClearSession extends ProviderSession<ClearCredential
ProviderInternalCallback callbacks,
int userId, RemoteCredentialService remoteCredentialService,
ClearCredentialStateRequest providerRequest) {
- super(context, info, providerRequest, callbacks, userId, remoteCredentialService);
+ super(context, providerRequest, callbacks, info.getComponentName(),
+ userId, remoteCredentialService);
setStatus(Status.PENDING);
}
@@ -95,7 +96,7 @@ public final class ProviderClearSession extends ProviderSession<ClearCredential
/** Called when provider service dies. */
@Override // Callback from the remote provider
public void onProviderServiceDied(RemoteCredentialService service) {
- if (service.getComponentName().equals(mProviderInfo.getServiceInfo().getComponentName())) {
+ if (service.getComponentName().equals(mComponentName)) {
updateStatusAndInvokeCallback(Status.SERVICE_DEAD);
} else {
Slog.i(TAG, "Component names different in onProviderServiceDied - "
diff --git a/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java b/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java
index 640cc3331b1a..8c9c6cfe24da 100644
--- a/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java
+++ b/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java
@@ -133,7 +133,7 @@ public final class ProviderCreateSession extends ProviderSession<
@NonNull BeginCreateCredentialRequest beginCreateRequest,
@NonNull CreateCredentialRequest completeCreateRequest,
String hybridService) {
- super(context, info, beginCreateRequest, callbacks, userId,
+ super(context, beginCreateRequest, callbacks, info.getComponentName(), userId,
remoteCredentialService);
mCompleteRequest = completeCreateRequest;
setStatus(Status.PENDING);
@@ -161,7 +161,7 @@ public final class ProviderCreateSession extends ProviderSession<
/** Called when provider service dies. */
@Override
public void onProviderServiceDied(RemoteCredentialService service) {
- if (service.getComponentName().equals(mProviderInfo.getServiceInfo().getComponentName())) {
+ if (service.getComponentName().equals(mComponentName)) {
updateStatusAndInvokeCallback(Status.SERVICE_DEAD);
} else {
Slog.i(TAG, "Component names different in onProviderServiceDied - "
diff --git a/services/credentials/java/com/android/server/credentials/ProviderGetSession.java b/services/credentials/java/com/android/server/credentials/ProviderGetSession.java
index 07e2f877bfc0..851ccb33aa43 100644
--- a/services/credentials/java/com/android/server/credentials/ProviderGetSession.java
+++ b/services/credentials/java/com/android/server/credentials/ProviderGetSession.java
@@ -169,7 +169,8 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential
CallingAppInfo callingAppInfo,
Map<String, CredentialOption> beginGetOptionToCredentialOptionMap,
String hybridService) {
- super(context, info, beginGetRequest, callbacks, userId, remoteCredentialService);
+ super(context, beginGetRequest, callbacks, info.getComponentName() ,
+ userId, remoteCredentialService);
mCompleteRequest = completeGetRequest;
mCallingAppInfo = callingAppInfo;
setStatus(Status.PENDING);
@@ -196,7 +197,7 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential
/** Called when provider service dies. */
@Override // Callback from the remote provider
public void onProviderServiceDied(RemoteCredentialService service) {
- if (service.getComponentName().equals(mProviderInfo.getServiceInfo().getComponentName())) {
+ if (service.getComponentName().equals(mComponentName)) {
updateStatusAndInvokeCallback(Status.SERVICE_DEAD);
} else {
Slog.i(TAG, "Component names different in onProviderServiceDied - "
diff --git a/services/credentials/java/com/android/server/credentials/ProviderRegistryGetSession.java b/services/credentials/java/com/android/server/credentials/ProviderRegistryGetSession.java
index 457806de7c9f..a5196c519740 100644
--- a/services/credentials/java/com/android/server/credentials/ProviderRegistryGetSession.java
+++ b/services/credentials/java/com/android/server/credentials/ProviderRegistryGetSession.java
@@ -19,10 +19,10 @@ package com.android.server.credentials;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.credentials.CredentialOption;
-import android.credentials.CredentialProviderInfo;
import android.credentials.GetCredentialException;
import android.credentials.GetCredentialResponse;
import android.credentials.ui.Entry;
@@ -64,7 +64,6 @@ public class ProviderRegistryGetSession extends ProviderSession<CredentialOption
@NonNull Context context,
@UserIdInt int userId,
@NonNull GetRequestSession getRequestSession,
- @NonNull CredentialProviderInfo credentialProviderInfo,
@NonNull CallingAppInfo callingAppInfo,
@NonNull String credentialProviderPackageName,
@NonNull CredentialOption requestOption) {
@@ -72,7 +71,6 @@ public class ProviderRegistryGetSession extends ProviderSession<CredentialOption
context,
userId,
getRequestSession,
- credentialProviderInfo,
callingAppInfo,
credentialProviderPackageName,
requestOption);
@@ -94,11 +92,12 @@ public class ProviderRegistryGetSession extends ProviderSession<CredentialOption
protected ProviderRegistryGetSession(@NonNull Context context,
@NonNull int userId,
@NonNull GetRequestSession session,
- @NonNull CredentialProviderInfo credentialProviderInfo,
@NonNull CallingAppInfo callingAppInfo,
@NonNull String servicePackageName,
@NonNull CredentialOption requestOption) {
- super(context, credentialProviderInfo, requestOption, session, userId, null);
+ super(context, requestOption, session,
+ new ComponentName(servicePackageName, servicePackageName) ,
+ userId, null);
mCredentialDescriptionRegistry = CredentialDescriptionRegistry.forUser(userId);
mCallingAppInfo = callingAppInfo;
mCredentialProviderPackageName = servicePackageName;
diff --git a/services/credentials/java/com/android/server/credentials/ProviderSession.java b/services/credentials/java/com/android/server/credentials/ProviderSession.java
index a8b9bf6b1fd6..03e2a3264a9e 100644
--- a/services/credentials/java/com/android/server/credentials/ProviderSession.java
+++ b/services/credentials/java/com/android/server/credentials/ProviderSession.java
@@ -31,7 +31,7 @@ import android.os.ICancellationSignal;
import android.os.RemoteException;
import android.util.Log;
-import com.android.server.credentials.metrics.CandidateProviderMetric;
+import com.android.server.credentials.metrics.CandidatePhaseMetric;
import com.android.server.credentials.metrics.ProviderStatusForMetrics;
import java.util.UUID;
@@ -59,7 +59,7 @@ public abstract class ProviderSession<T, R>
@Nullable protected R mProviderResponse;
@NonNull protected Boolean mProviderResponseSet = false;
// Specific candidate provider metric for the provider this session handles
- @Nullable protected CandidateProviderMetric mCandidateProviderMetric;
+ @Nullable protected CandidatePhaseMetric mCandidateProviderMetric;
@NonNull private int mProviderSessionUid;
/**
@@ -114,19 +114,20 @@ public abstract class ProviderSession<T, R>
@Nullable String message);
}
- protected ProviderSession(@NonNull Context context, @Nullable CredentialProviderInfo info,
+ protected ProviderSession(@NonNull Context context,
@NonNull T providerRequest,
@Nullable ProviderInternalCallback callbacks,
+ @NonNull ComponentName componentName,
@NonNull int userId,
@Nullable RemoteCredentialService remoteCredentialService) {
mContext = context;
- mProviderInfo = info;
+ mProviderInfo = null;
mProviderRequest = providerRequest;
mCallbacks = callbacks;
mUserId = userId;
- mComponentName = info.getServiceInfo().getComponentName();
+ mComponentName = componentName;
mRemoteCredentialService = remoteCredentialService;
- mCandidateProviderMetric = new CandidateProviderMetric();
+ mCandidateProviderMetric = new CandidatePhaseMetric();
mProviderSessionUid = MetricUtilities.getPackageUid(mContext, mComponentName);
}
diff --git a/services/credentials/java/com/android/server/credentials/RequestSession.java b/services/credentials/java/com/android/server/credentials/RequestSession.java
index c1f35d0f8195..42ec42b170ca 100644
--- a/services/credentials/java/com/android/server/credentials/RequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/RequestSession.java
@@ -36,7 +36,7 @@ import android.util.Log;
import com.android.internal.R;
import com.android.server.credentials.metrics.ApiName;
import com.android.server.credentials.metrics.ApiStatus;
-import com.android.server.credentials.metrics.CandidateProviderMetric;
+import com.android.server.credentials.metrics.CandidatePhaseMetric;
import com.android.server.credentials.metrics.ChosenProviderMetric;
import java.util.ArrayList;
@@ -218,7 +218,7 @@ abstract class RequestSession<T, U> implements CredentialManagerUi.CredentialMan
* @param componentName the componentName to associate with a provider
*/
protected void setChosenMetric(ComponentName componentName) {
- CandidateProviderMetric metric = this.mProviders.get(componentName.flattenToString())
+ CandidatePhaseMetric metric = this.mProviders.get(componentName.flattenToString())
.mCandidateProviderMetric;
mChosenProviderMetric.setChosenUid(metric.getCandidateUid());
mChosenProviderMetric.setFinalFinishTimeNanoseconds(System.nanoTime());
diff --git a/services/credentials/java/com/android/server/credentials/metrics/CandidatePhaseMetric.java b/services/credentials/java/com/android/server/credentials/metrics/CandidatePhaseMetric.java
new file mode 100644
index 000000000000..1c7fb69548fc
--- /dev/null
+++ b/services/credentials/java/com/android/server/credentials/metrics/CandidatePhaseMetric.java
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2023 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.credentials.metrics;
+
+import android.util.Log;
+
+import com.android.server.credentials.MetricUtilities;
+
+/**
+ * The central candidate provider metric object that mimics our defined metric setup.
+ * Some types are redundant across these metric collectors, but that has debug use-cases as
+ * these data-types are available at different moments of the flow (and typically, one can feed
+ * into the next).
+ * TODO(b/270403549) - iterate on this in V3+
+ */
+public class CandidatePhaseMetric {
+
+ private static final String TAG = "CandidateProviderMetric";
+ // Since this will always be the second in the split sequence, this is statically 2
+ private static final int SESSION_ID = 2;
+ // The sequence number of this emit of the API call, default -1, equal for all candidates
+ private int mSequenceId = -1;
+ // Indicates if this provider returned from the query phase, default false
+ private boolean mQueryReturned = false;
+
+ // The candidate provider uid
+ private int mCandidateUid = -1;
+
+ // Raw timestamp in nanoseconds, will be converted to microseconds for logging
+
+ //For reference, the initial log timestamp when the service started running the API call
+ private long mServiceBeganTimeNanoseconds = -1;
+ // The moment when the query phase began
+ private long mStartQueryTimeNanoseconds = -1;
+ // The moment when the query phase ended
+ private long mQueryFinishTimeNanoseconds = -1;
+
+ // The status of this particular provider
+ private int mProviderQueryStatus = -1;
+ // Indicates if an exception was thrown by this provider, false by default
+ private boolean mHasException = false;
+ // Indicates the number of total entries available. We can also locally store the entries, but
+ // cannot emit them in the current split form. TODO(b/271135048) - possibly readjust candidate
+ // entries. Also, it may be okay to remove this and instead aggregate from inner counts.
+ // Defaults to -1
+ private int mNumEntriesTotal = -1;
+ // The count of action entries from this provider, defaults to -1
+ private int mActionEntryCount = -1;
+ // The count of credential entries from this provider, defaults to -1
+ private int mCredentialEntryCount = -1;
+ // The *type-count* of the credential entries, defaults to -1
+ private int mCredentialEntryTypeCount = -1;
+ // The count of remote entries from this provider, defaults to -1
+ private int mRemoteEntryCount = -1;
+ // The count of authentication entries from this provider, defaults to -1
+ private int mAuthenticationEntryCount = -1;
+
+ public CandidatePhaseMetric() {
+ }
+
+ /* ---------- Latencies ---------- */
+
+ /* -- Timestamps -- */
+
+ public void setServiceBeganTimeNanoseconds(long serviceBeganTimeNanoseconds) {
+ this.mServiceBeganTimeNanoseconds = serviceBeganTimeNanoseconds;
+ }
+
+ public void setStartQueryTimeNanoseconds(long startQueryTimeNanoseconds) {
+ this.mStartQueryTimeNanoseconds = startQueryTimeNanoseconds;
+ }
+
+ public void setQueryFinishTimeNanoseconds(long queryFinishTimeNanoseconds) {
+ this.mQueryFinishTimeNanoseconds = queryFinishTimeNanoseconds;
+ }
+
+ public long getServiceBeganTimeNanoseconds() {
+ return this.mServiceBeganTimeNanoseconds;
+ }
+
+ public long getStartQueryTimeNanoseconds() {
+ return this.mStartQueryTimeNanoseconds;
+ }
+
+ public long getQueryFinishTimeNanoseconds() {
+ return this.mQueryFinishTimeNanoseconds;
+ }
+
+ /* -- Actual time delta latencies (for local utility) -- */
+
+ /**
+ * Returns the latency in microseconds for the query phase.
+ */
+ public int getQueryLatencyMicroseconds() {
+ return (int) ((this.getQueryFinishTimeNanoseconds()
+ - this.getStartQueryTimeNanoseconds()) / 1000);
+ }
+
+ /* --- Time Stamp Conversion to Microseconds from Reference --- */
+
+ /**
+ * We collect raw timestamps in nanoseconds for ease of collection. However, given the scope
+ * of our logging timeframe, and size considerations of the metric, we require these to give us
+ * the microsecond timestamps from the start reference point.
+ *
+ * @param specificTimestamp the timestamp to consider, must be greater than the reference
+ * @return the microsecond integer timestamp from service start to query began
+ */
+ public int getTimestampFromReferenceStartMicroseconds(long specificTimestamp) {
+ if (specificTimestamp < this.mServiceBeganTimeNanoseconds) {
+ Log.i(TAG, "The timestamp is before service started, falling back to default int");
+ return MetricUtilities.DEFAULT_INT_32;
+ }
+ return (int) ((specificTimestamp
+ - this.mServiceBeganTimeNanoseconds) / 1000);
+ }
+
+ /* ------------- Provider Query Status ------------ */
+
+ public void setProviderQueryStatus(int providerQueryStatus) {
+ this.mProviderQueryStatus = providerQueryStatus;
+ }
+
+ public int getProviderQueryStatus() {
+ return this.mProviderQueryStatus;
+ }
+
+ /* -------------- Candidate Uid ---------------- */
+
+ public void setCandidateUid(int candidateUid) {
+ this.mCandidateUid = candidateUid;
+ }
+
+ public int getCandidateUid() {
+ return this.mCandidateUid;
+ }
+
+ /* -------------- Session Id ---------------- */
+ public int getSessionId() {
+ return SESSION_ID;
+ }
+
+ /* -------------- Sequence Id ---------------- */
+
+ public void setSequenceId(int sequenceId) {
+ mSequenceId = sequenceId;
+ }
+
+ public int getSequenceId() {
+ return mSequenceId;
+ }
+
+ /* -------------- Query Returned Status ---------------- */
+
+ public void setQueryReturned(boolean queryReturned) {
+ mQueryReturned = queryReturned;
+ }
+
+ public boolean isQueryReturned() {
+ return mQueryReturned;
+ }
+
+ /* -------------- Has Exception Status ---------------- */
+
+ public void setHasException(boolean hasException) {
+ mHasException = hasException;
+ }
+
+ public boolean isHasException() {
+ return mHasException;
+ }
+
+ /* -------------- Number of Entries ---------------- */
+
+ public void setNumEntriesTotal(int numEntriesTotal) {
+ mNumEntriesTotal = numEntriesTotal;
+ }
+
+ public int getNumEntriesTotal() {
+ return mNumEntriesTotal;
+ }
+
+ /* -------------- Count of Action Entries ---------------- */
+
+ public void setActionEntryCount(int actionEntryCount) {
+ mActionEntryCount = actionEntryCount;
+ }
+
+ public int getActionEntryCount() {
+ return mActionEntryCount;
+ }
+
+ /* -------------- Count of Credential Entries ---------------- */
+
+ public void setCredentialEntryCount(int credentialEntryCount) {
+ mCredentialEntryCount = credentialEntryCount;
+ }
+
+ public int getCredentialEntryCount() {
+ return mCredentialEntryCount;
+ }
+
+ /* -------------- Count of Credential Entry Types ---------------- */
+
+ public void setCredentialEntryTypeCount(int credentialEntryTypeCount) {
+ mCredentialEntryTypeCount = credentialEntryTypeCount;
+ }
+
+ public int getCredentialEntryTypeCount() {
+ return mCredentialEntryTypeCount;
+ }
+
+ /* -------------- Count of Remote Entries ---------------- */
+
+ public void setRemoteEntryCount(int remoteEntryCount) {
+ mRemoteEntryCount = remoteEntryCount;
+ }
+
+ public int getRemoteEntryCount() {
+ return mRemoteEntryCount;
+ }
+
+ /* -------------- Count of Authentication Entries ---------------- */
+
+ public void setAuthenticationEntryCount(int authenticationEntryCount) {
+ mAuthenticationEntryCount = authenticationEntryCount;
+ }
+
+ public int getAuthenticationEntryCount() {
+ return mAuthenticationEntryCount;
+ }
+}
diff --git a/services/credentials/java/com/android/server/credentials/metrics/CandidateProviderMetric.java b/services/credentials/java/com/android/server/credentials/metrics/CandidateProviderMetric.java
deleted file mode 100644
index 9f438ecc1146..000000000000
--- a/services/credentials/java/com/android/server/credentials/metrics/CandidateProviderMetric.java
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright (C) 2023 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.credentials.metrics;
-
-/**
- * The central candidate provider metric object that mimics our defined metric setup.
- * Some types are redundant across these metric collectors, but that has debug use-cases as
- * these data-types are available at different moments of the flow (and typically, one can feed
- * into the next).
- * TODO(b/270403549) - iterate on this in V3+
- */
-public class CandidateProviderMetric {
-
- private static final String TAG = "CandidateProviderMetric";
- private int mCandidateUid = -1;
-
- // Raw timestamp in nanoseconds, will be converted to microseconds for logging
-
- private long mStartQueryTimeNanoseconds = -1;
- private long mQueryFinishTimeNanoseconds = -1;
-
- private int mProviderQueryStatus = -1;
-
- public CandidateProviderMetric() {
- }
-
- /* ---------- Latencies ---------- */
-
- public void setStartQueryTimeNanoseconds(long startQueryTimeNanoseconds) {
- this.mStartQueryTimeNanoseconds = startQueryTimeNanoseconds;
- }
-
- public void setQueryFinishTimeNanoseconds(long queryFinishTimeNanoseconds) {
- this.mQueryFinishTimeNanoseconds = queryFinishTimeNanoseconds;
- }
-
- public long getStartQueryTimeNanoseconds() {
- return this.mStartQueryTimeNanoseconds;
- }
-
- public long getQueryFinishTimeNanoseconds() {
- return this.mQueryFinishTimeNanoseconds;
- }
-
- /**
- * Returns the latency in microseconds for the query phase.
- */
- public int getQueryLatencyMicroseconds() {
- return (int) ((this.getQueryFinishTimeNanoseconds()
- - this.getStartQueryTimeNanoseconds()) / 1000);
- }
-
- // TODO (in direct next dependent CL, so this is transient) - add reference timestamp in micro
- // seconds for this too.
-
- /* ------------- Provider Query Status ------------ */
-
- public void setProviderQueryStatus(int providerQueryStatus) {
- this.mProviderQueryStatus = providerQueryStatus;
- }
-
- public int getProviderQueryStatus() {
- return this.mProviderQueryStatus;
- }
-
- /* -------------- Candidate Uid ---------------- */
-
- public void setCandidateUid(int candidateUid) {
- this.mCandidateUid = candidateUid;
- }
-
- public int getCandidateUid() {
- return this.mCandidateUid;
- }
-}
diff --git a/services/credentials/java/com/android/server/credentials/metrics/ChosenProviderMetric.java b/services/credentials/java/com/android/server/credentials/metrics/ChosenProviderMetric.java
index 03102558d21b..1a6109116d38 100644
--- a/services/credentials/java/com/android/server/credentials/metrics/ChosenProviderMetric.java
+++ b/services/credentials/java/com/android/server/credentials/metrics/ChosenProviderMetric.java
@@ -64,7 +64,7 @@ public class ChosenProviderMetric {
/* ---------------- Latencies ------------------ */
- /* ----- Direct Latencies ------- */
+ /* ----- Direct Delta Latencies for Local Utility ------- */
/**
* In order for a chosen provider to be selected, the call must have successfully begun.
@@ -85,7 +85,7 @@ public class ChosenProviderMetric {
* metric.
*
* @param queryPhaseLatencyMicroseconds the millisecond latency for the query phase, typically
- * passed in through the {@link CandidateProviderMetric}
+ * passed in through the {@link CandidatePhaseMetric}
*/
public void setQueryPhaseLatencyMicroseconds(int queryPhaseLatencyMicroseconds) {
mQueryPhaseLatencyMicroseconds = queryPhaseLatencyMicroseconds;
@@ -106,7 +106,7 @@ public class ChosenProviderMetric {
/**
* Returns the full provider (invocation to response) latency in microseconds. Expects the
- * start time to be provided, such as from {@link CandidateProviderMetric}.
+ * start time to be provided, such as from {@link CandidatePhaseMetric}.
*/
public int getEntireProviderLatencyMicroseconds() {
return (int) ((this.mFinalFinishTimeNanoseconds
@@ -172,7 +172,7 @@ public class ChosenProviderMetric {
return mFinalFinishTimeNanoseconds;
}
- /* --- Time Stamp Conversion to Microseconds --- */
+ /* --- Time Stamp Conversion to Microseconds from Reference Point --- */
/**
* We collect raw timestamps in nanoseconds for ease of collection. However, given the scope
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index a4e563b21bec..a220ad151050 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -11219,7 +11219,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
} else {
Objects.requireNonNull(admin, "ComponentName is null");
Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller)
- || (parent && isProfileOwnerOfOrganizationOwnedDevice(caller)));
+ || isProfileOwnerOfOrganizationOwnedDevice(caller));
}
if (parent) {
userId = getProfileParentId(mInjector.userHandleGetCallingUserId());
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java
index 9501b9604fa0..7cd55f326366 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java
@@ -65,6 +65,7 @@ import com.android.server.wm.WindowManagerInternal;
import org.junit.After;
import org.junit.Before;
+import org.junit.BeforeClass;
import org.mockito.Mock;
import org.mockito.MockitoSession;
import org.mockito.quality.Strictness;
@@ -121,6 +122,14 @@ public class InputMethodManagerServiceTestBase {
protected InputMethodManagerService mInputMethodManagerService;
protected ServiceThread mServiceThread;
+ @BeforeClass
+ public static void setupClass() {
+ // Make sure DeviceConfig's lazy-initialized ContentProvider gets
+ // a real instance before we stub out all system services below.
+ // TODO(b/272229177): remove dependency on real ContentProvider
+ new InputMethodDeviceConfigs().destroy();
+ }
+
@Before
public void setUp() throws RemoteException {
mMockingSession =
@@ -231,6 +240,8 @@ public class InputMethodManagerServiceTestBase {
@After
public void tearDown() {
+ mInputMethodManagerService.mInputMethodDeviceConfigs.destroy();
+
if (mServiceThread != null) {
mServiceThread.quitSafely();
}
diff --git a/services/tests/servicestests/src/com/android/server/am/DropboxRateLimiterTest.java b/services/tests/servicestests/src/com/android/server/am/DropboxRateLimiterTest.java
index e68a8a0f3af8..01563e27a787 100644
--- a/services/tests/servicestests/src/com/android/server/am/DropboxRateLimiterTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/DropboxRateLimiterTest.java
@@ -106,6 +106,64 @@ public class DropboxRateLimiterTest {
mRateLimiter.shouldRateLimit("tag", "p").droppedCountSinceRateLimitActivated());
}
+ @Test
+ public void testStrictRepeatedLimiting() throws Exception {
+ // The first 6 entries should not be rate limited.
+ assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+ assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+ assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+ assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+ assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+ assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+ // The 7th entry of the same process should be rate limited.
+ assertTrue(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+
+ // After 11 minutes there should be nothing left in the buffer and the same type of entry
+ // should not get rate limited anymore.
+ mClock.setOffsetMillis(11 * 60 * 1000);
+ assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+ // The first 6 entries should not be rate limited again.
+ assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+ assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+ assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+ assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+ assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+
+ // The 7th entry of the same process should be rate limited.
+ assertTrue(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+
+ // After 11 more minutes there should be nothing left in the buffer and the same type of
+ // entry should not get rate limited anymore.
+ mClock.setOffsetMillis(22 * 60 * 1000);
+ assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+
+ // Repeated crashes after the last reset being rate limited should be restricted faster.
+ assertTrue(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+
+ // We now need to wait 61 minutes for the buffer should be empty again.
+ mClock.setOffsetMillis(83 * 60 * 1000);
+ assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+
+ // After yet another 61 minutes, this time without triggering rate limiting, the strict
+ // limiting should be turnd off.
+ mClock.setOffsetMillis(144 * 60 * 1000);
+ assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+ assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+
+ // As rate limiting was not triggered in the last reset, after another 11 minutes the
+ // buffer should still act as normal.
+ mClock.setOffsetMillis(155 * 60 * 1000);
+ // The first 6 entries should not be rate limited.
+ assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+ assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+ assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+ assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+ assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+ assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+ // The 7th entry of the same process should be rate limited.
+ assertTrue(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+ }
+
private static class TestClock implements DropboxRateLimiter.Clock {
long mOffsetMillis = 0L;
diff --git a/services/tests/servicestests/src/com/android/server/credentials/CredentialDescriptionRegistryTest.java b/services/tests/servicestests/src/com/android/server/credentials/CredentialDescriptionRegistryTest.java
index b7085f152fa0..169210f627b8 100644
--- a/services/tests/servicestests/src/com/android/server/credentials/CredentialDescriptionRegistryTest.java
+++ b/services/tests/servicestests/src/com/android/server/credentials/CredentialDescriptionRegistryTest.java
@@ -52,8 +52,10 @@ public class CredentialDescriptionRegistryTest {
private static final String CALLING_PACKAGE_NAME_2 = "com.credman.app2";
private static final String MDOC_CREDENTIAL_TYPE = "MDOC";
private static final String PASSKEY_CREDENTIAL_TYPE = "PASSKEY";
- private static final String FLATTENED_REQUEST = "FLATTENED_REQ";
- private static final String FLATTENED_REQUEST_2 = "FLATTENED_REQ_2";
+ private static final String FLATTENED_REGISTRY =
+ "FLATTENED_REQ;FLATTENED_REQ123;FLATTENED_REQa";
+ private static final String FLATTENED_REGISTRY_2 = "FLATTENED_REQ_2";
+ private static final String FLATTENED_REQUEST = "FLATTENED_REQ;FLATTENED_REQ123";
private CredentialDescriptionRegistry mCredentialDescriptionRegistry;
private CredentialEntry mEntry;
@@ -104,12 +106,12 @@ public class CredentialDescriptionRegistryTest {
@Test
public void testEvictProvider_existingProviders_succeeds() {
final CredentialDescription credentialDescription =
- new CredentialDescription(MDOC_CREDENTIAL_TYPE, FLATTENED_REQUEST,
+ new CredentialDescription(MDOC_CREDENTIAL_TYPE, FLATTENED_REGISTRY,
Collections.emptyList());
final RegisterCredentialDescriptionRequest registerCredentialDescriptionRequest =
new RegisterCredentialDescriptionRequest(credentialDescription);
final CredentialDescription credentialDescription2 =
- new CredentialDescription(MDOC_CREDENTIAL_TYPE, FLATTENED_REQUEST_2,
+ new CredentialDescription(MDOC_CREDENTIAL_TYPE, FLATTENED_REGISTRY_2,
Collections.emptyList());
final RegisterCredentialDescriptionRequest registerCredentialDescriptionRequest2 =
new RegisterCredentialDescriptionRequest(credentialDescription2);
@@ -130,12 +132,12 @@ public class CredentialDescriptionRegistryTest {
@Test
public void testGetMatchingProviders_existingProviders_succeeds() {
final CredentialDescription credentialDescription =
- new CredentialDescription(MDOC_CREDENTIAL_TYPE, FLATTENED_REQUEST,
+ new CredentialDescription(MDOC_CREDENTIAL_TYPE, FLATTENED_REGISTRY,
Collections.emptyList());
final RegisterCredentialDescriptionRequest registerCredentialDescriptionRequest =
new RegisterCredentialDescriptionRequest(credentialDescription);
final CredentialDescription credentialDescription2 =
- new CredentialDescription(MDOC_CREDENTIAL_TYPE, FLATTENED_REQUEST,
+ new CredentialDescription(MDOC_CREDENTIAL_TYPE, FLATTENED_REGISTRY,
Collections.emptyList());
final RegisterCredentialDescriptionRequest registerCredentialDescriptionRequest2 =
new RegisterCredentialDescriptionRequest(credentialDescription2);
@@ -171,11 +173,11 @@ public class CredentialDescriptionRegistryTest {
public void testExecuteRegisterRequest_existingProviders_filterSucceeds() {
final CredentialDescription credentialDescription =
new CredentialDescription(MDOC_CREDENTIAL_TYPE,
- FLATTENED_REQUEST,
+ FLATTENED_REGISTRY,
List.of(mEntry, mEntry2));
final CredentialDescription credentialDescription2 =
new CredentialDescription(PASSKEY_CREDENTIAL_TYPE,
- FLATTENED_REQUEST_2,
+ FLATTENED_REGISTRY_2,
List.of(mEntry3));
final RegisterCredentialDescriptionRequest registerCredentialDescriptionRequest =
new RegisterCredentialDescriptionRequest(Set.of(credentialDescription,
diff --git a/services/tests/servicestests/src/com/android/server/credentials/ProviderRegistryGetSessionTest.java b/services/tests/servicestests/src/com/android/server/credentials/ProviderRegistryGetSessionTest.java
index 3d52ac558605..4c8e70ae5109 100644
--- a/services/tests/servicestests/src/com/android/server/credentials/ProviderRegistryGetSessionTest.java
+++ b/services/tests/servicestests/src/com/android/server/credentials/ProviderRegistryGetSessionTest.java
@@ -36,7 +36,6 @@ import android.content.pm.SigningDetails;
import android.content.pm.SigningInfo;
import android.credentials.Credential;
import android.credentials.CredentialOption;
-import android.credentials.CredentialProviderInfo;
import android.credentials.GetCredentialException;
import android.credentials.GetCredentialResponse;
import android.credentials.ui.GetCredentialProviderData;
@@ -45,7 +44,6 @@ import android.net.Uri;
import android.os.Bundle;
import android.service.credentials.CallingAppInfo;
import android.service.credentials.CredentialEntry;
-import android.service.credentials.CredentialProviderInfoFactory;
import android.service.credentials.CredentialProviderService;
import android.service.credentials.GetCredentialRequest;
@@ -61,7 +59,6 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.security.cert.CertificateException;
-import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -89,7 +86,6 @@ public class ProviderRegistryGetSessionTest {
@Mock private GetRequestSession mGetRequestSession;
private CredentialOption mGetCredentialOption;
@Mock private ServiceInfo mServiceInfo;
- private CredentialProviderInfo mCredentialProviderInfo;
private CallingAppInfo mCallingAppInfo;
@Mock private CredentialDescriptionRegistry mCredentialDescriptionRegistry;
private Bundle mRetrievalData;
@@ -111,12 +107,6 @@ public class ProviderRegistryGetSessionTest {
mGetCredentialOption = new CredentialOption(CREDENTIAL_TYPE, mRetrievalData,
new Bundle(), false);
when(mServiceInfo.getComponentName()).thenReturn(CREDENTIAL_PROVIDER_COMPONENT);
- mCredentialProviderInfo = CredentialProviderInfoFactory
- .createForTests(mServiceInfo,
- /* overrideLabel= */ "test",
- /* isSystemProvider= */ false,
- /* isEnabled= */ true,
- /* capabilities= */ Collections.EMPTY_LIST);
CredentialDescriptionRegistry.setSession(USER_ID_1, mCredentialDescriptionRegistry);
mResponse = new HashSet<>();
mSlice = createSlice();
@@ -130,7 +120,7 @@ public class ProviderRegistryGetSessionTest {
when(mCredentialDescriptionRegistry.getFilteredResultForProvider(anyString(), anyString()))
.thenReturn(mResponse);
mProviderRegistryGetSession = ProviderRegistryGetSession
- .createNewSession(context, USER_ID_1, mGetRequestSession, mCredentialProviderInfo,
+ .createNewSession(context, USER_ID_1, mGetRequestSession,
mCallingAppInfo,
CALLING_PACKAGE_NAME,
mGetCredentialOption);
diff --git a/services/tests/servicestests/src/com/android/server/display/mode/SkinThermalStatusObserverTest.java b/services/tests/servicestests/src/com/android/server/display/mode/SkinThermalStatusObserverTest.java
index dd0cd965b8a0..fd1889ca0982 100644
--- a/services/tests/servicestests/src/com/android/server/display/mode/SkinThermalStatusObserverTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/mode/SkinThermalStatusObserverTest.java
@@ -18,6 +18,7 @@ package com.android.server.display.mode;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import android.hardware.display.DisplayManager;
@@ -49,6 +50,7 @@ public class SkinThermalStatusObserverTest {
private static final float FLOAT_TOLERANCE = 0.01f;
private static final int DISPLAY_ID = 1;
private static final int DISPLAY_ID_OTHER = 2;
+ private static final int DISPLAY_ID_ADDED = 3;
SkinThermalStatusObserver mObserver;
@@ -167,6 +169,42 @@ public class SkinThermalStatusObserverTest {
assertEquals(120, vote.refreshRateRanges.render.max, FLOAT_TOLERANCE);
}
+ @Test
+ public void testDisplayAdded() {
+ // GIVEN 2 displays with no thermalThrottling config AND temperature level CRITICAL
+ mObserver.observe();
+ mObserver.notifyThrottling(createTemperature(Temperature.THROTTLING_CRITICAL));
+ // WHEN new display is added
+ mObserver.onDisplayAdded(DISPLAY_ID_ADDED);
+ mHandler.flush();
+ // THEN 3rd vote is added to storage with (0,60) render refresh rate(default behaviour)
+ assertEquals(3, mStorage.mVoteRegistry.size());
+
+ SparseArray<DisplayModeDirector.Vote> displayVotes = mStorage.mVoteRegistry.get(
+ DISPLAY_ID_ADDED);
+ assertEquals(1, displayVotes.size());
+
+ DisplayModeDirector.Vote vote = displayVotes.get(
+ DisplayModeDirector.Vote.PRIORITY_SKIN_TEMPERATURE);
+ assertEquals(0, vote.refreshRateRanges.render.min, FLOAT_TOLERANCE);
+ assertEquals(60, vote.refreshRateRanges.render.max, FLOAT_TOLERANCE);
+ }
+
+ @Test
+ public void testDisplayAddedAndThenImmediatelyRemoved() {
+ // GIVEN 2 displays with no thermalThrottling config AND temperature level CRITICAL
+ mObserver.observe();
+ mObserver.notifyThrottling(createTemperature(Temperature.THROTTLING_CRITICAL));
+ // WHEN new display is added and immediately removed
+ mObserver.onDisplayAdded(DISPLAY_ID_ADDED);
+ mObserver.onDisplayRemoved(DISPLAY_ID_ADDED);
+ mHandler.flush();
+ // THEN there are 2 votes in registry
+ assertEquals(2, mStorage.mVoteRegistry.size());
+ assertNotNull(mStorage.mVoteRegistry.get(DISPLAY_ID));
+ assertNotNull(mStorage.mVoteRegistry.get(DISPLAY_ID_OTHER));
+ }
+
private static Temperature createTemperature(@Temperature.ThrottlingStatus int status) {
return new Temperature(40.0f, Temperature.TYPE_SKIN, "test_temp", status);
}
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 354420f46c2f..41a9504fba97 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -416,7 +416,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
// Shell permisssions will override permissions of our app, so add all necessary permissions
// for this test here:
InstrumentationRegistry.getInstrumentation().getUiAutomation().adoptShellPermissionIdentity(
- "android.permission.WRITE_DEVICE_CONFIG",
+ "android.permission.ALLOWLISTED_WRITE_DEVICE_CONFIG",
"android.permission.READ_DEVICE_CONFIG",
"android.permission.READ_CONTACTS");
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 49f215a83c2b..6147633f624a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -2377,7 +2377,7 @@ public class ActivityRecordTests extends WindowTestsBase {
.setScreenOrientation(SCREEN_ORIENTATION_BEHIND)
.build();
final int topOrientation = activityTop.getRequestedConfigurationOrientation();
- assertEquals(SCREEN_ORIENTATION_PORTRAIT, topOrientation);
+ assertEquals(ORIENTATION_PORTRAIT, topOrientation);
}
private void verifyProcessInfoUpdate(ActivityRecord activity, State state,
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index 65c71255cd2f..753cc623cf25 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -384,7 +384,25 @@ public class SizeCompatTests extends WindowTestsBase {
}
@Test
- public void testTranslucentActivitiesDontGoInSizeCompactMode() {
+ public void testNotApplyStrategyToTranslucentActivitiesOverEmbeddedActivities() {
+ mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true);
+ setUpDisplaySizeWithApp(2000, 1000);
+ mActivity.info.screenOrientation = SCREEN_ORIENTATION_PORTRAIT;
+ mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ // Mock the activity as embedded without additional TaskFragment layer in the task for
+ // simplicity.
+ doReturn(true).when(mActivity).isEmbedded();
+ // Translucent Activity
+ final ActivityRecord translucentActivity = new ActivityBuilder(mAtm).build();
+ doReturn(false).when(translucentActivity).matchParentBounds();
+ doReturn(false).when(translucentActivity).fillsParent();
+ mTask.addChild(translucentActivity);
+ // Check the strategy has not being applied
+ assertFalse(translucentActivity.mLetterboxUiController.hasInheritedLetterboxBehavior());
+ }
+
+ @Test
+ public void testTranslucentActivitiesDontGoInSizeCompatMode() {
mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true);
setUpDisplaySizeWithApp(2800, 1400);
mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
@@ -2472,11 +2490,11 @@ public class SizeCompatTests extends WindowTestsBase {
assertFalse(mActivity.inSizeCompatMode());
mActivity.setRequestedOrientation(SCREEN_ORIENTATION_UNSPECIFIED);
-
- assertTrue(mActivity.inSizeCompatMode());
- // We should remember the original orientation.
+ // Activity is not in size compat mode because the orientation change request came from the
+ // app itself
+ assertFalse(mActivity.inSizeCompatMode());
assertEquals(mActivity.getResolvedOverrideConfiguration().orientation,
- Configuration.ORIENTATION_PORTRAIT);
+ Configuration.ORIENTATION_UNDEFINED);
}
@Test
@@ -3051,6 +3069,25 @@ public class SizeCompatTests extends WindowTestsBase {
}
@Test
+ public void testAppRequestsOrientationChange_notInSizeCompat() {
+ setUpDisplaySizeWithApp(2200, 1800);
+ mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+
+ prepareUnresizable(mActivity, SCREEN_ORIENTATION_LANDSCAPE);
+
+ mActivity.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT);
+
+ // Activity is not in size compat mode because the orientation change request came from the
+ // app itself
+ assertFalse(mActivity.inSizeCompatMode());
+
+ rotateDisplay(mActivity.mDisplayContent, ROTATION_270);
+ // Activity should go into size compat mode now because the orientation change came from the
+ // system (device rotation)
+ assertTrue(mActivity.inSizeCompatMode());
+ }
+
+ @Test
public void testLetterboxDetailsForStatusBar_noLetterbox() {
setUpDisplaySizeWithApp(2800, 1000);
addStatusBar(mActivity.mDisplayContent);
diff --git a/services/tests/wmtests/src/com/android/server/wm/SurfaceSyncGroupTest.java b/services/tests/wmtests/src/com/android/server/wm/SurfaceSyncGroupTest.java
index c5387272f0bf..e30206ee0a64 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SurfaceSyncGroupTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SurfaceSyncGroupTest.java
@@ -41,6 +41,7 @@ import java.util.concurrent.TimeUnit;
@Presubmit
public class SurfaceSyncGroupTest {
private static final String TAG = "SurfaceSyncGroupTest";
+ private static final int TIMEOUT_MS = 100;
private final Executor mExecutor = Runnable::run;
@@ -86,7 +87,7 @@ public class SurfaceSyncGroupTest {
syncTarget2.markSyncReady();
- finishedLatch.await(5, TimeUnit.SECONDS);
+ finishedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS);
assertEquals(0, finishedLatch.getCount());
}
@@ -124,13 +125,13 @@ public class SurfaceSyncGroupTest {
syncTarget1.markSyncReady();
- finishedLatch1.await(5, TimeUnit.SECONDS);
+ finishedLatch1.await(TIMEOUT_MS, TimeUnit.MILLISECONDS);
assertEquals(0, finishedLatch1.getCount());
assertNotEquals(0, finishedLatch2.getCount());
syncTarget2.markSyncReady();
- finishedLatch2.await(5, TimeUnit.SECONDS);
+ finishedLatch2.await(TIMEOUT_MS, TimeUnit.MILLISECONDS);
assertEquals(0, finishedLatch2.getCount());
}
@@ -156,17 +157,17 @@ public class SurfaceSyncGroupTest {
// Finish syncTarget2 first to test that the syncGroup is not complete until the merged sync
// is also done.
syncTarget2.markSyncReady();
- finishedLatch2.await(1, TimeUnit.SECONDS);
+ finishedLatch2.await(TIMEOUT_MS, TimeUnit.MILLISECONDS);
// Sync did not complete yet
assertNotEquals(0, finishedLatch2.getCount());
syncTarget1.markSyncReady();
// The first sync will still get a callback when it's sync requirements are done.
- finishedLatch1.await(5, TimeUnit.SECONDS);
+ finishedLatch1.await(TIMEOUT_MS, TimeUnit.MILLISECONDS);
assertEquals(0, finishedLatch1.getCount());
- finishedLatch2.await(5, TimeUnit.SECONDS);
+ finishedLatch2.await(TIMEOUT_MS, TimeUnit.MILLISECONDS);
assertEquals(0, finishedLatch2.getCount());
}
@@ -189,7 +190,7 @@ public class SurfaceSyncGroupTest {
syncTarget1.markSyncReady();
// The first sync will still get a callback when it's sync requirements are done.
- finishedLatch1.await(5, TimeUnit.SECONDS);
+ finishedLatch1.await(TIMEOUT_MS, TimeUnit.MILLISECONDS);
assertEquals(0, finishedLatch1.getCount());
syncGroup2.add(syncGroup1, null /* runnable */);
@@ -198,7 +199,7 @@ public class SurfaceSyncGroupTest {
// Verify that the second sync will receive complete since the merged sync was already
// completed before the merge.
- finishedLatch2.await(5, TimeUnit.SECONDS);
+ finishedLatch2.await(TIMEOUT_MS, TimeUnit.MILLISECONDS);
assertEquals(0, finishedLatch2.getCount());
}
@@ -232,8 +233,8 @@ public class SurfaceSyncGroupTest {
syncTarget3.markSyncReady();
// Neither SyncGroup will be ready.
- finishedLatch1.await(1, TimeUnit.SECONDS);
- finishedLatch2.await(1, TimeUnit.SECONDS);
+ finishedLatch1.await(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ finishedLatch2.await(TIMEOUT_MS, TimeUnit.MILLISECONDS);
assertEquals(1, finishedLatch1.getCount());
assertEquals(1, finishedLatch2.getCount());
@@ -241,8 +242,8 @@ public class SurfaceSyncGroupTest {
syncTarget2.markSyncReady();
// Both sync groups should be ready after target2 completed.
- finishedLatch1.await(5, TimeUnit.SECONDS);
- finishedLatch2.await(5, TimeUnit.SECONDS);
+ finishedLatch1.await(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ finishedLatch2.await(TIMEOUT_MS, TimeUnit.MILLISECONDS);
assertEquals(0, finishedLatch1.getCount());
assertEquals(0, finishedLatch2.getCount());
}
@@ -275,8 +276,8 @@ public class SurfaceSyncGroupTest {
syncTarget1.markSyncReady();
// Only SyncGroup1 will be ready, but SyncGroup2 still needs its own targets to be ready.
- finishedLatch1.await(1, TimeUnit.SECONDS);
- finishedLatch2.await(1, TimeUnit.SECONDS);
+ finishedLatch1.await(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ finishedLatch2.await(TIMEOUT_MS, TimeUnit.MILLISECONDS);
assertEquals(0, finishedLatch1.getCount());
assertEquals(1, finishedLatch2.getCount());
@@ -284,7 +285,7 @@ public class SurfaceSyncGroupTest {
syncTarget3.markSyncReady();
// SyncGroup2 is finished after target3 completed.
- finishedLatch2.await(1, TimeUnit.SECONDS);
+ finishedLatch2.await(TIMEOUT_MS, TimeUnit.MILLISECONDS);
assertEquals(0, finishedLatch2.getCount());
}
@@ -357,6 +358,27 @@ public class SurfaceSyncGroupTest {
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
+
+ assertEquals(0, finishedLatch.getCount());
+ }
+
+ public void testSurfaceSyncGroupTimeout() throws InterruptedException {
+ final CountDownLatch finishedLatch = new CountDownLatch(1);
+ SurfaceSyncGroup syncGroup = new SurfaceSyncGroup(TAG);
+ syncGroup.addSyncCompleteCallback(mExecutor, finishedLatch::countDown);
+ SurfaceSyncGroup syncTarget1 = new SurfaceSyncGroup("FakeSyncTarget1");
+ SurfaceSyncGroup syncTarget2 = new SurfaceSyncGroup("FakeSyncTarget2");
+
+ syncGroup.add(syncTarget1, null /* runnable */);
+ syncGroup.add(syncTarget2, null /* runnable */);
+ syncGroup.markSyncReady();
+
+ syncTarget1.markSyncReady();
+ assertNotEquals(0, finishedLatch.getCount());
+
+ // Never finish syncTarget2 so it forces the timeout. Timeout is 1 second so wait a little
+ // over 1 second to make sure it completes.
+ finishedLatch.await(1100, TimeUnit.MILLISECONDS);
assertEquals(0, finishedLatch.getCount());
}
}
diff --git a/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java b/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java
index cbac34f50a96..0325ba648af6 100644
--- a/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java
+++ b/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java
@@ -31,6 +31,7 @@ import android.os.RemoteException;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
+import android.provider.DeviceConfig;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyFrameworkInitializer;
import android.telephony.TelephonyManager;
@@ -258,6 +259,9 @@ public final class TelephonyUtils {
*/
public static void showErrorIfSubscriptionAssociatedWithManagedProfile(Context context,
int subId) {
+ if (!isSwitchToManagedProfileDialogFlagEnabled()) {
+ return;
+ }
final long token = Binder.clearCallingIdentity();
try {
SubscriptionManager subscriptionManager = context.getSystemService(
@@ -286,6 +290,11 @@ public final class TelephonyUtils {
}
}
+ public static boolean isSwitchToManagedProfileDialogFlagEnabled() {
+ return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_DEVICE_POLICY_MANAGER,
+ "enable_switch_to_managed_profile_dialog", false);
+ }
+
/**
* Check if the process with given uid is foreground.
*
diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java b/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java
index 604641520252..15fd817ba73b 100644
--- a/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java
+++ b/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java
@@ -161,9 +161,9 @@ public class SharedConnectivityManager {
Resources resources = context.getResources();
try {
String servicePackageName = resources.getString(
- R.string.shared_connectivity_service_package);
+ R.string.config_sharedConnectivityServicePackage);
String serviceIntentAction = resources.getString(
- R.string.shared_connectivity_service_intent_action);
+ R.string.config_sharedConnectivityServiceIntentAction);
return new SharedConnectivityManager(context, servicePackageName, serviceIntentAction);
} catch (Resources.NotFoundException e) {
Log.e(TAG, "To support shared connectivity service on this device, the service's"
diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityService.java b/wifi/java/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityService.java
index c53da9c15d4d..57108e4aa227 100644
--- a/wifi/java/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityService.java
+++ b/wifi/java/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityService.java
@@ -25,6 +25,7 @@ import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.app.Service;
+import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.wifi.sharedconnectivity.app.HotspotNetwork;
@@ -40,8 +41,11 @@ import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.util.Log;
+import com.android.internal.R;
+
import java.util.Collections;
import java.util.List;
+import java.util.Objects;
/**
@@ -382,6 +386,30 @@ public abstract class SharedConnectivityService extends Service {
}
/**
+ * System and settings UI support on the device for instant tether.
+ * @return True if the UI can display Instant Tether network data. False otherwise.
+ */
+ public static boolean areHotspotNetworksEnabledForService(@NonNull Context context) {
+ String servicePackage = context.getResources()
+ .getString(R.string.config_sharedConnectivityServicePackage);
+ return Objects.equals(context.getPackageName(), servicePackage)
+ && context.getResources()
+ .getBoolean(R.bool.config_hotspotNetworksEnabledForService);
+ }
+
+ /**
+ * System and settings UI support on the device for known networks.
+ * @return True if the UI can display known networks data. False otherwise.
+ */
+ public static boolean areKnownNetworksEnabledForService(@NonNull Context context) {
+ String servicePackage = context.getResources()
+ .getString(R.string.config_sharedConnectivityServicePackage);
+ return Objects.equals(context.getPackageName(), servicePackage)
+ && context.getResources()
+ .getBoolean(R.bool.config_knownNetworksEnabledForService);
+ }
+
+ /**
* Implementing application should implement this method.
*
* Implementation should initiate a connection to the Hotspot Network indicated.
diff --git a/wifi/tests/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityServiceTest.java b/wifi/tests/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityServiceTest.java
index 19effe5d6f14..b8b6b767eed3 100644
--- a/wifi/tests/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityServiceTest.java
+++ b/wifi/tests/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityServiceTest.java
@@ -26,10 +26,12 @@ import static android.net.wifi.sharedconnectivity.app.NetworkProviderInfo.DEVICE
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.Intent;
+import android.content.res.Resources;
import android.net.wifi.sharedconnectivity.app.HotspotNetwork;
import android.net.wifi.sharedconnectivity.app.HotspotNetworkConnectionStatus;
import android.net.wifi.sharedconnectivity.app.KnownNetwork;
@@ -86,6 +88,9 @@ public class SharedConnectivityServiceTest {
@Mock
Context mContext;
+ @Mock
+ Resources mResources;
+
static class FakeSharedConnectivityService extends SharedConnectivityService {
public void attachBaseContext(Context context) {
super.attachBaseContext(context);
@@ -180,6 +185,48 @@ public class SharedConnectivityServiceTest {
.isEqualTo(KNOWN_NETWORK_CONNECTION_STATUS);
}
+ @Test
+ public void areHotspotNetworksEnabledForService() {
+ when(mContext.getResources()).thenReturn(mResources);
+ when(mContext.getPackageName()).thenReturn("package");
+ when(mResources.getString(anyInt())).thenReturn("package");
+ when(mResources.getBoolean(anyInt())).thenReturn(true);
+
+ assertThat(SharedConnectivityService.areHotspotNetworksEnabledForService(mContext))
+ .isTrue();
+ }
+
+ @Test
+ public void areHotspotNetworksEnabledForService_notSamePackage_shouldReturnFalse() {
+ when(mContext.getResources()).thenReturn(mResources);
+ when(mContext.getPackageName()).thenReturn("package");
+ when(mResources.getString(anyInt())).thenReturn("other_package");
+ when(mResources.getBoolean(anyInt())).thenReturn(true);
+
+ assertThat(SharedConnectivityService.areHotspotNetworksEnabledForService(mContext))
+ .isFalse();
+ }
+
+ @Test
+ public void areKnownNetworksEnabledForService() {
+ when(mContext.getResources()).thenReturn(mResources);
+ when(mContext.getPackageName()).thenReturn("package");
+ when(mResources.getString(anyInt())).thenReturn("package");
+ when(mResources.getBoolean(anyInt())).thenReturn(true);
+
+ assertThat(SharedConnectivityService.areKnownNetworksEnabledForService(mContext)).isTrue();
+ }
+
+ @Test
+ public void areKnownNetworksEnabledForService_notSamePackage_shouldReturnFalse() {
+ when(mContext.getResources()).thenReturn(mResources);
+ when(mContext.getPackageName()).thenReturn("package");
+ when(mResources.getString(anyInt())).thenReturn("other_package");
+ when(mResources.getBoolean(anyInt())).thenReturn(true);
+
+ assertThat(SharedConnectivityService.areKnownNetworksEnabledForService(mContext)).isFalse();
+ }
+
private SharedConnectivityService createService() {
FakeSharedConnectivityService service = new FakeSharedConnectivityService();
service.attachBaseContext(mContext);