summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/idle/DeviceIdlenessTracker.java120
-rw-r--r--apex/media/framework/java/android/media/MediaParser.java52
-rw-r--r--cmds/statsd/Android.bp2
-rw-r--r--cmds/statsd/src/StatsService.cpp16
-rw-r--r--cmds/statsd/src/StatsService.h20
-rw-r--r--cmds/statsd/src/atoms.proto81
-rw-r--r--cmds/statsd/src/utils/NamedLatch.cpp48
-rw-r--r--cmds/statsd/src/utils/NamedLatch.h58
-rw-r--r--cmds/statsd/tests/utils/NamedLatch_test.cpp96
-rw-r--r--core/java/android/app/AppOpsManager.java38
-rw-r--r--core/java/android/app/TEST_MAPPING4
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java22
-rw-r--r--core/java/android/app/admin/IDevicePolicyManager.aidl1
-rw-r--r--core/java/android/app/admin/PasswordMetrics.java2
-rw-r--r--core/java/android/content/pm/PackageManager.java17
-rw-r--r--core/java/android/hardware/camera2/legacy/LegacyFaceDetectMapper.java6
-rw-r--r--core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java12
-rw-r--r--core/java/android/hardware/camera2/legacy/LegacyRequestMapper.java3
-rw-r--r--core/java/android/hardware/camera2/legacy/LegacyResultMapper.java13
-rw-r--r--core/java/android/hardware/camera2/legacy/ParameterUtils.java101
-rw-r--r--core/java/android/net/UrlQuerySanitizer.java2
-rw-r--r--core/java/android/os/UserManager.java30
-rw-r--r--core/java/android/os/incremental/V4Signature.java28
-rw-r--r--core/java/android/service/autofill/InlinePresentation.java15
-rw-r--r--core/java/android/service/dreams/DreamService.java44
-rw-r--r--core/java/android/service/notification/StatusBarNotification.java12
-rw-r--r--core/java/android/speech/RecognizerIntent.java4
-rw-r--r--core/java/android/view/SurfaceControl.java28
-rw-r--r--core/java/android/view/WindowInsetsAnimationController.java5
-rw-r--r--core/java/android/view/autofill/AutofillManager.java11
-rw-r--r--core/java/android/view/inline/InlineContentView.java208
-rw-r--r--core/java/android/view/inline/InlinePresentationSpec.java347
-rw-r--r--core/java/android/view/inputmethod/InlineSuggestionInfo.java16
-rw-r--r--core/java/android/view/inputmethod/InlineSuggestionsRequest.java30
-rw-r--r--core/java/com/android/internal/accessibility/AccessibilityShortcutController.java14
-rw-r--r--core/java/com/android/internal/app/SystemUserHomeActivity.java17
-rw-r--r--core/java/com/android/internal/app/procstats/DumpUtils.java88
-rw-r--r--core/proto/android/server/jobscheduler.proto1
-rw-r--r--core/proto/android/server/windowmanagerservice.proto2
-rw-r--r--core/proto/android/service/procstats_enum.proto24
-rw-r--r--core/res/res/layout/system_user_home.xml44
-rw-r--r--core/res/res/values/symbols.xml1
-rw-r--r--core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java6
-rw-r--r--graphics/java/android/graphics/ColorSpace.java59
-rw-r--r--location/java/android/location/LocationManager.java5
-rw-r--r--media/java/android/media/MediaRouter2.java51
-rw-r--r--media/tests/AudioPolicyTest/Android.bp17
-rw-r--r--media/tests/AudioPolicyTest/AndroidManifest.xml45
-rw-r--r--media/tests/AudioPolicyTest/AndroidTest.xml27
-rw-r--r--media/tests/AudioPolicyTest/res/layout/audiopolicytest.xml21
-rw-r--r--media/tests/AudioPolicyTest/res/values/strings.xml5
-rw-r--r--media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioManagerTest.java328
-rw-r--r--media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioPolicyTest.java (renamed from core/java/android/view/inline/InlinePresentationSpec.aidl)29
-rw-r--r--media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioProductStrategyTest.java213
-rw-r--r--media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioVolumeGroupCallbackHelper.java69
-rw-r--r--media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioVolumeGroupChangeHandlerTest.java180
-rw-r--r--media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioVolumeGroupTest.java131
-rw-r--r--media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioVolumesTestBase.java146
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java7
-rw-r--r--packages/CarSystemUI/tests/src/com/android/systemui/car/keyguard/CarKeyguardViewControllerTest.java209
-rw-r--r--packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java19
-rw-r--r--packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java22
-rw-r--r--packages/SystemUI/res/anim/control_state_list_animator.xml4
-rw-r--r--packages/SystemUI/res/drawable/control_background.xml3
-rw-r--r--packages/SystemUI/res/interpolator/control_state.xml22
-rw-r--r--packages/SystemUI/res/layout/photo_preview_overlay.xml19
-rw-r--r--packages/SystemUI/res/layout/status_bar_expanded.xml2
-rw-r--r--packages/SystemUI/res/values/dimens.xml1
-rw-r--r--packages/SystemUI/res/values/strings.xml6
-rw-r--r--packages/SystemUI/res/values/styles.xml2
-rwxr-xr-xpackages/SystemUI/scripts/update_statsd_lib.sh35
-rw-r--r--packages/SystemUI/src/com/android/systemui/Interpolators.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/ScreenDecorations.java34
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java58
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleExperimentConfig.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinator.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt65
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ui/ToggleBehavior.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt89
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ui/TouchBehavior.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/SeekBarViewModel.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java20
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSPanel.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QuickQSMediaPlayer.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java22
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/magnetictarget/MagnetizedObject.kt34
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/SeekBarViewModelTest.kt62
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java35
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/util/magnetictarget/MagnetizedObjectTest.kt12
-rw-r--r--packages/Tethering/src/com/android/networkstack/tethering/Tethering.java4
-rw-r--r--packages/Tethering/tests/integration/Android.bp56
-rw-r--r--packages/Tethering/tests/integration/AndroidManifest.xml1
-rw-r--r--packages/Tethering/tests/integration/AndroidManifest_coverage.xml29
-rw-r--r--packages/Tethering/tests/integration/AndroidTest_Coverage.xml12
-rw-r--r--packages/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java4
-rw-r--r--packages/Tethering/tests/unit/Android.bp46
-rw-r--r--read-snapshot.txt0
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java13
-rw-r--r--services/autofill/java/com/android/server/autofill/Session.java5
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java5
-rw-r--r--services/core/java/com/android/server/am/ProcessList.java50
-rw-r--r--services/core/java/com/android/server/am/UserController.java139
-rw-r--r--services/core/java/com/android/server/appop/AppOpsService.java1
-rw-r--r--services/core/java/com/android/server/appop/TEST_MAPPING3
-rwxr-xr-xservices/core/java/com/android/server/audio/AudioService.java53
-rw-r--r--services/core/java/com/android/server/audio/AudioSystemAdapter.java69
-rw-r--r--services/core/java/com/android/server/audio/SystemServerAdapter.java90
-rw-r--r--services/core/java/com/android/server/display/DisplayPowerController.java5
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodManagerService.java3
-rw-r--r--services/core/java/com/android/server/location/AppOpsHelper.java4
-rw-r--r--services/core/java/com/android/server/location/GeofenceManager.java2
-rw-r--r--services/core/java/com/android/server/location/LocationManagerService.java (renamed from services/core/java/com/android/server/LocationManagerService.java)36
-rw-r--r--services/core/java/com/android/server/location/LocationManagerServiceUtils.java (renamed from services/core/java/com/android/server/LocationManagerServiceUtils.java)6
-rw-r--r--services/core/java/com/android/server/location/LocationUsageLogger.java2
-rw-r--r--services/core/java/com/android/server/location/SettingsHelper.java4
-rw-r--r--services/core/java/com/android/server/location/UserInfoHelper.java4
-rw-r--r--services/core/java/com/android/server/location/gnss/GnssManagerService.java4
-rw-r--r--services/core/java/com/android/server/locksettings/LockSettingsService.java29
-rw-r--r--services/core/java/com/android/server/media/SystemMediaRoute2Provider.java6
-rw-r--r--services/core/java/com/android/server/net/NetworkStatsService.java86
-rw-r--r--services/core/java/com/android/server/notification/ConditionProviders.java17
-rw-r--r--services/core/java/com/android/server/notification/ManagedServices.java73
-rwxr-xr-xservices/core/java/com/android/server/notification/NotificationManagerService.java152
-rw-r--r--services/core/java/com/android/server/notification/NotificationRecord.java2
-rw-r--r--services/core/java/com/android/server/notification/PreferencesHelper.java34
-rw-r--r--services/core/java/com/android/server/notification/ShortcutHelper.java31
-rw-r--r--services/core/java/com/android/server/pm/BackgroundDexOptService.java7
-rw-r--r--services/core/java/com/android/server/pm/DataLoaderManagerService.java6
-rw-r--r--services/core/java/com/android/server/pm/Installer.java2
-rw-r--r--services/core/java/com/android/server/pm/PackageDexOptimizer.java11
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java68
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerServiceUtils.java75
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java25
-rw-r--r--services/core/java/com/android/server/pm/dex/DexManager.java44
-rw-r--r--services/core/java/com/android/server/pm/dex/DexoptOptions.java24
-rw-r--r--services/core/java/com/android/server/pm/permission/OneTimePermissionUserManager.java11
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerService.java19
-rw-r--r--services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Enforcer.java56
-rw-r--r--services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java6
-rw-r--r--services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java80
-rw-r--r--services/core/java/com/android/server/wm/AccessibilityController.java47
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java15
-rw-r--r--services/core/java/com/android/server/wm/ActivityStack.java49
-rw-r--r--services/core/java/com/android/server/wm/ActivityStackSupervisor.java2
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java105
-rw-r--r--services/core/java/com/android/server/wm/InsetsSourceProvider.java5
-rw-r--r--services/core/java/com/android/server/wm/RecentsAnimationController.java4
-rw-r--r--services/core/java/com/android/server/wm/Task.java72
-rw-r--r--services/core/java/com/android/server/wm/WindowContainer.java30
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java27
-rw-r--r--services/core/java/com/android/server/wm/WindowOrganizerController.java2
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java33
-rw-r--r--services/core/java/com/android/server/wm/WindowStateAnimator.java2
-rw-r--r--services/core/java/com/android/server/wm/WindowToken.java9
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java75
-rw-r--r--services/incremental/IncrementalService.cpp76
-rw-r--r--services/incremental/IncrementalService.h13
-rw-r--r--services/incremental/test/IncrementalServiceTest.cpp125
-rw-r--r--services/java/com/android/server/SystemServer.java1
-rw-r--r--services/tests/servicestests/AndroidManifest.xml1
-rw-r--r--services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/audio/AudioServiceTest.java115
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java27
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java20
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/dex/DexoptOptionsTests.java5
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/ArchiveTest.java143
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java116
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java4
-rwxr-xr-xservices/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java34
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java115
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/ShortcutHelperTest.java86
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java6
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java106
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskTests.java4
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java48
-rw-r--r--startop/iorap/functional_tests/Android.bp2
-rw-r--r--startop/iorap/functional_tests/src/com/google/android/startop/iorap/IorapWorkFlowTest.java2
l---------startop/iorap/functional_tests/test_data/iorap_test_app_v1.apk1
l---------startop/iorap/functional_tests/test_data/iorap_test_app_v2.apk1
l---------startop/iorap/functional_tests/test_data/iorap_test_app_v3.apk1
-rw-r--r--telecomm/java/android/telecom/Conference.java20
-rwxr-xr-xtelecomm/java/android/telecom/ConnectionService.java5
-rwxr-xr-xtelephony/java/android/telephony/CarrierConfigManager.java21
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java17
-rw-r--r--telephony/java/android/telephony/data/DataCallResponse.java53
-rw-r--r--telephony/java/com/android/internal/telephony/ITelephony.aidl5
-rw-r--r--tests/AppLaunch/Android.bp4
-rw-r--r--tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java151
-rw-r--r--tests/DozeTest/src/com/android/dreams/dozetest/DozeTestDream.java1
-rw-r--r--tests/net/java/com/android/server/net/NetworkStatsServiceTest.java44
210 files changed, 5464 insertions, 1903 deletions
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/idle/DeviceIdlenessTracker.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/idle/DeviceIdlenessTracker.java
index f74eb3b9a45f..e2c8f649fdb7 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/idle/DeviceIdlenessTracker.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/idle/DeviceIdlenessTracker.java
@@ -19,10 +19,12 @@ package com.android.server.job.controllers.idle;
import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
import android.app.AlarmManager;
+import android.app.UiModeManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.os.PowerManager;
import android.util.Log;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
@@ -39,6 +41,7 @@ public final class DeviceIdlenessTracker extends BroadcastReceiver implements Id
|| Log.isLoggable(TAG, Log.DEBUG);
private AlarmManager mAlarm;
+ private PowerManager mPowerManager;
// After construction, mutations of idle/screen-on state will only happen
// on the main looper thread, either in onReceive() or in an alarm callback.
@@ -47,6 +50,7 @@ public final class DeviceIdlenessTracker extends BroadcastReceiver implements Id
private boolean mIdle;
private boolean mScreenOn;
private boolean mDockIdle;
+ private boolean mInCarMode;
private IdlenessListener mIdleListener;
private AlarmManager.OnAlarmListener mIdleAlarmListener = () -> {
@@ -59,6 +63,7 @@ public final class DeviceIdlenessTracker extends BroadcastReceiver implements Id
mIdle = false;
mScreenOn = true;
mDockIdle = false;
+ mInCarMode = false;
}
@Override
@@ -74,6 +79,7 @@ public final class DeviceIdlenessTracker extends BroadcastReceiver implements Id
mIdleWindowSlop = context.getResources().getInteger(
com.android.internal.R.integer.config_jobSchedulerIdleWindowSlop);
mAlarm = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
+ mPowerManager = context.getSystemService(PowerManager.class);
IntentFilter filter = new IntentFilter();
@@ -92,6 +98,10 @@ public final class DeviceIdlenessTracker extends BroadcastReceiver implements Id
filter.addAction(Intent.ACTION_DOCK_IDLE);
filter.addAction(Intent.ACTION_DOCK_ACTIVE);
+ // Car mode
+ filter.addAction(UiModeManager.ACTION_ENTER_CAR_MODE_PRIORITIZED);
+ filter.addAction(UiModeManager.ACTION_EXIT_CAR_MODE_PRIORITIZED);
+
context.registerReceiver(this, filter);
}
@@ -100,6 +110,8 @@ public final class DeviceIdlenessTracker extends BroadcastReceiver implements Id
pw.print(" mIdle: "); pw.println(mIdle);
pw.print(" mScreenOn: "); pw.println(mScreenOn);
pw.print(" mDockIdle: "); pw.println(mDockIdle);
+ pw.print(" mInCarMode: ");
+ pw.println(mInCarMode);
}
@Override
@@ -116,6 +128,9 @@ public final class DeviceIdlenessTracker extends BroadcastReceiver implements Id
proto.write(
StateControllerProto.IdleController.IdlenessTracker.DeviceIdlenessTracker.IS_DOCK_IDLE,
mDockIdle);
+ proto.write(
+ StateControllerProto.IdleController.IdlenessTracker.DeviceIdlenessTracker.IN_CAR_MODE,
+ mInCarMode);
proto.end(diToken);
proto.end(token);
@@ -124,63 +139,90 @@ public final class DeviceIdlenessTracker extends BroadcastReceiver implements Id
@Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
- if (action.equals(Intent.ACTION_SCREEN_ON)
- || action.equals(Intent.ACTION_DREAMING_STOPPED)
- || action.equals(Intent.ACTION_DOCK_ACTIVE)) {
- if (action.equals(Intent.ACTION_DOCK_ACTIVE)) {
+ if (DEBUG) {
+ Slog.v(TAG, "Received action: " + action);
+ }
+ switch (action) {
+ case Intent.ACTION_DOCK_ACTIVE:
if (!mScreenOn) {
// Ignore this intent during screen off
return;
- } else {
- mDockIdle = false;
}
- } else {
+ // Intentional fallthrough
+ case Intent.ACTION_DREAMING_STOPPED:
+ if (!mPowerManager.isInteractive()) {
+ // Ignore this intent if the device isn't interactive.
+ return;
+ }
+ // Intentional fallthrough
+ case Intent.ACTION_SCREEN_ON:
mScreenOn = true;
mDockIdle = false;
- }
- if (DEBUG) {
- Slog.v(TAG,"exiting idle : " + action);
- }
- //cancel the alarm
- mAlarm.cancel(mIdleAlarmListener);
- if (mIdle) {
- // possible transition to not-idle
- mIdle = false;
- mIdleListener.reportNewIdleState(mIdle);
- }
- } else if (action.equals(Intent.ACTION_SCREEN_OFF)
- || action.equals(Intent.ACTION_DREAMING_STARTED)
- || action.equals(Intent.ACTION_DOCK_IDLE)) {
- // when the screen goes off or dreaming starts or wireless charging dock in idle,
- // we schedule the alarm that will tell us when we have decided the device is
- // truly idle.
- if (action.equals(Intent.ACTION_DOCK_IDLE)) {
- if (!mScreenOn) {
- // Ignore this intent during screen off
- return;
+ if (DEBUG) {
+ Slog.v(TAG, "exiting idle");
+ }
+ cancelIdlenessCheck();
+ if (mIdle) {
+ mIdle = false;
+ mIdleListener.reportNewIdleState(mIdle);
+ }
+ break;
+ case Intent.ACTION_SCREEN_OFF:
+ case Intent.ACTION_DREAMING_STARTED:
+ case Intent.ACTION_DOCK_IDLE:
+ // when the screen goes off or dreaming starts or wireless charging dock in idle,
+ // we schedule the alarm that will tell us when we have decided the device is
+ // truly idle.
+ if (action.equals(Intent.ACTION_DOCK_IDLE)) {
+ if (!mScreenOn) {
+ // Ignore this intent during screen off
+ return;
+ } else {
+ mDockIdle = true;
+ }
} else {
- mDockIdle = true;
+ mScreenOn = false;
+ mDockIdle = false;
}
- } else {
- mScreenOn = false;
- mDockIdle = false;
- }
+ maybeScheduleIdlenessCheck(action);
+ break;
+ case UiModeManager.ACTION_ENTER_CAR_MODE_PRIORITIZED:
+ mInCarMode = true;
+ cancelIdlenessCheck();
+ if (mIdle) {
+ mIdle = false;
+ mIdleListener.reportNewIdleState(mIdle);
+ }
+ break;
+ case UiModeManager.ACTION_EXIT_CAR_MODE_PRIORITIZED:
+ mInCarMode = false;
+ maybeScheduleIdlenessCheck(action);
+ break;
+ case ActivityManagerService.ACTION_TRIGGER_IDLE:
+ handleIdleTrigger();
+ break;
+ }
+ }
+
+ private void maybeScheduleIdlenessCheck(String reason) {
+ if ((!mScreenOn || mDockIdle) && !mInCarMode) {
final long nowElapsed = sElapsedRealtimeClock.millis();
final long when = nowElapsed + mInactivityIdleThreshold;
if (DEBUG) {
- Slog.v(TAG, "Scheduling idle : " + action + " now:" + nowElapsed + " when="
- + when);
+ Slog.v(TAG, "Scheduling idle : " + reason + " now:" + nowElapsed + " when=" + when);
}
mAlarm.setWindow(AlarmManager.ELAPSED_REALTIME_WAKEUP,
when, mIdleWindowSlop, "JS idleness", mIdleAlarmListener, null);
- } else if (action.equals(ActivityManagerService.ACTION_TRIGGER_IDLE)) {
- handleIdleTrigger();
}
}
+ private void cancelIdlenessCheck() {
+ mAlarm.cancel(mIdleAlarmListener);
+ }
+
private void handleIdleTrigger() {
// idle time starts now. Do not set mIdle if screen is on.
- if (!mIdle && (!mScreenOn || mDockIdle)) {
+ if (!mIdle && (!mScreenOn || mDockIdle) && !mInCarMode) {
if (DEBUG) {
Slog.v(TAG, "Idle trigger fired @ " + sElapsedRealtimeClock.millis());
}
@@ -189,7 +231,7 @@ public final class DeviceIdlenessTracker extends BroadcastReceiver implements Id
} else {
if (DEBUG) {
Slog.v(TAG, "TRIGGER_IDLE received but not changing state; idle="
- + mIdle + " screen=" + mScreenOn);
+ + mIdle + " screen=" + mScreenOn + " car=" + mInCarMode);
}
}
}
diff --git a/apex/media/framework/java/android/media/MediaParser.java b/apex/media/framework/java/android/media/MediaParser.java
index e533b7a7d6f3..073fddf8f615 100644
--- a/apex/media/framework/java/android/media/MediaParser.java
+++ b/apex/media/framework/java/android/media/MediaParser.java
@@ -51,6 +51,7 @@ import com.google.android.exoplayer2.extractor.ts.DefaultTsPayloadReaderFactory;
import com.google.android.exoplayer2.extractor.ts.PsExtractor;
import com.google.android.exoplayer2.extractor.ts.TsExtractor;
import com.google.android.exoplayer2.extractor.wav.WavExtractor;
+import com.google.android.exoplayer2.upstream.DataReader;
import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.DataSpec;
import com.google.android.exoplayer2.upstream.TransferListener;
@@ -60,7 +61,6 @@ import com.google.android.exoplayer2.video.ColorInfo;
import java.io.EOFException;
import java.io.IOException;
-import java.io.InterruptedIOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.nio.ByteBuffer;
@@ -848,9 +848,9 @@ public final class MediaParser {
private final String[] mParserNamesPool;
private final PositionHolder mPositionHolder;
private final InputReadingDataSource mDataSource;
- private final ExtractorInputAdapter mScratchExtractorInputAdapter;
+ private final DataReaderAdapter mScratchDataReaderAdapter;
private final ParsableByteArrayAdapter mScratchParsableByteArrayAdapter;
- private String mExtractorName;
+ private String mParserName;
private Extractor mExtractor;
private ExtractorInput mExtractorInput;
private long mPendingSeekPosition;
@@ -924,7 +924,7 @@ public final class MediaParser {
@NonNull
@ParserName
public String getParserName() {
- return mExtractorName;
+ return mParserName;
}
/**
@@ -958,25 +958,21 @@ public final class MediaParser {
// TODO: Apply parameters when creating extractor instances.
if (mExtractor == null) {
- if (!mExtractorName.equals(PARSER_NAME_UNKNOWN)) {
- mExtractor = EXTRACTOR_FACTORIES_BY_NAME.get(mExtractorName).createInstance();
+ if (!mParserName.equals(PARSER_NAME_UNKNOWN)) {
+ mExtractor = createExtractor(mParserName);
mExtractor.init(new ExtractorOutputAdapter());
} else {
for (String parserName : mParserNamesPool) {
Extractor extractor = createExtractor(parserName);
try {
if (extractor.sniff(mExtractorInput)) {
- mExtractorName = parserName;
+ mParserName = parserName;
mExtractor = extractor;
mExtractor.init(new ExtractorOutputAdapter());
break;
}
} catch (EOFException e) {
// Do nothing.
- } catch (InterruptedException e) {
- // TODO: Remove this exception replacement once we update the ExoPlayer
- // version.
- throw new InterruptedIOException();
} finally {
mExtractorInput.resetPeekPosition();
}
@@ -999,9 +995,6 @@ public final class MediaParser {
result = mExtractor.read(mExtractorInput, mPositionHolder);
} catch (ParserException e) {
throw new ParsingException(e);
- } catch (InterruptedException e) {
- // TODO: Remove this exception replacement once we update the ExoPlayer version.
- throw new InterruptedIOException();
}
if (result == Extractor.RESULT_END_OF_INPUT) {
return false;
@@ -1051,11 +1044,11 @@ public final class MediaParser {
mParserParameters = new HashMap<>();
mOutputConsumer = outputConsumer;
mParserNamesPool = parserNamesPool;
- mExtractorName = sniff ? PARSER_NAME_UNKNOWN : parserNamesPool[0];
+ mParserName = sniff ? PARSER_NAME_UNKNOWN : parserNamesPool[0];
mPositionHolder = new PositionHolder();
mDataSource = new InputReadingDataSource();
removePendingSeek();
- mScratchExtractorInputAdapter = new ExtractorInputAdapter();
+ mScratchDataReaderAdapter = new DataReaderAdapter();
mScratchParsableByteArrayAdapter = new ParsableByteArrayAdapter();
}
@@ -1097,7 +1090,7 @@ public final class MediaParser {
getBooleanParameter(PARAMETER_MP4_IGNORE_EDIT_LISTS)
? Mp4Extractor.FLAG_WORKAROUND_IGNORE_EDIT_LISTS
: 0;
- return new Mp4Extractor();
+ return new Mp4Extractor(flags);
case PARSER_NAME_MP3:
flags |=
getBooleanParameter(PARAMETER_MP3_DISABLE_ID3)
@@ -1270,12 +1263,12 @@ public final class MediaParser {
}
@Override
- public int sampleData(ExtractorInput input, int length, boolean allowEndOfInput)
+ public int sampleData(DataReader input, int length, boolean allowEndOfInput)
throws IOException {
- mScratchExtractorInputAdapter.setExtractorInput(input, length);
- long positionBeforeReading = mScratchExtractorInputAdapter.getPosition();
- mOutputConsumer.onSampleDataFound(mTrackIndex, mScratchExtractorInputAdapter);
- return (int) (mScratchExtractorInputAdapter.getPosition() - positionBeforeReading);
+ mScratchDataReaderAdapter.setDataReader(input, length);
+ long positionBeforeReading = mScratchDataReaderAdapter.getPosition();
+ mOutputConsumer.onSampleDataFound(mTrackIndex, mScratchDataReaderAdapter);
+ return (int) (mScratchDataReaderAdapter.getPosition() - positionBeforeReading);
}
@Override
@@ -1297,14 +1290,14 @@ public final class MediaParser {
}
}
- private static final class ExtractorInputAdapter implements InputReader {
+ private static final class DataReaderAdapter implements InputReader {
- private ExtractorInput mExtractorInput;
+ private DataReader mDataReader;
private int mCurrentPosition;
private long mLength;
- public void setExtractorInput(ExtractorInput extractorInput, long length) {
- mExtractorInput = extractorInput;
+ public void setDataReader(DataReader dataReader, long length) {
+ mDataReader = dataReader;
mCurrentPosition = 0;
mLength = length;
}
@@ -1314,12 +1307,7 @@ public final class MediaParser {
@Override
public int read(byte[] buffer, int offset, int readLength) throws IOException {
int readBytes = 0;
- try {
- readBytes = mExtractorInput.read(buffer, offset, readLength);
- } catch (InterruptedException e) {
- // TODO: Remove this exception replacement once we update the ExoPlayer version.
- throw new InterruptedIOException();
- }
+ readBytes = mDataReader.read(buffer, offset, readLength);
mCurrentPosition += readBytes;
return readBytes;
}
diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp
index b3579045b6a5..6e8ceb7cb367 100644
--- a/cmds/statsd/Android.bp
+++ b/cmds/statsd/Android.bp
@@ -104,6 +104,7 @@ cc_defaults {
"src/subscriber/IncidentdReporter.cpp",
"src/subscriber/SubscriberReporter.cpp",
"src/uid_data.proto",
+ "src/utils/NamedLatch.cpp",
],
local_include_dirs: [
@@ -361,6 +362,7 @@ cc_test {
"tests/StatsService_test.cpp",
"tests/storage/StorageManager_test.cpp",
"tests/UidMap_test.cpp",
+ "tests/utils/NamedLatch_test.cpp",
],
static_libs: [
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index dd1d40083a6b..ae7a8d0d30cc 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -118,8 +118,9 @@ StatsService::StatsService(const sp<Looper>& handlerLooper, shared_ptr<LogEventQ
}
})),
mEventQueue(queue),
- mStatsCompanionServiceDeathRecipient(AIBinder_DeathRecipient_new(
- StatsService::statsCompanionServiceDied)) {
+ mBootCompleteLatch({kBootCompleteTag, kUidMapReceivedTag, kAllPullersRegisteredTag}),
+ mStatsCompanionServiceDeathRecipient(
+ AIBinder_DeathRecipient_new(StatsService::statsCompanionServiceDied)) {
mUidMap = UidMap::getInstance();
mPullerManager = new StatsPullerManager();
StatsPuller::SetUidMap(mUidMap);
@@ -164,6 +165,12 @@ StatsService::StatsService(const sp<Looper>& handlerLooper, shared_ptr<LogEventQ
std::thread pushedEventThread([this] { readLogs(); });
pushedEventThread.detach();
}
+
+ std::thread bootCompletedThread([this] {
+ mBootCompleteLatch.wait();
+ VLOG("In the boot completed thread");
+ });
+ bootCompletedThread.detach();
}
StatsService::~StatsService() {
@@ -939,6 +946,7 @@ Status StatsService::informAllUidData(const ScopedFileDescriptor& fd) {
packageNames,
installers);
+ mBootCompleteLatch.countDown(kUidMapReceivedTag);
VLOG("StatsService::informAllUidData UidData proto parsed successfully.");
return Status::ok();
}
@@ -1058,7 +1066,7 @@ Status StatsService::bootCompleted() {
ENFORCE_UID(AID_SYSTEM);
VLOG("StatsService::bootCompleted was called");
-
+ mBootCompleteLatch.countDown(kBootCompleteTag);
return Status::ok();
}
@@ -1227,7 +1235,7 @@ Status StatsService::allPullersFromBootRegistered() {
ENFORCE_UID(AID_SYSTEM);
VLOG("StatsService::allPullersFromBootRegistered was called");
-
+ mBootCompleteLatch.countDown(kAllPullersRegisteredTag);
return Status::ok();
}
diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h
index 23d4c1bd199d..79324d89d8e8 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -17,7 +17,14 @@
#ifndef STATS_SERVICE_H
#define STATS_SERVICE_H
+#include <aidl/android/os/BnStatsd.h>
+#include <aidl/android/os/IPendingIntentRef.h>
+#include <aidl/android/os/IPullAtomCallback.h>
#include <gtest/gtest_prod.h>
+#include <utils/Looper.h>
+
+#include <mutex>
+
#include "StatsLogProcessor.h"
#include "anomaly/AlarmMonitor.h"
#include "config/ConfigManager.h"
@@ -26,13 +33,7 @@
#include "packages/UidMap.h"
#include "shell/ShellSubscriber.h"
#include "statscompanion_util.h"
-
-#include <aidl/android/os/BnStatsd.h>
-#include <aidl/android/os/IPendingIntentRef.h>
-#include <aidl/android/os/IPullAtomCallback.h>
-#include <utils/Looper.h>
-
-#include <mutex>
+#include "utils/NamedLatch.h"
using namespace android;
using namespace android::os;
@@ -385,6 +386,11 @@ private:
mutable mutex mShellSubscriberMutex;
std::shared_ptr<LogEventQueue> mEventQueue;
+ NamedLatch mBootCompleteLatch;
+ static const inline string kBootCompleteTag = "BOOT_COMPLETE";
+ static const inline string kUidMapReceivedTag = "UID_MAP";
+ static const inline string kAllPullersRegisteredTag = "PULLERS_REGISTERED";
+
ScopedAIBinder_DeathRecipient mStatsCompanionServiceDeathRecipient;
FRIEND_TEST(StatsLogProcessorTest, TestActivationsPersistAcrossSystemServerRestart);
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 1f090fda2ce7..b7ed6eb16013 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -421,6 +421,8 @@ message Atom {
TvSettingsUIInteracted tvsettings_ui_interacted = 261 [(module) = "tv_settings"];
LauncherStaticLayout launcher_snapshot = 262 [(module) = "sysui"];
PackageInstallerV2Reported package_installer_v2_reported = 263 [(module) = "framework"];
+ UserLifecycleJourneyReported user_lifecycle_journey_reported = 264 [(module) = "framework"];
+ UserLifecycleEventOccurred user_lifecycle_event_occurred = 265 [(module) = "framework"];
SdkExtensionStatus sdk_extension_status = 354;
}
@@ -9357,3 +9359,82 @@ message SettingSnapshot {
// Android user index. 0 for primary user, 10, 11 for secondary or profile user
optional int32 user_id = 7;
}
+
+/**
+ * An event logged to indicate that a user journey is about to be performed. This atom includes
+ * relevant information about the users involved in the journey. A UserLifecycleEventOccurred event
+ * will immediately follow this atom which will describe the event(s) and its state.
+ *
+ * Logged from:
+ * frameworks/base/services/core/java/com/android/server/am/UserController.java
+ * frameworks/base/services/core/java/com/android/server/pm/UserManagerService.java
+ */
+message UserLifecycleJourneyReported {
+ // An identifier to track a chain of user lifecycle events occurring (referenced in the
+ // UserLifecycleEventOccurred atom)
+ optional int64 session_id = 1;
+
+ // Indicates what type of user journey this session is related to
+ enum Journey {
+ UNKNOWN = 0; // Undefined user lifecycle journey
+ USER_SWITCH_UI = 1; // A user switch journey where a UI is shown
+ USER_SWITCH_FG = 2; // A user switch journey without a UI shown
+ USER_START = 3; // A user start journey
+ USER_CREATE = 4; // A user creation journey
+ }
+ optional Journey journey = 2;
+ // Which user the journey is originating from - could be -1 for certain phases (eg USER_CREATE)
+ // This integer is a UserIdInt (eg 0 for the system user, 10 for secondary/guest)
+ optional int32 origin_user = 3;
+ // Which user the journey is targeting
+ // This integer is a UserIdInt (eg 0 for the system user, 10 for secondary/guest)
+ optional int32 target_user = 4;
+
+ // What is the user type of the target user
+ // These should be in sync with USER_TYPE_* flags defined in UserManager.java
+ enum UserType {
+ TYPE_UNKNOWN = 0;
+ FULL_SYSTEM = 1;
+ FULL_SECONDARY = 2;
+ FULL_GUEST = 3;
+ FULL_DEMO = 4;
+ FULL_RESTRICTED = 5;
+ PROFILE_MANAGED = 6;
+ SYSTEM_HEADLESS = 7;
+ }
+ optional UserType user_type = 5;
+ // What are the flags attached to the target user
+ optional int32 user_flags = 6;
+}
+
+/**
+ * An event logged when a specific user lifecycle event is performed. These events should be
+ * correlated with a UserLifecycleJourneyReported atom via the session_id.
+ * Note: journeys can span over multiple events, hence some events may share a single session id.
+ *
+ * Logged from:
+ * frameworks/base/services/core/java/com/android/server/am/UserController.java
+ * frameworks/base/services/core/java/com/android/server/pm/UserManagerService.java
+ */
+message UserLifecycleEventOccurred {
+ // An id which links back to user details (reported in the UserLifecycleJourneyReported atom)
+ optional int64 session_id = 1;
+ // The target user for this event (same as target_user in the UserLifecycleJourneyReported atom)
+ // This integer is a UserIdInt (eg 0 for the system user, 10 for secondary/guest)
+ optional int32 user_id = 2;
+
+ enum Event {
+ UNKNOWN = 0; // Indicates that the associated user journey timed-out or resulted in an error
+ SWITCH_USER = 1; // Indicates that this is a user switch event
+ START_USER = 2; // Indicates that this is a user start event
+ CREATE_USER = 3; // Indicates that this is a user create event
+ }
+ optional Event event = 3;
+
+ enum State {
+ NONE = 0; // Indicates the associated event has no start/end defined
+ BEGIN = 1;
+ FINISH = 2;
+ }
+ optional State state = 4; // Represents the state of an event (beginning/ending)
+}
diff --git a/cmds/statsd/src/utils/NamedLatch.cpp b/cmds/statsd/src/utils/NamedLatch.cpp
new file mode 100644
index 000000000000..6e77977857cc
--- /dev/null
+++ b/cmds/statsd/src/utils/NamedLatch.cpp
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define DEBUG false // STOPSHIP if true
+
+#include "NamedLatch.h"
+
+using namespace std;
+
+namespace android {
+namespace os {
+namespace statsd {
+
+NamedLatch::NamedLatch(const set<string>& eventNames) : mRemainingEventNames(eventNames) {
+}
+
+void NamedLatch::countDown(const string& eventName) {
+ bool notify = false;
+ {
+ lock_guard<mutex> lg(mMutex);
+ mRemainingEventNames.erase(eventName);
+ notify = mRemainingEventNames.empty();
+ }
+ if (notify) {
+ mConditionVariable.notify_all();
+ }
+}
+
+void NamedLatch::wait() const {
+ unique_lock<mutex> unique_lk(mMutex);
+ mConditionVariable.wait(unique_lk, [this] { return mRemainingEventNames.empty(); });
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/utils/NamedLatch.h b/cmds/statsd/src/utils/NamedLatch.h
new file mode 100644
index 000000000000..70238370f647
--- /dev/null
+++ b/cmds/statsd/src/utils/NamedLatch.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <gtest/gtest_prod.h>
+
+#include <condition_variable>
+#include <mutex>
+#include <set>
+
+namespace android {
+namespace os {
+namespace statsd {
+
+/**
+ * This class provides a threading primitive similar to a latch.
+ * The primary difference is that it waits for named events to occur instead of waiting for
+ * N threads to reach a certain point.
+ *
+ * It uses a condition variable under the hood.
+ */
+class NamedLatch {
+public:
+ explicit NamedLatch(const std::set<std::string>& eventNames);
+
+ NamedLatch(const NamedLatch&) = delete;
+ NamedLatch& operator=(const NamedLatch&) = delete;
+
+ // Mark a specific event as completed. If this event has called countDown already or if the
+ // event was not specified in the constructor, the function is a no-op.
+ void countDown(const std::string& eventName);
+
+ // Blocks the calling thread until all events in eventNames have called countDown.
+ void wait() const;
+
+private:
+ mutable std::mutex mMutex;
+ mutable std::condition_variable mConditionVariable;
+ std::set<std::string> mRemainingEventNames;
+
+ FRIEND_TEST(NamedLatchTest, TestCountDownCalledBySameEventName);
+};
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/tests/utils/NamedLatch_test.cpp b/cmds/statsd/tests/utils/NamedLatch_test.cpp
new file mode 100644
index 000000000000..de48a133823e
--- /dev/null
+++ b/cmds/statsd/tests/utils/NamedLatch_test.cpp
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "utils/NamedLatch.h"
+
+#include <gtest/gtest.h>
+
+#include <chrono>
+#include <set>
+#include <thread>
+#include <vector>
+
+#ifdef __ANDROID__
+
+using namespace std;
+using std::this_thread::sleep_for;
+
+namespace android {
+namespace os {
+namespace statsd {
+
+TEST(NamedLatchTest, TestWait) {
+ int numEvents = 5;
+ string t1 = "t1", t2 = "t2", t3 = "t3", t4 = "t4", t5 = "t5";
+ set<string> eventNames = {t1, t2, t3, t4, t5};
+
+ NamedLatch latch(eventNames);
+ vector<thread> threads;
+ vector<bool> done(numEvents, false);
+
+ int i = 0;
+ for (const string& eventName : eventNames) {
+ threads.emplace_back([&done, &eventName, &latch, i] {
+ sleep_for(chrono::milliseconds(3));
+ done[i] = true;
+ latch.countDown(eventName);
+ });
+ i++;
+ }
+
+ latch.wait();
+
+ for (i = 0; i < numEvents; i++) {
+ EXPECT_EQ(done[i], 1);
+ }
+
+ for (i = 0; i < numEvents; i++) {
+ threads[i].join();
+ }
+}
+
+TEST(NamedLatchTest, TestNoWorkers) {
+ NamedLatch latch({});
+ latch.wait();
+ // Ensure that latch does not wait if no events need to countDown.
+}
+
+TEST(NamedLatchTest, TestCountDownCalledBySameEventName) {
+ string t1 = "t1", t2 = "t2";
+ set<string> eventNames = {t1, t2};
+
+ NamedLatch latch(eventNames);
+
+ thread waiterThread([&latch] { latch.wait(); });
+
+ latch.countDown(t1);
+ latch.countDown(t1);
+
+ // Ensure that the latch's remaining threads still has t2.
+ latch.mMutex.lock();
+ ASSERT_EQ(latch.mRemainingEventNames.size(), 1);
+ EXPECT_NE(latch.mRemainingEventNames.find(t2), latch.mRemainingEventNames.end());
+ latch.mMutex.unlock();
+
+ latch.countDown(t2);
+ waiterThread.join();
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 3a708a6f699b..8e0d939487e3 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -2586,6 +2586,44 @@ public class AppOpsManager {
}
/**
+ * Returns a listenerId suitable for use with {@link #noteOp(int, int, String, String, String)}.
+ *
+ * This is intended for use client side, when the receiver id must be created before the
+ * associated call is made to the system server. If using {@link PendingIntent} as the receiver,
+ * avoid using this method as it will include a pointless additional x-process call. Instead to
+ * prefer passing the PendingIntent to the system server, and then invoking
+ * {@link #toReceiverId(PendingIntent)} instead.
+ *
+ * @param obj the receiver in use
+ * @return a string representation of the receiver suitable for app ops use
+ * @hide
+ */
+ // TODO: this should probably be @SystemApi as well
+ public static @NonNull String toReceiverId(@NonNull Object obj) {
+ if (obj instanceof PendingIntent) {
+ return toReceiverId((PendingIntent) obj);
+ } else {
+ return obj.getClass().getName() + "@" + System.identityHashCode(obj);
+ }
+ }
+
+ /**
+ * Returns a listenerId suitable for use with {@link #noteOp(int, int, String, String, String)}.
+ *
+ * This is intended for use server side, where ActivityManagerService can be referenced without
+ * an additional x-process call.
+ *
+ * @param pendingIntent the pendingIntent in use
+ * @return a string representation of the pending intent suitable for app ops use
+ * @see #toReceiverId(Object)
+ * @hide
+ */
+ // TODO: this should probably be @SystemApi as well
+ public static @NonNull String toReceiverId(@NonNull PendingIntent pendingIntent) {
+ return pendingIntent.getTag("");
+ }
+
+ /**
* When to not enforce {@link #setUserRestriction restrictions}.
*
* @hide
diff --git a/core/java/android/app/TEST_MAPPING b/core/java/android/app/TEST_MAPPING
index ab868604dfde..344a4d79b766 100644
--- a/core/java/android/app/TEST_MAPPING
+++ b/core/java/android/app/TEST_MAPPING
@@ -20,6 +20,10 @@
},
{
"file_patterns": ["(/|^)AppOpsManager.java"],
+ "name": "UidAtomTests:testAppOps"
+ },
+ {
+ "file_patterns": ["(/|^)AppOpsManager.java"],
"name": "FrameworksServicesTests",
"options": [
{
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index fb9adb730314..41e2dc0de4d6 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -3666,6 +3666,28 @@ public class DevicePolicyManager {
}
/**
+ * Returns whether the given user's credential will be sufficient for all password policy
+ * requirement, once the user's profile has switched to unified challenge.
+ *
+ * <p>This is different from {@link #isActivePasswordSufficient()} since once the profile
+ * switches to unified challenge, policies set explicitly on the profile will start to affect
+ * the parent user.
+ * @param userHandle the user whose password requirement will be checked
+ * @param profileUser the profile user whose lockscreen challenge will be unified.
+ * @hide
+ */
+ public boolean isPasswordSufficientAfterProfileUnification(int userHandle, int profileUser) {
+ if (mService != null) {
+ try {
+ return mService.isPasswordSufficientAfterProfileUnification(userHandle,
+ profileUser);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ return false;
+ }
+ /**
* Retrieve the number of times the user has failed at entering a password since that last
* successful password entry.
* <p>
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 591a3f68eed0..d10153c11723 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -85,6 +85,7 @@ interface IDevicePolicyManager {
boolean isActivePasswordSufficient(int userHandle, boolean parent);
boolean isProfileActivePasswordSufficientForParent(int userHandle);
+ boolean isPasswordSufficientAfterProfileUnification(int userHandle, int profileUser);
int getPasswordComplexity(boolean parent);
boolean isUsingUnifiedPassword(in ComponentName admin);
int getCurrentFailedPasswordAttempts(int userHandle, boolean parent);
diff --git a/core/java/android/app/admin/PasswordMetrics.java b/core/java/android/app/admin/PasswordMetrics.java
index 86ebb47400c7..39e1f0dc2d2c 100644
--- a/core/java/android/app/admin/PasswordMetrics.java
+++ b/core/java/android/app/admin/PasswordMetrics.java
@@ -350,7 +350,7 @@ public final class PasswordMetrics implements Parcelable {
*
* TODO: move to PasswordPolicy
*/
- private void maxWith(PasswordMetrics other) {
+ public void maxWith(PasswordMetrics other) {
credType = Math.max(credType, other.credType);
if (credType != CREDENTIAL_TYPE_PASSWORD) {
return;
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 9ca2db970eb7..d36d583559a0 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -3158,6 +3158,23 @@ public abstract class PackageManager {
"android.content.pm.extra.VERIFICATION_LONG_VERSION_CODE";
/**
+ * Extra field name for the Merkle tree root hash of a package.
+ * <p>Passed to a package verifier both prior to verification and as a result
+ * of verification.
+ * <p>The value of the extra is a specially formatted list:
+ * {@code filename1:HASH_1;filename2:HASH_2;...;filenameN:HASH_N}
+ * <p>The extra must include an entry for every APK within an installation. If
+ * a hash is not physically present, a hash value of {@code 0} will be used.
+ * <p>The root hash is generated using SHA-256, no salt with a 4096 byte block
+ * size. See the description of the
+ * <a href="https://www.kernel.org/doc/html/latest/filesystems/fsverity.html#merkle-tree">fs-verity merkle-tree</a>
+ * for more details.
+ * @hide
+ */
+ public static final String EXTRA_VERIFICATION_ROOT_HASH =
+ "android.content.pm.extra.EXTRA_VERIFICATION_ROOT_HASH";
+
+ /**
* Extra field name for the ID of a intent filter pending verification.
* Passed to an intent filter verifier and is used to call back to
* {@link #verifyIntentFilter}
diff --git a/core/java/android/hardware/camera2/legacy/LegacyFaceDetectMapper.java b/core/java/android/hardware/camera2/legacy/LegacyFaceDetectMapper.java
index 882a7f4ab37d..b3b4549426f0 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyFaceDetectMapper.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyFaceDetectMapper.java
@@ -233,8 +233,10 @@ public class LegacyFaceDetectMapper {
Camera.Parameters params = legacyRequest.parameters;
Rect activeArray = characteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
- ZoomData zoomData = ParameterUtils.convertScalerCropRegion(activeArray,
- request.get(CaptureRequest.SCALER_CROP_REGION), previewSize, params);
+ ZoomData zoomData = ParameterUtils.convertToLegacyZoom(activeArray,
+ request.get(CaptureRequest.SCALER_CROP_REGION),
+ request.get(CaptureRequest.CONTROL_ZOOM_RATIO),
+ previewSize, params);
List<Face> convertedFaces = new ArrayList<>();
if (faces != null) {
diff --git a/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java b/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java
index 6953a5b793c3..362ddfae67bf 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java
@@ -771,6 +771,7 @@ public class LegacyMetadataMapper {
CameraCharacteristics.CONTROL_AWB_AVAILABLE_MODES ,
CameraCharacteristics.CONTROL_AWB_LOCK_AVAILABLE ,
CameraCharacteristics.CONTROL_MAX_REGIONS ,
+ CameraCharacteristics.CONTROL_ZOOM_RATIO_RANGE ,
CameraCharacteristics.FLASH_INFO_AVAILABLE ,
CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL ,
CameraCharacteristics.JPEG_AVAILABLE_THUMBNAIL_SIZES ,
@@ -828,6 +829,7 @@ public class LegacyMetadataMapper {
CaptureRequest.CONTROL_MODE,
CaptureRequest.CONTROL_SCENE_MODE,
CaptureRequest.CONTROL_VIDEO_STABILIZATION_MODE,
+ CaptureRequest.CONTROL_ZOOM_RATIO,
CaptureRequest.FLASH_MODE,
CaptureRequest.JPEG_GPS_COORDINATES,
CaptureRequest.JPEG_GPS_PROCESSING_METHOD,
@@ -872,6 +874,7 @@ public class LegacyMetadataMapper {
CaptureResult.CONTROL_AWB_MODE ,
CaptureResult.CONTROL_AWB_LOCK ,
CaptureResult.CONTROL_MODE ,
+ CaptureResult.CONTROL_ZOOM_RATIO ,
CaptureResult.FLASH_MODE ,
CaptureResult.JPEG_GPS_COORDINATES ,
CaptureResult.JPEG_GPS_PROCESSING_METHOD ,
@@ -935,6 +938,12 @@ public class LegacyMetadataMapper {
private static void mapScaler(CameraMetadataNative m, Parameters p) {
/*
+ * control.zoomRatioRange
+ */
+ Range<Float> zoomRatioRange = new Range<Float>(1.0f, ParameterUtils.getMaxZoomRatio(p));
+ m.set(CONTROL_ZOOM_RATIO_RANGE, zoomRatioRange);
+
+ /*
* scaler.availableMaxDigitalZoom
*/
m.set(SCALER_AVAILABLE_MAX_DIGITAL_ZOOM, ParameterUtils.getMaxZoomRatio(p));
@@ -1383,6 +1392,9 @@ public class LegacyMetadataMapper {
// control.sceneMode -- DISABLED is always available
m.set(CaptureRequest.CONTROL_SCENE_MODE, CONTROL_SCENE_MODE_DISABLED);
+ // control.zoomRatio -- 1.0
+ m.set(CaptureRequest.CONTROL_ZOOM_RATIO, 1.0f);
+
/*
* statistics.*
*/
diff --git a/core/java/android/hardware/camera2/legacy/LegacyRequestMapper.java b/core/java/android/hardware/camera2/legacy/LegacyRequestMapper.java
index 2e06d5fb3b6f..3a46379477e9 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyRequestMapper.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyRequestMapper.java
@@ -68,8 +68,9 @@ public class LegacyRequestMapper {
*/
ParameterUtils.ZoomData zoomData;
{
- zoomData = ParameterUtils.convertScalerCropRegion(activeArray,
+ zoomData = ParameterUtils.convertToLegacyZoom(activeArray,
request.get(SCALER_CROP_REGION),
+ request.get(CONTROL_ZOOM_RATIO),
previewSize,
params);
diff --git a/core/java/android/hardware/camera2/legacy/LegacyResultMapper.java b/core/java/android/hardware/camera2/legacy/LegacyResultMapper.java
index dc5823d80f21..09edf74f0d4c 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyResultMapper.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyResultMapper.java
@@ -118,8 +118,10 @@ public class LegacyResultMapper {
Rect activeArraySize = characteristics.get(
CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
- ZoomData zoomData = ParameterUtils.convertScalerCropRegion(activeArraySize,
- request.get(CaptureRequest.SCALER_CROP_REGION), previewSize, params);
+ ZoomData zoomData = ParameterUtils.convertToLegacyZoom(activeArraySize,
+ request.get(CaptureRequest.SCALER_CROP_REGION),
+ request.get(CaptureRequest.CONTROL_ZOOM_RATIO),
+ previewSize, params);
/*
* colorCorrection
@@ -516,5 +518,12 @@ public class LegacyResultMapper {
{
m.set(SCALER_CROP_REGION, zoomData.reportedCrop);
}
+
+ /*
+ * control.zoomRatio
+ */
+ {
+ m.set(CONTROL_ZOOM_RATIO, zoomData.reportedZoomRatio);
+ }
}
}
diff --git a/core/java/android/hardware/camera2/legacy/ParameterUtils.java b/core/java/android/hardware/camera2/legacy/ParameterUtils.java
index 3cfd020aeee3..eb435989e9a0 100644
--- a/core/java/android/hardware/camera2/legacy/ParameterUtils.java
+++ b/core/java/android/hardware/camera2/legacy/ParameterUtils.java
@@ -73,11 +73,15 @@ public class ParameterUtils {
public final Rect previewCrop;
/** Reported crop-region given the zoom index, coordinates relative to active-array */
public final Rect reportedCrop;
+ /** Reported zoom ratio given the zoom index */
+ public final float reportedZoomRatio;
- public ZoomData(int zoomIndex, Rect previewCrop, Rect reportedCrop) {
+ public ZoomData(int zoomIndex, Rect previewCrop, Rect reportedCrop,
+ float reportedZoomRatio) {
this.zoomIndex = zoomIndex;
this.previewCrop = previewCrop;
this.reportedCrop = reportedCrop;
+ this.reportedZoomRatio = reportedZoomRatio;
}
}
@@ -371,7 +375,8 @@ public class ParameterUtils {
* @throws NullPointerException if any of the args were {@code null}
*/
public static int getClosestAvailableZoomCrop(
- Camera.Parameters params, Rect activeArray, Size streamSize, Rect cropRegion,
+ Camera.Parameters params, Rect activeArray,
+ Size streamSize, Rect cropRegion,
/*out*/
Rect reportedCropRegion,
Rect previewCropRegion) {
@@ -733,6 +738,92 @@ public class ParameterUtils {
}
/**
+ * Convert the user-specified crop region/zoom into zoom data; which can be used
+ * to set the parameters to a specific zoom index, or to report back to the user what
+ * the actual zoom was, or for other calculations requiring the current preview crop region.
+ *
+ * <p>None of the parameters are mutated.<p>
+ *
+ * @param activeArraySize active array size of the sensor (e.g. max jpeg size)
+ * @param cropRegion the user-specified crop region
+ * @param zoomRatio the user-specified zoom ratio
+ * @param previewSize the current preview size (in pixels)
+ * @param params the current camera parameters (not mutated)
+ *
+ * @return the zoom index, and the effective/reported crop regions (relative to active array)
+ */
+ public static ZoomData convertToLegacyZoom(Rect activeArraySize, Rect
+ cropRegion, Float zoomRatio, Size previewSize, Camera.Parameters params) {
+ final float FLOAT_EQUAL_THRESHOLD = 0.0001f;
+ if (zoomRatio != null &&
+ Math.abs(1.0f - zoomRatio) > FLOAT_EQUAL_THRESHOLD) {
+ // User uses CONTROL_ZOOM_RATIO to control zoom
+ return convertZoomRatio(activeArraySize, zoomRatio, previewSize, params);
+ }
+
+ return convertScalerCropRegion(activeArraySize, cropRegion, previewSize, params);
+ }
+
+ /**
+ * Convert the user-specified zoom ratio into zoom data; which can be used
+ * to set the parameters to a specific zoom index, or to report back to the user what the
+ * actual zoom was, or for other calculations requiring the current preview crop region.
+ *
+ * <p>None of the parameters are mutated.</p>
+ *
+ * @param activeArraySize active array size of the sensor (e.g. max jpeg size)
+ * @param zoomRatio the current zoom ratio
+ * @param previewSize the current preview size (in pixels)
+ * @param params the current camera parameters (not mutated)
+ *
+ * @return the zoom index, and the effective/reported crop regions (relative to active array)
+ */
+ public static ZoomData convertZoomRatio(Rect activeArraySize, float zoomRatio,
+ Size previewSize, Camera.Parameters params) {
+ if (DEBUG) {
+ Log.v(TAG, "convertZoomRatio - user zoom ratio was " + zoomRatio);
+ }
+
+ List<Rect> availableReportedCropRegions =
+ getAvailableZoomCropRectangles(params, activeArraySize);
+ List<Rect> availablePreviewCropRegions =
+ getAvailablePreviewZoomCropRectangles(params, activeArraySize, previewSize);
+ if (availableReportedCropRegions.size() != availablePreviewCropRegions.size()) {
+ throw new AssertionError("available reported/preview crop region size mismatch");
+ }
+
+ // Find the best matched legacy zoom ratio for the requested camera2 zoom ratio.
+ int bestZoomIndex = 0;
+ Rect reportedCropRegion = new Rect(availableReportedCropRegions.get(0));
+ Rect previewCropRegion = new Rect(availablePreviewCropRegions.get(0));
+ float reportedZoomRatio = 1.0f;
+ if (params.isZoomSupported()) {
+ List<Integer> zoomRatios = params.getZoomRatios();
+ for (int i = 1; i < zoomRatios.size(); i++) {
+ if (zoomRatio * ZOOM_RATIO_MULTIPLIER >= zoomRatios.get(i)) {
+ bestZoomIndex = i;
+ reportedCropRegion = availableReportedCropRegions.get(i);
+ previewCropRegion = availablePreviewCropRegions.get(i);
+ reportedZoomRatio = zoomRatios.get(i);
+ } else {
+ break;
+ }
+ }
+ }
+
+ if (DEBUG) {
+ Log.v(TAG, "convertZoomRatio - zoom calculated to: " +
+ "zoomIndex = " + bestZoomIndex +
+ ", reported crop region = " + reportedCropRegion +
+ ", preview crop region = " + previewCropRegion +
+ ", reported zoom ratio = " + reportedZoomRatio);
+ }
+
+ return new ZoomData(bestZoomIndex, reportedCropRegion,
+ previewCropRegion, reportedZoomRatio);
+ }
+
+ /**
* Convert the user-specified crop region into zoom data; which can be used
* to set the parameters to a specific zoom index, or to report back to the user what the
* actual zoom was, or for other calculations requiring the current preview crop region.
@@ -767,15 +858,17 @@ public class ParameterUtils {
final int zoomIdx = ParameterUtils.getClosestAvailableZoomCrop(params, activeArraySizeOnly,
previewSize, userCropRegion,
/*out*/reportedCropRegion, /*out*/previewCropRegion);
+ final float reportedZoomRatio = 1.0f;
if (DEBUG) {
Log.v(TAG, "convertScalerCropRegion - zoom calculated to: " +
"zoomIndex = " + zoomIdx +
", reported crop region = " + reportedCropRegion +
- ", preview crop region = " + previewCropRegion);
+ ", preview crop region = " + previewCropRegion +
+ ", reported zoom ratio = " + reportedZoomRatio);
}
- return new ZoomData(zoomIdx, previewCropRegion, reportedCropRegion);
+ return new ZoomData(zoomIdx, previewCropRegion, reportedCropRegion, reportedZoomRatio);
}
/**
diff --git a/core/java/android/net/UrlQuerySanitizer.java b/core/java/android/net/UrlQuerySanitizer.java
index cf08b653d0f4..b1cf044e8173 100644
--- a/core/java/android/net/UrlQuerySanitizer.java
+++ b/core/java/android/net/UrlQuerySanitizer.java
@@ -306,7 +306,7 @@ public class UrlQuerySanitizer {
return null;
}
int length = value.length();
- if ((mFlags & SCRIPT_URL_OK) != 0) {
+ if ((mFlags & SCRIPT_URL_OK) == 0) {
if (length >= MIN_SCRIPT_PREFIX_LENGTH) {
String asLower = value.toLowerCase(Locale.ROOT);
if (asLower.startsWith(JAVASCRIPT_PREFIX) ||
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 0f2060a36ac9..187274a837a0 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -57,6 +57,7 @@ import android.view.WindowManager.LayoutParams;
import com.android.internal.R;
import com.android.internal.os.RoSystemProperties;
+import com.android.internal.util.FrameworkStatsLog;
import java.io.IOException;
import java.lang.annotation.Retention;
@@ -1841,6 +1842,35 @@ public class UserManager {
}
/**
+ * Returns the enum defined in the statsd UserLifecycleJourneyReported atom corresponding to the
+ * user type.
+ * @hide
+ */
+ public static int getUserTypeForStatsd(@NonNull String userType) {
+ switch (userType) {
+ case USER_TYPE_FULL_SYSTEM:
+ return FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__FULL_SYSTEM;
+ case USER_TYPE_FULL_SECONDARY:
+ return FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__FULL_SECONDARY;
+ case USER_TYPE_FULL_GUEST:
+ return FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__FULL_GUEST;
+ case USER_TYPE_FULL_DEMO:
+ return FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__FULL_DEMO;
+ case USER_TYPE_FULL_RESTRICTED:
+ return FrameworkStatsLog
+ .USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__FULL_RESTRICTED;
+ case USER_TYPE_PROFILE_MANAGED:
+ return FrameworkStatsLog
+ .USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__PROFILE_MANAGED;
+ case USER_TYPE_SYSTEM_HEADLESS:
+ return FrameworkStatsLog
+ .USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__SYSTEM_HEADLESS;
+ default:
+ return FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__TYPE_UNKNOWN;
+ }
+ }
+
+ /**
* @hide
* @deprecated Use {@link #isRestrictedProfile()}
*/
diff --git a/core/java/android/os/incremental/V4Signature.java b/core/java/android/os/incremental/V4Signature.java
index 5cc73caa4f60..d35ce5b2c3f8 100644
--- a/core/java/android/os/incremental/V4Signature.java
+++ b/core/java/android/os/incremental/V4Signature.java
@@ -16,6 +16,8 @@
package android.os.incremental;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.os.ParcelFileDescriptor;
import java.io.ByteArrayInputStream;
@@ -45,8 +47,8 @@ public class V4Signature {
public static class HashingInfo {
public final int hashAlgorithm; // only 1 == SHA256 supported
public final byte log2BlockSize; // only 12 (block size 4096) supported now
- public final byte[] salt; // used exactly as in fs-verity, 32 bytes max
- public final byte[] rawRootHash; // salted digest of the first Merkle tree page
+ @Nullable public final byte[] salt; // used exactly as in fs-verity, 32 bytes max
+ @Nullable public final byte[] rawRootHash; // salted digest of the first Merkle tree page
HashingInfo(int hashAlgorithm, byte log2BlockSize, byte[] salt, byte[] rawRootHash) {
this.hashAlgorithm = hashAlgorithm;
@@ -58,7 +60,8 @@ public class V4Signature {
/**
* Constructs HashingInfo from byte array.
*/
- public static HashingInfo fromByteArray(byte[] bytes) throws IOException {
+ @NonNull
+ public static HashingInfo fromByteArray(@NonNull byte[] bytes) throws IOException {
ByteBuffer buffer = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN);
final int hashAlgorithm = buffer.getInt();
final byte log2BlockSize = buffer.get();
@@ -106,8 +109,18 @@ public class V4Signature {
}
public final int version; // Always 2 for now.
- public final byte[] hashingInfo;
- public final byte[] signingInfo; // Passed as-is to the kernel. Can be retrieved later.
+ /**
+ * Raw byte array containing the IncFS hashing data.
+ * @see HashingInfo#fromByteArray(byte[])
+ */
+ @Nullable public final byte[] hashingInfo;
+
+ /**
+ * Raw byte array containing the V4 signature data.
+ * <p>Passed as-is to the kernel. Can be retrieved later.
+ * @see SigningInfo#fromByteArray(byte[])
+ */
+ @Nullable public final byte[] signingInfo;
/**
* Construct a V4Signature from .idsig file.
@@ -121,7 +134,8 @@ public class V4Signature {
/**
* Construct a V4Signature from a byte array.
*/
- public static V4Signature readFrom(byte[] bytes) throws IOException {
+ @NonNull
+ public static V4Signature readFrom(@NonNull byte[] bytes) throws IOException {
try (InputStream stream = new ByteArrayInputStream(bytes)) {
return readFrom(stream);
}
@@ -169,7 +183,7 @@ public class V4Signature {
return this.version == SUPPORTED_VERSION;
}
- private V4Signature(int version, byte[] hashingInfo, byte[] signingInfo) {
+ private V4Signature(int version, @Nullable byte[] hashingInfo, @Nullable byte[] signingInfo) {
this.version = version;
this.hashingInfo = hashingInfo;
this.signingInfo = signingInfo;
diff --git a/core/java/android/service/autofill/InlinePresentation.java b/core/java/android/service/autofill/InlinePresentation.java
index 63b380404217..9cf1b87f7eab 100644
--- a/core/java/android/service/autofill/InlinePresentation.java
+++ b/core/java/android/service/autofill/InlinePresentation.java
@@ -19,7 +19,6 @@ package android.service.autofill;
import android.annotation.NonNull;
import android.annotation.Size;
import android.app.slice.Slice;
-import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
import android.widget.inline.InlinePresentationSpec;
@@ -67,18 +66,6 @@ public final class InlinePresentation implements Parcelable {
return hints.toArray(new String[hints.size()]);
}
- /**
- * @hide
- * @removed
- */
- @UnsupportedAppUsage
- public InlinePresentation(
- @NonNull Slice slice,
- @NonNull android.view.inline.InlinePresentationSpec inlinePresentationSpec,
- boolean pinned) {
- this(slice, inlinePresentationSpec.toWidget(), pinned);
- }
-
// Code below generated by codegen v1.0.15.
@@ -245,7 +232,7 @@ public final class InlinePresentation implements Parcelable {
};
@DataClass.Generated(
- time = 1585633564226L,
+ time = 1586992400667L,
codegenVersion = "1.0.15",
sourceFile = "frameworks/base/core/java/android/service/autofill/InlinePresentation.java",
inputSignatures = "private final @android.annotation.NonNull android.app.slice.Slice mSlice\nprivate final @android.annotation.NonNull android.widget.inline.InlinePresentationSpec mInlinePresentationSpec\nprivate final boolean mPinned\npublic @android.annotation.NonNull @android.annotation.Size(min=0L) java.lang.String[] getAutofillHints()\nclass InlinePresentation extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genHiddenConstDefs=true, genEqualsHashCode=true)")
diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java
index 3f0787350075..337027ef5bc9 100644
--- a/core/java/android/service/dreams/DreamService.java
+++ b/core/java/android/service/dreams/DreamService.java
@@ -182,7 +182,6 @@ public class DreamService extends Service implements Window.Callback {
private Window mWindow;
private Activity mActivity;
private boolean mInteractive;
- private boolean mLowProfile = true;
private boolean mFullscreen;
private boolean mScreenBright = true;
private boolean mStarted;
@@ -530,32 +529,6 @@ public class DreamService extends Service implements Window.Callback {
}
/**
- * Sets View.SYSTEM_UI_FLAG_LOW_PROFILE on the content view.
- *
- * @param lowProfile True to set View.SYSTEM_UI_FLAG_LOW_PROFILE
- * @hide There is no reason to have this -- dreams can set this flag
- * on their own content view, and from there can actually do the
- * correct interactions with it (seeing when it is cleared etc).
- */
- public void setLowProfile(boolean lowProfile) {
- if (mLowProfile != lowProfile) {
- mLowProfile = lowProfile;
- int flag = View.SYSTEM_UI_FLAG_LOW_PROFILE;
- applySystemUiVisibilityFlags(mLowProfile ? flag : 0, flag);
- }
- }
-
- /**
- * Returns whether or not this dream is in low profile mode. Defaults to true.
- *
- * @see #setLowProfile(boolean)
- * @hide
- */
- public boolean isLowProfile() {
- return getSystemUiVisibilityFlagValue(View.SYSTEM_UI_FLAG_LOW_PROFILE, mLowProfile);
- }
-
- /**
* Controls {@link android.view.WindowManager.LayoutParams#FLAG_FULLSCREEN}
* on the dream's window.
*
@@ -1094,10 +1067,6 @@ public class DreamService extends Service implements Window.Callback {
// along well. Dreams usually don't need such bars anyways, so disable them by default.
mWindow.clearFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
- applySystemUiVisibilityFlags(
- (mLowProfile ? View.SYSTEM_UI_FLAG_LOW_PROFILE : 0),
- View.SYSTEM_UI_FLAG_LOW_PROFILE);
-
mWindow.getDecorView().addOnAttachStateChangeListener(
new View.OnAttachStateChangeListener() {
@Override
@@ -1126,18 +1095,6 @@ public class DreamService extends Service implements Window.Callback {
}
}
- private boolean getSystemUiVisibilityFlagValue(int flag, boolean defaultValue) {
- View v = mWindow == null ? null : mWindow.getDecorView();
- return v == null ? defaultValue : (v.getSystemUiVisibility() & flag) != 0;
- }
-
- private void applySystemUiVisibilityFlags(int flags, int mask) {
- View v = mWindow == null ? null : mWindow.getDecorView();
- if (v != null) {
- v.setSystemUiVisibility(applyFlags(v.getSystemUiVisibility(), flags, mask));
- }
- }
-
private int applyFlags(int oldFlags, int flags, int mask) {
return (oldFlags&~mask) | (flags&mask);
}
@@ -1163,7 +1120,6 @@ public class DreamService extends Service implements Window.Callback {
pw.println(" window: " + mWindow);
pw.print(" flags:");
if (isInteractive()) pw.print(" interactive");
- if (isLowProfile()) pw.print(" lowprofile");
if (isFullscreen()) pw.print(" fullscreen");
if (isScreenBright()) pw.print(" bright");
if (isWindowless()) pw.print(" windowless");
diff --git a/core/java/android/service/notification/StatusBarNotification.java b/core/java/android/service/notification/StatusBarNotification.java
index 21b83c660446..5c43f8f829b0 100644
--- a/core/java/android/service/notification/StatusBarNotification.java
+++ b/core/java/android/service/notification/StatusBarNotification.java
@@ -292,6 +292,18 @@ public class StatusBarNotification implements Parcelable {
return this.user.getIdentifier();
}
+ /**
+ * Like {@link #getUserId()} but handles special users.
+ * @hide
+ */
+ public int getNormalizedUserId() {
+ int userId = getUserId();
+ if (userId == UserHandle.USER_ALL) {
+ userId = UserHandle.USER_SYSTEM;
+ }
+ return userId;
+ }
+
/** The package that the notification belongs to. */
public String getPackageName() {
return pkg;
diff --git a/core/java/android/speech/RecognizerIntent.java b/core/java/android/speech/RecognizerIntent.java
index 362b94b83c3f..3b5a6d59e7e6 100644
--- a/core/java/android/speech/RecognizerIntent.java
+++ b/core/java/android/speech/RecognizerIntent.java
@@ -413,6 +413,10 @@ public class RecognizerIntent {
* {@link #ACTION_VOICE_SEARCH_HANDS_FREE}, {@link #ACTION_WEB_SEARCH} to indicate whether to
* only use an offline speech recognition engine. The default is false, meaning that either
* network or offline recognition engines may be used.
+ *
+ * <p>Depending on the recognizer implementation, these values may have
+ * no effect.</p>
+ *
*/
public static final String EXTRA_PREFER_OFFLINE = "android.speech.extra.PREFER_OFFLINE";
}
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 1086774fc8ff..76ed37c51bfe 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -228,6 +228,7 @@ public final class SurfaceControl implements Parcelable {
*/
public long mNativeObject;
private long mNativeHandle;
+ private Throwable mReleaseStack = null;
// TODO: Move this to native.
private final Object mSizeLock = new Object();
@@ -426,11 +427,18 @@ public final class SurfaceControl implements Parcelable {
if (mNativeObject != 0) {
release();
}
- if (nativeObject != 0) {
+ if (nativeObject != 0) {
mCloseGuard.open("release");
}
mNativeObject = nativeObject;
mNativeHandle = mNativeObject != 0 ? nativeGetHandle(nativeObject) : 0;
+ if (mNativeObject == 0) {
+ if (Build.IS_DEBUGGABLE) {
+ mReleaseStack = new Throwable("assigned zero nativeObject here");
+ }
+ } else {
+ mReleaseStack = null;
+ }
}
/**
@@ -989,11 +997,22 @@ public final class SurfaceControl implements Parcelable {
nativeRelease(mNativeObject);
mNativeObject = 0;
mNativeHandle = 0;
+ if (Build.IS_DEBUGGABLE) {
+ mReleaseStack = new Throwable("released here");
+ }
mCloseGuard.close();
}
}
/**
+ * Returns the call stack that assigned mNativeObject to zero.
+ * @hide
+ */
+ public Throwable getReleaseStack() {
+ return mReleaseStack;
+ }
+
+ /**
* Disconnect any client still connected to the surface.
* @hide
*/
@@ -1004,8 +1023,11 @@ public final class SurfaceControl implements Parcelable {
}
private void checkNotReleased() {
- if (mNativeObject == 0) throw new NullPointerException(
- "mNativeObject is null. Have you called release() already?");
+ if (mNativeObject == 0) {
+ Log.wtf(TAG, "Invalid " + this + " caused by:", mReleaseStack);
+ throw new NullPointerException(
+ "mNativeObject of " + this + " is null. Have you called release() already?");
+ }
}
/**
diff --git a/core/java/android/view/WindowInsetsAnimationController.java b/core/java/android/view/WindowInsetsAnimationController.java
index c191a54283fb..fb9d05e2d730 100644
--- a/core/java/android/view/WindowInsetsAnimationController.java
+++ b/core/java/android/view/WindowInsetsAnimationController.java
@@ -141,7 +141,10 @@ public interface WindowInsetsAnimationController {
/**
* Finishes the animation, and leaves the windows shown or hidden.
* <p>
- * After invoking {@link #finish(boolean)}, this instance is no longer {@link #isReady ready}.
+ * After invoking {@link #finish}, this instance is no longer {@link #isReady ready}.
+ * <p>
+ * Note: Finishing an animation implicitly {@link #setInsetsAndAlpha sets insets and alpha}
+ * according to the requested end state without any further animation.
*
* @param shown if {@code true}, the windows will be shown after finishing the
* animation. Otherwise they will be hidden.
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 83a79344917c..6d3dbfe16b78 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -1258,13 +1258,6 @@ public final class AutofillManager {
}
}
- if (mForAugmentedAutofillOnly) {
- if (sVerbose) {
- Log.v(TAG, "notifyValueChanged(): not notifying system server on "
- + "augmented-only mode");
- }
- return;
- }
if (!mEnabled || !isActiveLocked()) {
if (!startAutofillIfNeededLocked(view)) {
if (sVerbose) {
@@ -1299,10 +1292,6 @@ public final class AutofillManager {
return;
}
synchronized (mLock) {
- if (mForAugmentedAutofillOnly) {
- if (sVerbose) Log.v(TAG, "notifyValueChanged(): ignoring on augmented only mode");
- return;
- }
if (!mEnabled || !isActiveLocked()) {
if (sVerbose) {
Log.v(TAG, "notifyValueChanged(" + view.getAutofillId() + ":" + virtualId
diff --git a/core/java/android/view/inline/InlineContentView.java b/core/java/android/view/inline/InlineContentView.java
deleted file mode 100644
index 3df201c9145d..000000000000
--- a/core/java/android/view/inline/InlineContentView.java
+++ /dev/null
@@ -1,208 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view.inline;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.Context;
-import android.graphics.PixelFormat;
-import android.util.AttributeSet;
-import android.view.SurfaceControl;
-import android.view.SurfaceControlViewHost;
-import android.view.SurfaceHolder;
-import android.view.SurfaceView;
-import android.view.ViewGroup;
-
-/**
- * This class represents a view that holds opaque content from another app that
- * you can inline in your UI.
- *
- * <p>Since the content presented by this view is from another security domain,it is
- * shown on a remote surface preventing the host application from accessing that content.
- * Also the host application cannot interact with the inlined content by injecting touch
- * events or clicking programmatically.
- *
- * <p>This view can be overlaid by other windows, i.e. redressed, but if this is the case
- * the inined UI would not be interactive. Sometimes this is desirable, e.g. animating
- * transitions.
- *
- * <p>By default the surface backing this view is shown on top of the hosting window such
- * that the inlined content is interactive. However, you can temporarily move the surface
- * under the hosting window which could be useful in some cases, e.g. animating transitions.
- * At this point the inlined content will not be interactive and the touch events would
- * be delivered to your app.
- *
- * @hide
- * @removed
- */
-public class InlineContentView extends ViewGroup {
-
- /**
- * Callback for observing the lifecycle of the surface control
- * that manipulates the backing secure embedded UI surface.
- */
- public interface SurfaceControlCallback {
- /**
- * Called when the backing surface is being created.
- *
- * @param surfaceControl The surface control to manipulate the surface.
- */
- void onCreated(@NonNull SurfaceControl surfaceControl);
-
- /**
- * Called when the backing surface is being destroyed.
- *
- * @param surfaceControl The surface control to manipulate the surface.
- */
- void onDestroyed(@NonNull SurfaceControl surfaceControl);
- }
-
- private final @NonNull SurfaceHolder.Callback mSurfaceCallback = new SurfaceHolder.Callback() {
- @Override
- public void surfaceCreated(@NonNull SurfaceHolder holder) {
- mSurfaceControlCallback.onCreated(mSurfaceView.getSurfaceControl());
- }
-
- @Override
- public void surfaceChanged(@NonNull SurfaceHolder holder,
- int format, int width, int height) {
- /* do nothing */
- }
-
- @Override
- public void surfaceDestroyed(@NonNull SurfaceHolder holder) {
- mSurfaceControlCallback.onDestroyed(mSurfaceView.getSurfaceControl());
- }
- };
-
- private final @NonNull SurfaceView mSurfaceView;
-
- private @Nullable SurfaceControlCallback mSurfaceControlCallback;
-
- /**
- * @inheritDoc
- *
- * @hide
- */
- public InlineContentView(@NonNull Context context) {
- this(context, null);
- }
-
- /**
- * @inheritDoc
- *
- * @hide
- */
- public InlineContentView(@NonNull Context context, @Nullable AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- /**
- * @inheritDoc
- *
- * @hide
- */
- public InlineContentView(@NonNull Context context, @Nullable AttributeSet attrs,
- int defStyleAttr) {
- this(context, attrs, defStyleAttr, 0);
- }
-
- /**
- * Gets the surface control. If the surface is not created this method
- * returns {@code null}.
- *
- * @return The surface control.
- *
- * @see #setSurfaceControlCallback(SurfaceControlCallback)
- */
- public @Nullable SurfaceControl getSurfaceControl() {
- return mSurfaceView.getSurfaceControl();
- }
-
- /**
- * @inheritDoc
- *
- * @hide
- */
- public InlineContentView(@NonNull Context context, @Nullable AttributeSet attrs,
- int defStyleAttr, int defStyleRes) {
- super(context, attrs, defStyleAttr, defStyleRes);
- mSurfaceView = new SurfaceView(context, attrs, defStyleAttr, defStyleRes);
- mSurfaceView.setZOrderOnTop(true);
- mSurfaceView.getHolder().setFormat(PixelFormat.TRANSPARENT);
- addView(mSurfaceView);
- }
-
- /**
- * Sets the embedded UI.
- * @param surfacePackage The embedded UI.
- *
- * @hide
- */
- public void setChildSurfacePackage(
- @Nullable SurfaceControlViewHost.SurfacePackage surfacePackage) {
- mSurfaceView.setChildSurfacePackage(surfacePackage);
- }
-
- @Override
- public void onLayout(boolean changed, int l, int t, int r, int b) {
- mSurfaceView.layout(0, 0, getMeasuredWidth(), getMeasuredHeight());
- }
-
- /**
- * Sets a callback to observe the lifecycle of the surface control for
- * managing the backing surface.
- *
- * @param callback The callback to set or {@code null} to clear.
- */
- public void setSurfaceControlCallback(@Nullable SurfaceControlCallback callback) {
- if (mSurfaceControlCallback != null) {
- mSurfaceView.getHolder().removeCallback(mSurfaceCallback);
- }
- mSurfaceControlCallback = callback;
- if (mSurfaceControlCallback != null) {
- mSurfaceView.getHolder().addCallback(mSurfaceCallback);
- }
- }
-
- /**
- * @return Whether the surface backing this view appears on top of its parent.
- *
- * @see #setZOrderedOnTop(boolean)
- */
- public boolean isZOrderedOnTop() {
- return mSurfaceView.isZOrderedOnTop();
- }
-
- /**
- * Controls whether the backing surface is placed on top of this view's window.
- * Normally, it is placed on top of the window, to allow interaction
- * with the inlined UI. Via this method, you can place the surface below the
- * window. This means that all of the contents of the window this view is in
- * will be visible on top of its surface.
- *
- * <p> The Z ordering can be changed dynamically if the backing surface is
- * created, otherwise the ordering would be applied at surface construction time.
- *
- * @param onTop Whether to show the surface on top of this view's window.
- *
- * @see #isZOrderedOnTop()
- */
- public boolean setZOrderedOnTop(boolean onTop) {
- return mSurfaceView.setZOrderedOnTop(onTop, /*allowDynamicChange*/ true);
- }
-}
diff --git a/core/java/android/view/inline/InlinePresentationSpec.java b/core/java/android/view/inline/InlinePresentationSpec.java
deleted file mode 100644
index d777cb8d8e0b..000000000000
--- a/core/java/android/view/inline/InlinePresentationSpec.java
+++ /dev/null
@@ -1,347 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view.inline;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.compat.annotation.UnsupportedAppUsage;
-import android.os.Bundle;
-import android.os.Parcelable;
-import android.util.Size;
-
-import com.android.internal.util.DataClass;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * This class represents the presentation specification by which an inline suggestion
- * should abide when constructing its UI. Since suggestions are inlined in a
- * host application while provided by another source, they need to be consistent
- * with the host's look at feel to allow building smooth and integrated UIs.
- *
- * @hide
- * @removed
- */
-@DataClass(genEqualsHashCode = true, genToString = true, genBuilder = true)
-public final class InlinePresentationSpec implements Parcelable {
-
- /** The minimal size of the suggestion. */
- @NonNull
- private final Size mMinSize;
- /** The maximal size of the suggestion. */
- @NonNull
- private final Size mMaxSize;
-
- /**
- * The extras encoding the UI style information. Defaults to {@code Bundle.EMPTY} in which case
- * the default system UI style will be used.
- */
- @NonNull
- private final Bundle mStyle;
-
- private static Bundle defaultStyle() {
- return Bundle.EMPTY;
- }
-
- /** @hide */
- @DataClass.Suppress({"setMaxSize", "setMinSize"})
- abstract static class BaseBuilder {
- }
-
- /**
- * @hide
- */
- public android.widget.inline.InlinePresentationSpec toWidget() {
- final android.widget.inline.InlinePresentationSpec.Builder builder =
- new android.widget.inline.InlinePresentationSpec.Builder(
- getMinSize(), getMaxSize());
- final Bundle style = getStyle();
- if (style != null) {
- builder.setStyle(style);
- }
- return builder.build();
- }
-
- /**
- * @hide
- */
- public static android.view.inline.InlinePresentationSpec fromWidget(
- android.widget.inline.InlinePresentationSpec widget) {
- final android.view.inline.InlinePresentationSpec.Builder builder =
- new android.view.inline.InlinePresentationSpec.Builder(
- widget.getMinSize(), widget.getMaxSize());
- final Bundle style = widget.getStyle();
- if (style != null) {
- builder.setStyle(style);
- }
- return builder.build();
- }
-
- /**
- * @hide
- */
- public static List<android.view.inline.InlinePresentationSpec> fromWidgets(
- List<android.widget.inline.InlinePresentationSpec> widgets) {
- final ArrayList<android.view.inline.InlinePresentationSpec> convertedSpecs =
- new ArrayList<>();
- for (int i = 0; i < widgets.size(); i++) {
- convertedSpecs.add(fromWidget(widgets.get(i)));
- }
- return convertedSpecs;
- }
-
-
-
- // Code below generated by codegen v1.0.15.
- //
- // DO NOT MODIFY!
- // CHECKSTYLE:OFF Generated code
- //
- // To regenerate run:
- // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/view/inline/InlinePresentationSpec.java
- //
- // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
- // Settings > Editor > Code Style > Formatter Control
- //@formatter:off
-
-
- @DataClass.Generated.Member
- /* package-private */ InlinePresentationSpec(
- @NonNull Size minSize,
- @NonNull Size maxSize,
- @NonNull Bundle style) {
- this.mMinSize = minSize;
- com.android.internal.util.AnnotationValidations.validate(
- NonNull.class, null, mMinSize);
- this.mMaxSize = maxSize;
- com.android.internal.util.AnnotationValidations.validate(
- NonNull.class, null, mMaxSize);
- this.mStyle = style;
- com.android.internal.util.AnnotationValidations.validate(
- NonNull.class, null, mStyle);
-
- // onConstructed(); // You can define this method to get a callback
- }
-
- /**
- * The minimal size of the suggestion.
- */
- @UnsupportedAppUsage
- @DataClass.Generated.Member
- public @NonNull Size getMinSize() {
- return mMinSize;
- }
-
- /**
- * The maximal size of the suggestion.
- */
- @UnsupportedAppUsage
- @DataClass.Generated.Member
- public @NonNull Size getMaxSize() {
- return mMaxSize;
- }
-
- /**
- * The extras encoding the UI style information. Defaults to {@code Bundle.EMPTY} in which case
- * the default system UI style will be used.
- */
- @DataClass.Generated.Member
- public @NonNull Bundle getStyle() {
- return mStyle;
- }
-
- @Override
- @DataClass.Generated.Member
- public String toString() {
- // You can override field toString logic by defining methods like:
- // String fieldNameToString() { ... }
-
- return "InlinePresentationSpec { " +
- "minSize = " + mMinSize + ", " +
- "maxSize = " + mMaxSize + ", " +
- "style = " + mStyle +
- " }";
- }
-
- @Override
- @DataClass.Generated.Member
- public boolean equals(@Nullable Object o) {
- // You can override field equality logic by defining either of the methods like:
- // boolean fieldNameEquals(InlinePresentationSpec other) { ... }
- // boolean fieldNameEquals(FieldType otherValue) { ... }
-
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- @SuppressWarnings("unchecked")
- InlinePresentationSpec that = (InlinePresentationSpec) o;
- //noinspection PointlessBooleanExpression
- return true
- && java.util.Objects.equals(mMinSize, that.mMinSize)
- && java.util.Objects.equals(mMaxSize, that.mMaxSize)
- && java.util.Objects.equals(mStyle, that.mStyle);
- }
-
- @Override
- @DataClass.Generated.Member
- public int hashCode() {
- // You can override field hashCode logic by defining methods like:
- // int fieldNameHashCode() { ... }
-
- int _hash = 1;
- _hash = 31 * _hash + java.util.Objects.hashCode(mMinSize);
- _hash = 31 * _hash + java.util.Objects.hashCode(mMaxSize);
- _hash = 31 * _hash + java.util.Objects.hashCode(mStyle);
- return _hash;
- }
-
- @Override
- @DataClass.Generated.Member
- public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
- // You can override field parcelling by defining methods like:
- // void parcelFieldName(Parcel dest, int flags) { ... }
-
- dest.writeSize(mMinSize);
- dest.writeSize(mMaxSize);
- dest.writeBundle(mStyle);
- }
-
- @Override
- @DataClass.Generated.Member
- public int describeContents() { return 0; }
-
- /** @hide */
- @SuppressWarnings({"unchecked", "RedundantCast"})
- @DataClass.Generated.Member
- /* package-private */ InlinePresentationSpec(@NonNull android.os.Parcel in) {
- // You can override field unparcelling by defining methods like:
- // static FieldType unparcelFieldName(Parcel in) { ... }
-
- Size minSize = (Size) in.readSize();
- Size maxSize = (Size) in.readSize();
- Bundle style = in.readBundle();
-
- this.mMinSize = minSize;
- com.android.internal.util.AnnotationValidations.validate(
- NonNull.class, null, mMinSize);
- this.mMaxSize = maxSize;
- com.android.internal.util.AnnotationValidations.validate(
- NonNull.class, null, mMaxSize);
- this.mStyle = style;
- com.android.internal.util.AnnotationValidations.validate(
- NonNull.class, null, mStyle);
-
- // onConstructed(); // You can define this method to get a callback
- }
-
- @DataClass.Generated.Member
- public static final @NonNull Parcelable.Creator<InlinePresentationSpec> CREATOR
- = new Parcelable.Creator<InlinePresentationSpec>() {
- @Override
- public InlinePresentationSpec[] newArray(int size) {
- return new InlinePresentationSpec[size];
- }
-
- @Override
- public InlinePresentationSpec createFromParcel(@NonNull android.os.Parcel in) {
- return new InlinePresentationSpec(in);
- }
- };
-
- /**
- * A builder for {@link InlinePresentationSpec}
- */
- @SuppressWarnings("WeakerAccess")
- @DataClass.Generated.Member
- public static final class Builder extends BaseBuilder {
-
- private @NonNull Size mMinSize;
- private @NonNull Size mMaxSize;
- private @NonNull Bundle mStyle;
-
- private long mBuilderFieldsSet = 0L;
-
- /**
- * Creates a new Builder.
- *
- * @param minSize
- * The minimal size of the suggestion.
- * @param maxSize
- * The maximal size of the suggestion.
- */
- @UnsupportedAppUsage
- public Builder(
- @NonNull Size minSize,
- @NonNull Size maxSize) {
- mMinSize = minSize;
- com.android.internal.util.AnnotationValidations.validate(
- NonNull.class, null, mMinSize);
- mMaxSize = maxSize;
- com.android.internal.util.AnnotationValidations.validate(
- NonNull.class, null, mMaxSize);
- }
-
- /**
- * The extras encoding the UI style information. Defaults to {@code Bundle.EMPTY} in which case
- * the default system UI style will be used.
- */
- @DataClass.Generated.Member
- public @NonNull Builder setStyle(@NonNull Bundle value) {
- checkNotUsed();
- mBuilderFieldsSet |= 0x4;
- mStyle = value;
- return this;
- }
-
- /** Builds the instance. This builder should not be touched after calling this! */
- @UnsupportedAppUsage
- @NonNull
- public InlinePresentationSpec build() {
- checkNotUsed();
- mBuilderFieldsSet |= 0x8; // Mark builder used
-
- if ((mBuilderFieldsSet & 0x4) == 0) {
- mStyle = defaultStyle();
- }
- InlinePresentationSpec o = new InlinePresentationSpec(
- mMinSize,
- mMaxSize,
- mStyle);
- return o;
- }
-
- private void checkNotUsed() {
- if ((mBuilderFieldsSet & 0x8) != 0) {
- throw new IllegalStateException(
- "This Builder should not be reused. Use a new Builder instance instead");
- }
- }
- }
-
- @DataClass.Generated(
- time = 1585691139012L,
- codegenVersion = "1.0.15",
- sourceFile = "frameworks/base/core/java/android/view/inline/InlinePresentationSpec.java",
- inputSignatures = "private final @android.annotation.NonNull android.util.Size mMinSize\nprivate final @android.annotation.NonNull android.util.Size mMaxSize\nprivate final @android.annotation.NonNull android.os.Bundle mStyle\nprivate static android.os.Bundle defaultStyle()\npublic android.widget.inline.InlinePresentationSpec toWidget()\npublic static android.view.inline.InlinePresentationSpec fromWidget(android.widget.inline.InlinePresentationSpec)\npublic static java.util.List<android.view.inline.InlinePresentationSpec> fromWidgets(java.util.List<android.widget.inline.InlinePresentationSpec>)\nclass InlinePresentationSpec extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\nclass BaseBuilder extends java.lang.Object implements []")
- @Deprecated
- private void __metadata() {}
-
-
- //@formatter:on
- // End of generated code
-
-}
diff --git a/core/java/android/view/inputmethod/InlineSuggestionInfo.java b/core/java/android/view/inputmethod/InlineSuggestionInfo.java
index 3e9ffa7787f6..1c703ecf06ca 100644
--- a/core/java/android/view/inputmethod/InlineSuggestionInfo.java
+++ b/core/java/android/view/inputmethod/InlineSuggestionInfo.java
@@ -20,7 +20,6 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.annotation.TestApi;
-import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcelable;
import android.widget.inline.InlinePresentationSpec;
@@ -87,17 +86,6 @@ public final class InlineSuggestionInfo implements Parcelable {
return new InlineSuggestionInfo(presentationSpec, source, autofillHints, type, isPinned);
}
- /**
- * The presentation spec to which the inflated suggestion view abides.
- *
- * @hide
- * @removed
- */
- @UnsupportedAppUsage
- public @NonNull android.view.inline.InlinePresentationSpec getPresentationSpec() {
- return android.view.inline.InlinePresentationSpec.fromWidget(mInlinePresentationSpec);
- }
-
// Code below generated by codegen v1.0.15.
@@ -358,10 +346,10 @@ public final class InlineSuggestionInfo implements Parcelable {
};
@DataClass.Generated(
- time = 1585633580662L,
+ time = 1586992414034L,
codegenVersion = "1.0.15",
sourceFile = "frameworks/base/core/java/android/view/inputmethod/InlineSuggestionInfo.java",
- inputSignatures = "public static final @android.view.inputmethod.InlineSuggestionInfo.Source java.lang.String SOURCE_AUTOFILL\npublic static final @android.view.inputmethod.InlineSuggestionInfo.Source java.lang.String SOURCE_PLATFORM\npublic static final @android.view.inputmethod.InlineSuggestionInfo.Type java.lang.String TYPE_SUGGESTION\npublic static final @android.annotation.SuppressLint({\"IntentName\"}) @android.view.inputmethod.InlineSuggestionInfo.Type java.lang.String TYPE_ACTION\nprivate final @android.annotation.NonNull android.widget.inline.InlinePresentationSpec mInlinePresentationSpec\nprivate final @android.annotation.NonNull @android.view.inputmethod.InlineSuggestionInfo.Source java.lang.String mSource\nprivate final @android.annotation.Nullable java.lang.String[] mAutofillHints\nprivate final @android.annotation.NonNull @android.view.inputmethod.InlineSuggestionInfo.Type java.lang.String mType\nprivate final boolean mPinned\npublic static @android.annotation.TestApi @android.annotation.NonNull android.view.inputmethod.InlineSuggestionInfo newInlineSuggestionInfo(android.widget.inline.InlinePresentationSpec,java.lang.String,java.lang.String[],java.lang.String,boolean)\npublic @android.compat.annotation.UnsupportedAppUsage @android.annotation.NonNull android.view.inline.InlinePresentationSpec getPresentationSpec()\nclass InlineSuggestionInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genHiddenConstDefs=true, genHiddenConstructor=true)")
+ inputSignatures = "public static final @android.view.inputmethod.InlineSuggestionInfo.Source java.lang.String SOURCE_AUTOFILL\npublic static final @android.view.inputmethod.InlineSuggestionInfo.Source java.lang.String SOURCE_PLATFORM\npublic static final @android.view.inputmethod.InlineSuggestionInfo.Type java.lang.String TYPE_SUGGESTION\npublic static final @android.annotation.SuppressLint({\"IntentName\"}) @android.view.inputmethod.InlineSuggestionInfo.Type java.lang.String TYPE_ACTION\nprivate final @android.annotation.NonNull android.widget.inline.InlinePresentationSpec mInlinePresentationSpec\nprivate final @android.annotation.NonNull @android.view.inputmethod.InlineSuggestionInfo.Source java.lang.String mSource\nprivate final @android.annotation.Nullable java.lang.String[] mAutofillHints\nprivate final @android.annotation.NonNull @android.view.inputmethod.InlineSuggestionInfo.Type java.lang.String mType\nprivate final boolean mPinned\npublic static @android.annotation.TestApi @android.annotation.NonNull android.view.inputmethod.InlineSuggestionInfo newInlineSuggestionInfo(android.widget.inline.InlinePresentationSpec,java.lang.String,java.lang.String[],java.lang.String,boolean)\nclass InlineSuggestionInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genHiddenConstDefs=true, genHiddenConstructor=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/view/inputmethod/InlineSuggestionsRequest.java b/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
index af896fca932a..d282b56aedb6 100644
--- a/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
+++ b/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
@@ -19,7 +19,6 @@ package android.view.inputmethod;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityThread;
-import android.compat.annotation.UnsupportedAppUsage;
import android.os.Bundle;
import android.os.IBinder;
import android.os.LocaleList;
@@ -93,20 +92,6 @@ public final class InlineSuggestionsRequest implements Parcelable {
private int mHostDisplayId;
/**
- * The {@link InlinePresentationSpec} for each suggestion in the response. If the max suggestion
- * count is larger than the number of specs in the list, then the last spec is used for the
- * remainder of the suggestions. The list should not be empty.
- *
- * @hide
- * @removed
- */
- @UnsupportedAppUsage
- @NonNull
- public List<android.view.inline.InlinePresentationSpec> getPresentationSpecs() {
- return android.view.inline.InlinePresentationSpec.fromWidgets(mInlinePresentationSpecs);
- }
-
- /**
* @hide
* @see {@link #mHostInputToken}.
*/
@@ -170,17 +155,6 @@ public final class InlineSuggestionsRequest implements Parcelable {
/** @hide */
abstract static class BaseBuilder {
- /**
- * @hide
- * @removed
- */
- @UnsupportedAppUsage
- @NonNull
- public Builder addPresentationSpecs(
- @NonNull android.view.inline.InlinePresentationSpec value) {
- return ((Builder) this).addInlinePresentationSpecs(value.toWidget());
- }
-
abstract Builder setInlinePresentationSpecs(
@NonNull List<android.widget.inline.InlinePresentationSpec> specs);
@@ -608,10 +582,10 @@ public final class InlineSuggestionsRequest implements Parcelable {
}
@DataClass.Generated(
- time = 1585768018462L,
+ time = 1586992395497L,
codegenVersion = "1.0.15",
sourceFile = "frameworks/base/core/java/android/view/inputmethod/InlineSuggestionsRequest.java",
- inputSignatures = "public static final int SUGGESTION_COUNT_UNLIMITED\nprivate final int mMaxSuggestionCount\nprivate final @android.annotation.NonNull java.util.List<android.widget.inline.InlinePresentationSpec> mInlinePresentationSpecs\nprivate @android.annotation.NonNull java.lang.String mHostPackageName\nprivate @android.annotation.NonNull android.os.LocaleList mSupportedLocales\nprivate @android.annotation.NonNull android.os.Bundle mExtras\nprivate @android.annotation.Nullable android.os.IBinder mHostInputToken\nprivate int mHostDisplayId\npublic @android.compat.annotation.UnsupportedAppUsage @android.annotation.NonNull java.util.List<android.view.inline.InlinePresentationSpec> getPresentationSpecs()\npublic void setHostInputToken(android.os.IBinder)\nprivate boolean extrasEquals(android.os.Bundle)\nprivate void parcelHostInputToken(android.os.Parcel,int)\nprivate @android.annotation.Nullable android.os.IBinder unparcelHostInputToken(android.os.Parcel)\npublic void setHostDisplayId(int)\nprivate void onConstructed()\nprivate static int defaultMaxSuggestionCount()\nprivate static java.lang.String defaultHostPackageName()\nprivate static android.os.LocaleList defaultSupportedLocales()\nprivate static @android.annotation.Nullable android.os.IBinder defaultHostInputToken()\nprivate static @android.annotation.Nullable int defaultHostDisplayId()\nprivate static @android.annotation.NonNull android.os.Bundle defaultExtras()\nclass InlineSuggestionsRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\npublic @android.compat.annotation.UnsupportedAppUsage @android.annotation.NonNull android.view.inputmethod.InlineSuggestionsRequest.Builder addPresentationSpecs(android.view.inline.InlinePresentationSpec)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setInlinePresentationSpecs(java.util.List<android.widget.inline.InlinePresentationSpec>)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostPackageName(java.lang.String)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostInputToken(android.os.IBinder)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostDisplayId(int)\nclass BaseBuilder extends java.lang.Object implements []")
+ inputSignatures = "public static final int SUGGESTION_COUNT_UNLIMITED\nprivate final int mMaxSuggestionCount\nprivate final @android.annotation.NonNull java.util.List<android.widget.inline.InlinePresentationSpec> mInlinePresentationSpecs\nprivate @android.annotation.NonNull java.lang.String mHostPackageName\nprivate @android.annotation.NonNull android.os.LocaleList mSupportedLocales\nprivate @android.annotation.NonNull android.os.Bundle mExtras\nprivate @android.annotation.Nullable android.os.IBinder mHostInputToken\nprivate int mHostDisplayId\npublic void setHostInputToken(android.os.IBinder)\nprivate boolean extrasEquals(android.os.Bundle)\nprivate void parcelHostInputToken(android.os.Parcel,int)\nprivate @android.annotation.Nullable android.os.IBinder unparcelHostInputToken(android.os.Parcel)\npublic void setHostDisplayId(int)\nprivate void onConstructed()\nprivate static int defaultMaxSuggestionCount()\nprivate static java.lang.String defaultHostPackageName()\nprivate static android.os.LocaleList defaultSupportedLocales()\nprivate static @android.annotation.Nullable android.os.IBinder defaultHostInputToken()\nprivate static @android.annotation.Nullable int defaultHostDisplayId()\nprivate static @android.annotation.NonNull android.os.Bundle defaultExtras()\nclass InlineSuggestionsRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setInlinePresentationSpecs(java.util.List<android.widget.inline.InlinePresentationSpec>)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostPackageName(java.lang.String)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostInputToken(android.os.IBinder)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostDisplayId(int)\nclass BaseBuilder extends java.lang.Object implements []")
@Deprecated
private void __metadata() {}
diff --git a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
index d64b5f1118dc..be66d0c238cc 100644
--- a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
+++ b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
@@ -232,9 +232,8 @@ public class AccessibilityShortcutController {
}
/**
- * Show toast if current assigned shortcut target is an accessibility service and its target
- * sdk version is less than or equal to Q, or greater than Q and does not request
- * accessibility button.
+ * Show toast to alert the user that the accessibility shortcut turned on or off an
+ * accessibility service.
*/
private void showToast() {
final AccessibilityServiceInfo serviceInfo = getInfoForTargetService();
@@ -247,12 +246,15 @@ public class AccessibilityShortcutController {
}
final boolean requestA11yButton = (serviceInfo.flags
& AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0;
- if (serviceInfo.getResolveInfo().serviceInfo.applicationInfo
- .targetSdkVersion > Build.VERSION_CODES.Q && requestA11yButton) {
+ final boolean isServiceEnabled = isServiceEnabled(serviceInfo);
+ if (serviceInfo.getResolveInfo().serviceInfo.applicationInfo.targetSdkVersion
+ > Build.VERSION_CODES.Q && requestA11yButton && isServiceEnabled) {
+ // An accessibility button callback is sent to the target accessibility service.
+ // No need to show up a toast in this case.
return;
}
// For accessibility services, show a toast explaining what we're doing.
- String toastMessageFormatString = mContext.getString(isServiceEnabled(serviceInfo)
+ String toastMessageFormatString = mContext.getString(isServiceEnabled
? R.string.accessibility_shortcut_disabling_service
: R.string.accessibility_shortcut_enabling_service);
String toastMessage = String.format(toastMessageFormatString, serviceName);
diff --git a/core/java/com/android/internal/app/SystemUserHomeActivity.java b/core/java/com/android/internal/app/SystemUserHomeActivity.java
index 26fbf6f826a8..ee936a3f9abc 100644
--- a/core/java/com/android/internal/app/SystemUserHomeActivity.java
+++ b/core/java/com/android/internal/app/SystemUserHomeActivity.java
@@ -17,10 +17,27 @@
package com.android.internal.app;
import android.app.Activity;
+import android.os.Bundle;
+import android.util.Log;
+
+import com.android.internal.R;
/**
* Placeholder home activity, which is always installed on the system user. At least one home
* activity must be present and enabled in order for the system to boot.
*/
public class SystemUserHomeActivity extends Activity {
+ private static final String TAG = "SystemUserHome";
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ Log.i(TAG, "onCreate");
+ setContentView(R.layout.system_user_home);
+ }
+
+ protected void onDestroy() {
+ super.onDestroy();
+ Log.i(TAG, "onDestroy");
+ }
}
diff --git a/core/java/com/android/internal/app/procstats/DumpUtils.java b/core/java/com/android/internal/app/procstats/DumpUtils.java
index eda04a6a322a..ea41618a982f 100644
--- a/core/java/com/android/internal/app/procstats/DumpUtils.java
+++ b/core/java/com/android/internal/app/procstats/DumpUtils.java
@@ -16,14 +16,39 @@
package com.android.internal.app.procstats;
+import static com.android.internal.app.procstats.ProcessStats.ADJ_COUNT;
+import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_COUNT;
+import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_CRITICAL;
+import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_LOW;
+import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_MODERATE;
+import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_NORMAL;
+import static com.android.internal.app.procstats.ProcessStats.ADJ_NOTHING;
+import static com.android.internal.app.procstats.ProcessStats.ADJ_SCREEN_MOD;
+import static com.android.internal.app.procstats.ProcessStats.ADJ_SCREEN_OFF;
+import static com.android.internal.app.procstats.ProcessStats.ADJ_SCREEN_ON;
+import static com.android.internal.app.procstats.ProcessStats.STATE_BACKUP;
+import static com.android.internal.app.procstats.ProcessStats.STATE_CACHED_ACTIVITY;
+import static com.android.internal.app.procstats.ProcessStats.STATE_CACHED_ACTIVITY_CLIENT;
+import static com.android.internal.app.procstats.ProcessStats.STATE_CACHED_EMPTY;
+import static com.android.internal.app.procstats.ProcessStats.STATE_COUNT;
+import static com.android.internal.app.procstats.ProcessStats.STATE_HEAVY_WEIGHT;
+import static com.android.internal.app.procstats.ProcessStats.STATE_HOME;
+import static com.android.internal.app.procstats.ProcessStats.STATE_IMPORTANT_BACKGROUND;
+import static com.android.internal.app.procstats.ProcessStats.STATE_IMPORTANT_FOREGROUND;
+import static com.android.internal.app.procstats.ProcessStats.STATE_LAST_ACTIVITY;
+import static com.android.internal.app.procstats.ProcessStats.STATE_NOTHING;
+import static com.android.internal.app.procstats.ProcessStats.STATE_PERSISTENT;
+import static com.android.internal.app.procstats.ProcessStats.STATE_RECEIVER;
+import static com.android.internal.app.procstats.ProcessStats.STATE_SERVICE;
+import static com.android.internal.app.procstats.ProcessStats.STATE_SERVICE_RESTARTING;
+import static com.android.internal.app.procstats.ProcessStats.STATE_TOP;
+
import android.os.UserHandle;
import android.service.procstats.ProcessStatsEnums;
import android.service.procstats.ProcessStatsStateProto;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
-import static com.android.internal.app.procstats.ProcessStats.*;
-
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -38,6 +63,7 @@ public final class DumpUtils {
public static final String[] STATE_NAMES_CSV;
static final String[] STATE_TAGS;
static final int[] STATE_PROTO_ENUMS;
+ private static final int[] PROCESS_STATS_STATE_TO_AGGREGATED_STATE;
// Make the mapping easy to update.
static {
@@ -126,6 +152,39 @@ public final class DumpUtils {
STATE_PROTO_ENUMS[STATE_CACHED_ACTIVITY_CLIENT] =
ProcessStatsEnums.PROCESS_STATE_CACHED_ACTIVITY_CLIENT;
STATE_PROTO_ENUMS[STATE_CACHED_EMPTY] = ProcessStatsEnums.PROCESS_STATE_CACHED_EMPTY;
+
+ // Remap states, as defined by ProcessStats.java, to a reduced subset of states for data
+ // aggregation / size reduction purposes.
+ PROCESS_STATS_STATE_TO_AGGREGATED_STATE = new int[STATE_COUNT];
+ PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_PERSISTENT] =
+ ProcessStatsEnums.AGGREGATED_PROCESS_STATE_PERSISTENT;
+ PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_TOP] =
+ ProcessStatsEnums.AGGREGATED_PROCESS_STATE_TOP;
+ PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_IMPORTANT_FOREGROUND] =
+ ProcessStatsEnums.AGGREGATED_PROCESS_STATE_IMPORTANT_FOREGROUND;
+ PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_IMPORTANT_BACKGROUND] =
+ ProcessStatsEnums.AGGREGATED_PROCESS_STATE_BACKGROUND;
+ PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_BACKUP] =
+ ProcessStatsEnums.AGGREGATED_PROCESS_STATE_BACKGROUND;
+ PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_SERVICE] =
+ ProcessStatsEnums.AGGREGATED_PROCESS_STATE_BACKGROUND;
+ // "Restarting" is not a real state, so this shouldn't exist.
+ PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_SERVICE_RESTARTING] =
+ ProcessStatsEnums.AGGREGATED_PROCESS_STATE_UNKNOWN;
+ PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_RECEIVER] =
+ ProcessStatsEnums.AGGREGATED_PROCESS_STATE_RECEIVER;
+ PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_HEAVY_WEIGHT] =
+ ProcessStatsEnums.AGGREGATED_PROCESS_STATE_BACKGROUND;
+ PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_HOME] =
+ ProcessStatsEnums.AGGREGATED_PROCESS_STATE_CACHED;
+ PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_LAST_ACTIVITY] =
+ ProcessStatsEnums.AGGREGATED_PROCESS_STATE_CACHED;
+ PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_CACHED_ACTIVITY] =
+ ProcessStatsEnums.AGGREGATED_PROCESS_STATE_CACHED;
+ PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_CACHED_ACTIVITY_CLIENT] =
+ ProcessStatsEnums.AGGREGATED_PROCESS_STATE_CACHED;
+ PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_CACHED_EMPTY] =
+ ProcessStatsEnums.AGGREGATED_PROCESS_STATE_CACHED;
}
public static final String[] ADJ_SCREEN_NAMES_CSV = new String[] {
@@ -455,4 +514,29 @@ public final class DumpUtils {
}
return itemName;
}
+
+ /**
+ * Aggregate process states to reduce size of statistics logs.
+ *
+ * <p>Involves unpacking the three parts of state (process state / device memory state /
+ * screen state), manipulating the elements, then re-packing the new values into a single
+ * int. This integer is guaranteed to be unique for any given combination of state elements.
+ *
+ * @param curState current state as used in mCurState in {@class ProcessState} ie. a value
+ * combined from the process's state, the device's memory pressure state, and
+ * the device's screen on/off state.
+ * @return an integer representing the combination of screen state and process state, where
+ * process state has been aggregated.
+ */
+ public static int aggregateCurrentProcessState(int curState) {
+ int screenStateIndex = curState / (ADJ_SCREEN_MOD * STATE_COUNT);
+ // extract process state from the compound state variable (discarding memory state)
+ int procStateIndex = curState % STATE_COUNT;
+
+ // Remap process state per array above.
+ procStateIndex = PROCESS_STATS_STATE_TO_AGGREGATED_STATE[procStateIndex];
+
+ // Pack screen & process state using bit shifting
+ return (procStateIndex << 8) | screenStateIndex;
+ }
}
diff --git a/core/proto/android/server/jobscheduler.proto b/core/proto/android/server/jobscheduler.proto
index bd1bae6d83fb..b678d627d48c 100644
--- a/core/proto/android/server/jobscheduler.proto
+++ b/core/proto/android/server/jobscheduler.proto
@@ -523,6 +523,7 @@ message StateControllerProto {
optional bool is_idle = 1;
optional bool is_screen_on = 2;
optional bool is_dock_idle = 3;
+ optional bool in_car_mode = 4;
}
oneof active_tracker {
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index 60892557891b..55ea3159c44c 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -250,7 +250,7 @@ message TaskProto {
reserved 3; // activity
optional bool fills_parent = 4;
optional .android.graphics.RectProto bounds = 5;
- optional .android.graphics.RectProto displayed_bounds = 6;
+ optional .android.graphics.RectProto displayed_bounds = 6 [deprecated=true];
optional bool defer_removal = 7;
optional int32 surface_width = 8;
optional int32 surface_height = 9;
diff --git a/core/proto/android/service/procstats_enum.proto b/core/proto/android/service/procstats_enum.proto
index cc3fe5af775b..2abf3730aa9f 100644
--- a/core/proto/android/service/procstats_enum.proto
+++ b/core/proto/android/service/procstats_enum.proto
@@ -76,3 +76,27 @@ enum ServiceOperationState {
SERVICE_OPERATION_STATE_BOUND = 4;
SERVICE_OPERATION_STATE_EXECUTING = 5;
}
+
+// this enum list is from frameworks/base/core/java/com/android/internal/app/procstats/ProcessStats.java
+// and not frameworks/base/core/java/android/app/ActivityManager.java
+enum AggregatedProcessState {
+ AGGREGATED_PROCESS_STATE_UNKNOWN = 0;
+ // Persistent system process; PERSISTENT or PERSISTENT_UI in ActivityManager
+ AGGREGATED_PROCESS_STATE_PERSISTENT = 1;
+ // Top activity; actually any visible activity; TOP or TOP_SLEEPING in ActivityManager
+ AGGREGATED_PROCESS_STATE_TOP = 2;
+ // Bound top foreground process; BOUND_TOP or BOUND_FOREGROUND_SERVICE in ActivityManager
+ AGGREGATED_PROCESS_STATE_BOUND_TOP_OR_FGS = 3;
+ // Important foreground process; FOREGROUND_SERVICE in ActivityManager
+ AGGREGATED_PROCESS_STATE_FGS = 4;
+ // Important foreground process ; IMPORTANT_FOREGROUND in ActivityManager
+ AGGREGATED_PROCESS_STATE_IMPORTANT_FOREGROUND = 5;
+ // Various background processes; IMPORTANT_BACKGROUND, TRANSIENT_BACKGROUND, BACKUP, SERVICE,
+ // HEAVY_WEIGHT in ActivityManager
+ AGGREGATED_PROCESS_STATE_BACKGROUND = 6;
+ // Process running a receiver; RECEIVER in ActivityManager
+ AGGREGATED_PROCESS_STATE_RECEIVER = 7;
+ // Various cached processes; HOME, LAST_ACTIVITY, CACHED_ACTIVITY, CACHED_RECENT,
+ // CACHED_ACTIVITY_CLIENT, CACHED_EMPTY in ActivityManager
+ AGGREGATED_PROCESS_STATE_CACHED = 8;
+} \ No newline at end of file
diff --git a/core/res/res/layout/system_user_home.xml b/core/res/res/layout/system_user_home.xml
new file mode 100644
index 000000000000..8afa42338e36
--- /dev/null
+++ b/core/res/res/layout/system_user_home.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="#80000000"
+ android:forceHasOverlappingRendering="false">
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:layout_gravity="center"
+ android:layout_marginStart="16dp"
+ android:layout_marginEnd="16dp">
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textSize="20sp"
+ android:textColor="?android:attr/textColorPrimary"
+ android:text="Framework Fallback Home"/>
+ <ProgressBar
+ style="@android:style/Widget.Material.ProgressBar.Horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="12.75dp"
+ android:colorControlActivated="?android:attr/textColorPrimary"
+ android:indeterminate="true"/>
+ </LinearLayout>
+</FrameLayout>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 01d4e8c56249..717f326d0378 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1468,6 +1468,7 @@
<java-symbol type="layout" name="select_dialog" />
<java-symbol type="layout" name="simple_dropdown_hint" />
<java-symbol type="layout" name="status_bar_latest_event_content" />
+ <java-symbol type="layout" name="system_user_home" />
<java-symbol type="layout" name="text_edit_action_popup_text" />
<java-symbol type="layout" name="text_drag_thumbnail" />
<java-symbol type="layout" name="typing_filter" />
diff --git a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
index 4a33da680585..b21504c73772 100644
--- a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
+++ b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
@@ -462,6 +462,7 @@ public class AccessibilityShortcutControllerTest {
configureValidShortcutService();
configureApplicationTargetSdkVersion(Build.VERSION_CODES.R);
configureRequestAccessibilityButton();
+ configureEnabledService();
Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 1);
getController().performAccessibilityShortcut();
@@ -610,6 +611,11 @@ public class AccessibilityShortcutControllerTest {
}).when(mHandler).sendMessageAtTime(any(), anyLong());
}
+ private void configureEnabledService() throws Exception {
+ when(mAccessibilityManagerService.getEnabledAccessibilityServiceList(anyInt(), anyInt()))
+ .thenReturn(Collections.singletonList(mServiceInfo));
+ }
+
private AccessibilityShortcutController getController() {
AccessibilityShortcutController accessibilityShortcutController =
new AccessibilityShortcutController(mContext, mHandler, 0);
diff --git a/graphics/java/android/graphics/ColorSpace.java b/graphics/java/android/graphics/ColorSpace.java
index ce8ff7dc38ba..1aeafa391b41 100644
--- a/graphics/java/android/graphics/ColorSpace.java
+++ b/graphics/java/android/graphics/ColorSpace.java
@@ -199,6 +199,11 @@ public abstract class ColorSpace {
private static final float[] SRGB_PRIMARIES = { 0.640f, 0.330f, 0.300f, 0.600f, 0.150f, 0.060f };
private static final float[] NTSC_1953_PRIMARIES = { 0.67f, 0.33f, 0.21f, 0.71f, 0.14f, 0.08f };
+ /**
+ * A gray color space does not have meaningful primaries, so we use this arbitrary set.
+ */
+ private static final float[] GRAY_PRIMARIES = { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f };
+
private static final float[] ILLUMINANT_D50_XYZ = { 0.964212f, 1.0f, 0.825188f };
private static final Rgb.TransferParameters SRGB_TRANSFER_PARAMETERS =
@@ -1457,6 +1462,7 @@ public abstract class ColorSpace {
"sRGB IEC61966-2.1",
SRGB_PRIMARIES,
ILLUMINANT_D65,
+ null,
SRGB_TRANSFER_PARAMETERS,
Named.SRGB.ordinal()
);
@@ -1491,6 +1497,7 @@ public abstract class ColorSpace {
"Rec. ITU-R BT.709-5",
new float[] { 0.640f, 0.330f, 0.300f, 0.600f, 0.150f, 0.060f },
ILLUMINANT_D65,
+ null,
new Rgb.TransferParameters(1 / 1.099, 0.099 / 1.099, 1 / 4.5, 0.081, 1 / 0.45),
Named.BT709.ordinal()
);
@@ -1498,6 +1505,7 @@ public abstract class ColorSpace {
"Rec. ITU-R BT.2020-1",
new float[] { 0.708f, 0.292f, 0.170f, 0.797f, 0.131f, 0.046f },
ILLUMINANT_D65,
+ null,
new Rgb.TransferParameters(1 / 1.0993, 0.0993 / 1.0993, 1 / 4.5, 0.08145, 1 / 0.45),
Named.BT2020.ordinal()
);
@@ -1513,6 +1521,7 @@ public abstract class ColorSpace {
"Display P3",
new float[] { 0.680f, 0.320f, 0.265f, 0.690f, 0.150f, 0.060f },
ILLUMINANT_D65,
+ null,
SRGB_TRANSFER_PARAMETERS,
Named.DISPLAY_P3.ordinal()
);
@@ -1520,6 +1529,7 @@ public abstract class ColorSpace {
"NTSC (1953)",
NTSC_1953_PRIMARIES,
ILLUMINANT_C,
+ null,
new Rgb.TransferParameters(1 / 1.099, 0.099 / 1.099, 1 / 4.5, 0.081, 1 / 0.45),
Named.NTSC_1953.ordinal()
);
@@ -1527,6 +1537,7 @@ public abstract class ColorSpace {
"SMPTE-C RGB",
new float[] { 0.630f, 0.340f, 0.310f, 0.595f, 0.155f, 0.070f },
ILLUMINANT_D65,
+ null,
new Rgb.TransferParameters(1 / 1.099, 0.099 / 1.099, 1 / 4.5, 0.081, 1 / 0.45),
Named.SMPTE_C.ordinal()
);
@@ -1542,6 +1553,7 @@ public abstract class ColorSpace {
"ROMM RGB ISO 22028-2:2013",
new float[] { 0.7347f, 0.2653f, 0.1596f, 0.8404f, 0.0366f, 0.0001f },
ILLUMINANT_D50,
+ null,
new Rgb.TransferParameters(1.0, 0.0, 1 / 16.0, 0.031248, 1.8),
Named.PRO_PHOTO_RGB.ordinal()
);
@@ -2471,7 +2483,11 @@ public abstract class ColorSpace {
@NonNull @Size(min = 1) String name,
@NonNull @Size(9) float[] toXYZ,
@NonNull TransferParameters function) {
- this(name, computePrimaries(toXYZ), computeWhitePoint(toXYZ), function, MIN_ID);
+ // Note: when isGray() returns false, this passes null for the transform for
+ // consistency with other constructors, which compute the transform from the primaries
+ // and white point.
+ this(name, isGray(toXYZ) ? GRAY_PRIMARIES : computePrimaries(toXYZ),
+ computeWhitePoint(toXYZ), isGray(toXYZ) ? toXYZ : null, function, MIN_ID);
}
/**
@@ -2511,7 +2527,7 @@ public abstract class ColorSpace {
@NonNull @Size(min = 6, max = 9) float[] primaries,
@NonNull @Size(min = 2, max = 3) float[] whitePoint,
@NonNull TransferParameters function) {
- this(name, primaries, whitePoint, function, MIN_ID);
+ this(name, primaries, whitePoint, null, function, MIN_ID);
}
/**
@@ -2534,6 +2550,8 @@ public abstract class ColorSpace {
* @param name Name of the color space, cannot be null, its length must be >= 1
* @param primaries RGB primaries as an array of 6 (xy) or 9 (XYZ) floats
* @param whitePoint Reference white as an array of 2 (xy) or 3 (XYZ) floats
+ * @param transform Computed transform matrix that converts from RGB to XYZ, or
+ * {@code null} to compute it from {@code primaries} and {@code whitePoint}.
* @param function Parameters for the transfer functions
* @param id ID of this color space as an integer between {@link #MIN_ID} and {@link #MAX_ID}
*
@@ -2552,9 +2570,10 @@ public abstract class ColorSpace {
@NonNull @Size(min = 1) String name,
@NonNull @Size(min = 6, max = 9) float[] primaries,
@NonNull @Size(min = 2, max = 3) float[] whitePoint,
+ @Nullable @Size(9) float[] transform,
@NonNull TransferParameters function,
@IntRange(from = MIN_ID, to = MAX_ID) int id) {
- this(name, primaries, whitePoint, null,
+ this(name, primaries, whitePoint, transform,
function.e == 0.0 && function.f == 0.0 ?
x -> rcpResponse(x, function.a, function.b,
function.c, function.d, function.g) :
@@ -2846,7 +2865,7 @@ public abstract class ColorSpace {
*
* @return The destination array passed as a parameter
*
- * @see #getWhitePoint(float[])
+ * @see #getWhitePoint()
*/
@NonNull
@Size(min = 2)
@@ -2864,7 +2883,7 @@ public abstract class ColorSpace {
*
* @return A new non-null array of 2 floats
*
- * @see #getWhitePoint()
+ * @see #getWhitePoint(float[])
*/
@NonNull
@Size(2)
@@ -2878,12 +2897,16 @@ public abstract class ColorSpace {
* destination. The x and y components of the first primary are written
* in the array at positions 0 and 1 respectively.
*
+ * <p>Note: Some ColorSpaces represent gray profiles. The concept of
+ * primaries for such a ColorSpace does not make sense, so we use a special
+ * set of primaries that are all 1s.</p>
+ *
* @param primaries The destination array, cannot be null, its length
* must be >= 6
*
* @return The destination array passed as a parameter
*
- * @see #getPrimaries(float[])
+ * @see #getPrimaries()
*/
@NonNull
@Size(min = 6)
@@ -2898,9 +2921,13 @@ public abstract class ColorSpace {
* the destination. The x and y components of the first primary are
* written in the array at positions 0 and 1 respectively.
*
+ * <p>Note: Some ColorSpaces represent gray profiles. The concept of
+ * primaries for such a ColorSpace does not make sense, so we use a special
+ * set of primaries that are all 1s.</p>
+ *
* @return A new non-null array of 2 floats
*
- * @see #getWhitePoint()
+ * @see #getPrimaries(float[])
*/
@NonNull
@Size(6)
@@ -2922,7 +2949,7 @@ public abstract class ColorSpace {
*
* @return The destination array passed as a parameter
*
- * @see #getInverseTransform()
+ * @see #getTransform()
*/
@NonNull
@Size(min = 9)
@@ -2942,7 +2969,7 @@ public abstract class ColorSpace {
*
* @return A new array of 9 floats
*
- * @see #getInverseTransform(float[])
+ * @see #getTransform(float[])
*/
@NonNull
@Size(9)
@@ -2964,7 +2991,7 @@ public abstract class ColorSpace {
*
* @return The destination array passed as a parameter
*
- * @see #getTransform()
+ * @see #getInverseTransform()
*/
@NonNull
@Size(min = 9)
@@ -2984,7 +3011,7 @@ public abstract class ColorSpace {
*
* @return A new array of 9 floats
*
- * @see #getTransform(float[])
+ * @see #getInverseTransform(float[])
*/
@NonNull
@Size(9)
@@ -3287,6 +3314,16 @@ public abstract class ColorSpace {
return true;
}
+ /**
+ * Report whether this matrix is a special gray matrix.
+ * @param toXYZ A XYZD50 matrix. Skia uses a special form for a gray profile.
+ * @return true if this is a special gray matrix.
+ */
+ private static boolean isGray(@NonNull @Size(9) float[] toXYZ) {
+ return toXYZ.length == 9 && toXYZ[1] == 0 && toXYZ[2] == 0 && toXYZ[3] == 0
+ && toXYZ[5] == 0 && toXYZ[6] == 0 && toXYZ[7] == 0;
+ }
+
private static boolean compare(double point, @NonNull DoubleUnaryOperator a,
@NonNull DoubleUnaryOperator b) {
double rA = a.applyAsDouble(point);
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index d1b41dfccf63..9b4aebcd8aff 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -34,6 +34,7 @@ import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
import android.app.AlarmManager;
+import android.app.AppOpsManager;
import android.app.PendingIntent;
import android.app.PropertyInvalidatedCache;
import android.compat.Compatibility;
@@ -2561,7 +2562,7 @@ public class LocationManager {
}
public String getListenerId() {
- return mConsumer.getClass().getName() + "@" + System.identityHashCode(mConsumer);
+ return AppOpsManager.toReceiverId(mConsumer);
}
public synchronized void register(AlarmManager alarmManager,
@@ -2690,7 +2691,7 @@ public class LocationManager {
}
public String getListenerId() {
- return mListener.getClass().getName() + "@" + System.identityHashCode(mListener);
+ return AppOpsManager.toReceiverId(mListener);
}
public void register(@NonNull Executor executor) {
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index 25f6059c35de..bd8fb9602656 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -409,7 +409,7 @@ public final class MediaRouter2 {
// TODO: Check thread-safety
if (!mRoutes.containsKey(route.getId())) {
- notifyTransferFailed(route);
+ notifyTransferFailure(route);
return;
}
if (controller.getRoutingSessionInfo().getTransferableRoutes().contains(route.getId())) {
@@ -588,14 +588,14 @@ public final class MediaRouter2 {
if (sessionInfo == null) {
// TODO: We may need to distinguish between failure and rejection.
// One way can be introducing 'reason'.
- notifyTransferFailed(requestedRoute);
+ notifyTransferFailure(requestedRoute);
return;
} else if (!sessionInfo.getSelectedRoutes().contains(requestedRoute.getId())) {
Log.w(TAG, "The session does not contain the requested route. "
+ "(requestedRouteId=" + requestedRoute.getId()
+ ", actualRoutes=" + sessionInfo.getSelectedRoutes()
+ ")");
- notifyTransferFailed(requestedRoute);
+ notifyTransferFailure(requestedRoute);
return;
} else if (!TextUtils.equals(requestedRoute.getProviderId(),
sessionInfo.getProviderId())) {
@@ -603,7 +603,7 @@ public final class MediaRouter2 {
+ "(requested route's providerId=" + requestedRoute.getProviderId()
+ ", actual providerId=" + sessionInfo.getProviderId()
+ ")");
- notifyTransferFailed(requestedRoute);
+ notifyTransferFailure(requestedRoute);
return;
}
}
@@ -619,7 +619,7 @@ public final class MediaRouter2 {
}
}
//TODO: Determine oldController properly when transfer is launched by Output Switcher.
- notifyTransferred(matchingRequest != null ? matchingRequest.mController :
+ notifyTransfer(matchingRequest != null ? matchingRequest.mController :
getSystemController(), newController);
}
}
@@ -687,15 +687,7 @@ public final class MediaRouter2 {
return;
}
- boolean removed;
- synchronized (sRouterLock) {
- removed = mRoutingControllers.remove(uniqueSessionId, matchingController);
- }
-
- if (removed) {
- matchingController.release();
- notifyStopped(matchingController);
- }
+ matchingController.releaseInternal(/* shouldReleaseSession= */ false);
}
private List<MediaRoute2Info> filterRoutes(List<MediaRoute2Info> routes,
@@ -736,22 +728,21 @@ public final class MediaRouter2 {
}
}
- private void notifyTransferred(RoutingController oldController,
- RoutingController newController) {
+ private void notifyTransfer(RoutingController oldController, RoutingController newController) {
for (TransferCallbackRecord record: mTransferCallbackRecords) {
record.mExecutor.execute(
() -> record.mTransferCallback.onTransfer(oldController, newController));
}
}
- private void notifyTransferFailed(MediaRoute2Info route) {
+ private void notifyTransferFailure(MediaRoute2Info route) {
for (TransferCallbackRecord record: mTransferCallbackRecords) {
record.mExecutor.execute(
() -> record.mTransferCallback.onTransferFailure(route));
}
}
- private void notifyStopped(RoutingController controller) {
+ private void notifyStop(RoutingController controller) {
for (TransferCallbackRecord record: mTransferCallbackRecords) {
record.mExecutor.execute(
() -> record.mTransferCallback.onStop(controller));
@@ -1189,32 +1180,38 @@ public final class MediaRouter2 {
*/
// TODO: Add tests using {@link MediaRouter2Manager#getActiveSessions()}.
public void release() {
+ releaseInternal(/* shouldReleaseSession= */ true);
+ }
+
+ void releaseInternal(boolean shouldReleaseSession) {
synchronized (mControllerLock) {
if (mIsReleased) {
- Log.w(TAG, "release() called on released controller. Ignoring.");
+ Log.w(TAG, "releaseInternal() called on released controller. Ignoring.");
return;
}
mIsReleased = true;
}
MediaRouter2Stub stub;
- boolean removed;
synchronized (sRouterLock) {
- removed = mRoutingControllers.remove(getId(), this);
+ mRoutingControllers.remove(getId(), this);
stub = mStub;
}
- if (removed) {
- mHandler.post(() -> notifyStopped(RoutingController.this));
- }
-
- if (stub != null) {
+ if (shouldReleaseSession && stub != null) {
try {
mMediaRouterService.releaseSessionWithRouter2(stub, getId());
} catch (RemoteException ex) {
- Log.e(TAG, "Unable to notify of controller release", ex);
+ Log.e(TAG, "Unable to release session", ex);
}
}
+
+ if (Thread.currentThread() == mHandler.getLooper().getThread()) {
+ notifyStop(this);
+ } else {
+ mHandler.sendMessage(obtainMessage(MediaRouter2::notifyStop, MediaRouter2.this,
+ RoutingController.this));
+ }
}
@Override
diff --git a/media/tests/AudioPolicyTest/Android.bp b/media/tests/AudioPolicyTest/Android.bp
new file mode 100644
index 000000000000..ed3383752695
--- /dev/null
+++ b/media/tests/AudioPolicyTest/Android.bp
@@ -0,0 +1,17 @@
+android_test {
+ name: "audiopolicytest",
+ srcs: ["**/*.java"],
+ libs: [
+ "android.test.runner",
+ "android.test.base",
+ ],
+ static_libs: [
+ "mockito-target-minus-junit4",
+ "androidx.test.rules",
+ "android-ex-camera2",
+ "testng",
+ ],
+ platform_apis: true,
+ certificate: "platform",
+ resource_dirs: ["res"],
+}
diff --git a/media/tests/AudioPolicyTest/AndroidManifest.xml b/media/tests/AudioPolicyTest/AndroidManifest.xml
new file mode 100644
index 000000000000..adb058c82870
--- /dev/null
+++ b/media/tests/AudioPolicyTest/AndroidManifest.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.audiopolicytest">
+
+ <uses-permission android:name="android.permission.RECORD_AUDIO" />
+ <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
+ <uses-permission android:name="android.permission.MODIFY_AUDIO_ROUTING" />
+ <uses-permission android:name="android.permission.CHANGE_ACCESSIBILITY_VOLUME" />
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+ <activity android:label="@string/app_name" android:name="AudioPolicyTest"
+ android:screenOrientation="landscape">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
+ </application>
+
+ <!--instrumentation android:name=".AudioPolicyTestRunner"
+ android:targetPackage="com.android.audiopolicytest"
+ android:label="AudioManager policy oriented integration tests InstrumentationRunner">
+ </instrumentation-->
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.audiopolicytest"
+ android:label="AudioManager policy oriented integration tests InstrumentationRunner">
+ </instrumentation>
+</manifest>
diff --git a/media/tests/AudioPolicyTest/AndroidTest.xml b/media/tests/AudioPolicyTest/AndroidTest.xml
new file mode 100644
index 000000000000..f3ca9a165d1b
--- /dev/null
+++ b/media/tests/AudioPolicyTest/AndroidTest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Runs Media Framework Tests">
+ <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+ <option name="test-file-name" value="audiopolicytest.apk" />
+ </target_preparer>
+
+ <option name="test-tag" value="AudioPolicyTest" />
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="com.android.audiopolicytest" />
+ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+ <option name="hidden-api-checks" value="false"/>
+ </test>
+</configuration>
diff --git a/media/tests/AudioPolicyTest/res/layout/audiopolicytest.xml b/media/tests/AudioPolicyTest/res/layout/audiopolicytest.xml
new file mode 100644
index 000000000000..17fdba6f7c15
--- /dev/null
+++ b/media/tests/AudioPolicyTest/res/layout/audiopolicytest.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:orientation="vertical">
+</LinearLayout>
diff --git a/media/tests/AudioPolicyTest/res/values/strings.xml b/media/tests/AudioPolicyTest/res/values/strings.xml
new file mode 100644
index 000000000000..036592770450
--- /dev/null
+++ b/media/tests/AudioPolicyTest/res/values/strings.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <!-- name of the app [CHAR LIMIT=25]-->
+ <string name="app_name">Audio Policy APIs Tests</string>
+</resources>
diff --git a/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioManagerTest.java b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioManagerTest.java
new file mode 100644
index 000000000000..1131c623e428
--- /dev/null
+++ b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioManagerTest.java
@@ -0,0 +1,328 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.audiopolicytest;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.testng.Assert.assertThrows;
+
+import android.media.AudioAttributes;
+import android.media.AudioManager;
+import android.media.AudioSystem;
+import android.media.audiopolicy.AudioProductStrategy;
+import android.media.audiopolicy.AudioVolumeGroup;
+import android.util.Log;
+
+import com.google.common.primitives.Ints;
+
+import java.util.List;
+
+public class AudioManagerTest extends AudioVolumesTestBase {
+ private static final String TAG = "AudioManagerTest";
+
+ //-----------------------------------------------------------------
+ // Test getAudioProductStrategies and validate strategies
+ //-----------------------------------------------------------------
+ public void testGetAndValidateProductStrategies() throws Exception {
+ List<AudioProductStrategy> audioProductStrategies =
+ mAudioManager.getAudioProductStrategies();
+ assertTrue(audioProductStrategies.size() > 0);
+
+ List<AudioVolumeGroup> audioVolumeGroups = mAudioManager.getAudioVolumeGroups();
+ assertTrue(audioVolumeGroups.size() > 0);
+
+ // Validate Audio Product Strategies
+ for (final AudioProductStrategy audioProductStrategy : audioProductStrategies) {
+ AudioAttributes attributes = audioProductStrategy.getAudioAttributes();
+ int strategyStreamType =
+ audioProductStrategy.getLegacyStreamTypeForAudioAttributes(attributes);
+
+ assertTrue("Strategy shall support the attributes retrieved from its getter API",
+ audioProductStrategy.supportsAudioAttributes(attributes));
+
+ int volumeGroupId =
+ audioProductStrategy.getVolumeGroupIdForAudioAttributes(attributes);
+
+ // A strategy must be associated to a volume group
+ assertNotEquals("strategy not assigned to any volume group",
+ volumeGroupId, AudioVolumeGroup.DEFAULT_VOLUME_GROUP);
+
+ // Valid Group ?
+ AudioVolumeGroup audioVolumeGroup = null;
+ for (final AudioVolumeGroup avg : audioVolumeGroups) {
+ if (avg.getId() == volumeGroupId) {
+ audioVolumeGroup = avg;
+ break;
+ }
+ }
+ assertNotNull("Volume Group not found", audioVolumeGroup);
+
+ // Cross check: the group shall have at least one aa / stream types following the
+ // considered strategy
+ boolean strategyAttributesSupported = false;
+ for (final AudioAttributes aa : audioVolumeGroup.getAudioAttributes()) {
+ if (audioProductStrategy.supportsAudioAttributes(aa)) {
+ strategyAttributesSupported = true;
+ break;
+ }
+ }
+ assertTrue("Volume Group and Strategy mismatching", strategyAttributesSupported);
+
+ // Some Product strategy may not have corresponding stream types as they intends
+ // to address volume setting per attributes to avoid adding new stream type
+ // and going on deprecating the stream type even for volume
+ if (strategyStreamType != AudioSystem.STREAM_DEFAULT) {
+ boolean strategStreamTypeSupported = false;
+ for (final int vgStreamType : audioVolumeGroup.getLegacyStreamTypes()) {
+ if (vgStreamType == strategyStreamType) {
+ strategStreamTypeSupported = true;
+ break;
+ }
+ }
+ assertTrue("Volume Group and Strategy mismatching", strategStreamTypeSupported);
+ }
+ }
+ }
+
+ //-----------------------------------------------------------------
+ // Test getAudioVolumeGroups and validate volume groups
+ //-----------------------------------------------------------------
+
+ public void testGetAndValidateVolumeGroups() throws Exception {
+ List<AudioVolumeGroup> audioVolumeGroups = mAudioManager.getAudioVolumeGroups();
+ assertTrue(audioVolumeGroups.size() > 0);
+
+ List<AudioProductStrategy> audioProductStrategies =
+ mAudioManager.getAudioProductStrategies();
+ assertTrue(audioProductStrategies.size() > 0);
+
+ // Validate Audio Volume Groups, check all
+ for (final AudioVolumeGroup audioVolumeGroup : audioVolumeGroups) {
+ List<AudioAttributes> avgAttributes = audioVolumeGroup.getAudioAttributes();
+ int[] avgStreamTypes = audioVolumeGroup.getLegacyStreamTypes();
+
+ // for each volume group attributes, find the matching product strategy and ensure
+ // it is linked the considered volume group
+ for (final AudioAttributes aa : avgAttributes) {
+ if (aa.equals(sDefaultAttributes)) {
+ // Some volume groups may not have valid attributes, used for internal
+ // volume management like patch/rerouting
+ // so bailing out strategy retrieval from attributes
+ continue;
+ }
+ boolean isVolumeGroupAssociatedToStrategy = false;
+ for (final AudioProductStrategy strategy : audioProductStrategies) {
+ int groupId = strategy.getVolumeGroupIdForAudioAttributes(aa);
+ if (groupId != AudioVolumeGroup.DEFAULT_VOLUME_GROUP) {
+
+ assertEquals("Volume Group ID (" + audioVolumeGroup.toString()
+ + "), and Volume group ID associated to Strategy ("
+ + strategy.toString() + ") both supporting attributes "
+ + aa.toString() + " are mismatching",
+ audioVolumeGroup.getId(), groupId);
+ isVolumeGroupAssociatedToStrategy = true;
+ break;
+ }
+ }
+ assertTrue("Volume Group (" + audioVolumeGroup.toString()
+ + ") has no associated strategy for attributes " + aa.toString(),
+ isVolumeGroupAssociatedToStrategy);
+ }
+
+ // for each volume group stream type, find the matching product strategy and ensure
+ // it is linked the considered volume group
+ for (final int avgStreamType : avgStreamTypes) {
+ if (avgStreamType == AudioSystem.STREAM_DEFAULT) {
+ // Some Volume Groups may not have corresponding stream types as they
+ // intends to address volume setting per attributes to avoid adding new
+ // stream type and going on deprecating the stream type even for volume
+ // so bailing out strategy retrieval from stream type
+ continue;
+ }
+ boolean isVolumeGroupAssociatedToStrategy = false;
+ for (final AudioProductStrategy strategy : audioProductStrategies) {
+ Log.i(TAG, "strategy:" + strategy.toString());
+ int groupId = strategy.getVolumeGroupIdForLegacyStreamType(avgStreamType);
+ if (groupId != AudioVolumeGroup.DEFAULT_VOLUME_GROUP) {
+
+ assertEquals("Volume Group ID (" + audioVolumeGroup.toString()
+ + "), and Volume group ID associated to Strategy ("
+ + strategy.toString() + ") both supporting stream "
+ + AudioSystem.streamToString(avgStreamType) + "("
+ + avgStreamType + ") are mismatching",
+ audioVolumeGroup.getId(), groupId);
+ isVolumeGroupAssociatedToStrategy = true;
+ break;
+ }
+ }
+ assertTrue("Volume Group (" + audioVolumeGroup.toString()
+ + ") has no associated strategy for stream "
+ + AudioSystem.streamToString(avgStreamType) + "(" + avgStreamType + ")",
+ isVolumeGroupAssociatedToStrategy);
+ }
+ }
+ }
+
+ //-----------------------------------------------------------------
+ // Test Volume per Attributes setter/getters
+ //-----------------------------------------------------------------
+ public void testSetGetVolumePerAttributesWithInvalidAttributes() throws Exception {
+ AudioAttributes nullAttributes = null;
+
+ assertThrows(NullPointerException.class,
+ () -> mAudioManager.getMaxVolumeIndexForAttributes(nullAttributes));
+
+ assertThrows(NullPointerException.class,
+ () -> mAudioManager.getMinVolumeIndexForAttributes(nullAttributes));
+
+ assertThrows(NullPointerException.class,
+ () -> mAudioManager.getVolumeIndexForAttributes(nullAttributes));
+
+ assertThrows(NullPointerException.class,
+ () -> mAudioManager.setVolumeIndexForAttributes(
+ nullAttributes, 0 /*index*/, 0/*flags*/));
+ }
+
+ public void testSetGetVolumePerAttributes() throws Exception {
+ for (int usage : AudioAttributes.SDK_USAGES) {
+ if (usage == AudioAttributes.USAGE_UNKNOWN) {
+ continue;
+ }
+ AudioAttributes aaForUsage = new AudioAttributes.Builder().setUsage(usage).build();
+ int indexMin = 0;
+ int indexMax = 0;
+ int index = 0;
+ Exception ex = null;
+
+ try {
+ indexMax = mAudioManager.getMaxVolumeIndexForAttributes(aaForUsage);
+ } catch (Exception e) {
+ ex = e; // unexpected
+ }
+ assertNull("Exception was thrown for valid attributes", ex);
+ ex = null;
+ try {
+ indexMin = mAudioManager.getMinVolumeIndexForAttributes(aaForUsage);
+ } catch (Exception e) {
+ ex = e; // unexpected
+ }
+ assertNull("Exception was thrown for valid attributes", ex);
+ ex = null;
+ try {
+ index = mAudioManager.getVolumeIndexForAttributes(aaForUsage);
+ } catch (Exception e) {
+ ex = e; // unexpected
+ }
+ assertNull("Exception was thrown for valid attributes", ex);
+ ex = null;
+ try {
+ mAudioManager.setVolumeIndexForAttributes(aaForUsage, indexMin, 0/*flags*/);
+ } catch (Exception e) {
+ ex = e; // unexpected
+ }
+ assertNull("Exception was thrown for valid attributes", ex);
+
+ index = mAudioManager.getVolumeIndexForAttributes(aaForUsage);
+ assertEquals(index, indexMin);
+
+ mAudioManager.setVolumeIndexForAttributes(aaForUsage, indexMax, 0/*flags*/);
+ index = mAudioManager.getVolumeIndexForAttributes(aaForUsage);
+ assertEquals(index, indexMax);
+ }
+ }
+
+ //-----------------------------------------------------------------
+ // Test register/unregister VolumeGroupCallback
+ //-----------------------------------------------------------------
+ public void testVolumeGroupCallback() throws Exception {
+ List<AudioVolumeGroup> audioVolumeGroups = mAudioManager.getAudioVolumeGroups();
+ assertTrue(audioVolumeGroups.size() > 0);
+
+ AudioVolumeGroupCallbackHelper vgCbReceiver = new AudioVolumeGroupCallbackHelper();
+ mAudioManager.registerVolumeGroupCallback(mContext.getMainExecutor(), vgCbReceiver);
+
+ final List<Integer> publicStreams = Ints.asList(PUBLIC_STREAM_TYPES);
+ try {
+ // Validate Audio Volume Groups callback reception
+ for (final AudioVolumeGroup audioVolumeGroup : audioVolumeGroups) {
+ int volumeGroupId = audioVolumeGroup.getId();
+
+ // Set the receiver to filter only the current group callback
+ vgCbReceiver.setExpectedVolumeGroup(volumeGroupId);
+
+ List<AudioAttributes> avgAttributes = audioVolumeGroup.getAudioAttributes();
+ int[] avgStreamTypes = audioVolumeGroup.getLegacyStreamTypes();
+
+ int index = 0;
+ int indexMax = 0;
+ int indexMin = 0;
+
+ // Set the volume per attributes (if valid) and wait the callback
+ for (final AudioAttributes aa : avgAttributes) {
+ if (aa.equals(sDefaultAttributes)) {
+ // Some volume groups may not have valid attributes, used for internal
+ // volume management like patch/rerouting
+ // so bailing out strategy retrieval from attributes
+ continue;
+ }
+ index = mAudioManager.getVolumeIndexForAttributes(aa);
+ indexMax = mAudioManager.getMaxVolumeIndexForAttributes(aa);
+ indexMin = mAudioManager.getMinVolumeIndexForAttributes(aa);
+ index = incrementVolumeIndex(index, indexMin, indexMax);
+
+ vgCbReceiver.setExpectedVolumeGroup(volumeGroupId);
+ mAudioManager.setVolumeIndexForAttributes(aa, index, 0/*flags*/);
+ assertTrue(vgCbReceiver.waitForExpectedVolumeGroupChanged(
+ AudioVolumeGroupCallbackHelper.ASYNC_TIMEOUT_MS));
+
+ int readIndex = mAudioManager.getVolumeIndexForAttributes(aa);
+ assertEquals(readIndex, index);
+ }
+ // Set the volume per stream type (if valid) and wait the callback
+ for (final int avgStreamType : avgStreamTypes) {
+ if (avgStreamType == AudioSystem.STREAM_DEFAULT) {
+ // Some Volume Groups may not have corresponding stream types as they
+ // intends to address volume setting per attributes to avoid adding new
+ // stream type and going on deprecating the stream type even for volume
+ // so bailing out strategy retrieval from stream type
+ continue;
+ }
+ if (!publicStreams.contains(avgStreamType)
+ || avgStreamType == AudioManager.STREAM_ACCESSIBILITY) {
+ // Limit scope of test to public stream that do not require any
+ // permission (e.g. Changing ACCESSIBILITY is subject to permission).
+ continue;
+ }
+ index = mAudioManager.getStreamVolume(avgStreamType);
+ indexMax = mAudioManager.getStreamMaxVolume(avgStreamType);
+ indexMin = mAudioManager.getStreamMinVolumeInt(avgStreamType);
+ index = incrementVolumeIndex(index, indexMin, indexMax);
+
+ vgCbReceiver.setExpectedVolumeGroup(volumeGroupId);
+ mAudioManager.setStreamVolume(avgStreamType, index, 0/*flags*/);
+ assertTrue(vgCbReceiver.waitForExpectedVolumeGroupChanged(
+ AudioVolumeGroupCallbackHelper.ASYNC_TIMEOUT_MS));
+
+ int readIndex = mAudioManager.getStreamVolume(avgStreamType);
+ assertEquals(index, readIndex);
+ }
+ }
+ } finally {
+ mAudioManager.unregisterVolumeGroupCallback(vgCbReceiver);
+ }
+ }
+}
diff --git a/core/java/android/view/inline/InlinePresentationSpec.aidl b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioPolicyTest.java
index 680ee4e14b54..e0c7b223a2e4 100644
--- a/core/java/android/view/inline/InlinePresentationSpec.aidl
+++ b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioPolicyTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,10 +14,25 @@
* limitations under the License.
*/
-package android.view.inline;
+package com.android.audiopolicytest;
-/**
- * @hide
- * @removed
- */
-parcelable InlinePresentationSpec;
+import android.app.Activity;
+import android.os.Bundle;
+
+public class AudioPolicyTest extends Activity {
+
+ public AudioPolicyTest() {
+ }
+
+ /** Called when the activity is first created. */
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ setContentView(R.layout.audiopolicytest);
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ }
+}
diff --git a/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioProductStrategyTest.java b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioProductStrategyTest.java
new file mode 100644
index 000000000000..c0f596b974e1
--- /dev/null
+++ b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioProductStrategyTest.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.audiopolicytest;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import android.media.AudioAttributes;
+import android.media.AudioSystem;
+import android.media.audiopolicy.AudioProductStrategy;
+import android.media.audiopolicy.AudioVolumeGroup;
+import android.util.Log;
+
+import java.util.List;
+
+public class AudioProductStrategyTest extends AudioVolumesTestBase {
+ private static final String TAG = "AudioProductStrategyTest";
+
+ //-----------------------------------------------------------------
+ // Test getAudioProductStrategies and validate strategies
+ //-----------------------------------------------------------------
+ public void testGetProductStrategies() throws Exception {
+ List<AudioProductStrategy> audioProductStrategies =
+ AudioProductStrategy.getAudioProductStrategies();
+
+ assertNotNull(audioProductStrategies);
+ assertTrue(audioProductStrategies.size() > 0);
+
+ for (final AudioProductStrategy aps : audioProductStrategies) {
+ assertTrue(aps.getId() >= 0);
+
+ AudioAttributes aa = aps.getAudioAttributes();
+ assertNotNull(aa);
+
+ // Ensure API consistency
+ assertTrue(aps.supportsAudioAttributes(aa));
+
+ int streamType = aps.getLegacyStreamTypeForAudioAttributes(aa);
+ if (streamType == AudioSystem.STREAM_DEFAULT) {
+ // bailing out test for volume group APIs consistency
+ continue;
+ }
+ final int volumeGroupFromStream = aps.getVolumeGroupIdForLegacyStreamType(streamType);
+ final int volumeGroupFromAttributes = aps.getVolumeGroupIdForAudioAttributes(aa);
+ assertNotEquals(volumeGroupFromStream, AudioVolumeGroup.DEFAULT_VOLUME_GROUP);
+ assertEquals(volumeGroupFromStream, volumeGroupFromAttributes);
+ }
+ }
+
+ //-----------------------------------------------------------------
+ // Test stream to/from attributes conversion
+ //-----------------------------------------------------------------
+ public void testAudioAttributesFromStreamTypes() throws Exception {
+ List<AudioProductStrategy> audioProductStrategies =
+ AudioProductStrategy.getAudioProductStrategies();
+
+ assertNotNull(audioProductStrategies);
+ assertTrue(audioProductStrategies.size() > 0);
+
+ for (final int streamType : PUBLIC_STREAM_TYPES) {
+ AudioAttributes aaFromStreamType =
+ AudioProductStrategy.getAudioAttributesForStrategyWithLegacyStreamType(
+ streamType);
+
+ // No strategy found for this stream type or no attributes defined for the strategy
+ // hosting this stream type; Bailing out the test, just ensure that any request
+ // for reciproque API with the unknown attributes would return default stream
+ // for volume control, aka STREAM_MUSIC.
+ if (aaFromStreamType.equals(sInvalidAttributes)) {
+ assertEquals(AudioSystem.STREAM_MUSIC,
+ AudioProductStrategy.getLegacyStreamTypeForStrategyWithAudioAttributes(
+ aaFromStreamType));
+ } else {
+ // Attributes are valid, i.e. a strategy was found supporting this stream type
+ // with valid attributes. Ensure reciproque works fine
+ int streamTypeFromAttributes =
+ AudioProductStrategy.getLegacyStreamTypeForStrategyWithAudioAttributes(
+ aaFromStreamType);
+ assertEquals("stream " + AudioSystem.streamToString(streamType) + "("
+ + streamType + ") expected to match attributes "
+ + aaFromStreamType.toString() + " got instead stream "
+ + AudioSystem.streamToString(streamTypeFromAttributes) + "("
+ + streamTypeFromAttributes + ") expected to match attributes ",
+ streamType, streamTypeFromAttributes);
+ }
+
+ // Now identify the strategy supporting this stream type, ensure uniqueness
+ boolean strategyFound = false;
+ for (final AudioProductStrategy aps : audioProductStrategies) {
+ AudioAttributes aaFromAps =
+ aps.getAudioAttributesForLegacyStreamType(streamType);
+
+ if (aaFromAps == null) {
+ // not this one...
+ continue;
+ }
+ // Got it!
+ assertFalse("Unique ProductStrategy shall match for a given stream type",
+ strategyFound);
+ strategyFound = true;
+
+ // Ensure getters aligned
+ assertEquals(aaFromStreamType, aaFromAps);
+ assertTrue(aps.supportsAudioAttributes(aaFromStreamType));
+
+ // Ensure reciproque works fine
+ assertEquals(streamType,
+ aps.getLegacyStreamTypeForAudioAttributes(aaFromStreamType));
+
+ // Ensure consistency of volume group getter API
+ final int volumeGroupFromStream =
+ aps.getVolumeGroupIdForLegacyStreamType(streamType);
+ final int volumeGroupFromAttributes =
+ aps.getVolumeGroupIdForAudioAttributes(aaFromStreamType);
+ assertNotEquals(volumeGroupFromStream, AudioVolumeGroup.DEFAULT_VOLUME_GROUP);
+ assertEquals(volumeGroupFromStream, volumeGroupFromAttributes);
+ }
+ if (!strategyFound) {
+ // No strategy found, ensure volume control is MUSIC
+ assertEquals(AudioSystem.STREAM_MUSIC,
+ AudioProductStrategy.getLegacyStreamTypeForStrategyWithAudioAttributes(
+ aaFromStreamType));
+ }
+ }
+ }
+
+ public void testAudioAttributesToStreamTypes() throws Exception {
+ List<AudioProductStrategy> audioProductStrategies =
+ AudioProductStrategy.getAudioProductStrategies();
+
+ assertNotNull(audioProductStrategies);
+ assertTrue(audioProductStrategies.size() > 0);
+
+ for (int usage : AudioAttributes.SDK_USAGES) {
+ AudioAttributes aaForUsage = new AudioAttributes.Builder().setUsage(usage).build();
+
+ int streamTypeFromUsage =
+ AudioProductStrategy.getLegacyStreamTypeForStrategyWithAudioAttributes(
+ aaForUsage);
+
+ // Cannot be undefined, always shall fall back on a valid stream type
+ // to be able to control the volume
+ assertNotEquals(streamTypeFromUsage, AudioSystem.STREAM_DEFAULT);
+
+ Log.w(TAG, "GUSTAVE aaForUsage=" + aaForUsage.toString());
+
+ // Now identify the strategy hosting these Audio Attributes and ensure informations
+ // matches.
+ // Now identify the strategy supporting this stream type, ensure uniqueness
+ boolean strategyFound = false;
+ for (final AudioProductStrategy aps : audioProductStrategies) {
+ if (!aps.supportsAudioAttributes(aaForUsage)) {
+ // Not this one
+ continue;
+ }
+ // Got it!
+ String msg = "Unique ProductStrategy shall match for a given audio attributes "
+ + aaForUsage.toString() + " already associated also matches with"
+ + aps.toString();
+ assertFalse(msg, strategyFound);
+ strategyFound = true;
+
+ // It may not return the expected stream type if the strategy does not have
+ // associated stream type.
+ // Behavior of member function getLegacyStreamTypeForAudioAttributes is
+ // different than getLegacyStreamTypeForStrategyWithAudioAttributes since it
+ // does not fallback on MUSIC stream type for volume operation
+ int streamTypeFromAps = aps.getLegacyStreamTypeForAudioAttributes(aaForUsage);
+ if (streamTypeFromAps == AudioSystem.STREAM_DEFAULT) {
+ // No stream type assigned to this strategy
+ // Expect static API to return default stream type for volume (aka MUSIC)
+ assertEquals("Strategy (" + aps.toString() + ") has no associated stream "
+ + ", must fallback on MUSIC stream as default",
+ streamTypeFromUsage, AudioSystem.STREAM_MUSIC);
+ } else {
+ assertEquals("Attributes " + aaForUsage.toString() + " associated to stream "
+ + AudioSystem.streamToString(streamTypeFromUsage)
+ + " are supported by strategy (" + aps.toString() + ") which reports "
+ + " these attributes are associated to stream "
+ + AudioSystem.streamToString(streamTypeFromAps),
+ streamTypeFromUsage, streamTypeFromAps);
+
+ // Ensure consistency of volume group getter API
+ int volumeGroupFromStream =
+ aps.getVolumeGroupIdForLegacyStreamType(streamTypeFromAps);
+ int volumeGroupFromAttributes =
+ aps.getVolumeGroupIdForAudioAttributes(aaForUsage);
+ assertNotEquals(
+ volumeGroupFromStream, AudioVolumeGroup.DEFAULT_VOLUME_GROUP);
+ assertEquals(volumeGroupFromStream, volumeGroupFromAttributes);
+ }
+ }
+ if (!strategyFound) {
+ // No strategy found for the given attributes, the expected stream must be MUSIC
+ assertEquals(streamTypeFromUsage, AudioSystem.STREAM_MUSIC);
+ }
+ }
+ }
+}
diff --git a/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioVolumeGroupCallbackHelper.java b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioVolumeGroupCallbackHelper.java
new file mode 100644
index 000000000000..0c1d52c57020
--- /dev/null
+++ b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioVolumeGroupCallbackHelper.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.audiopolicytest;
+
+import static org.junit.Assert.assertNotNull;
+
+import android.media.AudioManager;
+import android.util.Log;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+
+final class AudioVolumeGroupCallbackHelper extends AudioManager.VolumeGroupCallback {
+ private static final String TAG = "AudioVolumeGroupCallbackHelper";
+ public static final long ASYNC_TIMEOUT_MS = 800;
+
+ private int mExpectedVolumeGroupId;
+
+ private CountDownLatch mVolumeGroupChanged = null;
+
+ void setExpectedVolumeGroup(int group) {
+ mVolumeGroupChanged = new CountDownLatch(1);
+ mExpectedVolumeGroupId = group;
+ }
+
+ @Override
+ public void onAudioVolumeGroupChanged(int group, int flags) {
+ if (group != mExpectedVolumeGroupId) {
+ return;
+ }
+ if (mVolumeGroupChanged == null) {
+ Log.wtf(TAG, "Received callback but object not initialized");
+ return;
+ }
+ if (mVolumeGroupChanged.getCount() <= 0) {
+ Log.i(TAG, "callback for group: " + group + " already received");
+ return;
+ }
+ mVolumeGroupChanged.countDown();
+ }
+
+ public boolean waitForExpectedVolumeGroupChanged(long timeOutMs) {
+ assertNotNull("Call first setExpectedVolumeGroup before waiting...", mVolumeGroupChanged);
+ boolean timeoutReached = false;
+ if (mVolumeGroupChanged.getCount() == 0) {
+ // done already...
+ return true;
+ }
+ try {
+ timeoutReached = !mVolumeGroupChanged.await(ASYNC_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) { }
+ return !timeoutReached;
+ }
+}
diff --git a/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioVolumeGroupChangeHandlerTest.java b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioVolumeGroupChangeHandlerTest.java
new file mode 100644
index 000000000000..221f1f7fef17
--- /dev/null
+++ b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioVolumeGroupChangeHandlerTest.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.audiopolicytest;
+
+import static org.junit.Assert.assertEquals;
+import static org.testng.Assert.assertThrows;
+
+import android.media.AudioAttributes;
+import android.media.AudioManager;
+import android.media.audiopolicy.AudioVolumeGroup;
+import android.media.audiopolicy.AudioVolumeGroupChangeHandler;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class AudioVolumeGroupChangeHandlerTest extends AudioVolumesTestBase {
+ private static final String TAG = "AudioVolumeGroupChangeHandlerTest";
+
+ public void testRegisterInvalidCallback() throws Exception {
+ final AudioVolumeGroupChangeHandler audioAudioVolumeGroupChangedHandler =
+ new AudioVolumeGroupChangeHandler();
+
+ audioAudioVolumeGroupChangedHandler.init();
+
+ assertThrows(NullPointerException.class, () -> {
+ AudioManager.VolumeGroupCallback nullCb = null;
+ audioAudioVolumeGroupChangedHandler.registerListener(nullCb);
+ });
+ }
+
+ public void testUnregisterInvalidCallback() throws Exception {
+ final AudioVolumeGroupChangeHandler audioAudioVolumeGroupChangedHandler =
+ new AudioVolumeGroupChangeHandler();
+
+ audioAudioVolumeGroupChangedHandler.init();
+
+ final AudioVolumeGroupCallbackHelper cb = new AudioVolumeGroupCallbackHelper();
+ audioAudioVolumeGroupChangedHandler.registerListener(cb);
+
+ assertThrows(NullPointerException.class, () -> {
+ AudioManager.VolumeGroupCallback nullCb = null;
+ audioAudioVolumeGroupChangedHandler.unregisterListener(nullCb);
+ });
+ audioAudioVolumeGroupChangedHandler.unregisterListener(cb);
+ }
+
+ public void testRegisterUnregisterCallback() throws Exception {
+ final AudioVolumeGroupChangeHandler audioAudioVolumeGroupChangedHandler =
+ new AudioVolumeGroupChangeHandler();
+
+ audioAudioVolumeGroupChangedHandler.init();
+ final AudioVolumeGroupCallbackHelper validCb = new AudioVolumeGroupCallbackHelper();
+
+ // Should not assert, otherwise test will fail
+ audioAudioVolumeGroupChangedHandler.registerListener(validCb);
+
+ // Should not assert, otherwise test will fail
+ audioAudioVolumeGroupChangedHandler.unregisterListener(validCb);
+ }
+
+ public void testCallbackReceived() throws Exception {
+ final AudioVolumeGroupChangeHandler audioAudioVolumeGroupChangedHandler =
+ new AudioVolumeGroupChangeHandler();
+
+ audioAudioVolumeGroupChangedHandler.init();
+
+ final AudioVolumeGroupCallbackHelper validCb = new AudioVolumeGroupCallbackHelper();
+ audioAudioVolumeGroupChangedHandler.registerListener(validCb);
+
+ List<AudioVolumeGroup> audioVolumeGroups = mAudioManager.getAudioVolumeGroups();
+ assertTrue(audioVolumeGroups.size() > 0);
+
+ try {
+ for (final AudioVolumeGroup audioVolumeGroup : audioVolumeGroups) {
+ int volumeGroupId = audioVolumeGroup.getId();
+
+ List<AudioAttributes> avgAttributes = audioVolumeGroup.getAudioAttributes();
+ // Set the volume per attributes (if valid) and wait the callback
+ if (avgAttributes.size() == 0 || avgAttributes.get(0).equals(sDefaultAttributes)) {
+ // Some volume groups may not have valid attributes, used for internal
+ // volume management like patch/rerouting
+ // so bailing out strategy retrieval from attributes
+ continue;
+ }
+ final AudioAttributes aa = avgAttributes.get(0);
+
+ int index = mAudioManager.getVolumeIndexForAttributes(aa);
+ int indexMax = mAudioManager.getMaxVolumeIndexForAttributes(aa);
+ int indexMin = mAudioManager.getMinVolumeIndexForAttributes(aa);
+
+ final int indexForAa = incrementVolumeIndex(index, indexMin, indexMax);
+
+ // Set the receiver to filter only the current group callback
+ validCb.setExpectedVolumeGroup(volumeGroupId);
+ mAudioManager.setVolumeIndexForAttributes(aa, indexForAa, 0/*flags*/);
+ assertTrue(validCb.waitForExpectedVolumeGroupChanged(
+ AudioVolumeGroupCallbackHelper.ASYNC_TIMEOUT_MS));
+
+ final int readIndex = mAudioManager.getVolumeIndexForAttributes(aa);
+ assertEquals(readIndex, indexForAa);
+ }
+ } finally {
+ audioAudioVolumeGroupChangedHandler.unregisterListener(validCb);
+ }
+ }
+
+ public void testMultipleCallbackReceived() throws Exception {
+
+ final AudioVolumeGroupChangeHandler audioAudioVolumeGroupChangedHandler =
+ new AudioVolumeGroupChangeHandler();
+
+ audioAudioVolumeGroupChangedHandler.init();
+
+ final int callbackCount = 10;
+ final List<AudioVolumeGroupCallbackHelper> validCbs =
+ new ArrayList<AudioVolumeGroupCallbackHelper>();
+ for (int i = 0; i < callbackCount; i++) {
+ validCbs.add(new AudioVolumeGroupCallbackHelper());
+ }
+ for (final AudioVolumeGroupCallbackHelper cb : validCbs) {
+ audioAudioVolumeGroupChangedHandler.registerListener(cb);
+ }
+
+ List<AudioVolumeGroup> audioVolumeGroups = mAudioManager.getAudioVolumeGroups();
+ assertTrue(audioVolumeGroups.size() > 0);
+
+ try {
+ for (final AudioVolumeGroup audioVolumeGroup : audioVolumeGroups) {
+ int volumeGroupId = audioVolumeGroup.getId();
+
+ List<AudioAttributes> avgAttributes = audioVolumeGroup.getAudioAttributes();
+ // Set the volume per attributes (if valid) and wait the callback
+ if (avgAttributes.size() == 0 || avgAttributes.get(0).equals(sDefaultAttributes)) {
+ // Some volume groups may not have valid attributes, used for internal
+ // volume management like patch/rerouting
+ // so bailing out strategy retrieval from attributes
+ continue;
+ }
+ AudioAttributes aa = avgAttributes.get(0);
+
+ int index = mAudioManager.getVolumeIndexForAttributes(aa);
+ int indexMax = mAudioManager.getMaxVolumeIndexForAttributes(aa);
+ int indexMin = mAudioManager.getMinVolumeIndexForAttributes(aa);
+
+ final int indexForAa = incrementVolumeIndex(index, indexMin, indexMax);
+
+ // Set the receiver to filter only the current group callback
+ for (final AudioVolumeGroupCallbackHelper cb : validCbs) {
+ cb.setExpectedVolumeGroup(volumeGroupId);
+ }
+ mAudioManager.setVolumeIndexForAttributes(aa, indexForAa, 0/*flags*/);
+
+ for (final AudioVolumeGroupCallbackHelper cb : validCbs) {
+ assertTrue(cb.waitForExpectedVolumeGroupChanged(
+ AudioVolumeGroupCallbackHelper.ASYNC_TIMEOUT_MS));
+ }
+ int readIndex = mAudioManager.getVolumeIndexForAttributes(aa);
+ assertEquals(readIndex, indexForAa);
+ }
+ } finally {
+ for (final AudioVolumeGroupCallbackHelper cb : validCbs) {
+ audioAudioVolumeGroupChangedHandler.unregisterListener(cb);
+ }
+ }
+ }
+}
diff --git a/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioVolumeGroupTest.java b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioVolumeGroupTest.java
new file mode 100644
index 000000000000..84b24b8fcab3
--- /dev/null
+++ b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioVolumeGroupTest.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.audiopolicytest;
+
+import static org.junit.Assert.assertNotEquals;
+
+import android.media.AudioAttributes;
+import android.media.AudioSystem;
+import android.media.audiopolicy.AudioProductStrategy;
+import android.media.audiopolicy.AudioVolumeGroup;
+
+import java.util.List;
+
+public class AudioVolumeGroupTest extends AudioVolumesTestBase {
+ private static final String TAG = "AudioVolumeGroupTest";
+
+ //-----------------------------------------------------------------
+ // Test getAudioVolumeGroups and validate groud id
+ //-----------------------------------------------------------------
+ public void testGetVolumeGroupsFromNonServiceCaller() throws Exception {
+ // The transaction behind getAudioVolumeGroups will fail. Check is done at binder level
+ // with policy service. Error is not reported, the list is just empty.
+ // Request must come from service components
+ List<AudioVolumeGroup> audioVolumeGroup = AudioVolumeGroup.getAudioVolumeGroups();
+
+ assertNotNull(audioVolumeGroup);
+ assertEquals(audioVolumeGroup.size(), 0);
+ }
+
+ //-----------------------------------------------------------------
+ // Test getAudioVolumeGroups and validate groud id
+ //-----------------------------------------------------------------
+ public void testGetVolumeGroups() throws Exception {
+ // Through AudioManager, the transaction behind getAudioVolumeGroups will succeed
+ final List<AudioVolumeGroup> audioVolumeGroup = mAudioManager.getAudioVolumeGroups();
+ assertNotNull(audioVolumeGroup);
+ assertTrue(audioVolumeGroup.size() > 0);
+
+ final List<AudioProductStrategy> audioProductStrategies =
+ mAudioManager.getAudioProductStrategies();
+ assertTrue(audioProductStrategies.size() > 0);
+
+ for (final AudioVolumeGroup avg : audioVolumeGroup) {
+ int avgId = avg.getId();
+ assertNotEquals(avgId, AudioVolumeGroup.DEFAULT_VOLUME_GROUP);
+
+ List<AudioAttributes> avgAttributes = avg.getAudioAttributes();
+ assertNotNull(avgAttributes);
+
+ final int[] avgStreamTypes = avg.getLegacyStreamTypes();
+ assertNotNull(avgStreamTypes);
+
+ // for each volume group attributes, find the matching product strategy and ensure
+ // it is linked the considered volume group
+ for (final AudioAttributes aa : avgAttributes) {
+ if (aa.equals(sDefaultAttributes)) {
+ // Some volume groups may not have valid attributes, used for internal
+ // volume management like patch/rerouting
+ // so bailing out strategy retrieval from attributes
+ continue;
+ }
+ boolean isVolumeGroupAssociatedToStrategy = false;
+ for (final AudioProductStrategy aps : audioProductStrategies) {
+ int groupId = aps.getVolumeGroupIdForAudioAttributes(aa);
+ if (groupId != AudioVolumeGroup.DEFAULT_VOLUME_GROUP) {
+ // Note that Audio Product Strategies are priority ordered, and the
+ // the first one matching the AudioAttributes will be used to identify
+ // the volume group associated to the request.
+ assertTrue(aps.supportsAudioAttributes(aa));
+ assertEquals("Volume Group ID (" + avg.toString()
+ + "), and Volume group ID associated to Strategy ("
+ + aps.toString() + ") both supporting attributes "
+ + aa.toString() + " are mismatching",
+ avgId, groupId);
+ isVolumeGroupAssociatedToStrategy = true;
+ break;
+ }
+ }
+ assertTrue("Volume Group (" + avg.toString()
+ + ") has no associated strategy for attributes " + aa.toString(),
+ isVolumeGroupAssociatedToStrategy);
+ }
+
+ // for each volume group stream type, find the matching product strategy and ensure
+ // it is linked the considered volume group
+ for (final int avgStreamType : avgStreamTypes) {
+ if (avgStreamType == AudioSystem.STREAM_DEFAULT) {
+ // Some Volume Groups may not have corresponding stream types as they
+ // intends to address volume setting per attributes to avoid adding new
+ // stream type and going on deprecating the stream type even for volume
+ // so bailing out strategy retrieval from stream type
+ continue;
+ }
+ boolean isVolumeGroupAssociatedToStrategy = false;
+ for (final AudioProductStrategy aps : audioProductStrategies) {
+ int groupId = aps.getVolumeGroupIdForLegacyStreamType(avgStreamType);
+ if (groupId != AudioVolumeGroup.DEFAULT_VOLUME_GROUP) {
+
+ assertEquals("Volume Group ID (" + avg.toString()
+ + "), and Volume group ID associated to Strategy ("
+ + aps.toString() + ") both supporting stream "
+ + AudioSystem.streamToString(avgStreamType) + "("
+ + avgStreamType + ") are mismatching",
+ avgId, groupId);
+
+ isVolumeGroupAssociatedToStrategy = true;
+ break;
+ }
+ }
+ assertTrue("Volume Group (" + avg.toString()
+ + ") has no associated strategy for stream "
+ + AudioSystem.streamToString(avgStreamType) + "(" + avgStreamType + ")",
+ isVolumeGroupAssociatedToStrategy);
+ }
+ }
+ }
+}
diff --git a/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioVolumesTestBase.java b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioVolumesTestBase.java
new file mode 100644
index 000000000000..a17d65cf7376
--- /dev/null
+++ b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioVolumesTestBase.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.audiopolicytest;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.media.AudioAttributes;
+import android.media.AudioManager;
+import android.media.audiopolicy.AudioProductStrategy;
+import android.media.audiopolicy.AudioVolumeGroup;
+import android.test.ActivityInstrumentationTestCase2;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class AudioVolumesTestBase extends ActivityInstrumentationTestCase2<AudioPolicyTest> {
+ public AudioManager mAudioManager;
+ Context mContext;
+ private Map<Integer, Integer> mOriginalStreamVolumes = new HashMap<>();
+ private Map<Integer, Integer> mOriginalVolumeGroupVolumes = new HashMap<>();
+
+ // Default matches the invalid (empty) attributes from native.
+ // The difference is the input source default which is not aligned between native and java
+ public static final AudioAttributes sDefaultAttributes =
+ AudioProductStrategy.sDefaultAttributes;
+
+ public static final AudioAttributes sInvalidAttributes = new AudioAttributes.Builder().build();
+
+ public final int[] PUBLIC_STREAM_TYPES = { AudioManager.STREAM_VOICE_CALL,
+ AudioManager.STREAM_SYSTEM, AudioManager.STREAM_RING, AudioManager.STREAM_MUSIC,
+ AudioManager.STREAM_ALARM, AudioManager.STREAM_NOTIFICATION,
+ AudioManager.STREAM_DTMF, AudioManager.STREAM_ACCESSIBILITY };
+
+ public AudioVolumesTestBase() {
+ super("com.android.audiopolicytest", AudioPolicyTest.class);
+ }
+
+ /**
+ * <p>Note: must be called with shell permission (MODIFY_AUDIO_ROUTING)
+ */
+ private void storeAllVolumes() {
+ List<AudioVolumeGroup> audioVolumeGroups = mAudioManager.getAudioVolumeGroups();
+ for (final AudioVolumeGroup avg : audioVolumeGroups) {
+ if (avg.getAudioAttributes().isEmpty()) {
+ // some volume group may not supports volume control per attributes
+ // like rerouting/patch since these groups are internal to audio policy manager
+ continue;
+ }
+ AudioAttributes avgAttributes = sDefaultAttributes;
+ for (final AudioAttributes aa : avg.getAudioAttributes()) {
+ if (!aa.equals(AudioProductStrategy.sDefaultAttributes)) {
+ avgAttributes = aa;
+ break;
+ }
+ }
+ if (avgAttributes.equals(sDefaultAttributes)) {
+ // This shall not happen, however, not purpose of this base class.
+ // so bailing out.
+ continue;
+ }
+ mOriginalVolumeGroupVolumes.put(
+ avg.getId(), mAudioManager.getVolumeIndexForAttributes(avgAttributes));
+ }
+ }
+
+ /**
+ * <p>Note: must be called with shell permission (MODIFY_AUDIO_ROUTING)
+ */
+ private void restoreAllVolumes() {
+ List<AudioVolumeGroup> audioVolumeGroups = mAudioManager.getAudioVolumeGroups();
+ for (Map.Entry<Integer, Integer> e : mOriginalVolumeGroupVolumes.entrySet()) {
+ for (final AudioVolumeGroup avg : audioVolumeGroups) {
+ if (avg.getId() == e.getKey()) {
+ assertTrue(!avg.getAudioAttributes().isEmpty());
+ AudioAttributes avgAttributes = sDefaultAttributes;
+ for (final AudioAttributes aa : avg.getAudioAttributes()) {
+ if (!aa.equals(AudioProductStrategy.sDefaultAttributes)) {
+ avgAttributes = aa;
+ break;
+ }
+ }
+ assertTrue(!avgAttributes.equals(sDefaultAttributes));
+ mAudioManager.setVolumeIndexForAttributes(
+ avgAttributes, e.getValue(), AudioManager.FLAG_ALLOW_RINGER_MODES);
+ }
+ }
+ }
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ mContext = getActivity();
+ mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
+
+ assertEquals(PackageManager.PERMISSION_GRANTED,
+ mContext.checkSelfPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING));
+
+ // Store the original volumes that that they can be recovered in tearDown().
+ mOriginalStreamVolumes.clear();
+ for (int streamType : PUBLIC_STREAM_TYPES) {
+ mOriginalStreamVolumes.put(streamType, mAudioManager.getStreamVolume(streamType));
+ }
+ // Store the original volume per attributes so that they can be recovered in tearDown()
+ mOriginalVolumeGroupVolumes.clear();
+ storeAllVolumes();
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+
+ // Recover the volume and the ringer mode that the test may have overwritten.
+ for (Map.Entry<Integer, Integer> e : mOriginalStreamVolumes.entrySet()) {
+ mAudioManager.setStreamVolume(e.getKey(), e.getValue(),
+ AudioManager.FLAG_ALLOW_RINGER_MODES);
+ }
+
+ // Recover the original volume per attributes
+ restoreAllVolumes();
+ }
+
+ public static int resetVolumeIndex(int indexMin, int indexMax) {
+ return (indexMax + indexMin) / 2;
+ }
+
+ public static int incrementVolumeIndex(int index, int indexMin, int indexMax) {
+ return (index + 1 > indexMax) ? resetVolumeIndex(indexMin, indexMax) : ++index;
+ }
+}
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java
index 1814fd0403d0..a4bb916b16ea 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java
@@ -26,6 +26,8 @@ import android.view.View;
import android.view.ViewGroup;
import android.view.ViewRootImpl;
+import androidx.annotation.VisibleForTesting;
+
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardViewController;
@@ -332,6 +334,11 @@ public class CarKeyguardViewController extends OverlayViewController implements
getLayout().setVisibility(View.INVISIBLE);
}
+ @VisibleForTesting
+ void setKeyguardBouncer(KeyguardBouncer keyguardBouncer) {
+ mBouncer = keyguardBouncer;
+ }
+
private void revealKeyguardIfBouncerPrepared() {
int reattemptDelayMillis = 50;
Runnable revealKeyguard = () -> {
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/keyguard/CarKeyguardViewControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/keyguard/CarKeyguardViewControllerTest.java
new file mode 100644
index 000000000000..d4cf6ccf4b9e
--- /dev/null
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/keyguard/CarKeyguardViewControllerTest.java
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.car.keyguard;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.os.Handler;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.android.internal.widget.LockPatternUtils;
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.keyguard.ViewMediatorCallback;
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.car.CarServiceProvider;
+import com.android.systemui.keyguard.DismissCallbackRegistry;
+import com.android.systemui.navigationbar.car.CarNavigationBarController;
+import com.android.systemui.plugins.FalsingManager;
+import com.android.systemui.statusbar.phone.BiometricUnlockController;
+import com.android.systemui.statusbar.phone.KeyguardBouncer;
+import com.android.systemui.statusbar.phone.KeyguardBypassController;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.window.OverlayViewGlobalStateController;
+import com.android.systemui.window.SystemUIOverlayWindowController;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class CarKeyguardViewControllerTest extends SysuiTestCase {
+
+ private TestableCarKeyguardViewController mCarKeyguardViewController;
+ private OverlayViewGlobalStateController mOverlayViewGlobalStateController;
+ private ViewGroup mBaseLayout;
+
+ @Mock
+ private KeyguardBouncer mBouncer;
+ @Mock
+ private CarNavigationBarController mCarNavigationBarController;
+ @Mock
+ private SystemUIOverlayWindowController mSystemUIOverlayWindowController;
+ @Mock
+ private CarKeyguardViewController.OnKeyguardCancelClickedListener mCancelClickedListener;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ mOverlayViewGlobalStateController = new OverlayViewGlobalStateController(
+ mCarNavigationBarController, mSystemUIOverlayWindowController);
+ mBaseLayout = (ViewGroup) LayoutInflater.from(mContext).inflate(
+ R.layout.sysui_overlay_window, /* root= */ null);
+ when(mSystemUIOverlayWindowController.getBaseLayout()).thenReturn(mBaseLayout);
+
+ mCarKeyguardViewController = new TestableCarKeyguardViewController(
+ mContext,
+ Handler.getMain(),
+ mock(CarServiceProvider.class),
+ mOverlayViewGlobalStateController,
+ mock(KeyguardStateController.class),
+ mock(KeyguardUpdateMonitor.class),
+ mock(BiometricUnlockController.class),
+ mock(ViewMediatorCallback.class),
+ mock(CarNavigationBarController.class),
+ mock(LockPatternUtils.class),
+ mock(DismissCallbackRegistry.class),
+ mock(FalsingManager.class),
+ mock(KeyguardBypassController.class)
+ );
+ }
+
+ @Test
+ public void onShow_bouncerIsSecure_showsBouncerWithSecuritySelectionReset() {
+ when(mBouncer.isSecure()).thenReturn(true);
+ mCarKeyguardViewController.show(/* options= */ null);
+
+ verify(mBouncer).show(/* resetSecuritySelection= */ true);
+ }
+
+ @Test
+ public void onShow_bouncerIsSecure_keyguardIsVisible() {
+ when(mBouncer.isSecure()).thenReturn(true);
+ mCarKeyguardViewController.show(/* options= */ null);
+
+ assertThat(mBaseLayout.findViewById(R.id.keyguard_container).getVisibility()).isEqualTo(
+ View.VISIBLE);
+ }
+
+ @Test
+ public void onShow_bouncerNotSecure_hidesBouncerAndDestroysTheView() {
+ when(mBouncer.isSecure()).thenReturn(false);
+ mCarKeyguardViewController.show(/* options= */ null);
+
+ verify(mBouncer).hide(/* destroyView= */ true);
+ }
+
+ @Test
+ public void onShow_bouncerNotSecure_keyguardIsNotVisible() {
+ when(mBouncer.isSecure()).thenReturn(false);
+ mCarKeyguardViewController.show(/* options= */ null);
+
+ assertThat(mBaseLayout.findViewById(R.id.keyguard_container).getVisibility()).isEqualTo(
+ View.GONE);
+ }
+
+ @Test
+ public void onHide_keyguardShowing_hidesBouncerAndDestroysTheView() {
+ when(mBouncer.isSecure()).thenReturn(true);
+ mCarKeyguardViewController.show(/* options= */ null);
+ mCarKeyguardViewController.hide(/* startTime= */ 0, /* fadeoutDelay= */ 0);
+
+ verify(mBouncer).hide(/* destroyView= */ true);
+ }
+
+ @Test
+ public void onHide_keyguardNotShown_doesNotHideOrDestroyBouncer() {
+ mCarKeyguardViewController.hide(/* startTime= */ 0, /* fadeoutDelay= */ 0);
+
+ verify(mBouncer, never()).hide(anyBoolean());
+ }
+
+ @Test
+ public void onHide_KeyguardNotVisible() {
+ when(mBouncer.isSecure()).thenReturn(true);
+ mCarKeyguardViewController.show(/* options= */ null);
+ mCarKeyguardViewController.hide(/* startTime= */ 0, /* fadeoutDelay= */ 0);
+
+ assertThat(mBaseLayout.findViewById(R.id.keyguard_container).getVisibility()).isEqualTo(
+ View.GONE);
+ }
+
+ @Test
+ public void onCancelClicked_callsCancelClickedListener() {
+ when(mBouncer.isSecure()).thenReturn(true);
+ mCarKeyguardViewController.show(/* options= */ null);
+ mCarKeyguardViewController.registerOnKeyguardCancelClickedListener(mCancelClickedListener);
+ mCarKeyguardViewController.onCancelClicked();
+
+ verify(mCancelClickedListener).onCancelClicked();
+ }
+
+ @Test
+ public void onCancelClicked_hidesBouncerAndDestroysTheView() {
+ when(mBouncer.isSecure()).thenReturn(true);
+ mCarKeyguardViewController.show(/* options= */ null);
+ mCarKeyguardViewController.registerOnKeyguardCancelClickedListener(mCancelClickedListener);
+ mCarKeyguardViewController.onCancelClicked();
+
+ verify(mBouncer).hide(/* destroyView= */ true);
+ }
+
+ private class TestableCarKeyguardViewController extends CarKeyguardViewController {
+
+ TestableCarKeyguardViewController(Context context,
+ Handler mainHandler,
+ CarServiceProvider carServiceProvider,
+ OverlayViewGlobalStateController overlayViewGlobalStateController,
+ KeyguardStateController keyguardStateController,
+ KeyguardUpdateMonitor keyguardUpdateMonitor,
+ BiometricUnlockController biometricUnlockController,
+ ViewMediatorCallback viewMediatorCallback,
+ CarNavigationBarController carNavigationBarController,
+ LockPatternUtils lockPatternUtils,
+ DismissCallbackRegistry dismissCallbackRegistry,
+ FalsingManager falsingManager,
+ KeyguardBypassController keyguardBypassController) {
+ super(context, mainHandler, carServiceProvider, overlayViewGlobalStateController,
+ keyguardStateController, keyguardUpdateMonitor, biometricUnlockController,
+ viewMediatorCallback, carNavigationBarController, lockPatternUtils,
+ dismissCallbackRegistry, falsingManager, keyguardBypassController);
+ }
+
+ @Override
+ public void onFinishInflate() {
+ super.onFinishInflate();
+ setKeyguardBouncer(CarKeyguardViewControllerTest.this.mBouncer);
+ }
+ }
+
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java
index c11e1a03cb00..6fbee16e3dae 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java
@@ -30,13 +30,17 @@ import android.content.pm.IPackageManager;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.content.pm.ProviderInfo;
+import android.content.pm.UserInfo;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.RemoteException;
+import android.os.UserManager;
import android.permission.IPermissionManager;
import android.util.Log;
+import java.util.List;
+
/**
* Select which activity is the first visible activity of the installation and forward the intent to
* it.
@@ -47,6 +51,7 @@ public class InstallStart extends Activity {
private static final String DOWNLOADS_AUTHORITY = "downloads";
private IPackageManager mIPackageManager;
private IPermissionManager mIPermissionManager;
+ private UserManager mUserManager;
private boolean mAbortInstall = false;
@Override
@@ -54,6 +59,7 @@ public class InstallStart extends Activity {
super.onCreate(savedInstanceState);
mIPackageManager = AppGlobals.getPackageManager();
mIPermissionManager = AppGlobals.getPermissionManager();
+ mUserManager = getSystemService(UserManager.class);
Intent intent = getIntent();
String callingPackage = getCallingPackage();
@@ -144,13 +150,16 @@ public class InstallStart extends Activity {
if (packages == null) {
return false;
}
+ final List<UserInfo> users = mUserManager.getUsers();
for (String packageName : packages) {
- try {
- if (uid == getPackageManager().getPackageUid(packageName, 0)) {
- return true;
+ for (UserInfo user : users) {
+ try {
+ if (uid == getPackageManager().getPackageUidAsUser(packageName, user.id)) {
+ return true;
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ // Ignore and try the next package
}
- } catch (PackageManager.NameNotFoundException e) {
- // Ignore and try the next package
}
}
} catch (RemoteException rexc) {
diff --git a/packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java b/packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java
index fa2ec55bd81a..a77e34b4af1e 100644
--- a/packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java
+++ b/packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java
@@ -147,6 +147,28 @@ public class RestrictedLockUtils {
public EnforcedAdmin() {
}
+ /**
+ * Combines two {@link EnforcedAdmin} into one: if one of them is null, then just return
+ * the other. If both of them are the same, then return that. Otherwise return the symbolic
+ * {@link #MULTIPLE_ENFORCED_ADMIN}
+ */
+ public static EnforcedAdmin combine(EnforcedAdmin admin1, EnforcedAdmin admin2) {
+ if (admin1 == null) {
+ return admin2;
+ }
+ if (admin2 == null) {
+ return admin1;
+ }
+ if (admin1.equals(admin2)) {
+ return admin1;
+ }
+ if (!admin1.enforcedRestriction.equals(admin2.enforcedRestriction)) {
+ throw new IllegalArgumentException(
+ "Admins with different restriction cannot be combined");
+ }
+ return MULTIPLE_ENFORCED_ADMIN;
+ }
+
@Override
public boolean equals(Object o) {
if (this == o) return true;
diff --git a/packages/SystemUI/res/anim/control_state_list_animator.xml b/packages/SystemUI/res/anim/control_state_list_animator.xml
index 7940b8874ece..a20a9255f86f 100644
--- a/packages/SystemUI/res/anim/control_state_list_animator.xml
+++ b/packages/SystemUI/res/anim/control_state_list_animator.xml
@@ -18,11 +18,13 @@
<item android:state_pressed="true">
<set>
<objectAnimator
+ android:interpolator="@interpolator/control_state"
android:duration="50"
android:propertyName="scaleX"
android:valueTo="0.97"
android:valueType="floatType" />
<objectAnimator
+ android:interpolator="@interpolator/control_state"
android:duration="50"
android:propertyName="scaleY"
android:valueTo="0.97"
@@ -33,11 +35,13 @@
<item>
<set>
<objectAnimator
+ android:interpolator="@interpolator/control_state"
android:duration="250"
android:propertyName="scaleX"
android:valueTo="1"
android:valueType="floatType" />
<objectAnimator
+ android:interpolator="@interpolator/control_state"
android:duration="250"
android:propertyName="scaleY"
android:valueTo="1"
diff --git a/packages/SystemUI/res/drawable/control_background.xml b/packages/SystemUI/res/drawable/control_background.xml
index 29b4efa48fa1..cf298b70f3db 100644
--- a/packages/SystemUI/res/drawable/control_background.xml
+++ b/packages/SystemUI/res/drawable/control_background.xml
@@ -17,7 +17,8 @@
*/
-->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
- <item>
+ <item
+ android:id="@+id/background">
<shape>
<solid android:color="@color/control_default_background" />
<corners android:radius="@dimen/control_corner_radius" />
diff --git a/packages/SystemUI/res/interpolator/control_state.xml b/packages/SystemUI/res/interpolator/control_state.xml
new file mode 100644
index 000000000000..66106d48d507
--- /dev/null
+++ b/packages/SystemUI/res/interpolator/control_state.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<pathInterpolator
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:controlX1="0"
+ android:controlY1="0"
+ android:controlX2="1"
+ android:controlY2="1"/>
diff --git a/packages/SystemUI/res/layout/photo_preview_overlay.xml b/packages/SystemUI/res/layout/photo_preview_overlay.xml
new file mode 100644
index 000000000000..9210996fb9c6
--- /dev/null
+++ b/packages/SystemUI/res/layout/photo_preview_overlay.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<!-- empty stub -->
+<merge /> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index 115b4a86b86d..1d4b98242519 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -66,6 +66,8 @@
<include layout="@layout/ambient_indication"
android:id="@+id/ambient_indication_container" />
+ <include layout="@layout/photo_preview_overlay" />
+
<ViewStub
android:id="@+id/keyguard_user_switcher"
android:layout="@layout/keyguard_user_switcher"
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index e7ef8ccf4eba..622e4ccef487 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1252,6 +1252,7 @@
<dimen name="control_status_expanded">18sp</dimen>
<dimen name="control_base_item_margin">2dp</dimen>
<dimen name="control_status_padding">3dp</dimen>
+ <fraction name="controls_toggle_bg_intensity">5%</fraction>
<!-- Home Controls activity view detail panel-->
<dimen name="controls_activity_view_top_padding">25dp</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index d2654d6d7d9a..49420e8620d1 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1829,15 +1829,15 @@
<!-- [CHAR LIMIT=150] Notification Importance title: normal importance level summary -->
<string name="notification_channel_summary_default">Gets your attention with sound or vibration.</string>
+ <!-- [CHAR LIMIT=150] Conversation Notification Importance title: normal conversation level, with bubbling summary -->
+ <string name="notification_channel_summary_default_with_bubbles">Gets your attention with sound or vibration. Conversations from <xliff:g id="app_name" example="YouTube">%1$s</xliff:g> bubble by default.</string>
+
<!-- [CHAR LIMIT=150] Notification Importance title: bubble level summary -->
<string name="notification_channel_summary_bubble">Keeps your attention with a floating shortcut to this content.</string>
<!-- [CHAR LIMIT=150] Notification Importance title: important conversation level summary -->
<string name="notification_channel_summary_priority">Shows at top of conversation section and appears as a bubble.</string>
- <!--[CHAR LIMIT=150] Conversation inline controls footer shown when all conversations from the app are allowed to show as bubbles -->
- <string name="notification_conversation_channel_all_bubble">All conversations from <xliff:g id="app_name" example="YouTube">%1$s</xliff:g> bubble by default. Manage in <xliff:g id="app_name" example="Settings">%2$s</xliff:g>.</string>
-
<!--[CHAR LIMIT=30] Linkable text to Settings app -->
<string name="notification_conversation_channel_settings">Settings</string>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 118aa5b3f96a..7e24f5dbbd50 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -564,7 +564,7 @@
<style name="TextAppearance.NotificationImportanceButton">
<item name="android:textSize">@dimen/notification_importance_button_text</item>
<item name="android:fontFamily">@*android:string/config_headlineFontFamilyMedium</item>
- <item name="android:textColor">?android:attr/colorAccent</item>
+ <item name="android:textColor">@color/notification_guts_priority_contents</item>
<item name="android:gravity">center</item>
</style>
diff --git a/packages/SystemUI/scripts/update_statsd_lib.sh b/packages/SystemUI/scripts/update_statsd_lib.sh
new file mode 100755
index 000000000000..79b9497a5f3f
--- /dev/null
+++ b/packages/SystemUI/scripts/update_statsd_lib.sh
@@ -0,0 +1,35 @@
+#!/bin/sh
+
+NUM_ARGS=$#
+JAR_DESTINATION="$1/prebuilts/framework_intermediates/libs/systemui_statsd.jar"
+
+has_croot() {
+ declare -F croot > /dev/null
+ return $?
+}
+
+check_environment() {
+ if ! has_croot; then
+ echo "Run script in a shell that has had envsetup run. Run '. update_statsd_lib.sh' from scripts directory"
+ return 1
+ fi
+
+ if [ $NUM_ARGS -ne 1 ]; then
+ echo "Usage: . update_statsd_lib.sh PATH_TO_UNBUNDLED_LAUNCER e.g. . update_statsd_lib ~/src/ub-launcher3-master"
+ return 1
+ fi
+ return 0
+}
+
+main() {
+ if check_environment ; then
+ pushd .
+ croot
+ mma -j16 SystemUI-statsd
+ cp out/target/product/$TARGET_PRODUCT/obj/JAVA_LIBRARIES/SystemUI-statsd_intermediates/javalib.jar $JAR_DESTINATION
+ popd
+ fi
+}
+
+main
+
diff --git a/packages/SystemUI/src/com/android/systemui/Interpolators.java b/packages/SystemUI/src/com/android/systemui/Interpolators.java
index 6923079dd5c4..2eba9521b9e7 100644
--- a/packages/SystemUI/src/com/android/systemui/Interpolators.java
+++ b/packages/SystemUI/src/com/android/systemui/Interpolators.java
@@ -54,6 +54,11 @@ public class Interpolators {
public static final Interpolator PANEL_CLOSE_ACCELERATED
= new PathInterpolator(0.3f, 0, 0.5f, 1);
public static final Interpolator BOUNCE = new BounceInterpolator();
+ /**
+ * For state transitions on the control panel that lives in GlobalActions.
+ */
+ public static final Interpolator CONTROL_STATE = new PathInterpolator(0.4f, 0f, 0.2f,
+ 1.0f);
/**
* Interpolator to be used when animating a move based on a click. Pair with enough duration.
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index 8df3dd2ad845..7861211e802d 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -54,8 +54,10 @@ import android.graphics.Region;
import android.graphics.drawable.VectorDrawable;
import android.hardware.display.DisplayManager;
import android.os.Handler;
+import android.os.HandlerExecutor;
import android.os.HandlerThread;
import android.os.SystemProperties;
+import android.os.UserHandle;
import android.provider.Settings.Secure;
import android.util.DisplayMetrics;
import android.util.Log;
@@ -298,13 +300,15 @@ public class ScreenDecorations extends SystemUI implements Tunable {
updateColorInversion(value);
}
};
+
+ mColorInversionSetting.setListening(true);
+ mColorInversionSetting.onChange(false);
}
- mColorInversionSetting.setListening(true);
- mColorInversionSetting.onChange(false);
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_USER_SWITCHED);
- mBroadcastDispatcher.registerReceiverWithHandler(mIntentReceiver, filter, mHandler);
+ mBroadcastDispatcher.registerReceiver(mUserSwitchIntentReceiver, filter,
+ new HandlerExecutor(mHandler), UserHandle.ALL);
mIsRegistered = true;
} else {
mMainHandler.post(() -> mTunerService.removeTunable(this));
@@ -313,7 +317,7 @@ public class ScreenDecorations extends SystemUI implements Tunable {
mColorInversionSetting.setListening(false);
}
- mBroadcastDispatcher.unregisterReceiver(mIntentReceiver);
+ mBroadcastDispatcher.unregisterReceiver(mUserSwitchIntentReceiver);
mIsRegistered = false;
}
}
@@ -503,17 +507,16 @@ public class ScreenDecorations extends SystemUI implements Tunable {
}
}
- private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
+ private final BroadcastReceiver mUserSwitchIntentReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- if (action.equals(Intent.ACTION_USER_SWITCHED)) {
- int newUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
- ActivityManager.getCurrentUser());
- // update color inversion setting to the new user
- mColorInversionSetting.setUserId(newUserId);
- updateColorInversion(mColorInversionSetting.getValue());
+ int newUserId = ActivityManager.getCurrentUser();
+ if (DEBUG) {
+ Log.d(TAG, "UserSwitched newUserId=" + newUserId);
}
+ // update color inversion setting to the new user
+ mColorInversionSetting.setUserId(newUserId);
+ updateColorInversion(mColorInversionSetting.getValue());
}
};
@@ -945,7 +948,12 @@ public class ScreenDecorations extends SystemUI implements Tunable {
int dw = flipped ? lh : lw;
int dh = flipped ? lw : lh;
- mBoundingPath.set(DisplayCutout.pathFromResources(getResources(), dw, dh));
+ Path path = DisplayCutout.pathFromResources(getResources(), dw, dh);
+ if (path != null) {
+ mBoundingPath.set(path);
+ } else {
+ mBoundingPath.reset();
+ }
Matrix m = new Matrix();
transformPhysicalToLogicalCoordinates(mInfo.rotation, dw, dh, m);
mBoundingPath.transform(m);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java
index d8a11d36a335..e6a62c26712a 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java
@@ -17,6 +17,7 @@
package com.android.systemui.biometrics;
import android.content.Context;
+import android.os.UserHandle;
import android.text.InputType;
import android.util.AttributeSet;
import android.view.KeyEvent;
@@ -68,6 +69,7 @@ public class AuthCredentialPasswordView extends AuthCredentialView
protected void onAttachedToWindow() {
super.onAttachedToWindow();
+ mPasswordField.setTextOperationUser(UserHandle.of(mUserId));
if (mCredentialType == Utils.CREDENTIAL_PIN) {
mPasswordField.setInputType(
InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_VARIATION_PASSWORD);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java
index 8bf259182544..496e60ddf99e 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java
@@ -286,6 +286,7 @@ public abstract class AuthCredentialView extends LinearLayout {
if (matched) {
mClearErrorRunnable.run();
+ mLockPatternUtils.userPresent(mEffectiveUserId);
mCallback.onCredentialMatched(attestation);
} else {
if (timeoutMs > 0) {
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index da5c2968c6ac..c8e9a687ad23 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -108,7 +108,6 @@ import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.util.ArrayList;
-import java.util.HashSet;
import java.util.List;
/**
@@ -162,14 +161,6 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
// Used when ranking updates occur and we check if things should bubble / unbubble
private NotificationListenerService.Ranking mTmpRanking;
- // Saves notification keys of user created "fake" bubbles so that we can allow notifications
- // like these to bubble by default. Doesn't persist across reboots, not a long-term solution.
- private final HashSet<String> mUserCreatedBubbles;
- // If we're auto-bubbling bubbles via a whitelist, we need to track which notifs from that app
- // have been "demoted" back to a notification so that we don't auto-bubbles those again.
- // Doesn't persist across reboots, not a long-term solution.
- private final HashSet<String> mUserBlockedBubbles;
-
// Bubbles get added to the status bar view
private final NotificationShadeWindowController mNotificationShadeWindowController;
private final ZenModeController mZenModeController;
@@ -412,9 +403,6 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
}
});
- mUserCreatedBubbles = new HashSet<>();
- mUserBlockedBubbles = new HashSet<>();
-
mBubbleIconFactory = new BubbleIconFactory(context);
}
@@ -474,8 +462,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
(entry != null && entry.isRowDismissed() && !isAppCancel)
|| isClearAll || isUserDimiss || isSummaryCancel;
- if (userRemovedNotif || isUserCreatedBubble(key)
- || isSummaryOfUserCreatedBubble(entry)) {
+ if (userRemovedNotif) {
return handleDismissalInterception(entry);
}
@@ -860,27 +847,6 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
}
/**
- * Whether this bubble was explicitly created by the user via a SysUI affordance.
- */
- boolean isUserCreatedBubble(String key) {
- return mUserCreatedBubbles.contains(key);
- }
-
- boolean isSummaryOfUserCreatedBubble(NotificationEntry entry) {
- if (isSummaryOfBubbles(entry)) {
- List<Bubble> bubbleChildren =
- mBubbleData.getBubblesInGroup(entry.getSbn().getGroupKey());
- for (int i = 0; i < bubbleChildren.size(); i++) {
- // Check if any are user-created (i.e. experimental bubbles)
- if (isUserCreatedBubble(bubbleChildren.get(i).getKey())) {
- return true;
- }
- }
- }
- return false;
- }
-
- /**
* Removes the bubble with the given NotificationEntry.
* <p>
* Must be called from the main thread.
@@ -893,37 +859,19 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
}
private void onEntryAdded(NotificationEntry entry) {
- boolean previouslyUserCreated = mUserCreatedBubbles.contains(entry.getKey());
- boolean userBlocked = mUserBlockedBubbles.contains(entry.getKey());
- boolean wasAdjusted = BubbleExperimentConfig.adjustForExperiments(
- mContext, entry, previouslyUserCreated, userBlocked);
-
if (mNotificationInterruptStateProvider.shouldBubbleUp(entry)
- && (canLaunchInActivityView(mContext, entry) || wasAdjusted)) {
- if (wasAdjusted && !previouslyUserCreated) {
- // Gotta treat the auto-bubbled / whitelisted packaged bubbles as usercreated
- mUserCreatedBubbles.add(entry.getKey());
- }
+ && canLaunchInActivityView(mContext, entry)) {
updateBubble(entry);
}
}
private void onEntryUpdated(NotificationEntry entry) {
- boolean previouslyUserCreated = mUserCreatedBubbles.contains(entry.getKey());
- boolean userBlocked = mUserBlockedBubbles.contains(entry.getKey());
- boolean wasAdjusted = BubbleExperimentConfig.adjustForExperiments(
- mContext, entry, previouslyUserCreated, userBlocked);
-
boolean shouldBubble = mNotificationInterruptStateProvider.shouldBubbleUp(entry)
- && (canLaunchInActivityView(mContext, entry) || wasAdjusted);
+ && canLaunchInActivityView(mContext, entry);
if (!shouldBubble && mBubbleData.hasBubbleWithKey(entry.getKey())) {
// It was previously a bubble but no longer a bubble -- lets remove it
removeBubble(entry, DISMISS_NO_LONGER_BUBBLE);
} else if (shouldBubble) {
- if (wasAdjusted && !previouslyUserCreated) {
- // Gotta treat the auto-bubbled / whitelisted packaged bubbles as usercreated
- mUserCreatedBubbles.add(entry.getKey());
- }
updateBubble(entry);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
index 93fb6972fad5..3524696dbc79 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
@@ -193,7 +193,7 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList
+ " mActivityViewStatus=" + mActivityViewStatus
+ " bubble=" + getBubbleKey());
}
- if (mBubble != null && !mBubbleController.isUserCreatedBubble(mBubble.getKey())) {
+ if (mBubble != null) {
// Must post because this is called from a binder thread.
post(() -> mBubbleController.removeBubble(mBubble.getEntry(),
BubbleController.DISMISS_TASK_FINISHED));
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExperimentConfig.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExperimentConfig.java
index 41dbb489c2f6..2060391d38a3 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExperimentConfig.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExperimentConfig.java
@@ -57,16 +57,13 @@ import java.util.List;
public class BubbleExperimentConfig {
private static final String TAG = TAG_WITH_CLASS_NAME ? "BubbleController" : TAG_BUBBLES;
- private static final String SHORTCUT_DUMMY_INTENT = "bubble_experiment_shortcut_intent";
- private static PendingIntent sDummyShortcutIntent;
-
private static final int BUBBLE_HEIGHT = 10000;
private static final String ALLOW_ANY_NOTIF_TO_BUBBLE = "allow_any_notif_to_bubble";
private static final boolean ALLOW_ANY_NOTIF_TO_BUBBLE_DEFAULT = false;
private static final String ALLOW_MESSAGE_NOTIFS_TO_BUBBLE = "allow_message_notifs_to_bubble";
- private static final boolean ALLOW_MESSAGE_NOTIFS_TO_BUBBLE_DEFAULT = true;
+ private static final boolean ALLOW_MESSAGE_NOTIFS_TO_BUBBLE_DEFAULT = false;
private static final String ALLOW_SHORTCUTS_TO_BUBBLE = "allow_shortcuts_to_bubble";
private static final boolean ALLOW_SHORTCUT_TO_BUBBLE_DEFAULT = false;
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index 1cabe221bef1..0534efc0949d 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -374,8 +374,9 @@ public class BubbleStackView extends FrameLayout {
@Override
public void onReleasedInTarget(@NonNull MagnetizedObject.MagneticTarget target) {
mExpandedAnimationController.dismissDraggedOutBubble(
- mExpandedAnimationController.getDraggedOutBubble(),
- BubbleStackView.this::dismissMagnetizedObject);
+ mExpandedAnimationController.getDraggedOutBubble() /* bubble */,
+ mDismissTargetContainer.getHeight() /* translationYBy */,
+ BubbleStackView.this::dismissMagnetizedObject /* after */);
hideDismissTarget();
}
};
@@ -405,7 +406,8 @@ public class BubbleStackView extends FrameLayout {
@Override
public void onReleasedInTarget(@NonNull MagnetizedObject.MagneticTarget target) {
- mStackAnimationController.implodeStack(
+ mStackAnimationController.animateStackDismissal(
+ mDismissTargetContainer.getHeight() /* translationYBy */,
() -> {
resetDesaturationAndDarken();
dismissMagnetizedObject();
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java
index 501e5024d940..c96f9a470ca4 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java
@@ -139,22 +139,11 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask
StatusBarNotification sbn = b.getEntry().getSbn();
String packageName = sbn.getPackageName();
- // Real shortcut info for this bubble
String bubbleShortcutId = b.getEntry().getBubbleMetadata().getShortcutId();
if (bubbleShortcutId != null) {
- info.shortcutInfo = BubbleExperimentConfig.getShortcutInfo(c, packageName,
- sbn.getUser(), bubbleShortcutId);
- } else {
- // Check for experimental shortcut
- String shortcutId = sbn.getNotification().getShortcutId();
- if (BubbleExperimentConfig.useShortcutInfoToBubble(c) && shortcutId != null) {
- info.shortcutInfo = BubbleExperimentConfig.getShortcutInfo(c,
- packageName,
- sbn.getUser(), shortcutId);
- }
+ info.shortcutInfo = b.getEntry().getRanking().getShortcutInfo();
}
-
// App name & app icon
PackageManager pm = c.getPackageManager();
ApplicationInfo appInfo;
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
index d974adc34ee0..0002e862bb41 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
@@ -329,7 +329,7 @@ public class ExpandedAnimationController
}
/** Plays a dismiss animation on the dragged out bubble. */
- public void dismissDraggedOutBubble(View bubble, Runnable after) {
+ public void dismissDraggedOutBubble(View bubble, float translationYBy, Runnable after) {
if (bubble == null) {
return;
}
@@ -337,6 +337,7 @@ public class ExpandedAnimationController
.withStiffness(SpringForce.STIFFNESS_HIGH)
.scaleX(1.1f)
.scaleY(1.1f)
+ .translationY(bubble.getTranslationY() + translationYBy)
.alpha(0f, after)
.start();
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java
index 00de8b4a51b8..5f3a2bd9eb8b 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java
@@ -647,17 +647,18 @@ public class StackAnimationController extends
}
/**
- * 'Implode' the stack by shrinking the bubbles via chained animations and fading them out.
+ * 'Implode' the stack by shrinking the bubbles, fading them out, and translating them down.
*/
- public void implodeStack(Runnable after) {
- // Pop and fade the bubbles sequentially.
- animationForChildAtIndex(0)
- .scaleX(0.5f)
- .scaleY(0.5f)
- .alpha(0f)
- .withDampingRatio(SpringForce.DAMPING_RATIO_NO_BOUNCY)
- .withStiffness(SpringForce.STIFFNESS_HIGH)
- .start(after);
+ public void animateStackDismissal(float translationYBy, Runnable after) {
+ animationsForChildrenFromIndex(0, (index, animation) ->
+ animation
+ .scaleX(0.5f)
+ .scaleY(0.5f)
+ .alpha(0f)
+ .translationY(
+ mLayout.getChildAt(index).getTranslationY() + translationYBy)
+ .withStiffness(SpringForce.STIFFNESS_HIGH))
+ .startAll(after);
}
/**
@@ -710,8 +711,6 @@ public class StackAnimationController extends
if (property.equals(DynamicAnimation.TRANSLATION_X)
|| property.equals(DynamicAnimation.TRANSLATION_Y)) {
return index + 1;
- } else if (isStackStuckToTarget()) {
- return index + 1; // Chain all animations in dismiss (scale, alpha, etc. are used).
} else {
return NONE;
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinator.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinator.kt
index ad86eeb089c8..2c1a91dca225 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinator.kt
@@ -24,12 +24,11 @@ import android.service.controls.actions.BooleanAction
import android.service.controls.actions.CommandAction
import android.util.Log
import android.view.HapticFeedbackConstants
-
import com.android.systemui.R
object ControlActionCoordinator {
- public const val MIN_LEVEL = 0
- public const val MAX_LEVEL = 10000
+ const val MIN_LEVEL = 0
+ const val MAX_LEVEL = 10000
private var dialog: Dialog? = null
@@ -40,9 +39,6 @@ object ControlActionCoordinator {
fun toggle(cvh: ControlViewHolder, templateId: String, isChecked: Boolean) {
cvh.action(BooleanAction(templateId, !isChecked))
-
- val nextLevel = if (isChecked) MIN_LEVEL else MAX_LEVEL
- cvh.clipLayer.setLevel(nextLevel)
}
fun touch(cvh: ControlViewHolder, templateId: String, control: Control) {
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
index 0eb6cb100933..93e1bd444938 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
@@ -16,6 +16,9 @@
package com.android.systemui.controls.ui
+import android.animation.Animator
+import android.animation.AnimatorListenerAdapter
+import android.animation.ValueAnimator
import android.content.Context
import android.graphics.drawable.ClipDrawable
import android.graphics.drawable.GradientDrawable
@@ -32,11 +35,11 @@ import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
-
+import com.android.internal.graphics.ColorUtils
+import com.android.systemui.Interpolators
+import com.android.systemui.R
import com.android.systemui.controls.controller.ControlsController
import com.android.systemui.util.concurrency.DelayableExecutor
-import com.android.systemui.R
-
import kotlin.reflect.KClass
/**
@@ -53,15 +56,20 @@ class ControlViewHolder(
) {
companion object {
+ const val STATE_ANIMATION_DURATION = 700L
private const val UPDATE_DELAY_IN_MILLIS = 3000L
private const val ALPHA_ENABLED = (255.0 * 0.2).toInt()
- private const val ALPHA_DISABLED = 255
+ private const val ALPHA_DISABLED = 0
private val FORCE_PANEL_DEVICES = setOf(
DeviceTypes.TYPE_THERMOSTAT,
DeviceTypes.TYPE_CAMERA
)
}
+ private val toggleBackgroundIntensity: Float = layout.context.resources
+ .getFraction(R.fraction.controls_toggle_bg_intensity, 1, 1)
+ private var stateAnimator: ValueAnimator? = null
+ private val baseLayer: GradientDrawable
val icon: ImageView = layout.requireViewById(R.id.icon)
val status: TextView = layout.requireViewById(R.id.status)
val title: TextView = layout.requireViewById(R.id.title)
@@ -79,6 +87,8 @@ class ControlViewHolder(
val ld = layout.getBackground() as LayerDrawable
ld.mutate()
clipLayer = ld.findDrawableByLayerId(R.id.clip_layer) as ClipDrawable
+ clipLayer.alpha = ALPHA_DISABLED
+ baseLayer = ld.findDrawableByLayerId(R.id.background) as GradientDrawable
// needed for marquee to start
status.setSelected(true)
}
@@ -160,30 +170,59 @@ class ControlViewHolder(
}
}
- internal fun applyRenderInfo(enabled: Boolean, offset: Int = 0) {
+ internal fun applyRenderInfo(enabled: Boolean, offset: Int = 0, animated: Boolean = true) {
setEnabled(enabled)
val ri = RenderInfo.lookup(context, cws.componentName, deviceType, enabled, offset)
- val fg = context.getResources().getColorStateList(ri.foreground, context.getTheme())
- val (bg, alpha) = if (enabled) {
- Pair(ri.enabledBackground, ALPHA_ENABLED)
+ val fg = context.resources.getColorStateList(ri.foreground, context.theme)
+ val bg = context.resources.getColor(R.color.control_default_background, context.theme)
+ val (clip, newAlpha) = if (enabled) {
+ listOf(ri.enabledBackground, ALPHA_ENABLED)
} else {
- Pair(R.color.control_default_background, ALPHA_DISABLED)
+ listOf(R.color.control_default_background, ALPHA_DISABLED)
}
status.setTextColor(fg)
-
icon.setImageDrawable(ri.icon)
// do not color app icons
if (deviceType != DeviceTypes.TYPE_ROUTINE) {
- icon.setImageTintList(fg)
+ icon.imageTintList = fg
}
(clipLayer.getDrawable() as GradientDrawable).apply {
- setColor(context.getResources().getColor(bg, context.getTheme()))
- setAlpha(alpha)
+ val newClipColor = context.resources.getColor(clip, context.theme)
+ val newBaseColor = if (behavior is ToggleRangeBehavior) {
+ ColorUtils.blendARGB(bg, newClipColor, toggleBackgroundIntensity)
+ } else {
+ bg
+ }
+ stateAnimator?.cancel()
+ if (animated) {
+ val oldColor = color?.defaultColor ?: newClipColor
+ val oldBaseColor = baseLayer.color?.defaultColor ?: newBaseColor
+ stateAnimator = ValueAnimator.ofInt(clipLayer.alpha, newAlpha).apply {
+ addUpdateListener {
+ alpha = it.animatedValue as Int
+ setColor(ColorUtils.blendARGB(oldColor, newClipColor, it.animatedFraction))
+ baseLayer.setColor(ColorUtils.blendARGB(oldBaseColor,
+ newBaseColor, it.animatedFraction))
+ }
+ addListener(object : AnimatorListenerAdapter() {
+ override fun onAnimationEnd(animation: Animator?) {
+ stateAnimator = null
+ }
+ })
+ duration = STATE_ANIMATION_DURATION
+ interpolator = Interpolators.CONTROL_STATE
+ start()
+ }
+ } else {
+ alpha = newAlpha
+ setColor(newClipColor)
+ baseLayer.setColor(newBaseColor)
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleBehavior.kt
index a3368ef77a56..368d1399971d 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleBehavior.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleBehavior.kt
@@ -18,12 +18,10 @@ package com.android.systemui.controls.ui
import android.graphics.drawable.Drawable
import android.graphics.drawable.LayerDrawable
-import android.view.View
import android.service.controls.Control
import android.service.controls.templates.ToggleTemplate
-
+import android.view.View
import com.android.systemui.R
-import com.android.systemui.controls.ui.ControlActionCoordinator.MIN_LEVEL
import com.android.systemui.controls.ui.ControlActionCoordinator.MAX_LEVEL
class ToggleBehavior : Behavior {
@@ -34,7 +32,7 @@ class ToggleBehavior : Behavior {
override fun initialize(cvh: ControlViewHolder) {
this.cvh = cvh
- cvh.applyRenderInfo(false)
+ cvh.applyRenderInfo(false /* enabled */, 0 /* offset */, false /* animated */)
cvh.layout.setOnClickListener(View.OnClickListener() {
ControlActionCoordinator.toggle(cvh, template.getTemplateId(), template.isChecked())
@@ -49,9 +47,9 @@ class ToggleBehavior : Behavior {
val ld = cvh.layout.getBackground() as LayerDrawable
clipLayer = ld.findDrawableByLayerId(R.id.clip_layer)
+ clipLayer.level = MAX_LEVEL
val checked = template.isChecked()
- clipLayer.setLevel(if (checked) MAX_LEVEL else MIN_LEVEL)
cvh.applyRenderInfo(checked)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt
index e8785e12a883..d8b26e2e68d8 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt
@@ -16,11 +16,20 @@
package com.android.systemui.controls.ui
-import android.os.Bundle
+import android.animation.Animator
+import android.animation.AnimatorListenerAdapter
+import android.animation.ValueAnimator
import android.content.Context
import android.graphics.drawable.Drawable
import android.graphics.drawable.LayerDrawable
+import android.os.Bundle
+import android.service.controls.Control
+import android.service.controls.actions.FloatAction
+import android.service.controls.templates.RangeTemplate
+import android.service.controls.templates.ToggleRangeTemplate
import android.util.Log
+import android.util.MathUtils
+import android.util.TypedValue
import android.view.GestureDetector
import android.view.GestureDetector.SimpleOnGestureListener
import android.view.MotionEvent
@@ -29,19 +38,14 @@ import android.view.ViewGroup
import android.view.accessibility.AccessibilityEvent
import android.view.accessibility.AccessibilityNodeInfo
import android.widget.TextView
-import android.service.controls.Control
-import android.service.controls.actions.FloatAction
-import android.service.controls.templates.RangeTemplate
-import android.service.controls.templates.ToggleRangeTemplate
-import android.util.TypedValue
-
+import com.android.systemui.Interpolators
import com.android.systemui.R
-import com.android.systemui.controls.ui.ControlActionCoordinator.MIN_LEVEL
import com.android.systemui.controls.ui.ControlActionCoordinator.MAX_LEVEL
-
+import com.android.systemui.controls.ui.ControlActionCoordinator.MIN_LEVEL
import java.util.IllegalFormatException
class ToggleRangeBehavior : Behavior {
+ private var rangeAnimator: ValueAnimator? = null
lateinit var clipLayer: Drawable
lateinit var template: ToggleRangeTemplate
lateinit var control: Control
@@ -61,20 +65,21 @@ class ToggleRangeBehavior : Behavior {
status = cvh.status
context = status.getContext()
- cvh.applyRenderInfo(false)
+ cvh.applyRenderInfo(false /* enabled */, 0 /* offset */, false /* animated */)
val gestureListener = ToggleRangeGestureListener(cvh.layout)
val gestureDetector = GestureDetector(context, gestureListener)
cvh.layout.setOnTouchListener { v: View, e: MotionEvent ->
if (gestureDetector.onTouchEvent(e)) {
- return@setOnTouchListener true
+ // Don't return true to let the state list change to "pressed"
+ return@setOnTouchListener false
}
if (e.getAction() == MotionEvent.ACTION_UP && gestureListener.isDragging) {
v.getParent().requestDisallowInterceptTouchEvent(false)
gestureListener.isDragging = false
endUpdateRange()
- return@setOnTouchListener true
+ return@setOnTouchListener false
}
return@setOnTouchListener false
@@ -87,17 +92,18 @@ class ToggleRangeBehavior : Behavior {
currentStatusText = control.getStatusText()
status.setText(currentStatusText)
+ // ControlViewHolder sets a long click listener, but we want to handle touch in
+ // here instead, otherwise we'll have state conflicts.
+ cvh.layout.setOnLongClickListener(null)
+
val ld = cvh.layout.getBackground() as LayerDrawable
clipLayer = ld.findDrawableByLayerId(R.id.clip_layer)
- clipLayer.setLevel(MIN_LEVEL)
template = control.getControlTemplate() as ToggleRangeTemplate
rangeTemplate = template.getRange()
val checked = template.isChecked()
- val currentRatio = rangeTemplate.getCurrentValue() /
- (rangeTemplate.getMaxValue() - rangeTemplate.getMinValue())
- updateRange(currentRatio, checked, /* isDragging */ false)
+ updateRange(rangeToLevelValue(rangeTemplate.currentValue), checked, /* isDragging */ false)
cvh.applyRenderInfo(checked)
@@ -146,9 +152,8 @@ class ToggleRangeBehavior : Behavior {
} else {
val value = arguments.getFloat(
AccessibilityNodeInfo.ACTION_ARGUMENT_PROGRESS_VALUE)
- val ratioDiff = (value - rangeTemplate.getCurrentValue()) /
- (rangeTemplate.getMaxValue() - rangeTemplate.getMinValue())
- updateRange(ratioDiff, template.isChecked(), /* isDragging */ false)
+ val level = rangeToLevelValue(value - rangeTemplate.getCurrentValue())
+ updateRange(level, template.isChecked(), /* isDragging */ false)
endUpdateRange()
true
}
@@ -172,13 +177,30 @@ class ToggleRangeBehavior : Behavior {
.getDimensionPixelSize(R.dimen.control_status_expanded).toFloat())
}
- fun updateRange(ratioDiff: Float, checked: Boolean, isDragging: Boolean) {
- val changeAmount = if (checked) (MAX_LEVEL * ratioDiff).toInt() else MIN_LEVEL
- val newLevel = Math.max(MIN_LEVEL, Math.min(MAX_LEVEL, clipLayer.getLevel() + changeAmount))
- clipLayer.setLevel(newLevel)
+ fun updateRange(level: Int, checked: Boolean, isDragging: Boolean) {
+ val newLevel = if (checked) Math.max(MIN_LEVEL, Math.min(MAX_LEVEL, level)) else MIN_LEVEL
+
+ rangeAnimator?.cancel()
+ if (isDragging) {
+ clipLayer.level = newLevel
+ } else {
+ rangeAnimator = ValueAnimator.ofInt(cvh.clipLayer.level, newLevel).apply {
+ addUpdateListener {
+ cvh.clipLayer.level = it.animatedValue as Int
+ }
+ addListener(object : AnimatorListenerAdapter() {
+ override fun onAnimationEnd(animation: Animator?) {
+ rangeAnimator = null
+ }
+ })
+ duration = ControlViewHolder.STATE_ANIMATION_DURATION
+ interpolator = Interpolators.CONTROL_STATE
+ start()
+ }
+ }
if (checked) {
- val newValue = levelToRangeValue(clipLayer.getLevel())
+ val newValue = levelToRangeValue(newLevel)
currentRangeValue = format(rangeTemplate.getFormatString().toString(),
DEFAULT_FORMAT, newValue)
val text = if (isDragging) {
@@ -206,9 +228,13 @@ class ToggleRangeBehavior : Behavior {
}
private fun levelToRangeValue(i: Int): Float {
- val ratio = i.toFloat() / MAX_LEVEL
- return rangeTemplate.getMinValue() +
- (ratio * (rangeTemplate.getMaxValue() - rangeTemplate.getMinValue()))
+ return MathUtils.constrainedMap(rangeTemplate.minValue, rangeTemplate.maxValue,
+ MIN_LEVEL.toFloat(), MAX_LEVEL.toFloat(), i.toFloat())
+ }
+
+ private fun rangeToLevelValue(i: Float): Int {
+ return MathUtils.constrainedMap(MIN_LEVEL.toFloat(), MAX_LEVEL.toFloat(),
+ rangeTemplate.minValue, rangeTemplate.maxValue, i).toInt()
}
fun endUpdateRange() {
@@ -247,6 +273,9 @@ class ToggleRangeBehavior : Behavior {
}
override fun onLongPress(e: MotionEvent) {
+ if (isDragging) {
+ return
+ }
ControlActionCoordinator.longPress(this@ToggleRangeBehavior.cvh)
}
@@ -265,8 +294,10 @@ class ToggleRangeBehavior : Behavior {
isDragging = true
}
- this@ToggleRangeBehavior.updateRange(-xDiff / v.getWidth(),
- /* checked */ true, /* isDragging */ true)
+ val ratioDiff = -xDiff / v.width
+ val changeAmount = ((MAX_LEVEL - MIN_LEVEL) * ratioDiff).toInt()
+ this@ToggleRangeBehavior.updateRange(clipLayer.level + changeAmount,
+ checked = true, isDragging = true)
return true
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/TouchBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/TouchBehavior.kt
index b02c9c8972fc..fd96cea1541e 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/TouchBehavior.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/TouchBehavior.kt
@@ -37,7 +37,7 @@ class TouchBehavior : Behavior {
override fun initialize(cvh: ControlViewHolder) {
this.cvh = cvh
- cvh.applyRenderInfo(false)
+ cvh.applyRenderInfo(false /* enabled */, 0 /* offset */, false /* animated */)
cvh.layout.setOnClickListener(View.OnClickListener() {
ControlActionCoordinator.touch(cvh, template.getTemplateId(), control)
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index e6af36b2c86b..2c080b8efc63 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -43,7 +43,6 @@ import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.database.ContentObserver;
import android.graphics.Color;
-import android.graphics.Insets;
import android.graphics.drawable.Drawable;
import android.media.AudioManager;
import android.net.ConnectivityManager;
@@ -2178,8 +2177,10 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
ViewGroup root = (ViewGroup) mGlobalActionsLayout.getRootView();
root.setOnApplyWindowInsetsListener((v, windowInsets) -> {
if (mControlsUiController != null) {
- Insets insets = windowInsets.getInsets(WindowInsets.Type.all());
- root.setPadding(insets.left, insets.top, insets.right, insets.bottom);
+ root.setPadding(windowInsets.getStableInsetLeft(),
+ windowInsets.getStableInsetTop(),
+ windowInsets.getStableInsetRight(),
+ windowInsets.getStableInsetBottom());
}
return WindowInsets.CONSUMED;
});
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
index 62efd8ce4cee..8492fef97df5 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
@@ -82,6 +82,7 @@ public class MediaControlPanel {
protected ComponentName mRecvComponent;
private MediaDevice mDevice;
private boolean mIsRegistered = false;
+ private String mKey;
private final int[] mActionIds;
@@ -203,14 +204,15 @@ public class MediaControlPanel {
* @param bgColor
* @param contentIntent
* @param appNameString
- * @param device
+ * @param key
*/
public void setMediaSession(MediaSession.Token token, Icon icon, int iconColor,
- int bgColor, PendingIntent contentIntent, String appNameString) {
+ int bgColor, PendingIntent contentIntent, String appNameString, String key) {
mToken = token;
mForegroundColor = iconColor;
mBackgroundColor = bgColor;
mController = new MediaController(mContext, mToken);
+ mKey = key;
MediaMetadata mediaMetadata = mController.getMetadata();
@@ -326,6 +328,14 @@ public class MediaControlPanel {
}
/**
+ * Return the original notification's key
+ * @return The notification key
+ */
+ public String getKey() {
+ return mKey;
+ }
+
+ /**
* Check whether this player has an attached media session.
* @return whether there is a controller with a current media session.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt b/packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt
index b7658a9f178d..51c157a56560 100644
--- a/packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt
@@ -61,6 +61,7 @@ class SeekBarObserver(view: View) : Observer<SeekBarViewModel.Progress> {
if (!data.enabled) {
seekBarView.setEnabled(false)
seekBarView.getThumb().setAlpha(0)
+ seekBarView.setProgress(0)
elapsedTimeView.setText("")
totalTimeView.setText("")
return
diff --git a/packages/SystemUI/src/com/android/systemui/media/SeekBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/SeekBarViewModel.kt
index dd83e42cde2d..142510030a5f 100644
--- a/packages/SystemUI/src/com/android/systemui/media/SeekBarViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/SeekBarViewModel.kt
@@ -77,13 +77,25 @@ class SeekBarViewModel(val bgExecutor: DelayableExecutor) {
val seekAvailable = ((playbackState?.actions ?: 0L) and PlaybackState.ACTION_SEEK_TO) != 0L
val position = playbackState?.position?.toInt()
val duration = mediaMetadata?.getLong(MediaMetadata.METADATA_KEY_DURATION)?.toInt()
- val enabled = if (duration != null && duration <= 0) false else true
+ val enabled = if (playbackState == null ||
+ playbackState?.getState() == PlaybackState.STATE_NONE ||
+ (duration != null && duration <= 0)) false else true
_data = Progress(enabled, seekAvailable, position, duration, color)
if (shouldPollPlaybackPosition()) {
checkPlaybackPosition()
}
}
+ /**
+ * Puts the seek bar into a resumption state.
+ *
+ * This should be called when the media session behind the controller has been destroyed.
+ */
+ @AnyThread
+ fun clearController() = bgExecutor.execute {
+ _data = _data.copy(enabled = false)
+ }
+
@AnyThread
private fun checkPlaybackPosition(): Runnable = bgExecutor.executeDelayed({
val currentPosition = controller?.playbackState?.position?.toInt()
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
index c3779efcf4b2..ba9a30fb554f 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
@@ -339,18 +339,18 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio
@Override
public void onPipTransitionFinished(ComponentName activity, int direction) {
- onPipTransitionFinishedOrCanceled();
+ onPipTransitionFinishedOrCanceled(direction);
}
@Override
public void onPipTransitionCanceled(ComponentName activity, int direction) {
- onPipTransitionFinishedOrCanceled();
+ onPipTransitionFinishedOrCanceled(direction);
}
- private void onPipTransitionFinishedOrCanceled() {
+ private void onPipTransitionFinishedOrCanceled(int direction) {
// Re-enable touches after the animation completes
mTouchHandler.setTouchEnabled(true);
- mTouchHandler.onPinnedStackAnimationEnded();
+ mTouchHandler.onPinnedStackAnimationEnded(direction);
mMenuController.onPinnedStackAnimationEnded();
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
index 2b9b1716cb18..ec15dd16f46e 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
@@ -54,6 +54,7 @@ import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
+import android.os.Looper;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
@@ -129,9 +130,7 @@ public class PipMenuActivity extends Activity {
}
};
- private Handler mHandler = new Handler();
- private Messenger mToControllerMessenger;
- private Messenger mMessenger = new Messenger(new Handler() {
+ private Handler mHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
@@ -174,7 +173,9 @@ public class PipMenuActivity extends Activity {
}
}
}
- });
+ };
+ private Messenger mToControllerMessenger;
+ private Messenger mMessenger = new Messenger(mHandler);
private final Runnable mFinishRunnable = new Runnable() {
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
index d660b670446b..61ed40d5d782 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
@@ -30,6 +30,7 @@ import android.graphics.Rect;
import android.os.Bundle;
import android.os.Debug;
import android.os.Handler;
+import android.os.Looper;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
@@ -122,7 +123,7 @@ public class PipMenuActivityController {
private boolean mStartActivityRequested;
private long mStartActivityRequestedTime;
private Messenger mToActivityMessenger;
- private Handler mHandler = new Handler() {
+ private Handler mHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
@@ -133,15 +134,15 @@ public class PipMenuActivityController {
break;
}
case MESSAGE_EXPAND_PIP: {
- mListeners.forEach(l -> l.onPipExpand());
+ mListeners.forEach(Listener::onPipExpand);
break;
}
case MESSAGE_DISMISS_PIP: {
- mListeners.forEach(l -> l.onPipDismiss());
+ mListeners.forEach(Listener::onPipDismiss);
break;
}
case MESSAGE_SHOW_MENU: {
- mListeners.forEach(l -> l.onPipShowMenu());
+ mListeners.forEach(Listener::onPipShowMenu);
break;
}
case MESSAGE_UPDATE_ACTIVITY_CALLBACK: {
@@ -259,6 +260,8 @@ public class PipMenuActivityController {
if (DEBUG) {
Log.d(TAG, "showMenu() state=" + menuState
+ " hasActivity=" + (mToActivityMessenger != null)
+ + " allowMenuTimeout=" + allowMenuTimeout
+ + " willResizeMenu=" + willResizeMenu
+ " callers=\n" + Debug.getCallers(5, " "));
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java
index 0b076559ae36..d80f18a983ee 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java
@@ -56,7 +56,6 @@ public class PipResizeGestureHandler {
private final DisplayMetrics mDisplayMetrics = new DisplayMetrics();
private final PipBoundsHandler mPipBoundsHandler;
- private final PipTouchHandler mPipTouchHandler;
private final PipMotionHelper mMotionHelper;
private final int mDisplayId;
private final Executor mMainExecutor;
@@ -70,10 +69,10 @@ public class PipResizeGestureHandler {
private final Rect mTmpBounds = new Rect();
private final int mDelta;
- private boolean mAllowGesture = false;
+ private boolean mAllowGesture;
private boolean mIsAttached;
private boolean mIsEnabled;
- private boolean mEnablePipResize;
+ private boolean mEnableUserResize;
private InputMonitor mInputMonitor;
private InputEventReceiver mInputEventReceiver;
@@ -82,21 +81,20 @@ public class PipResizeGestureHandler {
private int mCtrlType;
public PipResizeGestureHandler(Context context, PipBoundsHandler pipBoundsHandler,
- PipTouchHandler pipTouchHandler, PipMotionHelper motionHelper,
- DeviceConfigProxy deviceConfig, PipTaskOrganizer pipTaskOrganizer) {
+ PipMotionHelper motionHelper, DeviceConfigProxy deviceConfig,
+ PipTaskOrganizer pipTaskOrganizer) {
final Resources res = context.getResources();
context.getDisplay().getMetrics(mDisplayMetrics);
mDisplayId = context.getDisplayId();
mMainExecutor = context.getMainExecutor();
mPipBoundsHandler = pipBoundsHandler;
- mPipTouchHandler = pipTouchHandler;
mMotionHelper = motionHelper;
mPipTaskOrganizer = pipTaskOrganizer;
context.getDisplay().getRealSize(mMaxSize);
mDelta = res.getDimensionPixelSize(R.dimen.pip_resize_edge_size);
- mEnablePipResize = DeviceConfig.getBoolean(
+ mEnableUserResize = DeviceConfig.getBoolean(
DeviceConfig.NAMESPACE_SYSTEMUI,
PIP_USER_RESIZE,
/* defaultValue = */ true);
@@ -105,7 +103,7 @@ public class PipResizeGestureHandler {
@Override
public void onPropertiesChanged(DeviceConfig.Properties properties) {
if (properties.getKeyset().contains(PIP_USER_RESIZE)) {
- mEnablePipResize = properties.getBoolean(
+ mEnableUserResize = properties.getBoolean(
PIP_USER_RESIZE, /* defaultValue = */ true);
}
}
@@ -134,7 +132,7 @@ public class PipResizeGestureHandler {
}
private void updateIsEnabled() {
- boolean isEnabled = mIsAttached && mEnablePipResize;
+ boolean isEnabled = mIsAttached && mEnableUserResize;
if (isEnabled == mIsEnabled) {
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
index 350ce293a13f..f5c83c1fffd7 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -16,6 +16,7 @@
package com.android.systemui.pip.phone;
+import static com.android.systemui.pip.PipAnimationController.TRANSITION_DIRECTION_TO_PIP;
import static com.android.systemui.pip.phone.PipMenuActivityController.MENU_STATE_CLOSE;
import static com.android.systemui.pip.phone.PipMenuActivityController.MENU_STATE_FULL;
import static com.android.systemui.pip.phone.PipMenuActivityController.MENU_STATE_NONE;
@@ -56,6 +57,7 @@ import androidx.dynamicanimation.animation.SpringForce;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.logging.MetricsLoggerWrapper;
import com.android.systemui.R;
+import com.android.systemui.pip.PipAnimationController;
import com.android.systemui.pip.PipBoundsHandler;
import com.android.systemui.pip.PipSnapAlgorithm;
import com.android.systemui.pip.PipTaskOrganizer;
@@ -229,7 +231,7 @@ public class PipTouchHandler {
mMotionHelper = new PipMotionHelper(mContext, activityTaskManager, pipTaskOrganizer,
mMenuController, mSnapAlgorithm, mFlingAnimationUtils, floatingContentCoordinator);
mPipResizeGestureHandler =
- new PipResizeGestureHandler(context, pipBoundsHandler, this, mMotionHelper,
+ new PipResizeGestureHandler(context, pipBoundsHandler, mMotionHelper,
deviceConfig, pipTaskOrganizer);
mTouchState = new PipTouchState(ViewConfiguration.get(context), mHandler,
() -> mMenuController.showMenu(MENU_STATE_FULL, mMotionHelper.getBounds(),
@@ -266,6 +268,10 @@ public class PipTouchHandler {
mMagnetizedPip = mMotionHelper.getMagnetizedPip();
mMagneticTarget = mMagnetizedPip.addTarget(mTargetView, 0);
+
+ // Set the magnetic field radius equal to twice the size of the target.
+ mMagneticTarget.setMagneticFieldRadiusPx(targetSize * 2);
+
mMagnetizedPip.setPhysicsAnimatorUpdateListener(mMotionHelper.mResizePipUpdateListener);
mMagnetizedPip.setMagnetListener(new MagnetizedObject.MagnetListener() {
@Override
@@ -339,11 +345,16 @@ public class PipTouchHandler {
mPipResizeGestureHandler.onActivityUnpinned();
}
- public void onPinnedStackAnimationEnded() {
+ public void onPinnedStackAnimationEnded(
+ @PipAnimationController.TransitionDirection int direction) {
// Always synchronize the motion helper bounds once PiP animations finish
mMotionHelper.synchronizePinnedStackBounds();
updateMovementBounds();
- mResizedBounds.set(mMotionHelper.getBounds());
+ if (direction == TRANSITION_DIRECTION_TO_PIP) {
+ // updates mResizedBounds only if it's an entering PiP animation
+ // mResized should be otherwise updated in setMenuState.
+ mResizedBounds.set(mMotionHelper.getBounds());
+ }
if (mShowPipMenuOnAnimationEnd) {
mMenuController.showMenu(MENU_STATE_CLOSE, mMotionHelper.getBounds(),
@@ -504,9 +515,6 @@ public class PipTouchHandler {
mTargetView.setTranslationY(mTargetViewContainer.getHeight());
mTargetViewContainer.setVisibility(View.VISIBLE);
- // Set the magnetic field radius to half of PIP's width.
- mMagneticTarget.setMagneticFieldRadiusPx(mMotionHelper.getBounds().width());
-
// Cancel in case we were in the middle of animating it out.
mMagneticTargetAnimator.cancel();
mMagneticTargetAnimator
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java b/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java
index e636707a9722..e4bcb0921284 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java
@@ -99,15 +99,14 @@ public class QSMediaPlayer extends MediaControlPanel {
* @param bgColor background color
* @param actionsContainer a LinearLayout containing the media action buttons
* @param notif reference to original notification
- * @param device current playback device
+ * @param key original notification's key
*/
public void setMediaSession(MediaSession.Token token, Icon icon, int iconColor,
- int bgColor, View actionsContainer, Notification notif) {
+ int bgColor, View actionsContainer, Notification notif, String key) {
String appName = Notification.Builder.recoverBuilder(getContext(), notif)
.loadHeaderAppName();
- super.setMediaSession(token, icon, iconColor, bgColor, notif.contentIntent,
- appName);
+ super.setMediaSession(token, icon, iconColor, bgColor, notif.contentIntent, appName, key);
// Media controls
LinearLayout parentActionsLayout = (LinearLayout) actionsContainer;
@@ -171,6 +170,8 @@ public class QSMediaPlayer extends MediaControlPanel {
public void clearControls() {
super.clearControls();
+ mSeekBarViewModel.clearController();
+
View guts = mMediaNotifView.findViewById(R.id.media_guts);
View options = mMediaNotifView.findViewById(R.id.qs_media_controls_options);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 0566b2e621db..40c8aadfd5d4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -208,9 +208,10 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne
* @param bgColor
* @param actionsContainer
* @param notif
+ * @param key
*/
public void addMediaSession(MediaSession.Token token, Icon icon, int iconColor, int bgColor,
- View actionsContainer, StatusBarNotification notif) {
+ View actionsContainer, StatusBarNotification notif, String key) {
if (!useQsMediaPlayer(mContext)) {
// Shouldn't happen, but just in case
Log.e(TAG, "Tried to add media session without player!");
@@ -225,13 +226,12 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne
String packageName = notif.getPackageName();
for (QSMediaPlayer p : mMediaPlayers) {
if (p.getMediaSessionToken().equals(token)) {
- Log.d(TAG, "a player for this session already exists");
+ Log.d(TAG, "Found matching player by token " + packageName);
player = p;
break;
- }
-
- if (packageName.equals(p.getMediaPlayerPackage())) {
- Log.d(TAG, "found an old session for this app");
+ } else if (packageName.equals(p.getMediaPlayerPackage()) && key.equals(p.getKey())) {
+ // Also match if it's the same package and notification key
+ Log.d(TAG, "Found matching player by package " + packageName + ", " + key);
player = p;
break;
}
@@ -267,7 +267,7 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne
Log.d(TAG, "setting player session");
player.setMediaSession(token, icon, iconColor, bgColor, actionsContainer,
- notif.getNotification());
+ notif.getNotification(), key);
if (mMediaPlayers.size() > 0) {
((View) mMediaCarousel.getParent()).setVisibility(View.VISIBLE);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSMediaPlayer.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSMediaPlayer.java
index 0ba4cb159024..794677912626 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSMediaPlayer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSMediaPlayer.java
@@ -67,9 +67,10 @@ public class QuickQSMediaPlayer extends MediaControlPanel {
* @param actionsToShow indices of which actions to display in the mini player
* (max 3: Notification.MediaStyle.MAX_MEDIA_BUTTONS_IN_COMPACT)
* @param contentIntent Intent to send when user taps on the view
+ * @param key original notification's key
*/
public void setMediaSession(MediaSession.Token token, Icon icon, int iconColor, int bgColor,
- View actionsContainer, int[] actionsToShow, PendingIntent contentIntent) {
+ View actionsContainer, int[] actionsToShow, PendingIntent contentIntent, String key) {
// Only update if this is a different session and currently playing
String oldPackage = "";
if (getController() != null) {
@@ -84,7 +85,7 @@ public class QuickQSMediaPlayer extends MediaControlPanel {
return;
}
- super.setMediaSession(token, icon, iconColor, bgColor, contentIntent, null);
+ super.setMediaSession(token, icon, iconColor, bgColor, contentIntent, null, key);
LinearLayout parentActionsLayout = (LinearLayout) actionsContainer;
int i = 0;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
index 666323766c12..9cee7e7ccba4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
@@ -72,6 +72,7 @@ public class ScreenRecordTile extends QSTileImpl<QSTile.BooleanState>
state.value = isRecording || isStarting;
state.state = (isRecording || isStarting) ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
+ state.label = mContext.getString(R.string.quick_settings_screen_record_label);
if (isRecording) {
state.icon = ResourceIcon.get(R.drawable.ic_qs_screenrecord);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
index 0d7715958995..25f1a974bc36 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
@@ -250,7 +250,8 @@ class NotificationShadeDepthController @Inject constructor(
private fun updateShadeBlur() {
var newBlur = 0
val state = statusBarStateController.state
- if (state == StatusBarState.SHADE || state == StatusBarState.SHADE_LOCKED) {
+ if ((state == StatusBarState.SHADE || state == StatusBarState.SHADE_LOCKED) &&
+ !keyguardStateController.isKeyguardFadingAway) {
newBlur = blurUtils.blurRadiusOfRatio(shadeExpansion)
}
shadeSpring.animateTo(newBlur)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
index a27199370b16..ab2cffa57c3e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.notification.row;
import static android.app.Notification.EXTRA_IS_GROUP_CONVERSATION;
+import static android.app.NotificationManager.BUBBLE_PREFERENCE_ALL;
import static android.app.NotificationManager.BUBBLE_PREFERENCE_NONE;
import static android.app.NotificationManager.BUBBLE_PREFERENCE_SELECTED;
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
@@ -33,6 +34,7 @@ import android.app.INotificationManager;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationChannelGroup;
+import android.app.NotificationManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
@@ -51,6 +53,7 @@ import android.transition.TransitionManager;
import android.transition.TransitionSet;
import android.util.AttributeSet;
import android.util.Log;
+import android.util.Slog;
import android.view.View;
import android.view.accessibility.AccessibilityEvent;
import android.widget.ImageView;
@@ -91,6 +94,7 @@ public class NotificationConversationInfo extends LinearLayout implements
private String mConversationId;
private StatusBarNotification mSbn;
private boolean mIsDeviceProvisioned;
+ private int mAppBubble;
private TextView mPriorityDescriptionView;
private TextView mDefaultDescriptionView;
@@ -206,6 +210,13 @@ public class NotificationConversationInfo extends LinearLayout implements
mNotificationChannel = NotificationChannelHelper.createConversationChannelIfNeeded(
getContext(), mINotificationManager, entry, mNotificationChannel);
+ try {
+ mAppBubble = mINotificationManager.getBubblePreferenceForPackage(mPackageName, mAppUid);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "can't reach OS", e);
+ mAppBubble = BUBBLE_PREFERENCE_SELECTED;
+ }
+
bindHeader();
bindActions();
@@ -227,6 +238,11 @@ public class NotificationConversationInfo extends LinearLayout implements
snooze.setOnClickListener(mOnSnoozeClick);
*/
+ if (mAppBubble == BUBBLE_PREFERENCE_ALL) {
+ ((TextView) findViewById(R.id.default_summary)).setText(getResources().getString(
+ R.string.notification_channel_summary_default_with_bubbles, mAppName));
+ }
+
findViewById(R.id.priority).setOnClickListener(mOnFavoriteClick);
findViewById(R.id.default_behavior).setOnClickListener(mOnDefaultClick);
findViewById(R.id.silence).setOnClickListener(mOnMuteClick);
@@ -264,7 +280,6 @@ public class NotificationConversationInfo extends LinearLayout implements
// bindName();
bindPackage();
bindIcon(mNotificationChannel.isImportantConversation());
-
}
private void bindIcon(boolean important) {
@@ -560,10 +575,7 @@ public class NotificationConversationInfo extends LinearLayout implements
!mChannelToUpdate.isImportantConversation());
if (mChannelToUpdate.isImportantConversation()) {
mChannelToUpdate.setAllowBubbles(true);
- int currentPref =
- mINotificationManager.getBubblePreferenceForPackage(
- mAppPkg, mAppUid);
- if (currentPref == BUBBLE_PREFERENCE_NONE) {
+ if (mAppBubble == BUBBLE_PREFERENCE_NONE) {
mINotificationManager.setBubblesAllowed(mAppPkg, mAppUid,
BUBBLE_PREFERENCE_SELECTED);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java
index 874d81db0bd2..2da2724aacb2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java
@@ -193,7 +193,8 @@ public class NotificationMediaTemplateViewWrapper extends NotificationTemplateVi
mBackgroundColor,
mActions,
compactActions,
- notif.contentIntent);
+ notif.contentIntent,
+ sbn.getKey());
QSPanel bigPanel = ctrl.getNotificationShadeView().findViewById(
com.android.systemui.R.id.quick_settings_panel);
bigPanel.addMediaSession(token,
@@ -201,7 +202,8 @@ public class NotificationMediaTemplateViewWrapper extends NotificationTemplateVi
tintColor,
mBackgroundColor,
mActions,
- sbn);
+ sbn,
+ sbn.getKey());
}
boolean showCompactSeekbar = mMediaManager.getShowCompactMediaSeekbar();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
index f103bd01fc3f..7d422e3c15a2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
@@ -312,7 +312,7 @@ public class EdgeBackGestureHandler implements DisplayListener,
WindowManagerGlobal.getWindowManagerService()
.unregisterSystemGestureExclusionListener(
mGestureExclusionListener, mDisplayId);
- } catch (RemoteException e) {
+ } catch (RemoteException | IllegalArgumentException e) {
Log.e(TAG, "Failed to unregister window manager callbacks", e);
}
@@ -326,7 +326,7 @@ public class EdgeBackGestureHandler implements DisplayListener,
WindowManagerGlobal.getWindowManagerService()
.registerSystemGestureExclusionListener(
mGestureExclusionListener, mDisplayId);
- } catch (RemoteException e) {
+ } catch (RemoteException | IllegalArgumentException e) {
Log.e(TAG, "Failed to register window manager callbacks", e);
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/magnetictarget/MagnetizedObject.kt b/packages/SystemUI/src/com/android/systemui/util/magnetictarget/MagnetizedObject.kt
index f27bdbfbeda0..e905e6772074 100644
--- a/packages/SystemUI/src/com/android/systemui/util/magnetictarget/MagnetizedObject.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/magnetictarget/MagnetizedObject.kt
@@ -27,6 +27,7 @@ import android.provider.Settings
import android.view.MotionEvent
import android.view.VelocityTracker
import android.view.View
+import android.view.ViewConfiguration
import androidx.dynamicanimation.animation.DynamicAnimation
import androidx.dynamicanimation.animation.FloatPropertyCompat
import androidx.dynamicanimation.animation.SpringForce
@@ -146,6 +147,10 @@ abstract class MagnetizedObject<T : Any>(
private val velocityTracker: VelocityTracker = VelocityTracker.obtain()
private val vibrator: Vibrator = context.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
+ private var touchDown = PointF()
+ private var touchSlop = 0
+ private var movedBeyondSlop = false
+
/** Whether touch events are presently occurring within the magnetic field area of a target. */
val objectStuckToTarget: Boolean
get() = targetObjectIsStuckTo != null
@@ -324,15 +329,32 @@ abstract class MagnetizedObject<T : Any>(
// When a gesture begins, recalculate target views' positions on the screen in case they
// have changed. Also, clear state.
if (ev.action == MotionEvent.ACTION_DOWN) {
- updateTargetViewLocations()
+ updateTargetViews()
- // Clear the velocity tracker and assume we're not stuck to a target yet.
+ // Clear the velocity tracker and stuck target.
velocityTracker.clear()
targetObjectIsStuckTo = null
+
+ // Set the touch down coordinates and reset movedBeyondSlop.
+ touchDown.set(ev.rawX, ev.rawY)
+ movedBeyondSlop = false
}
+ // Always pass events to the VelocityTracker.
addMovement(ev)
+ // If we haven't yet moved beyond the slop distance, check if we have.
+ if (!movedBeyondSlop) {
+ val dragDistance = hypot(ev.rawX - touchDown.x, ev.rawY - touchDown.y)
+ if (dragDistance > touchSlop) {
+ // If we're beyond the slop distance, save that and continue.
+ movedBeyondSlop = true
+ } else {
+ // Otherwise, don't do anything yet.
+ return false
+ }
+ }
+
val targetObjectIsInMagneticFieldOf = associatedTargets.firstOrNull { target ->
val distanceFromTargetCenter = hypot(
ev.rawX - target.centerOnScreen.x,
@@ -559,8 +581,14 @@ abstract class MagnetizedObject<T : Any>(
}
/** Updates the locations on screen of all of the [associatedTargets]. */
- internal fun updateTargetViewLocations() {
+ internal fun updateTargetViews() {
associatedTargets.forEach { it.updateLocationOnScreen() }
+
+ // Update the touch slop, since the configuration may have changed.
+ if (associatedTargets.size > 0) {
+ touchSlop =
+ ViewConfiguration.get(associatedTargets[0].targetView.context).scaledTouchSlop
+ }
}
/**
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarViewModelTest.kt
index f316d0480fac..28a3d6a32a61 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarViewModelTest.kt
@@ -86,7 +86,7 @@ public class SeekBarViewModelTest : SysuiTestCase() {
}
@Test
- fun updateDuration() {
+ fun updateDurationWithPlayback() {
// GIVEN that the duration is contained within the metadata
val duration = 12000L
val metadata = MediaMetadata.Builder().run {
@@ -94,6 +94,12 @@ public class SeekBarViewModelTest : SysuiTestCase() {
build()
}
whenever(mockController.getMetadata()).thenReturn(metadata)
+ // AND a valid playback state (ie. media session is not destroyed)
+ val state = PlaybackState.Builder().run {
+ setState(PlaybackState.STATE_PLAYING, 200L, 1f)
+ build()
+ }
+ whenever(mockController.getPlaybackState()).thenReturn(state)
// WHEN the controller is updated
viewModel.updateController(mockController, Color.RED)
// THEN the duration is extracted
@@ -102,6 +108,22 @@ public class SeekBarViewModelTest : SysuiTestCase() {
}
@Test
+ fun updateDurationWithoutPlayback() {
+ // GIVEN that the duration is contained within the metadata
+ val duration = 12000L
+ val metadata = MediaMetadata.Builder().run {
+ putLong(MediaMetadata.METADATA_KEY_DURATION, duration)
+ build()
+ }
+ whenever(mockController.getMetadata()).thenReturn(metadata)
+ // WHEN the controller is updated
+ viewModel.updateController(mockController, Color.RED)
+ // THEN the duration is extracted
+ assertThat(viewModel.progress.value!!.duration).isEqualTo(duration)
+ assertThat(viewModel.progress.value!!.enabled).isFalse()
+ }
+
+ @Test
fun updateDurationNegative() {
// GIVEN that the duration is negative
val duration = -1L
@@ -110,6 +132,12 @@ public class SeekBarViewModelTest : SysuiTestCase() {
build()
}
whenever(mockController.getMetadata()).thenReturn(metadata)
+ // AND a valid playback state (ie. media session is not destroyed)
+ val state = PlaybackState.Builder().run {
+ setState(PlaybackState.STATE_PLAYING, 200L, 1f)
+ build()
+ }
+ whenever(mockController.getPlaybackState()).thenReturn(state)
// WHEN the controller is updated
viewModel.updateController(mockController, Color.RED)
// THEN the seek bar is disabled
@@ -125,6 +153,12 @@ public class SeekBarViewModelTest : SysuiTestCase() {
build()
}
whenever(mockController.getMetadata()).thenReturn(metadata)
+ // AND a valid playback state (ie. media session is not destroyed)
+ val state = PlaybackState.Builder().run {
+ setState(PlaybackState.STATE_PLAYING, 200L, 1f)
+ build()
+ }
+ whenever(mockController.getPlaybackState()).thenReturn(state)
// WHEN the controller is updated
viewModel.updateController(mockController, Color.RED)
// THEN the seek bar is disabled
@@ -372,4 +406,30 @@ public class SeekBarViewModelTest : SysuiTestCase() {
// THEN an update task is queued
assertThat(fakeExecutor.numPending()).isEqualTo(1)
}
+
+ @Test
+ fun clearSeekBar() {
+ // GIVEN that the duration is contained within the metadata
+ val metadata = MediaMetadata.Builder().run {
+ putLong(MediaMetadata.METADATA_KEY_DURATION, 12000L)
+ build()
+ }
+ whenever(mockController.getMetadata()).thenReturn(metadata)
+ // AND a valid playback state (ie. media session is not destroyed)
+ val state = PlaybackState.Builder().run {
+ setState(PlaybackState.STATE_PLAYING, 200L, 1f)
+ build()
+ }
+ whenever(mockController.getPlaybackState()).thenReturn(state)
+ // AND the controller has been updated
+ viewModel.updateController(mockController, Color.RED)
+ // WHEN the controller is cleared on the event when the session is destroyed
+ viewModel.clearController()
+ with(fakeExecutor) {
+ advanceClockToNext()
+ runAllReady()
+ }
+ // THEN the seek bar is disabled
+ assertThat(viewModel.progress.value!!.enabled).isFalse()
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
index b6bd5e213dd4..6bcaee100496 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
@@ -17,6 +17,8 @@
package com.android.systemui.statusbar.notification.row;
import static android.app.Notification.FLAG_BUBBLE;
+import static android.app.NotificationManager.BUBBLE_PREFERENCE_ALL;
+import static android.app.NotificationManager.BUBBLE_PREFERENCE_SELECTED;
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
import static android.app.NotificationManager.IMPORTANCE_HIGH;
import static android.app.NotificationManager.IMPORTANCE_LOW;
@@ -458,7 +460,9 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
}
@Test
- public void testBindNotification_defaultSelected_notFave_notSilent() {
+ public void testBindNotification_defaultSelected_notFave_notSilent() throws Exception {
+ when(mMockINotificationManager.getBubblePreferenceForPackage(anyString(), anyInt()))
+ .thenReturn(BUBBLE_PREFERENCE_SELECTED);
mConversationChannel.setImportance(IMPORTANCE_HIGH);
mConversationChannel.setImportantConversation(false);
mConversationChannel.setAllowBubbles(true);
@@ -476,6 +480,35 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
true);
View view = mNotificationInfo.findViewById(R.id.default_behavior);
assertThat(view.isSelected()).isTrue();
+ assertThat(((TextView) view.findViewById(R.id.default_summary)).getText()).isEqualTo(
+ mContext.getString(R.string.notification_channel_summary_default));
+ }
+
+ @Test
+ public void testBindNotification_default_allCanBubble() throws Exception {
+ when(mMockINotificationManager.getBubblePreferenceForPackage(anyString(), anyInt()))
+ .thenReturn(BUBBLE_PREFERENCE_ALL);
+ when(mMockPackageManager.getApplicationLabel(any())).thenReturn("App Name");
+ mConversationChannel.setImportance(IMPORTANCE_HIGH);
+ mConversationChannel.setImportantConversation(false);
+ mConversationChannel.setAllowBubbles(true);
+ mNotificationInfo.bindNotification(
+ mShortcutManager,
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mEntry,
+ null,
+ null,
+ mIconFactory,
+ true);
+ View view = mNotificationInfo.findViewById(R.id.default_behavior);
+ assertThat(view.isSelected()).isTrue();
+ assertThat(((TextView) view.findViewById(R.id.default_summary)).getText()).isEqualTo(
+ mContext.getString(R.string.notification_channel_summary_default_with_bubbles,
+ "App Name"));
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/magnetictarget/MagnetizedObjectTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/magnetictarget/MagnetizedObjectTest.kt
index f6b7b74d4bfc..251ca9c8dcb2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/magnetictarget/MagnetizedObjectTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/magnetictarget/MagnetizedObjectTest.kt
@@ -186,8 +186,8 @@ class MagnetizedObjectTest : SysuiTestCase() {
@Test
fun testMotionEventConsumption_downInMagneticField() {
- // We should consume DOWN events if they occur in the field.
- assertTrue(magnetizedObject.maybeConsumeMotionEvent(getMotionEvent(
+ // We should not consume DOWN events even if they occur in the field.
+ assertFalse(magnetizedObject.maybeConsumeMotionEvent(getMotionEvent(
x = targetCenterX, y = targetCenterY, action = MotionEvent.ACTION_DOWN)))
}
@@ -342,10 +342,14 @@ class MagnetizedObjectTest : SysuiTestCase() {
// Trigger the magnet animation, and block the test until it ends.
PhysicsAnimatorTestUtils.setAllAnimationsBlock(true)
magnetizedObject.maybeConsumeMotionEvent(getMotionEvent(
- x = targetCenterX,
- y = targetCenterY,
+ x = targetCenterX - 250,
+ y = targetCenterY - 250,
action = MotionEvent.ACTION_DOWN))
+ magnetizedObject.maybeConsumeMotionEvent(getMotionEvent(
+ x = targetCenterX,
+ y = targetCenterY))
+
// The object's (top-left) position should now position it centered over the target.
assertEquals(targetCenterX - objectSize / 2, objectX)
assertEquals(targetCenterY - objectSize / 2, objectY)
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java b/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java
index f3cead92be7e..bae54a5c76b9 100644
--- a/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java
+++ b/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java
@@ -922,8 +922,10 @@ public class Tethering {
case WifiManager.WIFI_AP_STATE_ENABLED:
enableWifiIpServingLocked(ifname, ipmode);
break;
- case WifiManager.WIFI_AP_STATE_DISABLED:
case WifiManager.WIFI_AP_STATE_DISABLING:
+ // We can see this state on the way to disabled.
+ break;
+ case WifiManager.WIFI_AP_STATE_DISABLED:
case WifiManager.WIFI_AP_STATE_FAILED:
default:
disableWifiIpServingLocked(ifname, curState);
diff --git a/packages/Tethering/tests/integration/Android.bp b/packages/Tethering/tests/integration/Android.bp
index 620261b375d2..6b751afdf58b 100644
--- a/packages/Tethering/tests/integration/Android.bp
+++ b/packages/Tethering/tests/integration/Android.bp
@@ -13,19 +13,12 @@
// See the License for the specific language governing permissions and
// limitations under the License.
//
-
-android_test {
- name: "TetheringIntegrationTests",
- certificate: "platform",
- platform_apis: true,
+java_defaults {
+ name: "TetheringIntegrationTestsDefaults",
srcs: [
"src/**/*.java",
"src/**/*.kt",
],
- test_suites: [
- "device-tests",
- "mts",
- ],
static_libs: [
"NetworkStackApiStableLib",
"androidx.test.rules",
@@ -44,4 +37,49 @@ android_test {
"libdexmakerjvmtiagent",
"libstaticjvmtiagent",
],
+ jarjar_rules: ":NetworkStackJarJarRules",
+}
+
+android_library {
+ name: "TetheringIntegrationTestsLib",
+ platform_apis: true,
+ defaults: ["TetheringIntegrationTestsDefaults"],
+ visibility: ["//cts/tests/tests/tethering"]
+}
+
+android_test {
+ name: "TetheringIntegrationTests",
+ platform_apis: true,
+ defaults: ["TetheringIntegrationTestsDefaults"],
+ test_suites: [
+ "device-tests",
+ "mts",
+ ],
+ compile_multilib: "both",
}
+
+// Special version of the tethering tests that includes all tests necessary for code coverage
+// purposes. This is currently the union of TetheringTests, TetheringIntegrationTests and
+// NetworkStackTests.
+android_test {
+ name: "TetheringCoverageTests",
+ certificate: "platform",
+ platform_apis: true,
+ test_suites: ["device-tests", "mts"],
+ test_config: "AndroidTest_Coverage.xml",
+ defaults: ["libnetworkstackutilsjni_deps"],
+ static_libs: [
+ "NetworkStackTestsLib",
+ "TetheringTestsLib",
+ "TetheringIntegrationTestsLib",
+ ],
+ jni_libs: [
+ // For mockito extended
+ "libdexmakerjvmtiagent",
+ "libstaticjvmtiagent",
+ // For NetworkStackUtils included in NetworkStackBase
+ "libnetworkstackutilsjni",
+ ],
+ compile_multilib: "both",
+ manifest: "AndroidManifest_coverage.xml",
+} \ No newline at end of file
diff --git a/packages/Tethering/tests/integration/AndroidManifest.xml b/packages/Tethering/tests/integration/AndroidManifest.xml
index 233ba40b5d35..fddfaad29f0f 100644
--- a/packages/Tethering/tests/integration/AndroidManifest.xml
+++ b/packages/Tethering/tests/integration/AndroidManifest.xml
@@ -17,7 +17,6 @@
package="com.android.networkstack.tethering.tests.integration">
<uses-permission android:name="android.permission.INTERNET"/>
- <uses-permission android:name="android.permission.TETHER_PRIVILEGED"/>
<application android:debuggable="true">
<uses-library android:name="android.test.runner" />
diff --git a/packages/Tethering/tests/integration/AndroidManifest_coverage.xml b/packages/Tethering/tests/integration/AndroidManifest_coverage.xml
new file mode 100644
index 000000000000..06de00d78558
--- /dev/null
+++ b/packages/Tethering/tests/integration/AndroidManifest_coverage.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ package="com.android.networkstack.tethering.tests.coverage">
+
+ <application tools:replace="android:label"
+ android:debuggable="true"
+ android:label="Tethering coverage tests">
+ <uses-library android:name="android.test.runner" />
+ </application>
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.networkstack.tethering.tests.coverage"
+ android:label="Tethering coverage tests">
+ </instrumentation>
+</manifest>
diff --git a/packages/Tethering/tests/integration/AndroidTest_Coverage.xml b/packages/Tethering/tests/integration/AndroidTest_Coverage.xml
new file mode 100644
index 000000000000..3def2099e45f
--- /dev/null
+++ b/packages/Tethering/tests/integration/AndroidTest_Coverage.xml
@@ -0,0 +1,12 @@
+<configuration description="Runs coverage tests for Tethering">
+ <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+ <option name="test-file-name" value="TetheringCoverageTests.apk" />
+ </target_preparer>
+
+ <option name="test-tag" value="TetheringCoverageTests" />
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="com.android.networkstack.tethering.tests.coverage" />
+ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+ <option name="hidden-api-checks" value="false"/>
+ </test>
+</configuration> \ No newline at end of file
diff --git a/packages/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java b/packages/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
index b02bb23f9807..4bac9da9c3d2 100644
--- a/packages/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
+++ b/packages/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
@@ -18,6 +18,7 @@ package android.net;
import static android.Manifest.permission.MANAGE_TEST_NETWORKS;
import static android.Manifest.permission.NETWORK_SETTINGS;
+import static android.Manifest.permission.TETHER_PRIVILEGED;
import static android.net.TetheringManager.TETHERING_ETHERNET;
import static org.junit.Assert.assertEquals;
@@ -109,7 +110,8 @@ public class EthernetTetheringTest {
mTetheredInterfaceRequester = new TetheredInterfaceRequester(mHandler, mEm);
// Needed to create a TestNetworkInterface, to call requestTetheredInterface, and to receive
// tethered client callbacks.
- mUiAutomation.adoptShellPermissionIdentity(MANAGE_TEST_NETWORKS, NETWORK_SETTINGS);
+ mUiAutomation.adoptShellPermissionIdentity(
+ MANAGE_TEST_NETWORKS, NETWORK_SETTINGS, TETHER_PRIVILEGED);
}
private void cleanUp() throws Exception {
diff --git a/packages/Tethering/tests/unit/Android.bp b/packages/Tethering/tests/unit/Android.bp
index 59681e9eb56a..4849fd5d01f5 100644
--- a/packages/Tethering/tests/unit/Android.bp
+++ b/packages/Tethering/tests/unit/Android.bp
@@ -14,39 +14,33 @@
// limitations under the License.
//
-android_test {
- name: "TetheringTests",
- certificate: "platform",
+java_defaults {
+ name: "TetheringTestsDefaults",
srcs: [
"src/**/*.java",
"src/**/*.kt",
],
- test_suites: [
- "device-tests",
- "mts",
- ],
- compile_multilib: "both",
static_libs: [
+ "TetheringApiCurrentLib",
"androidx.test.rules",
"frameworks-base-testutils",
- "net-tests-utils",
"mockito-target-extended-minus-junit4",
- "TetheringApiCurrentLib",
+ "net-tests-utils",
"testables",
],
// TODO(b/147200698) change sdk_version to module-current and
// remove framework-minus-apex, ext, and framework-res
sdk_version: "core_platform",
libs: [
- "framework-minus-apex",
- "ext",
- "framework-res",
- "framework-wifi-stubs-module_libs_api",
- "framework-telephony-stubs",
"android.test.runner",
"android.test.base",
"android.test.mock",
+ "ext",
+ "framework-minus-apex",
+ "framework-res",
+ "framework-telephony-stubs",
"framework-tethering",
+ "framework-wifi-stubs-module_libs_api",
],
jni_libs: [
// For mockito extended
@@ -55,3 +49,25 @@ android_test {
],
jarjar_rules: "jarjar-rules.txt",
}
+
+// Library containing the unit tests. This is used by the coverage test target to pull in the
+// unit test code. It is not currently used by the tests themselves because all the build
+// configuration needed by the tests is in the TetheringTestsDefaults rule.
+android_library {
+ name: "TetheringTestsLib",
+ defaults: ["TetheringTestsDefaults"],
+ visibility: [
+ "//frameworks/base/packages/Tethering/tests/integration",
+ ]
+}
+
+android_test {
+ name: "TetheringTests",
+ certificate: "platform",
+ test_suites: [
+ "device-tests",
+ "mts",
+ ],
+ defaults: ["TetheringTestsDefaults"],
+ compile_multilib: "both",
+}
diff --git a/read-snapshot.txt b/read-snapshot.txt
new file mode 100644
index 000000000000..e69de29bb2d1
--- /dev/null
+++ b/read-snapshot.txt
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 7230b00f87ad..d252f9e69a22 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -2441,7 +2441,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
* accessibility button.
* 2) For {@link AccessibilityManager#ACCESSIBILITY_SHORTCUT_KEY} type and service targeting sdk
* version <= Q: turns on / off the accessibility service.
- * 3) For services targeting sdk version > Q:
+ * 3) For {@link AccessibilityManager#ACCESSIBILITY_SHORTCUT_KEY} type and service targeting sdk
+ * version > Q and request accessibility button: turn on the accessibility service if it's
+ * not in the enabled state.
+ * (It'll happen when a service is disabled and assigned to shortcut then upgraded.)
+ * 4) For services targeting sdk version > Q:
* a) Turns on / off the accessibility service, if service does not request accessibility
* button.
* b) Callbacks to accessibility service if service is bounded and requests accessibility
@@ -2475,6 +2479,13 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
return true;
}
+ if (shortcutType == ACCESSIBILITY_SHORTCUT_KEY && targetSdk > Build.VERSION_CODES.Q
+ && requestA11yButton) {
+ if (!userState.getEnabledServicesLocked().contains(assignedTarget)) {
+ enableAccessibilityServiceLocked(assignedTarget, mCurrentUserId);
+ return true;
+ }
+ }
// Callbacks to a11y service if it's bounded and requests a11y button.
if (serviceConnection == null
|| !userState.mBoundServices.contains(serviceConnection)
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 3d6861898aaf..9d1ad4239a24 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -2653,6 +2653,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
} else if (viewState.id.equals(this.mCurrentViewId)
&& (viewState.getState() & ViewState.STATE_INLINE_SHOWN) != 0) {
requestShowInlineSuggestionsLocked(viewState.getResponse(), filterText);
+ } else if (viewState.id.equals(this.mCurrentViewId)
+ && (viewState.getState() & ViewState.STATE_TRIGGERED_AUGMENTED_AUTOFILL) != 0) {
+ if (!TextUtils.isEmpty(filterText)) {
+ mInlineSessionController.hideInlineSuggestionsUiLocked(mCurrentViewId);
+ }
}
viewState.setState(ViewState.STATE_CHANGED);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index e41ba0e1745d..2a9f503602ac 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -465,6 +465,8 @@ public class ActivityManagerService extends IActivityManager.Stub
static final String SYSTEM_DEBUGGABLE = "ro.debuggable";
+ static final String SYSTEM_USER_HOME_NEEDED = "ro.system_user_home_needed";
+
public static final String ANR_TRACE_DIR = "/data/anr";
// Maximum number of receivers an app can register.
@@ -9592,7 +9594,8 @@ public class ActivityManagerService extends IActivityManager.Stub
// to handle home activity in this case.
if (UserManager.isSplitSystemUser() &&
Settings.Secure.getInt(mContext.getContentResolver(),
- Settings.Secure.USER_SETUP_COMPLETE, 0) != 0) {
+ Settings.Secure.USER_SETUP_COMPLETE, 0) != 0
+ || SystemProperties.getBoolean(SYSTEM_USER_HOME_NEEDED, false)) {
t.traceBegin("enableHomeActivity");
ComponentName cName = new ComponentName(mContext, SystemUserHomeActivity.class);
try {
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 89fa02bbbd64..cce749d5a7ef 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -1668,6 +1668,33 @@ public final class ProcessList {
return gidArray;
}
+ private boolean shouldEnableTaggedPointers(ProcessRecord app) {
+ // Ensure we have platform + kernel support for TBI.
+ if (!Zygote.nativeSupportsTaggedPointers()) {
+ return false;
+ }
+
+ // Check to ensure the app hasn't explicitly opted-out of TBI via. the manifest attribute.
+ if (!app.info.allowsNativeHeapPointerTagging()) {
+ return false;
+ }
+
+ // Check to see that the compat feature for TBI is enabled.
+ if (!mPlatformCompat.isChangeEnabled(NATIVE_HEAP_POINTER_TAGGING, app.info)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ private int decideTaggingLevel(ProcessRecord app) {
+ if (shouldEnableTaggedPointers(app)) {
+ return Zygote.MEMORY_TAG_LEVEL_TBI;
+ }
+
+ return 0;
+ }
+
private int decideGwpAsanLevel(ProcessRecord app) {
// Look at the process attribute first.
if (app.processInfo != null
@@ -1856,15 +1883,6 @@ public final class ProcessList {
runtimeFlags |= Zygote.USE_APP_IMAGE_STARTUP_CACHE;
}
- if (Zygote.nativeSupportsTaggedPointers()) {
- // Enable heap pointer tagging if supported by the kernel, unless disabled by the
- // app manifest, target sdk level, or compat feature.
- if (app.info.allowsNativeHeapPointerTagging()
- && mPlatformCompat.isChangeEnabled(NATIVE_HEAP_POINTER_TAGGING, app.info)) {
- runtimeFlags |= Zygote.MEMORY_TAG_LEVEL_TBI;
- }
- }
-
runtimeFlags |= decideGwpAsanLevel(app);
String invokeWith = null;
@@ -1895,6 +1913,20 @@ public final class ProcessList {
app.setRequiredAbi(requiredAbi);
app.instructionSet = instructionSet;
+ // If instructionSet is non-null, this indicates that the system_server is spawning a
+ // process with an ISA that may be different from its own. System (kernel and hardware)
+ // compatililty for these features is checked in the decideTaggingLevel in the
+ // system_server process (not the child process). As TBI is only supported in aarch64,
+ // we can simply ensure that the new process is also aarch64. This prevents the mismatch
+ // where a 64-bit system server spawns a 32-bit child that thinks it should enable some
+ // tagging variant. Theoretically, a 32-bit system server could exist that spawns 64-bit
+ // processes, in which case the new process won't get any tagging. This is fine as we
+ // haven't seen this configuration in practice, and we can reasonable assume that if
+ // tagging is desired, the system server will be 64-bit.
+ if (instructionSet == null || instructionSet.equals("arm64")) {
+ runtimeFlags |= decideTaggingLevel(app);
+ }
+
// the per-user SELinux context must be set
if (TextUtils.isEmpty(app.info.seInfoUser)) {
Slog.wtf(ActivityManagerService.TAG, "SELinux tag not defined",
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index e02c6f9d5497..546025a2498f 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -40,6 +40,7 @@ import static com.android.server.am.UserState.STATE_RUNNING_LOCKED;
import static com.android.server.am.UserState.STATE_RUNNING_UNLOCKED;
import static com.android.server.am.UserState.STATE_RUNNING_UNLOCKING;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
@@ -89,6 +90,7 @@ import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseIntArray;
+import android.util.SparseLongArray;
import android.util.proto.ProtoOutputStream;
import com.android.internal.R;
@@ -112,6 +114,7 @@ import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
+import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.atomic.AtomicInteger;
/**
@@ -162,6 +165,46 @@ class UserController implements Handler.Callback {
// TODO(b/149604218): STOPSHIP remove this constant and the logcat
private static final boolean TESTS_NEED_LOGCAT = true;
+ // Used for statsd logging with UserLifecycleJourneyReported + UserLifecycleEventOccurred atoms
+ private static final long INVALID_SESSION_ID = 0;
+
+ // The various user journeys, defined in the UserLifecycleJourneyReported atom for statsd
+ private static final int USER_JOURNEY_UNKNOWN =
+ FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__UNKNOWN;
+ private static final int USER_JOURNEY_USER_SWITCH_FG =
+ FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_SWITCH_FG;
+ private static final int USER_JOURNEY_USER_SWITCH_UI =
+ FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_SWITCH_UI;
+ private static final int USER_JOURNEY_USER_START =
+ FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_START;
+ private static final int USER_JOURNEY_USER_CREATE =
+ FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_CREATE;
+ @IntDef(prefix = { "USER_JOURNEY" }, value = {
+ USER_JOURNEY_UNKNOWN,
+ USER_JOURNEY_USER_SWITCH_FG,
+ USER_JOURNEY_USER_SWITCH_UI,
+ USER_JOURNEY_USER_START,
+ USER_JOURNEY_USER_CREATE,
+ })
+ @interface UserJourney {}
+
+ // The various user lifecycle events, defined in the UserLifecycleEventOccurred atom for statsd
+ private static final int USER_LIFECYCLE_EVENT_UNKNOWN =
+ FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__UNKNOWN;
+ private static final int USER_LIFECYCLE_EVENT_SWITCH_USER =
+ FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__SWITCH_USER;
+ private static final int USER_LIFECYCLE_EVENT_START_USER =
+ FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__START_USER;
+ private static final int USER_LIFECYCLE_EVENT_CREATE_USER =
+ FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__CREATE_USER;
+ @IntDef(prefix = { "USER_LIFECYCLE_EVENT" }, value = {
+ USER_LIFECYCLE_EVENT_UNKNOWN,
+ USER_LIFECYCLE_EVENT_SWITCH_USER,
+ USER_LIFECYCLE_EVENT_START_USER,
+ USER_LIFECYCLE_EVENT_CREATE_USER,
+ })
+ @interface UserLifecycleEvent {}
+
/**
* Maximum number of users we allow to be running at a time, including system user.
*
@@ -270,6 +313,13 @@ class UserController implements Handler.Callback {
@GuardedBy("mLock")
private final ArrayList<Integer> mLastActiveUsers = new ArrayList<>();
+ /**
+ * A per-user, journey to session id map, used for statsd logging for the
+ * UserLifecycleJourneyReported and UserLifecycleEventOccurred atoms.
+ */
+ @GuardedBy("mUserJourneyToSessionIdMap")
+ private final SparseArray<SparseLongArray> mUserJourneyToSessionIdMap = new SparseArray<>();
+
UserController(ActivityManagerService service) {
this(new Injector(service));
}
@@ -2349,6 +2399,10 @@ class UserController implements Handler.Callback {
public boolean handleMessage(Message msg) {
switch (msg.what) {
case START_USER_SWITCH_FG_MSG:
+ logUserJourneyInfo(getUserInfo(getCurrentUserId()), getUserInfo(msg.arg1),
+ USER_JOURNEY_USER_SWITCH_FG);
+ logUserLifecycleEvent(msg.arg1, USER_JOURNEY_USER_SWITCH_FG,
+ USER_LIFECYCLE_EVENT_SWITCH_USER, true);
startUserInForeground(msg.arg1);
break;
case REPORT_USER_SWITCH_MSG:
@@ -2370,8 +2424,14 @@ class UserController implements Handler.Callback {
mInjector.batteryStatsServiceNoteEvent(
BatteryStats.HistoryItem.EVENT_USER_RUNNING_START,
Integer.toString(msg.arg1), msg.arg1);
+ logUserJourneyInfo(null, getUserInfo(msg.arg1), USER_JOURNEY_USER_START);
+ logUserLifecycleEvent(msg.arg1, USER_JOURNEY_USER_START,
+ USER_LIFECYCLE_EVENT_START_USER, true);
mInjector.getSystemServiceManager().startUser(TimingsTraceAndSlog.newAsyncLog(),
msg.arg1);
+ logUserLifecycleEvent(msg.arg1, USER_JOURNEY_USER_START,
+ USER_LIFECYCLE_EVENT_START_USER, false);
+ clearSessionId(msg.arg1, USER_JOURNEY_USER_START);
break;
case USER_UNLOCK_MSG:
final int userId = msg.arg1;
@@ -2400,17 +2460,94 @@ class UserController implements Handler.Callback {
break;
case REPORT_USER_SWITCH_COMPLETE_MSG:
dispatchUserSwitchComplete(msg.arg1);
+ final int currentJourney = mUserSwitchUiEnabled ? USER_JOURNEY_USER_SWITCH_UI
+ : USER_JOURNEY_USER_SWITCH_FG;
+ logUserLifecycleEvent(msg.arg1, currentJourney,
+ USER_LIFECYCLE_EVENT_SWITCH_USER, false);
+ clearSessionId(msg.arg1, currentJourney);
break;
case REPORT_LOCKED_BOOT_COMPLETE_MSG:
dispatchLockedBootComplete(msg.arg1);
break;
case START_USER_SWITCH_UI_MSG:
- showUserSwitchDialog((Pair<UserInfo, UserInfo>) msg.obj);
+ final Pair<UserInfo, UserInfo> fromToUserPair = (Pair<UserInfo, UserInfo>) msg.obj;
+ logUserJourneyInfo(fromToUserPair.first, fromToUserPair.second,
+ USER_JOURNEY_USER_SWITCH_UI);
+ logUserLifecycleEvent(fromToUserPair.second.id, USER_JOURNEY_USER_SWITCH_UI,
+ USER_LIFECYCLE_EVENT_SWITCH_USER, true);
+ showUserSwitchDialog(fromToUserPair);
break;
}
return false;
}
+ /**
+ * statsd helper method for logging the start of a user journey via a UserLifecycleEventOccurred
+ * atom given the originating and targeting users for the journey.
+ *
+ * Note: these info atoms are currently logged more than once per journey since there is no
+ * state associated with the user's ongoing journey - this will be updated in a later CL.
+ */
+ private void logUserJourneyInfo(UserInfo origin, UserInfo target, @UserJourney int journey) {
+ final long newSessionId = ThreadLocalRandom.current().nextLong(1, Long.MAX_VALUE);
+ synchronized (mUserJourneyToSessionIdMap) {
+ SparseLongArray userSessions = mUserJourneyToSessionIdMap.get(target.id);
+ if (userSessions == null) {
+ userSessions = new SparseLongArray();
+ mUserJourneyToSessionIdMap.put(target.id, userSessions);
+ }
+ final long oldSessionId = userSessions.get(journey);
+ if (oldSessionId != INVALID_SESSION_ID) {
+ // potentially an incomplete or timed-out session
+ FrameworkStatsLog.write(FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED,
+ oldSessionId, target.id, USER_LIFECYCLE_EVENT_UNKNOWN,
+ FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__STATE__NONE);
+ }
+ // update session id
+ userSessions.put(journey, newSessionId);
+ }
+
+ FrameworkStatsLog.write(FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED, newSessionId,
+ journey, origin != null ? origin.id : -1,
+ target.id, UserManager.getUserTypeForStatsd(target.userType), target.flags);
+ }
+
+ /**
+ * statsd helper method for logging the begin or finish of the given event for the
+ * UserLifecycleEventOccurred statsd atom.
+ * Note: This does not clear the user's journey session id - if this event represents the end of
+ * a particular journey, call {@link #clearSessionId} to indicate that the session is over.
+ */
+ private void logUserLifecycleEvent(@UserIdInt int userId, @UserJourney int journey,
+ @UserLifecycleEvent int event, boolean begin) {
+ final long sessionId;
+ synchronized (mUserJourneyToSessionIdMap) {
+ final SparseLongArray eventToSessionMap = mUserJourneyToSessionIdMap.get(userId);
+ if (eventToSessionMap == null || eventToSessionMap.size() == 0) {
+ return;
+ }
+ sessionId = eventToSessionMap.get(journey);
+ if (sessionId == INVALID_SESSION_ID) {
+ return;
+ }
+ }
+
+ FrameworkStatsLog.write(FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED, sessionId, userId,
+ event, begin ? FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__STATE__BEGIN
+ : FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__STATE__FINISH);
+ }
+
+ /**
+ * Clears the user's session id associated with the given UserJourney (for statsd).
+ */
+ private void clearSessionId(@UserIdInt int userId, @UserJourney int journey) {
+ synchronized (mUserJourneyToSessionIdMap) {
+ if (mUserJourneyToSessionIdMap.get(userId) != null) {
+ mUserJourneyToSessionIdMap.get(userId).delete(journey);
+ }
+ }
+ }
+
private static class UserProgressListener extends IProgressListener.Stub {
private volatile long mUnlockStarted;
@Override
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 31bcceaba889..8ecda8f1a131 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -2830,7 +2830,6 @@ public class AppOpsService extends IAppOpsService.Stub {
private int checkOperationImpl(int code, int uid, String packageName,
boolean raw) {
- verifyIncomingUid(uid);
verifyIncomingOp(code);
String resolvedPackageName = resolvePackageName(uid, packageName);
if (resolvedPackageName == null) {
diff --git a/services/core/java/com/android/server/appop/TEST_MAPPING b/services/core/java/com/android/server/appop/TEST_MAPPING
index 9c03a3606e6c..604b9f1ead6d 100644
--- a/services/core/java/com/android/server/appop/TEST_MAPPING
+++ b/services/core/java/com/android/server/appop/TEST_MAPPING
@@ -38,6 +38,9 @@
},
{
"name": "CtsAppTestCases:ActivityManagerApi29Test"
+ },
+ {
+ "name": "UidAtomTests:testAppOps"
}
]
}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 387a2be41d3c..f840f2d359d5 100755
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -185,6 +185,9 @@ public class AudioService extends IAudioService.Stub
private static final String TAG = "AS.AudioService";
+ private final AudioSystemAdapter mAudioSystem;
+ private final SystemServerAdapter mSystemServer;
+
/** Debug audio mode */
protected static final boolean DEBUG_MODE = false;
@@ -649,10 +652,19 @@ public class AudioService extends IAudioService.Stub
/** @hide */
public AudioService(Context context) {
+ this(context, AudioSystemAdapter.getDefaultAdapter(),
+ SystemServerAdapter.getDefaultAdapter(context));
+ }
+
+ public AudioService(Context context, AudioSystemAdapter audioSystem,
+ SystemServerAdapter systemServer) {
mContext = context;
mContentResolver = context.getContentResolver();
mAppOps = (AppOpsManager)context.getSystemService(Context.APP_OPS_SERVICE);
+ mAudioSystem = audioSystem;
+ mSystemServer = systemServer;
+
mPlatformType = AudioSystem.getPlatformType(context);
mIsSingleVolume = AudioSystem.isSingleVolume(context);
@@ -842,11 +854,13 @@ public class AudioService extends IAudioService.Stub
context.registerReceiverAsUser(mReceiver, UserHandle.ALL, intentFilter, null, null);
- LocalServices.addService(AudioManagerInternal.class, new AudioServiceInternal());
+ if (mSystemServer.isPrivileged()) {
+ LocalServices.addService(AudioManagerInternal.class, new AudioServiceInternal());
- mUserManagerInternal.addUserRestrictionsListener(mUserRestrictionsListener);
+ mUserManagerInternal.addUserRestrictionsListener(mUserRestrictionsListener);
- mRecordMonitor.initMonitor();
+ mRecordMonitor.initMonitor();
+ }
final float[] preScale = new float[3];
preScale[0] = mContext.getResources().getFraction(
@@ -935,7 +949,7 @@ public class AudioService extends IAudioService.Stub
onIndicateSystemReady();
- mMicMuteFromSystemCached = AudioSystem.isMicrophoneMuted();
+ mMicMuteFromSystemCached = mAudioSystem.isMicrophoneMuted();
setMicMuteFromSwitchInput();
}
@@ -1636,12 +1650,15 @@ public class AudioService extends IAudioService.Stub
}
if (currentImeUid != mCurrentImeUid || forceUpdate) {
- AudioSystem.setCurrentImeUid(currentImeUid);
+ mAudioSystem.setCurrentImeUid(currentImeUid);
mCurrentImeUid = currentImeUid;
}
}
private void readPersistedSettings() {
+ if (!mSystemServer.isPrivileged()) {
+ return;
+ }
final ContentResolver cr = mContentResolver;
int ringerModeFromSettings =
@@ -1712,6 +1729,9 @@ public class AudioService extends IAudioService.Stub
}
private void readUserRestrictions() {
+ if (!mSystemServer.isPrivileged()) {
+ return;
+ }
final int currentUser = getCurrentUserId();
// Check the current user restriction.
@@ -2782,6 +2802,9 @@ public class AudioService extends IAudioService.Stub
}
private void sendBroadcastToAll(Intent intent) {
+ if (!mSystemServer.isPrivileged()) {
+ return;
+ }
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
final long ident = Binder.clearCallingIdentity();
@@ -3174,12 +3197,12 @@ public class AudioService extends IAudioService.Stub
}
// only mute for the current user
if (getCurrentUserId() == userId || userId == android.os.Process.SYSTEM_UID) {
- final boolean currentMute = AudioSystem.isMicrophoneMuted();
+ final boolean currentMute = mAudioSystem.isMicrophoneMuted();
final long identity = Binder.clearCallingIdentity();
- final int ret = AudioSystem.muteMicrophone(muted);
+ final int ret = mAudioSystem.muteMicrophone(muted);
// update cache with the real state independently from what was set
- mMicMuteFromSystemCached = AudioSystem.isMicrophoneMuted();
+ mMicMuteFromSystemCached = mAudioSystem.isMicrophoneMuted();
if (ret != AudioSystem.AUDIO_STATUS_OK) {
Log.e(TAG, "Error changing mic mute state to " + muted + " current:"
+ mMicMuteFromSystemCached);
@@ -4518,6 +4541,9 @@ public class AudioService extends IAudioService.Stub
}
private void broadcastRingerMode(String action, int ringerMode) {
+ if (!mSystemServer.isPrivileged()) {
+ return;
+ }
// Send sticky broadcast
Intent broadcast = new Intent(action);
broadcast.putExtra(AudioManager.EXTRA_RINGER_MODE, ringerMode);
@@ -4527,6 +4553,9 @@ public class AudioService extends IAudioService.Stub
}
private void broadcastVibrateSetting(int vibrateType) {
+ if (!mSystemServer.isPrivileged()) {
+ return;
+ }
// Send broadcast
if (mActivityManagerInternal.isSystemReady()) {
Intent broadcast = new Intent(AudioManager.VIBRATE_SETTING_CHANGED_ACTION);
@@ -5258,6 +5287,9 @@ public class AudioService extends IAudioService.Stub
}
public int observeDevicesForStream_syncVSS(boolean checkOthers) {
+ if (!mSystemServer.isPrivileged()) {
+ return AudioSystem.DEVICE_NONE;
+ }
final int devices = AudioSystem.getDevicesForStream(mStreamType);
if (devices == mObservedDevices) {
return devices;
@@ -5998,10 +6030,7 @@ public class AudioService extends IAudioService.Stub
break;
case MSG_BROADCAST_MICROPHONE_MUTE:
- mContext.sendBroadcastAsUser(
- new Intent(AudioManager.ACTION_MICROPHONE_MUTE_CHANGED)
- .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY),
- UserHandle.ALL);
+ mSystemServer.sendMicrophoneMuteChangedIntent();
break;
}
}
diff --git a/services/core/java/com/android/server/audio/AudioSystemAdapter.java b/services/core/java/com/android/server/audio/AudioSystemAdapter.java
index 9f8f9f8fddde..40c13904fbc9 100644
--- a/services/core/java/com/android/server/audio/AudioSystemAdapter.java
+++ b/services/core/java/com/android/server/audio/AudioSystemAdapter.java
@@ -40,10 +40,11 @@ public class AudioSystemAdapter {
/**
* Create an adapter for AudioSystem that always succeeds, and does nothing.
- * @return a no-op AudioSystem adapter
+ * Overridden methods can be configured
+ * @return a no-op AudioSystem adapter with configurable adapter
*/
- static final @NonNull AudioSystemAdapter getAlwaysOkAdapter() {
- return new AudioSystemOkAdapter();
+ static final @NonNull AudioSystemAdapter getConfigurableAdapter() {
+ return new AudioSystemConfigurableAdapter();
}
/**
@@ -113,10 +114,51 @@ public class AudioSystemAdapter {
return AudioSystem.setParameters(keyValuePairs);
}
+ /**
+ * Same as {@link AudioSystem#isMicrophoneMuted()}}
+ * Checks whether the microphone mute is on or off.
+ * @return true if microphone is muted, false if it's not
+ */
+ public boolean isMicrophoneMuted() {
+ return AudioSystem.isMicrophoneMuted();
+ }
+
+ /**
+ * Same as {@link AudioSystem#muteMicrophone(boolean)}
+ * Sets the microphone mute on or off.
+ *
+ * @param on set <var>true</var> to mute the microphone;
+ * <var>false</var> to turn mute off
+ * @return command completion status see AUDIO_STATUS_OK, see AUDIO_STATUS_ERROR
+ */
+ public int muteMicrophone(boolean on) {
+ return AudioSystem.muteMicrophone(on);
+ }
+
+ /**
+ * Same as {@link AudioSystem#setCurrentImeUid(int)}
+ * Communicate UID of current InputMethodService to audio policy service.
+ */
+ public int setCurrentImeUid(int uid) {
+ return AudioSystem.setCurrentImeUid(uid);
+ }
+
//--------------------------------------------------------------------
- protected static class AudioSystemOkAdapter extends AudioSystemAdapter {
+ protected static class AudioSystemConfigurableAdapter extends AudioSystemAdapter {
private static final String TAG = "ASA";
+ private boolean mIsMicMuted = false;
+ private boolean mMuteMicrophoneFails = false;
+
+ public void configureIsMicrophoneMuted(boolean muted) {
+ mIsMicMuted = muted;
+ }
+ public void configureMuteMicrophoneToFail(boolean fail) {
+ mMuteMicrophoneFails = fail;
+ }
+
+ //-----------------------------------------------------------------
+ // Overrides of AudioSystemAdapter
@Override
public int setDeviceConnectionState(int device, int state, String deviceAddress,
String deviceName, int codecFormat) {
@@ -152,5 +194,24 @@ public class AudioSystemAdapter {
public int setParameters(String keyValuePairs) {
return AudioSystem.AUDIO_STATUS_OK;
}
+
+ @Override
+ public boolean isMicrophoneMuted() {
+ return mIsMicMuted;
+ }
+
+ @Override
+ public int muteMicrophone(boolean on) {
+ if (mMuteMicrophoneFails) {
+ return AudioSystem.AUDIO_STATUS_ERROR;
+ }
+ mIsMicMuted = on;
+ return AudioSystem.AUDIO_STATUS_OK;
+ }
+
+ @Override
+ public int setCurrentImeUid(int uid) {
+ return AudioSystem.AUDIO_STATUS_OK;
+ }
}
}
diff --git a/services/core/java/com/android/server/audio/SystemServerAdapter.java b/services/core/java/com/android/server/audio/SystemServerAdapter.java
new file mode 100644
index 000000000000..509f6be76f17
--- /dev/null
+++ b/services/core/java/com/android/server/audio/SystemServerAdapter.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.audio;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.Intent;
+import android.media.AudioManager;
+import android.os.UserHandle;
+
+/**
+ * Provides an adapter to access functionality reserved to components running in system_server
+ * Functionality such as sending privileged broadcasts is to be accessed through the default
+ * adapter, whereas tests can inject a no-op adapter.
+ */
+public class SystemServerAdapter {
+
+ protected final Context mContext;
+
+ private SystemServerAdapter(@Nullable Context context) {
+ mContext = context;
+ }
+ /**
+ * Create a wrapper around privileged functionality.
+ * @return the adapter
+ */
+ static final @NonNull SystemServerAdapter getDefaultAdapter(Context context) {
+ return new SystemServerAdapter(context);
+ }
+
+ /**
+ * Create an adapter that does nothing.
+ * Use for running non-privileged tests, such as unit tests
+ * @return a no-op adapter
+ */
+ static final @NonNull SystemServerAdapter getNoOpAdapter() {
+ return new NoOpSystemServerAdapter();
+ }
+
+ /**
+ * @return true if this is supposed to be run in system_server, false otherwise (e.g. for a
+ * unit test)
+ */
+ public boolean isPrivileged() {
+ return true;
+ }
+
+ /**
+ * Broadcast ACTION_MICROPHONE_MUTE_CHANGED
+ */
+ public void sendMicrophoneMuteChangedIntent() {
+ mContext.sendBroadcastAsUser(
+ new Intent(AudioManager.ACTION_MICROPHONE_MUTE_CHANGED)
+ .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY),
+ UserHandle.ALL);
+ }
+
+ //--------------------------------------------------------------------
+ protected static class NoOpSystemServerAdapter extends SystemServerAdapter {
+
+ NoOpSystemServerAdapter() {
+ super(null);
+ }
+
+ @Override
+ public boolean isPrivileged() {
+ return false;
+ }
+
+ @Override
+ public void sendMicrophoneMuteChangedIntent() {
+ // no-op
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 4687a5117343..48e30bf42c2d 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -390,6 +390,10 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
private ObjectAnimator mColorFadeOffAnimator;
private RampAnimator<DisplayPowerState> mScreenBrightnessRampAnimator;
+ // The brightness synchronizer to allow changes in the int brightness value to be reflected in
+ // the float brightness value and vice versa.
+ @Nullable
+ private final BrightnessSynchronizer mBrightnessSynchronizer;
/**
* Creates the display power controller.
@@ -406,6 +410,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
mWindowManagerPolicy = LocalServices.getService(WindowManagerPolicy.class);
mBlanker = blanker;
mContext = context;
+ mBrightnessSynchronizer = new BrightnessSynchronizer(context);
mDisplayDevice = displayDevice;
PowerManager pm = context.getSystemService(PowerManager.class);
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index e6cb37185d71..b949d6bcf2e2 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -784,6 +784,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
private static final AtomicInteger sSequenceNumber = new AtomicInteger(0);
private static final class Entry {
+ final int mSequenceNumber = sSequenceNumber.getAndIncrement();
final ClientState mClientState;
@SoftInputModeFlags
final int mFocusedWindowSoftInputMode;
@@ -831,7 +832,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
continue;
}
pw.print(prefix);
- pw.println("SoftInputShowHideHistory #" + sSequenceNumber.getAndIncrement() + ":");
+ pw.println("SoftInputShowHideHistory #" + entry.mSequenceNumber + ":");
pw.print(prefix);
pw.println(" time=" + dataFormat.format(new Date(entry.mWallTime))
diff --git a/services/core/java/com/android/server/location/AppOpsHelper.java b/services/core/java/com/android/server/location/AppOpsHelper.java
index cb64c50bf11d..c598fb1dbe26 100644
--- a/services/core/java/com/android/server/location/AppOpsHelper.java
+++ b/services/core/java/com/android/server/location/AppOpsHelper.java
@@ -19,8 +19,8 @@ package com.android.server.location;
import static android.app.AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION;
import static android.app.AppOpsManager.OP_MONITOR_LOCATION;
-import static com.android.server.LocationManagerService.D;
-import static com.android.server.LocationManagerService.TAG;
+import static com.android.server.location.LocationManagerService.D;
+import static com.android.server.location.LocationManagerService.TAG;
import android.annotation.Nullable;
import android.app.AppOpsManager;
diff --git a/services/core/java/com/android/server/location/GeofenceManager.java b/services/core/java/com/android/server/location/GeofenceManager.java
index 195b059b7374..095cd146da4f 100644
--- a/services/core/java/com/android/server/location/GeofenceManager.java
+++ b/services/core/java/com/android/server/location/GeofenceManager.java
@@ -253,7 +253,7 @@ public class GeofenceManager implements LocationListener, PendingIntent.OnFinish
int op = CallerIdentity.asAppOp(identity.permissionLevel);
if (op >= 0) {
if (mAppOps.noteOpNoThrow(AppOpsManager.OP_FINE_LOCATION, identity.uid,
- identity.packageName, identity.featureId, null)
+ identity.packageName, identity.featureId, identity.listenerId)
!= AppOpsManager.MODE_ALLOWED) {
continue;
}
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java
index 7f25de6b3470..4f8708a7599a 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/location/LocationManagerService.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server;
+package com.android.server.location;
import static android.Manifest.permission.ACCESS_FINE_LOCATION;
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
@@ -36,6 +36,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
+import android.app.AppOpsManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -91,27 +92,14 @@ import com.android.internal.location.ProviderRequest;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
-import com.android.server.location.AbstractLocationProvider;
+import com.android.server.FgThread;
+import com.android.server.LocalServices;
+import com.android.server.PendingIntentUtils;
+import com.android.server.SystemService;
import com.android.server.location.AbstractLocationProvider.State;
-import com.android.server.location.AppForegroundHelper;
-import com.android.server.location.AppOpsHelper;
-import com.android.server.location.CallerIdentity;
import com.android.server.location.CallerIdentity.PermissionLevel;
-import com.android.server.location.GeocoderProxy;
-import com.android.server.location.GeofenceManager;
-import com.android.server.location.GeofenceProxy;
-import com.android.server.location.HardwareActivityRecognitionProxy;
-import com.android.server.location.LocationFudger;
-import com.android.server.location.LocationProviderProxy;
-import com.android.server.location.LocationRequestStatistics;
import com.android.server.location.LocationRequestStatistics.PackageProviderKey;
import com.android.server.location.LocationRequestStatistics.PackageStatistics;
-import com.android.server.location.LocationUsageLogger;
-import com.android.server.location.MockProvider;
-import com.android.server.location.MockableLocationProvider;
-import com.android.server.location.PassiveProvider;
-import com.android.server.location.SettingsHelper;
-import com.android.server.location.UserInfoHelper;
import com.android.server.location.UserInfoHelper.UserListener;
import com.android.server.location.gnss.GnssManagerService;
import com.android.server.pm.permission.PermissionManagerServiceInternal;
@@ -1620,8 +1608,8 @@ public class LocationManagerService extends ILocationManager.Stub {
// For now, make sure callers have supplied an attribution tag for use with
// AppOpsManager. This might be relaxed in the future.
final List<WorkChain> workChains = workSource.getWorkChains();
- return workChains != null && !workChains.isEmpty() &&
- workChains.get(0).getAttributionTag() != null;
+ return workChains != null && !workChains.isEmpty()
+ && workChains.get(0).getAttributionTag() != null;
}
}
@@ -1840,6 +1828,9 @@ public class LocationManagerService extends ILocationManager.Stub {
if (request == null) {
request = DEFAULT_LOCATION_REQUEST;
}
+ if (listenerId == null && intent != null) {
+ listenerId = AppOpsManager.toReceiverId(intent);
+ }
CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, featureId,
listenerId);
@@ -2106,7 +2097,8 @@ public class LocationManagerService extends ILocationManager.Stub {
request = DEFAULT_LOCATION_REQUEST;
}
- CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, featureId);
+ CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, featureId,
+ AppOpsManager.toReceiverId(intent));
identity.enforceLocationPermission();
Objects.requireNonNull(intent);
diff --git a/services/core/java/com/android/server/LocationManagerServiceUtils.java b/services/core/java/com/android/server/location/LocationManagerServiceUtils.java
index 9d0fe5e936bb..c33a70662cb5 100644
--- a/services/core/java/com/android/server/LocationManagerServiceUtils.java
+++ b/services/core/java/com/android/server/location/LocationManagerServiceUtils.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,15 +14,13 @@
* limitations under the License.
*/
-package com.android.server;
+package com.android.server.location;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.IBinder;
import android.os.RemoteException;
-import com.android.server.location.CallerIdentity;
-
import java.util.NoSuchElementException;
import java.util.function.Consumer;
diff --git a/services/core/java/com/android/server/location/LocationUsageLogger.java b/services/core/java/com/android/server/location/LocationUsageLogger.java
index 93e19df01cf3..b325deb786d6 100644
--- a/services/core/java/com/android/server/location/LocationUsageLogger.java
+++ b/services/core/java/com/android/server/location/LocationUsageLogger.java
@@ -16,7 +16,7 @@
package com.android.server.location;
-import static com.android.server.LocationManagerService.TAG;
+import static com.android.server.location.LocationManagerService.TAG;
import android.app.ActivityManager;
import android.location.Geofence;
diff --git a/services/core/java/com/android/server/location/SettingsHelper.java b/services/core/java/com/android/server/location/SettingsHelper.java
index 7ab258c29b46..cbb06b86a291 100644
--- a/services/core/java/com/android/server/location/SettingsHelper.java
+++ b/services/core/java/com/android/server/location/SettingsHelper.java
@@ -26,8 +26,8 @@ import static android.provider.Settings.Secure.LOCATION_COARSE_ACCURACY_M;
import static android.provider.Settings.Secure.LOCATION_MODE;
import static android.provider.Settings.Secure.LOCATION_MODE_OFF;
-import static com.android.server.LocationManagerService.D;
-import static com.android.server.LocationManagerService.TAG;
+import static com.android.server.location.LocationManagerService.D;
+import static com.android.server.location.LocationManagerService.TAG;
import android.app.ActivityManager;
import android.content.Context;
diff --git a/services/core/java/com/android/server/location/UserInfoHelper.java b/services/core/java/com/android/server/location/UserInfoHelper.java
index 28f3f476847b..a3dcc40bdf2d 100644
--- a/services/core/java/com/android/server/location/UserInfoHelper.java
+++ b/services/core/java/com/android/server/location/UserInfoHelper.java
@@ -18,8 +18,8 @@ package com.android.server.location;
import static android.os.UserManager.DISALLOW_SHARE_LOCATION;
-import static com.android.server.LocationManagerService.D;
-import static com.android.server.LocationManagerService.TAG;
+import static com.android.server.location.LocationManagerService.D;
+import static com.android.server.location.LocationManagerService.TAG;
import android.annotation.IntDef;
import android.annotation.Nullable;
diff --git a/services/core/java/com/android/server/location/gnss/GnssManagerService.java b/services/core/java/com/android/server/location/gnss/GnssManagerService.java
index 711f45cb7d28..3c509c380374 100644
--- a/services/core/java/com/android/server/location/gnss/GnssManagerService.java
+++ b/services/core/java/com/android/server/location/gnss/GnssManagerService.java
@@ -46,11 +46,11 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
import com.android.server.LocalServices;
-import com.android.server.LocationManagerServiceUtils.LinkedListener;
-import com.android.server.LocationManagerServiceUtils.LinkedListenerBase;
import com.android.server.location.AppForegroundHelper;
import com.android.server.location.AppOpsHelper;
import com.android.server.location.CallerIdentity;
+import com.android.server.location.LocationManagerServiceUtils.LinkedListener;
+import com.android.server.location.LocationManagerServiceUtils.LinkedListenerBase;
import com.android.server.location.LocationUsageLogger;
import com.android.server.location.RemoteListenerHelper;
import com.android.server.location.SettingsHelper;
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 9297a43b04aa..7972f247b46d 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -366,10 +366,15 @@ public class LockSettingsService extends ILockSettings.Stub {
if (mStorage.hasChildProfileLock(managedUserId)) {
return;
}
- // Do not tie it to parent when parent does not have a screen lock
+ // If parent does not have a screen lock, simply clear credential from the managed profile,
+ // to maintain the invariant that unified profile should always have the same secure state
+ // as its parent.
final int parentId = mUserManager.getProfileParent(managedUserId).id;
- if (!isUserSecure(parentId)) {
- if (DEBUG) Slog.v(TAG, "Parent does not have a screen lock");
+ if (!isUserSecure(parentId) && !managedUserPassword.isNone()) {
+ if (DEBUG) Slog.v(TAG, "Parent does not have a screen lock but profile has one");
+
+ setLockCredentialInternal(LockscreenCredential.createNone(), managedUserPassword,
+ managedUserId, /* isLockTiedToParent= */ true);
return;
}
// Do not tie when the parent has no SID (but does have a screen lock).
@@ -3161,6 +3166,21 @@ public class LockSettingsService extends ILockSettings.Stub {
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date(timestamp));
}
+ private static String credentialTypeToString(int credentialType) {
+ switch (credentialType) {
+ case CREDENTIAL_TYPE_NONE:
+ return "None";
+ case CREDENTIAL_TYPE_PATTERN:
+ return "Pattern";
+ case CREDENTIAL_TYPE_PIN:
+ return "Pin";
+ case CREDENTIAL_TYPE_PASSWORD:
+ return "Password";
+ default:
+ return "Unknown " + credentialType;
+ }
+ }
+
@Override
protected void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, TAG, printWriter)) return;
@@ -3192,7 +3212,8 @@ public class LockSettingsService extends ILockSettings.Stub {
// It's OK to dump the password type since anyone with physical access can just
// observe it from the keyguard directly.
pw.println("Quality: " + getKeyguardStoredQuality(userId));
- pw.println("CredentialType: " + getCredentialTypeInternal(userId));
+ pw.println("CredentialType: " + credentialTypeToString(
+ getCredentialTypeInternal(userId)));
pw.println("SeparateChallenge: " + getSeparateProfileChallengeEnabledInternal(userId));
pw.println(String.format("Metrics: %s",
getUserPasswordMetrics(userId) != null ? "known" : "unknown"));
diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
index 6e2feeb15e21..1345e3759d2f 100644
--- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
@@ -261,8 +261,10 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
.build();
builder.addSelectedRoute(mSelectedRouteId);
- for (MediaRoute2Info route : mBtRouteProvider.getTransferableRoutes()) {
- builder.addTransferableRoute(route.getId());
+ if (mBtRouteProvider != null) {
+ for (MediaRoute2Info route : mBtRouteProvider.getTransferableRoutes()) {
+ builder.addTransferableRoute(route.getId());
+ }
}
RoutingSessionInfo newSessionInfo = builder.setProviderId(mUniqueId).build();
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index d8264b36256d..f7d0d4ee16eb 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -46,7 +46,6 @@ import static android.net.NetworkStats.UID_ALL;
import static android.net.NetworkStatsHistory.FIELD_ALL;
import static android.net.NetworkTemplate.buildTemplateMobileWildcard;
import static android.net.NetworkTemplate.buildTemplateWifiWildcard;
-import static android.net.NetworkTemplate.getCollapsedRatType;
import static android.net.TrafficStats.KB_IN_BYTES;
import static android.net.TrafficStats.MB_IN_BYTES;
import static android.os.Trace.TRACE_TAG_NETWORK;
@@ -67,9 +66,6 @@ import static android.provider.Settings.Global.NETSTATS_UID_TAG_BUCKET_DURATION;
import static android.provider.Settings.Global.NETSTATS_UID_TAG_DELETE_AGE;
import static android.provider.Settings.Global.NETSTATS_UID_TAG_PERSIST_BYTES;
import static android.provider.Settings.Global.NETSTATS_UID_TAG_ROTATE_AGE;
-import static android.telephony.PhoneStateListener.LISTEN_NONE;
-import static android.telephony.PhoneStateListener.LISTEN_SERVICE_STATE;
-import static android.telephony.TelephonyManager.NETWORK_TYPE_UNKNOWN;
import static android.text.format.DateUtils.DAY_IN_MILLIS;
import static android.text.format.DateUtils.HOUR_IN_MILLIS;
import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
@@ -133,9 +129,7 @@ import android.provider.Settings.Global;
import android.service.NetworkInterfaceProto;
import android.service.NetworkStatsServiceDumpProto;
import android.telephony.PhoneStateListener;
-import android.telephony.ServiceState;
import android.telephony.SubscriptionPlan;
-import android.telephony.TelephonyManager;
import android.text.format.DateUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -206,7 +200,6 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
private final NetworkStatsFactory mStatsFactory;
private final AlarmManager mAlarmManager;
private final Clock mClock;
- private final TelephonyManager mTeleManager;
private final NetworkStatsSettings mSettings;
private final NetworkStatsObservers mStatsObservers;
@@ -352,6 +345,9 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
@NonNull
private final Dependencies mDeps;
+ @NonNull
+ private final NetworkStatsSubscriptionsMonitor mNetworkStatsSubscriptionsMonitor;
+
private static @NonNull File getDefaultSystemDir() {
return new File(Environment.getDataDirectory(), "system");
}
@@ -401,8 +397,8 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
PowerManager.WakeLock wakeLock =
powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
- NetworkStatsService service = new NetworkStatsService(context, networkManager, alarmManager,
- wakeLock, getDefaultClock(), context.getSystemService(TelephonyManager.class),
+ final NetworkStatsService service = new NetworkStatsService(context, networkManager,
+ alarmManager, wakeLock, getDefaultClock(),
new DefaultNetworkStatsSettings(context), new NetworkStatsFactory(),
new NetworkStatsObservers(), getDefaultSystemDir(), getDefaultBaseDir(),
new Dependencies());
@@ -416,16 +412,15 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
@VisibleForTesting
NetworkStatsService(Context context, INetworkManagementService networkManager,
AlarmManager alarmManager, PowerManager.WakeLock wakeLock, Clock clock,
- TelephonyManager teleManager, NetworkStatsSettings settings,
- NetworkStatsFactory factory, NetworkStatsObservers statsObservers, File systemDir,
- File baseDir, @NonNull Dependencies deps) {
+ NetworkStatsSettings settings, NetworkStatsFactory factory,
+ NetworkStatsObservers statsObservers, File systemDir, File baseDir,
+ @NonNull Dependencies deps) {
mContext = Objects.requireNonNull(context, "missing Context");
mNetworkManager = Objects.requireNonNull(networkManager,
- "missing INetworkManagementService");
+ "missing INetworkManagementService");
mAlarmManager = Objects.requireNonNull(alarmManager, "missing AlarmManager");
mClock = Objects.requireNonNull(clock, "missing Clock");
mSettings = Objects.requireNonNull(settings, "missing NetworkStatsSettings");
- mTeleManager = Objects.requireNonNull(teleManager, "missing TelephonyManager");
mWakeLock = Objects.requireNonNull(wakeLock, "missing WakeLock");
mStatsFactory = Objects.requireNonNull(factory, "missing factory");
mStatsObservers = Objects.requireNonNull(statsObservers, "missing NetworkStatsObservers");
@@ -437,7 +432,8 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
final HandlerThread handlerThread = mDeps.makeHandlerThread();
handlerThread.start();
mHandler = new NetworkStatsHandler(handlerThread.getLooper());
- mPhoneListener = new NetworkTypeListener(new HandlerExecutor(mHandler));
+ mNetworkStatsSubscriptionsMonitor = deps.makeSubscriptionsMonitor(mContext,
+ new HandlerExecutor(mHandler), this);
}
/**
@@ -453,6 +449,19 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
public HandlerThread makeHandlerThread() {
return new HandlerThread(TAG);
}
+
+ /**
+ * Create a {@link NetworkStatsSubscriptionsMonitor}, can be used to monitor RAT change
+ * event in NetworkStatsService.
+ */
+ @NonNull
+ public NetworkStatsSubscriptionsMonitor makeSubscriptionsMonitor(@NonNull Context context,
+ @NonNull Executor executor, @NonNull NetworkStatsService service) {
+ // TODO: Update RatType passively in NSS, instead of querying into the monitor
+ // when forceUpdateIface.
+ return new NetworkStatsSubscriptionsMonitor(context, executor, (subscriberId, type) ->
+ service.handleOnCollapsedRatTypeChanged());
+ }
}
private void registerLocalService() {
@@ -517,11 +526,10 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME, currentRealtime,
mSettings.getPollInterval(), pollIntent);
- // TODO: 1. listen to changes from all subscriptions.
- // 2. listen to settings changed to support dynamically enable/disable.
+ // TODO: listen to settings changed to support dynamically enable/disable.
// watch for networkType changes
if (!mSettings.getCombineSubtypeEnabled()) {
- mTeleManager.listen(mPhoneListener, LISTEN_SERVICE_STATE);
+ mNetworkStatsSubscriptionsMonitor.start();
}
registerGlobalAlert();
@@ -544,7 +552,9 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
mContext.unregisterReceiver(mUserReceiver);
mContext.unregisterReceiver(mShutdownReceiver);
- mTeleManager.listen(mPhoneListener, LISTEN_NONE);
+ if (!mSettings.getCombineSubtypeEnabled()) {
+ mNetworkStatsSubscriptionsMonitor.stop();
+ }
final long currentTime = mClock.millis();
@@ -1197,35 +1207,14 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
};
/**
- * Receiver that watches for {@link TelephonyManager} changes, such as
- * transitioning between Radio Access Technology(RAT) types.
+ * Handle collapsed RAT type changed event.
*/
- @NonNull
- private final NetworkTypeListener mPhoneListener;
-
- class NetworkTypeListener extends PhoneStateListener {
- private volatile int mLastCollapsedRatType = NETWORK_TYPE_UNKNOWN;
-
- NetworkTypeListener(@NonNull Executor executor) {
- super(executor);
- }
-
- @Override
- public void onServiceStateChanged(@NonNull ServiceState ss) {
- final int networkType = ss.getDataNetworkType();
- final int collapsedRatType = getCollapsedRatType(networkType);
- if (collapsedRatType == mLastCollapsedRatType) return;
-
- if (LOGD) {
- Log.d(TAG, "subtype changed for mobile: "
- + mLastCollapsedRatType + " -> " + collapsedRatType);
- }
- // Protect service from frequently updating. Remove pending messages if any.
- mHandler.removeMessages(MSG_UPDATE_IFACES);
- mLastCollapsedRatType = collapsedRatType;
- mHandler.sendMessageDelayed(
- mHandler.obtainMessage(MSG_UPDATE_IFACES), mSettings.getPollDelay());
- }
+ @VisibleForTesting
+ public void handleOnCollapsedRatTypeChanged() {
+ // Protect service from frequently updating. Remove pending messages if any.
+ mHandler.removeMessages(MSG_UPDATE_IFACES);
+ mHandler.sendMessageDelayed(
+ mHandler.obtainMessage(MSG_UPDATE_IFACES), mSettings.getPollDelay());
}
private void updateIfaces(
@@ -1352,8 +1341,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
return 0;
}
- // TODO: return different subType for different subscriptions.
- return mPhoneListener.mLastCollapsedRatType;
+ return mNetworkStatsSubscriptionsMonitor.getRatTypeForSubscriberId(state.subscriberId);
}
private static <K> NetworkIdentitySet findOrCreateNetworkIdentitySet(
diff --git a/services/core/java/com/android/server/notification/ConditionProviders.java b/services/core/java/com/android/server/notification/ConditionProviders.java
index aed29272cada..e98326b620b2 100644
--- a/services/core/java/com/android/server/notification/ConditionProviders.java
+++ b/services/core/java/com/android/server/notification/ConditionProviders.java
@@ -30,6 +30,7 @@ import android.provider.Settings;
import android.service.notification.Condition;
import android.service.notification.ConditionProviderService;
import android.service.notification.IConditionProvider;
+import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Slog;
@@ -54,7 +55,6 @@ public class ConditionProviders extends ManagedServices {
private final ArraySet<String> mSystemConditionProviderNames;
private final ArraySet<SystemConditionProviderService> mSystemConditionProviders
= new ArraySet<>();
-
private Callback mCallback;
public ConditionProviders(Context context, UserProfiles userProfiles, IPackageManager pm) {
@@ -195,6 +195,21 @@ public class ConditionProviders extends ManagedServices {
}
@Override
+ protected void loadDefaultsFromConfig() {
+ String defaultDndAccess = mContext.getResources().getString(
+ R.string.config_defaultDndAccessPackages);
+ if (defaultDndAccess != null) {
+ String[] dnds = defaultDndAccess.split(ManagedServices.ENABLED_SERVICES_SEPARATOR);
+ for (int i = 0; i < dnds.length; i++) {
+ if (TextUtils.isEmpty(dnds[i])) {
+ continue;
+ }
+ addDefaultComponentOrPackage(dnds[i]);
+ }
+ }
+ }
+
+ @Override
protected void onServiceRemovedLocked(ManagedServiceInfo removed) {
if (removed == null) return;
for (int i = mRecords.size() - 1; i >= 0; i--) {
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index 45df3686d056..5d3dc5f19714 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -21,6 +21,7 @@ import static android.content.Context.BIND_AUTO_CREATE;
import static android.content.Context.BIND_FOREGROUND_SERVICE;
import static android.content.Context.DEVICE_POLICY_SERVICE;
import static android.os.UserHandle.USER_ALL;
+import static android.os.UserHandle.USER_SYSTEM;
import android.annotation.NonNull;
import android.app.ActivityManager;
@@ -96,6 +97,8 @@ abstract public class ManagedServices {
private static final int ON_BINDING_DIED_REBIND_DELAY_MS = 10000;
protected static final String ENABLED_SERVICES_SEPARATOR = ":";
+ private static final String DB_VERSION_1 = "1";
+
/**
* List of components and apps that can have running {@link ManagedServices}.
@@ -107,7 +110,7 @@ abstract public class ManagedServices {
static final String ATT_VERSION = "version";
static final String ATT_DEFAULTS = "defaults";
- static final int DB_VERSION = 1;
+ static final int DB_VERSION = 2;
static final int APPROVAL_BY_PACKAGE = 0;
static final int APPROVAL_BY_COMPONENT = 1;
@@ -187,17 +190,22 @@ abstract public class ManagedServices {
protected void addDefaultComponentOrPackage(String packageOrComponent) {
if (!TextUtils.isEmpty(packageOrComponent)) {
synchronized (mDefaultsLock) {
- ComponentName cn = ComponentName.unflattenFromString(packageOrComponent);
- if (cn == null) {
+ if (mApprovalLevel == APPROVAL_BY_PACKAGE) {
mDefaultPackages.add(packageOrComponent);
- } else {
+ return;
+ }
+ ComponentName cn = ComponentName.unflattenFromString(packageOrComponent);
+ if (cn != null && mApprovalLevel == APPROVAL_BY_COMPONENT) {
mDefaultPackages.add(cn.getPackageName());
mDefaultComponents.add(cn);
+ return;
}
}
}
}
+ protected abstract void loadDefaultsFromConfig();
+
boolean isDefaultComponentOrPackage(String packageOrComponent) {
synchronized (mDefaultsLock) {
ComponentName cn = ComponentName.unflattenFromString(packageOrComponent);
@@ -504,19 +512,19 @@ abstract public class ManagedServices {
void readDefaults(XmlPullParser parser) {
String defaultComponents = XmlUtils.readStringAttribute(parser, ATT_DEFAULTS);
- if (defaultComponents == null) {
- return;
- }
- String[] components = defaultComponents.split(ENABLED_SERVICES_SEPARATOR);
- synchronized (mDefaultsLock) {
- for (int i = 0; i < components.length; i++) {
- if (!TextUtils.isEmpty(components[i])) {
- ComponentName cn = ComponentName.unflattenFromString(components[i]);
- if (cn != null) {
- mDefaultPackages.add(cn.getPackageName());
- mDefaultComponents.add(cn);
- } else {
- mDefaultPackages.add(components[i]);
+
+ if (!TextUtils.isEmpty(defaultComponents)) {
+ String[] components = defaultComponents.split(ENABLED_SERVICES_SEPARATOR);
+ synchronized (mDefaultsLock) {
+ for (int i = 0; i < components.length; i++) {
+ if (!TextUtils.isEmpty(components[i])) {
+ ComponentName cn = ComponentName.unflattenFromString(components[i]);
+ if (cn != null) {
+ mDefaultPackages.add(cn.getPackageName());
+ mDefaultComponents.add(cn);
+ } else {
+ mDefaultPackages.add(components[i]);
+ }
}
}
}
@@ -531,9 +539,11 @@ abstract public class ManagedServices {
throws XmlPullParserException, IOException {
// read grants
int type;
+ String version = "";
readDefaults(parser);
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
String tag = parser.getName();
+ version = XmlUtils.readStringAttribute(parser, ATT_VERSION);
if (type == XmlPullParser.END_TAG
&& getConfig().xmlTag.equals(tag)) {
break;
@@ -561,9 +571,38 @@ abstract public class ManagedServices {
}
}
}
+ boolean isVersionOne = TextUtils.isEmpty(version) || DB_VERSION_1.equals(version);
+ if (isVersionOne) {
+ upgradeToVersionTwo();
+ }
rebindServices(false, USER_ALL);
}
+ private void upgradeToVersionTwo() {
+ // check if any defaults are loaded
+ int defaultsSize = mDefaultComponents.size() + mDefaultPackages.size();
+ if (defaultsSize == 0) {
+ // load defaults from current allowed
+ if (this.mApprovalLevel == APPROVAL_BY_COMPONENT) {
+ List<ComponentName> approvedComponents = getAllowedComponents(USER_SYSTEM);
+ for (int i = 0; i < approvedComponents.size(); i++) {
+ addDefaultComponentOrPackage(approvedComponents.get(i).flattenToString());
+ }
+ }
+ if (this.mApprovalLevel == APPROVAL_BY_PACKAGE) {
+ List<String> approvedPkgs = getAllowedPackages(USER_SYSTEM);
+ for (int i = 0; i < approvedPkgs.size(); i++) {
+ addDefaultComponentOrPackage(approvedPkgs.get(i));
+ }
+ }
+ }
+ // if no defaults are loaded, then load from config
+ defaultsSize = mDefaultComponents.size() + mDefaultPackages.size();
+ if (defaultsSize == 0) {
+ loadDefaultsFromConfig();
+ }
+ }
+
/**
* Read extra attributes in the {@link #TAG_MANAGED_SERVICES} tag.
*/
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 54efe543a29f..9b02b48f7825 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -108,6 +108,7 @@ import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
+import android.annotation.UserIdInt;
import android.annotation.WorkerThread;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
@@ -156,6 +157,7 @@ import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ParceledListSlice;
import android.content.pm.ShortcutInfo;
+import android.content.pm.ShortcutServiceInternal;
import android.content.pm.UserInfo;
import android.content.res.Resources;
import android.database.ContentObserver;
@@ -220,6 +222,7 @@ import android.util.Log;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.SparseArrayMap;
import android.util.StatsEvent;
import android.util.Xml;
import android.util.proto.ProtoOutputStream;
@@ -291,6 +294,7 @@ import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
+import java.util.LinkedList;
import java.util.List;
import java.util.Map.Entry;
import java.util.Objects;
@@ -528,13 +532,15 @@ public class NotificationManagerService extends SystemService {
private NotificationRecordLogger mNotificationRecordLogger;
private InstanceIdSequence mNotificationInstanceIdSequence;
- private static class Archive {
+ static class Archive {
+ final SparseArray<Boolean> mEnabled;
final int mBufferSize;
- final ArrayDeque<Pair<StatusBarNotification, Integer>> mBuffer;
+ final LinkedList<Pair<StatusBarNotification, Integer>> mBuffer;
public Archive(int size) {
mBufferSize = size;
- mBuffer = new ArrayDeque<>(mBufferSize);
+ mBuffer = new LinkedList<>();
+ mEnabled = new SparseArray<>();
}
public String toString() {
@@ -547,7 +553,10 @@ public class NotificationManagerService extends SystemService {
return sb.toString();
}
- public void record(StatusBarNotification nr, int reason) {
+ public void record(StatusBarNotification sbn, int reason) {
+ if (!mEnabled.get(sbn.getNormalizedUserId(), false)) {
+ return;
+ }
if (mBuffer.size() == mBufferSize) {
mBuffer.removeFirst();
}
@@ -555,7 +564,7 @@ public class NotificationManagerService extends SystemService {
// We don't want to store the heavy bits of the notification in the archive,
// but other clients in the system process might be using the object, so we
// store a (lightened) copy.
- mBuffer.addLast(new Pair<>(nr.cloneLight(), reason));
+ mBuffer.addLast(new Pair<>(sbn.cloneLight(), reason));
}
public Iterator<Pair<StatusBarNotification, Integer>> descendingIterator() {
@@ -577,60 +586,25 @@ public class NotificationManagerService extends SystemService {
return a.toArray(new StatusBarNotification[a.size()]);
}
- }
+ public void updateHistoryEnabled(@UserIdInt int userId, boolean enabled) {
+ mEnabled.put(userId, enabled);
- void loadDefaultApprovedServices(int userId) {
- String defaultListenerAccess = getContext().getResources().getString(
- com.android.internal.R.string.config_defaultListenerAccessPackages);
- if (defaultListenerAccess != null) {
- String[] listeners =
- defaultListenerAccess.split(ManagedServices.ENABLED_SERVICES_SEPARATOR);
- for (int i = 0; i < listeners.length; i++) {
- if (TextUtils.isEmpty(listeners[i])) {
- continue;
- }
- ArraySet<ComponentName> approvedListeners =
- mListeners.queryPackageForServices(listeners[i],
- MATCH_DIRECT_BOOT_AWARE
- | MATCH_DIRECT_BOOT_UNAWARE, userId);
- for (int k = 0; k < approvedListeners.size(); k++) {
- ComponentName cn = approvedListeners.valueAt(k);
- mListeners.addDefaultComponentOrPackage(cn.flattenToString());
+ if (!enabled) {
+ for (int i = mBuffer.size() - 1; i >= 0; i--) {
+ if (userId == mBuffer.get(i).first.getNormalizedUserId()) {
+ mBuffer.remove(i);
+ }
}
}
}
+ }
- String defaultDndAccess = getContext().getResources().getString(
- com.android.internal.R.string.config_defaultDndAccessPackages);
- if (defaultDndAccess != null) {
- String[] dnds = defaultDndAccess.split(ManagedServices.ENABLED_SERVICES_SEPARATOR);
- for (int i = 0; i < dnds.length; i++) {
- if (TextUtils.isEmpty(dnds[i])) {
- continue;
- }
- mConditionProviders.addDefaultComponentOrPackage(dnds[i]);
- }
- }
+ void loadDefaultApprovedServices(int userId) {
+ mListeners.loadDefaultsFromConfig();
+ mConditionProviders.loadDefaultsFromConfig();
- ArraySet<String> assistants = new ArraySet<>();
- String deviceAssistant = DeviceConfig.getProperty(
- DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.NAS_DEFAULT_SERVICE);
- if (deviceAssistant != null) {
- assistants.addAll(Arrays.asList(deviceAssistant.split(
- ManagedServices.ENABLED_SERVICES_SEPARATOR)));
- }
- assistants.addAll(Arrays.asList(getContext().getResources().getString(
- com.android.internal.R.string.config_defaultAssistantAccessComponent)
- .split(ManagedServices.ENABLED_SERVICES_SEPARATOR)));
- for (int i = 0; i < assistants.size(); i++) {
- String cnString = assistants.valueAt(i);
- if (TextUtils.isEmpty(cnString)) {
- continue;
- }
- mAssistants.addDefaultComponentOrPackage(cnString);
- }
+ mAssistants.loadDefaultsFromConfig();
}
protected void allowDefaultApprovedServices(int userId) {
@@ -653,11 +627,14 @@ public class NotificationManagerService extends SystemService {
DeviceConfig.NAMESPACE_SYSTEMUI,
SystemUiDeviceConfigFlags.NAS_DEFAULT_SERVICE);
if (overrideDefaultAssistantString != null) {
- ComponentName overrideDefaultAssistant =
- ComponentName.unflattenFromString(overrideDefaultAssistantString);
- if (allowAssistant(userId, overrideDefaultAssistant)) return;
+ ArraySet<ComponentName> approved = mAssistants.queryPackageForServices(
+ overrideDefaultAssistantString,
+ MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE,
+ userId);
+ for (int i = 0; i < approved.size(); i++) {
+ if (allowAssistant(userId, approved.valueAt(i))) return;
+ }
}
-
ArraySet<ComponentName> defaults = mAssistants.getDefaultComponents();
// We should have only one default assistant by default
// allowAssistant should execute once in practice
@@ -1638,6 +1615,9 @@ public class NotificationManagerService extends SystemService {
= Settings.System.getUriFor(Settings.System.NOTIFICATION_LIGHT_PULSE);
private final Uri NOTIFICATION_RATE_LIMIT_URI
= Settings.Global.getUriFor(Settings.Global.MAX_NOTIFICATION_ENQUEUE_RATE);
+ private final Uri NOTIFICATION_HISTORY_ENABLED
+ = Settings.Secure.getUriFor(Settings.Secure.NOTIFICATION_HISTORY_ENABLED);
+
SettingsObserver(Handler handler) {
super(handler);
@@ -1653,10 +1633,12 @@ public class NotificationManagerService extends SystemService {
false, this, UserHandle.USER_ALL);
resolver.registerContentObserver(NOTIFICATION_BUBBLES_URI,
false, this, UserHandle.USER_ALL);
+ resolver.registerContentObserver(NOTIFICATION_HISTORY_ENABLED,
+ false, this, UserHandle.USER_ALL);
update(null);
}
- @Override public void onChange(boolean selfChange, Uri uri) {
+ @Override public void onChange(boolean selfChange, Uri uri, int userId) {
update(uri);
}
@@ -1681,6 +1663,14 @@ public class NotificationManagerService extends SystemService {
if (uri == null || NOTIFICATION_BUBBLES_URI.equals(uri)) {
mPreferencesHelper.updateBubblesEnabled();
}
+ if (uri == null || NOTIFICATION_HISTORY_ENABLED.equals(uri)) {
+ final IntArray userIds = mUserProfiles.getCurrentProfileIds();
+
+ for (int i = 0; i < userIds.size(); i++) {
+ mArchive.updateHistoryEnabled(userIds.get(i), Settings.Secure.getInt(resolver,
+ Settings.Secure.NOTIFICATION_HISTORY_ENABLED, 0) == 1);
+ }
+ }
}
}
@@ -1959,7 +1949,8 @@ public class NotificationManagerService extends SystemService {
mPackageManagerClient,
mRankingHandler,
mZenModeHelper,
- new NotificationChannelLoggerImpl());
+ new NotificationChannelLoggerImpl(),
+ mAppOps);
mRankingHelper = new RankingHelper(getContext(),
mRankingHandler,
mPreferencesHelper,
@@ -2300,7 +2291,8 @@ public class NotificationManagerService extends SystemService {
mRoleObserver.init();
LauncherApps launcherApps =
(LauncherApps) getContext().getSystemService(Context.LAUNCHER_APPS_SERVICE);
- mShortcutHelper = new ShortcutHelper(launcherApps, mShortcutListener);
+ mShortcutHelper = new ShortcutHelper(launcherApps, mShortcutListener, getLocalService(
+ ShortcutServiceInternal.class));
BubbleExtractor bubbsExtractor = mRankingHelper.findExtractor(BubbleExtractor.class);
if (bubbsExtractor != null) {
bubbsExtractor.setShortcutHelper(mShortcutHelper);
@@ -8568,6 +8560,26 @@ public class NotificationManagerService extends SystemService {
private ArrayMap<Integer, Boolean> mUserSetMap = new ArrayMap<>();
private Set<String> mAllowedAdjustments = new ArraySet<>();
+ @Override
+ protected void loadDefaultsFromConfig() {
+ ArraySet<String> assistants = new ArraySet<>();
+ assistants.addAll(Arrays.asList(mContext.getResources().getString(
+ com.android.internal.R.string.config_defaultAssistantAccessComponent)
+ .split(ManagedServices.ENABLED_SERVICES_SEPARATOR)));
+ for (int i = 0; i < assistants.size(); i++) {
+ String cnString = assistants.valueAt(i);
+ if (TextUtils.isEmpty(cnString)) {
+ continue;
+ }
+ ArraySet<ComponentName> approved = queryPackageForServices(cnString,
+ MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE, USER_SYSTEM);
+ for (int k = 0; k < approved.size(); k++) {
+ ComponentName cn = approved.valueAt(k);
+ addDefaultComponentOrPackage(cn.flattenToString());
+ }
+ }
+ }
+
public NotificationAssistants(Context context, Object lock, UserProfiles up,
IPackageManager pm) {
super(context, lock, up, pm);
@@ -9005,7 +9017,29 @@ public class NotificationManagerService extends SystemService {
public NotificationListeners(IPackageManager pm) {
super(getContext(), mNotificationLock, mUserProfiles, pm);
+ }
+ @Override
+ protected void loadDefaultsFromConfig() {
+ String defaultListenerAccess = mContext.getResources().getString(
+ R.string.config_defaultListenerAccessPackages);
+ if (defaultListenerAccess != null) {
+ String[] listeners =
+ defaultListenerAccess.split(ManagedServices.ENABLED_SERVICES_SEPARATOR);
+ for (int i = 0; i < listeners.length; i++) {
+ if (TextUtils.isEmpty(listeners[i])) {
+ continue;
+ }
+ ArraySet<ComponentName> approvedListeners =
+ this.queryPackageForServices(listeners[i],
+ MATCH_DIRECT_BOOT_AWARE
+ | MATCH_DIRECT_BOOT_UNAWARE, USER_SYSTEM);
+ for (int k = 0; k < approvedListeners.size(); k++) {
+ ComponentName cn = approvedListeners.valueAt(k);
+ addDefaultComponentOrPackage(cn.flattenToString());
+ }
+ }
+ }
}
@Override
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 192df4139b37..2bbbffc203f4 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -590,6 +590,8 @@ public final class NotificationRecord {
pw.println(prefix + "snoozeCriteria=" + TextUtils.join(",", getSnoozeCriteria()));
}
pw.println(prefix + "mAdjustments=" + mAdjustments);
+ pw.println(prefix + "shortcut=" + notification.getShortcutId()
+ + " found valid? " + (mShortcutInfo != null));
}
@Override
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index b3d373ffab3a..d432fc83b52a 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -16,7 +16,9 @@
package com.android.server.notification;
+import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
import static android.app.NotificationChannel.PLACEHOLDER_CONVERSATION_ID;
+import static android.app.NotificationManager.BUBBLE_PREFERENCE_ALL;
import static android.app.NotificationManager.BUBBLE_PREFERENCE_NONE;
import static android.app.NotificationManager.IMPORTANCE_NONE;
import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
@@ -30,6 +32,7 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
+import android.app.AppOpsManager;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationChannelGroup;
@@ -79,7 +82,9 @@ import java.util.concurrent.ConcurrentHashMap;
public class PreferencesHelper implements RankingConfig {
private static final String TAG = "NotificationPrefHelper";
- private static final int XML_VERSION = 1;
+ private static final int XML_VERSION = 2;
+ /** What version to check to do the upgrade for bubbles. */
+ private static final int XML_VERSION_BUBBLES_UPGRADE = 1;
private static final int UNKNOWN_UID = UserHandle.USER_NULL;
private static final String NON_BLOCKABLE_CHANNEL_DELIM = ":";
@@ -151,6 +156,7 @@ public class PreferencesHelper implements RankingConfig {
private final RankingHandler mRankingHandler;
private final ZenModeHelper mZenModeHelper;
private final NotificationChannelLogger mNotificationChannelLogger;
+ private final AppOpsManager mAppOps;
private SparseBooleanArray mBadgingEnabled;
private boolean mBubblesEnabledGlobally = DEFAULT_GLOBAL_ALLOW_BUBBLE;
@@ -167,12 +173,14 @@ public class PreferencesHelper implements RankingConfig {
}
public PreferencesHelper(Context context, PackageManager pm, RankingHandler rankingHandler,
- ZenModeHelper zenHelper, NotificationChannelLogger notificationChannelLogger) {
+ ZenModeHelper zenHelper, NotificationChannelLogger notificationChannelLogger,
+ AppOpsManager appOpsManager) {
mContext = context;
mZenModeHelper = zenHelper;
mRankingHandler = rankingHandler;
mPm = pm;
mNotificationChannelLogger = notificationChannelLogger;
+ mAppOps = appOpsManager;
// STOPSHIP (b/142218092) this should be removed before ship
if (!wasBadgingForcedTrue(context)) {
@@ -195,6 +203,15 @@ public class PreferencesHelper implements RankingConfig {
if (type != XmlPullParser.START_TAG) return;
String tag = parser.getName();
if (!TAG_RANKING.equals(tag)) return;
+
+ boolean upgradeForBubbles = false;
+ if (parser.getAttributeCount() > 0) {
+ String attribute = parser.getAttributeName(0);
+ if (ATT_VERSION.equals(attribute)) {
+ int xmlVersion = Integer.parseInt(parser.getAttributeValue(0));
+ upgradeForBubbles = xmlVersion == XML_VERSION_BUBBLES_UPGRADE;
+ }
+ }
synchronized (mPackagePreferences) {
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
tag = parser.getName();
@@ -220,6 +237,16 @@ public class PreferencesHelper implements RankingConfig {
}
}
boolean skipWarningLogged = false;
+ boolean hasSAWPermission = false;
+ if (upgradeForBubbles) {
+ hasSAWPermission = mAppOps.noteOpNoThrow(
+ OP_SYSTEM_ALERT_WINDOW, uid, name, null,
+ "check-notif-bubble") == AppOpsManager.MODE_ALLOWED;
+ }
+ int bubblePref = hasSAWPermission
+ ? BUBBLE_PREFERENCE_ALL
+ : XmlUtils.readIntAttribute(parser, ATT_ALLOW_BUBBLE,
+ DEFAULT_BUBBLE_PREFERENCE);
PackagePreferences r = getOrCreatePackagePreferencesLocked(
name, userId, uid,
@@ -231,8 +258,7 @@ public class PreferencesHelper implements RankingConfig {
parser, ATT_VISIBILITY, DEFAULT_VISIBILITY),
XmlUtils.readBooleanAttribute(
parser, ATT_SHOW_BADGE, DEFAULT_SHOW_BADGE),
- XmlUtils.readIntAttribute(
- parser, ATT_ALLOW_BUBBLE, DEFAULT_BUBBLE_PREFERENCE));
+ bubblePref);
r.importance = XmlUtils.readIntAttribute(
parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE);
r.priority = XmlUtils.readIntAttribute(
diff --git a/services/core/java/com/android/server/notification/ShortcutHelper.java b/services/core/java/com/android/server/notification/ShortcutHelper.java
index f1ce3a7d9f48..1d4843822931 100644
--- a/services/core/java/com/android/server/notification/ShortcutHelper.java
+++ b/services/core/java/com/android/server/notification/ShortcutHelper.java
@@ -21,11 +21,15 @@ import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_DYNAMIC;
import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_PINNED;
import android.annotation.NonNull;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.content.pm.LauncherApps;
import android.content.pm.ShortcutInfo;
+import android.content.pm.ShortcutServiceInternal;
import android.os.Binder;
import android.os.Handler;
import android.os.UserHandle;
+import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
@@ -38,6 +42,7 @@ import java.util.List;
* Helper for querying shortcuts.
*/
class ShortcutHelper {
+ private static final String TAG = "ShortcutHelper";
/**
* Listener to call when a shortcut we're tracking has been removed.
@@ -48,6 +53,8 @@ class ShortcutHelper {
private LauncherApps mLauncherAppsService;
private ShortcutListener mShortcutListener;
+ private ShortcutServiceInternal mShortcutServiceInternal;
+ private IntentFilter mSharingFilter;
// Key: packageName Value: <shortcutId, notifId>
private HashMap<String, HashMap<String, String>> mActiveShortcutBubbles = new HashMap<>();
@@ -111,9 +118,17 @@ class ShortcutHelper {
}
};
- ShortcutHelper(LauncherApps launcherApps, ShortcutListener listener) {
+ ShortcutHelper(LauncherApps launcherApps, ShortcutListener listener,
+ ShortcutServiceInternal shortcutServiceInternal) {
mLauncherAppsService = launcherApps;
mShortcutListener = listener;
+ mSharingFilter = new IntentFilter();
+ try {
+ mSharingFilter.addDataType("*/*");
+ } catch (IntentFilter.MalformedMimeTypeException e) {
+ Slog.e(TAG, "Bad mime type", e);
+ }
+ mShortcutServiceInternal = shortcutServiceInternal;
}
@VisibleForTesting
@@ -121,6 +136,11 @@ class ShortcutHelper {
mLauncherAppsService = launcherApps;
}
+ @VisibleForTesting
+ void setShortcutServiceInternal(ShortcutServiceInternal shortcutServiceInternal) {
+ mShortcutServiceInternal = shortcutServiceInternal;
+ }
+
/**
* Only returns shortcut info if it's found and if it's {@link ShortcutInfo#isLongLived()}.
*/
@@ -141,7 +161,14 @@ class ShortcutHelper {
ShortcutInfo info = shortcuts != null && shortcuts.size() > 0
? shortcuts.get(0)
: null;
- return info != null && info.isLongLived() ? info : null;
+ if (info == null || !info.isLongLived() || !info.isEnabled()) {
+ return null;
+ }
+ if (mShortcutServiceInternal.isSharingShortcut(user.getIdentifier(),
+ "android", packageName, shortcutId, user.getIdentifier(), mSharingFilter)) {
+ return info;
+ }
+ return null;
} finally {
Binder.restoreCallingIdentity(token);
}
diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
index dab4bfd4df5a..5415967c3bdc 100644
--- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java
+++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
@@ -17,6 +17,7 @@
package com.android.server.pm;
import static com.android.server.pm.PackageManagerService.DEBUG_DEXOPT;
+import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
import android.annotation.Nullable;
import android.app.job.JobInfo;
@@ -434,7 +435,7 @@ public class BackgroundDexOptService extends JobService {
| DexoptOptions.DEXOPT_DOWNGRADE;
long package_size_before = getPackageSize(pm, pkg);
- if (isForPrimaryDex) {
+ if (isForPrimaryDex || PLATFORM_PACKAGE_NAME.equals(pkg)) {
// This applies for system apps or if packages location is not a directory, i.e.
// monolithic install.
if (!pm.canHaveOatDir(pkg)) {
@@ -486,7 +487,9 @@ public class BackgroundDexOptService extends JobService {
| DexoptOptions.DEXOPT_BOOT_COMPLETE
| DexoptOptions.DEXOPT_IDLE_BACKGROUND_JOB;
- return isForPrimaryDex
+ // System server share the same code path as primary dex files.
+ // PackageManagerService will select the right optimization path for it.
+ return (isForPrimaryDex || PLATFORM_PACKAGE_NAME.equals(pkg))
? performDexOptPrimary(pm, pkg, reason, dexoptFlags)
: performDexOptSecondary(pm, pkg, reason, dexoptFlags);
}
diff --git a/services/core/java/com/android/server/pm/DataLoaderManagerService.java b/services/core/java/com/android/server/pm/DataLoaderManagerService.java
index 09baf6e0a817..ae9c38498b10 100644
--- a/services/core/java/com/android/server/pm/DataLoaderManagerService.java
+++ b/services/core/java/com/android/server/pm/DataLoaderManagerService.java
@@ -204,6 +204,12 @@ public class DataLoaderManagerService extends SystemService {
@Override
public void onServiceDisconnected(ComponentName arg0) {
+ if (mListener != null) {
+ try {
+ mListener.onStatusChanged(mId, IDataLoaderStatusListener.DATA_LOADER_DESTROYED);
+ } catch (RemoteException ignored) {
+ }
+ }
remove();
}
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index 9fb468e8db6e..7cee286c4451 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -71,6 +71,8 @@ public class Installer extends SystemService {
public static final int DEXOPT_GENERATE_COMPACT_DEX = 1 << 11;
/** Indicates that dexopt should generate an app image */
public static final int DEXOPT_GENERATE_APP_IMAGE = 1 << 12;
+ /** Indicates that dexopt may be run with different performance / priority tuned for restore */
+ public static final int DEXOPT_FOR_RESTORE = 1 << 13; // TODO(b/135202722): remove
public static final int FLAG_STORAGE_DE = IInstalld.FLAG_STORAGE_DE;
public static final int FLAG_STORAGE_CE = IInstalld.FLAG_STORAGE_CE;
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 65b7cf3eabd1..4b8a24204ca7 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -22,6 +22,7 @@ import static com.android.server.pm.Installer.DEXOPT_BOOTCOMPLETE;
import static com.android.server.pm.Installer.DEXOPT_DEBUGGABLE;
import static com.android.server.pm.Installer.DEXOPT_ENABLE_HIDDEN_API_CHECKS;
import static com.android.server.pm.Installer.DEXOPT_FORCE;
+import static com.android.server.pm.Installer.DEXOPT_FOR_RESTORE;
import static com.android.server.pm.Installer.DEXOPT_GENERATE_APP_IMAGE;
import static com.android.server.pm.Installer.DEXOPT_GENERATE_COMPACT_DEX;
import static com.android.server.pm.Installer.DEXOPT_IDLE_BACKGROUND_JOB;
@@ -32,6 +33,7 @@ import static com.android.server.pm.Installer.DEXOPT_STORAGE_CE;
import static com.android.server.pm.Installer.DEXOPT_STORAGE_DE;
import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets;
+import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
import static com.android.server.pm.PackageManagerService.WATCHDOG_TIMEOUT;
import static com.android.server.pm.PackageManagerServiceCompilerMapping.getReasonName;
@@ -115,7 +117,9 @@ public class PackageDexOptimizer {
static boolean canOptimizePackage(AndroidPackage pkg) {
// We do not dexopt a package with no code.
- if (!pkg.isHasCode()) {
+ // Note that the system package is marked as having no code, however we can
+ // still optimize it via dexoptSystemServerPath.
+ if (!PLATFORM_PACKAGE_NAME.equals(pkg.getPackageName()) && !pkg.isHasCode()) {
return false;
}
@@ -132,6 +136,10 @@ public class PackageDexOptimizer {
int performDexOpt(AndroidPackage pkg, @NonNull PackageSetting pkgSetting,
String[] instructionSets, CompilerStats.PackageStats packageStats,
PackageDexUsage.PackageUseInfo packageUseInfo, DexoptOptions options) {
+ if (PLATFORM_PACKAGE_NAME.equals(pkg.getPackageName())) {
+ throw new IllegalArgumentException("System server dexopting should be done via "
+ + " DexManager and PackageDexOptimizer#dexoptSystemServerPath");
+ }
if (pkg.getUid() == -1) {
throw new IllegalArgumentException("Dexopt for " + pkg.getPackageName()
+ " has invalid uid.");
@@ -699,6 +707,7 @@ public class PackageDexOptimizer {
| (options.isDexoptIdleBackgroundJob() ? DEXOPT_IDLE_BACKGROUND_JOB : 0)
| (generateCompactDex ? DEXOPT_GENERATE_COMPACT_DEX : 0)
| (generateAppImage ? DEXOPT_GENERATE_APP_IMAGE : 0)
+ | (options.isDexoptInstallForRestore() ? DEXOPT_FOR_RESTORE : 0)
| hiddenApiFlag;
return adjustDexoptFlags(dexFlags);
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 59ac603875e2..d2481b758e82 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -66,6 +66,8 @@ import static android.content.pm.PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE
import static android.content.pm.PackageManager.INSTALL_INTERNAL;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES;
+import static android.content.pm.PackageManager.INSTALL_REASON_DEVICE_RESTORE;
+import static android.content.pm.PackageManager.INSTALL_REASON_DEVICE_SETUP;
import static android.content.pm.PackageManager.INSTALL_SUCCEEDED;
import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK;
@@ -94,6 +96,7 @@ import static android.content.pm.PackageManager.PERMISSION_DENIED;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.content.pm.PackageManager.RESTRICTION_NONE;
import static android.content.pm.PackageManager.UNINSTALL_REASON_UNKNOWN;
+import static android.content.pm.PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V4;
import static android.content.pm.PackageParser.isApkFile;
import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
import static android.os.incremental.IncrementalManager.isIncrementalPath;
@@ -1820,10 +1823,12 @@ public class PackageManagerService extends IPackageManager.Stub
state.setVerifierResponse(Binder.getCallingUid(),
PackageManager.VERIFICATION_ALLOW_WITHOUT_SUFFICIENT);
broadcastPackageVerified(verificationId, originUri,
- PackageManager.VERIFICATION_ALLOW, user);
+ PackageManager.VERIFICATION_ALLOW, null, args.mDataLoaderType,
+ user);
} else {
broadcastPackageVerified(verificationId, originUri,
- PackageManager.VERIFICATION_REJECT, user);
+ PackageManager.VERIFICATION_REJECT, null, args.mDataLoaderType,
+ user);
params.setReturnCode(
PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE);
state.setVerifierResponse(Binder.getCallingUid(),
@@ -1899,7 +1904,7 @@ public class PackageManagerService extends IPackageManager.Stub
if (state.isInstallAllowed()) {
broadcastPackageVerified(verificationId, originUri,
- response.code, args.getUser());
+ response.code, null, args.mDataLoaderType, args.getUser());
} else {
params.setReturnCode(
PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE);
@@ -3576,7 +3581,8 @@ public class PackageManagerService extends IPackageManager.Stub
// Prepare a supplier of package parser for the staging manager to parse apex file
// during the staging installation.
final Supplier<PackageParser2> apexParserSupplier = () -> new PackageParser2(
- mSeparateProcesses, mOnlyCore, mMetrics, mCacheDir, mPackageParserCallback);
+ mSeparateProcesses, mOnlyCore, mMetrics, null /* cacheDir */,
+ mPackageParserCallback);
mInstallerService = new PackageInstallerService(mContext, this, apexParserSupplier);
final Pair<ComponentName, String> instantAppResolverComponent =
getInstantAppResolverLPr();
@@ -13575,12 +13581,17 @@ public class PackageManagerService extends IPackageManager.Stub
}
private void broadcastPackageVerified(int verificationId, Uri packageUri,
- int verificationCode, UserHandle user) {
+ int verificationCode, @Nullable String rootHashString, int dataLoaderType,
+ UserHandle user) {
final Intent intent = new Intent(Intent.ACTION_PACKAGE_VERIFIED);
intent.setDataAndType(packageUri, PACKAGE_MIME_TYPE);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.putExtra(PackageManager.EXTRA_VERIFICATION_ID, verificationId);
intent.putExtra(PackageManager.EXTRA_VERIFICATION_RESULT, verificationCode);
+ if (rootHashString != null) {
+ intent.putExtra(PackageManager.EXTRA_VERIFICATION_ROOT_HASH, rootHashString);
+ }
+ intent.putExtra(PackageInstaller.EXTRA_DATA_LOADER_TYPE, dataLoaderType);
mContext.sendBroadcastAsUser(intent, user,
android.Manifest.permission.PACKAGE_VERIFICATION_AGENT);
@@ -14952,8 +14963,17 @@ public class PackageManagerService extends IPackageManager.Stub
verificationState.setRequiredVerifierUid(requiredUid);
final int installerUid =
verificationInfo == null ? -1 : verificationInfo.installerUid;
- if (!origin.existing && isVerificationEnabled(pkgLite, verifierUser.getIdentifier(),
- installFlags, installerUid)) {
+ final boolean isVerificationEnabled = isVerificationEnabled(
+ pkgLite, verifierUser.getIdentifier(), installFlags, installerUid);
+ final boolean isV4Signed =
+ (mArgs.signingDetails.signatureSchemeVersion == SIGNING_BLOCK_V4);
+ final boolean isIncrementalInstall =
+ (mArgs.mDataLoaderType == DataLoaderType.INCREMENTAL);
+ // NOTE: We purposefully skip verification for only incremental installs when there's
+ // a v4 signature block. Otherwise, proceed with verification as usual.
+ if (!origin.existing
+ && isVerificationEnabled
+ && (!isIncrementalInstall || !isV4Signed)) {
final Intent verification = new Intent(
Intent.ACTION_PACKAGE_NEEDS_VERIFICATION);
verification.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
@@ -16569,7 +16589,29 @@ public class PackageManagerService extends IPackageManager.Stub
}
executePostCommitSteps(commitRequest);
} finally {
- if (!success) {
+ if (success) {
+ for (InstallRequest request : requests) {
+ final InstallArgs args = request.args;
+ if (args.mDataLoaderType != DataLoaderType.INCREMENTAL) {
+ continue;
+ }
+ if (args.signingDetails.signatureSchemeVersion != SIGNING_BLOCK_V4) {
+ continue;
+ }
+ // For incremental installs, we bypass the verifier prior to install. Now
+ // that we know the package is valid, send a notice to the verifier with
+ // the root hash of the base.apk.
+ final String baseCodePath = request.installResult.pkg.getBaseCodePath();
+ final String[] splitCodePaths = request.installResult.pkg.getSplitCodePaths();
+ final Uri originUri = Uri.fromFile(args.origin.resolvedFile);
+ final int verificationId = mPendingVerificationToken++;
+ final String rootHashString = PackageManagerServiceUtils
+ .buildVerificationRootHashString(baseCodePath, splitCodePaths);
+ broadcastPackageVerified(verificationId, originUri,
+ PackageManager.VERIFICATION_ALLOW, rootHashString,
+ args.mDataLoaderType, args.getUser());
+ }
+ } else {
for (ScanResult result : preparedScans.values()) {
if (createdAppId.getOrDefault(result.request.parsedPackage.getPackageName(),
false)) {
@@ -16670,10 +16712,15 @@ public class PackageManagerService extends IPackageManager.Stub
// method because `pkg` may not be in `mPackages` yet.
//
// Also, don't fail application installs if the dexopt step fails.
+ int flags = DexoptOptions.DEXOPT_BOOT_COMPLETE
+ | DexoptOptions.DEXOPT_INSTALL_WITH_DEX_METADATA_FILE;
+ if (reconciledPkg.installArgs.installReason == INSTALL_REASON_DEVICE_RESTORE
+ || reconciledPkg.installArgs.installReason == INSTALL_REASON_DEVICE_SETUP) {
+ flags |= DexoptOptions.DEXOPT_FOR_RESTORE;
+ }
DexoptOptions dexoptOptions = new DexoptOptions(packageName,
REASON_INSTALL,
- DexoptOptions.DEXOPT_BOOT_COMPLETE
- | DexoptOptions.DEXOPT_INSTALL_WITH_DEX_METADATA_FILE);
+ flags);
ScanResult result = reconciledPkg.scanResult;
// This mirrors logic from commitReconciledScanResultLocked, where the library files
@@ -16911,7 +16958,6 @@ public class PackageManagerService extends IPackageManager.Stub
if (args.signingDetails != PackageParser.SigningDetails.UNKNOWN) {
parsedPackage.setSigningDetails(args.signingDetails);
} else {
- // TODO(b/136132412): skip for Incremental installation
parsedPackage.setSigningDetails(
ParsingPackageUtils.collectCertificates(parsedPackage, false /* skipVerify */));
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index 91afd846a9c3..5c175a6ef847 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -32,7 +32,6 @@ import android.annotation.Nullable;
import android.app.AppGlobals;
import android.content.Context;
import android.content.Intent;
-import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfoLite;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
@@ -40,7 +39,6 @@ import android.content.pm.PackageParser;
import android.content.pm.PackageParser.PackageParserException;
import android.content.pm.ResolveInfo;
import android.content.pm.Signature;
-import android.content.pm.parsing.ParsingPackageUtils;
import android.os.Build;
import android.os.Debug;
import android.os.Environment;
@@ -50,6 +48,9 @@ import android.os.RemoteException;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManagerInternal;
+import android.os.incremental.IncrementalManager;
+import android.os.incremental.V4Signature;
+import android.os.incremental.V4Signature.HashingInfo;
import android.service.pm.PackageServiceDumpProto;
import android.system.ErrnoException;
import android.system.Os;
@@ -62,6 +63,7 @@ import com.android.internal.content.NativeLibraryHelper;
import com.android.internal.content.PackageHelper;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FastPrintWriter;
+import com.android.internal.util.HexDump;
import com.android.server.EventLogTags;
import com.android.server.pm.dex.DexManager;
import com.android.server.pm.dex.PackageDexUsage;
@@ -94,8 +96,6 @@ import java.util.Collections;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
-import java.util.Map;
-import java.util.Set;
import java.util.function.Predicate;
import java.util.zip.GZIPInputStream;
@@ -943,4 +943,71 @@ public class PackageManagerServiceUtils {
Os.chmod(currentDir.getAbsolutePath(), mode);
}
}
+
+ /**
+ * Returns a string that's compatible with the verification root hash extra.
+ * @see PackageManager#EXTRA_VERIFICATION_ROOT_HASH
+ */
+ @NonNull
+ public static String buildVerificationRootHashString(@NonNull String baseFilename,
+ @Nullable String[] splitFilenameArray) {
+ final StringBuilder sb = new StringBuilder();
+ final String baseFilePath =
+ baseFilename.substring(baseFilename.lastIndexOf(File.separator) + 1);
+ sb.append(baseFilePath).append(":");
+ final byte[] baseRootHash = getRootHash(baseFilename);
+ if (baseRootHash == null) {
+ sb.append("0");
+ } else {
+ sb.append(HexDump.toHexString(baseRootHash));
+ }
+ if (splitFilenameArray == null || splitFilenameArray.length == 0) {
+ return sb.toString();
+ }
+
+ for (int i = splitFilenameArray.length - 1; i >= 0; i--) {
+ final String splitFilename = splitFilenameArray[i];
+ final String splitFilePath =
+ splitFilename.substring(splitFilename.lastIndexOf(File.separator) + 1);
+ final byte[] splitRootHash = getRootHash(splitFilename);
+ sb.append(";").append(splitFilePath).append(":");
+ if (splitRootHash == null) {
+ sb.append("0");
+ } else {
+ sb.append(HexDump.toHexString(splitRootHash));
+ }
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Returns the root has for the given file.
+ * <p>Otherwise, returns {@code null} if the root hash could not be found or calculated.
+ * <p>NOTE: This currently only works on files stored on the incremental file system. The
+ * eventual goal is that this hash [among others] can be retrieved for any file.
+ */
+ @Nullable
+ private static byte[] getRootHash(String filename) {
+ try {
+ final byte[] baseFileSignature =
+ IncrementalManager.unsafeGetFileSignature(filename);
+ if (baseFileSignature == null) {
+ throw new IOException("File signature not present");
+ }
+ final V4Signature signature =
+ V4Signature.readFrom(baseFileSignature);
+ if (signature.hashingInfo == null) {
+ throw new IOException("Hashing info not present");
+ }
+ final HashingInfo hashInfo =
+ HashingInfo.fromByteArray(signature.hashingInfo);
+ if (ArrayUtils.isEmpty(hashInfo.rawRootHash)) {
+ throw new IOException("Root has not present");
+ }
+ return hashInfo.rawRootHash;
+ } catch (IOException ignore) {
+ Slog.e(TAG, "ERROR: could not load root hash from incremental install");
+ }
+ return null;
+ }
}
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 323ffcfc2a1c..fc70af4e7bd4 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -103,6 +103,7 @@ import com.android.internal.logging.MetricsLogger;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.Preconditions;
import com.android.internal.util.XmlUtils;
import com.android.internal.widget.LockPatternUtils;
@@ -137,6 +138,7 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.Set;
+import java.util.concurrent.ThreadLocalRandom;
/**
* Service for {@link UserManager}.
@@ -3244,16 +3246,39 @@ public class UserManagerService extends IUserManager.Stub {
@NonNull String userType, @UserInfoFlag int flags, @UserIdInt int parentId,
boolean preCreate, @Nullable String[] disallowedPackages)
throws UserManager.CheckedUserOperationException {
+ final int nextProbableUserId = getNextAvailableId();
final TimingsTraceAndSlog t = new TimingsTraceAndSlog();
t.traceBegin("createUser-" + flags);
+ final long sessionId = logUserCreateJourneyBegin(nextProbableUserId, userType, flags);
try {
return createUserInternalUncheckedNoTracing(name, userType, flags, parentId,
preCreate, disallowedPackages, t);
} finally {
+ logUserCreateJourneyFinish(sessionId, nextProbableUserId);
t.traceEnd();
}
}
+ private long logUserCreateJourneyBegin(@UserIdInt int userId, String userType,
+ @UserInfoFlag int flags) {
+ final long sessionId = ThreadLocalRandom.current().nextLong(1, Long.MAX_VALUE);
+ // log the journey atom with the user metadata
+ FrameworkStatsLog.write(FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED, sessionId,
+ FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_CREATE,
+ /* origin_user= */ -1, userId, UserManager.getUserTypeForStatsd(userType), flags);
+ // log the event atom to indicate the event start
+ FrameworkStatsLog.write(FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED, sessionId, userId,
+ FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__CREATE_USER,
+ FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__STATE__BEGIN);
+ return sessionId;
+ }
+
+ private void logUserCreateJourneyFinish(long sessionId, @UserIdInt int userId) {
+ FrameworkStatsLog.write(FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED, sessionId, userId,
+ FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__CREATE_USER,
+ FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__STATE__FINISH);
+ }
+
private UserInfo createUserInternalUncheckedNoTracing(@Nullable String name,
@NonNull String userType, @UserInfoFlag int flags, @UserIdInt int parentId,
boolean preCreate, @Nullable String[] disallowedPackages,
diff --git a/services/core/java/com/android/server/pm/dex/DexManager.java b/services/core/java/com/android/server/pm/dex/DexManager.java
index 6dcf71e9fbf0..f7bf1d985786 100644
--- a/services/core/java/com/android/server/pm/dex/DexManager.java
+++ b/services/core/java/com/android/server/pm/dex/DexManager.java
@@ -79,6 +79,10 @@ public class DexManager {
private static final String PROPERTY_NAME_PM_DEXOPT_PRIV_APPS_OOB_LIST =
"pm.dexopt.priv-apps-oob-list";
+ // System server cannot load executable code outside system partitions.
+ // However it can load verification data - thus we pick the "verify" compiler filter.
+ private static final String SYSTEM_SERVER_COMPILER_FILTER = "verify";
+
private final Context mContext;
// Maps package name to code locations.
@@ -443,6 +447,14 @@ public class DexManager {
* because they don't need to be compiled)..
*/
public boolean dexoptSecondaryDex(DexoptOptions options) {
+ if (PLATFORM_PACKAGE_NAME.equals(options.getPackageName())) {
+ // We could easily redirect to #dexoptSystemServer in this case. But there should be
+ // no-one calling this method directly for system server.
+ // As such we prefer to abort in this case.
+ Slog.wtf(TAG, "System server jars should be optimized with dexoptSystemServer");
+ return false;
+ }
+
PackageDexOptimizer pdo = getPackageDexOptimizer(options);
String packageName = options.getPackageName();
PackageUseInfo useInfo = getPackageUseInfoOrDefault(packageName);
@@ -501,8 +513,17 @@ public class DexManager {
return PackageDexOptimizer.DEX_OPT_FAILED;
}
- PackageDexOptimizer pdo = getPackageDexOptimizer(options);
- String packageName = options.getPackageName();
+ // Override compiler filter for system server to the expected one.
+ //
+ // We could let the caller do this every time the invoke PackageManagerServer#dexopt.
+ // However, there are a few places were this will need to be done which creates
+ // redundancy and the danger of overlooking the config (and thus generating code that will
+ // waste storage and time).
+ DexoptOptions overriddenOptions = options.overrideCompilerFilter(
+ SYSTEM_SERVER_COMPILER_FILTER);
+
+ PackageDexOptimizer pdo = getPackageDexOptimizer(overriddenOptions);
+ String packageName = overriddenOptions.getPackageName();
PackageUseInfo useInfo = getPackageUseInfoOrDefault(packageName);
if (useInfo.getDexUseInfoMap().isEmpty()) {
if (DEBUG) {
@@ -527,7 +548,7 @@ public class DexManager {
continue;
}
- int newResult = pdo.dexoptSystemServerPath(dexPath, dexUseInfo, options);
+ int newResult = pdo.dexoptSystemServerPath(dexPath, dexUseInfo, overriddenOptions);
// The end result is:
// - FAILED if any path failed,
@@ -600,6 +621,23 @@ public class DexManager {
packageName, dexUseInfo.getOwnerUserId()) || updated;
continue;
}
+
+ // Special handle system server files.
+ // We don't need an installd call because we have permissions to check if the file
+ // exists.
+ if (PLATFORM_PACKAGE_NAME.equals(packageName)) {
+ if (!Files.exists(Paths.get(dexPath))) {
+ if (DEBUG) {
+ Slog.w(TAG, "A dex file previously loaded by System Server does not exist "
+ + " anymore: " + dexPath);
+ }
+ updated = mPackageDexUsage.removeUserPackage(
+ packageName, dexUseInfo.getOwnerUserId()) || updated;
+ }
+ continue;
+ }
+
+ // This is a regular application.
ApplicationInfo info = pkg.applicationInfo;
int flags = 0;
if (info.deviceProtectedDataDir != null &&
diff --git a/services/core/java/com/android/server/pm/dex/DexoptOptions.java b/services/core/java/com/android/server/pm/dex/DexoptOptions.java
index de3c9f28218d..68f38861d7e4 100644
--- a/services/core/java/com/android/server/pm/dex/DexoptOptions.java
+++ b/services/core/java/com/android/server/pm/dex/DexoptOptions.java
@@ -61,6 +61,10 @@ public final class DexoptOptions {
// should get the dex metdata file if present.
public static final int DEXOPT_INSTALL_WITH_DEX_METADATA_FILE = 1 << 10;
+ // When set, indicates that dexopt is being invoked from the install flow during device restore
+ // or device setup and should be scheduled appropriately.
+ public static final int DEXOPT_FOR_RESTORE = 1 << 11; // TODO(b/135202722): remove
+
// The name of package to optimize.
private final String mPackageName;
@@ -99,7 +103,8 @@ public final class DexoptOptions {
DEXOPT_DOWNGRADE |
DEXOPT_AS_SHARED_LIBRARY |
DEXOPT_IDLE_BACKGROUND_JOB |
- DEXOPT_INSTALL_WITH_DEX_METADATA_FILE;
+ DEXOPT_INSTALL_WITH_DEX_METADATA_FILE |
+ DEXOPT_FOR_RESTORE;
if ((flags & (~validityMask)) != 0) {
throw new IllegalArgumentException("Invalid flags : " + Integer.toHexString(flags));
}
@@ -155,6 +160,10 @@ public final class DexoptOptions {
return (mFlags & DEXOPT_INSTALL_WITH_DEX_METADATA_FILE) != 0;
}
+ public boolean isDexoptInstallForRestore() {
+ return (mFlags & DEXOPT_FOR_RESTORE) != 0;
+ }
+
public String getSplitName() {
return mSplitName;
}
@@ -166,4 +175,17 @@ public final class DexoptOptions {
public int getCompilationReason() {
return mCompilationReason;
}
+
+ /**
+ * Creates a new set of DexoptOptions which are the same with the exception of the compiler
+ * filter (set to the given value).
+ */
+ public DexoptOptions overrideCompilerFilter(String newCompilerFilter) {
+ return new DexoptOptions(
+ mPackageName,
+ mCompilationReason,
+ newCompilerFilter,
+ mSplitName,
+ mFlags);
+ }
}
diff --git a/services/core/java/com/android/server/pm/permission/OneTimePermissionUserManager.java b/services/core/java/com/android/server/pm/permission/OneTimePermissionUserManager.java
index 1c4568095ce3..4bbe3733719e 100644
--- a/services/core/java/com/android/server/pm/permission/OneTimePermissionUserManager.java
+++ b/services/core/java/com/android/server/pm/permission/OneTimePermissionUserManager.java
@@ -81,9 +81,6 @@ public class OneTimePermissionUserManager {
mAlarmManager = context.getSystemService(AlarmManager.class);
mPermissionControllerManager = context.getSystemService(PermissionControllerManager.class);
mHandler = context.getMainThreadHandler();
-
- // Listen for tracked uid being uninstalled
- context.registerReceiver(mUninstallListener, new IntentFilter(Intent.ACTION_UID_REMOVED));
}
/**
@@ -171,6 +168,14 @@ public class OneTimePermissionUserManager {
}
/**
+ * Register to listen for Uids being uninstalled. This must be done outside of the
+ * PermissionManagerService lock.
+ */
+ void registerUninstallListener() {
+ mContext.registerReceiver(mUninstallListener, new IntentFilter(Intent.ACTION_UID_REMOVED));
+ }
+
+ /**
* A class which watches a package for inactivity and notifies the permission controller when
* the package becomes inactive
*/
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index ccc749232dc3..bacc7acf3dc7 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -26,6 +26,7 @@ import static android.content.pm.ApplicationInfo.AUTO_REVOKE_DISCOURAGED;
import static android.content.pm.PackageManager.FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT;
import static android.content.pm.PackageManager.FLAG_PERMISSION_APPLY_RESTRICTION;
import static android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT;
+import static android.content.pm.PackageManager.FLAG_PERMISSION_ONE_TIME;
import static android.content.pm.PackageManager.FLAG_PERMISSION_POLICY_FIXED;
import static android.content.pm.PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED;
import static android.content.pm.PackageManager.FLAG_PERMISSION_REVOKED_COMPAT;
@@ -1656,7 +1657,8 @@ public class PermissionManagerService extends IPermissionManager.Stub {
final int userSettableMask = FLAG_PERMISSION_USER_SET
| FLAG_PERMISSION_USER_FIXED
| FLAG_PERMISSION_REVOKED_COMPAT
- | FLAG_PERMISSION_REVIEW_REQUIRED;
+ | FLAG_PERMISSION_REVIEW_REQUIRED
+ | FLAG_PERMISSION_ONE_TIME;
final int policyOrSystemFlags = FLAG_PERMISSION_SYSTEM_FIXED
| FLAG_PERMISSION_POLICY_FIXED;
@@ -3210,16 +3212,19 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
private OneTimePermissionUserManager getOneTimePermissionUserManager(@UserIdInt int userId) {
+ OneTimePermissionUserManager oneTimePermissionUserManager;
synchronized (mLock) {
- OneTimePermissionUserManager oneTimePermissionUserManager =
+ oneTimePermissionUserManager =
mOneTimePermissionUserManagers.get(userId);
- if (oneTimePermissionUserManager == null) {
- oneTimePermissionUserManager = new OneTimePermissionUserManager(
- mContext.createContextAsUser(UserHandle.of(userId), /*flags*/ 0));
- mOneTimePermissionUserManagers.put(userId, oneTimePermissionUserManager);
+ if (oneTimePermissionUserManager != null) {
+ return oneTimePermissionUserManager;
}
- return oneTimePermissionUserManager;
+ oneTimePermissionUserManager = new OneTimePermissionUserManager(
+ mContext.createContextAsUser(UserHandle.of(userId), /*flags*/ 0));
+ mOneTimePermissionUserManagers.put(userId, oneTimePermissionUserManager);
}
+ oneTimePermissionUserManager.registerUninstallListener();
+ return oneTimePermissionUserManager;
}
@Override
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Enforcer.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Enforcer.java
index 870d909fbbcc..a18b690f08cd 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Enforcer.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Enforcer.java
@@ -33,7 +33,7 @@ import java.util.Map;
* This is not necessarily a strict enforcement for the HAL contract, but a place to add checks for
* common HAL malfunctions, to help track them and assist in debugging.
*
- * The class is not thread-safe.
+ * The class is thread-safe.
*/
public class SoundTriggerHw2Enforcer implements ISoundTriggerHw2 {
static final String TAG = "SoundTriggerHw2Enforcer";
@@ -55,7 +55,9 @@ public class SoundTriggerHw2Enforcer implements ISoundTriggerHw2 {
public int loadSoundModel(ISoundTriggerHw.SoundModel soundModel, Callback callback,
int cookie) {
int handle = mUnderlying.loadSoundModel(soundModel, new CallbackEnforcer(callback), cookie);
- mModelStates.put(handle, false);
+ synchronized (mModelStates) {
+ mModelStates.put(handle, false);
+ }
return handle;
}
@@ -64,27 +66,35 @@ public class SoundTriggerHw2Enforcer implements ISoundTriggerHw2 {
int cookie) {
int handle = mUnderlying.loadPhraseSoundModel(soundModel, new CallbackEnforcer(callback),
cookie);
- mModelStates.put(handle, false);
+ synchronized (mModelStates) {
+ mModelStates.put(handle, false);
+ }
return handle;
}
@Override
public void unloadSoundModel(int modelHandle) {
mUnderlying.unloadSoundModel(modelHandle);
- mModelStates.remove(modelHandle);
+ synchronized (mModelStates) {
+ mModelStates.remove(modelHandle);
+ }
}
@Override
public void stopRecognition(int modelHandle) {
mUnderlying.stopRecognition(modelHandle);
- mModelStates.replace(modelHandle, false);
+ synchronized (mModelStates) {
+ mModelStates.replace(modelHandle, false);
+ }
}
@Override
public void stopAllRecognitions() {
mUnderlying.stopAllRecognitions();
- for (Map.Entry<Integer, Boolean> entry : mModelStates.entrySet()) {
- entry.setValue(false);
+ synchronized (mModelStates) {
+ for (Map.Entry<Integer, Boolean> entry : mModelStates.entrySet()) {
+ entry.setValue(false);
+ }
}
}
@@ -92,7 +102,9 @@ public class SoundTriggerHw2Enforcer implements ISoundTriggerHw2 {
public void startRecognition(int modelHandle, RecognitionConfig config, Callback callback,
int cookie) {
mUnderlying.startRecognition(modelHandle, config, new CallbackEnforcer(callback), cookie);
- mModelStates.replace(modelHandle, true);
+ synchronized (mModelStates) {
+ mModelStates.replace(modelHandle, true);
+ }
}
@Override
@@ -142,12 +154,14 @@ public class SoundTriggerHw2Enforcer implements ISoundTriggerHw2 {
public void recognitionCallback(ISoundTriggerHwCallback.RecognitionEvent event,
int cookie) {
int model = event.header.model;
- if (!mModelStates.getOrDefault(model, false)) {
- Log.wtfStack(TAG, "Unexpected recognition event for model: " + model);
- }
- if (event.header.status
- != android.media.soundtrigger_middleware.RecognitionStatus.FORCED) {
- mModelStates.replace(model, false);
+ synchronized (mModelStates) {
+ if (!mModelStates.getOrDefault(model, false)) {
+ Log.wtfStack(TAG, "Unexpected recognition event for model: " + model);
+ }
+ if (event.header.status
+ != android.media.soundtrigger_middleware.RecognitionStatus.FORCED) {
+ mModelStates.replace(model, false);
+ }
}
mUnderlying.recognitionCallback(event, cookie);
}
@@ -156,12 +170,14 @@ public class SoundTriggerHw2Enforcer implements ISoundTriggerHw2 {
public void phraseRecognitionCallback(ISoundTriggerHwCallback.PhraseRecognitionEvent event,
int cookie) {
int model = event.common.header.model;
- if (!mModelStates.getOrDefault(model, false)) {
- Log.wtfStack(TAG, "Unexpected recognition event for model: " + model);
- }
- if (event.common.header.status
- != android.media.soundtrigger_middleware.RecognitionStatus.FORCED) {
- mModelStates.replace(model, false);
+ synchronized (mModelStates) {
+ if (!mModelStates.getOrDefault(model, false)) {
+ Log.wtfStack(TAG, "Unexpected recognition event for model: " + model);
+ }
+ if (event.common.header.status
+ != android.media.soundtrigger_middleware.RecognitionStatus.FORCED) {
+ mModelStates.replace(model, false);
+ }
}
mUnderlying.phraseRecognitionCallback(event, cookie);
}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java
index 04ba6bfeb4ee..8b6ed1ff5081 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java
@@ -377,11 +377,7 @@ public class SoundTriggerMiddlewareLogging implements ISoundTriggerMiddlewareInt
}
private static void printObject(@NonNull StringBuilder builder, @Nullable Object obj) {
- if (obj instanceof Parcelable) {
- ObjectPrinter.print(builder, obj, true, 16);
- } else {
- builder.append(obj.toString());
- }
+ ObjectPrinter.print(builder, obj, true, 16);
}
private static String printObject(@Nullable Object obj) {
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java
index 23259558eeb7..bae244179346 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java
@@ -32,9 +32,9 @@ import android.media.soundtrigger_middleware.RecognitionEvent;
import android.media.soundtrigger_middleware.RecognitionStatus;
import android.media.soundtrigger_middleware.SoundModel;
import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor;
+import android.media.soundtrigger_middleware.SoundTriggerModuleProperties;
import android.media.soundtrigger_middleware.Status;
import android.os.IBinder;
-import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
import android.util.Log;
@@ -108,17 +108,26 @@ import java.util.Set;
public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddlewareInternal, Dumpable {
private static final String TAG = "SoundTriggerMiddlewareValidation";
- private enum ModuleState {
+ private enum ModuleStatus {
ALIVE,
DETACHED,
DEAD
};
+ private class ModuleState {
+ final @NonNull SoundTriggerModuleProperties properties;
+ Set<ModuleService> sessions = new HashSet<>();
+
+ private ModuleState(@NonNull SoundTriggerModuleProperties properties) {
+ this.properties = properties;
+ }
+ }
+
private Boolean mCaptureState;
private final @NonNull ISoundTriggerMiddlewareInternal mDelegate;
private final @NonNull Context mContext;
- private Map<Integer, Set<ModuleService>> mModules;
+ private Map<Integer, ModuleState> mModules;
public SoundTriggerMiddlewareValidation(
@NonNull ISoundTriggerMiddlewareInternal delegate, @NonNull Context context) {
@@ -168,7 +177,7 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
SoundTriggerModuleDescriptor[] result = mDelegate.listModules();
mModules = new HashMap<>(result.length);
for (SoundTriggerModuleDescriptor desc : result) {
- mModules.put(desc.handle, new HashSet<>());
+ mModules.put(desc.handle, new ModuleState(desc.properties));
}
return result;
} catch (Exception e) {
@@ -278,18 +287,21 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
@Override
public void dump(PrintWriter pw) {
synchronized (this) {
- pw.printf("Capture state is %s\n", mCaptureState == null ? "uninitialized"
+ pw.printf("Capture state is %s\n\n", mCaptureState == null ? "uninitialized"
: (mCaptureState ? "active" : "inactive"));
if (mModules != null) {
for (int handle : mModules.keySet()) {
+ final ModuleState module = mModules.get(handle);
pw.println("=========================================");
- pw.printf("Active sessions for module %d", handle);
- pw.println();
+ pw.printf("Module %d\n%s\n", handle,
+ ObjectPrinter.print(module.properties, true, 16));
pw.println("=========================================");
- for (ModuleService session : mModules.get(handle)) {
+ for (ModuleService session : module.sessions) {
session.dump(pw);
}
}
+ } else {
+ pw.println("Modules have not yet been enumerated.");
}
}
pw.println();
@@ -297,11 +309,18 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
if (mDelegate instanceof Dumpable) {
((Dumpable) mDelegate).dump(pw);
}
-
}
/** State of a sound model. */
static class ModelState {
+ ModelState(SoundModel model) {
+ this.description = ObjectPrinter.print(model, true, 16);
+ }
+
+ ModelState(PhraseSoundModel model) {
+ this.description = ObjectPrinter.print(model, true, 16);
+ }
+
/** Activity state of a sound model. */
enum Activity {
/** Model is loaded, recognition is inactive. */
@@ -313,6 +332,9 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
/** Activity state. */
Activity activityState = Activity.LOADED;
+ /** Human-readable description of the model. */
+ final String description;
+
/**
* A map of known parameter support. A missing key means we don't know yet whether the
* parameter is supported. A null value means it is known to not be supported. A non-null
@@ -375,7 +397,7 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
private ISoundTriggerModule mDelegate;
private @NonNull Map<Integer, ModelState> mLoadedModels = new HashMap<>();
private final int mHandle;
- private ModuleState mState = ModuleState.ALIVE;
+ private ModuleStatus mState = ModuleStatus.ALIVE;
ModuleService(int handle, @NonNull ISoundTriggerCallback callback) {
mCallback = callback;
@@ -389,7 +411,7 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
void attach(@NonNull ISoundTriggerModule delegate) {
mDelegate = delegate;
- mModules.get(mHandle).add(this);
+ mModules.get(mHandle).sessions.add(this);
}
@Override
@@ -401,14 +423,14 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
synchronized (SoundTriggerMiddlewareValidation.this) {
// State validation.
- if (mState == ModuleState.DETACHED) {
+ if (mState == ModuleStatus.DETACHED) {
throw new IllegalStateException("Module has been detached.");
}
// From here on, every exception isn't client's fault.
try {
int handle = mDelegate.loadModel(model);
- mLoadedModels.put(handle, new ModelState());
+ mLoadedModels.put(handle, new ModelState(model));
return handle;
} catch (Exception e) {
throw handleException(e);
@@ -425,14 +447,14 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
synchronized (SoundTriggerMiddlewareValidation.this) {
// State validation.
- if (mState == ModuleState.DETACHED) {
+ if (mState == ModuleStatus.DETACHED) {
throw new IllegalStateException("Module has been detached.");
}
// From here on, every exception isn't client's fault.
try {
int handle = mDelegate.loadPhraseModel(model);
- mLoadedModels.put(handle, new ModelState());
+ mLoadedModels.put(handle, new ModelState(model));
return handle;
} catch (Exception e) {
throw handleException(e);
@@ -448,7 +470,7 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
synchronized (SoundTriggerMiddlewareValidation.this) {
// State validation.
- if (mState == ModuleState.DETACHED) {
+ if (mState == ModuleStatus.DETACHED) {
throw new IllegalStateException("Module has been detached.");
}
ModelState modelState = mLoadedModels.get(
@@ -481,7 +503,7 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
synchronized (SoundTriggerMiddlewareValidation.this) {
// State validation.
- if (mState == ModuleState.DETACHED) {
+ if (mState == ModuleStatus.DETACHED) {
throw new IllegalStateException("Module has been detached.");
}
ModelState modelState = mLoadedModels.get(
@@ -515,7 +537,7 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
synchronized (SoundTriggerMiddlewareValidation.this) {
// State validation.
- if (mState == ModuleState.DETACHED) {
+ if (mState == ModuleStatus.DETACHED) {
throw new IllegalStateException("Module has been detached.");
}
ModelState modelState = mLoadedModels.get(
@@ -544,7 +566,7 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
synchronized (SoundTriggerMiddlewareValidation.this) {
// State validation.
- if (mState == ModuleState.DETACHED) {
+ if (mState == ModuleStatus.DETACHED) {
throw new IllegalStateException("Module has been detached.");
}
ModelState modelState = mLoadedModels.get(
@@ -572,7 +594,7 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
synchronized (SoundTriggerMiddlewareValidation.this) {
// State validation.
- if (mState == ModuleState.DETACHED) {
+ if (mState == ModuleStatus.DETACHED) {
throw new IllegalStateException("Module has been detached.");
}
ModelState modelState = mLoadedModels.get(
@@ -600,7 +622,7 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
synchronized (SoundTriggerMiddlewareValidation.this) {
// State validation.
- if (mState == ModuleState.DETACHED) {
+ if (mState == ModuleStatus.DETACHED) {
throw new IllegalStateException("Module has been detached.");
}
ModelState modelState = mLoadedModels.get(
@@ -629,7 +651,7 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
synchronized (SoundTriggerMiddlewareValidation.this) {
// State validation.
- if (mState == ModuleState.DETACHED) {
+ if (mState == ModuleStatus.DETACHED) {
throw new IllegalStateException("Module has been detached.");
}
ModelState modelState = mLoadedModels.get(
@@ -658,10 +680,10 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
synchronized (SoundTriggerMiddlewareValidation.this) {
// State validation.
- if (mState == ModuleState.DETACHED) {
+ if (mState == ModuleStatus.DETACHED) {
throw new IllegalStateException("Module has already been detached.");
}
- if (mState == ModuleState.ALIVE && !mLoadedModels.isEmpty()) {
+ if (mState == ModuleStatus.ALIVE && !mLoadedModels.isEmpty()) {
throw new IllegalStateException("Cannot detach while models are loaded.");
}
@@ -683,16 +705,16 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
private void detachInternal() {
try {
mDelegate.detach();
- mState = ModuleState.DETACHED;
+ mState = ModuleStatus.DETACHED;
mCallback.asBinder().unlinkToDeath(this, 0);
- mModules.get(mHandle).remove(this);
+ mModules.get(mHandle).sessions.remove(this);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
}
void dump(PrintWriter pw) {
- if (mState == ModuleState.ALIVE) {
+ if (mState == ModuleStatus.ALIVE) {
pw.printf("Loaded models for session %s (handle, active)", toString());
pw.println();
pw.println("-------------------------------");
@@ -700,6 +722,8 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
pw.print(entry.getKey());
pw.print('\t');
pw.print(entry.getValue().activityState.name());
+ pw.print('\t');
+ pw.print(entry.getValue().description);
pw.println();
}
} else {
@@ -762,7 +786,7 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
public void onModuleDied() {
synchronized (SoundTriggerMiddlewareValidation.this) {
try {
- mState = ModuleState.DEAD;
+ mState = ModuleStatus.DEAD;
mCallback.onModuleDied();
} catch (RemoteException e) {
// Dead client will be handled by binderDied() - no need to handle here.
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 75d6a090bd3d..4fe58433ddb6 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -674,6 +674,15 @@ final class AccessibilityController {
availableBounds.op(windowBounds, Region.Op.DIFFERENCE);
}
+ // If the navigation bar window doesn't have touchable region, count
+ // navigation bar insets into nonMagnifiedBounds. It happens when
+ // navigation mode is gestural.
+ if (isUntouchableNavigationBar(windowState, mTempRegion3)) {
+ final Rect navBarInsets = getNavBarInsets(mDisplayContent);
+ nonMagnifiedBounds.op(navBarInsets, Region.Op.UNION);
+ availableBounds.op(navBarInsets, Region.Op.DIFFERENCE);
+ }
+
// Count letterbox into nonMagnifiedBounds
if (windowState.isLetterboxedForDisplayCutoutLw()) {
Region letterboxBounds = getLetterboxBounds(windowState);
@@ -1091,6 +1100,24 @@ final class AccessibilityController {
}
}
+ static boolean isUntouchableNavigationBar(WindowState windowState,
+ Region touchableRegion) {
+ if (windowState.mAttrs.type != WindowManager.LayoutParams.TYPE_NAVIGATION_BAR) {
+ return false;
+ }
+
+ // Gets the touchable region.
+ windowState.getTouchableRegion(touchableRegion);
+
+ return touchableRegion.isEmpty();
+ }
+
+ static Rect getNavBarInsets(DisplayContent displayContent) {
+ final InsetsState insetsState =
+ displayContent.getInsetsStateController().getRawInsetsState();
+ return insetsState.getSource(ITYPE_NAVIGATION_BAR).getFrame();
+ }
+
/**
* This class encapsulates the functionality related to computing the windows
* reported for accessibility purposes. These windows are all windows a sighted
@@ -1205,16 +1232,12 @@ final class AccessibilityController {
updateUnaccountedSpace(windowState, regionInScreen, unaccountedSpace,
skipRemainingWindowsForTasks);
focusedWindowAdded |= windowState.isFocused();
- } else if (isUntouchableNavigationBar(windowState)) {
+ } else if (isUntouchableNavigationBar(windowState, mTempRegion1)) {
// If this widow is navigation bar without touchable region, accounting the
// region of navigation bar inset because all touch events from this region
// would be received by launcher, i.e. this region is a un-touchable one
// for the application.
- final InsetsState insetsState =
- dc.getInsetsStateController().getRawInsetsState();
- final Rect displayFrame =
- insetsState.getSource(ITYPE_NAVIGATION_BAR).getFrame();
- unaccountedSpace.op(displayFrame, unaccountedSpace,
+ unaccountedSpace.op(getNavBarInsets(dc), unaccountedSpace,
Region.Op.REVERSE_DIFFERENCE);
}
@@ -1294,18 +1317,6 @@ final class AccessibilityController {
return false;
}
- private boolean isUntouchableNavigationBar(WindowState windowState) {
- if (windowState.mAttrs.type != WindowManager.LayoutParams.TYPE_NAVIGATION_BAR) {
- return false;
- }
-
- // Gets the touchable region.
- Region touchableRegion = mTempRegion1;
- windowState.getTouchableRegion(touchableRegion);
-
- return touchableRegion.isEmpty();
- }
-
private void updateUnaccountedSpace(WindowState windowState, Region regionInScreen,
Region unaccountedSpace, HashSet<Integer> skipRemainingWindowsForTasks) {
if (windowState.mAttrs.type
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index e79b804f76f9..521ffa50f869 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -1374,8 +1374,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
final Rect spaceToFill = transformedBounds != null
? transformedBounds
: inMultiWindowMode()
- ? task.getDisplayedBounds()
- : getRootTask().getParent().getDisplayedBounds();
+ ? task.getBounds()
+ : getRootTask().getParent().getBounds();
mLetterbox.layout(spaceToFill, w.getFrameLw(), mTmpPoint);
} else if (mLetterbox != null) {
mLetterbox.hide();
@@ -6663,17 +6663,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
return super.getBounds();
}
- @Override
- Rect getDisplayedBounds() {
- if (task != null) {
- final Rect overrideDisplayedBounds = task.getOverrideDisplayedBounds();
- if (!overrideDisplayedBounds.isEmpty()) {
- return overrideDisplayedBounds;
- }
- }
- return getBounds();
- }
-
@VisibleForTesting
@Override
Rect getAnimationBounds(int appStackClipMode) {
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index 8bf46bc7c2e8..5968eede0a27 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -91,7 +91,6 @@ import static com.android.server.wm.TaskProto.ANIMATING_BOUNDS;
import static com.android.server.wm.TaskProto.BOUNDS;
import static com.android.server.wm.TaskProto.CREATED_BY_ORGANIZER;
import static com.android.server.wm.TaskProto.DEFER_REMOVAL;
-import static com.android.server.wm.TaskProto.DISPLAYED_BOUNDS;
import static com.android.server.wm.TaskProto.DISPLAY_ID;
import static com.android.server.wm.TaskProto.FILLS_PARENT;
import static com.android.server.wm.TaskProto.LAST_NON_FULLSCREEN_BOUNDS;
@@ -660,8 +659,7 @@ class ActivityStack extends Task {
setBounds(newBounds);
} else if (overrideWindowingMode != WINDOWING_MODE_PINNED) {
// For pinned stack, resize is now part of the {@link WindowContainerTransaction}
- resize(new Rect(newBounds), null /* configBounds */,
- PRESERVE_WINDOWS, true /* deferResume */);
+ resize(new Rect(newBounds), PRESERVE_WINDOWS, true /* deferResume */);
}
}
if (prevIsAlwaysOnTop != isAlwaysOnTop()) {
@@ -835,8 +833,7 @@ class ActivityStack extends Task {
}
if (!Objects.equals(getRequestedOverrideBounds(), mTmpRect2)) {
- resize(mTmpRect2, null /*configBounds*/,
- false /*preserveWindows*/, true /*deferResume*/);
+ resize(mTmpRect2, false /*preserveWindows*/, true /*deferResume*/);
}
} finally {
mAtmService.continueWindowLayout();
@@ -894,9 +891,6 @@ class ActivityStack extends Task {
setTaskBounds(mDeferredBounds);
setBounds(mDeferredBounds);
}
- if (mUpdateDisplayedBoundsDeferredCalled) {
- setTaskDisplayedBounds(mDeferredDisplayedBounds);
- }
}
}
@@ -2966,8 +2960,7 @@ class ActivityStack extends Task {
// TODO: Can only be called from special methods in ActivityStackSupervisor.
// Need to consolidate those calls points into this resize method so anyone can call directly.
- void resize(Rect displayedBounds, Rect configBounds, boolean preserveWindows,
- boolean deferResume) {
+ void resize(Rect displayedBounds, boolean preserveWindows, boolean deferResume) {
if (!updateBoundsAllowed(displayedBounds)) {
return;
}
@@ -2979,7 +2972,7 @@ class ActivityStack extends Task {
// Update override configurations of all tasks in the stack.
final PooledConsumer c = PooledLambda.obtainConsumer(
ActivityStack::processTaskResizeBounds, PooledLambda.__(Task.class),
- displayedBounds, configBounds);
+ displayedBounds);
forAllTasks(c, true /* traverseTopToBottom */);
c.recycle();
@@ -3000,17 +2993,10 @@ class ActivityStack extends Task {
}
}
- private static void processTaskResizeBounds(
- Task task, Rect displayedBounds, Rect configBounds) {
+ private static void processTaskResizeBounds(Task task, Rect displayedBounds) {
if (!task.isResizeable()) return;
- if (configBounds != null && !configBounds.isEmpty()) {
- task.setOverrideDisplayedBounds(displayedBounds);
- task.setBounds(configBounds);
- } else {
- task.setOverrideDisplayedBounds(null);
- task.setBounds(displayedBounds);
- }
+ task.setBounds(displayedBounds);
}
/**
@@ -3032,22 +3018,6 @@ class ActivityStack extends Task {
task.setBounds(task.isResizeable() ? bounds : null);
}
- /** Helper to setDisplayedBounds on all child tasks */
- private void setTaskDisplayedBounds(Rect bounds) {
- if (!updateDisplayedBoundsAllowed(bounds)) {
- return;
- }
-
- final PooledConsumer c = PooledLambda.obtainConsumer(ActivityStack::setTaskDisplayedBounds,
- PooledLambda.__(Task.class), bounds);
- forAllLeafTasks(c, true /* traverseTopToBottom */);
- c.recycle();
- }
-
- private static void setTaskDisplayedBounds(Task task, Rect bounds) {
- task.setOverrideDisplayedBounds(bounds == null || bounds.isEmpty() ? null : bounds);
- }
-
/**
* Returns the top-most activity that occludes the given one, or @{code null} if none.
*/
@@ -3569,8 +3539,8 @@ class ActivityStack extends Task {
}
@Override
- void getRelativeDisplayedPosition(Point outPos) {
- super.getRelativeDisplayedPosition(outPos);
+ void getRelativePosition(Point outPos) {
+ super.getRelativePosition(outPos);
final int outset = getStackOutset();
outPos.x -= outset;
outPos.y -= outset;
@@ -3581,7 +3551,7 @@ class ActivityStack extends Task {
return;
}
- final Rect stackBounds = getDisplayedBounds();
+ final Rect stackBounds = getBounds();
int width = stackBounds.width();
int height = stackBounds.height();
@@ -3776,7 +3746,6 @@ class ActivityStack extends Task {
proto.write(FILLS_PARENT, matchParentBounds());
getRawBounds().dumpDebug(proto, BOUNDS);
- getOverrideDisplayedBounds().dumpDebug(proto, DISPLAYED_BOUNDS);
if (mLastNonFullscreenBounds != null) {
mLastNonFullscreenBounds.dumpDebug(proto, LAST_NON_FULLSCREEN_BOUNDS);
}
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index f924bd4ea194..3bcccedb6de5 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -1357,7 +1357,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
// still need moveTaskToFrontLocked() below for any transition settings.
}
if (stack.shouldResizeStackWithLaunchBounds()) {
- stack.resize(bounds, null /* configBounds */, !PRESERVE_WINDOWS, !DEFER_RESUME);
+ stack.resize(bounds, !PRESERVE_WINDOWS, !DEFER_RESUME);
} else {
// WM resizeTask must be done after the task is moved to the correct stack,
// because Task's setBounds() also updates dim layer's bounds, but that has
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 36d558312c2f..f2d1a411c2c1 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -26,6 +26,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
@@ -73,7 +74,6 @@ import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN;
import static android.view.WindowManager.TRANSIT_TASK_OPEN;
import static android.view.WindowManager.TRANSIT_TASK_TO_FRONT;
-import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG;
@@ -142,6 +142,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
+import android.app.WindowConfiguration;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.pm.ActivityInfo.ScreenOrientation;
@@ -199,7 +200,6 @@ import android.view.ViewRootImpl;
import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.WindowManagerPolicyConstants.PointerEventListener;
-import android.window.ITaskOrganizer;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
@@ -540,6 +540,15 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
*/
WindowState mInputMethodTarget;
+ /**
+ * The window which receives input from the input method. This is also a candidate of the
+ * input method control target.
+ */
+ WindowState mInputMethodInputTarget;
+
+ /**
+ * This controls the visibility and animation of the input method window.
+ */
InsetsControlTarget mInputMethodControlTarget;
/** If true hold off on modifying the animation layer of mInputMethodTarget */
@@ -3247,6 +3256,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
mInsetsStateController.getSourceProvider(ITYPE_IME).setWindow(win,
null /* frameProvider */, null /* imeFrameProvider */);
computeImeTarget(true /* updateImeTarget */);
+ updateImeControlTarget();
}
/**
@@ -3362,34 +3372,18 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
}
}
+ private boolean isImeControlledByApp() {
+ return mInputMethodTarget != null && !WindowConfiguration.isSplitScreenWindowingMode(
+ mInputMethodTarget.getWindowingMode());
+ }
+
boolean isImeAttachedToApp() {
- return (mInputMethodTarget != null && mInputMethodTarget.mActivityRecord != null
+ return isImeControlledByApp()
+ && mInputMethodTarget.mActivityRecord != null
&& mInputMethodTarget.getWindowingMode() == WINDOWING_MODE_FULLSCREEN
// An activity with override bounds should be letterboxed inside its parent bounds,
// so it doesn't fill the screen.
- && mInputMethodTarget.mActivityRecord.matchParentBounds());
- }
-
- /**
- * Get IME target that should host IME when this display that is reparented to another
- * WindowState.
- * IME is never displayed in a child display.
- * Use {@link WindowState#getImeControlTarget()} when IME target window
- * which originally called
- * {@link android.view.inputmethod.InputMethodManager#showSoftInput(View, int)} is known.
- *
- * @return {@link WindowState} of host that controls IME.
- * {@code null} when {@param dc} is not a virtual display.
- * @see DisplayContent#reparent
- */
- @Nullable
- WindowState getImeControlTarget() {
- WindowState imeTarget = mInputMethodTarget;
- if (imeTarget != null) {
- return imeTarget.getImeControlTarget();
- }
-
- return getInsetsStateController().getImeSourceProvider().getControlTarget().getWindow();
+ && mInputMethodTarget.mActivityRecord.matchParentBounds();
}
/**
@@ -3399,7 +3393,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
*
* @param target current IME target.
* @return {@link WindowState} that can host IME.
- * @see DisplayContent#getImeControlTarget()
*/
WindowState getImeHostOrFallback(WindowState target) {
if (target != null && target.getDisplayContent().canShowIme()) {
@@ -3426,8 +3419,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
}
private void setInputMethodTarget(WindowState target, boolean targetWaitingAnim) {
- // Always update control target. This is needed to handle rotation.
- updateImeControlTarget(target);
if (target == mInputMethodTarget && mInputMethodTargetWaitingAnim == targetWaitingAnim) {
return;
}
@@ -3436,32 +3427,27 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
mInputMethodTargetWaitingAnim = targetWaitingAnim;
assignWindowLayers(false /* setLayoutNeeded */);
updateImeParent();
+ updateImeControlTarget();
}
/**
- * IME control target is the window that controls the IME visibility and animation.
- * This window is same as the window on which startInput is called.
- * @param target the window that receives IME control. This is ignored if we aren't attaching
- * the IME to an app (eg. when in multi-window mode).
- *
- * @see #getImeControlTarget()
+ * The IME input target is the window which receives input from IME. It is also a candidate
+ * which controls the visibility and animation of the input method window.
*/
- void updateImeControlTarget(InsetsControlTarget target) {
- if (!isImeAttachedToApp() && mRemoteInsetsControlTarget != null) {
- mInputMethodControlTarget = mRemoteInsetsControlTarget;
- } else {
- // Otherwise, we just use the ime target
- mInputMethodControlTarget = target;
+ void setInputMethodInputTarget(WindowState target) {
+ if (mInputMethodInputTarget != target) {
+ mInputMethodInputTarget = target;
+ updateImeControlTarget();
}
+ }
+
+ private void updateImeControlTarget() {
+ mInputMethodControlTarget = computeImeControlTarget();
mInsetsStateController.onImeControlTargetChanged(mInputMethodControlTarget);
}
private void updateImeParent() {
- // Force attaching IME to the display when magnifying, or it would be magnified with
- // target app together.
- final boolean shouldAttachToDisplay = (mMagnificationSpec != null);
- final SurfaceControl newParent =
- shouldAttachToDisplay ? mWindowContainers.getSurfaceControl() : computeImeParent();
+ final SurfaceControl newParent = computeImeParent();
if (newParent != null) {
getPendingTransaction().reparent(mImeWindowsContainers.mSurfaceControl, newParent);
scheduleAnimation();
@@ -3469,20 +3455,36 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
}
/**
+ * Computes the window where we hand IME control to.
+ */
+ @VisibleForTesting
+ InsetsControlTarget computeImeControlTarget() {
+ if (!isImeControlledByApp() && mRemoteInsetsControlTarget != null) {
+ return mRemoteInsetsControlTarget;
+ } else {
+ // Otherwise, we just use the ime target as received from IME.
+ return mInputMethodInputTarget;
+ }
+ }
+
+ /**
* Computes the window the IME should be attached to.
*/
@VisibleForTesting
SurfaceControl computeImeParent() {
+ // Force attaching IME to the display when magnifying, or it would be magnified with
+ // target app together.
+ final boolean allowAttachToApp = (mMagnificationSpec == null);
// Attach it to app if the target is part of an app and such app is covering the entire
// screen. If it's not covering the entire screen the IME might extend beyond the apps
// bounds.
- if (isImeAttachedToApp()) {
+ if (allowAttachToApp && isImeAttachedToApp()) {
return mInputMethodTarget.mActivityRecord.getSurfaceControl();
}
- // Otherwise, we just attach it to the display.
- return mWindowContainers.getSurfaceControl();
+ // Otherwise, we just attach it to where the display area policy put it.
+ return mImeWindowsContainers.getParent().getSurfaceControl();
}
void setLayoutNeeded() {
@@ -4727,6 +4729,11 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
return mWindowContainers.getSurfaceControl();
}
+ @VisibleForTesting
+ WindowContainer<?> getImeContainer() {
+ return mImeWindowsContainers;
+ }
+
SurfaceControl getOverlayLayer() {
return mOverlayContainers.getSurfaceControl();
}
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index cb0d8536fe72..1ca82ceeb570 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -266,7 +266,7 @@ class InsetsSourceProvider {
if (getSource().getType() == ITYPE_IME) {
setClientVisible(InsetsState.getDefaultVisibility(mSource.getType()));
}
- final Transaction t = mDisplayContent.getPendingTransaction();
+ final Transaction t = mDisplayContent.mWmService.mTransactionFactory.get();
mWin.startAnimation(t, mAdapter, !mClientVisible /* hidden */,
ANIMATION_TYPE_INSETS_CONTROL, null /* animationFinishedCallback */);
final SurfaceControl leash = mAdapter.mCapturedLeash;
@@ -281,6 +281,9 @@ class InsetsSourceProvider {
t.deferTransactionUntil(mWin.getSurfaceControl(), barrier, frameNumber);
t.deferTransactionUntil(leash, barrier, frameNumber);
}
+ // Applying the transaction here can prevent the client from applying its transaction sooner
+ // than us which makes us overwrite the client's operation to the leash.
+ t.apply();
mControlTarget = target;
mControl = new InsetsSourceControl(mSource.getType(), leash,
new Point(mWin.getWindowFrames().mFrame.left, mWin.getWindowFrames().mFrame.top));
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index 54210ae1c0b0..2ce10a74c949 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -898,11 +898,11 @@ public class RecentsAnimationController implements DeathRecipient {
TaskAnimationAdapter(Task task, boolean isRecentTaskInvisible) {
mTask = task;
mIsRecentTaskInvisible = isRecentTaskInvisible;
- mBounds.set(mTask.getDisplayedBounds());
+ mBounds.set(mTask.getBounds());
mLocalBounds.set(mBounds);
Point tmpPos = new Point();
- mTask.getRelativeDisplayedPosition(tmpPos);
+ mTask.getRelativePosition(tmpPos);
mLocalBounds.offsetTo(tmpPos.x, tmpPos.y);
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index ad1a205a4910..1ffa01c2bd66 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -52,7 +52,6 @@ import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_AND_PIPABLE_DEPRECATED;
import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
-import static android.content.res.Configuration.EMPTY;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.content.res.Configuration.ORIENTATION_UNDEFINED;
@@ -353,10 +352,6 @@ class Task extends WindowContainer<WindowContainer> {
final Rect mPreparedFrozenBounds = new Rect();
final Configuration mPreparedFrozenMergedConfig = new Configuration();
- // If non-empty, bounds used to display the task during animations/interactions.
- // TODO(b/119687367): This member is temporary.
- private final Rect mOverrideDisplayedBounds = new Rect();
-
// Id of the previous display the stack was on.
int mPrevDisplayId = INVALID_DISPLAY;
@@ -2795,29 +2790,6 @@ class Task extends WindowContainer<WindowContainer> {
}
}
- /**
- * Displayed bounds are used to set where the task is drawn at any given time. This is
- * separate from its actual bounds so that the app doesn't see any meaningful configuration
- * changes during transitionary periods.
- */
- void setOverrideDisplayedBounds(Rect overrideDisplayedBounds) {
- if (overrideDisplayedBounds != null) {
- adjustForMinimalTaskDimensions(overrideDisplayedBounds, mOverrideDisplayedBounds);
- mOverrideDisplayedBounds.set(overrideDisplayedBounds);
- } else {
- mOverrideDisplayedBounds.setEmpty();
- }
- updateSurfacePosition();
- }
-
- /**
- * Gets the bounds that override where the task is displayed. See
- * {@link android.app.IActivityTaskManager#resizeDockedStack} why this is needed.
- */
- Rect getOverrideDisplayedBounds() {
- return mOverrideDisplayedBounds;
- }
-
boolean isResizeable(boolean checkSupportsPip) {
return (mAtmService.mForceResizableActivities || ActivityInfo.isResizeableMode(mResizeMode)
|| (checkSupportsPip && mSupportsPictureInPicture));
@@ -2851,49 +2823,6 @@ class Task extends WindowContainer<WindowContainer> {
mPreparedFrozenMergedConfig.setTo(getConfiguration());
}
- /**
- * Align the task to the adjusted bounds.
- *
- * @param adjustedBounds Adjusted bounds to which the task should be aligned.
- * @param tempInsetBounds Insets bounds for the task.
- * @param alignBottom True if the task's bottom should be aligned to the adjusted
- * bounds's bottom; false if the task's top should be aligned
- * the adjusted bounds's top.
- */
- void alignToAdjustedBounds(Rect adjustedBounds, Rect tempInsetBounds, boolean alignBottom) {
- if (!isResizeable() || EMPTY.equals(getRequestedOverrideConfiguration())) {
- return;
- }
-
- getBounds(mTmpRect2);
- if (alignBottom) {
- int offsetY = adjustedBounds.bottom - mTmpRect2.bottom;
- mTmpRect2.offset(0, offsetY);
- } else {
- mTmpRect2.offsetTo(adjustedBounds.left, adjustedBounds.top);
- }
- if (tempInsetBounds == null || tempInsetBounds.isEmpty()) {
- setOverrideDisplayedBounds(null);
- setBounds(mTmpRect2);
- } else {
- setOverrideDisplayedBounds(mTmpRect2);
- setBounds(tempInsetBounds);
- }
- }
-
- /**
- * Gets the current overridden displayed bounds. These will be empty if the task is not
- * currently overriding where it is displayed.
- */
- @Override
- public Rect getDisplayedBounds() {
- if (mOverrideDisplayedBounds.isEmpty()) {
- return super.getDisplayedBounds();
- } else {
- return mOverrideDisplayedBounds;
- }
- }
-
@Override
void getAnimationFrames(Rect outFrame, Rect outInsets, Rect outStableInsets,
Rect outSurfaceInsets) {
@@ -3431,7 +3360,6 @@ class Task extends WindowContainer<WindowContainer> {
pw.println(prefix + "taskId=" + mTaskId);
pw.println(doublePrefix + "mBounds=" + getBounds().toShortString());
pw.println(doublePrefix + "appTokens=" + mChildren);
- pw.println(doublePrefix + "mDisplayedBounds=" + mOverrideDisplayedBounds.toShortString());
final String triplePrefix = doublePrefix + " ";
final String quadruplePrefix = triplePrefix + " ";
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 7219164ad2f1..899ab247077a 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -2062,7 +2062,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
// TODO: Remove this and use #getBounds() instead once we set an app transition animation
// on TaskStack.
Rect getAnimationBounds(int appStackClipMode) {
- return getDisplayedBounds();
+ return getBounds();
}
/**
@@ -2124,7 +2124,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
// Separate position and size for use in animators.
mTmpRect.set(getAnimationBounds(appStackClipMode));
if (sHierarchicalAnimations) {
- getRelativeDisplayedPosition(mTmpPoint);
+ getRelativePosition(mTmpPoint);
} else {
mTmpPoint.set(mTmpRect.left, mTmpRect.top);
}
@@ -2304,14 +2304,18 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
}
}
+ void resetSurfacePositionForAnimationLeash(Transaction t) {
+ t.setPosition(mSurfaceControl, 0, 0);
+ mLastSurfacePosition.set(0, 0);
+ }
+
@Override
public void onAnimationLeashCreated(Transaction t, SurfaceControl leash) {
mLastLayer = -1;
reassignLayer(t);
// Leash is now responsible for position, so set our position to 0.
- t.setPosition(mSurfaceControl, 0, 0);
- mLastSurfacePosition.set(0, 0);
+ resetSurfacePositionForAnimationLeash(t);
}
@Override
@@ -2395,7 +2399,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
return;
}
- getRelativeDisplayedPosition(mTmpPos);
+ getRelativePosition(mTmpPos);
if (mTmpPos.equals(mLastSurfacePosition)) {
return;
}
@@ -2410,16 +2414,6 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
}
/**
- * Displayed bounds specify where to display this container at. It differs from bounds during
- * certain operations (like animation or interactive dragging).
- *
- * @return the bounds to display this container at.
- */
- Rect getDisplayedBounds() {
- return getBounds();
- }
-
- /**
* The {@code outFrame} retrieved by this method specifies where the animation will finish
* the entrance animation, as the next frame will display the window at these coordinates. In
* case of exit animation, this is where the animation will start, as the frame before the
@@ -2439,7 +2433,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
outSurfaceInsets.setEmpty();
}
- void getRelativeDisplayedPosition(Point outPos) {
+ void getRelativePosition(Point outPos) {
// In addition to updateSurfacePosition, we keep other code that sets
// position from fighting with the organizer
if (isOrganized()) {
@@ -2447,11 +2441,11 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
return;
}
- final Rect dispBounds = getDisplayedBounds();
+ final Rect dispBounds = getBounds();
outPos.set(dispBounds.left, dispBounds.top);
final WindowContainer parent = getParent();
if (parent != null) {
- final Rect parentBounds = parent.getDisplayedBounds();
+ final Rect parentBounds = parent.getBounds();
outPos.offset(-parentBounds.left, -parentBounds.top);
}
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 5c7d37baef37..84cc19d68a24 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -1697,15 +1697,14 @@ public class WindowManagerService extends IWindowManager.Stub
}
displayContent.getInputMonitor().updateInputWindowsLw(false /*force*/);
- getInsetsSourceControls(win, outActiveControls);
-
ProtoLog.v(WM_DEBUG_ADD_REMOVE, "addWindow: New client %s"
+ ": window=%s Callers=%s", client.asBinder(), win, Debug.getCallers(5));
-
if (win.isVisibleOrAdding() && displayContent.updateOrientation()) {
displayContent.sendNewConfiguration();
}
+
+ getInsetsSourceControls(win, outActiveControls);
}
Binder.restoreCallingIdentity(origId);
@@ -2401,7 +2400,6 @@ public class WindowManagerService extends IWindowManager.Stub
outCutout.set(win.getWmDisplayCutout().getDisplayCutout());
outBackdropFrame.set(win.getBackdropFrame(win.getFrameLw()));
outInsetsState.set(win.getInsetsState(), win.isClientLocal());
- getInsetsSourceControls(win, outActiveControls);
if (DEBUG) {
Slog.v(TAG_WM, "Relayout given client " + client.asBinder()
+ ", requestedWidth=" + requestedWidth
@@ -2432,6 +2430,7 @@ public class WindowManagerService extends IWindowManager.Stub
outSurfaceSize.set(winAnimator.mSurfaceController.getWidth(),
winAnimator.mSurfaceController.getHeight());
}
+ getInsetsSourceControls(win, outActiveControls);
}
Binder.restoreCallingIdentity(origId);
@@ -2446,8 +2445,17 @@ public class WindowManagerService extends IWindowManager.Stub
if (controls != null) {
final int length = Math.min(controls.length, outControls.length);
for (int i = 0; i < length; i++) {
- outControls[i] = win.isClientLocal()
- ? new InsetsSourceControl(controls[i]) : controls[i];
+ final InsetsSourceControl control = controls[i];
+
+ // Check if we are sending invalid leashes.
+ final SurfaceControl leash = control != null ? control.getLeash() : null;
+ if (leash != null && !leash.isValid()) {
+ Slog.wtf(TAG, leash + " is not valid before sending to " + win,
+ leash.getReleaseStack());
+ }
+
+ outControls[i] = win.isClientLocal() && control != null
+ ? new InsetsSourceControl(control) : control;
}
}
}
@@ -6107,10 +6115,15 @@ public class WindowManagerService extends IWindowManager.Stub
mRoot.forAllDisplays(dc -> {
final int displayId = dc.getDisplayId();
final WindowState inputMethodTarget = dc.mInputMethodTarget;
+ final WindowState inputMethodInputTarget = dc.mInputMethodInputTarget;
if (inputMethodTarget != null) {
pw.print(" mInputMethodTarget in display# "); pw.print(displayId);
pw.print(' '); pw.println(inputMethodTarget);
}
+ if (inputMethodInputTarget != null) {
+ pw.print(" mInputMethodInputTarget in display# "); pw.print(displayId);
+ pw.print(' '); pw.println(inputMethodInputTarget);
+ }
if (mAccessibilityController != null) {
final Region magnificationRegion = new Region();
mAccessibilityController.getMagnificationRegionLocked(displayId,
@@ -7362,7 +7375,7 @@ public class WindowManagerService extends IWindowManager.Stub
synchronized (mGlobalLock) {
final WindowState imeTarget = mWindowMap.get(imeTargetWindowToken);
if (imeTarget != null) {
- imeTarget.getDisplayContent().updateImeControlTarget(imeTarget);
+ imeTarget.getDisplayContent().setInputMethodInputTarget(imeTarget);
}
}
}
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index d9c0219c4779..8b27667475a9 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -320,7 +320,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
final ActivityStack stack = (ActivityStack) container;
if (stack.inPinnedWindowingMode()) {
stack.resize(config.windowConfiguration.getBounds(),
- null /* configBounds */, PRESERVE_WINDOWS, true /* deferResume */);
+ PRESERVE_WINDOWS, true /* deferResume */);
}
}
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index c11c29b5deb6..af680701a33e 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -999,18 +999,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
frame.inset(left, top, right, bottom);
}
- @Override
- public Rect getDisplayedBounds() {
- final Task task = getTask();
- if (task != null) {
- Rect bounds = task.getOverrideDisplayedBounds();
- if (!bounds.isEmpty()) {
- return bounds;
- }
- }
- return super.getDisplayedBounds();
- }
-
void computeFrame(DisplayFrames displayFrames) {
getLayoutingWindowFrames().setDisplayCutout(displayFrames.mDisplayCutout);
computeFrameLw();
@@ -1065,7 +1053,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
layoutXDiff = 0;
layoutYDiff = 0;
} else {
- windowFrames.mContainingFrame.set(getDisplayedBounds());
+ windowFrames.mContainingFrame.set(getBounds());
if (mActivityRecord != null && !mActivityRecord.mFrozenBounds.isEmpty()) {
// If the bounds are frozen, we still want to translate the window freely and only
@@ -1223,7 +1211,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
parentLeft = ((WindowState) parent).mWindowFrames.mFrame.left;
parentTop = ((WindowState) parent).mWindowFrames.mFrame.top;
} else if (parent != null) {
- final Rect parentBounds = parent.getDisplayedBounds();
+ final Rect parentBounds = parent.getBounds();
parentLeft = parentBounds.left;
parentTop = parentBounds.top;
}
@@ -1469,6 +1457,11 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
@Override
void onDisplayChanged(DisplayContent dc) {
+ if (dc != null && mDisplayContent != null
+ && mDisplayContent.mInputMethodInputTarget == this) {
+ dc.setInputMethodInputTarget(mDisplayContent.mInputMethodInputTarget);
+ mDisplayContent.mInputMethodInputTarget = null;
+ }
super.onDisplayChanged(dc);
// Window was not laid out for this display yet, so make sure mLayoutSeq does not match.
if (dc != null && mInputWindowHandle.displayId != dc.getDisplayId()) {
@@ -5245,16 +5238,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
}
@Override
- public void onAnimationLeashCreated(Transaction t, SurfaceControl leash) {
- super.onAnimationLeashCreated(t, leash);
- }
-
- @Override
- public void onAnimationLeashLost(Transaction t) {
- super.onAnimationLeashLost(t);
- }
-
- @Override
@VisibleForTesting
void updateSurfacePosition(Transaction t) {
if (mSurfaceControl == null) {
@@ -5295,7 +5278,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
outPoint.offset(-parent.mWindowFrames.mFrame.left + mTmpPoint.x,
-parent.mWindowFrames.mFrame.top + mTmpPoint.y);
} else if (parentWindowContainer != null) {
- final Rect parentBounds = parentWindowContainer.getDisplayedBounds();
+ final Rect parentBounds = parentWindowContainer.getBounds();
outPoint.offset(-parentBounds.left, -parentBounds.top);
}
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index d4470f8334ea..99577077d65d 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -1030,7 +1030,7 @@ class WindowStateAnimator {
mTmpPos.x = 0;
mTmpPos.y = 0;
if (stack != null) {
- stack.getRelativeDisplayedPosition(mTmpPos);
+ stack.getRelativePosition(mTmpPos);
}
xOffset = -mTmpPos.x;
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index b4e770f24eaf..21c76874f5c0 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -632,6 +632,15 @@ class WindowToken extends WindowContainer<WindowState> {
}
}
+ @Override
+ void resetSurfacePositionForAnimationLeash(SurfaceControl.Transaction t) {
+ // Keep the transformed position to animate because the surface will show in different
+ // rotation than the animator of leash.
+ if (!isFixedRotationTransforming()) {
+ super.resetSurfacePositionForAnimationLeash(t);
+ }
+ }
+
/**
* Gives a chance to this {@link WindowToken} to adjust the {@link
* android.view.WindowManager.LayoutParams} of its windows.
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 1da074002456..2c0d4c0c9208 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -4733,33 +4733,13 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (!parent && isSeparateProfileChallengeEnabled(userHandle)) {
// If this user has a separate challenge, only return its restrictions.
return getUserDataUnchecked(userHandle).mAdminList;
- } else {
- // Return all admins for this user and the profiles that are visible from this
- // user that do not use a separate work challenge.
- ArrayList<ActiveAdmin> admins = new ArrayList<ActiveAdmin>();
- for (UserInfo userInfo : mUserManager.getProfiles(userHandle)) {
- DevicePolicyData policy = getUserData(userInfo.id);
- if (!userInfo.isManagedProfile()) {
- admins.addAll(policy.mAdminList);
- } else {
- // For managed profiles, we always include the policies set on the parent
- // profile. Additionally, we include the ones set on the managed profile
- // if no separate challenge is in place.
- boolean hasSeparateChallenge = isSeparateProfileChallengeEnabled(userInfo.id);
- final int N = policy.mAdminList.size();
- for (int i = 0; i < N; i++) {
- ActiveAdmin admin = policy.mAdminList.get(i);
- if (admin.hasParentActiveAdmin()) {
- admins.add(admin.getParentActiveAdmin());
- }
- if (!hasSeparateChallenge) {
- admins.add(admin);
- }
- }
- }
- }
- return admins;
}
+ // Either parent == true, or isSeparateProfileChallengeEnabled == false
+ // If parent is true, query the parent user of userHandle by definition,
+ // If isSeparateProfileChallengeEnabled is false, userHandle points to a managed profile
+ // with unified challenge so also need to query the parent user who owns the credential.
+ return getActiveAdminsForUserAndItsManagedProfilesLocked(getProfileParentId(userHandle),
+ (user) -> !mLockPatternUtils.isSeparateProfileChallengeEnabled(user.id));
}
/**
@@ -4777,6 +4757,19 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (isManagedProfile(userHandle)) {
return getUserDataUnchecked(userHandle).mAdminList;
}
+ return getActiveAdminsForUserAndItsManagedProfilesLocked(userHandle,
+ /* shouldIncludeProfileAdmins */ (user) -> false);
+ }
+
+ /**
+ * Returns the list of admins on the given user, as well as parent admins for each managed
+ * profile associated with the given user. Optionally also include the admin of each managed
+ * profile.
+ * <p> Should not be called on a profile user.
+ */
+ @GuardedBy("getLockObject()")
+ private List<ActiveAdmin> getActiveAdminsForUserAndItsManagedProfilesLocked(int userHandle,
+ Predicate<UserInfo> shouldIncludeProfileAdmins) {
ArrayList<ActiveAdmin> admins = new ArrayList<>();
mInjector.binderWithCleanCallingIdentity(() -> {
for (UserInfo userInfo : mUserManager.getProfiles(userHandle)) {
@@ -4784,12 +4777,14 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (userInfo.id == userHandle) {
admins.addAll(policy.mAdminList);
} else if (userInfo.isManagedProfile()) {
- // For managed profiles, policies set on the parent profile will be included
for (int i = 0; i < policy.mAdminList.size(); i++) {
ActiveAdmin admin = policy.mAdminList.get(i);
if (admin.hasParentActiveAdmin()) {
admins.add(admin.getParentActiveAdmin());
}
+ if (shouldIncludeProfileAdmins.test(userInfo)) {
+ admins.add(admin);
+ }
}
} else {
Slog.w(LOG_TAG, "Unknown user type: " + userInfo);
@@ -5366,6 +5361,32 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
}
+ @Override
+ public boolean isPasswordSufficientAfterProfileUnification(int userHandle, int profileUser) {
+ if (!mHasFeature) {
+ return true;
+ }
+ enforceFullCrossUsersPermission(userHandle);
+ enforceNotManagedProfile(userHandle, "check password sufficiency");
+ enforceUserUnlocked(userHandle);
+
+ synchronized (getLockObject()) {
+ PasswordMetrics metrics = mLockSettingsInternal.getUserPasswordMetrics(userHandle);
+
+ // Combine password policies across the user and its profiles. Profile admins are
+ // included if the profile is to be unified or currently has unified challenge
+ List<ActiveAdmin> admins = getActiveAdminsForUserAndItsManagedProfilesLocked(userHandle,
+ /* shouldIncludeProfileAdmins */ (user) -> user.id == profileUser
+ || !mLockPatternUtils.isSeparateProfileChallengeEnabled(user.id));
+ ArrayList<PasswordMetrics> adminMetrics = new ArrayList<>(admins.size());
+ for (ActiveAdmin admin : admins) {
+ adminMetrics.add(admin.mPasswordPolicy.getMinMetrics());
+ }
+ return PasswordMetrics.validatePasswordMetrics(PasswordMetrics.merge(adminMetrics),
+ PASSWORD_COMPLEXITY_NONE, false, metrics).isEmpty();
+ }
+ }
+
private boolean isActivePasswordSufficientForUserLocked(
boolean passwordValidAtLastCheckpoint, @Nullable PasswordMetrics metrics,
int userHandle, boolean parent) {
diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp
index 149dfa6be6c4..c1f237f91b44 100644
--- a/services/incremental/IncrementalService.cpp
+++ b/services/incremental/IncrementalService.cpp
@@ -258,10 +258,6 @@ IncrementalService::IncrementalService(ServiceManagerWrapper&& sm, std::string_v
mountExistingImages();
}
-FileId IncrementalService::idFromMetadata(std::span<const uint8_t> metadata) {
- return IncFs_FileIdFromMetadata({(const char*)metadata.data(), metadata.size()});
-}
-
IncrementalService::~IncrementalService() {
{
std::lock_guard lock(mJobMutex);
@@ -1016,7 +1012,7 @@ bool IncrementalService::startLoading(StorageId storage) const {
return false;
}
}
- return dataLoaderStub->start();
+ return dataLoaderStub->requestStart();
}
void IncrementalService::mountExistingImages() {
@@ -1337,13 +1333,13 @@ void IncrementalService::extractZipFile(const IfsMountPtr& ifs, ZipArchiveHandle
std::vector<IncFsDataBlock> instructions(numBlocks);
auto remainingData = std::span(libData.get(), entry.uncompressed_length);
for (int i = 0; i < numBlocks; i++) {
- const auto blockSize = std::min<uint16_t>(constants().blockSize, remainingData.size());
+ const auto blockSize = std::min<long>(constants().blockSize, remainingData.size());
instructions[i] = IncFsDataBlock{
.fileFd = writeFd.get(),
.pageIndex = static_cast<IncFsBlockIndex>(i),
.compression = INCFS_COMPRESSION_KIND_NONE,
.kind = INCFS_BLOCK_KIND_DATA,
- .dataSize = blockSize,
+ .dataSize = static_cast<uint32_t>(blockSize),
.data = reinterpret_cast<const char*>(remainingData.data()),
};
remainingData = remainingData.subspan(blockSize);
@@ -1475,12 +1471,15 @@ void IncrementalService::onAppOpChanged(const std::string& packageName) {
}
IncrementalService::DataLoaderStub::~DataLoaderStub() {
- CHECK(mStatus == -1 || mStatus == IDataLoaderStatusListener::DATA_LOADER_DESTROYED)
- << "Dataloader has to be destroyed prior to destructor: " << mId
- << ", status: " << mStatus;
+ waitForDestroy();
}
bool IncrementalService::DataLoaderStub::create() {
+ {
+ std::unique_lock lock(mStatusMutex);
+ mStartRequested = false;
+ mDestroyRequested = false;
+ }
bool created = false;
auto status = mService.mDataLoaderManager->initializeDataLoader(mId, mParams, mControl, this,
&created);
@@ -1491,12 +1490,18 @@ bool IncrementalService::DataLoaderStub::create() {
return true;
}
-bool IncrementalService::DataLoaderStub::start() {
- if (mStatus != IDataLoaderStatusListener::DATA_LOADER_CREATED) {
+bool IncrementalService::DataLoaderStub::requestStart() {
+ {
+ std::unique_lock lock(mStatusMutex);
mStartRequested = true;
- return true;
+ if (mStatus != IDataLoaderStatusListener::DATA_LOADER_CREATED) {
+ return true;
+ }
}
+ return start();
+}
+bool IncrementalService::DataLoaderStub::start() {
sp<IDataLoader> dataloader;
auto status = mService.mDataLoaderManager->getDataLoader(mId, &dataloader);
if (!status.isOk()) {
@@ -1513,8 +1518,21 @@ bool IncrementalService::DataLoaderStub::start() {
}
void IncrementalService::DataLoaderStub::destroy() {
- mDestroyRequested = true;
+ {
+ std::unique_lock lock(mStatusMutex);
+ mDestroyRequested = true;
+ }
mService.mDataLoaderManager->destroyDataLoader(mId);
+
+ waitForDestroy();
+}
+
+bool IncrementalService::DataLoaderStub::waitForDestroy() {
+ auto now = std::chrono::steady_clock::now();
+ std::unique_lock lock(mStatusMutex);
+ return mStatusCondition.wait_until(lock, now + 60s, [this] {
+ return mStatus == IDataLoaderStatusListener::DATA_LOADER_DESTROYED;
+ });
}
binder::Status IncrementalService::DataLoaderStub::onStatusChanged(MountId mountId, int newStatus) {
@@ -1523,34 +1541,36 @@ binder::Status IncrementalService::DataLoaderStub::onStatusChanged(MountId mount
}
if (mListener) {
- // Give an external listener a chance to act before we destroy something.
mListener->onStatusChanged(mountId, newStatus);
}
+ bool startRequested;
+ bool destroyRequested;
{
- std::unique_lock l(mService.mLock);
- const auto& ifs = mService.getIfsLocked(mountId);
- if (!ifs) {
- LOG(WARNING) << "Received data loader status " << int(newStatus)
- << " for unknown mount " << mountId;
+ std::unique_lock lock(mStatusMutex);
+ if (mStatus == newStatus) {
return binder::Status::ok();
}
- mStatus = newStatus;
- if (!mDestroyRequested && newStatus == IDataLoaderStatusListener::DATA_LOADER_DESTROYED) {
- mService.deleteStorageLocked(*ifs, std::move(l));
- return binder::Status::ok();
- }
+ startRequested = mStartRequested;
+ destroyRequested = mDestroyRequested;
+
+ mStatus = newStatus;
}
switch (newStatus) {
case IDataLoaderStatusListener::DATA_LOADER_CREATED: {
- if (mStartRequested) {
+ if (startRequested) {
+ LOG(WARNING) << "Start was requested, triggering, for mount: " << mountId;
start();
}
break;
}
case IDataLoaderStatusListener::DATA_LOADER_DESTROYED: {
+ if (!destroyRequested) {
+ LOG(WARNING) << "DataLoader destroyed, reconnecting, for mount: " << mountId;
+ create();
+ }
break;
}
case IDataLoaderStatusListener::DATA_LOADER_STARTED: {
@@ -1589,4 +1609,8 @@ binder::Status IncrementalService::IncrementalServiceConnector::setStorageParams
return binder::Status::ok();
}
+FileId IncrementalService::idFromMetadata(std::span<const uint8_t> metadata) {
+ return IncFs_FileIdFromMetadata({(const char*)metadata.data(), metadata.size()});
+}
+
} // namespace android::incremental
diff --git a/services/incremental/IncrementalService.h b/services/incremental/IncrementalService.h
index c016bab067be..8c79d7725dcf 100644
--- a/services/incremental/IncrementalService.h
+++ b/services/incremental/IncrementalService.h
@@ -180,27 +180,32 @@ private:
~DataLoaderStub();
bool create();
- bool start();
+ bool requestStart();
void destroy();
// accessors
MountId id() const { return mId; }
const DataLoaderParamsParcel& params() const { return mParams; }
- int status() const { return mStatus.load(); }
+ int status() const { return mStatus; }
bool startRequested() const { return mStartRequested; }
private:
binder::Status onStatusChanged(MountId mount, int newStatus) final;
+ bool start();
+ bool waitForDestroy();
+
IncrementalService& mService;
MountId const mId;
DataLoaderParamsParcel const mParams;
FileSystemControlParcel const mControl;
DataLoaderStatusListener const mListener;
- std::atomic<int> mStatus = -1;
+ std::mutex mStatusMutex;
+ std::condition_variable mStatusCondition;
+ int mStatus = IDataLoaderStatusListener::DATA_LOADER_DESTROYED;
bool mStartRequested = false;
- bool mDestroyRequested = false;
+ bool mDestroyRequested = true;
};
using DataLoaderStubPtr = sp<DataLoaderStub>;
diff --git a/services/incremental/test/IncrementalServiceTest.cpp b/services/incremental/test/IncrementalServiceTest.cpp
index 117dca8c37b3..f9737fee450d 100644
--- a/services/incremental/test/IncrementalServiceTest.cpp
+++ b/services/incremental/test/IncrementalServiceTest.cpp
@@ -103,25 +103,34 @@ private:
TemporaryFile logFile;
};
-class FakeDataLoader : public IDataLoader {
+class MockDataLoader : public IDataLoader {
public:
- IBinder* onAsBinder() override { return nullptr; }
- binder::Status create(int32_t, const DataLoaderParamsParcel&, const FileSystemControlParcel&,
- const sp<IDataLoaderStatusListener>&) override {
- return binder::Status::ok();
- }
- binder::Status start(int32_t) override { return binder::Status::ok(); }
- binder::Status stop(int32_t) override { return binder::Status::ok(); }
- binder::Status destroy(int32_t) override { return binder::Status::ok(); }
- binder::Status prepareImage(int32_t,
- const std::vector<InstallationFileParcel>&,
- const std::vector<std::string>&) override {
- return binder::Status::ok();
+ MockDataLoader() {
+ ON_CALL(*this, create(_, _, _, _)).WillByDefault(Return((binder::Status::ok())));
+ ON_CALL(*this, start(_)).WillByDefault(Return((binder::Status::ok())));
+ ON_CALL(*this, stop(_)).WillByDefault(Return((binder::Status::ok())));
+ ON_CALL(*this, destroy(_)).WillByDefault(Return((binder::Status::ok())));
+ ON_CALL(*this, prepareImage(_, _, _)).WillByDefault(Return((binder::Status::ok())));
}
+ IBinder* onAsBinder() override { return nullptr; }
+ MOCK_METHOD4(create,
+ binder::Status(int32_t id, const DataLoaderParamsParcel& params,
+ const FileSystemControlParcel& control,
+ const sp<IDataLoaderStatusListener>& listener));
+ MOCK_METHOD1(start, binder::Status(int32_t id));
+ MOCK_METHOD1(stop, binder::Status(int32_t id));
+ MOCK_METHOD1(destroy, binder::Status(int32_t id));
+ MOCK_METHOD3(prepareImage,
+ binder::Status(int32_t id, const std::vector<InstallationFileParcel>& addedFiles,
+ const std::vector<std::string>& removedFiles));
};
class MockDataLoaderManager : public DataLoaderManagerWrapper {
public:
+ MockDataLoaderManager(sp<IDataLoader> dataLoader) : mDataLoaderHolder(std::move(dataLoader)) {
+ EXPECT_TRUE(mDataLoaderHolder != nullptr);
+ }
+
MOCK_CONST_METHOD5(initializeDataLoader,
binder::Status(int32_t mountId, const DataLoaderParamsParcel& params,
const FileSystemControlParcel& control,
@@ -144,9 +153,9 @@ public:
ON_CALL(*this, getDataLoader(_, _))
.WillByDefault(Invoke(this, &MockDataLoaderManager::getDataLoaderOk));
}
- void destroyDataLoaderOk() {
+ void destroyDataLoaderSuccess() {
ON_CALL(*this, destroyDataLoader(_))
- .WillByDefault(Invoke(this, &MockDataLoaderManager::setDataLoaderStatusDestroyed));
+ .WillByDefault(Invoke(this, &MockDataLoaderManager::destroyDataLoaderOk));
}
binder::Status initializeDataLoaderOk(int32_t mountId, const DataLoaderParamsParcel& params,
const FileSystemControlParcel& control,
@@ -155,20 +164,30 @@ public:
mId = mountId;
mListener = listener;
mServiceConnector = control.service;
+ mDataLoader = mDataLoaderHolder;
*_aidl_return = true;
- return binder::Status::ok();
+ return mDataLoader->create(mountId, params, control, listener);
}
binder::Status getDataLoaderOk(int32_t mountId, sp<IDataLoader>* _aidl_return) {
*_aidl_return = mDataLoader;
return binder::Status::ok();
}
- void setDataLoaderStatusNotReady() {
- mListener->onStatusChanged(mId, IDataLoaderStatusListener::DATA_LOADER_DESTROYED);
- }
- void setDataLoaderStatusReady() {
+ void setDataLoaderStatusCreated() {
mListener->onStatusChanged(mId, IDataLoaderStatusListener::DATA_LOADER_CREATED);
}
- binder::Status setDataLoaderStatusDestroyed(int32_t id) {
+ void setDataLoaderStatusStarted() {
+ mListener->onStatusChanged(mId, IDataLoaderStatusListener::DATA_LOADER_STARTED);
+ }
+ void setDataLoaderStatusDestroyed() {
+ mListener->onStatusChanged(mId, IDataLoaderStatusListener::DATA_LOADER_DESTROYED);
+ }
+ binder::Status destroyDataLoaderOk(int32_t id) {
+ if (mDataLoader) {
+ if (auto status = mDataLoader->destroy(id); !status.isOk()) {
+ return status;
+ }
+ mDataLoader = nullptr;
+ }
if (mListener) {
mListener->onStatusChanged(id, IDataLoaderStatusListener::DATA_LOADER_DESTROYED);
}
@@ -185,7 +204,8 @@ private:
int mId;
sp<IDataLoaderStatusListener> mListener;
sp<IIncrementalServiceConnector> mServiceConnector;
- sp<IDataLoader> mDataLoader = sp<IDataLoader>(new FakeDataLoader());
+ sp<IDataLoader> mDataLoader;
+ sp<IDataLoader> mDataLoaderHolder;
};
class MockIncFs : public IncFsWrapper {
@@ -303,7 +323,9 @@ public:
void SetUp() override {
auto vold = std::make_unique<NiceMock<MockVoldService>>();
mVold = vold.get();
- auto dataloaderManager = std::make_unique<NiceMock<MockDataLoaderManager>>();
+ sp<NiceMock<MockDataLoader>> dataLoader{new NiceMock<MockDataLoader>};
+ mDataLoader = dataLoader.get();
+ auto dataloaderManager = std::make_unique<NiceMock<MockDataLoaderManager>>(dataLoader);
mDataLoaderManager = dataloaderManager.get();
auto incFs = std::make_unique<NiceMock<MockIncFs>>();
mIncFs = incFs.get();
@@ -321,7 +343,7 @@ public:
mRootDir.path);
mDataLoaderParcel.packageName = "com.test";
mDataLoaderParcel.arguments = "uri";
- mDataLoaderManager->destroyDataLoaderOk();
+ mDataLoaderManager->destroyDataLoaderSuccess();
mIncrementalService->onSystemReady();
}
@@ -352,6 +374,7 @@ protected:
NiceMock<MockDataLoaderManager>* mDataLoaderManager;
NiceMock<MockAppOpsManager>* mAppOpsManager;
NiceMock<MockJniWrapper>* mJni;
+ NiceMock<MockDataLoader>* mDataLoader;
std::unique_ptr<IncrementalService> mIncrementalService;
TemporaryDir mRootDir;
DataLoaderParamsParcel mDataLoaderParcel;
@@ -410,7 +433,11 @@ TEST_F(IncrementalServiceTest, testCreateStoragePrepareDataLoaderFails) {
mIncFs->makeFileSuccess();
mVold->bindMountSuccess();
mDataLoaderManager->initializeDataLoaderFails();
+ EXPECT_CALL(*mDataLoaderManager, initializeDataLoader(_, _, _, _, _)).Times(1);
EXPECT_CALL(*mDataLoaderManager, destroyDataLoader(_)).Times(1);
+ EXPECT_CALL(*mDataLoader, create(_, _, _, _)).Times(0);
+ EXPECT_CALL(*mDataLoader, start(_)).Times(0);
+ EXPECT_CALL(*mDataLoader, destroy(_)).Times(0);
EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
TemporaryDir tempDir;
int storageId =
@@ -424,46 +451,84 @@ TEST_F(IncrementalServiceTest, testDeleteStorageSuccess) {
mIncFs->makeFileSuccess();
mVold->bindMountSuccess();
mDataLoaderManager->initializeDataLoaderSuccess();
+ EXPECT_CALL(*mDataLoaderManager, initializeDataLoader(_, _, _, _, _)).Times(1);
EXPECT_CALL(*mDataLoaderManager, destroyDataLoader(_)).Times(1);
+ EXPECT_CALL(*mDataLoader, create(_, _, _, _)).Times(1);
+ EXPECT_CALL(*mDataLoader, start(_)).Times(0);
+ EXPECT_CALL(*mDataLoader, destroy(_)).Times(1);
EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
TemporaryDir tempDir;
int storageId =
mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), {},
IncrementalService::CreateOptions::CreateNew);
ASSERT_GE(storageId, 0);
+ mDataLoaderManager->setDataLoaderStatusCreated();
mIncrementalService->deleteStorage(storageId);
}
-TEST_F(IncrementalServiceTest, testOnStatusNotReady) {
+TEST_F(IncrementalServiceTest, testDataLoaderDestroyed) {
mVold->mountIncFsSuccess();
mIncFs->makeFileSuccess();
mVold->bindMountSuccess();
mDataLoaderManager->initializeDataLoaderSuccess();
- EXPECT_CALL(*mDataLoaderManager, destroyDataLoader(_));
+ mDataLoaderManager->getDataLoaderSuccess();
+ EXPECT_CALL(*mDataLoaderManager, initializeDataLoader(_, _, _, _, _)).Times(2);
+ EXPECT_CALL(*mDataLoaderManager, destroyDataLoader(_)).Times(1);
+ EXPECT_CALL(*mDataLoader, create(_, _, _, _)).Times(2);
+ EXPECT_CALL(*mDataLoader, start(_)).Times(0);
+ EXPECT_CALL(*mDataLoader, destroy(_)).Times(1);
EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
TemporaryDir tempDir;
int storageId =
mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), {},
IncrementalService::CreateOptions::CreateNew);
ASSERT_GE(storageId, 0);
- mDataLoaderManager->setDataLoaderStatusNotReady();
+ mDataLoaderManager->setDataLoaderStatusCreated();
+ // Simulated crash/other connection breakage.
+ mDataLoaderManager->setDataLoaderStatusDestroyed();
}
-TEST_F(IncrementalServiceTest, testStartDataLoaderSuccess) {
+TEST_F(IncrementalServiceTest, testStartDataLoaderCreate) {
mVold->mountIncFsSuccess();
mIncFs->makeFileSuccess();
mVold->bindMountSuccess();
mDataLoaderManager->initializeDataLoaderSuccess();
mDataLoaderManager->getDataLoaderSuccess();
- EXPECT_CALL(*mDataLoaderManager, destroyDataLoader(_));
+ EXPECT_CALL(*mDataLoaderManager, initializeDataLoader(_, _, _, _, _)).Times(1);
+ EXPECT_CALL(*mDataLoaderManager, destroyDataLoader(_)).Times(1);
+ EXPECT_CALL(*mDataLoader, create(_, _, _, _)).Times(1);
+ EXPECT_CALL(*mDataLoader, start(_)).Times(1);
+ EXPECT_CALL(*mDataLoader, destroy(_)).Times(1);
+ EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
+ TemporaryDir tempDir;
+ int storageId =
+ mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), {},
+ IncrementalService::CreateOptions::CreateNew);
+ ASSERT_GE(storageId, 0);
+ mDataLoaderManager->setDataLoaderStatusCreated();
+ ASSERT_TRUE(mIncrementalService->startLoading(storageId));
+ mDataLoaderManager->setDataLoaderStatusStarted();
+}
+
+TEST_F(IncrementalServiceTest, testStartDataLoaderPendingStart) {
+ mVold->mountIncFsSuccess();
+ mIncFs->makeFileSuccess();
+ mVold->bindMountSuccess();
+ mDataLoaderManager->initializeDataLoaderSuccess();
+ mDataLoaderManager->getDataLoaderSuccess();
+ EXPECT_CALL(*mDataLoaderManager, initializeDataLoader(_, _, _, _, _)).Times(1);
+ EXPECT_CALL(*mDataLoaderManager, destroyDataLoader(_)).Times(1);
+ EXPECT_CALL(*mDataLoader, create(_, _, _, _)).Times(1);
+ EXPECT_CALL(*mDataLoader, start(_)).Times(1);
+ EXPECT_CALL(*mDataLoader, destroy(_)).Times(1);
EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
TemporaryDir tempDir;
int storageId =
mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), {},
IncrementalService::CreateOptions::CreateNew);
ASSERT_GE(storageId, 0);
- mDataLoaderManager->setDataLoaderStatusReady();
ASSERT_TRUE(mIncrementalService->startLoading(storageId));
+ mDataLoaderManager->setDataLoaderStatusCreated();
}
TEST_F(IncrementalServiceTest, testSetIncFsMountOptionsSuccess) {
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index e2a247394a81..2c3e3df46e7c 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -112,6 +112,7 @@ import com.android.server.inputmethod.InputMethodSystemProperty;
import com.android.server.inputmethod.MultiClientInputMethodManagerService;
import com.android.server.integrity.AppIntegrityManagerService;
import com.android.server.lights.LightsService;
+import com.android.server.location.LocationManagerService;
import com.android.server.media.MediaResourceMonitorService;
import com.android.server.media.MediaRouterService;
import com.android.server.media.MediaSessionService;
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index 109c1191523b..270a3b50b934 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -78,6 +78,7 @@
<uses-permission android:name="android.permission.DUMP"/>
<uses-permission android:name="android.permission.READ_DREAM_STATE"/>
<uses-permission android:name="android.permission.WRITE_DREAM_STATE"/>
+ <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
<!-- Uses API introduced in O (26) -->
<uses-sdk android:minSdkVersion="1"
diff --git a/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java b/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java
index 73a191dc78cc..22f8b9c8ae92 100644
--- a/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java
+++ b/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java
@@ -66,7 +66,7 @@ public class AudioDeviceBrokerTest {
mContext = InstrumentationRegistry.getTargetContext();
mMockAudioService = mock(AudioService.class);
- mSpyAudioSystem = spy(AudioSystemAdapter.getAlwaysOkAdapter());
+ mSpyAudioSystem = spy(AudioSystemAdapter.getConfigurableAdapter());
mSpyDevInventory = spy(new AudioDeviceInventory(mSpyAudioSystem));
mAudioDeviceBroker = new AudioDeviceBroker(mContext, mMockAudioService, mSpyDevInventory);
mSpyDevInventory.setDeviceBroker(mAudioDeviceBroker);
diff --git a/services/tests/servicestests/src/com/android/server/audio/AudioServiceTest.java b/services/tests/servicestests/src/com/android/server/audio/AudioServiceTest.java
new file mode 100644
index 000000000000..6185ae6d93f9
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/audio/AudioServiceTest.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.audio;
+
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.os.Looper;
+import android.os.UserHandle;
+import android.util.Log;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.MediumTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Spy;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class AudioServiceTest {
+ private static final String TAG = "AudioServiceTest";
+
+ private static final int MAX_MESSAGE_HANDLING_DELAY_MS = 100;
+
+ private Context mContext;
+ private AudioSystemAdapter mAudioSystem;
+ @Spy private SystemServerAdapter mSpySystemServer;
+ // the class being unit-tested here
+ private AudioService mAudioService;
+
+ private static boolean sLooperPrepared = false;
+
+ @Before
+ public void setUp() throws Exception {
+ if (!sLooperPrepared) {
+ Looper.prepare();
+ sLooperPrepared = true;
+ }
+ mContext = InstrumentationRegistry.getTargetContext();
+ mAudioSystem = AudioSystemAdapter.getConfigurableAdapter();
+ mSpySystemServer = spy(SystemServerAdapter.getNoOpAdapter());
+ mAudioService = new AudioService(mContext, mAudioSystem, mSpySystemServer);
+ }
+
+ /**
+ * Test muting the mic reports the expected value, and the corresponding intent was fired
+ * @throws Exception
+ */
+ @Test
+ public void testMuteMicrophone() throws Exception {
+ Log.i(TAG, "running testMuteMicrophone");
+ Assert.assertNotNull(mAudioService);
+ final AudioSystemAdapter.AudioSystemConfigurableAdapter testAudioSystem =
+ (AudioSystemAdapter.AudioSystemConfigurableAdapter) mAudioSystem;
+ testAudioSystem.configureMuteMicrophoneToFail(false);
+ for (boolean muted : new boolean[] { true, false}) {
+ testAudioSystem.configureIsMicrophoneMuted(!muted);
+ mAudioService.setMicrophoneMute(muted, mContext.getOpPackageName(),
+ UserHandle.getCallingUserId());
+ Assert.assertEquals("mic mute reporting wrong value",
+ muted, mAudioService.isMicrophoneMuted());
+ // verify the intent for mic mute changed is supposed to be fired
+ Thread.sleep(MAX_MESSAGE_HANDLING_DELAY_MS);
+ verify(mSpySystemServer, times(1))
+ .sendMicrophoneMuteChangedIntent();
+ reset(mSpySystemServer);
+ }
+ }
+
+ /**
+ * Test muting the mic with simulated failure reports the expected value, and the corresponding
+ * intent was fired
+ * @throws Exception
+ */
+ @Test
+ public void testMuteMicrophoneWhenFail() throws Exception {
+ Log.i(TAG, "running testMuteMicrophoneWhenFail");
+ Assert.assertNotNull(mAudioService);
+ final AudioSystemAdapter.AudioSystemConfigurableAdapter testAudioSystem =
+ (AudioSystemAdapter.AudioSystemConfigurableAdapter) mAudioSystem;
+ testAudioSystem.configureMuteMicrophoneToFail(true);
+ for (boolean muted : new boolean[] { true, false}) {
+ testAudioSystem.configureIsMicrophoneMuted(!muted);
+ mAudioService.setMicrophoneMute(muted, mContext.getOpPackageName(),
+ UserHandle.getCallingUserId());
+ Assert.assertEquals("mic mute reporting wrong value",
+ !muted, mAudioService.isMicrophoneMuted());
+ // verify the intent for mic mute changed is supposed to be fired
+ Thread.sleep(MAX_MESSAGE_HANDLING_DELAY_MS);
+ verify(mSpySystemServer, times(1))
+ .sendMicrophoneMuteChangedIntent();
+ reset(mSpySystemServer);
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index fe224ce058f4..4faed659f5df 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -4841,6 +4841,33 @@ public class DevicePolicyManagerTest extends DpmTestBase {
assertFalse(dpm.isActivePasswordSufficient());
}
+ public void testIsPasswordSufficientAfterProfileUnification() throws Exception {
+ final int managedProfileUserId = DpmMockContext.CALLER_USER_HANDLE;
+ final int managedProfileAdminUid =
+ UserHandle.getUid(managedProfileUserId, DpmMockContext.SYSTEM_UID);
+ mContext.binder.callingUid = managedProfileAdminUid;
+
+ addManagedProfile(admin1, managedProfileAdminUid, admin1);
+ doReturn(true).when(getServices().lockPatternUtils)
+ .isSeparateProfileChallengeEnabled(managedProfileUserId);
+
+ dpm.setPasswordQuality(admin1, DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC);
+ parentDpm.setPasswordQuality(admin1, DevicePolicyManager.PASSWORD_QUALITY_NUMERIC);
+
+ when(getServices().lockSettingsInternal.getUserPasswordMetrics(UserHandle.USER_SYSTEM))
+ .thenReturn(computeForPassword("1234".getBytes()));
+
+ // Numeric password is compliant with current requirement (QUALITY_NUMERIC set explicitly
+ // on the parent admin)
+ assertTrue(dpm.isPasswordSufficientAfterProfileUnification(UserHandle.USER_SYSTEM,
+ UserHandle.USER_NULL));
+ // Numeric password is not compliant if profile is to be unified: the profile has a
+ // QUALITY_ALPHABETIC policy on itself which will be enforced on the password after
+ // unification.
+ assertFalse(dpm.isPasswordSufficientAfterProfileUnification(UserHandle.USER_SYSTEM,
+ managedProfileUserId));
+ }
+
private void setActivePasswordState(PasswordMetrics passwordMetrics)
throws Exception {
final int userHandle = UserHandle.getUserId(mContext.binder.callingUid);
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java
index 07d7830c9b0f..12b144f2b778 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java
@@ -209,6 +209,26 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests {
}
@Test
+ public void testManagedProfileChallengeUnification_parentUserNoPassword() throws Exception {
+ // Start with a profile with unified challenge, parent user has not password
+ mService.setSeparateProfileChallengeEnabled(MANAGED_PROFILE_USER_ID, false, null);
+ assertEquals(0, mGateKeeperService.getSecureUserId(MANAGED_PROFILE_USER_ID));
+ assertEquals(CREDENTIAL_TYPE_NONE, mService.getCredentialType(MANAGED_PROFILE_USER_ID));
+
+ // Set a separate challenge on the profile
+ assertTrue(mService.setLockCredential(
+ newPassword("12345678"), nonePassword(), MANAGED_PROFILE_USER_ID));
+ assertNotEquals(0, mGateKeeperService.getSecureUserId(MANAGED_PROFILE_USER_ID));
+ assertEquals(CREDENTIAL_TYPE_PASSWORD, mService.getCredentialType(MANAGED_PROFILE_USER_ID));
+
+ // Now unify again, profile should become passwordless again
+ mService.setSeparateProfileChallengeEnabled(MANAGED_PROFILE_USER_ID, false,
+ newPassword("12345678"));
+ assertEquals(0, mGateKeeperService.getSecureUserId(MANAGED_PROFILE_USER_ID));
+ assertEquals(CREDENTIAL_TYPE_NONE, mService.getCredentialType(MANAGED_PROFILE_USER_ID));
+ }
+
+ @Test
public void testSetLockCredential_forPrimaryUser_sendsCredentials() throws Exception {
assertTrue(mService.setLockCredential(
newPassword("password"),
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexoptOptionsTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexoptOptionsTests.java
index 62589ebb92fe..22020ad45666 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DexoptOptionsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexoptOptionsTests.java
@@ -54,6 +54,7 @@ public class DexoptOptionsTests {
assertFalse(opt.isForce());
assertFalse(opt.isDexoptIdleBackgroundJob());
assertFalse(opt.isDexoptInstallWithDexMetadata());
+ assertFalse(opt.isDexoptInstallForRestore());
}
@Test
@@ -67,7 +68,8 @@ public class DexoptOptionsTests {
DexoptOptions.DEXOPT_DOWNGRADE |
DexoptOptions.DEXOPT_AS_SHARED_LIBRARY |
DexoptOptions.DEXOPT_IDLE_BACKGROUND_JOB |
- DexoptOptions.DEXOPT_INSTALL_WITH_DEX_METADATA_FILE;
+ DexoptOptions.DEXOPT_INSTALL_WITH_DEX_METADATA_FILE |
+ DexoptOptions.DEXOPT_FOR_RESTORE;
DexoptOptions opt = new DexoptOptions(mPackageName, mCompilerFilter, flags);
assertEquals(mPackageName, opt.getPackageName());
@@ -82,6 +84,7 @@ public class DexoptOptionsTests {
assertTrue(opt.isDexoptAsSharedLibrary());
assertTrue(opt.isDexoptIdleBackgroundJob());
assertTrue(opt.isDexoptInstallWithDexMetadata());
+ assertTrue(opt.isDexoptInstallForRestore());
}
@Test
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ArchiveTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ArchiveTest.java
new file mode 100644
index 000000000000..c69ef8d93282
--- /dev/null
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ArchiveTest.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.notification;
+
+import static android.os.UserHandle.USER_CURRENT;
+import static android.os.UserHandle.USER_SYSTEM;
+import static android.service.notification.NotificationListenerService.REASON_CANCEL;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.Notification;
+import android.os.UserHandle;
+import android.service.notification.StatusBarNotification;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.UiServiceTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ArchiveTest extends UiServiceTestCase {
+ private static final int SIZE = 5;
+
+ private NotificationManagerService.Archive mArchive;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ mArchive = new NotificationManagerService.Archive(SIZE);
+ mArchive.updateHistoryEnabled(USER_SYSTEM, true);
+ mArchive.updateHistoryEnabled(USER_CURRENT, true);
+ }
+
+ private StatusBarNotification getNotification(String pkg, int id, UserHandle user) {
+ Notification n = new Notification.Builder(getContext(), "test")
+ .setContentTitle("A")
+ .setWhen(1205)
+ .build();
+ return new StatusBarNotification(
+ pkg, pkg, id, null, 0, 0, n, user, null, System.currentTimeMillis());
+ }
+
+
+ @Test
+ public void testRecordAndRead() {
+ List<String> expected = new ArrayList<>();
+ for (int i = 0; i < SIZE; i++) {
+ StatusBarNotification sbn = getNotification("pkg" + i, i,
+ UserHandle.of(i % 2 ==0 ? USER_SYSTEM : USER_CURRENT));
+ expected.add(sbn.getKey());
+ mArchive.record(sbn, REASON_CANCEL);
+ }
+
+ List<StatusBarNotification> actual = Arrays.asList(mArchive.getArray(SIZE, true));
+ assertThat(actual).hasSize(expected.size());
+ for (StatusBarNotification sbn : actual) {
+ assertThat(expected).contains(sbn.getKey());
+ }
+ }
+
+ @Test
+ public void testRecordAndRead_overLimit() {
+ List<String> expected = new ArrayList<>();
+ for (int i = 0; i < (SIZE * 2); i++) {
+ StatusBarNotification sbn = getNotification("pkg" + i, i, UserHandle.of(USER_SYSTEM));
+ mArchive.record(sbn, REASON_CANCEL);
+ if (i >= SIZE) {
+ expected.add(sbn.getKey());
+ }
+ }
+
+ List<StatusBarNotification> actual = Arrays.asList(mArchive.getArray((SIZE * 2), true));
+ assertThat(actual).hasSize(expected.size());
+ for (StatusBarNotification sbn : actual) {
+ assertThat(expected).contains(sbn.getKey());
+ }
+ }
+
+ @Test
+ public void testDoesNotRecordIfHistoryDisabled() {
+ mArchive.updateHistoryEnabled(USER_CURRENT, false);
+ List<String> expected = new ArrayList<>();
+ for (int i = 0; i < SIZE; i++) {
+ StatusBarNotification sbn = getNotification("pkg" + i, i,
+ UserHandle.of(i % 2 ==0 ? USER_SYSTEM : USER_CURRENT));
+ mArchive.record(sbn, REASON_CANCEL);
+ if (i % 2 ==0) {
+ expected.add(sbn.getKey());
+ }
+ }
+
+ List<StatusBarNotification> actual = Arrays.asList(mArchive.getArray(SIZE, true));
+ assertThat(actual).hasSize(expected.size());
+ for (StatusBarNotification sbn : actual) {
+ assertThat(expected).contains(sbn.getKey());
+ }
+ }
+
+ @Test
+ public void testRemovesEntriesWhenHistoryDisabled() {
+ mArchive.updateHistoryEnabled(USER_CURRENT, true);
+ List<String> expected = new ArrayList<>();
+ for (int i = 0; i < SIZE; i++) {
+ StatusBarNotification sbn = getNotification("pkg" + i, i,
+ UserHandle.of(i % 2 ==0 ? USER_SYSTEM : USER_CURRENT));
+ mArchive.record(sbn, REASON_CANCEL);
+ if (i % 2 ==0) {
+ expected.add(sbn.getKey());
+ }
+ }
+ mArchive.updateHistoryEnabled(USER_CURRENT, false);
+
+ List<StatusBarNotification> actual = Arrays.asList(mArchive.getArray(SIZE, true));
+ assertThat(actual).hasSize(expected.size());
+ for (StatusBarNotification sbn : actual) {
+ assertThat(expected).contains(sbn.getKey());
+ }
+ }
+}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
index 7b7470cca85a..28ff9a545513 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
@@ -77,6 +77,7 @@ import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
@@ -96,6 +97,10 @@ public class ManagedServicesTest extends UiServiceTestCase {
UserInfo mZero = new UserInfo(0, "zero", 0);
UserInfo mTen = new UserInfo(10, "ten", 0);
+ private String mDefaultsString;
+ private String mVersionString;
+ private final Set<ComponentName> mDefaults = new ArraySet();
+ private ManagedServices mService;
private static final String SETTING = "setting";
private static final String SECONDARY_SETTING = "secondary_setting";
@@ -106,8 +111,8 @@ public class ManagedServicesTest extends UiServiceTestCase {
private ArrayMap<Integer, String> mExpectedSecondaryComponentNames;
// type : user : list of approved
- private ArrayMap<Integer, ArrayMap<Integer, String>> mExpectedPrimary = new ArrayMap<>();
- private ArrayMap<Integer, ArrayMap<Integer, String>> mExpectedSecondary = new ArrayMap<>();
+ private ArrayMap<Integer, ArrayMap<Integer, String>> mExpectedPrimary;
+ private ArrayMap<Integer, ArrayMap<Integer, String>> mExpectedSecondary;
@Before
public void setUp() throws Exception {
@@ -132,6 +137,9 @@ public class ManagedServicesTest extends UiServiceTestCase {
profileIds.add(12);
when(mUserProfiles.getCurrentProfileIds()).thenReturn(profileIds);
+ mVersionString = "2";
+ mExpectedPrimary = new ArrayMap<>();
+ mExpectedSecondary = new ArrayMap<>();
mExpectedPrimaryPackages = new ArrayMap<>();
mExpectedPrimaryPackages.put(0, "this.is.a.package.name:another.package");
mExpectedPrimaryPackages.put(10, "this.is.another.package");
@@ -155,6 +163,8 @@ public class ManagedServicesTest extends UiServiceTestCase {
"this.is.another.package:component:package");
mExpectedSecondary.put(APPROVAL_BY_PACKAGE, mExpectedSecondaryPackages);
mExpectedSecondary.put(APPROVAL_BY_COMPONENT, mExpectedSecondaryComponentNames);
+ mService = new TestManagedServices(getContext(), mLock, mUserProfiles,
+ mIpm, APPROVAL_BY_COMPONENT);
}
@Test
@@ -1178,9 +1188,99 @@ public class ManagedServicesTest extends UiServiceTestCase {
}
}
+ @Test
+ public void loadDefaults_noVersionNoDefaults() throws Exception {
+ resetComponentsAndPackages();
+ loadXml(mService);
+ assertEquals(mService.getDefaultComponents().size(), 0);
+ }
+
+ @Test
+ public void loadDefaults_noVersionNoDefaultsOneActive() throws Exception {
+ resetComponentsAndPackages();
+ mService.addDefaultComponentOrPackage("package/class");
+ loadXml(mService);
+ assertEquals(1, mService.getDefaultComponents().size());
+ assertTrue(mService.getDefaultComponents()
+ .contains(ComponentName.unflattenFromString("package/class")));
+ }
+
+ @Test
+ public void loadDefaults_noVersionWithDefaults() throws Exception {
+ resetComponentsAndPackages();
+ mDefaults.add(new ComponentName("default", "class"));
+ loadXml(mService);
+ assertEquals(mService.getDefaultComponents(), mDefaults);
+ }
+
+ @Test
+ public void loadDefaults_versionOneWithDefaultsWithActive() throws Exception {
+ resetComponentsAndPackages();
+ mDefaults.add(new ComponentName("default", "class"));
+ mExpectedPrimaryComponentNames.put(0, "package/class");
+ mVersionString = "1";
+ loadXml(mService);
+ assertEquals(mService.getDefaultComponents(),
+ new ArraySet(Arrays.asList(new ComponentName("package", "class"))));
+ }
+
+ @Test
+ public void loadDefaults_versionTwoWithDefaultsWithActive() throws Exception {
+ resetComponentsAndPackages();
+ mDefaults.add(new ComponentName("default", "class"));
+ mDefaultsString = "default/class";
+ mExpectedPrimaryComponentNames.put(0, "package/class");
+ mVersionString = "2";
+ loadXml(mService);
+ assertEquals(1, mService.getDefaultComponents().size());
+ mDefaults.forEach(pkg -> {
+ assertTrue(mService.getDefaultComponents().contains(pkg));
+ });
+ }
+
+ @Test
+ public void loadDefaults_versionOneWithXMLDefaultsWithActive() throws Exception {
+ resetComponentsAndPackages();
+ mDefaults.add(new ComponentName("default", "class"));
+ mDefaultsString = "xml/class";
+ mExpectedPrimaryComponentNames.put(0, "package/class");
+ mVersionString = "1";
+ loadXml(mService);
+ assertEquals(mService.getDefaultComponents(),
+ new ArraySet(Arrays.asList(new ComponentName("xml", "class"))));
+ }
+
+ @Test
+ public void loadDefaults_versionTwoWithXMLDefaultsWithActive() throws Exception {
+ resetComponentsAndPackages();
+ mDefaults.add(new ComponentName("default", "class"));
+ mDefaultsString = "xml/class";
+ mExpectedPrimaryComponentNames.put(0, "package/class");
+ mVersionString = "2";
+ loadXml(mService);
+ assertEquals(mService.getDefaultComponents(),
+ new ArraySet(Arrays.asList(new ComponentName("xml", "class"))));
+ }
+
+ private void resetComponentsAndPackages() {
+ ArrayMap<Integer, ArrayMap<Integer, String>> empty = new ArrayMap(1);
+ ArrayMap<Integer, String> emptyPkgs = new ArrayMap(0);
+ empty.append(mService.mApprovalLevel, emptyPkgs);
+ mExpectedPrimary = empty;
+ mExpectedPrimaryComponentNames = emptyPkgs;
+ mExpectedPrimaryPackages = emptyPkgs;
+ mExpectedSecondary = empty;
+ mExpectedSecondaryComponentNames = emptyPkgs;
+ mExpectedSecondaryPackages = emptyPkgs;
+ }
+
private void loadXml(ManagedServices service) throws Exception {
final StringBuffer xml = new StringBuffer();
- xml.append("<" + service.getConfig().xmlTag + ">\n");
+ String xmlTag = service.getConfig().xmlTag;
+ xml.append("<" + xmlTag
+ + (mDefaultsString != null ? " defaults=\"" + mDefaultsString + "\" " : "")
+ + (mVersionString != null ? " version=\"" + mVersionString + "\" " : "")
+ + ">\n");
for (int userId : mExpectedPrimary.get(service.mApprovalLevel).keySet()) {
xml.append(getXmlEntry(
mExpectedPrimary.get(service.mApprovalLevel).get(userId), userId, true));
@@ -1197,7 +1297,7 @@ public class ManagedServicesTest extends UiServiceTestCase {
+ ManagedServices.ATT_USER_ID + "=\"98\" "
+ ManagedServices.ATT_IS_PRIMARY + "=\"false\" "
+ ManagedServices.ATT_APPROVED_LIST + "=\"98\" />\n");
- xml.append("</" + service.getConfig().xmlTag + ">");
+ xml.append("</" + xmlTag + ">");
XmlPullParser parser = Xml.newPullParser();
parser.setInput(new BufferedInputStream(
@@ -1224,6 +1324,7 @@ public class ManagedServicesTest extends UiServiceTestCase {
private void addExpectedServices(final ManagedServices service, final List<String> packages,
int userId) {
+ ManagedServices.Config config = service.getConfig();
when(mPm.queryIntentServicesAsUser(any(), anyInt(), eq(userId))).
thenAnswer(new Answer<List<ResolveInfo>>() {
@Override
@@ -1233,7 +1334,7 @@ public class ManagedServicesTest extends UiServiceTestCase {
Intent invocationIntent = (Intent) args[0];
if (invocationIntent != null) {
if (invocationIntent.getAction().equals(
- service.getConfig().serviceInterface)
+ config.serviceInterface)
&& packages.contains(invocationIntent.getPackage())) {
List<ResolveInfo> dummyServices = new ArrayList<>();
for (int i = 1; i <= 3; i ++) {
@@ -1431,6 +1532,11 @@ public class ManagedServicesTest extends UiServiceTestCase {
}
@Override
+ protected void loadDefaultsFromConfig() {
+ mDefaultComponents.addAll(mDefaults);
+ }
+
+ @Override
protected String getRequiredPermission() {
return null;
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
index 88186cdb3873..ab4dc476ff20 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
@@ -72,13 +72,13 @@ public class NotificationAssistantsTest extends UiServiceTestCase {
Object mLock = new Object();
+
UserInfo mZero = new UserInfo(0, "zero", 0);
UserInfo mTen = new UserInfo(10, "ten", 0);
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
-
getContext().setMockPackageManager(mPm);
getContext().addMockSystemService(Context.USER_SERVICE, mUm);
mAssistants = spy(mNm.new NotificationAssistants(getContext(), mLock, mUserProfiles, miPm));
@@ -122,7 +122,7 @@ public class NotificationAssistantsTest extends UiServiceTestCase {
@Test
public void testXmlUpgradeExistingApprovedComponents() throws Exception {
- String xml = "<enabled_assistants>"
+ String xml = "<enabled_assistants version=\"2\" defaults=\"b\\b\">"
+ "<service_listing approved=\"b/b\" user=\"10\" primary=\"true\" />"
+ "</enabled_assistants>";
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 3cd0e92964ec..ecdd9e548e6a 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -113,6 +113,7 @@ import android.content.pm.LauncherApps;
import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
import android.content.pm.ShortcutInfo;
+import android.content.pm.ShortcutServiceInternal;
import android.content.pm.UserInfo;
import android.content.res.Resources;
import android.graphics.Color;
@@ -234,6 +235,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
@Mock
private LauncherApps mLauncherApps;
@Mock
+ private ShortcutServiceInternal mShortcutServiceInternal;
+ @Mock
ActivityManager mActivityManager;
@Mock
Resources mResources;
@@ -466,6 +469,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
mShortcutHelper = mService.getShortcutHelper();
mShortcutHelper.setLauncherApps(mLauncherApps);
+ mShortcutHelper.setShortcutServiceInternal(mShortcutServiceInternal);
// Set the testable bubble extractor
RankingHelper rankingHelper = mService.getRankingHelper();
@@ -782,33 +786,16 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
userInfos.add(new UserInfo(0, "", 0));
final ArraySet<ComponentName> validAssistants = new ArraySet<>();
validAssistants.add(ComponentName.unflattenFromString(testComponent));
- final String originalComponent = DeviceConfig.getProperty(
- DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.NAS_DEFAULT_SERVICE
- );
- DeviceConfig.setProperty(
- DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.NAS_DEFAULT_SERVICE,
- testComponent,
- false
- );
when(mActivityManager.isLowRamDevice()).thenReturn(false);
when(mAssistants.queryPackageForServices(isNull(), anyInt(), anyInt()))
.thenReturn(validAssistants);
- when(mAssistants.getDefaultComponents()).thenReturn(new ArraySet<>());
+ when(mAssistants.getDefaultComponents()).thenReturn(validAssistants);
when(mUm.getEnabledProfiles(anyInt())).thenReturn(userInfos);
mService.setDefaultAssistantForUser(userId);
verify(mAssistants).setPackageOrComponentEnabled(
eq(testComponent), eq(userId), eq(true), eq(true));
-
- DeviceConfig.setProperty(
- DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.NAS_DEFAULT_SERVICE,
- originalComponent,
- false
- );
}
@Test
@@ -6088,8 +6075,11 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
List<ShortcutInfo> shortcutInfos = new ArrayList<>();
ShortcutInfo info = mock(ShortcutInfo.class);
when(info.isLongLived()).thenReturn(true);
+ when(info.isEnabled()).thenReturn(true);
shortcutInfos.add(info);
when(mLauncherApps.getShortcuts(any(), any())).thenReturn(shortcutInfos);
+ when(mShortcutServiceInternal.isSharingShortcut(anyInt(), anyString(), anyString(),
+ anyString(), anyInt(), any())).thenReturn(true);
// Test: Send the bubble notification
mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.getSbn().getTag(),
@@ -6148,8 +6138,11 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
List<ShortcutInfo> shortcutInfos = new ArrayList<>();
ShortcutInfo info = mock(ShortcutInfo.class);
when(info.isLongLived()).thenReturn(true);
+ when(info.isEnabled()).thenReturn(true);
shortcutInfos.add(info);
when(mLauncherApps.getShortcuts(any(), any())).thenReturn(shortcutInfos);
+ when(mShortcutServiceInternal.isSharingShortcut(anyInt(), anyString(), anyString(),
+ anyString(), anyInt(), any())).thenReturn(true);
// Test: Send the bubble notification
mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.getSbn().getTag(),
@@ -6289,7 +6282,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
mService.loadDefaultApprovedServices(USER_SYSTEM);
- verify(mConditionProviders, times(1)).addDefaultComponentOrPackage("test");
+ verify(mConditionProviders, times(1)).loadDefaultsFromConfig();
}
// TODO: add tests for the rest of the non-empty cases
@@ -6492,7 +6485,10 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
ShortcutInfo si = mock(ShortcutInfo.class);
when(si.getShortLabel()).thenReturn("Hello");
when(si.isLongLived()).thenReturn(true);
+ when(si.isEnabled()).thenReturn(true);
when(mLauncherApps.getShortcuts(any(), any())).thenReturn(Arrays.asList(si));
+ when(mShortcutServiceInternal.isSharingShortcut(anyInt(), anyString(), anyString(),
+ anyString(), anyInt(), any())).thenReturn(true);
List<ConversationChannelWrapper> conversations =
mBinderService.getConversationsForPackage(PKG_P, mUid).getList();
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index 427237c4be0f..ac51750f23f8 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -15,6 +15,9 @@
*/
package com.android.server.notification;
+import static android.app.AppOpsManager.MODE_ALLOWED;
+import static android.app.AppOpsManager.MODE_DEFAULT;
+import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
import static android.app.NotificationChannel.CONVERSATION_CHANNEL_ID_FORMAT;
import static android.app.NotificationManager.BUBBLE_PREFERENCE_ALL;
import static android.app.NotificationManager.BUBBLE_PREFERENCE_NONE;
@@ -50,6 +53,7 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.app.AppOpsManager;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationChannelGroup;
@@ -132,6 +136,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
@Spy IContentProvider mTestIContentProvider = new MockIContentProvider();
@Mock Context mContext;
@Mock ZenModeHelper mMockZenModeHelper;
+ @Mock AppOpsManager mAppOpsManager;
private NotificationManager.Policy mTestNotificationPolicy;
@@ -187,7 +192,10 @@ public class PreferencesHelperTest extends UiServiceTestCase {
mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0,
NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND, 0);
when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy);
- mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger);
+ when(mAppOpsManager.noteOpNoThrow(anyInt(), anyInt(),
+ anyString(), eq(null), anyString())).thenReturn(MODE_DEFAULT);
+ mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger,
+ mAppOpsManager);
resetZenModeHelper();
mAudioAttributes = new AudioAttributes.Builder()
@@ -1464,7 +1472,8 @@ public class PreferencesHelperTest extends UiServiceTestCase {
mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0,
NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND, 0);
when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy);
- mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger);
+ mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger,
+ mAppOpsManager);
assertFalse(mHelper.areChannelsBypassingDnd());
verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any());
resetZenModeHelper();
@@ -1475,7 +1484,8 @@ public class PreferencesHelperTest extends UiServiceTestCase {
// start notification policy off with mAreChannelsBypassingDnd = false
mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0, 0, 0);
when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy);
- mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger);
+ mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger,
+ mAppOpsManager);
assertFalse(mHelper.areChannelsBypassingDnd());
verify(mMockZenModeHelper, never()).setNotificationPolicy(any());
resetZenModeHelper();
@@ -2241,7 +2251,8 @@ public class PreferencesHelperTest extends UiServiceTestCase {
+ "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n"
+ "</package>\n"
+ "</ranking>\n";
- mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger);
+ mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger,
+ mAppOpsManager);
loadByteArrayXml(preQXml.getBytes(), true, UserHandle.USER_SYSTEM);
assertEquals(PreferencesHelper.DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS,
@@ -2253,7 +2264,8 @@ public class PreferencesHelperTest extends UiServiceTestCase {
mHelper.setHideSilentStatusIcons(!PreferencesHelper.DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS);
ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL);
- mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger);
+ mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger,
+ mAppOpsManager);
loadStreamXml(baos, false, UserHandle.USER_ALL);
assertEquals(!PreferencesHelper.DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS,
@@ -2349,7 +2361,8 @@ public class PreferencesHelperTest extends UiServiceTestCase {
mHelper.setImportance(PKG_O, UID_O, IMPORTANCE_UNSPECIFIED);
ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL);
- mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger);
+ mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger,
+ mAppOpsManager);
loadStreamXml(baos, false, UserHandle.USER_ALL);
assertNull(mHelper.getNotificationDelegate(PKG_O, UID_O));
@@ -2360,7 +2373,8 @@ public class PreferencesHelperTest extends UiServiceTestCase {
mHelper.setNotificationDelegate(PKG_O, UID_O, "other", 53);
ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL);
- mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger);
+ mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger,
+ mAppOpsManager);
loadStreamXml(baos, false, UserHandle.USER_ALL);
assertEquals("other", mHelper.getNotificationDelegate(PKG_O, UID_O));
@@ -2372,7 +2386,8 @@ public class PreferencesHelperTest extends UiServiceTestCase {
mHelper.revokeNotificationDelegate(PKG_O, UID_O);
ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL);
- mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger);
+ mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger,
+ mAppOpsManager);
loadStreamXml(baos, false, UserHandle.USER_ALL);
assertNull(mHelper.getNotificationDelegate(PKG_O, UID_O));
@@ -2384,7 +2399,8 @@ public class PreferencesHelperTest extends UiServiceTestCase {
mHelper.toggleNotificationDelegate(PKG_O, UID_O, false);
ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL);
- mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger);
+ mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger,
+ mAppOpsManager);
loadStreamXml(baos, false, UserHandle.USER_ALL);
// appears disabled
@@ -2402,7 +2418,8 @@ public class PreferencesHelperTest extends UiServiceTestCase {
mHelper.revokeNotificationDelegate(PKG_O, UID_O);
ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL);
- mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger);
+ mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger,
+ mAppOpsManager);
loadStreamXml(baos, false, UserHandle.USER_ALL);
// appears disabled
@@ -2417,14 +2434,71 @@ public class PreferencesHelperTest extends UiServiceTestCase {
@Test
public void testBubblePreference_defaults() throws Exception {
- assertEquals(mHelper.getBubblePreference(PKG_O, UID_O), BUBBLE_PREFERENCE_NONE);
+ assertEquals(BUBBLE_PREFERENCE_NONE, mHelper.getBubblePreference(PKG_O, UID_O));
ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL);
- mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger);
+ mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger,
+ mAppOpsManager);
loadStreamXml(baos, false, UserHandle.USER_ALL);
- assertEquals(mHelper.getBubblePreference(PKG_O, UID_O), BUBBLE_PREFERENCE_NONE);
+ assertEquals(BUBBLE_PREFERENCE_NONE, mHelper.getBubblePreference(PKG_O, UID_O));
+ assertEquals(0, mHelper.getAppLockedFields(PKG_O, UID_O));
+ }
+
+ @Test
+ public void testBubblePreference_upgradeWithSAWPermission() throws Exception {
+ when(mAppOpsManager.noteOpNoThrow(eq(OP_SYSTEM_ALERT_WINDOW), anyInt(),
+ anyString(), eq(null), anyString())).thenReturn(MODE_ALLOWED);
+
+ final String xml = "<ranking version=\"1\">\n"
+ + "<package name=\"" + PKG_O + "\" uid=\"" + UID_O + "\">\n"
+ + "<channel id=\"someId\" name=\"hi\""
+ + " importance=\"3\"/>"
+ + "</package>"
+ + "</ranking>";
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())),
+ null);
+ parser.nextTag();
+ mHelper.readXml(parser, false, UserHandle.USER_ALL);
+
+ assertEquals(BUBBLE_PREFERENCE_ALL, mHelper.getBubblePreference(PKG_O, UID_O));
+ assertEquals(0, mHelper.getAppLockedFields(PKG_O, UID_O));
+ }
+
+ @Test
+ public void testBubblePreference_upgradeWithSAWThenUserOverride() throws Exception {
+ when(mAppOpsManager.noteOpNoThrow(eq(OP_SYSTEM_ALERT_WINDOW), anyInt(),
+ anyString(), eq(null), anyString())).thenReturn(MODE_ALLOWED);
+
+ final String xml = "<ranking version=\"1\">\n"
+ + "<package name=\"" + PKG_O + "\" uid=\"" + UID_O + "\">\n"
+ + "<channel id=\"someId\" name=\"hi\""
+ + " importance=\"3\"/>"
+ + "</package>"
+ + "</ranking>";
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())),
+ null);
+ parser.nextTag();
+ mHelper.readXml(parser, false, UserHandle.USER_ALL);
+
+ assertEquals(BUBBLE_PREFERENCE_ALL, mHelper.getBubblePreference(PKG_O, UID_O));
assertEquals(0, mHelper.getAppLockedFields(PKG_O, UID_O));
+
+ mHelper.setBubblesAllowed(PKG_O, UID_O, BUBBLE_PREFERENCE_SELECTED);
+ assertEquals(BUBBLE_PREFERENCE_SELECTED, mHelper.getBubblePreference(PKG_O, UID_O));
+ assertEquals(PreferencesHelper.LockableAppFields.USER_LOCKED_BUBBLE,
+ mHelper.getAppLockedFields(PKG_O, UID_O));
+
+ ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL);
+ mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger,
+ mAppOpsManager);
+ loadStreamXml(baos, false, UserHandle.USER_ALL);
+
+ assertEquals(BUBBLE_PREFERENCE_SELECTED, mHelper.getBubblePreference(PKG_O, UID_O));
+ assertEquals(PreferencesHelper.LockableAppFields.USER_LOCKED_BUBBLE,
+ mHelper.getAppLockedFields(PKG_O, UID_O));
}
@Test
@@ -2435,7 +2509,8 @@ public class PreferencesHelperTest extends UiServiceTestCase {
mHelper.getAppLockedFields(PKG_O, UID_O));
ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL);
- mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger);
+ mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger,
+ mAppOpsManager);
loadStreamXml(baos, false, UserHandle.USER_ALL);
assertEquals(mHelper.getBubblePreference(PKG_O, UID_O), BUBBLE_PREFERENCE_NONE);
@@ -2949,7 +3024,8 @@ public class PreferencesHelperTest extends UiServiceTestCase {
Settings.Global.putInt(mContext.getContentResolver(),
Settings.Global.REQUIRE_SHORTCUTS_FOR_CONVERSATIONS, 0);
- mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger);
+ mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger,
+ mAppOpsManager);
final String xml = "<ranking version=\"1\">\n"
+ "<package name=\"" + PKG_O + "\" uid=\"" + UID_O + "\" >\n"
@@ -2969,7 +3045,8 @@ public class PreferencesHelperTest extends UiServiceTestCase {
public void testPlaceholderConversationId_shortcutRequired() throws Exception {
Settings.Global.putInt(mContext.getContentResolver(),
Settings.Global.REQUIRE_SHORTCUTS_FOR_CONVERSATIONS, 1);
- mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger);
+ mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger,
+ mAppOpsManager);
final String xml = "<ranking version=\"1\">\n"
+ "<package name=\"" + PKG_O + "\" uid=\"" + UID_O + "\" >\n"
@@ -2989,7 +3066,8 @@ public class PreferencesHelperTest extends UiServiceTestCase {
public void testNormalConversationId_shortcutRequired() throws Exception {
Settings.Global.putInt(mContext.getContentResolver(),
Settings.Global.REQUIRE_SHORTCUTS_FOR_CONVERSATIONS, 1);
- mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger);
+ mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger,
+ mAppOpsManager);
final String xml = "<ranking version=\"1\">\n"
+ "<package name=\"" + PKG_O + "\" uid=\"" + UID_O + "\" >\n"
@@ -3009,7 +3087,8 @@ public class PreferencesHelperTest extends UiServiceTestCase {
public void testNoConversationId_shortcutRequired() throws Exception {
Settings.Global.putInt(mContext.getContentResolver(),
Settings.Global.REQUIRE_SHORTCUTS_FOR_CONVERSATIONS, 1);
- mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger);
+ mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger,
+ mAppOpsManager);
final String xml = "<ranking version=\"1\">\n"
+ "<package name=\"" + PKG_O + "\" uid=\"" + UID_O + "\" >\n"
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ShortcutHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ShortcutHelperTest.java
index 50fb9b425652..f7304bd0075b 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ShortcutHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ShortcutHelperTest.java
@@ -16,7 +16,11 @@
package com.android.server.notification;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -25,6 +29,7 @@ import static org.mockito.Mockito.when;
import android.app.Notification;
import android.content.pm.LauncherApps;
import android.content.pm.ShortcutInfo;
+import android.content.pm.ShortcutServiceInternal;
import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
import android.test.suitebuilder.annotation.SmallTest;
@@ -58,6 +63,8 @@ public class ShortcutHelperTest extends UiServiceTestCase {
@Mock
ShortcutHelper.ShortcutListener mShortcutListener;
@Mock
+ ShortcutServiceInternal mShortcutServiceInternal;
+ @Mock
NotificationRecord mNr;
@Mock
Notification mNotif;
@@ -72,7 +79,8 @@ public class ShortcutHelperTest extends UiServiceTestCase {
public void setUp() {
MockitoAnnotations.initMocks(this);
- mShortcutHelper = new ShortcutHelper(mLauncherApps, mShortcutListener);
+ mShortcutHelper = new ShortcutHelper(
+ mLauncherApps, mShortcutListener, mShortcutServiceInternal);
when(mNr.getKey()).thenReturn(KEY);
when(mNr.getSbn()).thenReturn(mSbn);
when(mSbn.getPackageName()).thenReturn(PKG);
@@ -138,4 +146,80 @@ public class ShortcutHelperTest extends UiServiceTestCase {
callback.onShortcutsChanged(PKG, shortcutInfos, mock(UserHandle.class));
verify(mShortcutListener).onShortcutRemoved(mNr.getKey());
}
+
+ @Test
+ public void testGetValidShortcutInfo_noMatchingShortcut() {
+ when(mLauncherApps.getShortcuts(any(), any())).thenReturn(null);
+ when(mShortcutServiceInternal.isSharingShortcut(anyInt(), anyString(), anyString(),
+ anyString(), anyInt(), any())).thenReturn(true);
+
+ assertThat(mShortcutHelper.getValidShortcutInfo("a", "p", UserHandle.SYSTEM)).isNull();
+ }
+
+ @Test
+ public void testGetValidShortcutInfo_nullShortcut() {
+ ArrayList<ShortcutInfo> shortcuts = new ArrayList<>();
+ shortcuts.add(null);
+ when(mLauncherApps.getShortcuts(any(), any())).thenReturn(shortcuts);
+ when(mShortcutServiceInternal.isSharingShortcut(anyInt(), anyString(), anyString(),
+ anyString(), anyInt(), any())).thenReturn(true);
+
+ assertThat(mShortcutHelper.getValidShortcutInfo("a", "p", UserHandle.SYSTEM)).isNull();
+ }
+
+ @Test
+ public void testGetValidShortcutInfo_notLongLived() {
+ ShortcutInfo si = mock(ShortcutInfo.class);
+ when(si.isLongLived()).thenReturn(false);
+ when(si.isEnabled()).thenReturn(true);
+ ArrayList<ShortcutInfo> shortcuts = new ArrayList<>();
+ shortcuts.add(si);
+ when(mLauncherApps.getShortcuts(any(), any())).thenReturn(shortcuts);
+ when(mShortcutServiceInternal.isSharingShortcut(anyInt(), anyString(), anyString(),
+ anyString(), anyInt(), any())).thenReturn(true);
+
+ assertThat(mShortcutHelper.getValidShortcutInfo("a", "p", UserHandle.SYSTEM)).isNull();
+ }
+
+ @Test
+ public void testGetValidShortcutInfo_notSharingShortcut() {
+ ShortcutInfo si = mock(ShortcutInfo.class);
+ when(si.isLongLived()).thenReturn(true);
+ when(si.isEnabled()).thenReturn(true);
+ ArrayList<ShortcutInfo> shortcuts = new ArrayList<>();
+ shortcuts.add(si);
+ when(mLauncherApps.getShortcuts(any(), any())).thenReturn(shortcuts);
+ when(mShortcutServiceInternal.isSharingShortcut(anyInt(), anyString(), anyString(),
+ anyString(), anyInt(), any())).thenReturn(false);
+
+ assertThat(mShortcutHelper.getValidShortcutInfo("a", "p", UserHandle.SYSTEM)).isNull();
+ }
+
+ @Test
+ public void testGetValidShortcutInfo_notEnabled() {
+ ShortcutInfo si = mock(ShortcutInfo.class);
+ when(si.isLongLived()).thenReturn(true);
+ when(si.isEnabled()).thenReturn(false);
+ ArrayList<ShortcutInfo> shortcuts = new ArrayList<>();
+ shortcuts.add(si);
+ when(mLauncherApps.getShortcuts(any(), any())).thenReturn(shortcuts);
+ when(mShortcutServiceInternal.isSharingShortcut(anyInt(), anyString(), anyString(),
+ anyString(), anyInt(), any())).thenReturn(true);
+
+ assertThat(mShortcutHelper.getValidShortcutInfo("a", "p", UserHandle.SYSTEM)).isNull();
+ }
+
+ @Test
+ public void testGetValidShortcutInfo_isValid() {
+ ShortcutInfo si = mock(ShortcutInfo.class);
+ when(si.isLongLived()).thenReturn(true);
+ when(si.isEnabled()).thenReturn(true);
+ ArrayList<ShortcutInfo> shortcuts = new ArrayList<>();
+ shortcuts.add(si);
+ when(mLauncherApps.getShortcuts(any(), any())).thenReturn(shortcuts);
+ when(mShortcutServiceInternal.isSharingShortcut(anyInt(), anyString(), anyString(),
+ anyString(), anyInt(), any())).thenReturn(true);
+
+ assertThat(mShortcutHelper.getValidShortcutInfo("a", "p", UserHandle.SYSTEM)).isSameAs(si);
+ }
}
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 bc66fa7ff48d..5bbb4d9e712c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -1324,7 +1324,7 @@ public class ActivityRecordTests extends ActivityTestsBase {
display.rotateInDifferentOrientationIfNeeded(mActivity);
display.mFixedRotationLaunchingApp = mActivity;
- displayRotation.updateRotationUnchecked(false /* forceUpdate */);
+ displayRotation.updateRotationUnchecked(true /* forceUpdate */);
assertTrue(displayRotation.isRotatingSeamlessly());
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
index 4532400e3b34..17dd26ed18e9 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
@@ -89,13 +89,13 @@ public class ActivityTaskManagerServiceTests extends ActivityTestsBase {
public void testOnPictureInPictureRequested() throws RemoteException {
final ActivityStack stack = new StackBuilder(mRootWindowContainer).build();
final ActivityRecord activity = stack.getBottomMostTask().getTopNonFinishingActivity();
- ClientLifecycleManager lifecycleManager = mService.getLifecycleManager();
- doNothing().when(lifecycleManager).scheduleTransaction(any());
+ final ClientLifecycleManager mockLifecycleManager = mock(ClientLifecycleManager.class);
+ doReturn(mockLifecycleManager).when(mService).getLifecycleManager();
doReturn(true).when(activity).checkEnterPictureInPictureState(anyString(), anyBoolean());
mService.requestPictureInPictureMode(activity.token);
- verify(lifecycleManager).scheduleTransaction(mClientTransactionCaptor.capture());
+ verify(mockLifecycleManager).scheduleTransaction(mClientTransactionCaptor.capture());
final ClientTransaction transaction = mClientTransactionCaptor.getValue();
// Check that only an enter pip request item callback was scheduled.
assertEquals(1, transaction.getCallbacks().size());
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index a901d1ebd890..daff14992e94 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -65,6 +65,7 @@ import static com.google.common.truth.Truth.assertThat;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
@@ -84,12 +85,16 @@ import android.platform.test.annotations.Presubmit;
import android.util.DisplayMetrics;
import android.view.DisplayCutout;
import android.view.Gravity;
+import android.view.IDisplayWindowInsetsController;
import android.view.IDisplayWindowRotationCallback;
import android.view.IDisplayWindowRotationController;
import android.view.ISystemGestureExclusionListener;
import android.view.IWindowManager;
+import android.view.InsetsSourceControl;
+import android.view.InsetsState;
import android.view.MotionEvent;
import android.view.Surface;
+import android.view.SurfaceControl.Transaction;
import android.view.ViewRootImpl;
import android.view.WindowManager;
import android.view.test.InsetsModeSession;
@@ -809,25 +814,19 @@ public class DisplayContentTests extends WindowTestsBase {
@Test
public void testComputeImeParent_app() throws Exception {
- try (final InsetsModeSession session =
- new InsetsModeSession(ViewRootImpl.NEW_INSETS_MODE_IME)) {
- final DisplayContent dc = createNewDisplay();
- dc.mInputMethodTarget = createWindow(null, TYPE_BASE_APPLICATION, "app");
- assertEquals(dc.mInputMethodTarget.mActivityRecord.getSurfaceControl(),
- dc.computeImeParent());
- }
+ final DisplayContent dc = createNewDisplay();
+ dc.mInputMethodTarget = createWindow(null, TYPE_BASE_APPLICATION, "app");
+ assertEquals(dc.mInputMethodTarget.mActivityRecord.getSurfaceControl(),
+ dc.computeImeParent());
}
@Test
public void testComputeImeParent_app_notFullscreen() throws Exception {
- try (final InsetsModeSession session =
- new InsetsModeSession(ViewRootImpl.NEW_INSETS_MODE_IME)) {
- final DisplayContent dc = createNewDisplay();
- dc.mInputMethodTarget = createWindow(null, TYPE_STATUS_BAR, "app");
- dc.mInputMethodTarget.setWindowingMode(
- WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
- assertEquals(dc.getWindowingLayer(), dc.computeImeParent());
- }
+ final DisplayContent dc = createNewDisplay();
+ dc.mInputMethodTarget = createWindow(null, TYPE_STATUS_BAR, "app");
+ dc.mInputMethodTarget.setWindowingMode(
+ WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+ assertEquals(dc.getImeContainer().getParentSurfaceControl(), dc.computeImeParent());
}
@Test
@@ -836,17 +835,67 @@ public class DisplayContentTests extends WindowTestsBase {
doReturn(false).when(mAppWindow.mActivityRecord).matchParentBounds();
mDisplayContent.mInputMethodTarget = mAppWindow;
// The surface parent of IME should be the display instead of app window.
- assertEquals(mDisplayContent.getWindowingLayer(), mDisplayContent.computeImeParent());
+ assertEquals(mDisplayContent.getImeContainer().getParentSurfaceControl(),
+ mDisplayContent.computeImeParent());
}
@Test
public void testComputeImeParent_noApp() throws Exception {
- try (final InsetsModeSession session =
- new InsetsModeSession(ViewRootImpl.NEW_INSETS_MODE_IME)) {
- final DisplayContent dc = createNewDisplay();
- dc.mInputMethodTarget = createWindow(null, TYPE_STATUS_BAR, "statusBar");
- assertEquals(dc.getWindowingLayer(), dc.computeImeParent());
- }
+ final DisplayContent dc = createNewDisplay();
+ dc.mInputMethodTarget = createWindow(null, TYPE_STATUS_BAR, "statusBar");
+ assertEquals(dc.getImeContainer().getParentSurfaceControl(), dc.computeImeParent());
+ }
+
+ @Test
+ public void testComputeImeControlTarget() throws Exception {
+ final DisplayContent dc = createNewDisplay();
+ dc.setRemoteInsetsController(createDisplayWindowInsetsController());
+ dc.mInputMethodInputTarget = createWindow(null, TYPE_BASE_APPLICATION, "app");
+ dc.mInputMethodTarget = dc.mInputMethodInputTarget;
+ assertEquals(dc.mInputMethodInputTarget, dc.computeImeControlTarget());
+ }
+
+ @Test
+ public void testComputeImeControlTarget_splitscreen() throws Exception {
+ final DisplayContent dc = createNewDisplay();
+ dc.mInputMethodInputTarget = createWindow(null, TYPE_BASE_APPLICATION, "app");
+ dc.mInputMethodInputTarget.setWindowingMode(
+ WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+ dc.mInputMethodTarget = dc.mInputMethodInputTarget;
+ dc.setRemoteInsetsController(createDisplayWindowInsetsController());
+ assertNotEquals(dc.mInputMethodInputTarget, dc.computeImeControlTarget());
+ }
+
+ @Test
+ public void testComputeImeControlTarget_notMatchParentBounds() throws Exception {
+ spyOn(mAppWindow.mActivityRecord);
+ doReturn(false).when(mAppWindow.mActivityRecord).matchParentBounds();
+ mDisplayContent.mInputMethodInputTarget = mAppWindow;
+ mDisplayContent.mInputMethodTarget = mDisplayContent.mInputMethodInputTarget;
+ mDisplayContent.setRemoteInsetsController(createDisplayWindowInsetsController());
+ assertEquals(mAppWindow, mDisplayContent.computeImeControlTarget());
+ }
+
+ private IDisplayWindowInsetsController createDisplayWindowInsetsController() {
+ return new IDisplayWindowInsetsController.Stub() {
+
+ @Override
+ public void insetsChanged(InsetsState insetsState) throws RemoteException {
+ }
+
+ @Override
+ public void insetsControlChanged(InsetsState insetsState,
+ InsetsSourceControl[] insetsSourceControls) throws RemoteException {
+ }
+
+ @Override
+ public void showInsets(int i, boolean b) throws RemoteException {
+ }
+
+ @Override
+ public void hideInsets(int i, boolean b) throws RemoteException {
+ }
+ };
}
@Test
@@ -1039,6 +1088,12 @@ public class DisplayContentTests extends WindowTestsBase {
assertEquals(config90.orientation, app.getConfiguration().orientation);
assertEquals(config90.windowConfiguration.getBounds(), app.getBounds());
+ // Make wallaper laid out with the fixed rotation transform.
+ final WindowToken wallpaperToken = mWallpaperWindow.mToken;
+ wallpaperToken.linkFixedRotationTransform(app);
+ mWallpaperWindow.mLayoutNeeded = true;
+ performLayout(mDisplayContent);
+
// Force the negative offset to verify it can be updated.
mWallpaperWindow.mWinAnimator.mXOffset = mWallpaperWindow.mWinAnimator.mYOffset = -1;
assertTrue(mDisplayContent.mWallpaperController.updateWallpaperOffset(mWallpaperWindow,
@@ -1046,6 +1101,13 @@ public class DisplayContentTests extends WindowTestsBase {
assertThat(mWallpaperWindow.mWinAnimator.mXOffset).isGreaterThan(-1);
assertThat(mWallpaperWindow.mWinAnimator.mYOffset).isGreaterThan(-1);
+ // The wallpaper need to animate with transformed position, so its surface position should
+ // not be reset.
+ final Transaction t = wallpaperToken.getPendingTransaction();
+ spyOn(t);
+ mWallpaperWindow.mToken.onAnimationLeashCreated(t, null /* leash */);
+ verify(t, never()).setPosition(any(), eq(0), eq(0));
+
mDisplayContent.mAppTransition.notifyAppTransitionFinishedLocked(app.token);
// The animation in old rotation should be cancelled.
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
index ec77be85aebf..473c1c57d625 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
@@ -145,9 +145,5 @@ public class TaskTests extends WindowTestsBase {
Rect bounds = new Rect(10, 10, 100, 200);
task.setBounds(bounds);
assertEquals(new Point(bounds.left, bounds.top), task.getLastSurfacePosition());
-
- Rect dispBounds = new Rect(20, 30, 110, 220);
- task.setOverrideDisplayedBounds(dispBounds);
- assertEquals(new Point(dispBounds.left, dispBounds.top), task.getLastSurfacePosition());
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java
index 7be05a39cbde..eb2aa41192c2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java
@@ -310,27 +310,6 @@ public class WindowFrameTests extends WindowTestsBase {
assertContentFrame(w, new Rect(resolvedTaskBounds.left, resolvedTaskBounds.top,
resolvedTaskBounds.right - contentInsetRight,
resolvedTaskBounds.bottom - contentInsetBottom));
-
- pf.set(0, 0, logicalWidth, logicalHeight);
- // If we set displayed bounds, the insets will be computed with the main task bounds
- // but the frame will be positioned according to the displayed bounds.
- final int insetLeft = logicalWidth / 5;
- final int insetTop = logicalHeight / 5;
- final int insetRight = insetLeft + (resolvedTaskBounds.right - resolvedTaskBounds.left);
- final int insetBottom = insetTop + (resolvedTaskBounds.bottom - resolvedTaskBounds.top);
- task.setOverrideDisplayedBounds(resolvedTaskBounds);
- task.setBounds(insetLeft, insetTop, insetRight, insetBottom);
- windowFrames.setFrames(pf, pf, cf, cf, pf, cf);
- w.computeFrameLw();
- assertEquals(resolvedTaskBounds, w.getFrameLw());
- assertEquals(0, w.getRelativeFrameLw().left);
- assertEquals(0, w.getRelativeFrameLw().top);
- contentInsetRight = insetRight - cfRight;
- contentInsetBottom = insetBottom - cfBottom;
- assertContentInset(w, 0, 0, contentInsetRight, contentInsetBottom);
- assertContentFrame(w, new Rect(resolvedTaskBounds.left, resolvedTaskBounds.top,
- resolvedTaskBounds.right - contentInsetRight,
- resolvedTaskBounds.bottom - contentInsetBottom));
}
@Test
@@ -460,33 +439,6 @@ public class WindowFrameTests extends WindowTestsBase {
}
@Test
- @FlakyTest(bugId = 130388666)
- public void testDisplayCutout_tempDisplayedBounds() {
- // Regular fullscreen task and window
- WindowState w = createWindow();
- final Task task = w.getTask();
- task.setBounds(new Rect(0, 0, 1000, 2000));
- task.setOverrideDisplayedBounds(new Rect(0, -500, 1000, 1500));
- w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP;
-
- final Rect pf = new Rect(0, -500, 1000, 1500);
- // Create a display cutout of size 50x50, aligned top-center
- final WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
- fromBoundingRect(500, 0, 550, 50, BOUNDS_POSITION_TOP),
- pf.width(), pf.height());
-
- final WindowFrames windowFrames = w.getWindowFrames();
- windowFrames.setFrames(pf, pf, pf, pf, pf, pf);
- windowFrames.setDisplayCutout(cutout);
- w.computeFrameLw();
-
- assertEquals(w.getWmDisplayCutout().getDisplayCutout().getSafeInsetTop(), 50);
- assertEquals(w.getWmDisplayCutout().getDisplayCutout().getSafeInsetBottom(), 0);
- assertEquals(w.getWmDisplayCutout().getDisplayCutout().getSafeInsetLeft(), 0);
- assertEquals(w.getWmDisplayCutout().getDisplayCutout().getSafeInsetRight(), 0);
- }
-
- @Test
public void testFreeformContentInsets() {
removeGlobalMinSizeRestriction();
// fullscreen task doesn't use bounds for computeFrame
diff --git a/startop/iorap/functional_tests/Android.bp b/startop/iorap/functional_tests/Android.bp
index ad85f1430bdf..8a5bd34af653 100644
--- a/startop/iorap/functional_tests/Android.bp
+++ b/startop/iorap/functional_tests/Android.bp
@@ -15,7 +15,7 @@
android_test {
name: "iorap-functional-tests",
srcs: ["src/**/*.java"],
- data: ["test_data/*"],
+ data: [":iorap-functional-test-apps"],
static_libs: [
// Non-test dependencies
// library under test
diff --git a/startop/iorap/functional_tests/src/com/google/android/startop/iorap/IorapWorkFlowTest.java b/startop/iorap/functional_tests/src/com/google/android/startop/iorap/IorapWorkFlowTest.java
index c35dd3b783b1..5352be6f283f 100644
--- a/startop/iorap/functional_tests/src/com/google/android/startop/iorap/IorapWorkFlowTest.java
+++ b/startop/iorap/functional_tests/src/com/google/android/startop/iorap/IorapWorkFlowTest.java
@@ -397,7 +397,7 @@ public class IorapWorkFlowTest {
public LogcatTimestamp() throws Exception{
long currentTimeMillis = System.currentTimeMillis();
epochTime = String.format(
- "%d.%d", currentTimeMillis/1000, currentTimeMillis%1000);
+ "%d.%03d", currentTimeMillis/1000, currentTimeMillis%1000);
Log.i(TAG, "Current logcat timestamp is " + epochTime);
}
diff --git a/startop/iorap/functional_tests/test_data/iorap_test_app_v1.apk b/startop/iorap/functional_tests/test_data/iorap_test_app_v1.apk
deleted file mode 120000
index 1c1a437f6a55..000000000000
--- a/startop/iorap/functional_tests/test_data/iorap_test_app_v1.apk
+++ /dev/null
@@ -1 +0,0 @@
-../../../../../../packages/modules/ArtPrebuilt/iorap/test/iorap_test_app_v1.apk \ No newline at end of file
diff --git a/startop/iorap/functional_tests/test_data/iorap_test_app_v2.apk b/startop/iorap/functional_tests/test_data/iorap_test_app_v2.apk
deleted file mode 120000
index 7cd41c48ba3a..000000000000
--- a/startop/iorap/functional_tests/test_data/iorap_test_app_v2.apk
+++ /dev/null
@@ -1 +0,0 @@
-../../../../../../packages/modules/ArtPrebuilt/iorap/test/iorap_test_app_v2.apk \ No newline at end of file
diff --git a/startop/iorap/functional_tests/test_data/iorap_test_app_v3.apk b/startop/iorap/functional_tests/test_data/iorap_test_app_v3.apk
deleted file mode 120000
index 7f4e996e57d0..000000000000
--- a/startop/iorap/functional_tests/test_data/iorap_test_app_v3.apk
+++ /dev/null
@@ -1 +0,0 @@
-../../../../../../packages/modules/ArtPrebuilt/iorap/test/iorap_test_app_v3.apk \ No newline at end of file
diff --git a/telecomm/java/android/telecom/Conference.java b/telecomm/java/android/telecom/Conference.java
index 4e14fd3d59a1..d9605522b3b0 100644
--- a/telecomm/java/android/telecom/Conference.java
+++ b/telecomm/java/android/telecom/Conference.java
@@ -106,6 +106,7 @@ public abstract class Conference extends Conferenceable {
private int mCallerDisplayNamePresentation;
private int mCallDirection;
private boolean mRingbackRequested = false;
+ private boolean mIsMultiparty = true;
private final Connection.Listener mConnectionDeathListener = new Connection.Listener() {
@Override
@@ -998,8 +999,8 @@ public abstract class Conference extends Conferenceable {
public void onExtrasChanged(Bundle extras) {}
/**
- * Set whether Telecom should treat this {@link Conference} as a conference call or if it
- * should treat it as a single-party call.
+ * Set whether Telecom should treat this {@link Conference} as a multiparty conference call or
+ * if it should treat it as a single-party call.
* This method is used as part of a workaround regarding IMS conference calls and user
* expectation. In IMS, once a conference is formed, the UE is connected to an IMS conference
* server. If all participants of the conference drop out of the conference except for one, the
@@ -1020,6 +1021,7 @@ public abstract class Conference extends Conferenceable {
@TestApi
@RequiresPermission(MODIFY_PHONE_STATE)
public void setConferenceState(boolean isConference) {
+ mIsMultiparty = isConference;
for (Listener l : mListeners) {
l.onConferenceStateChanged(this, isConference);
}
@@ -1043,6 +1045,20 @@ public abstract class Conference extends Conferenceable {
}
}
+ /**
+ * Determines if the {@link Conference} is considered "multiparty" or not. By default all
+ * conferences are considered multiparty. A multiparty conference is one where there are
+ * multiple conference participants (other than the host) in the conference.
+ * This is tied to {@link #setConferenceState(boolean)}, which is used for some use cases to
+ * have a conference appear as if it is a standalone call, in which case the conference will
+ * no longer be multiparty.
+ * @return {@code true} if conference is treated as a conference (i.e. it is multiparty),
+ * {@code false} if it should emulate a standalone call (i.e. not multiparty).
+ * @hide
+ */
+ public boolean isMultiparty() {
+ return mIsMultiparty;
+ }
/**
* Sets the address of this {@link Conference}. Used when {@link #setConferenceState(boolean)}
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index 73296986d82e..1b60e4820ad0 100755
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -2505,6 +2505,11 @@ public abstract class ConnectionService extends Service {
mAdapter.addConferenceCall(id, parcelableConference);
mAdapter.setVideoProvider(id, conference.getVideoProvider());
mAdapter.setVideoState(id, conference.getVideoState());
+ // In some instances a conference can start its life as a standalone call with just a
+ // single participant; ensure we signal to Telecom in this case.
+ if (!conference.isMultiparty()) {
+ mAdapter.setConferenceState(id, conference.isMultiparty());
+ }
// Go through any child calls and set the parent.
for (Connection connection : conference.getConnections()) {
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 545c8a35058f..d2de19aaf89a 100755
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -505,6 +505,15 @@ public class CarrierConfigManager {
public static final String KEY_CARRIER_VT_AVAILABLE_BOOL = "carrier_vt_available_bool";
/**
+ * Flag specifying whether to show an alert dialog for 5G disable when the user disables VoLTE.
+ * By default this value is {@code false}.
+ *
+ * @hide
+ */
+ public static final String KEY_VOLTE_5G_LIMITED_ALERT_DIALOG_BOOL =
+ "volte_5g_limited_alert_dialog_bool";
+
+ /**
* Flag specifying whether the carrier wants to notify the user when a VT call has been handed
* over from WIFI to LTE.
* <p>
@@ -3084,6 +3093,16 @@ public class CarrierConfigManager {
public static final String KEY_UNMETERED_NR_NSA_SUB6_BOOL = "unmetered_nr_nsa_sub6_bool";
/**
+ * Whether NR (non-standalone) should be unmetered when the device is roaming.
+ * If false, then the values for {@link #KEY_UNMETERED_NR_NSA_BOOL},
+ * {@link #KEY_UNMETERED_NR_NSA_MMWAVE_BOOL}, {@link #KEY_UNMETERED_NR_NSA_SUB6_BOOL},
+ * and unmetered {@link SubscriptionPlan} will be ignored.
+ * @hide
+ */
+ public static final String KEY_UNMETERED_NR_NSA_WHEN_ROAMING_BOOL =
+ "unmetered_nr_nsa_when_roaming_bool";
+
+ /**
* Whether NR (standalone) should be unmetered for all frequencies.
* If either {@link #KEY_UNMETERED_NR_SA_MMWAVE_BOOL} or
* {@link #KEY_UNMETERED_NR_SA_SUB6_BOOL} are true, then this value will be ignored.
@@ -3703,6 +3722,7 @@ public class CarrierConfigManager {
sDefaults.putBoolean(KEY_CARRIER_SETTINGS_ENABLE_BOOL, false);
sDefaults.putBoolean(KEY_CARRIER_VOLTE_AVAILABLE_BOOL, false);
sDefaults.putBoolean(KEY_CARRIER_VT_AVAILABLE_BOOL, false);
+ sDefaults.putBoolean(KEY_VOLTE_5G_LIMITED_ALERT_DIALOG_BOOL, false);
sDefaults.putBoolean(KEY_NOTIFY_HANDOVER_VIDEO_FROM_WIFI_TO_LTE_BOOL, false);
sDefaults.putBoolean(KEY_ALLOW_MERGING_RTT_CALLS_BOOL, false);
sDefaults.putBoolean(KEY_NOTIFY_HANDOVER_VIDEO_FROM_LTE_TO_WIFI_BOOL, false);
@@ -4134,6 +4154,7 @@ public class CarrierConfigManager {
sDefaults.putBoolean(KEY_UNMETERED_NR_NSA_BOOL, false);
sDefaults.putBoolean(KEY_UNMETERED_NR_NSA_MMWAVE_BOOL, false);
sDefaults.putBoolean(KEY_UNMETERED_NR_NSA_SUB6_BOOL, false);
+ sDefaults.putBoolean(KEY_UNMETERED_NR_NSA_WHEN_ROAMING_BOOL, false);
sDefaults.putBoolean(KEY_UNMETERED_NR_SA_BOOL, false);
sDefaults.putBoolean(KEY_UNMETERED_NR_SA_MMWAVE_BOOL, false);
sDefaults.putBoolean(KEY_UNMETERED_NR_SA_SUB6_BOOL, false);
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index f623649fb25e..835ef59f9ef3 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -13250,4 +13250,21 @@ public class TelephonyManager {
public static void enableServiceHandleCaching() {
sServiceHandleCacheEnabled = true;
}
+
+ /**
+ * Whether device can connect to 5G network when two SIMs are active.
+ * @hide
+ * TODO b/153669716: remove or make system API.
+ */
+ public boolean canConnectTo5GInDsdsMode() {
+ ITelephony telephony = getITelephony();
+ if (telephony == null) return true;
+ try {
+ return telephony.canConnectTo5GInDsdsMode();
+ } catch (RemoteException ex) {
+ return true;
+ } catch (NullPointerException ex) {
+ return true;
+ }
+ }
}
diff --git a/telephony/java/android/telephony/data/DataCallResponse.java b/telephony/java/android/telephony/data/DataCallResponse.java
index a116c07e2646..242c2e979571 100644
--- a/telephony/java/android/telephony/data/DataCallResponse.java
+++ b/telephony/java/android/telephony/data/DataCallResponse.java
@@ -80,7 +80,6 @@ public final class DataCallResponse implements Parcelable {
private final int mMtu;
private final int mMtuV4;
private final int mMtuV6;
- private final int mVersion;
/**
* @param cause Data call fail cause. {@link DataFailCause#NONE} indicates no error.
@@ -126,9 +125,7 @@ public final class DataCallResponse implements Parcelable {
? new ArrayList<>() : new ArrayList<>(gatewayAddresses);
mPcscfAddresses = (pcscfAddresses == null)
? new ArrayList<>() : new ArrayList<>(pcscfAddresses);
- mMtu = mtu;
- mMtuV4 = mMtuV6 = 0;
- mVersion = 0;
+ mMtu = mMtuV4 = mMtuV6 = mtu;
}
/** @hide */
@@ -136,7 +133,7 @@ public final class DataCallResponse implements Parcelable {
@LinkStatus int linkStatus, @ProtocolType int protocolType,
@Nullable String interfaceName, @Nullable List<LinkAddress> addresses,
@Nullable List<InetAddress> dnsAddresses, @Nullable List<InetAddress> gatewayAddresses,
- @Nullable List<InetAddress> pcscfAddresses, int mtuV4, int mtuV6, int version) {
+ @Nullable List<InetAddress> pcscfAddresses, int mtu, int mtuV4, int mtuV6) {
mCause = cause;
mSuggestedRetryTime = suggestedRetryTime;
mId = id;
@@ -151,10 +148,9 @@ public final class DataCallResponse implements Parcelable {
? new ArrayList<>() : new ArrayList<>(gatewayAddresses);
mPcscfAddresses = (pcscfAddresses == null)
? new ArrayList<>() : new ArrayList<>(pcscfAddresses);
- mMtu = 0;
+ mMtu = mtu;
mMtuV4 = mtuV4;
mMtuV6 = mtuV6;
- mVersion = version;
}
/** @hide */
@@ -177,7 +173,6 @@ public final class DataCallResponse implements Parcelable {
mMtu = source.readInt();
mMtuV4 = source.readInt();
mMtuV6 = source.readInt();
- mVersion = source.readInt();
}
/**
@@ -247,7 +242,7 @@ public final class DataCallResponse implements Parcelable {
*/
@Deprecated
public int getMtu() {
- return mVersion < 5 ? mMtu : 0;
+ return mMtu;
}
/**
@@ -256,7 +251,7 @@ public final class DataCallResponse implements Parcelable {
* Zero or negative values means network has either not sent a value or sent an invalid value.
*/
public int getMtuV4() {
- return mVersion < 5 ? 0 : mMtuV4;
+ return mMtuV4;
}
/**
@@ -264,7 +259,7 @@ public final class DataCallResponse implements Parcelable {
* Zero or negative values means network has either not sent a value or sent an invalid value.
*/
public int getMtuV6() {
- return mVersion < 5 ? 0 : mMtuV6;
+ return mMtuV6;
}
@NonNull
@@ -282,10 +277,9 @@ public final class DataCallResponse implements Parcelable {
.append(" dnses=").append(mDnsAddresses)
.append(" gateways=").append(mGatewayAddresses)
.append(" pcscf=").append(mPcscfAddresses)
- .append(" mtu=").append(mMtu)
- .append(" mtuV4=").append(mMtuV4)
- .append(" mtuV6=").append(mMtuV6)
- .append(" version=").append(mVersion)
+ .append(" mtu=").append(getMtu())
+ .append(" mtuV4=").append(getMtuV4())
+ .append(" mtuV6=").append(getMtuV6())
.append("}");
return sb.toString();
}
@@ -315,15 +309,14 @@ public final class DataCallResponse implements Parcelable {
&& mPcscfAddresses.containsAll(other.mPcscfAddresses)
&& mMtu == other.mMtu
&& mMtuV4 == other.mMtuV4
- && mMtuV6 == other.mMtuV6
- && mVersion == other.mVersion;
+ && mMtuV6 == other.mMtuV6;
}
@Override
public int hashCode() {
return Objects.hash(mCause, mSuggestedRetryTime, mId, mLinkStatus, mProtocolType,
mInterfaceName, mAddresses, mDnsAddresses, mGatewayAddresses, mPcscfAddresses,
- mMtu, mMtuV4, mMtuV6, mVersion);
+ mMtu, mMtuV4, mMtuV6);
}
@Override
@@ -346,7 +339,6 @@ public final class DataCallResponse implements Parcelable {
dest.writeInt(mMtu);
dest.writeInt(mMtuV4);
dest.writeInt(mMtuV6);
- dest.writeInt(mVersion);
}
public static final @android.annotation.NonNull Parcelable.Creator<DataCallResponse> CREATOR =
@@ -403,8 +395,6 @@ public final class DataCallResponse implements Parcelable {
private int mMtuV6;
- private int mVersion;
-
/**
* Default constructor for Builder.
*/
@@ -563,29 +553,14 @@ public final class DataCallResponse implements Parcelable {
}
/**
- * Set the IRadio version for this DataCallResponse
- * @hide
- */
- public @NonNull Builder setVersion(int version) {
- mVersion = version;
- return this;
- }
-
- /**
* Build the DataCallResponse.
*
* @return the DataCallResponse object.
*/
public @NonNull DataCallResponse build() {
- if (mVersion >= 5) {
- return new DataCallResponse(mCause, mSuggestedRetryTime, mId, mLinkStatus,
- mProtocolType, mInterfaceName, mAddresses, mDnsAddresses, mGatewayAddresses,
- mPcscfAddresses, mMtuV4, mMtuV6, mVersion);
- } else {
- return new DataCallResponse(mCause, mSuggestedRetryTime, mId, mLinkStatus,
- mProtocolType, mInterfaceName, mAddresses, mDnsAddresses, mGatewayAddresses,
- mPcscfAddresses, mMtu);
- }
+ return new DataCallResponse(mCause, mSuggestedRetryTime, mId, mLinkStatus,
+ mProtocolType, mInterfaceName, mAddresses, mDnsAddresses, mGatewayAddresses,
+ mPcscfAddresses, mMtu, mMtuV4, mMtuV6);
}
}
}
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 43aeb19fe1bd..f5cd68f050a4 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -2268,4 +2268,9 @@ interface ITelephony {
* @return operatorinfo on success
*/
String getManualNetworkSelectionPlmn(int subId);
+
+ /**
+ * Whether device can connect to 5G network when two SIMs are active.
+ */
+ boolean canConnectTo5GInDsdsMode();
}
diff --git a/tests/AppLaunch/Android.bp b/tests/AppLaunch/Android.bp
index f90f26f00e6d..75db55122553 100644
--- a/tests/AppLaunch/Android.bp
+++ b/tests/AppLaunch/Android.bp
@@ -8,6 +8,8 @@ android_test {
"android.test.base",
"android.test.runner",
],
- static_libs: ["androidx.test.rules"],
+ static_libs: [
+ "androidx.test.rules",
+ "ub-uiautomator"],
test_suites: ["device-tests"],
}
diff --git a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
index 2d2f4dbdf907..7d750b7bf690 100644
--- a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
+++ b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
@@ -15,6 +15,8 @@
*/
package com.android.tests.applaunch;
+import static org.junit.Assert.assertNotNull;
+
import android.accounts.Account;
import android.accounts.AccountManager;
import android.app.ActivityManager;
@@ -29,7 +31,9 @@ import android.content.pm.ResolveInfo;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
+import android.os.SystemClock;
import android.os.UserHandle;
+import android.support.test.uiautomator.UiDevice;
import android.test.InstrumentationTestCase;
import android.test.InstrumentationTestRunner;
import android.util.Log;
@@ -46,6 +50,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
+import java.nio.file.Paths;
import java.time.format.DateTimeFormatter;
import java.time.ZonedDateTime;
import java.time.ZoneOffset;
@@ -67,6 +72,7 @@ import java.util.Set;
* in the following format:
* -e apps <app name>^<result key>|<app name>^<result key>
*/
+@Deprecated
public class AppLaunch extends InstrumentationTestCase {
private static final int JOIN_TIMEOUT = 10000;
@@ -94,6 +100,9 @@ public class AppLaunch extends InstrumentationTestCase {
private static final String KEY_TRACE_DUMPINTERVAL = "tracedump_interval";
private static final String KEY_COMPILER_FILTERS = "compiler_filters";
private static final String KEY_FORCE_STOP_APP = "force_stop_app";
+ private static final String ENABLE_SCREEN_RECORDING = "enable_screen_recording";
+ private static final int MAX_RECORDING_PARTS = 5;
+ private static final long VIDEO_TAIL_BUFFER = 500;
private static final String SIMPLEPERF_APP_CMD =
"simpleperf --log fatal stat --csv -e cpu-cycles,major-faults --app %s & %s";
@@ -144,14 +153,17 @@ public class AppLaunch extends InstrumentationTestCase {
private Map<String, Intent> mNameToIntent;
private List<LaunchOrder> mLaunchOrderList = new ArrayList<LaunchOrder>();
+ private RecordingThread mCurrentThread;
private Map<String, String> mNameToResultKey;
private Map<String, Map<String, List<AppLaunchResult>>> mNameToLaunchTime;
private IActivityManager mAm;
+ private File launchSubDir = null;
private String mSimplePerfCmd = null;
private String mLaunchOrder = null;
private boolean mDropCache = false;
private int mLaunchIterations = 10;
private boolean mForceStopApp = true;
+ private boolean mEnableRecording = false;
private int mTraceLaunchCount = 0;
private String mTraceDirectoryStr = null;
private Bundle mResult = new Bundle();
@@ -166,6 +178,7 @@ public class AppLaunch extends InstrumentationTestCase {
private boolean mCycleCleanUp = false;
private boolean mTraceAll = false;
private boolean mIterationCycle = false;
+ private UiDevice mDevice;
enum IorapStatus {
UNDEFINED,
@@ -222,7 +235,7 @@ public class AppLaunch extends InstrumentationTestCase {
}
try {
- File launchSubDir = new File(launchRootDir, LAUNCH_SUB_DIRECTORY);
+ launchSubDir = new File(launchRootDir, LAUNCH_SUB_DIRECTORY);
if (!launchSubDir.exists() && !launchSubDir.mkdirs()) {
throw new IOException("Unable to create the lauch file sub directory "
@@ -923,9 +936,16 @@ public class AppLaunch extends InstrumentationTestCase {
mLaunchIterations = Integer.parseInt(launchIterations);
}
String forceStopApp = args.getString(KEY_FORCE_STOP_APP);
+
if (forceStopApp != null) {
mForceStopApp = Boolean.parseBoolean(forceStopApp);
}
+
+ String enableRecording = args.getString(ENABLE_SCREEN_RECORDING);
+
+ if (enableRecording != null) {
+ mEnableRecording = Boolean.parseBoolean(enableRecording);
+ }
String appList = args.getString(KEY_APPS);
if (appList == null)
return;
@@ -1038,6 +1058,9 @@ public class AppLaunch extends InstrumentationTestCase {
private AppLaunchResult startApp(String appName, String launchReason)
throws NameNotFoundException, RemoteException {
Log.i(TAG, "Starting " + appName);
+ if(mEnableRecording) {
+ startRecording(appName, launchReason);
+ }
Intent startIntent = mNameToIntent.get(appName);
if (startIntent == null) {
@@ -1053,6 +1076,10 @@ public class AppLaunch extends InstrumentationTestCase {
} catch (InterruptedException e) {
// ignore
}
+
+ if(mEnableRecording) {
+ stopRecording();
+ }
return runnable.getResult();
}
@@ -1360,4 +1387,126 @@ public class AppLaunch extends InstrumentationTestCase {
}
}
+
+ /**
+ * Start the screen recording while launching the app.
+ *
+ * @param appName
+ * @param launchReason
+ */
+ private void startRecording(String appName, String launchReason) {
+ Log.v(TAG, "Started Recording");
+ mCurrentThread = new RecordingThread("test-screen-record",
+ String.format("%s_%s", appName, launchReason));
+ mCurrentThread.start();
+ }
+
+ /**
+ * Stop already started screen recording.
+ */
+ private void stopRecording() {
+ // Skip if not directory.
+ if (launchSubDir == null) {
+ return;
+ }
+
+ // Add some extra time to the video end.
+ SystemClock.sleep(VIDEO_TAIL_BUFFER);
+ // Ctrl + C all screen record processes.
+ mCurrentThread.cancel();
+ // Wait for the thread to completely die.
+ try {
+ mCurrentThread.join();
+ } catch (InterruptedException ex) {
+ Log.e(TAG, "Interrupted when joining the recording thread.", ex);
+ }
+ Log.v(TAG, "Stopped Recording");
+ }
+
+ /** Returns the recording's name for part {@code part} of launch description. */
+ private File getOutputFile(String description, int part) {
+ // Omit the iteration number for the first iteration.
+ final String fileName =
+ String.format(
+ "%s-video%s.mp4", description, part == 1 ? "" : part);
+ return Paths.get(launchSubDir.getAbsolutePath(), description).toFile();
+ }
+
+
+ /**
+ * Encapsulates the start and stop screen recording logic.
+ * Copied from ScreenRecordCollector.
+ */
+ private class RecordingThread extends Thread {
+ private final String mDescription;
+ private final List<File> mRecordings;
+
+ private boolean mContinue;
+
+ public RecordingThread(String name, String description) {
+ super(name);
+
+ mContinue = true;
+ mRecordings = new ArrayList<>();
+
+ assertNotNull("No test description provided for recording.", description);
+ mDescription = description;
+ }
+
+ @Override
+ public void run() {
+ try {
+ // Start at i = 1 to encode parts as X.mp4, X2.mp4, X3.mp4, etc.
+ for (int i = 1; i <= MAX_RECORDING_PARTS && mContinue; i++) {
+ File output = getOutputFile(mDescription, i);
+ Log.d(
+ TAG,
+ String.format("Recording screen to %s", output.getAbsolutePath()));
+ mRecordings.add(output);
+ // Make sure not to block on this background command in the main thread so
+ // that the test continues to run, but block in this thread so it does not
+ // trigger a new screen recording session before the prior one completes.
+ getDevice().executeShellCommand(
+ String.format("screenrecord %s", output.getAbsolutePath()));
+ }
+ } catch (IOException e) {
+ throw new RuntimeException("Caught exception while screen recording.");
+ }
+ }
+
+ public void cancel() {
+ mContinue = false;
+
+ // Identify the screenrecord PIDs and send SIGINT 2 (Ctrl + C) to each.
+ try {
+ String[] pids = getDevice().executeShellCommand(
+ "pidof screenrecord").split(" ");
+ for (String pid : pids) {
+ // Avoid empty process ids, because of weird splitting behavior.
+ if (pid.isEmpty()) {
+ continue;
+ }
+
+ getDevice().executeShellCommand(
+ String.format("kill -2 %s", pid));
+ Log.d(
+ TAG,
+ String.format("Sent SIGINT 2 to screenrecord process (%s)", pid));
+ }
+ } catch (IOException e) {
+ throw new RuntimeException("Failed to kill screen recording process.");
+ }
+ }
+
+ public List<File> getRecordings() {
+ return mRecordings;
+ }
+ }
+
+ public UiDevice getDevice() {
+ if (mDevice == null) {
+ mDevice = UiDevice.getInstance(getInstrumentation());
+ }
+ return mDevice;
+ }
}
diff --git a/tests/DozeTest/src/com/android/dreams/dozetest/DozeTestDream.java b/tests/DozeTest/src/com/android/dreams/dozetest/DozeTestDream.java
index f4f610b1b280..fa292bd0d57a 100644
--- a/tests/DozeTest/src/com/android/dreams/dozetest/DozeTestDream.java
+++ b/tests/DozeTest/src/com/android/dreams/dozetest/DozeTestDream.java
@@ -100,7 +100,6 @@ public class DozeTestDream extends DreamService {
public void onAttachedToWindow() {
super.onAttachedToWindow();
setInteractive(false);
- setLowProfile(true);
setFullscreen(true);
setContentView(R.layout.dream);
setScreenBright(false);
diff --git a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
index 6e6331312eac..a1bb0d586916 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
@@ -60,14 +60,13 @@ import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.app.AlarmManager;
import android.app.usage.NetworkStatsManager;
import android.content.Context;
@@ -95,8 +94,6 @@ import android.os.Message;
import android.os.Messenger;
import android.os.PowerManager;
import android.os.SimpleClock;
-import android.telephony.PhoneStateListener;
-import android.telephony.ServiceState;
import android.telephony.TelephonyManager;
import androidx.test.InstrumentationRegistry;
@@ -109,7 +106,7 @@ import com.android.internal.util.test.BroadcastInterceptingContext;
import com.android.server.net.NetworkStatsService.NetworkStatsSettings;
import com.android.server.net.NetworkStatsService.NetworkStatsSettings.Config;
import com.android.testutils.HandlerUtilsKt;
-import com.android.testutils.TestableNetworkStatsProvider;
+import com.android.testutils.TestableNetworkStatsProviderBinder;
import libcore.io.IoUtils;
@@ -126,6 +123,7 @@ import java.io.File;
import java.time.Clock;
import java.time.ZoneOffset;
import java.util.Objects;
+import java.util.concurrent.Executor;
/**
* Tests for {@link NetworkStatsService}.
@@ -168,14 +166,13 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest {
private @Mock NetworkStatsSettings mSettings;
private @Mock IBinder mBinder;
private @Mock AlarmManager mAlarmManager;
- private @Mock TelephonyManager mTelephonyManager;
+ @Mock
+ private NetworkStatsSubscriptionsMonitor mNetworkStatsSubscriptionsMonitor;
private HandlerThread mHandlerThread;
private NetworkStatsService mService;
private INetworkStatsSession mSession;
private INetworkManagementEventObserver mNetworkObserver;
- @Nullable
- private PhoneStateListener mPhoneStateListener;
private final Clock mClock = new SimpleClock(ZoneOffset.UTC) {
@Override
@@ -203,8 +200,8 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest {
mHandlerThread = new HandlerThread("HandlerThread");
final NetworkStatsService.Dependencies deps = makeDependencies();
mService = new NetworkStatsService(mServiceContext, mNetManager, mAlarmManager, wakeLock,
- mClock, mTelephonyManager, mSettings,
- mStatsFactory, new NetworkStatsObservers(), mStatsDir, getBaseDir(mStatsDir), deps);
+ mClock, mSettings, mStatsFactory, new NetworkStatsObservers(), mStatsDir,
+ getBaseDir(mStatsDir), deps);
mElapsedRealtime = 0L;
@@ -224,12 +221,6 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest {
ArgumentCaptor.forClass(INetworkManagementEventObserver.class);
verify(mNetManager).registerObserver(networkObserver.capture());
mNetworkObserver = networkObserver.getValue();
-
- // Capture the phone state listener that created by service.
- final ArgumentCaptor<PhoneStateListener> phoneStateListenerCaptor =
- ArgumentCaptor.forClass(PhoneStateListener.class);
- verify(mTelephonyManager).listen(phoneStateListenerCaptor.capture(), anyInt());
- mPhoneStateListener = phoneStateListenerCaptor.getValue();
}
@NonNull
@@ -239,6 +230,14 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest {
public HandlerThread makeHandlerThread() {
return mHandlerThread;
}
+
+ @Override
+ public NetworkStatsSubscriptionsMonitor makeSubscriptionsMonitor(
+ @NonNull Context context, @NonNull Executor executor,
+ @NonNull NetworkStatsService service) {
+
+ return mNetworkStatsSubscriptionsMonitor;
+ }
};
}
@@ -678,10 +677,9 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest {
// TODO: support per IMSI state
private void setMobileRatTypeAndWaitForIdle(int ratType) {
- final ServiceState mockSs = mock(ServiceState.class);
- when(mockSs.getDataNetworkType()).thenReturn(ratType);
- mPhoneStateListener.onServiceStateChanged(mockSs);
-
+ when(mNetworkStatsSubscriptionsMonitor.getRatTypeForSubscriberId(anyString()))
+ .thenReturn(ratType);
+ mService.handleOnCollapsedRatTypeChanged();
HandlerUtilsKt.waitForIdle(mHandlerThread, WAIT_TIMEOUT);
}
@@ -1118,7 +1116,8 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest {
expectNetworkStatsUidDetail(buildEmptyStats());
// Register custom provider and retrieve callback.
- final TestableNetworkStatsProvider provider = new TestableNetworkStatsProvider();
+ final TestableNetworkStatsProviderBinder provider =
+ new TestableNetworkStatsProviderBinder();
final INetworkStatsProviderCallback cb =
mService.registerNetworkStatsProvider("TEST", provider);
assertNotNull(cb);
@@ -1176,7 +1175,8 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest {
mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), new VpnInfo[0]);
// Register custom provider and retrieve callback.
- final TestableNetworkStatsProvider provider = new TestableNetworkStatsProvider();
+ final TestableNetworkStatsProviderBinder provider =
+ new TestableNetworkStatsProviderBinder();
final INetworkStatsProviderCallback cb =
mService.registerNetworkStatsProvider("TEST", provider);
assertNotNull(cb);