summaryrefslogtreecommitdiff
path: root/services
diff options
context:
space:
mode:
Diffstat (limited to 'services')
-rw-r--r--services/Android.bp49
-rw-r--r--services/OWNERS5
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java2
-rw-r--r--services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java3
-rw-r--r--services/art-profile2
-rw-r--r--services/autofill/OWNERS2
-rw-r--r--services/cloudsearch/OWNERS4
-rw-r--r--services/companion/OWNERS4
-rw-r--r--services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java2
-rw-r--r--services/core/Android.bp14
-rw-r--r--services/core/java/android/app/usage/OWNERS1
-rw-r--r--services/core/java/android/content/pm/OWNERS2
-rw-r--r--services/core/java/com/android/server/AppFuseMountException.java (renamed from services/core/java/com/android/server/NativeDaemonConnectorException.java)27
-rw-r--r--services/core/java/com/android/server/BatteryService.java499
-rw-r--r--services/core/java/com/android/server/BluetoothAirplaneModeListener.java140
-rw-r--r--services/core/java/com/android/server/BluetoothDeviceConfigListener.java76
-rw-r--r--services/core/java/com/android/server/BluetoothManagerService.java2970
-rw-r--r--services/core/java/com/android/server/BluetoothModeChangeHelper.java145
-rw-r--r--services/core/java/com/android/server/BluetoothService.java71
-rw-r--r--services/core/java/com/android/server/BootReceiver.java198
-rw-r--r--services/core/java/com/android/server/ConsumerIrService.java60
-rw-r--r--services/core/java/com/android/server/DynamicSystemService.java26
-rw-r--r--services/core/java/com/android/server/IpSecService.java1917
-rw-r--r--services/core/java/com/android/server/NativeDaemonConnector.java723
-rw-r--r--services/core/java/com/android/server/NativeDaemonEvent.java268
-rw-r--r--services/core/java/com/android/server/NativeDaemonTimeoutException.java28
-rw-r--r--services/core/java/com/android/server/NetworkManagementService.java175
-rw-r--r--services/core/java/com/android/server/NsdService.java984
-rw-r--r--services/core/java/com/android/server/OWNERS6
-rw-r--r--services/core/java/com/android/server/SerialService.java6
-rw-r--r--services/core/java/com/android/server/SmartStorageMaintIdler.java89
-rw-r--r--services/core/java/com/android/server/StorageManagerService.java254
-rw-r--r--services/core/java/com/android/server/SystemServiceManager.java19
-rw-r--r--services/core/java/com/android/server/TelephonyRegistry.java272
-rw-r--r--services/core/java/com/android/server/VcnManagementService.java62
-rw-r--r--services/core/java/com/android/server/VpnManagerService.java7
-rw-r--r--services/core/java/com/android/server/accounts/AccountManagerService.java3
-rw-r--r--services/core/java/com/android/server/accounts/OWNERS10
-rw-r--r--services/core/java/com/android/server/adb/AdbDebuggingManager.java12
-rw-r--r--services/core/java/com/android/server/adb/AdbService.java55
-rw-r--r--services/core/java/com/android/server/am/ActiveServices.java8
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java29
-rw-r--r--services/core/java/com/android/server/am/BatteryExternalStatsWorker.java11
-rw-r--r--services/core/java/com/android/server/am/BatteryStatsService.java12
-rw-r--r--services/core/java/com/android/server/am/CachedAppOptimizer.java14
-rw-r--r--services/core/java/com/android/server/am/DataConnectionStats.java3
-rw-r--r--services/core/java/com/android/server/am/OomAdjuster.java23
-rw-r--r--services/core/java/com/android/server/am/OomAdjuster.md3
-rw-r--r--services/core/java/com/android/server/am/PendingIntentController.java15
-rw-r--r--services/core/java/com/android/server/am/SettingsToPropertiesMapper.java3
-rw-r--r--services/core/java/com/android/server/app/OWNERS1
-rw-r--r--services/core/java/com/android/server/audio/AudioDeviceBroker.java516
-rw-r--r--services/core/java/com/android/server/audio/AudioDeviceInventory.java365
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java158
-rw-r--r--services/core/java/com/android/server/audio/AudioServiceEvents.java13
-rw-r--r--services/core/java/com/android/server/audio/BtHelper.java125
-rw-r--r--services/core/java/com/android/server/audio/PlaybackActivityMonitor.java2
-rw-r--r--services/core/java/com/android/server/audio/TEST_MAPPING6
-rw-r--r--services/core/java/com/android/server/biometrics/AuthService.java40
-rw-r--r--services/core/java/com/android/server/biometrics/OWNERS1
-rw-r--r--services/core/java/com/android/server/broadcastradio/hal1/Tuner.java6
-rw-r--r--services/core/java/com/android/server/broadcastradio/hal1/TunerCallback.java5
-rw-r--r--services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java4
-rw-r--r--services/core/java/com/android/server/clipboard/ClipboardService.java18
-rw-r--r--services/core/java/com/android/server/clipboard/OWNERS2
-rw-r--r--services/core/java/com/android/server/compat/OWNERS7
-rw-r--r--services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java54
-rw-r--r--services/core/java/com/android/server/connectivity/NetdEventListenerService.java25
-rw-r--r--services/core/java/com/android/server/connectivity/OWNERS8
-rw-r--r--services/core/java/com/android/server/connectivity/PacProxyService.java13
-rw-r--r--services/core/java/com/android/server/connectivity/Vpn.java232
-rw-r--r--services/core/java/com/android/server/criticalevents/OWNERS2
-rw-r--r--services/core/java/com/android/server/display/DisplayModeDirector.java5
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecKeycode.java109
-rwxr-xr-xservices/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java28
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java1
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java1
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java22
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecMessage.java9
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java18
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecStandbyModeHandler.java56
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiControlService.java54
-rw-r--r--services/core/java/com/android/server/health/HealthHalCallbackHidl.java117
-rw-r--r--services/core/java/com/android/server/health/HealthInfoCallback.java (renamed from services/core/java/com/android/server/INativeDaemonConnectorCallbacks.java)22
-rw-r--r--services/core/java/com/android/server/health/HealthRegCallbackAidl.java119
-rw-r--r--services/core/java/com/android/server/health/HealthServiceWrapper.java119
-rw-r--r--services/core/java/com/android/server/health/HealthServiceWrapperAidl.java215
-rw-r--r--services/core/java/com/android/server/health/HealthServiceWrapperHidl.java313
-rw-r--r--services/core/java/com/android/server/health/OWNERS1
-rw-r--r--services/core/java/com/android/server/health/Utils.java85
-rw-r--r--services/core/java/com/android/server/input/InputManagerService.java12
-rw-r--r--services/core/java/com/android/server/inputmethod/OWNERS1
-rw-r--r--services/core/java/com/android/server/location/listeners/PendingIntentListenerRegistration.java2
-rw-r--r--services/core/java/com/android/server/location/provider/LocationProviderManager.java4
-rw-r--r--services/core/java/com/android/server/media/BluetoothRouteProvider.java77
-rw-r--r--services/core/java/com/android/server/media/OWNERS10
-rw-r--r--services/core/java/com/android/server/net/DelayedDiskWrite.java98
-rw-r--r--services/core/java/com/android/server/net/IpConfigStore.java434
-rw-r--r--services/core/java/com/android/server/net/NetworkIdentitySet.java191
-rw-r--r--services/core/java/com/android/server/net/NetworkPolicyLogger.java100
-rw-r--r--services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java17
-rw-r--r--services/core/java/com/android/server/net/NetworkPolicyManagerService.java1100
-rw-r--r--services/core/java/com/android/server/net/NetworkStatsAccess.java199
-rw-r--r--services/core/java/com/android/server/net/NetworkStatsCollection.java815
-rw-r--r--services/core/java/com/android/server/net/NetworkStatsFactory.java489
-rw-r--r--services/core/java/com/android/server/net/NetworkStatsManagerInternal.java45
-rw-r--r--services/core/java/com/android/server/net/NetworkStatsObservers.java442
-rw-r--r--services/core/java/com/android/server/net/NetworkStatsRecorder.java505
-rw-r--r--services/core/java/com/android/server/net/NetworkStatsService.java2255
-rw-r--r--services/core/java/com/android/server/net/NetworkStatsSubscriptionsMonitor.java243
-rw-r--r--services/core/java/com/android/server/net/OWNERS7
-rw-r--r--services/core/java/com/android/server/notification/NotificationHistoryDatabase.java6
-rw-r--r--services/core/java/com/android/server/notification/NotificationHistoryManager.java1
-rw-r--r--services/core/java/com/android/server/pm/ApexManager.java26
-rw-r--r--services/core/java/com/android/server/pm/Installer.java23
-rw-r--r--services/core/java/com/android/server/pm/OWNERS26
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerService.java4
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java165
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerServiceUtils.java10
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerShellCommand.java15
-rw-r--r--services/core/java/com/android/server/pm/SELinuxMMAC.java16
-rw-r--r--services/core/java/com/android/server/pm/StagingManager.java152
-rw-r--r--services/core/java/com/android/server/pm/UserDataPreparer.java8
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java7
-rw-r--r--services/core/java/com/android/server/pm/dex/ArtManagerService.java4
-rw-r--r--services/core/java/com/android/server/pm/dex/OWNERS3
-rw-r--r--services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java1
-rw-r--r--services/core/java/com/android/server/pm/permission/OWNERS6
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerService.java39
-rw-r--r--services/core/java/com/android/server/policy/DisplayFoldDurationLogger.java4
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java38
-rw-r--r--services/core/java/com/android/server/power/PowerManagerService.java5
-rw-r--r--services/core/java/com/android/server/recoverysystem/RecoverySystemShellCommand.java15
-rw-r--r--services/core/java/com/android/server/security/OWNERS1
-rw-r--r--services/core/java/com/android/server/stats/bootstrap/StatsBootstrapAtomService.java98
-rw-r--r--services/core/java/com/android/server/stats/pull/StatsPullAtomService.java393
-rw-r--r--services/core/java/com/android/server/storage/AppFuseBridge.java10
-rw-r--r--services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java15
-rw-r--r--services/core/java/com/android/server/timedetector/OWNERS4
-rw-r--r--services/core/java/com/android/server/timezone/OWNERS5
-rw-r--r--services/core/java/com/android/server/timezonedetector/OWNERS6
-rw-r--r--services/core/java/com/android/server/timezonedetector/ReferenceWithHistory.java9
-rw-r--r--services/core/java/com/android/server/tracing/TracingServiceProxy.java193
-rwxr-xr-xservices/core/java/com/android/server/tv/TvInputHardwareManager.java74
-rwxr-xr-xservices/core/java/com/android/server/tv/TvInputManagerService.java140
-rw-r--r--services/core/java/com/android/server/vcn/OWNERS2
-rw-r--r--services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java103
-rw-r--r--services/core/java/com/android/server/vcn/VcnGatewayConnection.java68
-rw-r--r--services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java339
-rw-r--r--services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java (renamed from services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java)373
-rw-r--r--services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java222
-rw-r--r--services/core/java/com/android/server/vcn/util/PersistableBundleUtils.java16
-rw-r--r--services/core/java/com/android/server/vibrator/OWNERS2
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java26
-rw-r--r--services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java13
-rw-r--r--services/core/java/com/android/server/wm/DisplayPolicy.java14
-rw-r--r--services/core/java/com/android/server/wm/RefreshRatePolicy.java14
-rw-r--r--services/core/java/com/android/server/wm/SurfaceAnimator.java7
-rw-r--r--services/core/java/com/android/server/wm/Task.java3
-rw-r--r--services/core/java/com/android/server/wm/TaskSnapshotController.java10
-rw-r--r--services/core/jni/Android.bp11
-rw-r--r--services/core/jni/BroadcastRadio/types.h12
-rw-r--r--services/core/jni/com_android_server_ConsumerIrService.cpp8
-rw-r--r--services/core/jni/com_android_server_UsbAlsaJackDetector.cpp1
-rw-r--r--services/core/jni/com_android_server_UsbDescriptorParser.cpp31
-rw-r--r--services/core/jni/com_android_server_UsbDeviceManager.cpp1
-rw-r--r--services/core/jni/com_android_server_UsbHostManager.cpp3
-rw-r--r--services/core/jni/com_android_server_input_InputManagerService.cpp4
-rw-r--r--services/core/jni/com_android_server_net_NetworkStatsService.cpp132
-rw-r--r--services/core/jni/com_android_server_power_PowerManagerService.cpp39
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java3
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java123
-rw-r--r--services/incremental/Android.bp2
-rw-r--r--services/java/com/android/server/SystemConfigService.java17
-rw-r--r--services/java/com/android/server/SystemServer.java56
-rw-r--r--services/midi/OWNERS1
-rw-r--r--services/net/Android.bp2
-rw-r--r--services/net/OWNERS8
-rw-r--r--services/net/java/android/net/ConnectivityModuleConnector.java5
-rw-r--r--services/people/java/com/android/server/people/data/CallLogQueryHelper.java3
-rw-r--r--services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java151
-rw-r--r--services/proguard.flags105
-rw-r--r--services/tests/PackageManagerServiceTests/OWNERS4
-rw-r--r--services/tests/mockingservicestests/OWNERS1
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java15
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/appsearch/AppSearchConfigTest.java474
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/appsearch/FrameworkLimitConfigTest.java68
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/appsearch/OWNERS1
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/appsearch/stats/PlatformLoggerTest.java228
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java301
-rw-r--r--services/tests/servicestests/Android.bp3
-rw-r--r--services/tests/servicestests/AndroidManifest.xml1
-rw-r--r--services/tests/servicestests/OWNERS1
-rw-r--r--services/tests/servicestests/src/com/android/server/BatteryServiceTest.java148
-rw-r--r--services/tests/servicestests/src/com/android/server/BluetoothAirplaneModeListenerTest.java123
-rw-r--r--services/tests/servicestests/src/com/android/server/BootReceiverTest.java72
-rw-r--r--services/tests/servicestests/src/com/android/server/NativeDaemonConnectorTest.java97
-rw-r--r--services/tests/servicestests/src/com/android/server/OWNERS3
-rw-r--r--services/tests/servicestests/src/com/android/server/appsearch/AppSearchImplPlatformTest.java463
-rw-r--r--services/tests/servicestests/src/com/android/server/appsearch/FrameworkOptimizeStrategyTest.java70
-rw-r--r--services/tests/servicestests/src/com/android/server/appsearch/OWNERS1
-rw-r--r--services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java3346
-rw-r--r--services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchLoggerTest.java906
-rw-r--r--services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverterTest.java256
-rw-r--r--services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SchemaToProtoConverterTest.java151
-rw-r--r--services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SnippetTest.java322
-rw-r--r--services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/stats/AppSearchStatsTest.java393
-rw-r--r--services/tests/servicestests/src/com/android/server/appsearch/stats/PlatformLoggerTest.java199
-rw-r--r--services/tests/servicestests/src/com/android/server/appsearch/visibilitystore/VisibilityStoreImplTest.java469
-rw-r--r--services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java39
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/OWNERS8
-rw-r--r--services/tests/servicestests/src/com/android/server/criticalevents/OWNERS1
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java228
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java22
-rwxr-xr-xservices/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageTest.java49
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java129
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiUtilsTest.java47
-rw-r--r--services/tests/servicestests/src/com/android/server/health/HealthServiceWrapperTest.java242
-rw-r--r--services/tests/servicestests/src/com/android/server/health/OWNERS1
-rw-r--r--services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java270
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java16
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/OWNERS4
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/SELinuxMMACTest.java12
-rw-r--r--services/tests/servicestests/src/com/android/server/timedetector/OWNERS3
-rw-r--r--services/tests/servicestests/src/com/android/server/timedetector/ReferenceWithHistoryTest.java72
-rw-r--r--services/tests/servicestests/src/com/android/server/timezone/OWNERS5
-rw-r--r--services/tests/servicestests/src/com/android/server/timezonedetector/OWNERS3
-rw-r--r--services/tests/servicestests/src/com/android/server/wm/OWNERS1
-rw-r--r--services/tests/uiservicestests/Android.bp1
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java12
-rw-r--r--services/translation/OWNERS7
-rw-r--r--services/usage/OWNERS2
-rw-r--r--services/usb/java/com/android/server/usb/UsbDeviceManager.java9
-rw-r--r--services/wallpapereffectsgeneration/OWNERS4
235 files changed, 7402 insertions, 24611 deletions
diff --git a/services/Android.bp b/services/Android.bp
index c83a697a71c1..536dc5d8ccd6 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -23,6 +23,38 @@ java_defaults {
},
}
+// Opt-in config for optimizing and shrinking the services target using R8.
+// Enabled via `export SYSTEM_OPTIMIZE_JAVA=true`, or explicitly in Make via the
+// `SOONG_CONFIG_ANDROID_SYSTEM_OPTIMIZE_JAVA` variable.
+// TODO(b/196084106): Enable optimizations by default after stabilizing and
+// building out retrace infrastructure.
+soong_config_module_type {
+ name: "system_optimized_java_defaults",
+ module_type: "java_defaults",
+ config_namespace: "ANDROID",
+ bool_variables: ["SYSTEM_OPTIMIZE_JAVA"],
+ properties: ["optimize"],
+}
+
+system_optimized_java_defaults {
+ name: "services_java_defaults",
+ soong_config_variables: {
+ SYSTEM_OPTIMIZE_JAVA: {
+ optimize: {
+ enabled: true,
+ // TODO(b/210510433): Enable optimizations after improving
+ // retracing infra.
+ optimize: false,
+ shrink: true,
+ proguard_flags_files: ["proguard.flags"],
+ },
+ // Note: Optimizations are disabled by default if unspecified in
+ // the java_library rule.
+ conditions_default: {},
+ },
+ },
+}
+
filegroup {
name: "services-main-sources",
srcs: [
@@ -44,6 +76,7 @@ filegroup {
":services.appwidget-sources",
":services.autofill-sources",
":services.backup-sources",
+ ":services.bluetooth-sources", // TODO(b/214988855) : Remove once apex/service-bluetooth jar is ready
":backuplib-sources",
":services.companion-sources",
":services.contentcapture-sources",
@@ -81,6 +114,7 @@ java_library {
// ============================================================
java_library {
name: "services",
+ defaults: ["services_java_defaults"],
installable: true,
dex_preopt: {
@@ -130,6 +164,7 @@ java_library {
libs: [
"android.hidl.manager-V1.0-java",
"framework-tethering.stubs.module_lib",
+ "service-art.stubs.system_server",
],
// Uncomment to enable output of certain warnings (deprecated, unchecked)
@@ -143,6 +178,10 @@ cc_library_shared {
name: "libandroid_servers",
defaults: ["libservices.core-libs"],
whole_static_libs: ["libservices.core"],
+ required: [
+ // TODO: remove after NetworkStatsService is moved to the mainline module.
+ "libcom_android_net_module_util_jni",
+ ],
}
platform_compat_config {
@@ -189,19 +228,13 @@ droidstubs {
},
dists: [
{
- targets: [
- "sdk",
- "win_sdk",
- ],
+ targets: ["sdk"],
dir: "apistubs/android/system-server/api",
dest: "android-non-updatable.txt",
tag: ".api.txt",
},
{
- targets: [
- "sdk",
- "win_sdk",
- ],
+ targets: ["sdk"],
dir: "apistubs/android/system-server/api",
dest: "android-non-updatable-removed.txt",
tag: ".removed-api.txt",
diff --git a/services/OWNERS b/services/OWNERS
index 3b972e922e95..67cee5517913 100644
--- a/services/OWNERS
+++ b/services/OWNERS
@@ -1,6 +1,9 @@
per-file Android.bp = file:platform/build/soong:/OWNERS
# art-team@ manages the system server profile
-per-file art-profile* = calin@google.com, mathieuc@google.com, ngeoffray@google.com
+per-file art-profile* = calin@google.com, ngeoffray@google.com, vmarko@google.com
per-file java/com/android/server/* = toddke@google.com,patb@google.com
+per-file tests/servicestests/src/com/android/server/systemconfig/* = patb@google.com
+
+per-file proguard.flags = jdduke@google.com
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
index 467cab5fec04..f57534be2f7e 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
@@ -402,7 +402,7 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect
@Override
public void dispatchGesture(int sequence, ParceledListSlice gestureSteps, int displayId) {
synchronized (mLock) {
- if (mSecurityPolicy.canPerformGestures(this)) {
+ if (mServiceInterface != null && mSecurityPolicy.canPerformGestures(this)) {
MotionEventInjector motionEventInjector =
mSystemSupport.getMotionEventInjectorForDisplayLocked(displayId);
if (wmTracingEnabled()) {
diff --git a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
index 6cd23fcfd0fb..7ee0690265f3 100644
--- a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
@@ -127,7 +127,6 @@ class UiAutomationManager {
mUiAutomationServiceOwner = owner;
mUiAutomationServiceInfo = accessibilityServiceInfo;
mUiAutomationService.mServiceInterface = serviceClient;
- mUiAutomationService.onAdded();
try {
mUiAutomationService.mServiceInterface.asBinder().linkToDeath(mUiAutomationService,
0);
@@ -137,6 +136,8 @@ class UiAutomationManager {
return;
}
+ mUiAutomationService.onAdded();
+
mUiAutomationService.connectServiceUnknownThread();
}
}
diff --git a/services/art-profile b/services/art-profile
index af58bca129f8..2d9e95eebdc5 100644
--- a/services/art-profile
+++ b/services/art-profile
@@ -1464,7 +1464,7 @@ HSPLcom/android/server/DeviceIdleController;->reportTempWhitelistChangedLocked(I
HPLcom/android/server/DeviceIdleController;->resetIdleManagementLocked()V+]Lcom/android/server/AnyMotionDetector;Lcom/android/server/AnyMotionDetector;]Lcom/android/server/DeviceIdleController;Lcom/android/server/DeviceIdleController;
HPLcom/android/server/DeviceIdleController;->resetLightIdleManagementLocked()V+]Lcom/android/server/DeviceIdleController;Lcom/android/server/DeviceIdleController;
HPLcom/android/server/DeviceIdleController;->scheduleAlarmLocked(JZ)V+]Landroid/app/AlarmManager;Landroid/app/AlarmManager;
-HPLcom/android/server/DeviceIdleController;->scheduleLightAlarmLocked(JJ)V+]Landroid/app/AlarmManager;Landroid/app/AlarmManager;
+HPLcom/android/server/DeviceIdleController;->scheduleLightAlarmLocked(JJZ)V+]Landroid/app/AlarmManager;Landroid/app/AlarmManager;
HPLcom/android/server/DeviceIdleController;->scheduleMotionRegistrationAlarmLocked()V+]Lcom/android/server/DeviceIdleController$Injector;Lcom/android/server/DeviceIdleController$Injector;]Landroid/app/AlarmManager;Landroid/app/AlarmManager;
HSPLcom/android/server/DeviceIdleController;->scheduleMotionTimeoutAlarmLocked()V+]Landroid/app/AlarmManager;Landroid/app/AlarmManager;]Lcom/android/server/DeviceIdleController$Injector;Lcom/android/server/DeviceIdleController$Injector;
HPLcom/android/server/DeviceIdleController;->scheduleReportActiveLocked(Ljava/lang/String;I)V+]Lcom/android/server/DeviceIdleController$MyHandler;Lcom/android/server/DeviceIdleController$MyHandler;
diff --git a/services/autofill/OWNERS b/services/autofill/OWNERS
index c52751d79227..edfb2112198a 100644
--- a/services/autofill/OWNERS
+++ b/services/autofill/OWNERS
@@ -1 +1 @@
-include /core/java/android/service/autofill/OWNERS
+include /core/java/android/view/autofill/OWNERS
diff --git a/services/cloudsearch/OWNERS b/services/cloudsearch/OWNERS
new file mode 100644
index 000000000000..aa4da3b4bee0
--- /dev/null
+++ b/services/cloudsearch/OWNERS
@@ -0,0 +1,4 @@
+# Bug component: 758286
+
+huiwu@google.com
+srazdan@google.com
diff --git a/services/companion/OWNERS b/services/companion/OWNERS
new file mode 100644
index 000000000000..cb4cc56ca17b
--- /dev/null
+++ b/services/companion/OWNERS
@@ -0,0 +1,4 @@
+evanxinchen@google.com
+ewol@google.com
+guojing@google.com
+svetoslavganov@google.com \ No newline at end of file
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index 7c1e2da4d6a3..553072366e17 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -1576,7 +1576,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
}
@Override
- public void onDeviceDisconnected(BluetoothDevice device, @DisconnectReason int reason) {
+ public void onDeviceDisconnected(BluetoothDevice device, int reason) {
Slog.d(LOG_TAG, device.getAddress() + " disconnected w/ reason: (" + reason + ") "
+ BluetoothAdapter.BluetoothConnectionCallback.disconnectReasonText(reason));
CompanionDeviceManagerService.this.onDeviceDisconnected(device.getAddress());
diff --git a/services/core/Android.bp b/services/core/Android.bp
index e5716eee467e..9d190087e300 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -93,6 +93,7 @@ java_library_static {
defaults: ["platform_service_defaults"],
srcs: [
":statslog-art-java-gen",
+ ":services.bluetooth-sources", // TODO(b/214988855) : Remove once apex is ready
":services.core-sources",
":services.core.protologsrc",
":dumpstate_aidl",
@@ -111,6 +112,7 @@ java_library_static {
"java/com/android/server/am/EventLogTags.logtags",
"java/com/android/server/wm/EventLogTags.logtags",
"java/com/android/server/policy/EventLogTags.logtags",
+ ":services.connectivity-tiramisu-sources",
],
libs: [
@@ -138,9 +140,11 @@ java_library_static {
"android.hardware.boot-V1.1-java",
"android.hardware.boot-V1.2-java",
"android.hardware.broadcastradio-V2.0-java",
- "android.hardware.health-V1.0-java",
- "android.hardware.health-V2.0-java",
- "android.hardware.health-V2.1-java",
+ "android.hardware.health-V1.0-java", // HIDL
+ "android.hardware.health-V2.0-java", // HIDL
+ "android.hardware.health-V2.1-java", // HIDL
+ "android.hardware.health-V1-java", // AIDL
+ "android.hardware.health-translate-java",
"android.hardware.light-V1-java",
"android.hardware.tv.cec-V1.1-java",
"android.hardware.weaver-V1.0-java",
@@ -151,6 +155,7 @@ java_library_static {
"android.hardware.oemlock-V1.0-java",
"android.hardware.configstore-V1.1-java",
"android.hardware.contexthub-V1.0-java",
+ "android.hardware.ir-V1-java",
"android.hardware.rebootescrow-V1-java",
"android.hardware.soundtrigger-V2.3-java",
"android.hardware.power.stats-V1-java",
@@ -161,6 +166,9 @@ java_library_static {
"overlayable_policy_aidl-java",
"SurfaceFlingerProperties",
"com.android.sysprop.watchdog",
+ // This is used for services.connectivity-tiramisu-sources.
+ // TODO: delete when NetworkStatsService is moved to the mainline module.
+ "net-utils-device-common-bpf",
],
javac_shard_size: 50,
}
diff --git a/services/core/java/android/app/usage/OWNERS b/services/core/java/android/app/usage/OWNERS
new file mode 100644
index 000000000000..3a555143b11d
--- /dev/null
+++ b/services/core/java/android/app/usage/OWNERS
@@ -0,0 +1 @@
+include platform/frameworks/base:/core/java/android/app/usage/OWNERS
diff --git a/services/core/java/android/content/pm/OWNERS b/services/core/java/android/content/pm/OWNERS
index 5eed0b509688..39931407ec1f 100644
--- a/services/core/java/android/content/pm/OWNERS
+++ b/services/core/java/android/content/pm/OWNERS
@@ -1 +1 @@
-include /core/java/android/content/pm/OWNERS \ No newline at end of file
+include /PACKAGE_MANAGER_OWNERS \ No newline at end of file
diff --git a/services/core/java/com/android/server/NativeDaemonConnectorException.java b/services/core/java/com/android/server/AppFuseMountException.java
index 4d8881c68324..9a9379e4a1c7 100644
--- a/services/core/java/com/android/server/NativeDaemonConnectorException.java
+++ b/services/core/java/com/android/server/AppFuseMountException.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2006 The Android Open Source Project
+ * Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,34 +20,17 @@ import android.os.Parcel;
/**
* An exception that indicates there was an error with a
- * {@link NativeDaemonConnector} operation.
+ * app fuse mount operation.
*/
-public class NativeDaemonConnectorException extends Exception {
- private String mCmd;
- private NativeDaemonEvent mEvent;
-
- public NativeDaemonConnectorException(String detailMessage) {
+public class AppFuseMountException extends Exception {
+ public AppFuseMountException(String detailMessage) {
super(detailMessage);
}
- public NativeDaemonConnectorException(String detailMessage, Throwable throwable) {
+ public AppFuseMountException(String detailMessage, Throwable throwable) {
super(detailMessage, throwable);
}
- public NativeDaemonConnectorException(String cmd, NativeDaemonEvent event) {
- super("command '" + cmd + "' failed with '" + event + "'");
- mCmd = cmd;
- mEvent = event;
- }
-
- public int getCode() {
- return mEvent != null ? mEvent.getCode() : -1;
- }
-
- public String getCmd() {
- return mCmd;
- }
-
/**
* Rethrow as a {@link RuntimeException} subclass that is handled by
* {@link Parcel#writeException(Exception)}.
diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java
index 1e608f5c1240..844ac86e8eb5 100644
--- a/services/core/java/com/android/server/BatteryService.java
+++ b/services/core/java/com/android/server/BatteryService.java
@@ -17,6 +17,7 @@
package com.android.server;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import static com.android.server.health.Utils.copyV1Battery;
import android.annotation.Nullable;
import android.app.ActivityManager;
@@ -25,14 +26,8 @@ import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.database.ContentObserver;
-import android.hardware.health.V1_0.HealthInfo;
-import android.hardware.health.V2_0.IHealth;
-import android.hardware.health.V2_0.Result;
+import android.hardware.health.HealthInfo;
import android.hardware.health.V2_1.BatteryCapacityLevel;
-import android.hardware.health.V2_1.Constants;
-import android.hardware.health.V2_1.IHealthInfoCallback;
-import android.hidl.manager.V1_0.IServiceManager;
-import android.hidl.manager.V1_0.IServiceNotification;
import android.metrics.LogMaker;
import android.os.BatteryManager;
import android.os.BatteryManagerInternal;
@@ -44,7 +39,6 @@ import android.os.Bundle;
import android.os.DropBoxManager;
import android.os.FileUtils;
import android.os.Handler;
-import android.os.HandlerThread;
import android.os.IBatteryPropertiesRegistrar;
import android.os.IBinder;
import android.os.OsProtoEnums;
@@ -62,15 +56,14 @@ import android.provider.Settings;
import android.service.battery.BatteryServiceDumpProto;
import android.sysprop.PowerProperties;
import android.util.EventLog;
-import android.util.MutableInt;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IBatteryStats;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.util.DumpUtils;
import com.android.server.am.BatteryStatsService;
+import com.android.server.health.HealthServiceWrapper;
import com.android.server.lights.LightsManager;
import com.android.server.lights.LogicalLight;
@@ -81,11 +74,7 @@ import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayDeque;
import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
import java.util.NoSuchElementException;
-import java.util.Objects;
-import java.util.concurrent.atomic.AtomicReference;
/**
* <p>BatteryService monitors the charging status, and charge level of the device
@@ -149,7 +138,6 @@ public final class BatteryService extends SystemService {
private HealthInfo mHealthInfo;
private final HealthInfo mLastHealthInfo = new HealthInfo();
- private android.hardware.health.V2_1.HealthInfo mHealthInfo2p1;
private boolean mBatteryLevelCritical;
private int mLastBatteryStatus;
private int mLastBatteryHealth;
@@ -193,7 +181,6 @@ public final class BatteryService extends SystemService {
private ActivityManagerInternal mActivityManagerInternal;
private HealthServiceWrapper mHealthServiceWrapper;
- private HealthHalCallback mHealthHalCallback;
private BatteryPropertiesRegistrar mBatteryPropertiesRegistrar;
private ArrayDeque<Bundle> mBatteryLevelsEventQueue;
private long mLastBatteryLevelChangedSentMs;
@@ -276,13 +263,9 @@ public final class BatteryService extends SystemService {
private void registerHealthCallback() {
traceBegin("HealthInitWrapper");
- mHealthServiceWrapper = new HealthServiceWrapper();
- mHealthHalCallback = new HealthHalCallback();
// IHealth is lazily retrieved.
try {
- mHealthServiceWrapper.init(mHealthHalCallback,
- new HealthServiceWrapper.IServiceManagerSupplier() {},
- new HealthServiceWrapper.IHealthSupplier() {});
+ mHealthServiceWrapper = HealthServiceWrapper.create(this::update);
} catch (RemoteException ex) {
Slog.e(TAG, "health: cannot register callback. (RemoteException)");
throw ex.rethrowFromSystemServer();
@@ -370,8 +353,8 @@ public final class BatteryService extends SystemService {
}
private boolean shouldShutdownLocked() {
- if (mHealthInfo2p1.batteryCapacityLevel != BatteryCapacityLevel.UNSUPPORTED) {
- return (mHealthInfo2p1.batteryCapacityLevel == BatteryCapacityLevel.CRITICAL);
+ if (mHealthInfo.batteryCapacityLevel != BatteryCapacityLevel.UNSUPPORTED) {
+ return (mHealthInfo.batteryCapacityLevel == BatteryCapacityLevel.CRITICAL);
}
if (mHealthInfo.batteryLevel > 0) {
return false;
@@ -413,7 +396,7 @@ public final class BatteryService extends SystemService {
// shut down gracefully if temperature is too high (> 68.0C by default)
// wait until the system has booted before attempting to display the
// shutdown dialog.
- if (mHealthInfo.batteryTemperature > mShutdownBatteryTemperature) {
+ if (mHealthInfo.batteryTemperatureTenthsCelsius > mShutdownBatteryTemperature) {
mHandler.post(new Runnable() {
@Override
public void run() {
@@ -430,51 +413,28 @@ public final class BatteryService extends SystemService {
}
}
- private void update(android.hardware.health.V2_1.HealthInfo info) {
+ private void update(android.hardware.health.HealthInfo info) {
traceBegin("HealthInfoUpdate");
- Trace.traceCounter(Trace.TRACE_TAG_POWER, "BatteryChargeCounter",
- info.legacy.legacy.batteryChargeCounter);
- Trace.traceCounter(Trace.TRACE_TAG_POWER, "BatteryCurrent",
- info.legacy.legacy.batteryCurrent);
- Trace.traceCounter(Trace.TRACE_TAG_POWER, "PlugType",
- plugType(info.legacy.legacy));
- Trace.traceCounter(Trace.TRACE_TAG_POWER, "BatteryStatus",
- info.legacy.legacy.batteryStatus);
+ Trace.traceCounter(
+ Trace.TRACE_TAG_POWER, "BatteryChargeCounter", info.batteryChargeCounterUah);
+ Trace.traceCounter(Trace.TRACE_TAG_POWER, "BatteryCurrent", info.batteryCurrentMicroamps);
+ Trace.traceCounter(Trace.TRACE_TAG_POWER, "PlugType", plugType(info));
+ Trace.traceCounter(Trace.TRACE_TAG_POWER, "BatteryStatus", info.batteryStatus);
synchronized (mLock) {
if (!mUpdatesStopped) {
- mHealthInfo = info.legacy.legacy;
- mHealthInfo2p1 = info;
+ mHealthInfo = info;
// Process the new values.
processValuesLocked(false);
mLock.notifyAll(); // for any waiters on new info
} else {
- copy(mLastHealthInfo, info.legacy.legacy);
+ copyV1Battery(mLastHealthInfo, info);
}
}
traceEnd();
}
- private static void copy(HealthInfo dst, HealthInfo src) {
- dst.chargerAcOnline = src.chargerAcOnline;
- dst.chargerUsbOnline = src.chargerUsbOnline;
- dst.chargerWirelessOnline = src.chargerWirelessOnline;
- dst.maxChargingCurrent = src.maxChargingCurrent;
- dst.maxChargingVoltage = src.maxChargingVoltage;
- dst.batteryStatus = src.batteryStatus;
- dst.batteryHealth = src.batteryHealth;
- dst.batteryPresent = src.batteryPresent;
- dst.batteryLevel = src.batteryLevel;
- dst.batteryVoltage = src.batteryVoltage;
- dst.batteryTemperature = src.batteryTemperature;
- dst.batteryCurrent = src.batteryCurrent;
- dst.batteryCycleCount = src.batteryCycleCount;
- dst.batteryFullCharge = src.batteryFullCharge;
- dst.batteryChargeCounter = src.batteryChargeCounter;
- dst.batteryTechnology = src.batteryTechnology;
- }
-
private static int plugType(HealthInfo healthInfo) {
if (healthInfo.chargerAcOnline) {
return BatteryManager.BATTERY_PLUGGED_AC;
@@ -505,11 +465,16 @@ public final class BatteryService extends SystemService {
// Let the battery stats keep track of the current level.
try {
- mBatteryStats.setBatteryState(mHealthInfo.batteryStatus, mHealthInfo.batteryHealth,
- mPlugType, mHealthInfo.batteryLevel, mHealthInfo.batteryTemperature,
- mHealthInfo.batteryVoltage, mHealthInfo.batteryChargeCounter,
- mHealthInfo.batteryFullCharge,
- mHealthInfo2p1.batteryChargeTimeToFullNowSeconds);
+ mBatteryStats.setBatteryState(
+ mHealthInfo.batteryStatus,
+ mHealthInfo.batteryHealth,
+ mPlugType,
+ mHealthInfo.batteryLevel,
+ mHealthInfo.batteryTemperatureTenthsCelsius,
+ mHealthInfo.batteryVoltageMillivolts,
+ mHealthInfo.batteryChargeCounterUah,
+ mHealthInfo.batteryFullChargeUah,
+ mHealthInfo.batteryChargeTimeToFullNowSeconds);
} catch (RemoteException e) {
// Should never happen.
}
@@ -517,17 +482,18 @@ public final class BatteryService extends SystemService {
shutdownIfNoPowerLocked();
shutdownIfOverTempLocked();
- if (force || (mHealthInfo.batteryStatus != mLastBatteryStatus ||
- mHealthInfo.batteryHealth != mLastBatteryHealth ||
- mHealthInfo.batteryPresent != mLastBatteryPresent ||
- mHealthInfo.batteryLevel != mLastBatteryLevel ||
- mPlugType != mLastPlugType ||
- mHealthInfo.batteryVoltage != mLastBatteryVoltage ||
- mHealthInfo.batteryTemperature != mLastBatteryTemperature ||
- mHealthInfo.maxChargingCurrent != mLastMaxChargingCurrent ||
- mHealthInfo.maxChargingVoltage != mLastMaxChargingVoltage ||
- mHealthInfo.batteryChargeCounter != mLastChargeCounter ||
- mInvalidCharger != mLastInvalidCharger)) {
+ if (force
+ || (mHealthInfo.batteryStatus != mLastBatteryStatus
+ || mHealthInfo.batteryHealth != mLastBatteryHealth
+ || mHealthInfo.batteryPresent != mLastBatteryPresent
+ || mHealthInfo.batteryLevel != mLastBatteryLevel
+ || mPlugType != mLastPlugType
+ || mHealthInfo.batteryVoltageMillivolts != mLastBatteryVoltage
+ || mHealthInfo.batteryTemperatureTenthsCelsius != mLastBatteryTemperature
+ || mHealthInfo.maxChargingCurrentMicroamps != mLastMaxChargingCurrent
+ || mHealthInfo.maxChargingVoltageMicrovolts != mLastMaxChargingVoltage
+ || mHealthInfo.batteryChargeCounterUah != mLastChargeCounter
+ || mInvalidCharger != mLastInvalidCharger)) {
if (mPlugType != mLastPlugType) {
if (mLastPlugType == BATTERY_PLUGGED_NONE) {
@@ -584,8 +550,11 @@ public final class BatteryService extends SystemService {
if (mHealthInfo.batteryLevel != mLastBatteryLevel) {
// Don't do this just from voltage or temperature changes, that is
// too noisy.
- EventLog.writeEvent(EventLogTags.BATTERY_LEVEL,
- mHealthInfo.batteryLevel, mHealthInfo.batteryVoltage, mHealthInfo.batteryTemperature);
+ EventLog.writeEvent(
+ EventLogTags.BATTERY_LEVEL,
+ mHealthInfo.batteryLevel,
+ mHealthInfo.batteryVoltageMillivolts,
+ mHealthInfo.batteryTemperatureTenthsCelsius);
}
if (mBatteryLevelCritical && !mLastBatteryLevelCritical &&
mPlugType == BATTERY_PLUGGED_NONE) {
@@ -691,11 +660,11 @@ public final class BatteryService extends SystemService {
mLastBatteryPresent = mHealthInfo.batteryPresent;
mLastBatteryLevel = mHealthInfo.batteryLevel;
mLastPlugType = mPlugType;
- mLastBatteryVoltage = mHealthInfo.batteryVoltage;
- mLastBatteryTemperature = mHealthInfo.batteryTemperature;
- mLastMaxChargingCurrent = mHealthInfo.maxChargingCurrent;
- mLastMaxChargingVoltage = mHealthInfo.maxChargingVoltage;
- mLastChargeCounter = mHealthInfo.batteryChargeCounter;
+ mLastBatteryVoltage = mHealthInfo.batteryVoltageMillivolts;
+ mLastBatteryTemperature = mHealthInfo.batteryTemperatureTenthsCelsius;
+ mLastMaxChargingCurrent = mHealthInfo.maxChargingCurrentMicroamps;
+ mLastMaxChargingVoltage = mHealthInfo.maxChargingVoltageMicrovolts;
+ mLastChargeCounter = mHealthInfo.batteryChargeCounterUah;
mLastBatteryLevelCritical = mBatteryLevelCritical;
mLastInvalidCharger = mInvalidCharger;
}
@@ -718,13 +687,17 @@ public final class BatteryService extends SystemService {
intent.putExtra(BatteryManager.EXTRA_SCALE, BATTERY_SCALE);
intent.putExtra(BatteryManager.EXTRA_ICON_SMALL, icon);
intent.putExtra(BatteryManager.EXTRA_PLUGGED, mPlugType);
- intent.putExtra(BatteryManager.EXTRA_VOLTAGE, mHealthInfo.batteryVoltage);
- intent.putExtra(BatteryManager.EXTRA_TEMPERATURE, mHealthInfo.batteryTemperature);
+ intent.putExtra(BatteryManager.EXTRA_VOLTAGE, mHealthInfo.batteryVoltageMillivolts);
+ intent.putExtra(
+ BatteryManager.EXTRA_TEMPERATURE, mHealthInfo.batteryTemperatureTenthsCelsius);
intent.putExtra(BatteryManager.EXTRA_TECHNOLOGY, mHealthInfo.batteryTechnology);
intent.putExtra(BatteryManager.EXTRA_INVALID_CHARGER, mInvalidCharger);
- intent.putExtra(BatteryManager.EXTRA_MAX_CHARGING_CURRENT, mHealthInfo.maxChargingCurrent);
- intent.putExtra(BatteryManager.EXTRA_MAX_CHARGING_VOLTAGE, mHealthInfo.maxChargingVoltage);
- intent.putExtra(BatteryManager.EXTRA_CHARGE_COUNTER, mHealthInfo.batteryChargeCounter);
+ intent.putExtra(
+ BatteryManager.EXTRA_MAX_CHARGING_CURRENT, mHealthInfo.maxChargingCurrentMicroamps);
+ intent.putExtra(
+ BatteryManager.EXTRA_MAX_CHARGING_VOLTAGE,
+ mHealthInfo.maxChargingVoltageMicrovolts);
+ intent.putExtra(BatteryManager.EXTRA_CHARGE_COUNTER, mHealthInfo.batteryChargeCounterUah);
if (DEBUG) {
Slog.d(TAG, "Sending ACTION_BATTERY_CHANGED. scale:" + BATTERY_SCALE
+ ", info:" + mHealthInfo.toString());
@@ -744,9 +717,9 @@ public final class BatteryService extends SystemService {
event.putBoolean(BatteryManager.EXTRA_BATTERY_LOW, mSentLowBatteryBroadcast);
event.putInt(BatteryManager.EXTRA_SCALE, BATTERY_SCALE);
event.putInt(BatteryManager.EXTRA_PLUGGED, mPlugType);
- event.putInt(BatteryManager.EXTRA_VOLTAGE, mHealthInfo.batteryVoltage);
- event.putInt(BatteryManager.EXTRA_TEMPERATURE, mHealthInfo.batteryTemperature);
- event.putInt(BatteryManager.EXTRA_CHARGE_COUNTER, mHealthInfo.batteryChargeCounter);
+ event.putInt(BatteryManager.EXTRA_VOLTAGE, mHealthInfo.batteryVoltageMillivolts);
+ event.putInt(BatteryManager.EXTRA_TEMPERATURE, mHealthInfo.batteryTemperatureTenthsCelsius);
+ event.putInt(BatteryManager.EXTRA_CHARGE_COUNTER, mHealthInfo.batteryChargeCounterUah);
event.putLong(BatteryManager.EXTRA_EVENT_TIMESTAMP, now);
boolean queueWasEmpty = mBatteryLevelsEventQueue.isEmpty();
@@ -938,7 +911,7 @@ public final class BatteryService extends SystemService {
}
try {
if (!mUpdatesStopped) {
- copy(mLastHealthInfo, mHealthInfo);
+ copyV1Battery(mLastHealthInfo, mHealthInfo);
}
boolean update = true;
switch (key) {
@@ -961,10 +934,10 @@ public final class BatteryService extends SystemService {
mHealthInfo.batteryLevel = Integer.parseInt(value);
break;
case "counter":
- mHealthInfo.batteryChargeCounter = Integer.parseInt(value);
+ mHealthInfo.batteryChargeCounterUah = Integer.parseInt(value);
break;
case "temp":
- mHealthInfo.batteryTemperature = Integer.parseInt(value);
+ mHealthInfo.batteryTemperatureTenthsCelsius = Integer.parseInt(value);
break;
case "invalid":
mInvalidCharger = Integer.parseInt(value);
@@ -1008,7 +981,7 @@ public final class BatteryService extends SystemService {
private void setChargerAcOnline(boolean online, boolean forceUpdate) {
if (!mUpdatesStopped) {
- copy(mLastHealthInfo, mHealthInfo);
+ copyV1Battery(mLastHealthInfo, mHealthInfo);
}
mHealthInfo.chargerAcOnline = online;
mUpdatesStopped = true;
@@ -1017,7 +990,7 @@ public final class BatteryService extends SystemService {
private void setBatteryLevel(int level, boolean forceUpdate) {
if (!mUpdatesStopped) {
- copy(mLastHealthInfo, mHealthInfo);
+ copyV1Battery(mLastHealthInfo, mHealthInfo);
}
mHealthInfo.batteryLevel = level;
mUpdatesStopped = true;
@@ -1026,7 +999,7 @@ public final class BatteryService extends SystemService {
private void unplugBattery(boolean forceUpdate, PrintWriter pw) {
if (!mUpdatesStopped) {
- copy(mLastHealthInfo, mHealthInfo);
+ copyV1Battery(mLastHealthInfo, mHealthInfo);
}
mHealthInfo.chargerAcOnline = false;
mHealthInfo.chargerUsbOnline = false;
@@ -1038,7 +1011,7 @@ public final class BatteryService extends SystemService {
private void resetBattery(boolean forceUpdate, @Nullable PrintWriter pw) {
if (mUpdatesStopped) {
mUpdatesStopped = false;
- copy(mHealthInfo, mLastHealthInfo);
+ copyV1Battery(mHealthInfo, mLastHealthInfo);
Binder.withCleanCallingIdentity(() -> processValuesLocked(forceUpdate, pw));
}
if (mBatteryInputSuspended) {
@@ -1073,16 +1046,16 @@ public final class BatteryService extends SystemService {
pw.println(" AC powered: " + mHealthInfo.chargerAcOnline);
pw.println(" USB powered: " + mHealthInfo.chargerUsbOnline);
pw.println(" Wireless powered: " + mHealthInfo.chargerWirelessOnline);
- pw.println(" Max charging current: " + mHealthInfo.maxChargingCurrent);
- pw.println(" Max charging voltage: " + mHealthInfo.maxChargingVoltage);
- pw.println(" Charge counter: " + mHealthInfo.batteryChargeCounter);
+ pw.println(" Max charging current: " + mHealthInfo.maxChargingCurrentMicroamps);
+ pw.println(" Max charging voltage: " + mHealthInfo.maxChargingVoltageMicrovolts);
+ pw.println(" Charge counter: " + mHealthInfo.batteryChargeCounterUah);
pw.println(" status: " + mHealthInfo.batteryStatus);
pw.println(" health: " + mHealthInfo.batteryHealth);
pw.println(" present: " + mHealthInfo.batteryPresent);
pw.println(" level: " + mHealthInfo.batteryLevel);
pw.println(" scale: " + BATTERY_SCALE);
- pw.println(" voltage: " + mHealthInfo.batteryVoltage);
- pw.println(" temperature: " + mHealthInfo.batteryTemperature);
+ pw.println(" voltage: " + mHealthInfo.batteryVoltageMillivolts);
+ pw.println(" temperature: " + mHealthInfo.batteryTemperatureTenthsCelsius);
pw.println(" technology: " + mHealthInfo.batteryTechnology);
} else {
Shell shell = new Shell();
@@ -1105,16 +1078,23 @@ public final class BatteryService extends SystemService {
batteryPluggedValue = OsProtoEnums.BATTERY_PLUGGED_WIRELESS;
}
proto.write(BatteryServiceDumpProto.PLUGGED, batteryPluggedValue);
- proto.write(BatteryServiceDumpProto.MAX_CHARGING_CURRENT, mHealthInfo.maxChargingCurrent);
- proto.write(BatteryServiceDumpProto.MAX_CHARGING_VOLTAGE, mHealthInfo.maxChargingVoltage);
- proto.write(BatteryServiceDumpProto.CHARGE_COUNTER, mHealthInfo.batteryChargeCounter);
+ proto.write(
+ BatteryServiceDumpProto.MAX_CHARGING_CURRENT,
+ mHealthInfo.maxChargingCurrentMicroamps);
+ proto.write(
+ BatteryServiceDumpProto.MAX_CHARGING_VOLTAGE,
+ mHealthInfo.maxChargingVoltageMicrovolts);
+ proto.write(
+ BatteryServiceDumpProto.CHARGE_COUNTER, mHealthInfo.batteryChargeCounterUah);
proto.write(BatteryServiceDumpProto.STATUS, mHealthInfo.batteryStatus);
proto.write(BatteryServiceDumpProto.HEALTH, mHealthInfo.batteryHealth);
proto.write(BatteryServiceDumpProto.IS_PRESENT, mHealthInfo.batteryPresent);
proto.write(BatteryServiceDumpProto.LEVEL, mHealthInfo.batteryLevel);
proto.write(BatteryServiceDumpProto.SCALE, BATTERY_SCALE);
- proto.write(BatteryServiceDumpProto.VOLTAGE, mHealthInfo.batteryVoltage);
- proto.write(BatteryServiceDumpProto.TEMPERATURE, mHealthInfo.batteryTemperature);
+ proto.write(BatteryServiceDumpProto.VOLTAGE, mHealthInfo.batteryVoltageMillivolts);
+ proto.write(
+ BatteryServiceDumpProto.TEMPERATURE,
+ mHealthInfo.batteryTemperatureTenthsCelsius);
proto.write(BatteryServiceDumpProto.TECHNOLOGY, mHealthInfo.batteryTechnology);
}
proto.flush();
@@ -1186,64 +1166,6 @@ public final class BatteryService extends SystemService {
}
}
- private final class HealthHalCallback extends IHealthInfoCallback.Stub
- implements HealthServiceWrapper.Callback {
- @Override public void healthInfoChanged(android.hardware.health.V2_0.HealthInfo props) {
- android.hardware.health.V2_1.HealthInfo propsLatest =
- new android.hardware.health.V2_1.HealthInfo();
- propsLatest.legacy = props;
-
- propsLatest.batteryCapacityLevel = BatteryCapacityLevel.UNSUPPORTED;
- propsLatest.batteryChargeTimeToFullNowSeconds =
- Constants.BATTERY_CHARGE_TIME_TO_FULL_NOW_SECONDS_UNSUPPORTED;
-
- BatteryService.this.update(propsLatest);
- }
-
- @Override public void healthInfoChanged_2_1(android.hardware.health.V2_1.HealthInfo props) {
- BatteryService.this.update(props);
- }
-
- // on new service registered
- @Override public void onRegistration(IHealth oldService, IHealth newService,
- String instance) {
- if (newService == null) return;
-
- traceBegin("HealthUnregisterCallback");
- try {
- if (oldService != null) {
- int r = oldService.unregisterCallback(this);
- if (r != Result.SUCCESS) {
- Slog.w(TAG, "health: cannot unregister previous callback: " +
- Result.toString(r));
- }
- }
- } catch (RemoteException ex) {
- Slog.w(TAG, "health: cannot unregister previous callback (transaction error): "
- + ex.getMessage());
- } finally {
- traceEnd();
- }
-
- traceBegin("HealthRegisterCallback");
- try {
- int r = newService.registerCallback(this);
- if (r != Result.SUCCESS) {
- Slog.w(TAG, "health: cannot register callback: " + Result.toString(r));
- return;
- }
- // registerCallback does NOT guarantee that update is called
- // immediately, so request a manual update here.
- newService.update();
- } catch (RemoteException ex) {
- Slog.e(TAG, "health: cannot register callback (transaction error): "
- + ex.getMessage());
- } finally {
- traceEnd();
- }
- }
- }
-
private final class BinderService extends Binder {
@Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
@@ -1267,71 +1189,11 @@ public final class BatteryService extends SystemService {
private final class BatteryPropertiesRegistrar extends IBatteryPropertiesRegistrar.Stub {
@Override
public int getProperty(int id, final BatteryProperty prop) throws RemoteException {
- traceBegin("HealthGetProperty");
- try {
- IHealth service = mHealthServiceWrapper.getLastService();
- if (service == null) throw new RemoteException("no health service");
- final MutableInt outResult = new MutableInt(Result.NOT_SUPPORTED);
- switch(id) {
- case BatteryManager.BATTERY_PROPERTY_CHARGE_COUNTER:
- service.getChargeCounter((int result, int value) -> {
- outResult.value = result;
- if (result == Result.SUCCESS) prop.setLong(value);
- });
- break;
- case BatteryManager.BATTERY_PROPERTY_CURRENT_NOW:
- service.getCurrentNow((int result, int value) -> {
- outResult.value = result;
- if (result == Result.SUCCESS) prop.setLong(value);
- });
- break;
- case BatteryManager.BATTERY_PROPERTY_CURRENT_AVERAGE:
- service.getCurrentAverage((int result, int value) -> {
- outResult.value = result;
- if (result == Result.SUCCESS) prop.setLong(value);
- });
- break;
- case BatteryManager.BATTERY_PROPERTY_CAPACITY:
- service.getCapacity((int result, int value) -> {
- outResult.value = result;
- if (result == Result.SUCCESS) prop.setLong(value);
- });
- break;
- case BatteryManager.BATTERY_PROPERTY_STATUS:
- service.getChargeStatus((int result, int value) -> {
- outResult.value = result;
- if (result == Result.SUCCESS) prop.setLong(value);
- });
- break;
- case BatteryManager.BATTERY_PROPERTY_ENERGY_COUNTER:
- service.getEnergyCounter((int result, long value) -> {
- outResult.value = result;
- if (result == Result.SUCCESS) prop.setLong(value);
- });
- break;
- }
- return outResult.value;
- } finally {
- traceEnd();
- }
+ return mHealthServiceWrapper.getProperty(id, prop);
}
@Override
public void scheduleUpdate() throws RemoteException {
- mHealthServiceWrapper.getHandlerThread().getThreadHandler().post(() -> {
- traceBegin("HealthScheduleUpdate");
- try {
- IHealth service = mHealthServiceWrapper.getLastService();
- if (service == null) {
- Slog.e(TAG, "no health service");
- return;
- }
- service.update();
- } catch (RemoteException ex) {
- Slog.e(TAG, "Cannot call update on health HAL", ex);
- } finally {
- traceEnd();
- }
- });
+ mHealthServiceWrapper.scheduleUpdate();
}
}
@@ -1360,14 +1222,14 @@ public final class BatteryService extends SystemService {
@Override
public int getBatteryChargeCounter() {
synchronized (mLock) {
- return mHealthInfo.batteryChargeCounter;
+ return mHealthInfo.batteryChargeCounterUah;
}
}
@Override
public int getBatteryFullCharge() {
synchronized (mLock) {
- return mHealthInfo.batteryFullCharge;
+ return mHealthInfo.batteryFullChargeUah;
}
}
@@ -1420,191 +1282,4 @@ public final class BatteryService extends SystemService {
BatteryService.this.suspendBatteryInput();
}
}
-
- /**
- * HealthServiceWrapper wraps the internal IHealth service and refreshes the service when
- * necessary.
- *
- * On new registration of IHealth service, {@link #onRegistration onRegistration} is called and
- * the internal service is refreshed.
- * On death of an existing IHealth service, the internal service is NOT cleared to avoid
- * race condition between death notification and new service notification. Hence,
- * a caller must check for transaction errors when calling into the service.
- *
- * @hide Should only be used internally.
- */
- public static final class HealthServiceWrapper {
- private static final String TAG = "HealthServiceWrapper";
- public static final String INSTANCE_HEALTHD = "backup";
- public static final String INSTANCE_VENDOR = "default";
- // All interesting instances, sorted by priority high -> low.
- private static final List<String> sAllInstances =
- Arrays.asList(INSTANCE_VENDOR, INSTANCE_HEALTHD);
-
- private final IServiceNotification mNotification = new Notification();
- private final HandlerThread mHandlerThread = new HandlerThread("HealthServiceHwbinder");
- // These variables are fixed after init.
- private Callback mCallback;
- private IHealthSupplier mHealthSupplier;
- private String mInstanceName;
-
- // Last IHealth service received.
- private final AtomicReference<IHealth> mLastService = new AtomicReference<>();
-
- /**
- * init should be called after constructor. For testing purposes, init is not called by
- * constructor.
- */
- public HealthServiceWrapper() {
- }
-
- public IHealth getLastService() {
- return mLastService.get();
- }
-
- /**
- * See {@link #init(Callback, IServiceManagerSupplier, IHealthSupplier)}
- */
- public void init() throws RemoteException, NoSuchElementException {
- init(/* callback= */null, new HealthServiceWrapper.IServiceManagerSupplier() {},
- new HealthServiceWrapper.IHealthSupplier() {});
- }
-
- /**
- * Start monitoring registration of new IHealth services. Only instances that are in
- * {@code sAllInstances} and in device / framework manifest are used. This function should
- * only be called once.
- *
- * mCallback.onRegistration() is called synchronously (aka in init thread) before
- * this method returns if callback is not null.
- *
- * @throws RemoteException transaction error when talking to IServiceManager
- * @throws NoSuchElementException if one of the following cases:
- * - No service manager;
- * - none of {@code sAllInstances} are in manifests (i.e. not
- * available on this device), or none of these instances are available to current
- * process.
- * @throws NullPointerException when supplier is null
- */
- void init(@Nullable Callback callback,
- IServiceManagerSupplier managerSupplier,
- IHealthSupplier healthSupplier)
- throws RemoteException, NoSuchElementException, NullPointerException {
- if (managerSupplier == null || healthSupplier == null) {
- throw new NullPointerException();
- }
- IServiceManager manager;
-
- mHealthSupplier = healthSupplier;
-
- // Initialize mLastService and call callback for the first time (in init thread)
- IHealth newService = null;
- for (String name : sAllInstances) {
- traceBegin("HealthInitGetService_" + name);
- try {
- newService = healthSupplier.get(name);
- } catch (NoSuchElementException ex) {
- /* ignored, handled below */
- } finally {
- traceEnd();
- }
- if (newService != null) {
- mInstanceName = name;
- mLastService.set(newService);
- break;
- }
- }
-
- if (mInstanceName == null || newService == null) {
- throw new NoSuchElementException(String.format(
- "No IHealth service instance among %s is available. Perhaps no permission?",
- sAllInstances.toString()));
- }
-
- if (callback != null) {
- mCallback = callback;
- mCallback.onRegistration(null, newService, mInstanceName);
- }
-
- // Register for future service registrations
- traceBegin("HealthInitRegisterNotification");
- mHandlerThread.start();
- try {
- managerSupplier.get().registerForNotifications(
- IHealth.kInterfaceName, mInstanceName, mNotification);
- } finally {
- traceEnd();
- }
- Slog.i(TAG, "health: HealthServiceWrapper listening to instance " + mInstanceName);
- }
-
- @VisibleForTesting
- HandlerThread getHandlerThread() {
- return mHandlerThread;
- }
-
- interface Callback {
- /**
- * This function is invoked asynchronously when a new and related IServiceNotification
- * is received.
- * @param service the recently retrieved service from IServiceManager.
- * Can be a dead service before service notification of a new service is delivered.
- * Implementation must handle cases for {@link RemoteException}s when calling
- * into service.
- * @param instance instance name.
- */
- void onRegistration(IHealth oldService, IHealth newService, String instance);
- }
-
- /**
- * Supplier of services.
- * Must not return null; throw {@link NoSuchElementException} if a service is not available.
- */
- interface IServiceManagerSupplier {
- default IServiceManager get() throws NoSuchElementException, RemoteException {
- return IServiceManager.getService();
- }
- }
- /**
- * Supplier of services.
- * Must not return null; throw {@link NoSuchElementException} if a service is not available.
- */
- interface IHealthSupplier {
- default IHealth get(String name) throws NoSuchElementException, RemoteException {
- return IHealth.getService(name, true /* retry */);
- }
- }
-
- private class Notification extends IServiceNotification.Stub {
- @Override
- public final void onRegistration(String interfaceName, String instanceName,
- boolean preexisting) {
- if (!IHealth.kInterfaceName.equals(interfaceName)) return;
- if (!mInstanceName.equals(instanceName)) return;
-
- // This runnable only runs on mHandlerThread and ordering is ensured, hence
- // no locking is needed inside the runnable.
- mHandlerThread.getThreadHandler().post(new Runnable() {
- @Override
- public void run() {
- try {
- IHealth newService = mHealthSupplier.get(mInstanceName);
- IHealth oldService = mLastService.getAndSet(newService);
-
- // preexisting may be inaccurate (race). Check for equality here.
- if (Objects.equals(newService, oldService)) return;
-
- Slog.i(TAG, "health: new instance registered " + mInstanceName);
- // #init() may be called with null callback. Skip null callbacks.
- if (mCallback == null) return;
- mCallback.onRegistration(oldService, newService, mInstanceName);
- } catch (NoSuchElementException | RemoteException ex) {
- Slog.e(TAG, "health: Cannot get instance '" + mInstanceName
- + "': " + ex.getMessage() + ". Perhaps no permission?");
- }
- }
- });
- }
- }
- }
}
diff --git a/services/core/java/com/android/server/BluetoothAirplaneModeListener.java b/services/core/java/com/android/server/BluetoothAirplaneModeListener.java
deleted file mode 100644
index 197321f1cb6a..000000000000
--- a/services/core/java/com/android/server/BluetoothAirplaneModeListener.java
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * Copyright 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 com.android.server;
-
-import android.annotation.RequiresPermission;
-import android.content.Context;
-import android.database.ContentObserver;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.provider.Settings;
-import android.util.Log;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-/**
- * The BluetoothAirplaneModeListener handles system airplane mode change callback and checks
- * whether we need to inform BluetoothManagerService on this change.
- *
- * The information of airplane mode turns on would not be passed to the BluetoothManagerService
- * when Bluetooth is on and Bluetooth is in one of the following situations:
- * 1. Bluetooth A2DP is connected.
- * 2. Bluetooth Hearing Aid profile is connected.
- */
-class BluetoothAirplaneModeListener {
- private static final String TAG = "BluetoothAirplaneModeListener";
- @VisibleForTesting static final String TOAST_COUNT = "bluetooth_airplane_toast_count";
-
- private static final int MSG_AIRPLANE_MODE_CHANGED = 0;
-
- @VisibleForTesting static final int MAX_TOAST_COUNT = 10; // 10 times
-
- private final BluetoothManagerService mBluetoothManager;
- private final BluetoothAirplaneModeHandler mHandler;
- private BluetoothModeChangeHelper mAirplaneHelper;
-
- @VisibleForTesting int mToastCount = 0;
-
- BluetoothAirplaneModeListener(BluetoothManagerService service, Looper looper, Context context) {
- mBluetoothManager = service;
-
- mHandler = new BluetoothAirplaneModeHandler(looper);
- context.getContentResolver().registerContentObserver(
- Settings.Global.getUriFor(Settings.Global.AIRPLANE_MODE_ON), true,
- mAirplaneModeObserver);
- }
-
- private final ContentObserver mAirplaneModeObserver = new ContentObserver(null) {
- @Override
- public void onChange(boolean unused) {
- // Post from system main thread to android_io thread.
- Message msg = mHandler.obtainMessage(MSG_AIRPLANE_MODE_CHANGED);
- mHandler.sendMessage(msg);
- }
- };
-
- private class BluetoothAirplaneModeHandler extends Handler {
- BluetoothAirplaneModeHandler(Looper looper) {
- super(looper);
- }
-
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_AIRPLANE_MODE_CHANGED:
- handleAirplaneModeChange();
- break;
- default:
- Log.e(TAG, "Invalid message: " + msg.what);
- break;
- }
- }
- }
-
- /**
- * Call after boot complete
- */
- @VisibleForTesting
- void start(BluetoothModeChangeHelper helper) {
- Log.i(TAG, "start");
- mAirplaneHelper = helper;
- mToastCount = mAirplaneHelper.getSettingsInt(TOAST_COUNT);
- }
-
- @VisibleForTesting
- boolean shouldPopToast() {
- if (mToastCount >= MAX_TOAST_COUNT) {
- return false;
- }
- mToastCount++;
- mAirplaneHelper.setSettingsInt(TOAST_COUNT, mToastCount);
- return true;
- }
-
- @VisibleForTesting
- @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
- void handleAirplaneModeChange() {
- if (shouldSkipAirplaneModeChange()) {
- Log.i(TAG, "Ignore airplane mode change");
- // We have to store Bluetooth state here, so if user turns off Bluetooth
- // after airplane mode is turned on, we don't forget to turn on Bluetooth
- // when airplane mode turns off.
- mAirplaneHelper.setSettingsInt(Settings.Global.BLUETOOTH_ON,
- BluetoothManagerService.BLUETOOTH_ON_AIRPLANE);
- if (shouldPopToast()) {
- mAirplaneHelper.showToastMessage();
- }
- return;
- }
- if (mAirplaneHelper != null) {
- mAirplaneHelper.onAirplaneModeChanged(mBluetoothManager);
- }
- }
-
- @VisibleForTesting
- boolean shouldSkipAirplaneModeChange() {
- if (mAirplaneHelper == null) {
- return false;
- }
- if (!mAirplaneHelper.isBluetoothOn() || !mAirplaneHelper.isAirplaneModeOn()
- || !mAirplaneHelper.isA2dpOrHearingAidConnected()) {
- return false;
- }
- return true;
- }
-}
diff --git a/services/core/java/com/android/server/BluetoothDeviceConfigListener.java b/services/core/java/com/android/server/BluetoothDeviceConfigListener.java
deleted file mode 100644
index 611a37de70f4..000000000000
--- a/services/core/java/com/android/server/BluetoothDeviceConfigListener.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * 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;
-
-import android.provider.DeviceConfig;
-import android.util.Slog;
-
-import java.util.ArrayList;
-
-/**
- * The BluetoothDeviceConfigListener handles system device config change callback and checks
- * whether we need to inform BluetoothManagerService on this change.
- *
- * The information of device config change would not be passed to the BluetoothManagerService
- * when Bluetooth is on and Bluetooth is in one of the following situations:
- * 1. Bluetooth A2DP is connected.
- * 2. Bluetooth Hearing Aid profile is connected.
- */
-class BluetoothDeviceConfigListener {
- private static final String TAG = "BluetoothDeviceConfigListener";
-
- private final BluetoothManagerService mService;
- private final boolean mLogDebug;
-
- BluetoothDeviceConfigListener(BluetoothManagerService service, boolean logDebug) {
- mService = service;
- mLogDebug = logDebug;
- DeviceConfig.addOnPropertiesChangedListener(
- DeviceConfig.NAMESPACE_BLUETOOTH,
- (Runnable r) -> r.run(),
- mDeviceConfigChangedListener);
- }
-
- private final DeviceConfig.OnPropertiesChangedListener mDeviceConfigChangedListener =
- new DeviceConfig.OnPropertiesChangedListener() {
- @Override
- public void onPropertiesChanged(DeviceConfig.Properties properties) {
- if (!properties.getNamespace().equals(DeviceConfig.NAMESPACE_BLUETOOTH)) {
- return;
- }
- if (mLogDebug) {
- ArrayList<String> flags = new ArrayList<>();
- for (String name : properties.getKeyset()) {
- flags.add(name + "='" + properties.getString(name, "") + "'");
- }
- Slog.d(TAG, "onPropertiesChanged: " + String.join(",", flags));
- }
- boolean foundInit = false;
- for (String name : properties.getKeyset()) {
- if (name.startsWith("INIT_")) {
- foundInit = true;
- break;
- }
- }
- if (!foundInit) {
- return;
- }
- mService.onInitFlagsChanged();
- }
- };
-
-}
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
deleted file mode 100644
index ff24c6f16c42..000000000000
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ /dev/null
@@ -1,2970 +0,0 @@
-/*
- * Copyright (C) 2012 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;
-
-import static android.Manifest.permission.BLUETOOTH_CONNECT;
-import static android.content.PermissionChecker.PERMISSION_HARD_DENIED;
-import static android.content.PermissionChecker.PID_UNKNOWN;
-import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
-import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-import static android.os.PowerExemptionManager.TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
-import static android.os.UserHandle.USER_SYSTEM;
-
-import android.Manifest;
-import android.annotation.NonNull;
-import android.annotation.RequiresPermission;
-import android.annotation.SuppressLint;
-import android.app.ActivityManager;
-import android.app.AppGlobals;
-import android.app.AppOpsManager;
-import android.app.BroadcastOptions;
-import android.bluetooth.BluetoothA2dp;
-import android.bluetooth.BluetoothAdapter;
-import android.bluetooth.BluetoothHearingAid;
-import android.bluetooth.BluetoothProfile;
-import android.bluetooth.BluetoothProtoEnums;
-import android.bluetooth.IBluetooth;
-import android.bluetooth.IBluetoothCallback;
-import android.bluetooth.IBluetoothGatt;
-import android.bluetooth.IBluetoothHeadset;
-import android.bluetooth.IBluetoothManager;
-import android.bluetooth.IBluetoothManagerCallback;
-import android.bluetooth.IBluetoothProfileServiceConnection;
-import android.bluetooth.IBluetoothStateChangeCallback;
-import android.content.ActivityNotFoundException;
-import android.content.AttributionSource;
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.PermissionChecker;
-import android.content.ServiceConnection;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.IPackageManager;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManagerInternal;
-import android.content.pm.UserInfo;
-import android.database.ContentObserver;
-import android.os.Binder;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
-import android.os.PowerExemptionManager;
-import android.os.Process;
-import android.os.RemoteCallbackList;
-import android.os.RemoteException;
-import android.os.SystemClock;
-import android.os.SystemProperties;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.provider.Settings;
-import android.provider.Settings.SettingNotFoundException;
-import android.text.TextUtils;
-import android.util.FeatureFlagUtils;
-import android.util.Log;
-import android.util.Slog;
-import android.util.proto.ProtoOutputStream;
-
-import com.android.internal.R;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.DumpUtils;
-import com.android.internal.util.FrameworkStatsLog;
-import com.android.server.pm.UserManagerInternal;
-import com.android.server.pm.UserManagerInternal.UserRestrictionsListener;
-import com.android.server.pm.UserRestrictionsUtils;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.Locale;
-import java.util.Map;
-import java.util.NoSuchElementException;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.locks.ReentrantReadWriteLock;
-
-class BluetoothManagerService extends IBluetoothManager.Stub {
- private static final String TAG = "BluetoothManagerService";
- private static final boolean DBG = true;
-
- private static final String BLUETOOTH_PRIVILEGED =
- android.Manifest.permission.BLUETOOTH_PRIVILEGED;
-
- private static final int ACTIVE_LOG_MAX_SIZE = 20;
- private static final int CRASH_LOG_MAX_SIZE = 100;
-
- private static final int TIMEOUT_BIND_MS = 3000; //Maximum msec to wait for a bind
- //Maximum msec to wait for service restart
- private static final int SERVICE_RESTART_TIME_MS = 400;
- //Maximum msec to wait for restart due to error
- private static final int ERROR_RESTART_TIME_MS = 3000;
- //Maximum msec to delay MESSAGE_USER_SWITCHED
- private static final int USER_SWITCHED_TIME_MS = 200;
- // Delay for the addProxy function in msec
- private static final int ADD_PROXY_DELAY_MS = 100;
- // Delay for retrying enable and disable in msec
- private static final int ENABLE_DISABLE_DELAY_MS = 300;
- private static final int DELAY_BEFORE_RESTART_DUE_TO_INIT_FLAGS_CHANGED_MS = 300;
- private static final int DELAY_FOR_RETRY_INIT_FLAG_CHECK_MS = 86400;
-
- private static final int MESSAGE_ENABLE = 1;
- private static final int MESSAGE_DISABLE = 2;
- private static final int MESSAGE_HANDLE_ENABLE_DELAYED = 3;
- private static final int MESSAGE_HANDLE_DISABLE_DELAYED = 4;
- private static final int MESSAGE_REGISTER_STATE_CHANGE_CALLBACK = 30;
- private static final int MESSAGE_UNREGISTER_STATE_CHANGE_CALLBACK = 31;
- private static final int MESSAGE_BLUETOOTH_SERVICE_CONNECTED = 40;
- private static final int MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED = 41;
- private static final int MESSAGE_RESTART_BLUETOOTH_SERVICE = 42;
- private static final int MESSAGE_BLUETOOTH_STATE_CHANGE = 60;
- private static final int MESSAGE_TIMEOUT_BIND = 100;
- private static final int MESSAGE_TIMEOUT_UNBIND = 101;
- private static final int MESSAGE_GET_NAME_AND_ADDRESS = 200;
- private static final int MESSAGE_USER_SWITCHED = 300;
- private static final int MESSAGE_USER_UNLOCKED = 301;
- private static final int MESSAGE_ADD_PROXY_DELAYED = 400;
- private static final int MESSAGE_BIND_PROFILE_SERVICE = 401;
- private static final int MESSAGE_RESTORE_USER_SETTING = 500;
- private static final int MESSAGE_INIT_FLAGS_CHANGED = 600;
-
- private static final int RESTORE_SETTING_TO_ON = 1;
- private static final int RESTORE_SETTING_TO_OFF = 0;
-
- private static final int MAX_ERROR_RESTART_RETRIES = 6;
- private static final int MAX_WAIT_FOR_ENABLE_DISABLE_RETRIES = 10;
-
- // Bluetooth persisted setting is off
- private static final int BLUETOOTH_OFF = 0;
- // Bluetooth persisted setting is on
- // and Airplane mode won't affect Bluetooth state at start up
- private static final int BLUETOOTH_ON_BLUETOOTH = 1;
- // Bluetooth persisted setting is on
- // but Airplane mode will affect Bluetooth state at start up
- // and Airplane mode will have higher priority.
- @VisibleForTesting
- static final int BLUETOOTH_ON_AIRPLANE = 2;
-
- private static final int SERVICE_IBLUETOOTH = 1;
- private static final int SERVICE_IBLUETOOTHGATT = 2;
-
- private final Context mContext;
-
- // Locks are not provided for mName and mAddress.
- // They are accessed in handler or broadcast receiver, same thread context.
- private String mAddress;
- private String mName;
- private final ContentResolver mContentResolver;
- private final int mUserId;
- private final RemoteCallbackList<IBluetoothManagerCallback> mCallbacks;
- private final RemoteCallbackList<IBluetoothStateChangeCallback> mStateChangeCallbacks;
- private IBinder mBluetoothBinder;
- private IBluetooth mBluetooth;
- private IBluetoothGatt mBluetoothGatt;
- private final ReentrantReadWriteLock mBluetoothLock = new ReentrantReadWriteLock();
- private boolean mBinding;
- private boolean mUnbinding;
-
- private BluetoothModeChangeHelper mBluetoothModeChangeHelper;
-
- private BluetoothAirplaneModeListener mBluetoothAirplaneModeListener;
-
- private BluetoothDeviceConfigListener mBluetoothDeviceConfigListener;
-
- // used inside handler thread
- private boolean mQuietEnable = false;
- private boolean mEnable;
-
- private static CharSequence timeToLog(long timestamp) {
- return android.text.format.DateFormat.format("MM-dd HH:mm:ss", timestamp);
- }
-
- /**
- * Used for tracking apps that enabled / disabled Bluetooth.
- */
- private class ActiveLog {
- private int mReason;
- private String mPackageName;
- private boolean mEnable;
- private long mTimestamp;
-
- ActiveLog(int reason, String packageName, boolean enable, long timestamp) {
- mReason = reason;
- mPackageName = packageName;
- mEnable = enable;
- mTimestamp = timestamp;
- }
-
- public String toString() {
- return timeToLog(mTimestamp) + (mEnable ? " Enabled " : " Disabled ")
- + " due to " + getEnableDisableReasonString(mReason) + " by " + mPackageName;
- }
-
- void dump(ProtoOutputStream proto) {
- proto.write(BluetoothManagerServiceDumpProto.ActiveLog.TIMESTAMP_MS, mTimestamp);
- proto.write(BluetoothManagerServiceDumpProto.ActiveLog.ENABLE, mEnable);
- proto.write(BluetoothManagerServiceDumpProto.ActiveLog.PACKAGE_NAME, mPackageName);
- proto.write(BluetoothManagerServiceDumpProto.ActiveLog.REASON, mReason);
- }
- }
-
- private final LinkedList<ActiveLog> mActiveLogs = new LinkedList<>();
- private final LinkedList<Long> mCrashTimestamps = new LinkedList<>();
- private int mCrashes;
- private long mLastEnabledTime;
-
- // configuration from external IBinder call which is used to
- // synchronize with broadcast receiver.
- private boolean mQuietEnableExternal;
- private boolean mEnableExternal;
-
- // Map of apps registered to keep BLE scanning on.
- private Map<IBinder, ClientDeathRecipient> mBleApps =
- new ConcurrentHashMap<IBinder, ClientDeathRecipient>();
-
- private int mState;
- private final BluetoothHandler mHandler;
- private int mErrorRecoveryRetryCounter;
- private final int mSystemUiUid;
-
- private boolean mIsHearingAidProfileSupported;
-
- private AppOpsManager mAppOps;
-
- // Save a ProfileServiceConnections object for each of the bound
- // bluetooth profile services
- private final Map<Integer, ProfileServiceConnections> mProfileServices = new HashMap<>();
-
- private final boolean mWirelessConsentRequired;
-
- private final IBluetoothCallback mBluetoothCallback = new IBluetoothCallback.Stub() {
- @Override
- public void onBluetoothStateChange(int prevState, int newState) throws RemoteException {
- Message msg =
- mHandler.obtainMessage(MESSAGE_BLUETOOTH_STATE_CHANGE, prevState, newState);
- mHandler.sendMessage(msg);
- }
- };
-
- private final UserRestrictionsListener mUserRestrictionsListener =
- new UserRestrictionsListener() {
- @Override
- public void onUserRestrictionsChanged(int userId, Bundle newRestrictions,
- Bundle prevRestrictions) {
-
- if (UserRestrictionsUtils.restrictionsChanged(prevRestrictions, newRestrictions,
- UserManager.DISALLOW_BLUETOOTH_SHARING)) {
- updateOppLauncherComponentState(userId,
- newRestrictions.getBoolean(UserManager.DISALLOW_BLUETOOTH_SHARING));
- }
-
- // DISALLOW_BLUETOOTH can only be set by DO or PO on the system user.
- if (userId == USER_SYSTEM
- && UserRestrictionsUtils.restrictionsChanged(prevRestrictions,
- newRestrictions, UserManager.DISALLOW_BLUETOOTH)) {
- if (userId == USER_SYSTEM && newRestrictions.getBoolean(
- UserManager.DISALLOW_BLUETOOTH)) {
- updateOppLauncherComponentState(userId, true); // Sharing disallowed
- sendDisableMsg(BluetoothProtoEnums.ENABLE_DISABLE_REASON_DISALLOWED,
- mContext.getPackageName());
- } else {
- updateOppLauncherComponentState(userId, newRestrictions.getBoolean(
- UserManager.DISALLOW_BLUETOOTH_SHARING));
- }
- }
- }
- };
-
- @VisibleForTesting
- public void onInitFlagsChanged() {
- mHandler.removeMessages(MESSAGE_INIT_FLAGS_CHANGED);
- mHandler.sendEmptyMessageDelayed(
- MESSAGE_INIT_FLAGS_CHANGED,
- DELAY_BEFORE_RESTART_DUE_TO_INIT_FLAGS_CHANGED_MS);
- }
-
- public boolean onFactoryReset(AttributionSource attributionSource) {
- mContext.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED,
- "Need BLUETOOTH_PRIVILEGED permission");
-
- // Wait for stable state if bluetooth is temporary state.
- int state = getState();
- if (state == BluetoothAdapter.STATE_BLE_TURNING_ON
- || state == BluetoothAdapter.STATE_TURNING_ON
- || state == BluetoothAdapter.STATE_TURNING_OFF) {
- if (!waitForState(Set.of(BluetoothAdapter.STATE_BLE_ON, BluetoothAdapter.STATE_ON))) {
- return false;
- }
- }
-
- // Clear registered LE apps to force shut-off Bluetooth
- clearBleApps();
- state = getState();
- try {
- mBluetoothLock.readLock().lock();
- if (mBluetooth == null) {
- return false;
- }
- if (state == BluetoothAdapter.STATE_BLE_ON) {
- addActiveLog(
- BluetoothProtoEnums.ENABLE_DISABLE_REASON_FACTORY_RESET,
- mContext.getPackageName(), false);
- mBluetooth.onBrEdrDown(attributionSource);
- return true;
- } else if (state == BluetoothAdapter.STATE_ON) {
- addActiveLog(
- BluetoothProtoEnums.ENABLE_DISABLE_REASON_FACTORY_RESET,
- mContext.getPackageName(), false);
- mBluetooth.disable(attributionSource);
- return true;
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to shutdown Bluetooth", e);
- } finally {
- mBluetoothLock.readLock().unlock();
- }
- return false;
- }
-
- @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
- public void onAirplaneModeChanged() {
- synchronized (this) {
- if (isBluetoothPersistedStateOn()) {
- if (isAirplaneModeOn()) {
- persistBluetoothSetting(BLUETOOTH_ON_AIRPLANE);
- } else {
- persistBluetoothSetting(BLUETOOTH_ON_BLUETOOTH);
- }
- }
-
- int st = BluetoothAdapter.STATE_OFF;
- try {
- mBluetoothLock.readLock().lock();
- if (mBluetooth != null) {
- st = mBluetooth.getState();
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to call getState", e);
- return;
- } finally {
- mBluetoothLock.readLock().unlock();
- }
-
- Slog.d(TAG,
- "Airplane Mode change - current state: " + BluetoothAdapter.nameForState(
- st) + ", isAirplaneModeOn()=" + isAirplaneModeOn());
-
- if (isAirplaneModeOn()) {
- // Clear registered LE apps to force shut-off
- clearBleApps();
-
- // If state is BLE_ON make sure we trigger disableBLE
- if (st == BluetoothAdapter.STATE_BLE_ON) {
- try {
- mBluetoothLock.readLock().lock();
- if (mBluetooth != null) {
- addActiveLog(
- BluetoothProtoEnums.ENABLE_DISABLE_REASON_AIRPLANE_MODE,
- mContext.getPackageName(), false);
- mBluetooth.onBrEdrDown(mContext.getAttributionSource());
- mEnable = false;
- mEnableExternal = false;
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to call onBrEdrDown", e);
- } finally {
- mBluetoothLock.readLock().unlock();
- }
- } else if (st == BluetoothAdapter.STATE_ON) {
- sendDisableMsg(BluetoothProtoEnums.ENABLE_DISABLE_REASON_AIRPLANE_MODE,
- mContext.getPackageName());
- }
- } else if (mEnableExternal) {
- sendEnableMsg(mQuietEnableExternal,
- BluetoothProtoEnums.ENABLE_DISABLE_REASON_AIRPLANE_MODE,
- mContext.getPackageName());
- }
- }
- }
-
- private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- if (BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED.equals(action)) {
- String newName = intent.getStringExtra(BluetoothAdapter.EXTRA_LOCAL_NAME);
- if (DBG) {
- Slog.d(TAG, "Bluetooth Adapter name changed to " + newName + " by "
- + mContext.getPackageName());
- }
- if (newName != null) {
- storeNameAndAddress(newName, null);
- }
- } else if (BluetoothAdapter.ACTION_BLUETOOTH_ADDRESS_CHANGED.equals(action)) {
- String newAddress = intent.getStringExtra(BluetoothAdapter.EXTRA_BLUETOOTH_ADDRESS);
- if (newAddress != null) {
- if (DBG) {
- Slog.d(TAG, "Bluetooth Adapter address changed to " + newAddress);
- }
- storeNameAndAddress(null, newAddress);
- } else {
- if (DBG) {
- Slog.e(TAG, "No Bluetooth Adapter address parameter found");
- }
- }
- } else if (Intent.ACTION_SETTING_RESTORED.equals(action)) {
- final String name = intent.getStringExtra(Intent.EXTRA_SETTING_NAME);
- if (Settings.Global.BLUETOOTH_ON.equals(name)) {
- // The Bluetooth On state may be changed during system restore.
- final String prevValue =
- intent.getStringExtra(Intent.EXTRA_SETTING_PREVIOUS_VALUE);
- final String newValue = intent.getStringExtra(Intent.EXTRA_SETTING_NEW_VALUE);
-
- if (DBG) {
- Slog.d(TAG,
- "ACTION_SETTING_RESTORED with BLUETOOTH_ON, prevValue=" + prevValue
- + ", newValue=" + newValue);
- }
-
- if ((newValue != null) && (prevValue != null) && !prevValue.equals(newValue)) {
- Message msg = mHandler.obtainMessage(MESSAGE_RESTORE_USER_SETTING,
- newValue.equals("0") ? RESTORE_SETTING_TO_OFF
- : RESTORE_SETTING_TO_ON, 0);
- mHandler.sendMessage(msg);
- }
- }
- } else if (BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED.equals(action)
- || BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED.equals(action)) {
- final int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE,
- BluetoothProfile.STATE_CONNECTED);
- if (mHandler.hasMessages(MESSAGE_INIT_FLAGS_CHANGED)
- && state == BluetoothProfile.STATE_DISCONNECTED
- && !mBluetoothModeChangeHelper.isA2dpOrHearingAidConnected()) {
- Slog.i(TAG, "Device disconnected, reactivating pending flag changes");
- onInitFlagsChanged();
- }
- }
- }
- };
-
- BluetoothManagerService(Context context) {
- mHandler = new BluetoothHandler(IoThread.get().getLooper());
-
- mContext = context;
-
- mWirelessConsentRequired = context.getResources()
- .getBoolean(com.android.internal.R.bool.config_wirelessConsentRequired);
-
- mCrashes = 0;
- mBluetooth = null;
- mBluetoothBinder = null;
- mBluetoothGatt = null;
- mBinding = false;
- mUnbinding = false;
- mEnable = false;
- mState = BluetoothAdapter.STATE_OFF;
- mQuietEnableExternal = false;
- mEnableExternal = false;
- mAddress = null;
- mName = null;
- mErrorRecoveryRetryCounter = 0;
- mContentResolver = context.getContentResolver();
- mUserId = mContentResolver.getUserId();
- // Observe BLE scan only mode settings change.
- registerForBleScanModeChange();
- mCallbacks = new RemoteCallbackList<IBluetoothManagerCallback>();
- mStateChangeCallbacks = new RemoteCallbackList<IBluetoothStateChangeCallback>();
-
- mIsHearingAidProfileSupported = context.getResources()
- .getBoolean(com.android.internal.R.bool.config_hearing_aid_profile_supported);
-
- // TODO: We need a more generic way to initialize the persist keys of FeatureFlagUtils
- String value = SystemProperties.get(FeatureFlagUtils.PERSIST_PREFIX + FeatureFlagUtils.HEARING_AID_SETTINGS);
- if (!TextUtils.isEmpty(value)) {
- boolean isHearingAidEnabled = Boolean.parseBoolean(value);
- Log.v(TAG, "set feature flag HEARING_AID_SETTINGS to " + isHearingAidEnabled);
- FeatureFlagUtils.setEnabled(context, FeatureFlagUtils.HEARING_AID_SETTINGS, isHearingAidEnabled);
- if (isHearingAidEnabled && !mIsHearingAidProfileSupported) {
- // Overwrite to enable support by FeatureFlag
- mIsHearingAidProfileSupported = true;
- }
- }
-
- IntentFilter filter = new IntentFilter();
- filter.addAction(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED);
- filter.addAction(BluetoothAdapter.ACTION_BLUETOOTH_ADDRESS_CHANGED);
- filter.addAction(Intent.ACTION_SETTING_RESTORED);
- filter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
- filter.addAction(BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED);
- filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
- mContext.registerReceiver(mReceiver, filter);
-
- loadStoredNameAndAddress();
- if (isBluetoothPersistedStateOn()) {
- if (DBG) {
- Slog.d(TAG, "Startup: Bluetooth persisted state is ON.");
- }
- mEnableExternal = true;
- }
-
- String airplaneModeRadios =
- Settings.Global.getString(mContentResolver, Settings.Global.AIRPLANE_MODE_RADIOS);
- if (airplaneModeRadios == null || airplaneModeRadios.contains(
- Settings.Global.RADIO_BLUETOOTH)) {
- mBluetoothAirplaneModeListener = new BluetoothAirplaneModeListener(
- this, IoThread.get().getLooper(), context);
- }
-
- int systemUiUid = -1;
- // Check if device is configured with no home screen, which implies no SystemUI.
- boolean noHome = mContext.getResources().getBoolean(R.bool.config_noHomeScreen);
- if (!noHome) {
- PackageManagerInternal pm = LocalServices.getService(PackageManagerInternal.class);
- systemUiUid = pm.getPackageUid(pm.getSystemUiServiceComponent().getPackageName(),
- MATCH_SYSTEM_ONLY, USER_SYSTEM);
- }
- if (systemUiUid >= 0) {
- Slog.d(TAG, "Detected SystemUiUid: " + Integer.toString(systemUiUid));
- } else {
- // Some platforms, such as wearables do not have a system ui.
- Slog.w(TAG, "Unable to resolve SystemUI's UID.");
- }
- mSystemUiUid = systemUiUid;
- }
-
- /**
- * Returns true if airplane mode is currently on
- */
- private boolean isAirplaneModeOn() {
- return Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.AIRPLANE_MODE_ON, 0) == 1;
- }
-
- private boolean supportBluetoothPersistedState() {
- return mContext.getResources().getBoolean(R.bool.config_supportBluetoothPersistedState);
- }
-
- /**
- * Returns true if the Bluetooth saved state is "on"
- */
- private boolean isBluetoothPersistedStateOn() {
- if (!supportBluetoothPersistedState()) {
- return false;
- }
- int state = Settings.Global.getInt(mContentResolver, Settings.Global.BLUETOOTH_ON, -1);
- if (DBG) {
- Slog.d(TAG, "Bluetooth persisted state: " + state);
- }
- return state != BLUETOOTH_OFF;
- }
-
- private boolean isBluetoothPersistedStateOnAirplane() {
- if (!supportBluetoothPersistedState()) {
- return false;
- }
- int state = Settings.Global.getInt(mContentResolver, Settings.Global.BLUETOOTH_ON, -1);
- if (DBG) {
- Slog.d(TAG, "Bluetooth persisted state: " + state);
- }
- return state == BLUETOOTH_ON_AIRPLANE;
- }
-
- /**
- * Returns true if the Bluetooth saved state is BLUETOOTH_ON_BLUETOOTH
- */
- private boolean isBluetoothPersistedStateOnBluetooth() {
- if (!supportBluetoothPersistedState()) {
- return false;
- }
- return Settings.Global.getInt(mContentResolver, Settings.Global.BLUETOOTH_ON,
- BLUETOOTH_ON_BLUETOOTH) == BLUETOOTH_ON_BLUETOOTH;
- }
-
- /**
- * Save the Bluetooth on/off state
- */
- private void persistBluetoothSetting(int value) {
- if (DBG) {
- Slog.d(TAG, "Persisting Bluetooth Setting: " + value);
- }
- // waive WRITE_SECURE_SETTINGS permission check
- final long callingIdentity = Binder.clearCallingIdentity();
- Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.BLUETOOTH_ON, value);
- Binder.restoreCallingIdentity(callingIdentity);
- }
-
- /**
- * Returns true if the Bluetooth Adapter's name and address is
- * locally cached
- * @return
- */
- private boolean isNameAndAddressSet() {
- return mName != null && mAddress != null && mName.length() > 0 && mAddress.length() > 0;
- }
-
- /**
- * Retrieve the Bluetooth Adapter's name and address and save it in
- * in the local cache
- */
- private void loadStoredNameAndAddress() {
- if (DBG) {
- Slog.d(TAG, "Loading stored name and address");
- }
- if (mContext.getResources()
- .getBoolean(com.android.internal.R.bool.config_bluetooth_address_validation)
- && Settings.Secure.getIntForUser(mContentResolver,
- Settings.Secure.BLUETOOTH_NAME, 0, mUserId)
- == 0) {
- // if the valid flag is not set, don't load the address and name
- if (DBG) {
- Slog.d(TAG, "invalid bluetooth name and address stored");
- }
- return;
- }
- mName = Settings.Secure.getStringForUser(
- mContentResolver, Settings.Secure.BLUETOOTH_NAME, mUserId);
- mAddress = Settings.Secure.getStringForUser(
- mContentResolver, Settings.Secure.BLUETOOTH_ADDRESS, mUserId);
- if (DBG) {
- Slog.d(TAG, "Stored bluetooth Name=" + mName + ",Address=" + mAddress);
- }
- }
-
- /**
- * Save the Bluetooth name and address in the persistent store.
- * Only non-null values will be saved.
- * @param name
- * @param address
- */
- private void storeNameAndAddress(String name, String address) {
- if (name != null) {
- Settings.Secure.putStringForUser(mContentResolver, Settings.Secure.BLUETOOTH_NAME, name,
- mUserId);
- mName = name;
- if (DBG) {
- Slog.d(TAG, "Stored Bluetooth name: " + Settings.Secure.getStringForUser(
- mContentResolver, Settings.Secure.BLUETOOTH_NAME,
- mUserId));
- }
- }
-
- if (address != null) {
- Settings.Secure.putStringForUser(mContentResolver, Settings.Secure.BLUETOOTH_ADDRESS,
- address, mUserId);
- mAddress = address;
- if (DBG) {
- Slog.d(TAG,
- "Stored Bluetoothaddress: " + Settings.Secure.getStringForUser(
- mContentResolver, Settings.Secure.BLUETOOTH_ADDRESS,
- mUserId));
- }
- }
-
- if ((name != null) && (address != null)) {
- Settings.Secure.putIntForUser(mContentResolver, Settings.Secure.BLUETOOTH_ADDR_VALID, 1,
- mUserId);
- }
- }
-
- public IBluetooth registerAdapter(IBluetoothManagerCallback callback) {
- if (callback == null) {
- Slog.w(TAG, "Callback is null in registerAdapter");
- return null;
- }
- synchronized (mCallbacks) {
- mCallbacks.register(callback);
- }
- return mBluetooth;
- }
-
- public void unregisterAdapter(IBluetoothManagerCallback callback) {
- if (callback == null) {
- Slog.w(TAG, "Callback is null in unregisterAdapter");
- return;
- }
- synchronized (mCallbacks) {
- mCallbacks.unregister(callback);
- }
- }
-
- public void registerStateChangeCallback(IBluetoothStateChangeCallback callback) {
- if (callback == null) {
- Slog.w(TAG, "registerStateChangeCallback: Callback is null!");
- return;
- }
- Message msg = mHandler.obtainMessage(MESSAGE_REGISTER_STATE_CHANGE_CALLBACK);
- msg.obj = callback;
- mHandler.sendMessage(msg);
- }
-
- public void unregisterStateChangeCallback(IBluetoothStateChangeCallback callback) {
- if (callback == null) {
- Slog.w(TAG, "unregisterStateChangeCallback: Callback is null!");
- return;
- }
- Message msg = mHandler.obtainMessage(MESSAGE_UNREGISTER_STATE_CHANGE_CALLBACK);
- msg.obj = callback;
- mHandler.sendMessage(msg);
- }
-
- public boolean isEnabled() {
- return getState() == BluetoothAdapter.STATE_ON;
- }
-
- public int getState() {
- if ((Binder.getCallingUid() != Process.SYSTEM_UID) && (!checkIfCallerIsForegroundUser())) {
- Slog.w(TAG, "getState(): report OFF for non-active and non system user");
- return BluetoothAdapter.STATE_OFF;
- }
-
- try {
- mBluetoothLock.readLock().lock();
- if (mBluetooth != null) {
- return mBluetooth.getState();
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "getState()", e);
- } finally {
- mBluetoothLock.readLock().unlock();
- }
- return BluetoothAdapter.STATE_OFF;
- }
-
- class ClientDeathRecipient implements IBinder.DeathRecipient {
- private String mPackageName;
-
- ClientDeathRecipient(String packageName) {
- mPackageName = packageName;
- }
-
- public void binderDied() {
- if (DBG) {
- Slog.d(TAG, "Binder is dead - unregister " + mPackageName);
- }
-
- for (Map.Entry<IBinder, ClientDeathRecipient> entry : mBleApps.entrySet()) {
- IBinder token = entry.getKey();
- ClientDeathRecipient deathRec = entry.getValue();
- if (deathRec.equals(this)) {
- updateBleAppCount(token, false, mPackageName);
- break;
- }
- }
- }
-
- public String getPackageName() {
- return mPackageName;
- }
- }
-
- @Override
- public boolean isBleScanAlwaysAvailable() {
- if (isAirplaneModeOn() && !mEnable) {
- return false;
- }
- try {
- return Settings.Global.getInt(mContentResolver,
- Settings.Global.BLE_SCAN_ALWAYS_AVAILABLE) != 0;
- } catch (SettingNotFoundException e) {
- }
- return false;
- }
-
- @Override
- public boolean isHearingAidProfileSupported() {
- return mIsHearingAidProfileSupported;
- }
-
- @Override
- /** @hide */
- public java.util.List<String> getSystemConfigEnabledProfilesForPackage(String packageName) {
- if (Binder.getCallingUid() != Process.BLUETOOTH_UID) {
- Slog.w(TAG, "getSystemConfigEnabledProfilesForPackage(): not allowed for non-bluetooth");
- return null;
- }
-
- SystemConfig systemConfig = SystemConfig.getInstance();
- if (systemConfig == null) {
- return null;
- }
-
- android.util.ArrayMap<String, Boolean> componentEnabledStates =
- systemConfig.getComponentsEnabledStates(packageName);
- if (componentEnabledStates == null) {
- return null;
- }
-
- ArrayList enabledProfiles = new ArrayList<String>();
- for (Map.Entry<String, Boolean> entry : componentEnabledStates.entrySet()) {
- if (entry.getValue()) {
- enabledProfiles.add(entry.getKey());
- }
- }
-
- return enabledProfiles;
- }
-
- private boolean isDeviceProvisioned() {
- return Settings.Global.getInt(mContentResolver, Settings.Global.DEVICE_PROVISIONED,
- 0) != 0;
- }
-
- // Monitor change of BLE scan only mode settings.
- private void registerForProvisioningStateChange() {
- ContentObserver contentObserver = new ContentObserver(null) {
- @Override
- public void onChange(boolean selfChange) {
- if (!isDeviceProvisioned()) {
- if (DBG) {
- Slog.d(TAG, "DEVICE_PROVISIONED setting changed, but device is not "
- + "provisioned");
- }
- return;
- }
- if (mHandler.hasMessages(MESSAGE_INIT_FLAGS_CHANGED)) {
- Slog.i(TAG, "Device provisioned, reactivating pending flag changes");
- onInitFlagsChanged();
- }
- }
- };
-
- mContentResolver.registerContentObserver(
- Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED), false,
- contentObserver);
- }
-
- // Monitor change of BLE scan only mode settings.
- private void registerForBleScanModeChange() {
- ContentObserver contentObserver = new ContentObserver(null) {
- @Override
- public void onChange(boolean selfChange) {
- if (isBleScanAlwaysAvailable()) {
- // Nothing to do
- return;
- }
- // BLE scan is not available.
- disableBleScanMode();
- clearBleApps();
- try {
- mBluetoothLock.readLock().lock();
- if (mBluetooth != null) {
- addActiveLog(BluetoothProtoEnums.ENABLE_DISABLE_REASON_APPLICATION_REQUEST,
- mContext.getPackageName(), false);
- mBluetooth.onBrEdrDown(mContext.getAttributionSource());
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "error when disabling bluetooth", e);
- } finally {
- mBluetoothLock.readLock().unlock();
- }
- }
- };
-
- mContentResolver.registerContentObserver(
- Settings.Global.getUriFor(Settings.Global.BLE_SCAN_ALWAYS_AVAILABLE), false,
- contentObserver);
- }
-
- // Disable ble scan only mode.
- private void disableBleScanMode() {
- try {
- mBluetoothLock.writeLock().lock();
- if (mBluetooth != null && (mBluetooth.getState() != BluetoothAdapter.STATE_ON)) {
- if (DBG) {
- Slog.d(TAG, "Reseting the mEnable flag for clean disable");
- }
- mEnable = false;
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "getState()", e);
- } finally {
- mBluetoothLock.writeLock().unlock();
- }
- }
-
- private int updateBleAppCount(IBinder token, boolean enable, String packageName) {
- ClientDeathRecipient r = mBleApps.get(token);
- if (r == null && enable) {
- ClientDeathRecipient deathRec = new ClientDeathRecipient(packageName);
- try {
- token.linkToDeath(deathRec, 0);
- } catch (RemoteException ex) {
- throw new IllegalArgumentException("BLE app (" + packageName + ") already dead!");
- }
- mBleApps.put(token, deathRec);
- if (DBG) {
- Slog.d(TAG, "Registered for death of " + packageName);
- }
- } else if (!enable && r != null) {
- // Unregister death recipient as the app goes away.
- token.unlinkToDeath(r, 0);
- mBleApps.remove(token);
- if (DBG) {
- Slog.d(TAG, "Unregistered for death of " + packageName);
- }
- }
- int appCount = mBleApps.size();
- if (DBG) {
- Slog.d(TAG, appCount + " registered Ble Apps");
- }
- return appCount;
- }
-
- @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
- private boolean checkBluetoothPermissions(AttributionSource attributionSource, String message,
- boolean requireForeground) {
- if (isBluetoothDisallowed()) {
- if (DBG) {
- Slog.d(TAG, "checkBluetoothPermissions: bluetooth disallowed");
- }
- return false;
- }
- // Check if packageName belongs to callingUid
- final int callingUid = Binder.getCallingUid();
- final boolean isCallerSystem = UserHandle.getAppId(callingUid) == Process.SYSTEM_UID;
- if (!isCallerSystem) {
- checkPackage(callingUid, attributionSource.getPackageName());
-
- if (requireForeground && !checkIfCallerIsForegroundUser()) {
- Slog.w(TAG, "Not allowed for non-active and non system user");
- return false;
- }
-
- if (!checkConnectPermissionForDataDelivery(mContext, attributionSource, message)) {
- return false;
- }
- }
- return true;
- }
-
- public boolean enableBle(AttributionSource attributionSource, IBinder token)
- throws RemoteException {
- final String packageName = attributionSource.getPackageName();
- if (!checkBluetoothPermissions(attributionSource, "enableBle", false)) {
- if (DBG) {
- Slog.d(TAG, "enableBle(): bluetooth disallowed");
- }
- return false;
- }
-
- if (DBG) {
- Slog.d(TAG, "enableBle(" + packageName + "): mBluetooth =" + mBluetooth
- + " mBinding = " + mBinding + " mState = "
- + BluetoothAdapter.nameForState(mState));
- }
- updateBleAppCount(token, true, packageName);
-
- if (mState == BluetoothAdapter.STATE_ON
- || mState == BluetoothAdapter.STATE_BLE_ON
- || mState == BluetoothAdapter.STATE_TURNING_ON
- || mState == BluetoothAdapter.STATE_TURNING_OFF
- || mState == BluetoothAdapter.STATE_BLE_TURNING_ON) {
- Log.d(TAG, "enableBLE(): Bluetooth is already enabled or is turning on");
- return true;
- }
- synchronized (mReceiver) {
- // waive WRITE_SECURE_SETTINGS permission check
- sendEnableMsg(false, BluetoothProtoEnums.ENABLE_DISABLE_REASON_APPLICATION_REQUEST,
- packageName, true);
- }
- return true;
- }
-
- @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
- public boolean disableBle(AttributionSource attributionSource, IBinder token)
- throws RemoteException {
- final String packageName = attributionSource.getPackageName();
- if (!checkBluetoothPermissions(attributionSource, "disableBle", false)) {
- if (DBG) {
- Slog.d(TAG, "disableBLE(): bluetooth disallowed");
- }
- return false;
- }
-
- if (DBG) {
- Slog.d(TAG, "disableBle(" + packageName + "): mBluetooth =" + mBluetooth
- + " mBinding = " + mBinding + " mState = "
- + BluetoothAdapter.nameForState(mState));
- }
-
- if (mState == BluetoothAdapter.STATE_OFF) {
- Slog.d(TAG, "disableBLE(): Already disabled");
- return false;
- }
- updateBleAppCount(token, false, packageName);
-
- if (mState == BluetoothAdapter.STATE_BLE_ON && !isBleAppPresent()) {
- if (mEnable) {
- disableBleScanMode();
- }
- if (!mEnableExternal) {
- addActiveLog(BluetoothProtoEnums.ENABLE_DISABLE_REASON_APPLICATION_REQUEST,
- packageName, false);
- sendBrEdrDownCallback(attributionSource);
- }
- }
- return true;
- }
-
- // Clear all apps using BLE scan only mode.
- private void clearBleApps() {
- mBleApps.clear();
- }
-
- /** @hide */
- public boolean isBleAppPresent() {
- if (DBG) {
- Slog.d(TAG, "isBleAppPresent() count: " + mBleApps.size());
- }
- return mBleApps.size() > 0;
- }
-
- /**
- * Call IBluetooth.onLeServiceUp() to continue if Bluetooth should be on,
- * call IBluetooth.onBrEdrDown() to disable if Bluetooth should be off.
- */
- @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
- private void continueFromBleOnState() {
- if (DBG) {
- Slog.d(TAG, "continueFromBleOnState()");
- }
- try {
- mBluetoothLock.readLock().lock();
- if (mBluetooth == null) {
- Slog.e(TAG, "onBluetoothServiceUp: mBluetooth is null!");
- return;
- }
- if (!mEnableExternal && !isBleAppPresent()) {
- Slog.i(TAG, "Bluetooth was disabled while enabling BLE, disable BLE now");
- mEnable = false;
- mBluetooth.onBrEdrDown(mContext.getAttributionSource());
- return;
- }
- if (isBluetoothPersistedStateOnBluetooth() || !isBleAppPresent()) {
- // This triggers transition to STATE_ON
- mBluetooth.onLeServiceUp(mContext.getAttributionSource());
- persistBluetoothSetting(BLUETOOTH_ON_BLUETOOTH);
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to call onServiceUp", e);
- } finally {
- mBluetoothLock.readLock().unlock();
- }
- }
-
- /**
- * Inform BluetoothAdapter instances that BREDR part is down
- * and turn off all service and stack if no LE app needs it
- */
- @RequiresPermission(allOf = {
- android.Manifest.permission.BLUETOOTH_CONNECT,
- android.Manifest.permission.BLUETOOTH_PRIVILEGED,
- })
- private void sendBrEdrDownCallback(AttributionSource attributionSource) {
- if (DBG) {
- Slog.d(TAG, "Calling sendBrEdrDownCallback callbacks");
- }
-
- if (mBluetooth == null) {
- Slog.w(TAG, "Bluetooth handle is null");
- return;
- }
-
- if (isBleAppPresent()) {
- // Need to stay at BLE ON. Disconnect all Gatt connections
- try {
- mBluetoothGatt.unregAll(attributionSource);
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to disconnect all apps.", e);
- }
- } else {
- try {
- mBluetoothLock.readLock().lock();
- if (mBluetooth != null) {
- mBluetooth.onBrEdrDown(attributionSource);
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "Call to onBrEdrDown() failed.", e);
- } finally {
- mBluetoothLock.readLock().unlock();
- }
- }
-
- }
-
- public boolean enableNoAutoConnect(AttributionSource attributionSource) {
- final String packageName = attributionSource.getPackageName();
- if (!checkBluetoothPermissions(attributionSource, "enableNoAutoConnect", false)) {
- if (DBG) {
- Slog.d(TAG, "enableNoAutoConnect(): not enabling - bluetooth disallowed");
- }
- return false;
- }
-
- if (DBG) {
- Slog.d(TAG, "enableNoAutoConnect(): mBluetooth =" + mBluetooth + " mBinding = "
- + mBinding);
- }
-
- int callingAppId = UserHandle.getAppId(Binder.getCallingUid());
- if (callingAppId != Process.NFC_UID) {
- throw new SecurityException("no permission to enable Bluetooth quietly");
- }
-
- synchronized (mReceiver) {
- mQuietEnableExternal = true;
- mEnableExternal = true;
- sendEnableMsg(true,
- BluetoothProtoEnums.ENABLE_DISABLE_REASON_APPLICATION_REQUEST, packageName);
- }
- return true;
- }
-
- public boolean enable(AttributionSource attributionSource) throws RemoteException {
- final String packageName = attributionSource.getPackageName();
- if (!checkBluetoothPermissions(attributionSource, "enable", true)) {
- if (DBG) {
- Slog.d(TAG, "enable(): not enabling - bluetooth disallowed");
- }
- return false;
- }
-
- final int callingUid = Binder.getCallingUid();
- final boolean callerSystem = UserHandle.getAppId(callingUid) == Process.SYSTEM_UID;
- if (!callerSystem && !isEnabled() && mWirelessConsentRequired
- && startConsentUiIfNeeded(packageName,
- callingUid, BluetoothAdapter.ACTION_REQUEST_ENABLE)) {
- return false;
- }
-
- if (DBG) {
- Slog.d(TAG, "enable(" + packageName + "): mBluetooth =" + mBluetooth + " mBinding = "
- + mBinding + " mState = " + BluetoothAdapter.nameForState(mState));
- }
-
- synchronized (mReceiver) {
- mQuietEnableExternal = false;
- mEnableExternal = true;
- // waive WRITE_SECURE_SETTINGS permission check
- sendEnableMsg(false,
- BluetoothProtoEnums.ENABLE_DISABLE_REASON_APPLICATION_REQUEST, packageName);
- }
- if (DBG) {
- Slog.d(TAG, "enable returning");
- }
- return true;
- }
-
- public boolean disable(AttributionSource attributionSource, boolean persist)
- throws RemoteException {
- final String packageName = attributionSource.getPackageName();
- if (!checkBluetoothPermissions(attributionSource, "disable", true)) {
- if (DBG) {
- Slog.d(TAG, "disable(): not disabling - bluetooth disallowed");
- }
- return false;
- }
-
- final int callingUid = Binder.getCallingUid();
- final boolean callerSystem = UserHandle.getAppId(callingUid) == Process.SYSTEM_UID;
- if (!callerSystem && isEnabled() && mWirelessConsentRequired
- && startConsentUiIfNeeded(packageName,
- callingUid, BluetoothAdapter.ACTION_REQUEST_DISABLE)) {
- return false;
- }
-
- if (DBG) {
- Slog.d(TAG, "disable(): mBluetooth = " + mBluetooth + " mBinding = " + mBinding);
- }
-
- synchronized (mReceiver) {
- if (!isBluetoothPersistedStateOnAirplane()) {
- if (persist) {
- persistBluetoothSetting(BLUETOOTH_OFF);
- }
- mEnableExternal = false;
- }
- sendDisableMsg(BluetoothProtoEnums.ENABLE_DISABLE_REASON_APPLICATION_REQUEST,
- packageName);
- }
- return true;
- }
-
- private boolean startConsentUiIfNeeded(String packageName,
- int callingUid, String intentAction) throws RemoteException {
- if (checkBluetoothPermissionWhenWirelessConsentRequired()) {
- return false;
- }
- try {
- // Validate the package only if we are going to use it
- ApplicationInfo applicationInfo = mContext.getPackageManager()
- .getApplicationInfoAsUser(packageName,
- PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
- UserHandle.getUserId(callingUid));
- if (applicationInfo.uid != callingUid) {
- throw new SecurityException("Package " + packageName
- + " not in uid " + callingUid);
- }
-
- Intent intent = new Intent(intentAction);
- intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName);
- intent.setFlags(
- Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
- try {
- mContext.startActivity(intent);
- } catch (ActivityNotFoundException e) {
- // Shouldn't happen
- Slog.e(TAG, "Intent to handle action " + intentAction + " missing");
- return false;
- }
- return true;
- } catch (PackageManager.NameNotFoundException e) {
- throw new RemoteException(e.getMessage());
- }
- }
-
- /**
- * Check if AppOpsManager is available and the packageName belongs to uid
- *
- * A null package belongs to any uid
- */
- private void checkPackage(int uid, String packageName) {
- if (mAppOps == null) {
- Slog.w(TAG, "checkPackage(): called before system boot up, uid "
- + uid + ", packageName " + packageName);
- throw new IllegalStateException("System has not boot yet");
- }
- if (packageName == null) {
- Slog.w(TAG, "checkPackage(): called with null packageName from " + uid);
- return;
- }
- try {
- mAppOps.checkPackage(uid, packageName);
- } catch (SecurityException e) {
- Slog.w(TAG, "checkPackage(): " + packageName + " does not belong to uid " + uid);
- throw new SecurityException(e.getMessage());
- }
- }
-
- /**
- * Check if the caller must still pass permission check or if the caller is exempted
- * from the consent UI via the MANAGE_BLUETOOTH_WHEN_WIRELESS_CONSENT_REQUIRED check.
- *
- * Commands from some callers may be exempted from triggering the consent UI when
- * enabling bluetooth. This exemption is checked via the
- * MANAGE_BLUETOOTH_WHEN_WIRELESS_CONSENT_REQUIRED and allows calls to skip
- * the consent UI where it may otherwise be required.
- *
- * @hide
- */
- @SuppressLint("AndroidFrameworkRequiresPermission")
- private boolean checkBluetoothPermissionWhenWirelessConsentRequired() {
- int result = mContext.checkCallingPermission(
- android.Manifest.permission.MANAGE_BLUETOOTH_WHEN_WIRELESS_CONSENT_REQUIRED);
- return result == PackageManager.PERMISSION_GRANTED;
- }
-
- @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
- public void unbindAndFinish() {
- if (DBG) {
- Slog.d(TAG, "unbindAndFinish(): " + mBluetooth + " mBinding = " + mBinding
- + " mUnbinding = " + mUnbinding);
- }
-
- try {
- mBluetoothLock.writeLock().lock();
- if (mUnbinding) {
- return;
- }
- mUnbinding = true;
- mHandler.removeMessages(MESSAGE_BLUETOOTH_STATE_CHANGE);
- mHandler.removeMessages(MESSAGE_BIND_PROFILE_SERVICE);
- if (mBluetooth != null) {
- //Unregister callback object
- try {
- mBluetooth.unregisterCallback(mBluetoothCallback,
- mContext.getAttributionSource());
- } catch (RemoteException re) {
- Slog.e(TAG, "Unable to unregister BluetoothCallback", re);
- }
- mBluetoothBinder = null;
- mBluetooth = null;
- mContext.unbindService(mConnection);
- mUnbinding = false;
- mBinding = false;
- } else {
- mUnbinding = false;
- }
- mBluetoothGatt = null;
- } finally {
- mBluetoothLock.writeLock().unlock();
- }
- }
-
- public IBluetoothGatt getBluetoothGatt() {
- // sync protection
- return mBluetoothGatt;
- }
-
- @Override
- public boolean bindBluetoothProfileService(int bluetoothProfile,
- IBluetoothProfileServiceConnection proxy) {
- if (mState != BluetoothAdapter.STATE_ON) {
- if (DBG) {
- Slog.d(TAG, "Trying to bind to profile: " + bluetoothProfile
- + ", while Bluetooth was disabled");
- }
- return false;
- }
- synchronized (mProfileServices) {
- ProfileServiceConnections psc = mProfileServices.get(new Integer(bluetoothProfile));
- if (psc == null) {
- if (DBG) {
- Slog.d(TAG, "Creating new ProfileServiceConnections object for" + " profile: "
- + bluetoothProfile);
- }
-
- if (bluetoothProfile != BluetoothProfile.HEADSET) {
- return false;
- }
-
- Intent intent = new Intent(IBluetoothHeadset.class.getName());
- psc = new ProfileServiceConnections(intent);
- if (!psc.bindService()) {
- return false;
- }
-
- mProfileServices.put(new Integer(bluetoothProfile), psc);
- }
- }
-
- // Introducing a delay to give the client app time to prepare
- Message addProxyMsg = mHandler.obtainMessage(MESSAGE_ADD_PROXY_DELAYED);
- addProxyMsg.arg1 = bluetoothProfile;
- addProxyMsg.obj = proxy;
- mHandler.sendMessageDelayed(addProxyMsg, ADD_PROXY_DELAY_MS);
- return true;
- }
-
- @Override
- public void unbindBluetoothProfileService(int bluetoothProfile,
- IBluetoothProfileServiceConnection proxy) {
- synchronized (mProfileServices) {
- Integer profile = new Integer(bluetoothProfile);
- ProfileServiceConnections psc = mProfileServices.get(profile);
- if (psc == null) {
- return;
- }
- psc.removeProxy(proxy);
- if (psc.isEmpty()) {
- // All prxoies are disconnected, unbind with the service.
- try {
- mContext.unbindService(psc);
- } catch (IllegalArgumentException e) {
- Slog.e(TAG, "Unable to unbind service with intent: " + psc.mIntent, e);
- }
- mProfileServices.remove(profile);
- }
- }
- }
-
- private void unbindAllBluetoothProfileServices() {
- synchronized (mProfileServices) {
- for (Integer i : mProfileServices.keySet()) {
- ProfileServiceConnections psc = mProfileServices.get(i);
- try {
- mContext.unbindService(psc);
- } catch (IllegalArgumentException e) {
- Slog.e(TAG, "Unable to unbind service with intent: " + psc.mIntent, e);
- }
- psc.removeAllProxies();
- }
- mProfileServices.clear();
- }
- }
-
- /**
- * Send enable message and set adapter name and address. Called when the boot phase becomes
- * PHASE_SYSTEM_SERVICES_READY.
- */
- public void handleOnBootPhase() {
- if (DBG) {
- Slog.d(TAG, "Bluetooth boot completed");
- }
- mAppOps = mContext.getSystemService(AppOpsManager.class);
- UserManagerInternal userManagerInternal =
- LocalServices.getService(UserManagerInternal.class);
- userManagerInternal.addUserRestrictionsListener(mUserRestrictionsListener);
- final boolean isBluetoothDisallowed = isBluetoothDisallowed();
- if (isBluetoothDisallowed) {
- return;
- }
- final boolean isSafeMode = mContext.getPackageManager().isSafeMode();
- if (mEnableExternal && isBluetoothPersistedStateOnBluetooth() && !isSafeMode) {
- if (DBG) {
- Slog.d(TAG, "Auto-enabling Bluetooth.");
- }
- sendEnableMsg(mQuietEnableExternal,
- BluetoothProtoEnums.ENABLE_DISABLE_REASON_SYSTEM_BOOT,
- mContext.getPackageName());
- } else if (!isNameAndAddressSet()) {
- if (DBG) {
- Slog.d(TAG, "Getting adapter name and address");
- }
- Message getMsg = mHandler.obtainMessage(MESSAGE_GET_NAME_AND_ADDRESS);
- mHandler.sendMessage(getMsg);
- }
-
- mBluetoothModeChangeHelper = new BluetoothModeChangeHelper(mContext);
- if (mBluetoothAirplaneModeListener != null) {
- mBluetoothAirplaneModeListener.start(mBluetoothModeChangeHelper);
- }
- registerForProvisioningStateChange();
- mBluetoothDeviceConfigListener = new BluetoothDeviceConfigListener(this, DBG);
- }
-
- /**
- * Called when switching to a different foreground user.
- */
- public void handleOnSwitchUser(int userHandle) {
- if (DBG) {
- Slog.d(TAG, "User " + userHandle + " switched");
- }
- mHandler.obtainMessage(MESSAGE_USER_SWITCHED, userHandle, 0).sendToTarget();
- }
-
- /**
- * Called when user is unlocked.
- */
- public void handleOnUnlockUser(int userHandle) {
- if (DBG) {
- Slog.d(TAG, "User " + userHandle + " unlocked");
- }
- mHandler.obtainMessage(MESSAGE_USER_UNLOCKED, userHandle, 0).sendToTarget();
- }
-
- /**
- * This class manages the clients connected to a given ProfileService
- * and maintains the connection with that service.
- */
- private final class ProfileServiceConnections
- implements ServiceConnection, IBinder.DeathRecipient {
- final RemoteCallbackList<IBluetoothProfileServiceConnection> mProxies =
- new RemoteCallbackList<IBluetoothProfileServiceConnection>();
- IBinder mService;
- ComponentName mClassName;
- Intent mIntent;
- boolean mInvokingProxyCallbacks = false;
-
- ProfileServiceConnections(Intent intent) {
- mService = null;
- mClassName = null;
- mIntent = intent;
- }
-
- private boolean bindService() {
- int state = BluetoothAdapter.STATE_OFF;
- try {
- mBluetoothLock.readLock().lock();
- if (mBluetooth != null) {
- state = mBluetooth.getState();
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to call getState", e);
- return false;
- } finally {
- mBluetoothLock.readLock().unlock();
- }
-
- if (state != BluetoothAdapter.STATE_ON) {
- if (DBG) {
- Slog.d(TAG, "Unable to bindService while Bluetooth is disabled");
- }
- return false;
- }
-
- if (mIntent != null && mService == null && doBind(mIntent, this, 0,
- UserHandle.CURRENT_OR_SELF)) {
- Message msg = mHandler.obtainMessage(MESSAGE_BIND_PROFILE_SERVICE);
- msg.obj = this;
- mHandler.sendMessageDelayed(msg, TIMEOUT_BIND_MS);
- return true;
- }
- Slog.w(TAG, "Unable to bind with intent: " + mIntent);
- return false;
- }
-
- private void addProxy(IBluetoothProfileServiceConnection proxy) {
- mProxies.register(proxy);
- if (mService != null) {
- try {
- proxy.onServiceConnected(mClassName, mService);
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to connect to proxy", e);
- }
- } else {
- if (!mHandler.hasMessages(MESSAGE_BIND_PROFILE_SERVICE, this)) {
- Message msg = mHandler.obtainMessage(MESSAGE_BIND_PROFILE_SERVICE);
- msg.obj = this;
- mHandler.sendMessage(msg);
- }
- }
- }
-
- private void removeProxy(IBluetoothProfileServiceConnection proxy) {
- if (proxy != null) {
- if (mProxies.unregister(proxy)) {
- try {
- proxy.onServiceDisconnected(mClassName);
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to disconnect proxy", e);
- }
- }
- } else {
- Slog.w(TAG, "Trying to remove a null proxy");
- }
- }
-
- private void removeAllProxies() {
- onServiceDisconnected(mClassName);
- mProxies.kill();
- }
-
- private boolean isEmpty() {
- return mProxies.getRegisteredCallbackCount() == 0;
- }
-
- @Override
- public void onServiceConnected(ComponentName className, IBinder service) {
- // remove timeout message
- mHandler.removeMessages(MESSAGE_BIND_PROFILE_SERVICE, this);
- mService = service;
- mClassName = className;
- try {
- mService.linkToDeath(this, 0);
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to linkToDeath", e);
- }
-
- if (mInvokingProxyCallbacks) {
- Slog.e(TAG, "Proxy callbacks already in progress.");
- return;
- }
- mInvokingProxyCallbacks = true;
-
- final int n = mProxies.beginBroadcast();
- try {
- for (int i = 0; i < n; i++) {
- try {
- mProxies.getBroadcastItem(i).onServiceConnected(className, service);
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to connect to proxy", e);
- }
- }
- } finally {
- mProxies.finishBroadcast();
- mInvokingProxyCallbacks = false;
- }
- }
-
- @Override
- public void onServiceDisconnected(ComponentName className) {
- if (mService == null) {
- return;
- }
- try {
- mService.unlinkToDeath(this, 0);
- } catch (NoSuchElementException e) {
- Log.e(TAG, "error unlinking to death", e);
- }
- mService = null;
- mClassName = null;
-
- if (mInvokingProxyCallbacks) {
- Slog.e(TAG, "Proxy callbacks already in progress.");
- return;
- }
- mInvokingProxyCallbacks = true;
-
- final int n = mProxies.beginBroadcast();
- try {
- for (int i = 0; i < n; i++) {
- try {
- mProxies.getBroadcastItem(i).onServiceDisconnected(className);
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to disconnect from proxy", e);
- }
- }
- } finally {
- mProxies.finishBroadcast();
- mInvokingProxyCallbacks = false;
- }
- }
-
- @Override
- public void binderDied() {
- if (DBG) {
- Slog.w(TAG, "Profile service for profile: " + mClassName + " died.");
- }
- onServiceDisconnected(mClassName);
- // Trigger rebind
- Message msg = mHandler.obtainMessage(MESSAGE_BIND_PROFILE_SERVICE);
- msg.obj = this;
- mHandler.sendMessageDelayed(msg, TIMEOUT_BIND_MS);
- }
- }
-
- private void sendBluetoothStateCallback(boolean isUp) {
- try {
- int n = mStateChangeCallbacks.beginBroadcast();
- if (DBG) {
- Slog.d(TAG, "Broadcasting onBluetoothStateChange(" + isUp + ") to " + n
- + " receivers.");
- }
- for (int i = 0; i < n; i++) {
- try {
- mStateChangeCallbacks.getBroadcastItem(i).onBluetoothStateChange(isUp);
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to call onBluetoothStateChange() on callback #" + i, e);
- }
- }
- } finally {
- mStateChangeCallbacks.finishBroadcast();
- }
- }
-
- /**
- * Inform BluetoothAdapter instances that Adapter service is up
- */
- private void sendBluetoothServiceUpCallback() {
- synchronized (mCallbacks) {
- try {
- int n = mCallbacks.beginBroadcast();
- Slog.d(TAG, "Broadcasting onBluetoothServiceUp() to " + n + " receivers.");
- for (int i = 0; i < n; i++) {
- try {
- mCallbacks.getBroadcastItem(i).onBluetoothServiceUp(mBluetooth);
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to call onBluetoothServiceUp() on callback #" + i, e);
- }
- }
- } finally {
- mCallbacks.finishBroadcast();
- }
- }
- }
-
- /**
- * Inform BluetoothAdapter instances that Adapter service is down
- */
- private void sendBluetoothServiceDownCallback() {
- synchronized (mCallbacks) {
- try {
- int n = mCallbacks.beginBroadcast();
- Slog.d(TAG, "Broadcasting onBluetoothServiceDown() to " + n + " receivers.");
- for (int i = 0; i < n; i++) {
- try {
- mCallbacks.getBroadcastItem(i).onBluetoothServiceDown();
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to call onBluetoothServiceDown() on callback #" + i, e);
- }
- }
- } finally {
- mCallbacks.finishBroadcast();
- }
- }
- }
-
- public String getAddress(AttributionSource attributionSource) {
- if (!checkConnectPermissionForDataDelivery(mContext, attributionSource, "getAddress")) {
- return null;
- }
-
- if ((Binder.getCallingUid() != Process.SYSTEM_UID) && (!checkIfCallerIsForegroundUser())) {
- Slog.w(TAG, "getAddress(): not allowed for non-active and non system user");
- return null;
- }
-
- if (mContext.checkCallingOrSelfPermission(Manifest.permission.LOCAL_MAC_ADDRESS)
- != PackageManager.PERMISSION_GRANTED) {
- return BluetoothAdapter.DEFAULT_MAC_ADDRESS;
- }
-
- try {
- mBluetoothLock.readLock().lock();
- if (mBluetooth != null) {
- return mBluetooth.getAddressWithAttribution(attributionSource);
- }
- } catch (RemoteException e) {
- Slog.e(TAG,
- "getAddress(): Unable to retrieve address remotely. Returning cached address",
- e);
- } finally {
- mBluetoothLock.readLock().unlock();
- }
-
- // mAddress is accessed from outside.
- // It is alright without a lock. Here, bluetooth is off, no other thread is
- // changing mAddress
- return mAddress;
- }
-
- public String getName(AttributionSource attributionSource) {
- if (!checkConnectPermissionForDataDelivery(mContext, attributionSource, "getName")) {
- return null;
- }
-
- if ((Binder.getCallingUid() != Process.SYSTEM_UID) && (!checkIfCallerIsForegroundUser())) {
- Slog.w(TAG, "getName(): not allowed for non-active and non system user");
- return null;
- }
-
- try {
- mBluetoothLock.readLock().lock();
- if (mBluetooth != null) {
- return mBluetooth.getName(attributionSource);
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "getName(): Unable to retrieve name remotely. Returning cached name", e);
- } finally {
- mBluetoothLock.readLock().unlock();
- }
-
- // mName is accessed from outside.
- // It alright without a lock. Here, bluetooth is off, no other thread is
- // changing mName
- return mName;
- }
-
- private class BluetoothServiceConnection implements ServiceConnection {
- public void onServiceConnected(ComponentName componentName, IBinder service) {
- String name = componentName.getClassName();
- if (DBG) {
- Slog.d(TAG, "BluetoothServiceConnection: " + name);
- }
- Message msg = mHandler.obtainMessage(MESSAGE_BLUETOOTH_SERVICE_CONNECTED);
- if (name.equals("com.android.bluetooth.btservice.AdapterService")) {
- msg.arg1 = SERVICE_IBLUETOOTH;
- } else if (name.equals("com.android.bluetooth.gatt.GattService")) {
- msg.arg1 = SERVICE_IBLUETOOTHGATT;
- } else {
- Slog.e(TAG, "Unknown service connected: " + name);
- return;
- }
- msg.obj = service;
- mHandler.sendMessage(msg);
- }
-
- public void onServiceDisconnected(ComponentName componentName) {
- // Called if we unexpectedly disconnect.
- String name = componentName.getClassName();
- if (DBG) {
- Slog.d(TAG, "BluetoothServiceConnection, disconnected: " + name);
- }
- Message msg = mHandler.obtainMessage(MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED);
- if (name.equals("com.android.bluetooth.btservice.AdapterService")) {
- msg.arg1 = SERVICE_IBLUETOOTH;
- } else if (name.equals("com.android.bluetooth.gatt.GattService")) {
- msg.arg1 = SERVICE_IBLUETOOTHGATT;
- } else {
- Slog.e(TAG, "Unknown service disconnected: " + name);
- return;
- }
- mHandler.sendMessage(msg);
- }
- }
-
- private BluetoothServiceConnection mConnection = new BluetoothServiceConnection();
-
- private class BluetoothHandler extends Handler {
- boolean mGetNameAddressOnly = false;
- private int mWaitForEnableRetry;
- private int mWaitForDisableRetry;
-
- BluetoothHandler(Looper looper) {
- super(looper);
- }
-
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MESSAGE_GET_NAME_AND_ADDRESS:
- if (DBG) {
- Slog.d(TAG, "MESSAGE_GET_NAME_AND_ADDRESS");
- }
- try {
- mBluetoothLock.writeLock().lock();
- if ((mBluetooth == null) && (!mBinding)) {
- if (DBG) {
- Slog.d(TAG, "Binding to service to get name and address");
- }
- mGetNameAddressOnly = true;
- Message timeoutMsg = mHandler.obtainMessage(MESSAGE_TIMEOUT_BIND);
- mHandler.sendMessageDelayed(timeoutMsg, TIMEOUT_BIND_MS);
- Intent i = new Intent(IBluetooth.class.getName());
- if (!doBind(i, mConnection,
- Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT,
- UserHandle.CURRENT)) {
- mHandler.removeMessages(MESSAGE_TIMEOUT_BIND);
- } else {
- mBinding = true;
- }
- } else if (mBluetooth != null) {
- try {
- storeNameAndAddress(
- mBluetooth.getName(mContext.getAttributionSource()),
- mBluetooth.getAddressWithAttribution(
- mContext.getAttributionSource()));
- } catch (RemoteException re) {
- Slog.e(TAG, "Unable to grab names", re);
- }
- if (mGetNameAddressOnly && !mEnable) {
- unbindAndFinish();
- }
- mGetNameAddressOnly = false;
- }
- } finally {
- mBluetoothLock.writeLock().unlock();
- }
- break;
-
- case MESSAGE_ENABLE:
- int quietEnable = msg.arg1;
- int isBle = msg.arg2;
- if (mHandler.hasMessages(MESSAGE_HANDLE_DISABLE_DELAYED)
- || mHandler.hasMessages(MESSAGE_HANDLE_ENABLE_DELAYED)) {
- // We are handling enable or disable right now, wait for it.
- mHandler.sendMessageDelayed(mHandler.obtainMessage(MESSAGE_ENABLE,
- quietEnable, isBle), ENABLE_DISABLE_DELAY_MS);
- break;
- }
-
- if (DBG) {
- Slog.d(TAG, "MESSAGE_ENABLE(" + quietEnable + "): mBluetooth = "
- + mBluetooth);
- }
- mHandler.removeMessages(MESSAGE_RESTART_BLUETOOTH_SERVICE);
- mEnable = true;
-
- if (isBle == 0) {
- persistBluetoothSetting(BLUETOOTH_ON_BLUETOOTH);
- }
-
- // Use service interface to get the exact state
- try {
- mBluetoothLock.readLock().lock();
- if (mBluetooth != null) {
- boolean isHandled = true;
- int state = mBluetooth.getState();
- switch (state) {
- case BluetoothAdapter.STATE_BLE_ON:
- if (isBle == 1) {
- Slog.i(TAG, "Already at BLE_ON State");
- } else {
- Slog.w(TAG, "BT Enable in BLE_ON State, going to ON");
- mBluetooth.onLeServiceUp(mContext.getAttributionSource());
- }
- break;
- case BluetoothAdapter.STATE_BLE_TURNING_ON:
- case BluetoothAdapter.STATE_TURNING_ON:
- case BluetoothAdapter.STATE_ON:
- Slog.i(TAG, "MESSAGE_ENABLE: already enabled");
- break;
- default:
- isHandled = false;
- break;
- }
- if (isHandled) break;
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "", e);
- } finally {
- mBluetoothLock.readLock().unlock();
- }
-
- mQuietEnable = (quietEnable == 1);
- if (mBluetooth == null) {
- handleEnable(mQuietEnable);
- } else {
- //
- // We need to wait until transitioned to STATE_OFF and
- // the previous Bluetooth process has exited. The
- // waiting period has three components:
- // (a) Wait until the local state is STATE_OFF. This
- // is accomplished by sending delay a message
- // MESSAGE_HANDLE_ENABLE_DELAYED
- // (b) Wait until the STATE_OFF state is updated to
- // all components.
- // (c) Wait until the Bluetooth process exits, and
- // ActivityManager detects it.
- // The waiting for (b) and (c) is accomplished by
- // delaying the MESSAGE_RESTART_BLUETOOTH_SERVICE
- // message. The delay time is backed off if Bluetooth
- // continuously failed to turn on itself.
- //
- mWaitForEnableRetry = 0;
- Message enableDelayedMsg =
- mHandler.obtainMessage(MESSAGE_HANDLE_ENABLE_DELAYED);
- mHandler.sendMessageDelayed(enableDelayedMsg, ENABLE_DISABLE_DELAY_MS);
- }
- break;
-
- case MESSAGE_DISABLE:
- if (mHandler.hasMessages(MESSAGE_HANDLE_DISABLE_DELAYED) || mBinding
- || mHandler.hasMessages(MESSAGE_HANDLE_ENABLE_DELAYED)) {
- // We are handling enable or disable right now, wait for it.
- mHandler.sendMessageDelayed(mHandler.obtainMessage(MESSAGE_DISABLE),
- ENABLE_DISABLE_DELAY_MS);
- break;
- }
-
- if (DBG) {
- Slog.d(TAG, "MESSAGE_DISABLE: mBluetooth = " + mBluetooth
- + ", mBinding = " + mBinding);
- }
- mHandler.removeMessages(MESSAGE_RESTART_BLUETOOTH_SERVICE);
-
- if (mEnable && mBluetooth != null) {
- mWaitForDisableRetry = 0;
- Message disableDelayedMsg =
- mHandler.obtainMessage(MESSAGE_HANDLE_DISABLE_DELAYED, 0, 0);
- mHandler.sendMessageDelayed(disableDelayedMsg, ENABLE_DISABLE_DELAY_MS);
- } else {
- mEnable = false;
- handleDisable();
- }
- break;
-
- case MESSAGE_HANDLE_ENABLE_DELAYED: {
- // The Bluetooth is turning off, wait for STATE_OFF
- if (mState != BluetoothAdapter.STATE_OFF) {
- if (mWaitForEnableRetry < MAX_WAIT_FOR_ENABLE_DISABLE_RETRIES) {
- mWaitForEnableRetry++;
- Message enableDelayedMsg =
- mHandler.obtainMessage(MESSAGE_HANDLE_ENABLE_DELAYED);
- mHandler.sendMessageDelayed(enableDelayedMsg, ENABLE_DISABLE_DELAY_MS);
- break;
- } else {
- Slog.e(TAG, "Wait for STATE_OFF timeout");
- }
- }
- // Either state is changed to STATE_OFF or reaches the maximum retry, we
- // should move forward to the next step.
- mWaitForEnableRetry = 0;
- Message restartMsg =
- mHandler.obtainMessage(MESSAGE_RESTART_BLUETOOTH_SERVICE);
- mHandler.sendMessageDelayed(restartMsg, getServiceRestartMs());
- Slog.d(TAG, "Handle enable is finished");
- break;
- }
-
- case MESSAGE_HANDLE_DISABLE_DELAYED: {
- boolean disabling = (msg.arg1 == 1);
- Slog.d(TAG, "MESSAGE_HANDLE_DISABLE_DELAYED: disabling:" + disabling);
- if (!disabling) {
- // The Bluetooth is turning on, wait for STATE_ON
- if (mState != BluetoothAdapter.STATE_ON) {
- if (mWaitForDisableRetry < MAX_WAIT_FOR_ENABLE_DISABLE_RETRIES) {
- mWaitForDisableRetry++;
- Message disableDelayedMsg = mHandler.obtainMessage(
- MESSAGE_HANDLE_DISABLE_DELAYED, 0, 0);
- mHandler.sendMessageDelayed(disableDelayedMsg,
- ENABLE_DISABLE_DELAY_MS);
- break;
- } else {
- Slog.e(TAG, "Wait for STATE_ON timeout");
- }
- }
- // Either state is changed to STATE_ON or reaches the maximum retry, we
- // should move forward to the next step.
- mWaitForDisableRetry = 0;
- mEnable = false;
- handleDisable();
- // Wait for state exiting STATE_ON
- Message disableDelayedMsg =
- mHandler.obtainMessage(MESSAGE_HANDLE_DISABLE_DELAYED, 1, 0);
- mHandler.sendMessageDelayed(disableDelayedMsg, ENABLE_DISABLE_DELAY_MS);
- } else {
- // The Bluetooth is turning off, wait for exiting STATE_ON
- if (mState == BluetoothAdapter.STATE_ON) {
- if (mWaitForDisableRetry < MAX_WAIT_FOR_ENABLE_DISABLE_RETRIES) {
- mWaitForDisableRetry++;
- Message disableDelayedMsg = mHandler.obtainMessage(
- MESSAGE_HANDLE_DISABLE_DELAYED, 1, 0);
- mHandler.sendMessageDelayed(disableDelayedMsg,
- ENABLE_DISABLE_DELAY_MS);
- break;
- } else {
- Slog.e(TAG, "Wait for exiting STATE_ON timeout");
- }
- }
- // Either state is exited from STATE_ON or reaches the maximum retry, we
- // should move forward to the next step.
- Slog.d(TAG, "Handle disable is finished");
- }
- break;
- }
-
- case MESSAGE_RESTORE_USER_SETTING:
- if ((msg.arg1 == RESTORE_SETTING_TO_OFF) && mEnable) {
- if (DBG) {
- Slog.d(TAG, "Restore Bluetooth state to disabled");
- }
- persistBluetoothSetting(BLUETOOTH_OFF);
- mEnableExternal = false;
- sendDisableMsg(
- BluetoothProtoEnums.ENABLE_DISABLE_REASON_RESTORE_USER_SETTING,
- mContext.getPackageName());
- } else if ((msg.arg1 == RESTORE_SETTING_TO_ON) && !mEnable) {
- if (DBG) {
- Slog.d(TAG, "Restore Bluetooth state to enabled");
- }
- mQuietEnableExternal = false;
- mEnableExternal = true;
- // waive WRITE_SECURE_SETTINGS permission check
- sendEnableMsg(false,
- BluetoothProtoEnums.ENABLE_DISABLE_REASON_RESTORE_USER_SETTING,
- mContext.getPackageName());
- }
- break;
- case MESSAGE_REGISTER_STATE_CHANGE_CALLBACK: {
- IBluetoothStateChangeCallback callback =
- (IBluetoothStateChangeCallback) msg.obj;
- mStateChangeCallbacks.register(callback);
- break;
- }
- case MESSAGE_UNREGISTER_STATE_CHANGE_CALLBACK: {
- IBluetoothStateChangeCallback callback =
- (IBluetoothStateChangeCallback) msg.obj;
- mStateChangeCallbacks.unregister(callback);
- break;
- }
- case MESSAGE_ADD_PROXY_DELAYED: {
- ProfileServiceConnections psc = mProfileServices.get(msg.arg1);
- if (psc == null) {
- break;
- }
- IBluetoothProfileServiceConnection proxy =
- (IBluetoothProfileServiceConnection) msg.obj;
- psc.addProxy(proxy);
- break;
- }
- case MESSAGE_BIND_PROFILE_SERVICE: {
- ProfileServiceConnections psc = (ProfileServiceConnections) msg.obj;
- removeMessages(MESSAGE_BIND_PROFILE_SERVICE, msg.obj);
- if (psc == null) {
- break;
- }
- psc.bindService();
- break;
- }
- case MESSAGE_BLUETOOTH_SERVICE_CONNECTED: {
- if (DBG) {
- Slog.d(TAG, "MESSAGE_BLUETOOTH_SERVICE_CONNECTED: " + msg.arg1);
- }
-
- IBinder service = (IBinder) msg.obj;
- try {
- mBluetoothLock.writeLock().lock();
- if (msg.arg1 == SERVICE_IBLUETOOTHGATT) {
- mBluetoothGatt =
- IBluetoothGatt.Stub.asInterface(Binder.allowBlocking(service));
- continueFromBleOnState();
- break;
- } // else must be SERVICE_IBLUETOOTH
-
- //Remove timeout
- mHandler.removeMessages(MESSAGE_TIMEOUT_BIND);
-
- mBinding = false;
- mBluetoothBinder = service;
- mBluetooth = IBluetooth.Stub.asInterface(Binder.allowBlocking(service));
-
- if (!isNameAndAddressSet()) {
- Message getMsg = mHandler.obtainMessage(MESSAGE_GET_NAME_AND_ADDRESS);
- mHandler.sendMessage(getMsg);
- if (mGetNameAddressOnly) {
- return;
- }
- }
-
- //Register callback object
- try {
- mBluetooth.registerCallback(mBluetoothCallback,
- mContext.getAttributionSource());
- } catch (RemoteException re) {
- Slog.e(TAG, "Unable to register BluetoothCallback", re);
- }
- //Inform BluetoothAdapter instances that service is up
- sendBluetoothServiceUpCallback();
-
- //Do enable request
- try {
- if (!mBluetooth.enable(mQuietEnable, mContext.getAttributionSource())) {
- Slog.e(TAG, "IBluetooth.enable() returned false");
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to call enable()", e);
- }
- } finally {
- mBluetoothLock.writeLock().unlock();
- }
-
- if (!mEnable) {
- waitForState(Set.of(BluetoothAdapter.STATE_ON));
- handleDisable();
- waitForState(Set.of(BluetoothAdapter.STATE_OFF,
- BluetoothAdapter.STATE_TURNING_ON,
- BluetoothAdapter.STATE_TURNING_OFF,
- BluetoothAdapter.STATE_BLE_TURNING_ON,
- BluetoothAdapter.STATE_BLE_ON,
- BluetoothAdapter.STATE_BLE_TURNING_OFF));
- }
- break;
- }
- case MESSAGE_BLUETOOTH_STATE_CHANGE: {
- int prevState = msg.arg1;
- int newState = msg.arg2;
- if (DBG) {
- Slog.d(TAG,
- "MESSAGE_BLUETOOTH_STATE_CHANGE: " + BluetoothAdapter.nameForState(
- prevState) + " > " + BluetoothAdapter.nameForState(
- newState));
- }
- mState = newState;
- bluetoothStateChangeHandler(prevState, newState);
- // handle error state transition case from TURNING_ON to OFF
- // unbind and rebind bluetooth service and enable bluetooth
- if ((prevState == BluetoothAdapter.STATE_BLE_TURNING_ON) && (newState
- == BluetoothAdapter.STATE_OFF) && (mBluetooth != null) && mEnable) {
- recoverBluetoothServiceFromError(false);
- }
- if ((prevState == BluetoothAdapter.STATE_TURNING_ON) && (newState
- == BluetoothAdapter.STATE_BLE_ON) && (mBluetooth != null) && mEnable) {
- recoverBluetoothServiceFromError(true);
- }
- // If we tried to enable BT while BT was in the process of shutting down,
- // wait for the BT process to fully tear down and then force a restart
- // here. This is a bit of a hack (b/29363429).
- if ((prevState == BluetoothAdapter.STATE_BLE_TURNING_OFF) && (newState
- == BluetoothAdapter.STATE_OFF)) {
- if (mEnable) {
- Slog.d(TAG, "Entering STATE_OFF but mEnabled is true; restarting.");
- waitForState(Set.of(BluetoothAdapter.STATE_OFF));
- Message restartMsg =
- mHandler.obtainMessage(MESSAGE_RESTART_BLUETOOTH_SERVICE);
- mHandler.sendMessageDelayed(restartMsg, getServiceRestartMs());
- }
- }
- if (newState == BluetoothAdapter.STATE_ON
- || newState == BluetoothAdapter.STATE_BLE_ON) {
- // bluetooth is working, reset the counter
- if (mErrorRecoveryRetryCounter != 0) {
- Slog.w(TAG, "bluetooth is recovered from error");
- mErrorRecoveryRetryCounter = 0;
- }
- }
- break;
- }
- case MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED: {
- Slog.e(TAG, "MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED(" + msg.arg1 + ")");
- try {
- mBluetoothLock.writeLock().lock();
- if (msg.arg1 == SERVICE_IBLUETOOTH) {
- // if service is unbinded already, do nothing and return
- if (mBluetooth == null) {
- break;
- }
- mBluetooth = null;
- } else if (msg.arg1 == SERVICE_IBLUETOOTHGATT) {
- mBluetoothGatt = null;
- break;
- } else {
- Slog.e(TAG, "Unknown argument for service disconnect!");
- break;
- }
- } finally {
- mBluetoothLock.writeLock().unlock();
- }
-
- // log the unexpected crash
- addCrashLog();
- addActiveLog(BluetoothProtoEnums.ENABLE_DISABLE_REASON_CRASH,
- mContext.getPackageName(), false);
- if (mEnable) {
- mEnable = false;
- // Send a Bluetooth Restart message
- Message restartMsg =
- mHandler.obtainMessage(MESSAGE_RESTART_BLUETOOTH_SERVICE);
- mHandler.sendMessageDelayed(restartMsg, getServiceRestartMs());
- }
-
- sendBluetoothServiceDownCallback();
-
- // Send BT state broadcast to update
- // the BT icon correctly
- if ((mState == BluetoothAdapter.STATE_TURNING_ON) || (mState
- == BluetoothAdapter.STATE_ON)) {
- bluetoothStateChangeHandler(BluetoothAdapter.STATE_ON,
- BluetoothAdapter.STATE_TURNING_OFF);
- mState = BluetoothAdapter.STATE_TURNING_OFF;
- }
- if (mState == BluetoothAdapter.STATE_TURNING_OFF) {
- bluetoothStateChangeHandler(BluetoothAdapter.STATE_TURNING_OFF,
- BluetoothAdapter.STATE_OFF);
- }
-
- mHandler.removeMessages(MESSAGE_BLUETOOTH_STATE_CHANGE);
- mState = BluetoothAdapter.STATE_OFF;
- break;
- }
- case MESSAGE_RESTART_BLUETOOTH_SERVICE: {
- mErrorRecoveryRetryCounter++;
- Slog.d(TAG, "MESSAGE_RESTART_BLUETOOTH_SERVICE: retry count="
- + mErrorRecoveryRetryCounter);
- if (mErrorRecoveryRetryCounter < MAX_ERROR_RESTART_RETRIES) {
- /* Enable without persisting the setting as
- it doesnt change when IBluetooth
- service restarts */
- mEnable = true;
- addActiveLog(BluetoothProtoEnums.ENABLE_DISABLE_REASON_RESTARTED,
- mContext.getPackageName(), true);
- handleEnable(mQuietEnable);
- } else {
- Slog.e(TAG, "Reach maximum retry to restart Bluetooth!");
- }
- break;
- }
- case MESSAGE_TIMEOUT_BIND: {
- Slog.e(TAG, "MESSAGE_TIMEOUT_BIND");
- mBluetoothLock.writeLock().lock();
- mBinding = false;
- mBluetoothLock.writeLock().unlock();
- break;
- }
- case MESSAGE_TIMEOUT_UNBIND: {
- Slog.e(TAG, "MESSAGE_TIMEOUT_UNBIND");
- mBluetoothLock.writeLock().lock();
- mUnbinding = false;
- mBluetoothLock.writeLock().unlock();
- break;
- }
-
- case MESSAGE_USER_SWITCHED: {
- if (DBG) {
- Slog.d(TAG, "MESSAGE_USER_SWITCHED");
- }
- mHandler.removeMessages(MESSAGE_USER_SWITCHED);
-
- /* disable and enable BT when detect a user switch */
- if (mBluetooth != null && isEnabled()) {
- restartForReason(BluetoothProtoEnums.ENABLE_DISABLE_REASON_USER_SWITCH);
- } else if (mBinding || mBluetooth != null) {
- Message userMsg = mHandler.obtainMessage(MESSAGE_USER_SWITCHED);
- userMsg.arg2 = 1 + msg.arg2;
- // if user is switched when service is binding retry after a delay
- mHandler.sendMessageDelayed(userMsg, USER_SWITCHED_TIME_MS);
- if (DBG) {
- Slog.d(TAG, "Retry MESSAGE_USER_SWITCHED " + userMsg.arg2);
- }
- }
- break;
- }
- case MESSAGE_USER_UNLOCKED: {
- if (DBG) {
- Slog.d(TAG, "MESSAGE_USER_UNLOCKED");
- }
- mHandler.removeMessages(MESSAGE_USER_SWITCHED);
-
- if (mEnable && !mBinding && (mBluetooth == null)) {
- // We should be connected, but we gave up for some
- // reason; maybe the Bluetooth service wasn't encryption
- // aware, so try binding again.
- if (DBG) {
- Slog.d(TAG, "Enabled but not bound; retrying after unlock");
- }
- handleEnable(mQuietEnable);
- }
- break;
- }
- case MESSAGE_INIT_FLAGS_CHANGED: {
- if (DBG) {
- Slog.d(TAG, "MESSAGE_INIT_FLAGS_CHANGED");
- }
- mHandler.removeMessages(MESSAGE_INIT_FLAGS_CHANGED);
- if (mBluetoothModeChangeHelper.isA2dpOrHearingAidConnected()) {
- Slog.i(TAG, "Delaying MESSAGE_INIT_FLAGS_CHANGED by "
- + DELAY_FOR_RETRY_INIT_FLAG_CHECK_MS
- + " ms due to existing connections");
- mHandler.sendEmptyMessageDelayed(
- MESSAGE_INIT_FLAGS_CHANGED,
- DELAY_FOR_RETRY_INIT_FLAG_CHECK_MS);
- break;
- }
- if (!isDeviceProvisioned()) {
- Slog.i(TAG, "Delaying MESSAGE_INIT_FLAGS_CHANGED by "
- + DELAY_FOR_RETRY_INIT_FLAG_CHECK_MS
- + "ms because device is not provisioned");
- mHandler.sendEmptyMessageDelayed(
- MESSAGE_INIT_FLAGS_CHANGED,
- DELAY_FOR_RETRY_INIT_FLAG_CHECK_MS);
- break;
- }
- if (mBluetooth != null && isEnabled()) {
- Slog.i(TAG, "Restarting Bluetooth due to init flag change");
- restartForReason(
- BluetoothProtoEnums.ENABLE_DISABLE_REASON_INIT_FLAGS_CHANGED);
- }
- break;
- }
- }
- }
-
- @RequiresPermission(allOf = {
- android.Manifest.permission.BLUETOOTH_CONNECT,
- android.Manifest.permission.BLUETOOTH_PRIVILEGED
- })
- private void restartForReason(int reason) {
- try {
- mBluetoothLock.readLock().lock();
- if (mBluetooth != null) {
- mBluetooth.unregisterCallback(mBluetoothCallback,
- mContext.getAttributionSource());
- }
- } catch (RemoteException re) {
- Slog.e(TAG, "Unable to unregister", re);
- } finally {
- mBluetoothLock.readLock().unlock();
- }
-
- if (mState == BluetoothAdapter.STATE_TURNING_OFF) {
- // MESSAGE_USER_SWITCHED happened right after MESSAGE_ENABLE
- bluetoothStateChangeHandler(mState, BluetoothAdapter.STATE_OFF);
- mState = BluetoothAdapter.STATE_OFF;
- }
- if (mState == BluetoothAdapter.STATE_OFF) {
- bluetoothStateChangeHandler(mState, BluetoothAdapter.STATE_TURNING_ON);
- mState = BluetoothAdapter.STATE_TURNING_ON;
- }
-
- waitForState(Set.of(BluetoothAdapter.STATE_ON));
-
- if (mState == BluetoothAdapter.STATE_TURNING_ON) {
- bluetoothStateChangeHandler(mState, BluetoothAdapter.STATE_ON);
- }
-
- unbindAllBluetoothProfileServices();
- // disable
- addActiveLog(reason, mContext.getPackageName(), false);
- handleDisable();
- // Pbap service need receive STATE_TURNING_OFF intent to close
- bluetoothStateChangeHandler(BluetoothAdapter.STATE_ON,
- BluetoothAdapter.STATE_TURNING_OFF);
-
- boolean didDisableTimeout =
- !waitForState(Set.of(BluetoothAdapter.STATE_OFF));
-
- bluetoothStateChangeHandler(BluetoothAdapter.STATE_TURNING_OFF,
- BluetoothAdapter.STATE_OFF);
- sendBluetoothServiceDownCallback();
-
- try {
- mBluetoothLock.writeLock().lock();
- if (mBluetooth != null) {
- mBluetooth = null;
- // Unbind
- mContext.unbindService(mConnection);
- }
- mBluetoothGatt = null;
- } finally {
- mBluetoothLock.writeLock().unlock();
- }
-
- //
- // If disabling Bluetooth times out, wait for an
- // additional amount of time to ensure the process is
- // shut down completely before attempting to restart.
- //
- if (didDisableTimeout) {
- SystemClock.sleep(3000);
- } else {
- SystemClock.sleep(100);
- }
-
- mHandler.removeMessages(MESSAGE_BLUETOOTH_STATE_CHANGE);
- mState = BluetoothAdapter.STATE_OFF;
- // enable
- addActiveLog(reason, mContext.getPackageName(), true);
- // mEnable flag could have been reset on disableBLE. Reenable it.
- mEnable = true;
- handleEnable(mQuietEnable);
- }
- }
-
- @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
- private void handleEnable(boolean quietMode) {
- mQuietEnable = quietMode;
-
- try {
- mBluetoothLock.writeLock().lock();
- if ((mBluetooth == null) && (!mBinding)) {
- Slog.d(TAG, "binding Bluetooth service");
- //Start bind timeout and bind
- Message timeoutMsg = mHandler.obtainMessage(MESSAGE_TIMEOUT_BIND);
- mHandler.sendMessageDelayed(timeoutMsg, TIMEOUT_BIND_MS);
- Intent i = new Intent(IBluetooth.class.getName());
- if (!doBind(i, mConnection, Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT,
- UserHandle.CURRENT)) {
- mHandler.removeMessages(MESSAGE_TIMEOUT_BIND);
- } else {
- mBinding = true;
- }
- } else if (mBluetooth != null) {
- //Enable bluetooth
- try {
- if (!mBluetooth.enable(mQuietEnable, mContext.getAttributionSource())) {
- Slog.e(TAG, "IBluetooth.enable() returned false");
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to call enable()", e);
- }
- }
- } finally {
- mBluetoothLock.writeLock().unlock();
- }
- }
-
- boolean doBind(Intent intent, ServiceConnection conn, int flags, UserHandle user) {
- ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
- intent.setComponent(comp);
- if (comp == null || !mContext.bindServiceAsUser(intent, conn, flags, user)) {
- Slog.e(TAG, "Fail to bind to: " + intent);
- return false;
- }
- return true;
- }
-
- @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
- private void handleDisable() {
- try {
- mBluetoothLock.readLock().lock();
- if (mBluetooth != null) {
- if (DBG) {
- Slog.d(TAG, "Sending off request.");
- }
- if (!mBluetooth.disable(mContext.getAttributionSource())) {
- Slog.e(TAG, "IBluetooth.disable() returned false");
- }
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to call disable()", e);
- } finally {
- mBluetoothLock.readLock().unlock();
- }
- }
-
- private boolean checkIfCallerIsForegroundUser() {
- int foregroundUser;
- int callingUser = UserHandle.getCallingUserId();
- int callingUid = Binder.getCallingUid();
- final long callingIdentity = Binder.clearCallingIdentity();
- UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
- UserInfo ui = um.getProfileParent(callingUser);
- int parentUser = (ui != null) ? ui.id : UserHandle.USER_NULL;
- int callingAppId = UserHandle.getAppId(callingUid);
- boolean valid = false;
- try {
- foregroundUser = ActivityManager.getCurrentUser();
- valid = (callingUser == foregroundUser) || parentUser == foregroundUser
- || callingAppId == Process.NFC_UID || callingAppId == mSystemUiUid
- || callingAppId == Process.SHELL_UID;
- if (DBG && !valid) {
- Slog.d(TAG, "checkIfCallerIsForegroundUser: valid=" + valid + " callingUser="
- + callingUser + " parentUser=" + parentUser + " foregroundUser="
- + foregroundUser);
- }
- } finally {
- Binder.restoreCallingIdentity(callingIdentity);
- }
- return valid;
- }
-
- private void sendBleStateChanged(int prevState, int newState) {
- if (DBG) {
- Slog.d(TAG,
- "Sending BLE State Change: " + BluetoothAdapter.nameForState(prevState) + " > "
- + BluetoothAdapter.nameForState(newState));
- }
- // Send broadcast message to everyone else
- Intent intent = new Intent(BluetoothAdapter.ACTION_BLE_STATE_CHANGED);
- intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, prevState);
- intent.putExtra(BluetoothAdapter.EXTRA_STATE, newState);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
- mContext.sendBroadcastAsUser(intent, UserHandle.ALL, null, getTempAllowlistBroadcastOptions());
- }
-
- private boolean isBleState(int state) {
- switch (state) {
- case BluetoothAdapter.STATE_BLE_ON:
- case BluetoothAdapter.STATE_BLE_TURNING_ON:
- case BluetoothAdapter.STATE_BLE_TURNING_OFF:
- return true;
- }
- return false;
- }
-
- @RequiresPermission(allOf = {
- android.Manifest.permission.BLUETOOTH_CONNECT,
- android.Manifest.permission.BLUETOOTH_PRIVILEGED,
- })
- private void bluetoothStateChangeHandler(int prevState, int newState) {
- boolean isStandardBroadcast = true;
- if (prevState == newState) { // No change. Nothing to do.
- return;
- }
- // Notify all proxy objects first of adapter state change
- if (newState == BluetoothAdapter.STATE_BLE_ON || newState == BluetoothAdapter.STATE_OFF) {
- boolean intermediate_off = (prevState == BluetoothAdapter.STATE_TURNING_OFF
- && newState == BluetoothAdapter.STATE_BLE_ON);
-
- if (newState == BluetoothAdapter.STATE_OFF) {
- // If Bluetooth is off, send service down event to proxy objects, and unbind
- if (DBG) {
- Slog.d(TAG, "Bluetooth is complete send Service Down");
- }
- sendBluetoothServiceDownCallback();
- unbindAndFinish();
- sendBleStateChanged(prevState, newState);
-
- /* Currently, the OFF intent is broadcasted externally only when we transition
- * from TURNING_OFF to BLE_ON state. So if the previous state is a BLE state,
- * we are guaranteed that the OFF intent has been broadcasted earlier and we
- * can safely skip it.
- * Conversely, if the previous state is not a BLE state, it indicates that some
- * sort of crash has occurred, moving us directly to STATE_OFF without ever
- * passing through BLE_ON. We should broadcast the OFF intent in this case. */
- isStandardBroadcast = !isBleState(prevState);
-
- } else if (!intermediate_off) {
- // connect to GattService
- if (DBG) {
- Slog.d(TAG, "Bluetooth is in LE only mode");
- }
- if (mBluetoothGatt != null || !mContext.getPackageManager()
- .hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
- continueFromBleOnState();
- } else {
- if (DBG) {
- Slog.d(TAG, "Binding Bluetooth GATT service");
- }
- Intent i = new Intent(IBluetoothGatt.class.getName());
- doBind(i, mConnection, Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT,
- UserHandle.CURRENT);
- }
- sendBleStateChanged(prevState, newState);
- //Don't broadcase this as std intent
- isStandardBroadcast = false;
-
- } else if (intermediate_off) {
- if (DBG) {
- Slog.d(TAG, "Intermediate off, back to LE only mode");
- }
- // For LE only mode, broadcast as is
- sendBleStateChanged(prevState, newState);
- sendBluetoothStateCallback(false); // BT is OFF for general users
- // Broadcast as STATE_OFF
- newState = BluetoothAdapter.STATE_OFF;
- sendBrEdrDownCallback(mContext.getAttributionSource());
- }
- } else if (newState == BluetoothAdapter.STATE_ON) {
- boolean isUp = (newState == BluetoothAdapter.STATE_ON);
- sendBluetoothStateCallback(isUp);
- sendBleStateChanged(prevState, newState);
-
- } else if (newState == BluetoothAdapter.STATE_BLE_TURNING_ON
- || newState == BluetoothAdapter.STATE_BLE_TURNING_OFF) {
- sendBleStateChanged(prevState, newState);
- isStandardBroadcast = false;
-
- } else if (newState == BluetoothAdapter.STATE_TURNING_ON
- || newState == BluetoothAdapter.STATE_TURNING_OFF) {
- sendBleStateChanged(prevState, newState);
- }
-
- if (isStandardBroadcast) {
- if (prevState == BluetoothAdapter.STATE_BLE_ON) {
- // Show prevState of BLE_ON as OFF to standard users
- prevState = BluetoothAdapter.STATE_OFF;
- }
- if (DBG) {
- Slog.d(TAG,
- "Sending State Change: " + BluetoothAdapter.nameForState(prevState) + " > "
- + BluetoothAdapter.nameForState(newState));
- }
- Intent intent = new Intent(BluetoothAdapter.ACTION_STATE_CHANGED);
- intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, prevState);
- intent.putExtra(BluetoothAdapter.EXTRA_STATE, newState);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
- mContext.sendBroadcastAsUser(intent, UserHandle.ALL, null,
- getTempAllowlistBroadcastOptions());
- }
- }
-
- private boolean waitForState(Set<Integer> states) {
- int i = 0;
- while (i < 10) {
- try {
- mBluetoothLock.readLock().lock();
- if (mBluetooth == null) {
- break;
- }
- if (states.contains(mBluetooth.getState())) {
- return true;
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "getState()", e);
- break;
- } finally {
- mBluetoothLock.readLock().unlock();
- }
- SystemClock.sleep(300);
- i++;
- }
- Slog.e(TAG, "waitForState " + states + " time out");
- return false;
- }
-
- private void sendDisableMsg(int reason, String packageName) {
- mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_DISABLE));
- addActiveLog(reason, packageName, false);
- }
-
- private void sendEnableMsg(boolean quietMode, int reason, String packageName) {
- sendEnableMsg(quietMode, reason, packageName, false);
- }
-
- private void sendEnableMsg(boolean quietMode, int reason, String packageName, boolean isBle) {
- mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_ENABLE, quietMode ? 1 : 0,
- isBle ? 1 : 0));
- addActiveLog(reason, packageName, true);
- mLastEnabledTime = SystemClock.elapsedRealtime();
- }
-
- private void addActiveLog(int reason, String packageName, boolean enable) {
- synchronized (mActiveLogs) {
- if (mActiveLogs.size() > ACTIVE_LOG_MAX_SIZE) {
- mActiveLogs.remove();
- }
- mActiveLogs.add(
- new ActiveLog(reason, packageName, enable, System.currentTimeMillis()));
- }
-
- int state = enable ? FrameworkStatsLog.BLUETOOTH_ENABLED_STATE_CHANGED__STATE__ENABLED :
- FrameworkStatsLog.BLUETOOTH_ENABLED_STATE_CHANGED__STATE__DISABLED;
- FrameworkStatsLog.write_non_chained(FrameworkStatsLog.BLUETOOTH_ENABLED_STATE_CHANGED,
- Binder.getCallingUid(), null, state, reason, packageName);
- }
-
- private void addCrashLog() {
- synchronized (mCrashTimestamps) {
- if (mCrashTimestamps.size() == CRASH_LOG_MAX_SIZE) {
- mCrashTimestamps.removeFirst();
- }
- mCrashTimestamps.add(System.currentTimeMillis());
- mCrashes++;
- }
- }
-
- @RequiresPermission(allOf = {
- android.Manifest.permission.BLUETOOTH_CONNECT,
- android.Manifest.permission.BLUETOOTH_PRIVILEGED,
- })
- private void recoverBluetoothServiceFromError(boolean clearBle) {
- Slog.e(TAG, "recoverBluetoothServiceFromError");
- try {
- mBluetoothLock.readLock().lock();
- if (mBluetooth != null) {
- //Unregister callback object
- mBluetooth.unregisterCallback(mBluetoothCallback, mContext.getAttributionSource());
- }
- } catch (RemoteException re) {
- Slog.e(TAG, "Unable to unregister", re);
- } finally {
- mBluetoothLock.readLock().unlock();
- }
-
- SystemClock.sleep(500);
-
- // disable
- addActiveLog(BluetoothProtoEnums.ENABLE_DISABLE_REASON_START_ERROR,
- mContext.getPackageName(), false);
- handleDisable();
-
- waitForState(Set.of(BluetoothAdapter.STATE_OFF));
-
- sendBluetoothServiceDownCallback();
-
- try {
- mBluetoothLock.writeLock().lock();
- if (mBluetooth != null) {
- mBluetooth = null;
- // Unbind
- mContext.unbindService(mConnection);
- }
- mBluetoothGatt = null;
- } finally {
- mBluetoothLock.writeLock().unlock();
- }
-
- mHandler.removeMessages(MESSAGE_BLUETOOTH_STATE_CHANGE);
- mState = BluetoothAdapter.STATE_OFF;
-
- if (clearBle) {
- clearBleApps();
- }
-
- mEnable = false;
-
- // Send a Bluetooth Restart message to reenable bluetooth
- Message restartMsg = mHandler.obtainMessage(MESSAGE_RESTART_BLUETOOTH_SERVICE);
- mHandler.sendMessageDelayed(restartMsg, ERROR_RESTART_TIME_MS);
- }
-
- private boolean isBluetoothDisallowed() {
- final long callingIdentity = Binder.clearCallingIdentity();
- try {
- return mContext.getSystemService(UserManager.class)
- .hasUserRestriction(UserManager.DISALLOW_BLUETOOTH, UserHandle.SYSTEM);
- } finally {
- Binder.restoreCallingIdentity(callingIdentity);
- }
- }
-
- /**
- * Disables BluetoothOppLauncherActivity component, so the Bluetooth sharing option is not
- * offered to the user if Bluetooth or sharing is disallowed. Puts the component to its default
- * state if Bluetooth is not disallowed.
- *
- * @param userId user to disable bluetooth sharing for.
- * @param bluetoothSharingDisallowed whether bluetooth sharing is disallowed.
- */
- private void updateOppLauncherComponentState(int userId, boolean bluetoothSharingDisallowed) {
- final ComponentName oppLauncherComponent = new ComponentName("com.android.bluetooth",
- "com.android.bluetooth.opp.BluetoothOppLauncherActivity");
- final int newState =
- bluetoothSharingDisallowed ? PackageManager.COMPONENT_ENABLED_STATE_DISABLED
- : PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
- try {
- final IPackageManager imp = AppGlobals.getPackageManager();
- imp.setComponentEnabledSetting(oppLauncherComponent, newState,
- PackageManager.DONT_KILL_APP, userId);
- } catch (Exception e) {
- // The component was not found, do nothing.
- }
- }
-
- private int getServiceRestartMs() {
- return (mErrorRecoveryRetryCounter + 1) * SERVICE_RESTART_TIME_MS;
- }
-
- @Override
- public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
- if (!DumpUtils.checkDumpPermission(mContext, TAG, writer)) {
- return;
- }
- if ((args.length > 0) && args[0].startsWith("--proto")) {
- dumpProto(fd);
- return;
- }
- String errorMsg = null;
-
- writer.println("Bluetooth Status");
- writer.println(" enabled: " + isEnabled());
- writer.println(" state: " + BluetoothAdapter.nameForState(mState));
- writer.println(" address: " + mAddress);
- writer.println(" name: " + mName);
- if (mEnable) {
- long onDuration = SystemClock.elapsedRealtime() - mLastEnabledTime;
- String onDurationString = String.format(Locale.US, "%02d:%02d:%02d.%03d",
- (int) (onDuration / (1000 * 60 * 60)),
- (int) ((onDuration / (1000 * 60)) % 60), (int) ((onDuration / 1000) % 60),
- (int) (onDuration % 1000));
- writer.println(" time since enabled: " + onDurationString);
- }
-
- if (mActiveLogs.size() == 0) {
- writer.println("\nBluetooth never enabled!");
- } else {
- writer.println("\nEnable log:");
- for (ActiveLog log : mActiveLogs) {
- writer.println(" " + log);
- }
- }
-
- writer.println(
- "\nBluetooth crashed " + mCrashes + " time" + (mCrashes == 1 ? "" : "s"));
- if (mCrashes == CRASH_LOG_MAX_SIZE) {
- writer.println("(last " + CRASH_LOG_MAX_SIZE + ")");
- }
- for (Long time : mCrashTimestamps) {
- writer.println(" " + timeToLog(time));
- }
-
- writer.println("\n" + mBleApps.size() + " BLE app" + (mBleApps.size() == 1 ? "" : "s")
- + " registered");
- for (ClientDeathRecipient app : mBleApps.values()) {
- writer.println(" " + app.getPackageName());
- }
-
- writer.println("\nBluetoothManagerService:");
- writer.println(" mEnable:" + mEnable);
- writer.println(" mQuietEnable:" + mQuietEnable);
- writer.println(" mEnableExternal:" + mEnableExternal);
- writer.println(" mQuietEnableExternal:" + mQuietEnableExternal);
-
- writer.println("");
- writer.flush();
- if (args.length == 0) {
- // Add arg to produce output
- args = new String[1];
- args[0] = "--print";
- }
-
- if (mBluetoothBinder == null) {
- errorMsg = "Bluetooth Service not connected";
- } else {
- try {
- mBluetoothBinder.dump(fd, args);
- } catch (RemoteException re) {
- errorMsg = "RemoteException while dumping Bluetooth Service";
- }
- }
- if (errorMsg != null) {
- writer.println(errorMsg);
- }
- }
-
- private void dumpProto(FileDescriptor fd) {
- final ProtoOutputStream proto = new ProtoOutputStream(fd);
- proto.write(BluetoothManagerServiceDumpProto.ENABLED, isEnabled());
- proto.write(BluetoothManagerServiceDumpProto.STATE, mState);
- proto.write(BluetoothManagerServiceDumpProto.STATE_NAME,
- BluetoothAdapter.nameForState(mState));
- proto.write(BluetoothManagerServiceDumpProto.ADDRESS, mAddress);
- proto.write(BluetoothManagerServiceDumpProto.NAME, mName);
- if (mEnable) {
- proto.write(BluetoothManagerServiceDumpProto.LAST_ENABLED_TIME_MS, mLastEnabledTime);
- }
- proto.write(BluetoothManagerServiceDumpProto.CURR_TIMESTAMP_MS,
- SystemClock.elapsedRealtime());
- for (ActiveLog log : mActiveLogs) {
- long token = proto.start(BluetoothManagerServiceDumpProto.ACTIVE_LOGS);
- log.dump(proto);
- proto.end(token);
- }
- proto.write(BluetoothManagerServiceDumpProto.NUM_CRASHES, mCrashes);
- proto.write(BluetoothManagerServiceDumpProto.CRASH_LOG_MAXED,
- mCrashes == CRASH_LOG_MAX_SIZE);
- for (Long time : mCrashTimestamps) {
- proto.write(BluetoothManagerServiceDumpProto.CRASH_TIMESTAMPS_MS, time);
- }
- proto.write(BluetoothManagerServiceDumpProto.NUM_BLE_APPS, mBleApps.size());
- for (ClientDeathRecipient app : mBleApps.values()) {
- proto.write(BluetoothManagerServiceDumpProto.BLE_APP_PACKAGE_NAMES,
- app.getPackageName());
- }
- proto.flush();
- }
-
- private static String getEnableDisableReasonString(int reason) {
- switch (reason) {
- case BluetoothProtoEnums.ENABLE_DISABLE_REASON_APPLICATION_REQUEST:
- return "APPLICATION_REQUEST";
- case BluetoothProtoEnums.ENABLE_DISABLE_REASON_AIRPLANE_MODE:
- return "AIRPLANE_MODE";
- case BluetoothProtoEnums.ENABLE_DISABLE_REASON_DISALLOWED:
- return "DISALLOWED";
- case BluetoothProtoEnums.ENABLE_DISABLE_REASON_RESTARTED:
- return "RESTARTED";
- case BluetoothProtoEnums.ENABLE_DISABLE_REASON_START_ERROR:
- return "START_ERROR";
- case BluetoothProtoEnums.ENABLE_DISABLE_REASON_SYSTEM_BOOT:
- return "SYSTEM_BOOT";
- case BluetoothProtoEnums.ENABLE_DISABLE_REASON_CRASH:
- return "CRASH";
- case BluetoothProtoEnums.ENABLE_DISABLE_REASON_USER_SWITCH:
- return "USER_SWITCH";
- case BluetoothProtoEnums.ENABLE_DISABLE_REASON_RESTORE_USER_SETTING:
- return "RESTORE_USER_SETTING";
- case BluetoothProtoEnums.ENABLE_DISABLE_REASON_FACTORY_RESET:
- return "FACTORY_RESET";
- case BluetoothProtoEnums.ENABLE_DISABLE_REASON_INIT_FLAGS_CHANGED:
- return "INIT_FLAGS_CHANGED";
- case BluetoothProtoEnums.ENABLE_DISABLE_REASON_UNSPECIFIED:
- default: return "UNKNOWN[" + reason + "]";
- }
- }
-
- @SuppressLint("AndroidFrameworkRequiresPermission")
- private static boolean checkPermissionForDataDelivery(Context context, String permission,
- AttributionSource attributionSource, String message) {
- final int result = PermissionChecker.checkPermissionForDataDeliveryFromDataSource(
- context, permission, PID_UNKNOWN,
- new AttributionSource(context.getAttributionSource(), attributionSource), message);
- if (result == PERMISSION_GRANTED) {
- return true;
- }
-
- final String msg = "Need " + permission + " permission for " + attributionSource + ": "
- + message;
- if (result == PERMISSION_HARD_DENIED) {
- throw new SecurityException(msg);
- } else {
- Log.w(TAG, msg);
- return false;
- }
- }
-
- /**
- * Returns true if the BLUETOOTH_CONNECT permission is granted for the calling app. Returns
- * false if the result is a soft denial. Throws SecurityException if the result is a hard
- * denial.
- *
- * <p>Should be used in situations where the app op should not be noted.
- */
- @SuppressLint("AndroidFrameworkRequiresPermission")
- @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
- public static boolean checkConnectPermissionForDataDelivery(
- Context context, AttributionSource attributionSource, String message) {
- return checkPermissionForDataDelivery(context, BLUETOOTH_CONNECT,
- attributionSource, message);
- }
-
- static @NonNull Bundle getTempAllowlistBroadcastOptions() {
- final long duration = 10_000;
- final BroadcastOptions bOptions = BroadcastOptions.makeBasic();
- bOptions.setTemporaryAppAllowlist(duration,
- TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED,
- PowerExemptionManager.REASON_BLUETOOTH_BROADCAST, "");
- return bOptions.toBundle();
- }
-}
diff --git a/services/core/java/com/android/server/BluetoothModeChangeHelper.java b/services/core/java/com/android/server/BluetoothModeChangeHelper.java
deleted file mode 100644
index 3642e4dccf34..000000000000
--- a/services/core/java/com/android/server/BluetoothModeChangeHelper.java
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
- * 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;
-
-import android.annotation.RequiresPermission;
-import android.bluetooth.BluetoothA2dp;
-import android.bluetooth.BluetoothAdapter;
-import android.bluetooth.BluetoothHearingAid;
-import android.bluetooth.BluetoothProfile;
-import android.bluetooth.BluetoothProfile.ServiceListener;
-import android.content.Context;
-import android.content.res.Resources;
-import android.provider.Settings;
-import android.widget.Toast;
-
-import com.android.internal.R;
-import com.android.internal.annotations.VisibleForTesting;
-
-/**
- * Helper class that handles callout and callback methods without
- * complex logic.
- */
-public class BluetoothModeChangeHelper {
- private volatile BluetoothA2dp mA2dp;
- private volatile BluetoothHearingAid mHearingAid;
- private final BluetoothAdapter mAdapter;
- private final Context mContext;
-
- BluetoothModeChangeHelper(Context context) {
- mAdapter = BluetoothAdapter.getDefaultAdapter();
- mContext = context;
-
- mAdapter.getProfileProxy(mContext, mProfileServiceListener, BluetoothProfile.A2DP);
- mAdapter.getProfileProxy(mContext, mProfileServiceListener,
- BluetoothProfile.HEARING_AID);
- }
-
- private final ServiceListener mProfileServiceListener = new ServiceListener() {
- @Override
- public void onServiceConnected(int profile, BluetoothProfile proxy) {
- // Setup Bluetooth profile proxies
- switch (profile) {
- case BluetoothProfile.A2DP:
- mA2dp = (BluetoothA2dp) proxy;
- break;
- case BluetoothProfile.HEARING_AID:
- mHearingAid = (BluetoothHearingAid) proxy;
- break;
- default:
- break;
- }
- }
-
- @Override
- public void onServiceDisconnected(int profile) {
- // Clear Bluetooth profile proxies
- switch (profile) {
- case BluetoothProfile.A2DP:
- mA2dp = null;
- break;
- case BluetoothProfile.HEARING_AID:
- mHearingAid = null;
- break;
- default:
- break;
- }
- }
- };
-
- @VisibleForTesting
- public boolean isA2dpOrHearingAidConnected() {
- return isA2dpConnected() || isHearingAidConnected();
- }
-
- @VisibleForTesting
- public boolean isBluetoothOn() {
- final BluetoothAdapter adapter = mAdapter;
- if (adapter == null) {
- return false;
- }
- return adapter.getLeState() == BluetoothAdapter.STATE_ON;
- }
-
- @VisibleForTesting
- public boolean isAirplaneModeOn() {
- return Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.AIRPLANE_MODE_ON, 0) == 1;
- }
-
- @VisibleForTesting
- @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
- public void onAirplaneModeChanged(BluetoothManagerService managerService) {
- managerService.onAirplaneModeChanged();
- }
-
- @VisibleForTesting
- public int getSettingsInt(String name) {
- return Settings.Global.getInt(mContext.getContentResolver(),
- name, 0);
- }
-
- @VisibleForTesting
- public void setSettingsInt(String name, int value) {
- Settings.Global.putInt(mContext.getContentResolver(),
- name, value);
- }
-
- @VisibleForTesting
- public void showToastMessage() {
- Resources r = mContext.getResources();
- final CharSequence text = r.getString(
- R.string.bluetooth_airplane_mode_toast, 0);
- Toast.makeText(mContext, text, Toast.LENGTH_LONG).show();
- }
-
- private boolean isA2dpConnected() {
- final BluetoothA2dp a2dp = mA2dp;
- if (a2dp == null) {
- return false;
- }
- return a2dp.getConnectedDevices().size() > 0;
- }
-
- private boolean isHearingAidConnected() {
- final BluetoothHearingAid hearingAid = mHearingAid;
- if (hearingAid == null) {
- return false;
- }
- return hearingAid.getConnectedDevices().size() > 0;
- }
-}
diff --git a/services/core/java/com/android/server/BluetoothService.java b/services/core/java/com/android/server/BluetoothService.java
deleted file mode 100644
index 1a1eecd0f439..000000000000
--- a/services/core/java/com/android/server/BluetoothService.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2015 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;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.bluetooth.BluetoothAdapter;
-import android.content.Context;
-import android.os.UserManager;
-
-import com.android.server.SystemService.TargetUser;
-
-class BluetoothService extends SystemService {
- private BluetoothManagerService mBluetoothManagerService;
- private boolean mInitialized = false;
-
- public BluetoothService(Context context) {
- super(context);
- mBluetoothManagerService = new BluetoothManagerService(context);
- }
-
- private void initialize() {
- if (!mInitialized) {
- mBluetoothManagerService.handleOnBootPhase();
- mInitialized = true;
- }
- }
-
- @Override
- public void onStart() {
- }
-
- @Override
- public void onBootPhase(int phase) {
- if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
- publishBinderService(BluetoothAdapter.BLUETOOTH_MANAGER_SERVICE,
- mBluetoothManagerService);
- } else if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY &&
- !UserManager.isHeadlessSystemUserMode()) {
- initialize();
- }
- }
-
- @Override
- public void onUserSwitching(@Nullable TargetUser from, @NonNull TargetUser to) {
- if (!mInitialized) {
- initialize();
- } else {
- mBluetoothManagerService.handleOnSwitchUser(to.getUserIdentifier());
- }
- }
-
- @Override
- public void onUserUnlocking(@NonNull TargetUser user) {
- mBluetoothManagerService.handleOnUnlockUser(user.getUserIdentifier());
- }
-}
diff --git a/services/core/java/com/android/server/BootReceiver.java b/services/core/java/com/android/server/BootReceiver.java
index 48f5b51a8404..26d76a848a02 100644
--- a/services/core/java/com/android/server/BootReceiver.java
+++ b/services/core/java/com/android/server/BootReceiver.java
@@ -51,15 +51,12 @@ import com.android.internal.util.XmlUtils;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
-import java.io.BufferedReader;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
-import java.io.InputStreamReader;
-import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.regex.Matcher;
@@ -128,11 +125,9 @@ public class BootReceiver extends BroadcastReceiver {
// Location of ftrace pipe for notifications from kernel memory tools like KFENCE and KASAN.
private static final String ERROR_REPORT_TRACE_PIPE =
"/sys/kernel/tracing/instances/bootreceiver/trace_pipe";
- // Stop after sending this many reports. See http://b/182159975.
+ // Stop after sending too many reports. See http://b/182159975.
private static final int MAX_ERROR_REPORTS = 8;
private static int sSentReports = 0;
- // Avoid reporing the same bug from processDmesg() twice.
- private static String sLastReportedBug = null;
@Override
public void onReceive(final Context context, Intent intent) {
@@ -175,7 +170,8 @@ public class BootReceiver extends BroadcastReceiver {
* We read from /sys/kernel/tracing/instances/bootreceiver/trace_pipe (set up by the
* system), which will print an ftrace event when a memory corruption is detected in the
* kernel.
- * When an error is detected, we run the dmesg shell command and process its output.
+ * When an error is detected, we set the dmesg.start system property to notify dmesgd
+ * about a new error.
*/
OnFileDescriptorEventListener traceCallback = new OnFileDescriptorEventListener() {
final int mBufferSize = 1024;
@@ -191,8 +187,7 @@ public class BootReceiver extends BroadcastReceiver {
* line, but to be on the safe side we keep reading until the buffer
* contains a '\n' character. In the unlikely case of a very buggy kernel
* the buffer may contain multiple tracing events that cannot be attributed
- * to particular error reports. In that case the latest error report
- * residing in dmesg is picked.
+ * to particular error reports. dmesgd will take care of all errors.
*/
try {
int nbytes = Os.read(fd, mTraceBuffer, 0, mBufferSize);
@@ -201,10 +196,13 @@ public class BootReceiver extends BroadcastReceiver {
if (readStr.indexOf("\n") == -1) {
return OnFileDescriptorEventListener.EVENT_INPUT;
}
- processDmesg(context);
+ if (sSentReports < MAX_ERROR_REPORTS) {
+ SystemProperties.set("dmesgd.start", "1");
+ sSentReports++;
+ }
}
} catch (Exception e) {
- Slog.wtf(TAG, "Error processing dmesg output", e);
+ Slog.wtf(TAG, "Error watching for trace events", e);
return 0; // Unregister the handler.
}
return OnFileDescriptorEventListener.EVENT_INPUT;
@@ -216,157 +214,6 @@ public class BootReceiver extends BroadcastReceiver {
}
- /**
- * Check whether it is safe to collect this dmesg line or not.
- *
- * We only consider lines belonging to KASAN or KFENCE reports, but those may still contain
- * user information, namely the process name:
- *
- * [ 69.547684] [ T6006]c7 6006 CPU: 7 PID: 6006 Comm: sh Tainted: G S C O ...
- *
- * hardware information:
- *
- * [ 69.558923] [ T6006]c7 6006 Hardware name: <REDACTED>
- *
- * or register dump (in KASAN reports only):
- *
- * ... RIP: 0033:0x7f96443109da
- * ... RSP: 002b:00007ffcf0b51b08 EFLAGS: 00000202 ORIG_RAX: 00000000000000af
- * ... RAX: ffffffffffffffda RBX: 000055dc3ee521a0 RCX: 00007f96443109da
- *
- * (on x86_64)
- *
- * ... pc : lpm_cpuidle_enter+0x258/0x384
- * ... lr : lpm_cpuidle_enter+0x1d4/0x384
- * ... sp : ffffff800820bea0
- * ... x29: ffffff800820bea0 x28: ffffffc2305f3ce0
- * ... ...
- * ... x9 : 0000000000000001 x8 : 0000000000000000
- * (on ARM64)
- *
- * We therefore omit the lines that contain "Comm:", "Hardware name:", or match the general
- * purpose register regexp.
- *
- * @param line single line of `dmesg` output.
- * @return updated line with sensitive data removed, or null if the line must be skipped.
- */
- public static String stripSensitiveData(String line) {
- /*
- * General purpose register names begin with "R" on x86_64 and "x" on ARM64. The letter is
- * followed by two symbols (numbers, letters or spaces) and a colon, which is followed by a
- * 16-digit hex number. The optional "_" prefix accounts for ORIG_RAX on x86.
- */
- final String registerRegex = "[ _][Rx]..: [0-9a-f]{16}";
- final Pattern registerPattern = Pattern.compile(registerRegex);
-
- final String corruptionRegex = "Detected corrupted memory at 0x[0-9a-f]+";
- final Pattern corruptionPattern = Pattern.compile(corruptionRegex);
-
- if (line.contains("Comm: ") || line.contains("Hardware name: ")) return null;
- if (registerPattern.matcher(line).find()) return null;
-
- Matcher cm = corruptionPattern.matcher(line);
- if (cm.find()) return cm.group(0);
- return line;
- }
-
- /*
- * Search dmesg output for the last error report from KFENCE or KASAN and copy it to Dropbox.
- *
- * Example report printed by the kernel (redacted to fit into 100 column limit):
- * [ 69.236673] [ T6006]c7 6006 =========================================================
- * [ 69.245688] [ T6006]c7 6006 BUG: KFENCE: out-of-bounds in kfence_handle_page_fault
- * [ 69.245688] [ T6006]c7 6006
- * [ 69.257816] [ T6006]c7 6006 Out-of-bounds access at 0xffffffca75c45000 (...)
- * [ 69.267102] [ T6006]c7 6006 kfence_handle_page_fault+0x1bc/0x208
- * [ 69.273536] [ T6006]c7 6006 __do_kernel_fault+0xa8/0x11c
- * ...
- * [ 69.355427] [ T6006]c7 6006 kfence-#2 [0xffffffca75c46f30-0xffffffca75c46fff, ...
- * [ 69.366938] [ T6006]c7 6006 __d_alloc+0x3c/0x1b4
- * [ 69.371946] [ T6006]c7 6006 d_alloc_parallel+0x48/0x538
- * [ 69.377578] [ T6006]c7 6006 __lookup_slow+0x60/0x15c
- * ...
- * [ 69.547684] [ T6006]c7 6006 CPU: 7 PID: 6006 Comm: sh Tainted: G S C O ...
- * [ 69.558923] [ T6006]c7 6006 Hardware name: <REDACTED>
- * [ 69.567059] [ T6006]c7 6006 =========================================================
- *
- * We rely on the kernel printing task/CPU ID for every log line (CONFIG_PRINTK_CALLER=y).
- * E.g. for the above report the task ID is T6006. Report lines may interleave with lines
- * printed by other kernel tasks, which will have different task IDs, so in order to collect
- * the report we:
- * - find the next occurrence of the "BUG: " line in the kernel log, parse it to obtain the
- * task ID and the tool name;
- * - scan the rest of dmesg output and pick every line that has the same task ID, until we
- * encounter a horizontal ruler, i.e.:
- * [ 69.567059] [ T6006]c7 6006 ======================================================
- * - add that line to the error report, unless it contains sensitive information (see
- * logLinePotentiallySensitive())
- * - repeat the above steps till the last report is found.
- */
- private void processDmesg(Context ctx) throws IOException {
- if (sSentReports == MAX_ERROR_REPORTS) return;
- /*
- * Only SYSTEM_KASAN_ERROR_REPORT and SYSTEM_KFENCE_ERROR_REPORT are supported at the
- * moment.
- */
- final String[] bugTypes = new String[] { "KASAN", "KFENCE" };
- final String tsRegex = "^\\[[^]]+\\] ";
- final String bugRegex =
- tsRegex + "\\[([^]]+)\\].*BUG: (" + String.join("|", bugTypes) + "):";
- final Pattern bugPattern = Pattern.compile(bugRegex);
-
- Process p = new ProcessBuilder("/system/bin/timeout", "-k", "90s", "60s",
- "dmesg").redirectErrorStream(true).start();
- BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
- String line = null;
- String task = null;
- String tool = null;
- String bugTitle = null;
- Pattern reportPattern = null;
- ArrayList<String> currentReport = null;
- String lastReport = null;
-
- while ((line = reader.readLine()) != null) {
- if (currentReport == null) {
- Matcher bm = bugPattern.matcher(line);
- if (!bm.find()) continue;
- task = bm.group(1);
- tool = bm.group(2);
- bugTitle = line;
- currentReport = new ArrayList<String>();
- currentReport.add(line);
- String reportRegex = tsRegex + "\\[" + task + "\\].*";
- reportPattern = Pattern.compile(reportRegex);
- continue;
- }
- Matcher rm = reportPattern.matcher(line);
- if (!rm.matches()) continue;
- if ((line = stripSensitiveData(line)) == null) continue;
- if (line.contains("================================")) {
- lastReport = String.join("\n", currentReport);
- currentReport = null;
- continue;
- }
- currentReport.add(line);
- }
- if (lastReport == null) {
- Slog.w(TAG, "Could not find report in dmesg.");
- return;
- }
-
- // Avoid sending the same bug report twice.
- if (bugTitle.equals(sLastReportedBug)) return;
-
- final String reportTag = "SYSTEM_" + tool + "_ERROR_REPORT";
- final DropBoxManager db = ctx.getSystemService(DropBoxManager.class);
- final String headers = getCurrentBootHeaders();
- final String reportText = headers + lastReport;
-
- addTextToDropBox(db, reportTag, reportText, "/dev/kmsg", LOG_SIZE);
- sLastReportedBug = bugTitle;
- sSentReports++;
- }
-
private void removeOldUpdatePackages(Context context) {
Downloads.removeAllDownloadsByPackage(context, OLD_UPDATER_PACKAGE, OLD_UPDATER_CLASS);
}
@@ -484,7 +331,9 @@ public class BootReceiver extends BroadcastReceiver {
HashMap<String, Long> timestamps = readTimestamps();
try {
if (proto) {
- db.addFile(TAG_TOMBSTONE_PROTO, tombstone, 0);
+ if (recordFileTimestamp(tombstone, timestamps)) {
+ db.addFile(TAG_TOMBSTONE_PROTO, tombstone, 0);
+ }
} else {
final String headers = getBootHeadersToLogAndUpdate();
addFileToDropBox(db, timestamps, headers, tombstone.getPath(), LOG_SIZE,
@@ -526,16 +375,10 @@ public class BootReceiver extends BroadcastReceiver {
if (db == null || !db.isTagEnabled(tag)) return; // Logging disabled
File file = new File(filename);
- long fileTime = file.lastModified();
- if (fileTime <= 0) return; // File does not exist
-
- if (timestamps.containsKey(filename) && timestamps.get(filename) == fileTime) {
- return; // Already logged this particular file
+ if (!recordFileTimestamp(file, timestamps)) {
+ return;
}
- timestamps.put(filename, fileTime);
-
-
String fileContents = FileUtils.readTextFile(file, maxSize, TAG_TRUNCATED);
String text = headers + fileContents + footers;
// Create an additional report for system server native crashes, with a special tag.
@@ -548,6 +391,19 @@ public class BootReceiver extends BroadcastReceiver {
addTextToDropBox(db, tag, text, filename, maxSize);
}
+ private static boolean recordFileTimestamp(File file, HashMap<String, Long> timestamps) {
+ final long fileTime = file.lastModified();
+ if (fileTime <= 0) return false; // File does not exist
+
+ final String filename = file.getPath();
+ if (timestamps.containsKey(filename) && timestamps.get(filename) == fileTime) {
+ return false; // Already logged this particular file
+ }
+
+ timestamps.put(filename, fileTime);
+ return true;
+ }
+
private static void addTextToDropBox(DropBoxManager db, String tag, String text,
String filename, int maxSize) {
Slog.i(TAG, "Copying " + filename + " to DropBox (" + tag + ")");
diff --git a/services/core/java/com/android/server/ConsumerIrService.java b/services/core/java/com/android/server/ConsumerIrService.java
index 2ed6c77baa0d..c4e84a4cd138 100644
--- a/services/core/java/com/android/server/ConsumerIrService.java
+++ b/services/core/java/com/android/server/ConsumerIrService.java
@@ -19,17 +19,19 @@ package com.android.server;
import android.content.Context;
import android.content.pm.PackageManager;
import android.hardware.IConsumerIrService;
+import android.hardware.ir.ConsumerIrFreqRange;
+import android.hardware.ir.IConsumerIr;
import android.os.PowerManager;
+import android.os.RemoteException;
+import android.os.ServiceManager;
import android.util.Slog;
-import java.lang.RuntimeException;
-
public class ConsumerIrService extends IConsumerIrService.Stub {
private static final String TAG = "ConsumerIrService";
private static final int MAX_XMIT_TIME = 2000000; /* in microseconds */
- private static native boolean halOpen();
+ private static native boolean getHidlHalService();
private static native int halTransmit(int carrierFrequency, int[] pattern);
private static native int[] halGetCarrierFrequencies();
@@ -37,6 +39,7 @@ public class ConsumerIrService extends IConsumerIrService.Stub {
private final PowerManager.WakeLock mWakeLock;
private final boolean mHasNativeHal;
private final Object mHalLock = new Object();
+ private IConsumerIr mAidlService = null;
ConsumerIrService(Context context) {
mContext = context;
@@ -45,7 +48,8 @@ public class ConsumerIrService extends IConsumerIrService.Stub {
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
mWakeLock.setReferenceCounted(true);
- mHasNativeHal = halOpen();
+ mHasNativeHal = getHalService();
+
if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CONSUMER_IR)) {
if (!mHasNativeHal) {
throw new RuntimeException("FEATURE_CONSUMER_IR present, but no IR HAL loaded!");
@@ -60,6 +64,19 @@ public class ConsumerIrService extends IConsumerIrService.Stub {
return mHasNativeHal;
}
+ private boolean getHalService() {
+ // Attempt to get the AIDL HAL service first
+ final String fqName = IConsumerIr.DESCRIPTOR + "/default";
+ mAidlService = IConsumerIr.Stub.asInterface(
+ ServiceManager.waitForDeclaredService(fqName));
+ if (mAidlService != null) {
+ return true;
+ }
+
+ // Fall back to the HIDL HAL service
+ return getHidlHalService();
+ }
+
private void throwIfNoIrEmitter() {
if (!mHasNativeHal) {
throw new UnsupportedOperationException("IR emitter not available");
@@ -91,10 +108,18 @@ public class ConsumerIrService extends IConsumerIrService.Stub {
// Right now there is no mechanism to ensure fair queing of IR requests
synchronized (mHalLock) {
- int err = halTransmit(carrierFrequency, pattern);
-
- if (err < 0) {
- Slog.e(TAG, "Error transmitting: " + err);
+ if (mAidlService != null) {
+ try {
+ mAidlService.transmit(carrierFrequency, pattern);
+ } catch (RemoteException ignore) {
+ Slog.e(TAG, "Error transmitting frequency: " + carrierFrequency);
+ }
+ } else {
+ int err = halTransmit(carrierFrequency, pattern);
+
+ if (err < 0) {
+ Slog.e(TAG, "Error transmitting: " + err);
+ }
}
}
}
@@ -109,7 +134,24 @@ public class ConsumerIrService extends IConsumerIrService.Stub {
throwIfNoIrEmitter();
synchronized(mHalLock) {
- return halGetCarrierFrequencies();
+ if (mAidlService != null) {
+ try {
+ ConsumerIrFreqRange[] output = mAidlService.getCarrierFreqs();
+ if (output.length <= 0) {
+ Slog.e(TAG, "Error getting carrier frequencies.");
+ }
+ int[] result = new int[output.length * 2];
+ for (int i = 0; i < output.length; i++) {
+ result[i * 2] = output[i].minHz;
+ result[i * 2 + 1] = output[i].maxHz;
+ }
+ return result;
+ } catch (RemoteException ignore) {
+ return null;
+ }
+ } else {
+ return halGetCarrierFrequencies();
+ }
}
}
}
diff --git a/services/core/java/com/android/server/DynamicSystemService.java b/services/core/java/com/android/server/DynamicSystemService.java
index e29e894a5cc0..e924012c8892 100644
--- a/services/core/java/com/android/server/DynamicSystemService.java
+++ b/services/core/java/com/android/server/DynamicSystemService.java
@@ -16,8 +16,9 @@
package com.android.server;
+import android.annotation.EnforcePermission;
+import android.annotation.RequiresNoPermission;
import android.content.Context;
-import android.content.pm.PackageManager;
import android.gsi.AvbPublicKey;
import android.gsi.GsiProgress;
import android.gsi.IGsiService;
@@ -53,20 +54,12 @@ public class DynamicSystemService extends IDynamicSystemService.Stub {
}
private IGsiService getGsiService() {
- checkPermission();
if (mGsiService != null) {
return mGsiService;
}
return IGsiService.Stub.asInterface(ServiceManager.waitForService("gsiservice"));
}
- private void checkPermission() {
- if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
- != PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException("Requires MANAGE_DYNAMIC_SYSTEM permission");
- }
- }
-
class GsiServiceCallback extends IGsiServiceCallback.Stub {
// 0 for success
private int mResult = -1;
@@ -82,6 +75,7 @@ public class DynamicSystemService extends IDynamicSystemService.Stub {
}
@Override
+ @EnforcePermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
public boolean startInstallation(String dsuSlot) throws RemoteException {
IGsiService service = getGsiService();
mGsiService = service;
@@ -124,6 +118,7 @@ public class DynamicSystemService extends IDynamicSystemService.Stub {
}
@Override
+ @EnforcePermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
public boolean createPartition(String name, long size, boolean readOnly)
throws RemoteException {
IGsiService service = getGsiService();
@@ -135,6 +130,7 @@ public class DynamicSystemService extends IDynamicSystemService.Stub {
}
@Override
+ @EnforcePermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
public boolean closePartition() throws RemoteException {
IGsiService service = getGsiService();
if (service.closePartition() != 0) {
@@ -145,6 +141,7 @@ public class DynamicSystemService extends IDynamicSystemService.Stub {
}
@Override
+ @EnforcePermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
public boolean finishInstallation() throws RemoteException {
IGsiService service = getGsiService();
if (service.closeInstall() != 0) {
@@ -155,21 +152,25 @@ public class DynamicSystemService extends IDynamicSystemService.Stub {
}
@Override
+ @EnforcePermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
public GsiProgress getInstallationProgress() throws RemoteException {
return getGsiService().getInstallProgress();
}
@Override
+ @EnforcePermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
public boolean abort() throws RemoteException {
return getGsiService().cancelGsiInstall();
}
@Override
+ @RequiresNoPermission
public boolean isInUse() {
return SystemProperties.getBoolean("ro.gsid.image_running", false);
}
@Override
+ @RequiresNoPermission
public boolean isInstalled() {
boolean installed = SystemProperties.getBoolean("gsid.image_installed", false);
Slog.i(TAG, "isInstalled(): " + installed);
@@ -177,11 +178,13 @@ public class DynamicSystemService extends IDynamicSystemService.Stub {
}
@Override
+ @EnforcePermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
public boolean isEnabled() throws RemoteException {
return getGsiService().isGsiEnabled();
}
@Override
+ @EnforcePermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
public boolean remove() throws RemoteException {
try {
GsiServiceCallback callback = new GsiServiceCallback();
@@ -197,6 +200,7 @@ public class DynamicSystemService extends IDynamicSystemService.Stub {
}
@Override
+ @EnforcePermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
public boolean setEnable(boolean enable, boolean oneShot) throws RemoteException {
IGsiService gsiService = getGsiService();
if (enable) {
@@ -220,6 +224,7 @@ public class DynamicSystemService extends IDynamicSystemService.Stub {
}
@Override
+ @EnforcePermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
public boolean setAshmem(ParcelFileDescriptor ashmem, long size) {
try {
return getGsiService().setGsiAshmem(ashmem, size);
@@ -229,6 +234,7 @@ public class DynamicSystemService extends IDynamicSystemService.Stub {
}
@Override
+ @EnforcePermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
public boolean submitFromAshmem(long size) {
try {
return getGsiService().commitGsiChunkFromAshmem(size);
@@ -238,6 +244,7 @@ public class DynamicSystemService extends IDynamicSystemService.Stub {
}
@Override
+ @EnforcePermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
public boolean getAvbPublicKey(AvbPublicKey dst) {
try {
return getGsiService().getAvbPublicKey(dst) == 0;
@@ -247,6 +254,7 @@ public class DynamicSystemService extends IDynamicSystemService.Stub {
}
@Override
+ @EnforcePermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
public long suggestScratchSize() throws RemoteException {
return getGsiService().suggestScratchSize();
}
diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java
deleted file mode 100644
index d6ee95131ea9..000000000000
--- a/services/core/java/com/android/server/IpSecService.java
+++ /dev/null
@@ -1,1917 +0,0 @@
-/*
- * Copyright (C) 2017 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;
-
-import static android.Manifest.permission.DUMP;
-import static android.net.IpSecManager.INVALID_RESOURCE_ID;
-import static android.system.OsConstants.AF_INET;
-import static android.system.OsConstants.AF_INET6;
-import static android.system.OsConstants.AF_UNSPEC;
-import static android.system.OsConstants.EINVAL;
-import static android.system.OsConstants.IPPROTO_UDP;
-import static android.system.OsConstants.SOCK_DGRAM;
-
-import android.annotation.NonNull;
-import android.app.AppOpsManager;
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.net.ConnectivityManager;
-import android.net.IIpSecService;
-import android.net.INetd;
-import android.net.InetAddresses;
-import android.net.IpSecAlgorithm;
-import android.net.IpSecConfig;
-import android.net.IpSecManager;
-import android.net.IpSecSpiResponse;
-import android.net.IpSecTransform;
-import android.net.IpSecTransformResponse;
-import android.net.IpSecTunnelInterfaceResponse;
-import android.net.IpSecUdpEncapResponse;
-import android.net.LinkAddress;
-import android.net.LinkProperties;
-import android.net.Network;
-import android.net.TrafficStats;
-import android.net.util.NetdService;
-import android.os.Binder;
-import android.os.IBinder;
-import android.os.ParcelFileDescriptor;
-import android.os.Process;
-import android.os.RemoteException;
-import android.os.ServiceSpecificException;
-import android.system.ErrnoException;
-import android.system.Os;
-import android.system.OsConstants;
-import android.text.TextUtils;
-import android.util.Log;
-import android.util.Range;
-import android.util.Slog;
-import android.util.SparseArray;
-import android.util.SparseBooleanArray;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.Preconditions;
-import com.android.net.module.util.NetdUtils;
-import com.android.net.module.util.PermissionUtils;
-
-import libcore.io.IoUtils;
-
-import java.io.FileDescriptor;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.net.Inet4Address;
-import java.net.Inet6Address;
-import java.net.InetAddress;
-import java.net.InetSocketAddress;
-import java.net.UnknownHostException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Objects;
-
-/**
- * A service to manage multiple clients that want to access the IpSec API. The service is
- * responsible for maintaining a list of clients and managing the resources (and related quotas)
- * that each of them own.
- *
- * <p>Synchronization in IpSecService is done on all entrypoints due to potential race conditions at
- * the kernel/xfrm level. Further, this allows the simplifying assumption to be made that only one
- * thread is ever running at a time.
- *
- * @hide
- */
-public class IpSecService extends IIpSecService.Stub {
- private static final String TAG = "IpSecService";
- private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
-
- private static final String NETD_SERVICE_NAME = "netd";
- private static final int[] ADDRESS_FAMILIES =
- new int[] {OsConstants.AF_INET, OsConstants.AF_INET6};
-
- private static final int NETD_FETCH_TIMEOUT_MS = 5000; // ms
- private static final InetAddress INADDR_ANY;
-
- @VisibleForTesting static final int MAX_PORT_BIND_ATTEMPTS = 10;
-
- static {
- try {
- INADDR_ANY = InetAddress.getByAddress(new byte[] {0, 0, 0, 0});
- } catch (UnknownHostException e) {
- throw new RuntimeException(e);
- }
- }
-
- static final int FREE_PORT_MIN = 1024; // ports 1-1023 are reserved
- static final int PORT_MAX = 0xFFFF; // ports are an unsigned 16-bit integer
-
- /* Binder context for this service */
- private final Context mContext;
-
- /**
- * The next non-repeating global ID for tracking resources between users, this service, and
- * kernel data structures. Accessing this variable is not thread safe, so it is only read or
- * modified within blocks synchronized on IpSecService.this. We want to avoid -1
- * (INVALID_RESOURCE_ID) and 0 (we probably forgot to initialize it).
- */
- @GuardedBy("IpSecService.this")
- private int mNextResourceId = 1;
-
- interface IpSecServiceConfiguration {
- INetd getNetdInstance() throws RemoteException;
-
- static IpSecServiceConfiguration GETSRVINSTANCE =
- new IpSecServiceConfiguration() {
- @Override
- public INetd getNetdInstance() throws RemoteException {
- final INetd netd = NetdService.getInstance();
- if (netd == null) {
- throw new RemoteException("Failed to Get Netd Instance");
- }
- return netd;
- }
- };
- }
-
- private final IpSecServiceConfiguration mSrvConfig;
- final UidFdTagger mUidFdTagger;
-
- /**
- * Interface for user-reference and kernel-resource cleanup.
- *
- * <p>This interface must be implemented for a resource to be reference counted.
- */
- @VisibleForTesting
- public interface IResource {
- /**
- * Invalidates a IResource object, ensuring it is invalid for the purposes of allocating new
- * objects dependent on it.
- *
- * <p>Implementations of this method are expected to remove references to the IResource
- * object from the IpSecService's tracking arrays. The removal from the arrays ensures that
- * the resource is considered invalid for user access or allocation or use in other
- * resources.
- *
- * <p>References to the IResource object may be held by other RefcountedResource objects,
- * and as such, the underlying resources and quota may not be cleaned up.
- */
- void invalidate() throws RemoteException;
-
- /**
- * Releases underlying resources and related quotas.
- *
- * <p>Implementations of this method are expected to remove all system resources that are
- * tracked by the IResource object. Due to other RefcountedResource objects potentially
- * having references to the IResource object, freeUnderlyingResources may not always be
- * called from releaseIfUnreferencedRecursively().
- */
- void freeUnderlyingResources() throws RemoteException;
- }
-
- /**
- * RefcountedResource manages references and dependencies in an exclusively acyclic graph.
- *
- * <p>RefcountedResource implements both explicit and implicit resource management. Creating a
- * RefcountedResource object creates an explicit reference that must be freed by calling
- * userRelease(). Additionally, adding this object as a child of another RefcountedResource
- * object will add an implicit reference.
- *
- * <p>Resources are cleaned up when all references, both implicit and explicit, are released
- * (ie, when userRelease() is called and when all parents have called releaseReference() on this
- * object.)
- */
- @VisibleForTesting
- public class RefcountedResource<T extends IResource> implements IBinder.DeathRecipient {
- private final T mResource;
- private final List<RefcountedResource> mChildren;
- int mRefCount = 1; // starts at 1 for user's reference.
- IBinder mBinder;
-
- RefcountedResource(T resource, IBinder binder, RefcountedResource... children) {
- synchronized (IpSecService.this) {
- this.mResource = resource;
- this.mChildren = new ArrayList<>(children.length);
- this.mBinder = binder;
-
- for (RefcountedResource child : children) {
- mChildren.add(child);
- child.mRefCount++;
- }
-
- try {
- mBinder.linkToDeath(this, 0);
- } catch (RemoteException e) {
- binderDied();
- e.rethrowFromSystemServer();
- }
- }
- }
-
- /**
- * If the Binder object dies, this function is called to free the system resources that are
- * being tracked by this record and to subsequently release this record for garbage
- * collection
- */
- @Override
- public void binderDied() {
- synchronized (IpSecService.this) {
- try {
- userRelease();
- } catch (Exception e) {
- Log.e(TAG, "Failed to release resource: " + e);
- }
- }
- }
-
- public T getResource() {
- return mResource;
- }
-
- /**
- * Unlinks from binder and performs IpSecService resource cleanup (removes from resource
- * arrays)
- *
- * <p>If this method has been previously called, the RefcountedResource's binder field will
- * be null, and the method will return without performing the cleanup a second time.
- *
- * <p>Note that calling this function does not imply that kernel resources will be freed at
- * this time, or that the related quota will be returned. Such actions will only be
- * performed upon the reference count reaching zero.
- */
- @GuardedBy("IpSecService.this")
- public void userRelease() throws RemoteException {
- // Prevent users from putting reference counts into a bad state by calling
- // userRelease() multiple times.
- if (mBinder == null) {
- return;
- }
-
- mBinder.unlinkToDeath(this, 0);
- mBinder = null;
-
- mResource.invalidate();
-
- releaseReference();
- }
-
- /**
- * Removes a reference to this resource. If the resultant reference count is zero, the
- * underlying resources are freed, and references to all child resources are also dropped
- * recursively (resulting in them freeing their resources and children, etcetera)
- *
- * <p>This method also sets the reference count to an invalid value (-1) to signify that it
- * has been fully released. Any subsequent calls to this method will result in an
- * IllegalStateException being thrown due to resource already having been previously
- * released
- */
- @VisibleForTesting
- @GuardedBy("IpSecService.this")
- public void releaseReference() throws RemoteException {
- mRefCount--;
-
- if (mRefCount > 0) {
- return;
- } else if (mRefCount < 0) {
- throw new IllegalStateException(
- "Invalid operation - resource has already been released.");
- }
-
- // Cleanup own resources
- mResource.freeUnderlyingResources();
-
- // Cleanup child resources as needed
- for (RefcountedResource<? extends IResource> child : mChildren) {
- child.releaseReference();
- }
-
- // Enforce that resource cleanup can only be called once
- // By decrementing the refcount (from 0 to -1), the next call will throw an
- // IllegalStateException - it has already been released fully.
- mRefCount--;
- }
-
- @Override
- public String toString() {
- return new StringBuilder()
- .append("{mResource=")
- .append(mResource)
- .append(", mRefCount=")
- .append(mRefCount)
- .append(", mChildren=")
- .append(mChildren)
- .append("}")
- .toString();
- }
- }
-
- /**
- * Very simple counting class that looks much like a counting semaphore
- *
- * <p>This class is not thread-safe, and expects that that users of this class will ensure
- * synchronization and thread safety by holding the IpSecService.this instance lock.
- */
- @VisibleForTesting
- static class ResourceTracker {
- private final int mMax;
- int mCurrent;
-
- ResourceTracker(int max) {
- mMax = max;
- mCurrent = 0;
- }
-
- boolean isAvailable() {
- return (mCurrent < mMax);
- }
-
- void take() {
- if (!isAvailable()) {
- Log.wtf(TAG, "Too many resources allocated!");
- }
- mCurrent++;
- }
-
- void give() {
- if (mCurrent <= 0) {
- Log.wtf(TAG, "We've released this resource too many times");
- }
- mCurrent--;
- }
-
- @Override
- public String toString() {
- return new StringBuilder()
- .append("{mCurrent=")
- .append(mCurrent)
- .append(", mMax=")
- .append(mMax)
- .append("}")
- .toString();
- }
- }
-
- @VisibleForTesting
- static final class UserRecord {
- /* Maximum number of each type of resource that a single UID may possess */
-
- // Up to 4 active VPNs/IWLAN with potential soft handover.
- public static final int MAX_NUM_TUNNEL_INTERFACES = 8;
- public static final int MAX_NUM_ENCAP_SOCKETS = 16;
-
- // SPIs and Transforms are both cheap, and are 1:1 correlated.
- public static final int MAX_NUM_TRANSFORMS = 64;
- public static final int MAX_NUM_SPIS = 64;
-
- /**
- * Store each of the OwnedResource types in an (thinly wrapped) sparse array for indexing
- * and explicit (user) reference management.
- *
- * <p>These are stored in separate arrays to improve debuggability and dump output clarity.
- *
- * <p>Resources are removed from this array when the user releases their explicit reference
- * by calling one of the releaseResource() methods.
- */
- final RefcountedResourceArray<SpiRecord> mSpiRecords =
- new RefcountedResourceArray<>(SpiRecord.class.getSimpleName());
- final RefcountedResourceArray<TransformRecord> mTransformRecords =
- new RefcountedResourceArray<>(TransformRecord.class.getSimpleName());
- final RefcountedResourceArray<EncapSocketRecord> mEncapSocketRecords =
- new RefcountedResourceArray<>(EncapSocketRecord.class.getSimpleName());
- final RefcountedResourceArray<TunnelInterfaceRecord> mTunnelInterfaceRecords =
- new RefcountedResourceArray<>(TunnelInterfaceRecord.class.getSimpleName());
-
- /**
- * Trackers for quotas for each of the OwnedResource types.
- *
- * <p>These trackers are separate from the resource arrays, since they are incremented and
- * decremented at different points in time. Specifically, quota is only returned upon final
- * resource deallocation (after all explicit and implicit references are released). Note
- * that it is possible that calls to releaseResource() will not return the used quota if
- * there are other resources that depend on (are parents of) the resource being released.
- */
- final ResourceTracker mSpiQuotaTracker = new ResourceTracker(MAX_NUM_SPIS);
- final ResourceTracker mTransformQuotaTracker = new ResourceTracker(MAX_NUM_TRANSFORMS);
- final ResourceTracker mSocketQuotaTracker = new ResourceTracker(MAX_NUM_ENCAP_SOCKETS);
- final ResourceTracker mTunnelQuotaTracker = new ResourceTracker(MAX_NUM_TUNNEL_INTERFACES);
-
- void removeSpiRecord(int resourceId) {
- mSpiRecords.remove(resourceId);
- }
-
- void removeTransformRecord(int resourceId) {
- mTransformRecords.remove(resourceId);
- }
-
- void removeTunnelInterfaceRecord(int resourceId) {
- mTunnelInterfaceRecords.remove(resourceId);
- }
-
- void removeEncapSocketRecord(int resourceId) {
- mEncapSocketRecords.remove(resourceId);
- }
-
- @Override
- public String toString() {
- return new StringBuilder()
- .append("{mSpiQuotaTracker=")
- .append(mSpiQuotaTracker)
- .append(", mTransformQuotaTracker=")
- .append(mTransformQuotaTracker)
- .append(", mSocketQuotaTracker=")
- .append(mSocketQuotaTracker)
- .append(", mTunnelQuotaTracker=")
- .append(mTunnelQuotaTracker)
- .append(", mSpiRecords=")
- .append(mSpiRecords)
- .append(", mTransformRecords=")
- .append(mTransformRecords)
- .append(", mEncapSocketRecords=")
- .append(mEncapSocketRecords)
- .append(", mTunnelInterfaceRecords=")
- .append(mTunnelInterfaceRecords)
- .append("}")
- .toString();
- }
- }
-
- /**
- * This class is not thread-safe, and expects that that users of this class will ensure
- * synchronization and thread safety by holding the IpSecService.this instance lock.
- */
- @VisibleForTesting
- static final class UserResourceTracker {
- private final SparseArray<UserRecord> mUserRecords = new SparseArray<>();
-
- /** Lazy-initialization/getter that populates or retrieves the UserRecord as needed */
- public UserRecord getUserRecord(int uid) {
- checkCallerUid(uid);
-
- UserRecord r = mUserRecords.get(uid);
- if (r == null) {
- r = new UserRecord();
- mUserRecords.put(uid, r);
- }
- return r;
- }
-
- /** Safety method; guards against access of other user's UserRecords */
- private void checkCallerUid(int uid) {
- if (uid != Binder.getCallingUid() && Process.SYSTEM_UID != Binder.getCallingUid()) {
- throw new SecurityException("Attempted access of unowned resources");
- }
- }
-
- @Override
- public String toString() {
- return mUserRecords.toString();
- }
- }
-
- @VisibleForTesting final UserResourceTracker mUserResourceTracker = new UserResourceTracker();
-
- /**
- * The OwnedResourceRecord class provides a facility to cleanly and reliably track system
- * resources. It relies on a provided resourceId that should uniquely identify the kernel
- * resource. To use this class, the user should implement the invalidate() and
- * freeUnderlyingResources() methods that are responsible for cleaning up IpSecService resource
- * tracking arrays and kernel resources, respectively.
- *
- * <p>This class associates kernel resources with the UID that owns and controls them.
- */
- private abstract class OwnedResourceRecord implements IResource {
- final int pid;
- final int uid;
- protected final int mResourceId;
-
- OwnedResourceRecord(int resourceId) {
- super();
- if (resourceId == INVALID_RESOURCE_ID) {
- throw new IllegalArgumentException("Resource ID must not be INVALID_RESOURCE_ID");
- }
- mResourceId = resourceId;
- pid = Binder.getCallingPid();
- uid = Binder.getCallingUid();
-
- getResourceTracker().take();
- }
-
- @Override
- public abstract void invalidate() throws RemoteException;
-
- /** Convenience method; retrieves the user resource record for the stored UID. */
- protected UserRecord getUserRecord() {
- return mUserResourceTracker.getUserRecord(uid);
- }
-
- @Override
- public abstract void freeUnderlyingResources() throws RemoteException;
-
- /** Get the resource tracker for this resource */
- protected abstract ResourceTracker getResourceTracker();
-
- @Override
- public String toString() {
- return new StringBuilder()
- .append("{mResourceId=")
- .append(mResourceId)
- .append(", pid=")
- .append(pid)
- .append(", uid=")
- .append(uid)
- .append("}")
- .toString();
- }
- };
-
- /**
- * Thin wrapper over SparseArray to ensure resources exist, and simplify generic typing.
- *
- * <p>RefcountedResourceArray prevents null insertions, and throws an IllegalArgumentException
- * if a key is not found during a retrieval process.
- */
- static class RefcountedResourceArray<T extends IResource> {
- SparseArray<RefcountedResource<T>> mArray = new SparseArray<>();
- private final String mTypeName;
-
- public RefcountedResourceArray(String typeName) {
- this.mTypeName = typeName;
- }
-
- /**
- * Accessor method to get inner resource object.
- *
- * @throws IllegalArgumentException if no resource with provided key is found.
- */
- T getResourceOrThrow(int key) {
- return getRefcountedResourceOrThrow(key).getResource();
- }
-
- /**
- * Accessor method to get reference counting wrapper.
- *
- * @throws IllegalArgumentException if no resource with provided key is found.
- */
- RefcountedResource<T> getRefcountedResourceOrThrow(int key) {
- RefcountedResource<T> resource = mArray.get(key);
- if (resource == null) {
- throw new IllegalArgumentException(
- String.format("No such %s found for given id: %d", mTypeName, key));
- }
-
- return resource;
- }
-
- void put(int key, RefcountedResource<T> obj) {
- Objects.requireNonNull(obj, "Null resources cannot be added");
- mArray.put(key, obj);
- }
-
- void remove(int key) {
- mArray.remove(key);
- }
-
- @Override
- public String toString() {
- return mArray.toString();
- }
- }
-
- /**
- * Tracks an SA in the kernel, and manages cleanup paths. Once a TransformRecord is
- * created, the SpiRecord that originally tracked the SAs will reliquish the
- * responsibility of freeing the underlying SA to this class via the mOwnedByTransform flag.
- */
- private final class TransformRecord extends OwnedResourceRecord {
- private final IpSecConfig mConfig;
- private final SpiRecord mSpi;
- private final EncapSocketRecord mSocket;
-
- TransformRecord(
- int resourceId, IpSecConfig config, SpiRecord spi, EncapSocketRecord socket) {
- super(resourceId);
- mConfig = config;
- mSpi = spi;
- mSocket = socket;
-
- spi.setOwnedByTransform();
- }
-
- public IpSecConfig getConfig() {
- return mConfig;
- }
-
- public SpiRecord getSpiRecord() {
- return mSpi;
- }
-
- public EncapSocketRecord getSocketRecord() {
- return mSocket;
- }
-
- /** always guarded by IpSecService#this */
- @Override
- public void freeUnderlyingResources() {
- int spi = mSpi.getSpi();
- try {
- mSrvConfig
- .getNetdInstance()
- .ipSecDeleteSecurityAssociation(
- uid,
- mConfig.getSourceAddress(),
- mConfig.getDestinationAddress(),
- spi,
- mConfig.getMarkValue(),
- mConfig.getMarkMask(),
- mConfig.getXfrmInterfaceId());
- } catch (RemoteException | ServiceSpecificException e) {
- Log.e(TAG, "Failed to delete SA with ID: " + mResourceId, e);
- }
-
- getResourceTracker().give();
- }
-
- @Override
- public void invalidate() throws RemoteException {
- getUserRecord().removeTransformRecord(mResourceId);
- }
-
- @Override
- protected ResourceTracker getResourceTracker() {
- return getUserRecord().mTransformQuotaTracker;
- }
-
- @Override
- public String toString() {
- StringBuilder strBuilder = new StringBuilder();
- strBuilder
- .append("{super=")
- .append(super.toString())
- .append(", mSocket=")
- .append(mSocket)
- .append(", mSpi.mResourceId=")
- .append(mSpi.mResourceId)
- .append(", mConfig=")
- .append(mConfig)
- .append("}");
- return strBuilder.toString();
- }
- }
-
- /**
- * Tracks a single SA in the kernel, and manages cleanup paths. Once used in a Transform, the
- * responsibility for cleaning up underlying resources will be passed to the TransformRecord
- * object
- */
- private final class SpiRecord extends OwnedResourceRecord {
- private final String mSourceAddress;
- private final String mDestinationAddress;
- private int mSpi;
-
- private boolean mOwnedByTransform = false;
-
- SpiRecord(int resourceId, String sourceAddress, String destinationAddress, int spi) {
- super(resourceId);
- mSourceAddress = sourceAddress;
- mDestinationAddress = destinationAddress;
- mSpi = spi;
- }
-
- /** always guarded by IpSecService#this */
- @Override
- public void freeUnderlyingResources() {
- try {
- if (!mOwnedByTransform) {
- mSrvConfig
- .getNetdInstance()
- .ipSecDeleteSecurityAssociation(
- uid, mSourceAddress, mDestinationAddress, mSpi, 0 /* mark */,
- 0 /* mask */, 0 /* if_id */);
- }
- } catch (ServiceSpecificException | RemoteException e) {
- Log.e(TAG, "Failed to delete SPI reservation with ID: " + mResourceId, e);
- }
-
- mSpi = IpSecManager.INVALID_SECURITY_PARAMETER_INDEX;
-
- getResourceTracker().give();
- }
-
- public int getSpi() {
- return mSpi;
- }
-
- public String getDestinationAddress() {
- return mDestinationAddress;
- }
-
- public void setOwnedByTransform() {
- if (mOwnedByTransform) {
- // Programming error
- throw new IllegalStateException("Cannot own an SPI twice!");
- }
-
- mOwnedByTransform = true;
- }
-
- public boolean getOwnedByTransform() {
- return mOwnedByTransform;
- }
-
- @Override
- public void invalidate() throws RemoteException {
- getUserRecord().removeSpiRecord(mResourceId);
- }
-
- @Override
- protected ResourceTracker getResourceTracker() {
- return getUserRecord().mSpiQuotaTracker;
- }
-
- @Override
- public String toString() {
- StringBuilder strBuilder = new StringBuilder();
- strBuilder
- .append("{super=")
- .append(super.toString())
- .append(", mSpi=")
- .append(mSpi)
- .append(", mSourceAddress=")
- .append(mSourceAddress)
- .append(", mDestinationAddress=")
- .append(mDestinationAddress)
- .append(", mOwnedByTransform=")
- .append(mOwnedByTransform)
- .append("}");
- return strBuilder.toString();
- }
- }
-
- private final SparseBooleanArray mTunnelNetIds = new SparseBooleanArray();
- final Range<Integer> mNetIdRange = ConnectivityManager.getIpSecNetIdRange();
- private int mNextTunnelNetId = mNetIdRange.getLower();
-
- /**
- * Reserves a netId within the range of netIds allocated for IPsec tunnel interfaces
- *
- * <p>This method should only be called from Binder threads. Do not call this from within the
- * system server as it will crash the system on failure.
- *
- * @return an integer key within the netId range, if successful
- * @throws IllegalStateException if unsuccessful (all netId are currently reserved)
- */
- @VisibleForTesting
- int reserveNetId() {
- final int range = mNetIdRange.getUpper() - mNetIdRange.getLower() + 1;
- synchronized (mTunnelNetIds) {
- for (int i = 0; i < range; i++) {
- final int netId = mNextTunnelNetId;
- if (++mNextTunnelNetId > mNetIdRange.getUpper()) {
- mNextTunnelNetId = mNetIdRange.getLower();
- }
- if (!mTunnelNetIds.get(netId)) {
- mTunnelNetIds.put(netId, true);
- return netId;
- }
- }
- }
- throw new IllegalStateException("No free netIds to allocate");
- }
-
- @VisibleForTesting
- void releaseNetId(int netId) {
- synchronized (mTunnelNetIds) {
- mTunnelNetIds.delete(netId);
- }
- }
-
- /**
- * Tracks an tunnel interface, and manages cleanup paths.
- *
- * <p>This class is not thread-safe, and expects that that users of this class will ensure
- * synchronization and thread safety by holding the IpSecService.this instance lock
- */
- @VisibleForTesting
- final class TunnelInterfaceRecord extends OwnedResourceRecord {
- private final String mInterfaceName;
-
- // outer addresses
- private final String mLocalAddress;
- private final String mRemoteAddress;
-
- private final int mIkey;
- private final int mOkey;
-
- private final int mIfId;
-
- private Network mUnderlyingNetwork;
-
- TunnelInterfaceRecord(
- int resourceId,
- String interfaceName,
- Network underlyingNetwork,
- String localAddr,
- String remoteAddr,
- int ikey,
- int okey,
- int intfId) {
- super(resourceId);
-
- mInterfaceName = interfaceName;
- mUnderlyingNetwork = underlyingNetwork;
- mLocalAddress = localAddr;
- mRemoteAddress = remoteAddr;
- mIkey = ikey;
- mOkey = okey;
- mIfId = intfId;
- }
-
- /** always guarded by IpSecService#this */
- @Override
- public void freeUnderlyingResources() {
- // Calls to netd
- // Teardown VTI
- // Delete global policies
- try {
- final INetd netd = mSrvConfig.getNetdInstance();
- netd.ipSecRemoveTunnelInterface(mInterfaceName);
-
- for (int selAddrFamily : ADDRESS_FAMILIES) {
- netd.ipSecDeleteSecurityPolicy(
- uid,
- selAddrFamily,
- IpSecManager.DIRECTION_OUT,
- mOkey,
- 0xffffffff,
- mIfId);
- netd.ipSecDeleteSecurityPolicy(
- uid,
- selAddrFamily,
- IpSecManager.DIRECTION_IN,
- mIkey,
- 0xffffffff,
- mIfId);
- }
- } catch (ServiceSpecificException | RemoteException e) {
- Log.e(
- TAG,
- "Failed to delete VTI with interface name: "
- + mInterfaceName
- + " and id: "
- + mResourceId, e);
- }
-
- getResourceTracker().give();
- releaseNetId(mIkey);
- releaseNetId(mOkey);
- }
-
- @GuardedBy("IpSecService.this")
- public void setUnderlyingNetwork(Network underlyingNetwork) {
- // When #applyTunnelModeTransform is called, this new underlying network will be used to
- // update the output mark of the input transform.
- mUnderlyingNetwork = underlyingNetwork;
- }
-
- @GuardedBy("IpSecService.this")
- public Network getUnderlyingNetwork() {
- return mUnderlyingNetwork;
- }
-
- public String getInterfaceName() {
- return mInterfaceName;
- }
-
- /** Returns the local, outer address for the tunnelInterface */
- public String getLocalAddress() {
- return mLocalAddress;
- }
-
- /** Returns the remote, outer address for the tunnelInterface */
- public String getRemoteAddress() {
- return mRemoteAddress;
- }
-
- public int getIkey() {
- return mIkey;
- }
-
- public int getOkey() {
- return mOkey;
- }
-
- public int getIfId() {
- return mIfId;
- }
-
- @Override
- protected ResourceTracker getResourceTracker() {
- return getUserRecord().mTunnelQuotaTracker;
- }
-
- @Override
- public void invalidate() {
- getUserRecord().removeTunnelInterfaceRecord(mResourceId);
- }
-
- @Override
- public String toString() {
- return new StringBuilder()
- .append("{super=")
- .append(super.toString())
- .append(", mInterfaceName=")
- .append(mInterfaceName)
- .append(", mUnderlyingNetwork=")
- .append(mUnderlyingNetwork)
- .append(", mLocalAddress=")
- .append(mLocalAddress)
- .append(", mRemoteAddress=")
- .append(mRemoteAddress)
- .append(", mIkey=")
- .append(mIkey)
- .append(", mOkey=")
- .append(mOkey)
- .append("}")
- .toString();
- }
- }
-
- /**
- * Tracks a UDP encap socket, and manages cleanup paths
- *
- * <p>While this class does not manage non-kernel resources, race conditions around socket
- * binding require that the service creates the encap socket, binds it and applies the socket
- * policy before handing it to a user.
- */
- private final class EncapSocketRecord extends OwnedResourceRecord {
- private FileDescriptor mSocket;
- private final int mPort;
-
- EncapSocketRecord(int resourceId, FileDescriptor socket, int port) {
- super(resourceId);
- mSocket = socket;
- mPort = port;
- }
-
- /** always guarded by IpSecService#this */
- @Override
- public void freeUnderlyingResources() {
- Log.d(TAG, "Closing port " + mPort);
- IoUtils.closeQuietly(mSocket);
- mSocket = null;
-
- getResourceTracker().give();
- }
-
- public int getPort() {
- return mPort;
- }
-
- public FileDescriptor getFileDescriptor() {
- return mSocket;
- }
-
- @Override
- protected ResourceTracker getResourceTracker() {
- return getUserRecord().mSocketQuotaTracker;
- }
-
- @Override
- public void invalidate() {
- getUserRecord().removeEncapSocketRecord(mResourceId);
- }
-
- @Override
- public String toString() {
- return new StringBuilder()
- .append("{super=")
- .append(super.toString())
- .append(", mSocket=")
- .append(mSocket)
- .append(", mPort=")
- .append(mPort)
- .append("}")
- .toString();
- }
- }
-
- /**
- * Constructs a new IpSecService instance
- *
- * @param context Binder context for this service
- */
- private IpSecService(Context context) {
- this(context, IpSecServiceConfiguration.GETSRVINSTANCE);
- }
-
- static IpSecService create(Context context)
- throws InterruptedException {
- final IpSecService service = new IpSecService(context);
- service.connectNativeNetdService();
- return service;
- }
-
- @NonNull
- private AppOpsManager getAppOpsManager() {
- AppOpsManager appOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
- if(appOps == null) throw new RuntimeException("System Server couldn't get AppOps");
- return appOps;
- }
-
- /** @hide */
- @VisibleForTesting
- public IpSecService(Context context, IpSecServiceConfiguration config) {
- this(
- context,
- config,
- (fd, uid) -> {
- try {
- TrafficStats.setThreadStatsUid(uid);
- TrafficStats.tagFileDescriptor(fd);
- } finally {
- TrafficStats.clearThreadStatsUid();
- }
- });
- }
-
- /** @hide */
- @VisibleForTesting
- public IpSecService(Context context, IpSecServiceConfiguration config,
- UidFdTagger uidFdTagger) {
- mContext = context;
- mSrvConfig = config;
- mUidFdTagger = uidFdTagger;
- }
-
- public void systemReady() {
- if (isNetdAlive()) {
- Slog.d(TAG, "IpSecService is ready");
- } else {
- Slog.wtf(TAG, "IpSecService not ready: failed to connect to NetD Native Service!");
- }
- }
-
- private void connectNativeNetdService() {
- // Avoid blocking the system server to do this
- new Thread() {
- @Override
- public void run() {
- synchronized (IpSecService.this) {
- NetdService.get(NETD_FETCH_TIMEOUT_MS);
- }
- }
- }.start();
- }
-
- synchronized boolean isNetdAlive() {
- try {
- final INetd netd = mSrvConfig.getNetdInstance();
- if (netd == null) {
- return false;
- }
- return netd.isAlive();
- } catch (RemoteException re) {
- return false;
- }
- }
-
- /**
- * Checks that the provided InetAddress is valid for use in an IPsec SA. The address must not be
- * a wildcard address and must be in a numeric form such as 1.2.3.4 or 2001::1.
- */
- private static void checkInetAddress(String inetAddress) {
- if (TextUtils.isEmpty(inetAddress)) {
- throw new IllegalArgumentException("Unspecified address");
- }
-
- InetAddress checkAddr = InetAddresses.parseNumericAddress(inetAddress);
-
- if (checkAddr.isAnyLocalAddress()) {
- throw new IllegalArgumentException("Inappropriate wildcard address: " + inetAddress);
- }
- }
-
- /**
- * Checks the user-provided direction field and throws an IllegalArgumentException if it is not
- * DIRECTION_IN or DIRECTION_OUT
- */
- private void checkDirection(int direction) {
- switch (direction) {
- case IpSecManager.DIRECTION_OUT:
- case IpSecManager.DIRECTION_IN:
- return;
- case IpSecManager.DIRECTION_FWD:
- // Only NETWORK_STACK or MAINLINE_NETWORK_STACK allowed to use forward policies
- PermissionUtils.enforceNetworkStackPermission(mContext);
- return;
- }
- throw new IllegalArgumentException("Invalid Direction: " + direction);
- }
-
- /** Get a new SPI and maintain the reservation in the system server */
- @Override
- public synchronized IpSecSpiResponse allocateSecurityParameterIndex(
- String destinationAddress, int requestedSpi, IBinder binder) throws RemoteException {
- checkInetAddress(destinationAddress);
- // RFC 4303 Section 2.1 - 0=local, 1-255=reserved.
- if (requestedSpi > 0 && requestedSpi < 256) {
- throw new IllegalArgumentException("ESP SPI must not be in the range of 0-255.");
- }
- Objects.requireNonNull(binder, "Null Binder passed to allocateSecurityParameterIndex");
-
- int callingUid = Binder.getCallingUid();
- UserRecord userRecord = mUserResourceTracker.getUserRecord(callingUid);
- final int resourceId = mNextResourceId++;
-
- int spi = IpSecManager.INVALID_SECURITY_PARAMETER_INDEX;
- try {
- if (!userRecord.mSpiQuotaTracker.isAvailable()) {
- return new IpSecSpiResponse(
- IpSecManager.Status.RESOURCE_UNAVAILABLE, INVALID_RESOURCE_ID, spi);
- }
-
- spi =
- mSrvConfig
- .getNetdInstance()
- .ipSecAllocateSpi(callingUid, "", destinationAddress, requestedSpi);
- Log.d(TAG, "Allocated SPI " + spi);
- userRecord.mSpiRecords.put(
- resourceId,
- new RefcountedResource<SpiRecord>(
- new SpiRecord(resourceId, "", destinationAddress, spi), binder));
- } catch (ServiceSpecificException e) {
- if (e.errorCode == OsConstants.ENOENT) {
- return new IpSecSpiResponse(
- IpSecManager.Status.SPI_UNAVAILABLE, INVALID_RESOURCE_ID, spi);
- }
- throw e;
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- return new IpSecSpiResponse(IpSecManager.Status.OK, resourceId, spi);
- }
-
- /* This method should only be called from Binder threads. Do not call this from
- * within the system server as it will crash the system on failure.
- */
- private void releaseResource(RefcountedResourceArray resArray, int resourceId)
- throws RemoteException {
- resArray.getRefcountedResourceOrThrow(resourceId).userRelease();
- }
-
- /** Release a previously allocated SPI that has been registered with the system server */
- @Override
- public synchronized void releaseSecurityParameterIndex(int resourceId) throws RemoteException {
- UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
- releaseResource(userRecord.mSpiRecords, resourceId);
- }
-
- /**
- * This function finds and forcibly binds to a random system port, ensuring that the port cannot
- * be unbound.
- *
- * <p>A socket cannot be un-bound from a port if it was bound to that port by number. To select
- * a random open port and then bind by number, this function creates a temp socket, binds to a
- * random port (specifying 0), gets that port number, and then uses is to bind the user's UDP
- * Encapsulation Socket forcibly, so that it cannot be un-bound by the user with the returned
- * FileHandle.
- *
- * <p>The loop in this function handles the inherent race window between un-binding to a port
- * and re-binding, during which the system could *technically* hand that port out to someone
- * else.
- */
- private int bindToRandomPort(FileDescriptor sockFd) throws IOException {
- for (int i = MAX_PORT_BIND_ATTEMPTS; i > 0; i--) {
- try {
- FileDescriptor probeSocket = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
- Os.bind(probeSocket, INADDR_ANY, 0);
- int port = ((InetSocketAddress) Os.getsockname(probeSocket)).getPort();
- Os.close(probeSocket);
- Log.v(TAG, "Binding to port " + port);
- Os.bind(sockFd, INADDR_ANY, port);
- return port;
- } catch (ErrnoException e) {
- // Someone miraculously claimed the port just after we closed probeSocket.
- if (e.errno == OsConstants.EADDRINUSE) {
- continue;
- }
- throw e.rethrowAsIOException();
- }
- }
- throw new IOException("Failed " + MAX_PORT_BIND_ATTEMPTS + " attempts to bind to a port");
- }
-
- /**
- * Functional interface to do traffic tagging of given sockets to UIDs.
- *
- * <p>Specifically used by openUdpEncapsulationSocket to ensure data usage on the UDP encap
- * sockets are billed to the UID that the UDP encap socket was created on behalf of.
- *
- * <p>Separate class so that the socket tagging logic can be mocked; TrafficStats uses static
- * methods that cannot be easily mocked/tested.
- */
- @VisibleForTesting
- public interface UidFdTagger {
- /**
- * Sets socket tag to assign all traffic to the provided UID.
- *
- * <p>Since the socket is created on behalf of an unprivileged application, all traffic
- * should be accounted to the UID of the unprivileged application.
- */
- public void tag(FileDescriptor fd, int uid) throws IOException;
- }
-
- /**
- * Open a socket via the system server and bind it to the specified port (random if port=0).
- * This will return a PFD to the user that represent a bound UDP socket. The system server will
- * cache the socket and a record of its owner so that it can and must be freed when no longer
- * needed.
- */
- @Override
- public synchronized IpSecUdpEncapResponse openUdpEncapsulationSocket(int port, IBinder binder)
- throws RemoteException {
- if (port != 0 && (port < FREE_PORT_MIN || port > PORT_MAX)) {
- throw new IllegalArgumentException(
- "Specified port number must be a valid non-reserved UDP port");
- }
- Objects.requireNonNull(binder, "Null Binder passed to openUdpEncapsulationSocket");
-
- int callingUid = Binder.getCallingUid();
- UserRecord userRecord = mUserResourceTracker.getUserRecord(callingUid);
- final int resourceId = mNextResourceId++;
- FileDescriptor sockFd = null;
- try {
- if (!userRecord.mSocketQuotaTracker.isAvailable()) {
- return new IpSecUdpEncapResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
- }
-
- sockFd = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
- mUidFdTagger.tag(sockFd, callingUid);
-
- // This code is common to both the unspecified and specified port cases
- Os.setsockoptInt(
- sockFd,
- OsConstants.IPPROTO_UDP,
- OsConstants.UDP_ENCAP,
- OsConstants.UDP_ENCAP_ESPINUDP);
-
- mSrvConfig.getNetdInstance().ipSecSetEncapSocketOwner(
- new ParcelFileDescriptor(sockFd), callingUid);
- if (port != 0) {
- Log.v(TAG, "Binding to port " + port);
- Os.bind(sockFd, INADDR_ANY, port);
- } else {
- port = bindToRandomPort(sockFd);
- }
-
- userRecord.mEncapSocketRecords.put(
- resourceId,
- new RefcountedResource<EncapSocketRecord>(
- new EncapSocketRecord(resourceId, sockFd, port), binder));
- return new IpSecUdpEncapResponse(IpSecManager.Status.OK, resourceId, port, sockFd);
- } catch (IOException | ErrnoException e) {
- IoUtils.closeQuietly(sockFd);
- }
- // If we make it to here, then something has gone wrong and we couldn't open a socket.
- // The only reasonable condition that would cause that is resource unavailable.
- return new IpSecUdpEncapResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
- }
-
- /** close a socket that has been been allocated by and registered with the system server */
- @Override
- public synchronized void closeUdpEncapsulationSocket(int resourceId) throws RemoteException {
- UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
- releaseResource(userRecord.mEncapSocketRecords, resourceId);
- }
-
- /**
- * Create a tunnel interface for use in IPSec tunnel mode. The system server will cache the
- * tunnel interface and a record of its owner so that it can and must be freed when no longer
- * needed.
- */
- @Override
- public synchronized IpSecTunnelInterfaceResponse createTunnelInterface(
- String localAddr, String remoteAddr, Network underlyingNetwork, IBinder binder,
- String callingPackage) {
- enforceTunnelFeatureAndPermissions(callingPackage);
- Objects.requireNonNull(binder, "Null Binder passed to createTunnelInterface");
- Objects.requireNonNull(underlyingNetwork, "No underlying network was specified");
- checkInetAddress(localAddr);
- checkInetAddress(remoteAddr);
-
- // TODO: Check that underlying network exists, and IP addresses not assigned to a different
- // network (b/72316676).
-
- int callerUid = Binder.getCallingUid();
- UserRecord userRecord = mUserResourceTracker.getUserRecord(callerUid);
- if (!userRecord.mTunnelQuotaTracker.isAvailable()) {
- return new IpSecTunnelInterfaceResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
- }
-
- final int resourceId = mNextResourceId++;
- final int ikey = reserveNetId();
- final int okey = reserveNetId();
- String intfName = String.format("%s%d", INetd.IPSEC_INTERFACE_PREFIX, resourceId);
-
- try {
- // Calls to netd:
- // Create VTI
- // Add inbound/outbound global policies
- // (use reqid = 0)
- final INetd netd = mSrvConfig.getNetdInstance();
- netd.ipSecAddTunnelInterface(intfName, localAddr, remoteAddr, ikey, okey, resourceId);
-
- Binder.withCleanCallingIdentity(() -> {
- NetdUtils.setInterfaceUp(netd, intfName);
- });
-
- for (int selAddrFamily : ADDRESS_FAMILIES) {
- // Always send down correct local/remote addresses for template.
- netd.ipSecAddSecurityPolicy(
- callerUid,
- selAddrFamily,
- IpSecManager.DIRECTION_OUT,
- localAddr,
- remoteAddr,
- 0,
- okey,
- 0xffffffff,
- resourceId);
- netd.ipSecAddSecurityPolicy(
- callerUid,
- selAddrFamily,
- IpSecManager.DIRECTION_IN,
- remoteAddr,
- localAddr,
- 0,
- ikey,
- 0xffffffff,
- resourceId);
-
- // Add a forwarding policy on the tunnel interface. In order to support forwarding
- // the IpSecTunnelInterface must have a forwarding policy matching the incoming SA.
- //
- // Unless a IpSecTransform is also applied against this interface in DIRECTION_FWD,
- // forwarding will be blocked by default (as would be the case if this policy was
- // absent).
- //
- // This is necessary only on the tunnel interface, and not any the interface to
- // which traffic will be forwarded to.
- netd.ipSecAddSecurityPolicy(
- callerUid,
- selAddrFamily,
- IpSecManager.DIRECTION_FWD,
- remoteAddr,
- localAddr,
- 0,
- ikey,
- 0xffffffff,
- resourceId);
- }
-
- userRecord.mTunnelInterfaceRecords.put(
- resourceId,
- new RefcountedResource<TunnelInterfaceRecord>(
- new TunnelInterfaceRecord(
- resourceId,
- intfName,
- underlyingNetwork,
- localAddr,
- remoteAddr,
- ikey,
- okey,
- resourceId),
- binder));
- return new IpSecTunnelInterfaceResponse(IpSecManager.Status.OK, resourceId, intfName);
- } catch (RemoteException e) {
- // Release keys if we got an error.
- releaseNetId(ikey);
- releaseNetId(okey);
- throw e.rethrowFromSystemServer();
- } catch (Throwable t) {
- // Release keys if we got an error.
- releaseNetId(ikey);
- releaseNetId(okey);
- throw t;
- }
- }
-
- /**
- * Adds a new local address to the tunnel interface. This allows packets to be sent and received
- * from multiple local IP addresses over the same tunnel.
- */
- @Override
- public synchronized void addAddressToTunnelInterface(
- int tunnelResourceId, LinkAddress localAddr, String callingPackage) {
- enforceTunnelFeatureAndPermissions(callingPackage);
- UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
-
- // Get tunnelInterface record; if no such interface is found, will throw
- // IllegalArgumentException
- TunnelInterfaceRecord tunnelInterfaceInfo =
- userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelResourceId);
-
- try {
- // We can assume general validity of the IP address, since we get them as a
- // LinkAddress, which does some validation.
- mSrvConfig
- .getNetdInstance()
- .interfaceAddAddress(
- tunnelInterfaceInfo.mInterfaceName,
- localAddr.getAddress().getHostAddress(),
- localAddr.getPrefixLength());
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
- * Remove a new local address from the tunnel interface. After removal, the address will no
- * longer be available to send from, or receive on.
- */
- @Override
- public synchronized void removeAddressFromTunnelInterface(
- int tunnelResourceId, LinkAddress localAddr, String callingPackage) {
- enforceTunnelFeatureAndPermissions(callingPackage);
-
- UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
- // Get tunnelInterface record; if no such interface is found, will throw
- // IllegalArgumentException
- TunnelInterfaceRecord tunnelInterfaceInfo =
- userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelResourceId);
-
- try {
- // We can assume general validity of the IP address, since we get them as a
- // LinkAddress, which does some validation.
- mSrvConfig
- .getNetdInstance()
- .interfaceDelAddress(
- tunnelInterfaceInfo.mInterfaceName,
- localAddr.getAddress().getHostAddress(),
- localAddr.getPrefixLength());
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /** Set TunnelInterface to use a specific underlying network. */
- @Override
- public synchronized void setNetworkForTunnelInterface(
- int tunnelResourceId, Network underlyingNetwork, String callingPackage) {
- enforceTunnelFeatureAndPermissions(callingPackage);
- Objects.requireNonNull(underlyingNetwork, "No underlying network was specified");
-
- final UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
-
- // Get tunnelInterface record; if no such interface is found, will throw
- // IllegalArgumentException. userRecord.mTunnelInterfaceRecords is never null
- final TunnelInterfaceRecord tunnelInterfaceInfo =
- userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelResourceId);
-
- final ConnectivityManager connectivityManager =
- mContext.getSystemService(ConnectivityManager.class);
- final LinkProperties lp = connectivityManager.getLinkProperties(underlyingNetwork);
- if (tunnelInterfaceInfo.getInterfaceName().equals(lp.getInterfaceName())) {
- throw new IllegalArgumentException(
- "Underlying network cannot be the network being exposed by this tunnel");
- }
-
- // It is meaningless to check if the network exists or is valid because the network might
- // disconnect at any time after it passes the check.
-
- tunnelInterfaceInfo.setUnderlyingNetwork(underlyingNetwork);
- }
-
- /**
- * Delete a TunnelInterface that has been been allocated by and registered with the system
- * server
- */
- @Override
- public synchronized void deleteTunnelInterface(
- int resourceId, String callingPackage) throws RemoteException {
- enforceTunnelFeatureAndPermissions(callingPackage);
- UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
- releaseResource(userRecord.mTunnelInterfaceRecords, resourceId);
- }
-
- @VisibleForTesting
- void validateAlgorithms(IpSecConfig config) throws IllegalArgumentException {
- IpSecAlgorithm auth = config.getAuthentication();
- IpSecAlgorithm crypt = config.getEncryption();
- IpSecAlgorithm aead = config.getAuthenticatedEncryption();
-
- // Validate the algorithm set
- Preconditions.checkArgument(
- aead != null || crypt != null || auth != null,
- "No Encryption or Authentication algorithms specified");
- Preconditions.checkArgument(
- auth == null || auth.isAuthentication(),
- "Unsupported algorithm for Authentication");
- Preconditions.checkArgument(
- crypt == null || crypt.isEncryption(), "Unsupported algorithm for Encryption");
- Preconditions.checkArgument(
- aead == null || aead.isAead(),
- "Unsupported algorithm for Authenticated Encryption");
- Preconditions.checkArgument(
- aead == null || (auth == null && crypt == null),
- "Authenticated Encryption is mutually exclusive with other Authentication "
- + "or Encryption algorithms");
- }
-
- private int getFamily(String inetAddress) {
- int family = AF_UNSPEC;
- InetAddress checkAddress = InetAddresses.parseNumericAddress(inetAddress);
- if (checkAddress instanceof Inet4Address) {
- family = AF_INET;
- } else if (checkAddress instanceof Inet6Address) {
- family = AF_INET6;
- }
- return family;
- }
-
- /**
- * Checks an IpSecConfig parcel to ensure that the contents are valid and throws an
- * IllegalArgumentException if they are not.
- */
- private void checkIpSecConfig(IpSecConfig config) {
- UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
-
- switch (config.getEncapType()) {
- case IpSecTransform.ENCAP_NONE:
- break;
- case IpSecTransform.ENCAP_ESPINUDP:
- case IpSecTransform.ENCAP_ESPINUDP_NON_IKE:
- // Retrieve encap socket record; will throw IllegalArgumentException if not found
- userRecord.mEncapSocketRecords.getResourceOrThrow(
- config.getEncapSocketResourceId());
-
- int port = config.getEncapRemotePort();
- if (port <= 0 || port > 0xFFFF) {
- throw new IllegalArgumentException("Invalid remote UDP port: " + port);
- }
- break;
- default:
- throw new IllegalArgumentException("Invalid Encap Type: " + config.getEncapType());
- }
-
- validateAlgorithms(config);
-
- // Retrieve SPI record; will throw IllegalArgumentException if not found
- SpiRecord s = userRecord.mSpiRecords.getResourceOrThrow(config.getSpiResourceId());
-
- // Check to ensure that SPI has not already been used.
- if (s.getOwnedByTransform()) {
- throw new IllegalStateException("SPI already in use; cannot be used in new Transforms");
- }
-
- // If no remote address is supplied, then use one from the SPI.
- if (TextUtils.isEmpty(config.getDestinationAddress())) {
- config.setDestinationAddress(s.getDestinationAddress());
- }
-
- // All remote addresses must match
- if (!config.getDestinationAddress().equals(s.getDestinationAddress())) {
- throw new IllegalArgumentException("Mismatched remote addresseses.");
- }
-
- // This check is technically redundant due to the chain of custody between the SPI and
- // the IpSecConfig, but in the future if the dest is allowed to be set explicitly in
- // the transform, this will prevent us from messing up.
- checkInetAddress(config.getDestinationAddress());
-
- // Require a valid source address for all transforms.
- checkInetAddress(config.getSourceAddress());
-
- // Check to ensure source and destination have the same address family.
- String sourceAddress = config.getSourceAddress();
- String destinationAddress = config.getDestinationAddress();
- int sourceFamily = getFamily(sourceAddress);
- int destinationFamily = getFamily(destinationAddress);
- if (sourceFamily != destinationFamily) {
- throw new IllegalArgumentException(
- "Source address ("
- + sourceAddress
- + ") and destination address ("
- + destinationAddress
- + ") have different address families.");
- }
-
- // Throw an error if UDP Encapsulation is not used in IPv4.
- if (config.getEncapType() != IpSecTransform.ENCAP_NONE && sourceFamily != AF_INET) {
- throw new IllegalArgumentException(
- "UDP Encapsulation is not supported for this address family");
- }
-
- switch (config.getMode()) {
- case IpSecTransform.MODE_TRANSPORT:
- break;
- case IpSecTransform.MODE_TUNNEL:
- break;
- default:
- throw new IllegalArgumentException(
- "Invalid IpSecTransform.mode: " + config.getMode());
- }
-
- config.setMarkValue(0);
- config.setMarkMask(0);
- }
-
- private static final String TUNNEL_OP = AppOpsManager.OPSTR_MANAGE_IPSEC_TUNNELS;
-
- private void enforceTunnelFeatureAndPermissions(String callingPackage) {
- if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_IPSEC_TUNNELS)) {
- throw new UnsupportedOperationException(
- "IPsec Tunnel Mode requires PackageManager.FEATURE_IPSEC_TUNNELS");
- }
-
- Objects.requireNonNull(callingPackage, "Null calling package cannot create IpSec tunnels");
-
- // OP_MANAGE_IPSEC_TUNNELS will return MODE_ERRORED by default, including for the system
- // server. If the appop is not granted, require that the caller has the MANAGE_IPSEC_TUNNELS
- // permission or is the System Server.
- if (AppOpsManager.MODE_ALLOWED == getAppOpsManager().noteOpNoThrow(
- TUNNEL_OP, Binder.getCallingUid(), callingPackage)) {
- return;
- }
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.MANAGE_IPSEC_TUNNELS, "IpSecService");
- }
-
- private void createOrUpdateTransform(
- IpSecConfig c, int resourceId, SpiRecord spiRecord, EncapSocketRecord socketRecord)
- throws RemoteException {
-
- int encapType = c.getEncapType(), encapLocalPort = 0, encapRemotePort = 0;
- if (encapType != IpSecTransform.ENCAP_NONE) {
- encapLocalPort = socketRecord.getPort();
- encapRemotePort = c.getEncapRemotePort();
- }
-
- IpSecAlgorithm auth = c.getAuthentication();
- IpSecAlgorithm crypt = c.getEncryption();
- IpSecAlgorithm authCrypt = c.getAuthenticatedEncryption();
-
- String cryptName;
- if (crypt == null) {
- cryptName = (authCrypt == null) ? IpSecAlgorithm.CRYPT_NULL : "";
- } else {
- cryptName = crypt.getName();
- }
-
- mSrvConfig
- .getNetdInstance()
- .ipSecAddSecurityAssociation(
- Binder.getCallingUid(),
- c.getMode(),
- c.getSourceAddress(),
- c.getDestinationAddress(),
- (c.getNetwork() != null) ? c.getNetwork().getNetId() : 0,
- spiRecord.getSpi(),
- c.getMarkValue(),
- c.getMarkMask(),
- (auth != null) ? auth.getName() : "",
- (auth != null) ? auth.getKey() : new byte[] {},
- (auth != null) ? auth.getTruncationLengthBits() : 0,
- cryptName,
- (crypt != null) ? crypt.getKey() : new byte[] {},
- (crypt != null) ? crypt.getTruncationLengthBits() : 0,
- (authCrypt != null) ? authCrypt.getName() : "",
- (authCrypt != null) ? authCrypt.getKey() : new byte[] {},
- (authCrypt != null) ? authCrypt.getTruncationLengthBits() : 0,
- encapType,
- encapLocalPort,
- encapRemotePort,
- c.getXfrmInterfaceId());
- }
-
- /**
- * Create a IPsec transform, which represents a single security association in the kernel. The
- * transform will be cached by the system server and must be freed when no longer needed. It is
- * possible to free one, deleting the SA from underneath sockets that are using it, which will
- * result in all of those sockets becoming unable to send or receive data.
- */
- @Override
- public synchronized IpSecTransformResponse createTransform(
- IpSecConfig c, IBinder binder, String callingPackage) throws RemoteException {
- Objects.requireNonNull(c);
- if (c.getMode() == IpSecTransform.MODE_TUNNEL) {
- enforceTunnelFeatureAndPermissions(callingPackage);
- }
- checkIpSecConfig(c);
- Objects.requireNonNull(binder, "Null Binder passed to createTransform");
- final int resourceId = mNextResourceId++;
-
- UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
- List<RefcountedResource> dependencies = new ArrayList<>();
-
- if (!userRecord.mTransformQuotaTracker.isAvailable()) {
- return new IpSecTransformResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
- }
-
- EncapSocketRecord socketRecord = null;
- if (c.getEncapType() != IpSecTransform.ENCAP_NONE) {
- RefcountedResource<EncapSocketRecord> refcountedSocketRecord =
- userRecord.mEncapSocketRecords.getRefcountedResourceOrThrow(
- c.getEncapSocketResourceId());
- dependencies.add(refcountedSocketRecord);
- socketRecord = refcountedSocketRecord.getResource();
- }
-
- RefcountedResource<SpiRecord> refcountedSpiRecord =
- userRecord.mSpiRecords.getRefcountedResourceOrThrow(c.getSpiResourceId());
- dependencies.add(refcountedSpiRecord);
- SpiRecord spiRecord = refcountedSpiRecord.getResource();
-
- createOrUpdateTransform(c, resourceId, spiRecord, socketRecord);
-
- // SA was created successfully, time to construct a record and lock it away
- userRecord.mTransformRecords.put(
- resourceId,
- new RefcountedResource<TransformRecord>(
- new TransformRecord(resourceId, c, spiRecord, socketRecord),
- binder,
- dependencies.toArray(new RefcountedResource[dependencies.size()])));
- return new IpSecTransformResponse(IpSecManager.Status.OK, resourceId);
- }
-
- /**
- * Delete a transport mode transform that was previously allocated by + registered with the
- * system server. If this is called on an inactive (or non-existent) transform, it will not
- * return an error. It's safe to de-allocate transforms that may have already been deleted for
- * other reasons.
- */
- @Override
- public synchronized void deleteTransform(int resourceId) throws RemoteException {
- UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
- releaseResource(userRecord.mTransformRecords, resourceId);
- }
-
- /**
- * Apply an active transport mode transform to a socket, which will apply the IPsec security
- * association as a correspondent policy to the provided socket
- */
- @Override
- public synchronized void applyTransportModeTransform(
- ParcelFileDescriptor socket, int direction, int resourceId) throws RemoteException {
- int callingUid = Binder.getCallingUid();
- UserRecord userRecord = mUserResourceTracker.getUserRecord(callingUid);
- checkDirection(direction);
- // Get transform record; if no transform is found, will throw IllegalArgumentException
- TransformRecord info = userRecord.mTransformRecords.getResourceOrThrow(resourceId);
-
- // TODO: make this a function.
- if (info.pid != getCallingPid() || info.uid != callingUid) {
- throw new SecurityException("Only the owner of an IpSec Transform may apply it!");
- }
-
- // Get config and check that to-be-applied transform has the correct mode
- IpSecConfig c = info.getConfig();
- Preconditions.checkArgument(
- c.getMode() == IpSecTransform.MODE_TRANSPORT,
- "Transform mode was not Transport mode; cannot be applied to a socket");
-
- mSrvConfig
- .getNetdInstance()
- .ipSecApplyTransportModeTransform(
- socket,
- callingUid,
- direction,
- c.getSourceAddress(),
- c.getDestinationAddress(),
- info.getSpiRecord().getSpi());
- }
-
- /**
- * Remove transport mode transforms from a socket, applying the default (empty) policy. This
- * ensures that NO IPsec policy is applied to the socket (would be the equivalent of applying a
- * policy that performs no IPsec). Today the resourceId parameter is passed but not used:
- * reserved for future improved input validation.
- */
- @Override
- public synchronized void removeTransportModeTransforms(ParcelFileDescriptor socket)
- throws RemoteException {
- mSrvConfig
- .getNetdInstance()
- .ipSecRemoveTransportModeTransform(socket);
- }
-
- /**
- * Apply an active tunnel mode transform to a TunnelInterface, which will apply the IPsec
- * security association as a correspondent policy to the provided interface
- */
- @Override
- public synchronized void applyTunnelModeTransform(
- int tunnelResourceId, int direction,
- int transformResourceId, String callingPackage) throws RemoteException {
- enforceTunnelFeatureAndPermissions(callingPackage);
- checkDirection(direction);
-
- int callingUid = Binder.getCallingUid();
- UserRecord userRecord = mUserResourceTracker.getUserRecord(callingUid);
-
- // Get transform record; if no transform is found, will throw IllegalArgumentException
- TransformRecord transformInfo =
- userRecord.mTransformRecords.getResourceOrThrow(transformResourceId);
-
- // Get tunnelInterface record; if no such interface is found, will throw
- // IllegalArgumentException
- TunnelInterfaceRecord tunnelInterfaceInfo =
- userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelResourceId);
-
- // Get config and check that to-be-applied transform has the correct mode
- IpSecConfig c = transformInfo.getConfig();
- Preconditions.checkArgument(
- c.getMode() == IpSecTransform.MODE_TUNNEL,
- "Transform mode was not Tunnel mode; cannot be applied to a tunnel interface");
-
- EncapSocketRecord socketRecord = null;
- if (c.getEncapType() != IpSecTransform.ENCAP_NONE) {
- socketRecord =
- userRecord.mEncapSocketRecords.getResourceOrThrow(c.getEncapSocketResourceId());
- }
- SpiRecord spiRecord = transformInfo.getSpiRecord();
-
- int mark =
- (direction == IpSecManager.DIRECTION_OUT)
- ? tunnelInterfaceInfo.getOkey()
- : tunnelInterfaceInfo.getIkey(); // Ikey also used for FWD policies
-
- try {
- // Default to using the invalid SPI of 0 for inbound SAs. This allows policies to skip
- // SPI matching as part of the template resolution.
- int spi = IpSecManager.INVALID_SECURITY_PARAMETER_INDEX;
- c.setXfrmInterfaceId(tunnelInterfaceInfo.getIfId());
-
- // TODO: enable this when UPDSA supports updating marks. Adding kernel support upstream
- // (and backporting) would allow us to narrow the mark space, and ensure that the SA
- // and SPs have matching marks (as VTI are meant to be built).
- // Currently update does nothing with marks. Leave empty (defaulting to 0) to ensure the
- // config matches the actual allocated resources in the kernel.
- // All SAs will have zero marks (from creation time), and any policy that matches the
- // same src/dst could match these SAs. Non-IpSecService governed processes that
- // establish floating policies with the same src/dst may result in undefined
- // behavior. This is generally limited to vendor code due to the permissions
- // (CAP_NET_ADMIN) required.
- //
- // c.setMarkValue(mark);
- // c.setMarkMask(0xffffffff);
-
- if (direction == IpSecManager.DIRECTION_OUT) {
- // Set output mark via underlying network (output only)
- c.setNetwork(tunnelInterfaceInfo.getUnderlyingNetwork());
-
- // Set outbound SPI only. We want inbound to use any valid SA (old, new) on rekeys,
- // but want to guarantee outbound packets are sent over the new SA.
- spi = spiRecord.getSpi();
- }
-
- // Always update the policy with the relevant XFRM_IF_ID
- for (int selAddrFamily : ADDRESS_FAMILIES) {
- mSrvConfig
- .getNetdInstance()
- .ipSecUpdateSecurityPolicy(
- callingUid,
- selAddrFamily,
- direction,
- transformInfo.getConfig().getSourceAddress(),
- transformInfo.getConfig().getDestinationAddress(),
- spi, // If outbound, also add SPI to the policy.
- mark, // Must always set policy mark; ikey/okey for VTIs
- 0xffffffff,
- c.getXfrmInterfaceId());
- }
-
- // Update SA with tunnel mark (ikey or okey based on direction)
- createOrUpdateTransform(c, transformResourceId, spiRecord, socketRecord);
- } catch (ServiceSpecificException e) {
- if (e.errorCode == EINVAL) {
- throw new IllegalArgumentException(e.toString());
- } else {
- throw e;
- }
- }
- }
-
- @Override
- protected synchronized void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- mContext.enforceCallingOrSelfPermission(DUMP, TAG);
-
- pw.println("IpSecService dump:");
- pw.println("NetdNativeService Connection: " + (isNetdAlive() ? "alive" : "dead"));
- pw.println();
-
- pw.println("mUserResourceTracker:");
- pw.println(mUserResourceTracker);
- }
-}
diff --git a/services/core/java/com/android/server/NativeDaemonConnector.java b/services/core/java/com/android/server/NativeDaemonConnector.java
deleted file mode 100644
index eac767f7355c..000000000000
--- a/services/core/java/com/android/server/NativeDaemonConnector.java
+++ /dev/null
@@ -1,723 +0,0 @@
-/*
- * Copyright (C) 2007 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;
-
-import android.net.LocalSocket;
-import android.net.LocalSocketAddress;
-import android.os.Build;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.os.PowerManager;
-import android.os.SystemClock;
-import android.os.SystemProperties;
-import android.util.LocalLog;
-import android.util.Slog;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.Preconditions;
-import com.android.server.power.ShutdownThread;
-import com.google.android.collect.Lists;
-
-import java.io.FileDescriptor;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.PrintWriter;
-import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.ArrayBlockingQueue;
-import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-import java.util.LinkedList;
-import java.util.Objects;
-
-/**
- * Generic connector class for interfacing with a native daemon which uses the
- * {@code libsysutils} FrameworkListener protocol.
- */
-final class NativeDaemonConnector implements Runnable, Handler.Callback, Watchdog.Monitor {
- private final static boolean VDBG = false;
-
- private final String TAG;
-
- private String mSocket;
- private OutputStream mOutputStream;
- private LocalLog mLocalLog;
-
- private volatile boolean mDebug = false;
- private volatile Object mWarnIfHeld;
-
- private final ResponseQueue mResponseQueue;
-
- private final PowerManager.WakeLock mWakeLock;
-
- private final Looper mLooper;
-
- private INativeDaemonConnectorCallbacks mCallbacks;
- private Handler mCallbackHandler;
-
- private AtomicInteger mSequenceNumber;
-
- private static final long DEFAULT_TIMEOUT = 1 * 60 * 1000; /* 1 minute */
- private static final long WARN_EXECUTE_DELAY_MS = 500; /* .5 sec */
-
- /** Lock held whenever communicating with native daemon. */
- private final Object mDaemonLock = new Object();
-
- private final int BUFFER_SIZE = 4096;
-
- NativeDaemonConnector(INativeDaemonConnectorCallbacks callbacks, String socket,
- int responseQueueSize, String logTag, int maxLogSize, PowerManager.WakeLock wl) {
- this(callbacks, socket, responseQueueSize, logTag, maxLogSize, wl,
- FgThread.get().getLooper());
- }
-
- NativeDaemonConnector(INativeDaemonConnectorCallbacks callbacks, String socket,
- int responseQueueSize, String logTag, int maxLogSize, PowerManager.WakeLock wl,
- Looper looper) {
- mCallbacks = callbacks;
- mSocket = socket;
- mResponseQueue = new ResponseQueue(responseQueueSize);
- mWakeLock = wl;
- if (mWakeLock != null) {
- mWakeLock.setReferenceCounted(true);
- }
- mLooper = looper;
- mSequenceNumber = new AtomicInteger(0);
- TAG = logTag != null ? logTag : "NativeDaemonConnector";
- mLocalLog = new LocalLog(maxLogSize);
- }
-
- /**
- * Enable Set debugging mode, which causes messages to also be written to both
- * {@link Slog} in addition to internal log.
- */
- public void setDebug(boolean debug) {
- mDebug = debug;
- }
-
- /**
- * Like SystemClock.uptimeMillis, except truncated to an int so it will fit in a message arg.
- * Inaccurate across 49.7 days of uptime, but only used for debugging.
- */
- private int uptimeMillisInt() {
- return (int) SystemClock.uptimeMillis() & Integer.MAX_VALUE;
- }
-
- /**
- * Yell loudly if someone tries making future {@link #execute(Command)}
- * calls while holding a lock on the given object.
- */
- public void setWarnIfHeld(Object warnIfHeld) {
- Preconditions.checkState(mWarnIfHeld == null);
- mWarnIfHeld = Objects.requireNonNull(warnIfHeld);
- }
-
- @Override
- public void run() {
- mCallbackHandler = new Handler(mLooper, this);
-
- while (true) {
- if (isShuttingDown()) break;
- try {
- listenToSocket();
- } catch (Exception e) {
- loge("Error in NativeDaemonConnector: " + e);
- if (isShuttingDown()) break;
- SystemClock.sleep(5000);
- }
- }
- }
-
- private static boolean isShuttingDown() {
- String shutdownAct = SystemProperties.get(
- ShutdownThread.SHUTDOWN_ACTION_PROPERTY, "");
- return shutdownAct != null && shutdownAct.length() > 0;
- }
-
- @Override
- public boolean handleMessage(Message msg) {
- final String event = (String) msg.obj;
- final int start = uptimeMillisInt();
- final int sent = msg.arg1;
- try {
- if (!mCallbacks.onEvent(msg.what, event, NativeDaemonEvent.unescapeArgs(event))) {
- log(String.format("Unhandled event '%s'", event));
- }
- } catch (Exception e) {
- loge("Error handling '" + event + "': " + e);
- } finally {
- if (mCallbacks.onCheckHoldWakeLock(msg.what) && mWakeLock != null) {
- mWakeLock.release();
- }
- final int end = uptimeMillisInt();
- if (start > sent && start - sent > WARN_EXECUTE_DELAY_MS) {
- loge(String.format("NDC event {%s} processed too late: %dms", event, start - sent));
- }
- if (end > start && end - start > WARN_EXECUTE_DELAY_MS) {
- loge(String.format("NDC event {%s} took too long: %dms", event, end - start));
- }
- }
- return true;
- }
-
- private LocalSocketAddress determineSocketAddress() {
- // If we're testing, set up a socket in a namespace that's accessible to test code.
- // In order to ensure that unprivileged apps aren't able to impersonate native daemons on
- // production devices, even if said native daemons ill-advisedly pick a socket name that
- // starts with __test__, only allow this on debug builds.
- if (mSocket.startsWith("__test__") && Build.IS_DEBUGGABLE) {
- return new LocalSocketAddress(mSocket);
- } else {
- return new LocalSocketAddress(mSocket, LocalSocketAddress.Namespace.RESERVED);
- }
- }
-
- private void listenToSocket() throws IOException {
- LocalSocket socket = null;
-
- try {
- socket = new LocalSocket();
- LocalSocketAddress address = determineSocketAddress();
-
- socket.connect(address);
-
- InputStream inputStream = socket.getInputStream();
- synchronized (mDaemonLock) {
- mOutputStream = socket.getOutputStream();
- }
-
- mCallbacks.onDaemonConnected();
-
- FileDescriptor[] fdList = null;
- byte[] buffer = new byte[BUFFER_SIZE];
- int start = 0;
-
- while (true) {
- int count = inputStream.read(buffer, start, BUFFER_SIZE - start);
- if (count < 0) {
- loge("got " + count + " reading with start = " + start);
- break;
- }
- fdList = socket.getAncillaryFileDescriptors();
-
- // Add our starting point to the count and reset the start.
- count += start;
- start = 0;
-
- for (int i = 0; i < count; i++) {
- if (buffer[i] == 0) {
- // Note - do not log this raw message since it may contain
- // sensitive data
- final String rawEvent = new String(
- buffer, start, i - start, StandardCharsets.UTF_8);
-
- boolean releaseWl = false;
- try {
- final NativeDaemonEvent event =
- NativeDaemonEvent.parseRawEvent(rawEvent, fdList);
-
- log("RCV <- {" + event + "}");
-
- if (event.isClassUnsolicited()) {
- // TODO: migrate to sending NativeDaemonEvent instances
- if (mCallbacks.onCheckHoldWakeLock(event.getCode())
- && mWakeLock != null) {
- mWakeLock.acquire();
- releaseWl = true;
- }
- Message msg = mCallbackHandler.obtainMessage(
- event.getCode(), uptimeMillisInt(), 0, event.getRawEvent());
- if (mCallbackHandler.sendMessage(msg)) {
- releaseWl = false;
- }
- } else {
- mResponseQueue.add(event.getCmdNumber(), event);
- }
- } catch (IllegalArgumentException e) {
- log("Problem parsing message " + e);
- } finally {
- if (releaseWl) {
- mWakeLock.release();
- }
- }
-
- start = i + 1;
- }
- }
-
- if (start == 0) {
- log("RCV incomplete");
- }
-
- // We should end at the amount we read. If not, compact then
- // buffer and read again.
- if (start != count) {
- final int remaining = BUFFER_SIZE - start;
- System.arraycopy(buffer, start, buffer, 0, remaining);
- start = remaining;
- } else {
- start = 0;
- }
- }
- } catch (IOException ex) {
- loge("Communications error: " + ex);
- throw ex;
- } finally {
- synchronized (mDaemonLock) {
- if (mOutputStream != null) {
- try {
- loge("closing stream for " + mSocket);
- mOutputStream.close();
- } catch (IOException e) {
- loge("Failed closing output stream: " + e);
- }
- mOutputStream = null;
- }
- }
-
- try {
- if (socket != null) {
- socket.close();
- }
- } catch (IOException ex) {
- loge("Failed closing socket: " + ex);
- }
- }
- }
-
- /**
- * Wrapper around argument that indicates it's sensitive and shouldn't be
- * logged.
- */
- public static class SensitiveArg {
- private final Object mArg;
-
- public SensitiveArg(Object arg) {
- mArg = arg;
- }
-
- @Override
- public String toString() {
- return String.valueOf(mArg);
- }
- }
-
- /**
- * Make command for daemon, escaping arguments as needed.
- */
- @VisibleForTesting
- static void makeCommand(StringBuilder rawBuilder, StringBuilder logBuilder, int sequenceNumber,
- String cmd, Object... args) {
- if (cmd.indexOf('\0') >= 0) {
- throw new IllegalArgumentException("Unexpected command: " + cmd);
- }
- if (cmd.indexOf(' ') >= 0) {
- throw new IllegalArgumentException("Arguments must be separate from command");
- }
-
- rawBuilder.append(sequenceNumber).append(' ').append(cmd);
- logBuilder.append(sequenceNumber).append(' ').append(cmd);
- for (Object arg : args) {
- final String argString = String.valueOf(arg);
- if (argString.indexOf('\0') >= 0) {
- throw new IllegalArgumentException("Unexpected argument: " + arg);
- }
-
- rawBuilder.append(' ');
- logBuilder.append(' ');
-
- appendEscaped(rawBuilder, argString);
- if (arg instanceof SensitiveArg) {
- logBuilder.append("[scrubbed]");
- } else {
- appendEscaped(logBuilder, argString);
- }
- }
-
- rawBuilder.append('\0');
- }
-
- /**
- * Method that waits until all asychronous notifications sent by the native daemon have
- * been processed. This method must not be called on the notification thread or an
- * exception will be thrown.
- */
- public void waitForCallbacks() {
- if (Thread.currentThread() == mLooper.getThread()) {
- throw new IllegalStateException("Must not call this method on callback thread");
- }
-
- final CountDownLatch latch = new CountDownLatch(1);
- mCallbackHandler.post(new Runnable() {
- @Override
- public void run() {
- latch.countDown();
- }
- });
- try {
- latch.await();
- } catch (InterruptedException e) {
- Slog.wtf(TAG, "Interrupted while waiting for unsolicited response handling", e);
- }
- }
-
- /**
- * Issue the given command to the native daemon and return a single expected
- * response.
- *
- * @throws NativeDaemonConnectorException when problem communicating with
- * native daemon, or if the response matches
- * {@link NativeDaemonEvent#isClassClientError()} or
- * {@link NativeDaemonEvent#isClassServerError()}.
- */
- public NativeDaemonEvent execute(Command cmd) throws NativeDaemonConnectorException {
- return execute(cmd.mCmd, cmd.mArguments.toArray());
- }
-
- /**
- * Issue the given command to the native daemon and return a single expected
- * response. Any arguments must be separated from base command so they can
- * be properly escaped.
- *
- * @throws NativeDaemonConnectorException when problem communicating with
- * native daemon, or if the response matches
- * {@link NativeDaemonEvent#isClassClientError()} or
- * {@link NativeDaemonEvent#isClassServerError()}.
- */
- public NativeDaemonEvent execute(String cmd, Object... args)
- throws NativeDaemonConnectorException {
- return execute(DEFAULT_TIMEOUT, cmd, args);
- }
-
- public NativeDaemonEvent execute(long timeoutMs, String cmd, Object... args)
- throws NativeDaemonConnectorException {
- final NativeDaemonEvent[] events = executeForList(timeoutMs, cmd, args);
- if (events.length != 1) {
- throw new NativeDaemonConnectorException(
- "Expected exactly one response, but received " + events.length);
- }
- return events[0];
- }
-
- /**
- * Issue the given command to the native daemon and return any
- * {@link NativeDaemonEvent#isClassContinue()} responses, including the
- * final terminal response.
- *
- * @throws NativeDaemonConnectorException when problem communicating with
- * native daemon, or if the response matches
- * {@link NativeDaemonEvent#isClassClientError()} or
- * {@link NativeDaemonEvent#isClassServerError()}.
- */
- public NativeDaemonEvent[] executeForList(Command cmd) throws NativeDaemonConnectorException {
- return executeForList(cmd.mCmd, cmd.mArguments.toArray());
- }
-
- /**
- * Issue the given command to the native daemon and return any
- * {@link NativeDaemonEvent#isClassContinue()} responses, including the
- * final terminal response. Any arguments must be separated from base
- * command so they can be properly escaped.
- *
- * @throws NativeDaemonConnectorException when problem communicating with
- * native daemon, or if the response matches
- * {@link NativeDaemonEvent#isClassClientError()} or
- * {@link NativeDaemonEvent#isClassServerError()}.
- */
- public NativeDaemonEvent[] executeForList(String cmd, Object... args)
- throws NativeDaemonConnectorException {
- return executeForList(DEFAULT_TIMEOUT, cmd, args);
- }
-
- /**
- * Issue the given command to the native daemon and return any {@linke
- * NativeDaemonEvent@isClassContinue()} responses, including the final
- * terminal response. Note that the timeout does not count time in deep
- * sleep. Any arguments must be separated from base command so they can be
- * properly escaped.
- *
- * @throws NativeDaemonConnectorException when problem communicating with
- * native daemon, or if the response matches
- * {@link NativeDaemonEvent#isClassClientError()} or
- * {@link NativeDaemonEvent#isClassServerError()}.
- */
- public NativeDaemonEvent[] executeForList(long timeoutMs, String cmd, Object... args)
- throws NativeDaemonConnectorException {
- if (mWarnIfHeld != null && Thread.holdsLock(mWarnIfHeld)) {
- Slog.wtf(TAG, "Calling thread " + Thread.currentThread().getName() + " is holding 0x"
- + Integer.toHexString(System.identityHashCode(mWarnIfHeld)), new Throwable());
- }
-
- final long startTime = SystemClock.elapsedRealtime();
-
- final ArrayList<NativeDaemonEvent> events = Lists.newArrayList();
-
- final StringBuilder rawBuilder = new StringBuilder();
- final StringBuilder logBuilder = new StringBuilder();
- final int sequenceNumber = mSequenceNumber.incrementAndGet();
-
- makeCommand(rawBuilder, logBuilder, sequenceNumber, cmd, args);
-
- final String rawCmd = rawBuilder.toString();
- final String logCmd = logBuilder.toString();
-
- log("SND -> {" + logCmd + "}");
-
- synchronized (mDaemonLock) {
- if (mOutputStream == null) {
- throw new NativeDaemonConnectorException("missing output stream");
- } else {
- try {
- mOutputStream.write(rawCmd.getBytes(StandardCharsets.UTF_8));
- } catch (IOException e) {
- throw new NativeDaemonConnectorException("problem sending command", e);
- }
- }
- }
-
- NativeDaemonEvent event = null;
- do {
- event = mResponseQueue.remove(sequenceNumber, timeoutMs, logCmd);
- if (event == null) {
- loge("timed-out waiting for response to " + logCmd);
- throw new NativeDaemonTimeoutException(logCmd, event);
- }
- if (VDBG) log("RMV <- {" + event + "}");
- events.add(event);
- } while (event.isClassContinue());
-
- final long endTime = SystemClock.elapsedRealtime();
- if (endTime - startTime > WARN_EXECUTE_DELAY_MS) {
- loge("NDC Command {" + logCmd + "} took too long (" + (endTime - startTime) + "ms)");
- }
-
- if (event.isClassClientError()) {
- throw new NativeDaemonArgumentException(logCmd, event);
- }
- if (event.isClassServerError()) {
- throw new NativeDaemonFailureException(logCmd, event);
- }
-
- return events.toArray(new NativeDaemonEvent[events.size()]);
- }
-
- /**
- * Append the given argument to {@link StringBuilder}, escaping as needed,
- * and surrounding with quotes when it contains spaces.
- */
- @VisibleForTesting
- static void appendEscaped(StringBuilder builder, String arg) {
- final boolean hasSpaces = arg.indexOf(' ') >= 0;
- if (hasSpaces) {
- builder.append('"');
- }
-
- final int length = arg.length();
- for (int i = 0; i < length; i++) {
- final char c = arg.charAt(i);
-
- if (c == '"') {
- builder.append("\\\"");
- } else if (c == '\\') {
- builder.append("\\\\");
- } else {
- builder.append(c);
- }
- }
-
- if (hasSpaces) {
- builder.append('"');
- }
- }
-
- private static class NativeDaemonArgumentException extends NativeDaemonConnectorException {
- public NativeDaemonArgumentException(String command, NativeDaemonEvent event) {
- super(command, event);
- }
-
- @Override
- public IllegalArgumentException rethrowAsParcelableException() {
- throw new IllegalArgumentException(getMessage(), this);
- }
- }
-
- private static class NativeDaemonFailureException extends NativeDaemonConnectorException {
- public NativeDaemonFailureException(String command, NativeDaemonEvent event) {
- super(command, event);
- }
- }
-
- /**
- * Command builder that handles argument list building. Any arguments must
- * be separated from base command so they can be properly escaped.
- */
- public static class Command {
- private String mCmd;
- private ArrayList<Object> mArguments = Lists.newArrayList();
-
- public Command(String cmd, Object... args) {
- mCmd = cmd;
- for (Object arg : args) {
- appendArg(arg);
- }
- }
-
- public Command appendArg(Object arg) {
- mArguments.add(arg);
- return this;
- }
- }
-
- /** {@inheritDoc} */
- public void monitor() {
- synchronized (mDaemonLock) { }
- }
-
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- mLocalLog.dump(fd, pw, args);
- pw.println();
- mResponseQueue.dump(fd, pw, args);
- }
-
- private void log(String logstring) {
- if (mDebug) Slog.d(TAG, logstring);
- mLocalLog.log(logstring);
- }
-
- private void loge(String logstring) {
- Slog.e(TAG, logstring);
- mLocalLog.log(logstring);
- }
-
- private static class ResponseQueue {
-
- private static class PendingCmd {
- public final int cmdNum;
- public final String logCmd;
-
- public BlockingQueue<NativeDaemonEvent> responses =
- new ArrayBlockingQueue<NativeDaemonEvent>(10);
-
- // The availableResponseCount member is used to track when we can remove this
- // instance from the ResponseQueue.
- // This is used under the protection of a sync of the mPendingCmds object.
- // A positive value means we've had more writers retreive this object while
- // a negative value means we've had more readers. When we've had an equal number
- // (it goes to zero) we can remove this object from the mPendingCmds list.
- // Note that we may have more responses for this command (and more readers
- // coming), but that would result in a new PendingCmd instance being created
- // and added with the same cmdNum.
- // Also note that when this goes to zero it just means a parity of readers and
- // writers have retrieved this object - not that they are done using it. The
- // responses queue may well have more responses yet to be read or may get more
- // responses added to it. But all those readers/writers have retreived and
- // hold references to this instance already so it can be removed from
- // mPendingCmds queue.
- public int availableResponseCount;
-
- public PendingCmd(int cmdNum, String logCmd) {
- this.cmdNum = cmdNum;
- this.logCmd = logCmd;
- }
- }
-
- private final LinkedList<PendingCmd> mPendingCmds;
- private int mMaxCount;
-
- ResponseQueue(int maxCount) {
- mPendingCmds = new LinkedList<PendingCmd>();
- mMaxCount = maxCount;
- }
-
- public void add(int cmdNum, NativeDaemonEvent response) {
- PendingCmd found = null;
- synchronized (mPendingCmds) {
- for (PendingCmd pendingCmd : mPendingCmds) {
- if (pendingCmd.cmdNum == cmdNum) {
- found = pendingCmd;
- break;
- }
- }
- if (found == null) {
- // didn't find it - make sure our queue isn't too big before adding
- while (mPendingCmds.size() >= mMaxCount) {
- Slog.e("NativeDaemonConnector.ResponseQueue",
- "more buffered than allowed: " + mPendingCmds.size() +
- " >= " + mMaxCount);
- // let any waiter timeout waiting for this
- PendingCmd pendingCmd = mPendingCmds.remove();
- Slog.e("NativeDaemonConnector.ResponseQueue",
- "Removing request: " + pendingCmd.logCmd + " (" +
- pendingCmd.cmdNum + ")");
- }
- found = new PendingCmd(cmdNum, null);
- mPendingCmds.add(found);
- }
- found.availableResponseCount++;
- // if a matching remove call has already retrieved this we can remove this
- // instance from our list
- if (found.availableResponseCount == 0) mPendingCmds.remove(found);
- }
- try {
- found.responses.put(response);
- } catch (InterruptedException e) { }
- }
-
- // note that the timeout does not count time in deep sleep. If you don't want
- // the device to sleep, hold a wakelock
- public NativeDaemonEvent remove(int cmdNum, long timeoutMs, String logCmd) {
- PendingCmd found = null;
- synchronized (mPendingCmds) {
- for (PendingCmd pendingCmd : mPendingCmds) {
- if (pendingCmd.cmdNum == cmdNum) {
- found = pendingCmd;
- break;
- }
- }
- if (found == null) {
- found = new PendingCmd(cmdNum, logCmd);
- mPendingCmds.add(found);
- }
- found.availableResponseCount--;
- // if a matching add call has already retrieved this we can remove this
- // instance from our list
- if (found.availableResponseCount == 0) mPendingCmds.remove(found);
- }
- NativeDaemonEvent result = null;
- try {
- result = found.responses.poll(timeoutMs, TimeUnit.MILLISECONDS);
- } catch (InterruptedException e) {}
- if (result == null) {
- Slog.e("NativeDaemonConnector.ResponseQueue", "Timeout waiting for response");
- }
- return result;
- }
-
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- pw.println("Pending requests:");
- synchronized (mPendingCmds) {
- for (PendingCmd pendingCmd : mPendingCmds) {
- pw.println(" Cmd " + pendingCmd.cmdNum + " - " + pendingCmd.logCmd);
- }
- }
- }
- }
-}
diff --git a/services/core/java/com/android/server/NativeDaemonEvent.java b/services/core/java/com/android/server/NativeDaemonEvent.java
deleted file mode 100644
index e6feda3dad22..000000000000
--- a/services/core/java/com/android/server/NativeDaemonEvent.java
+++ /dev/null
@@ -1,268 +0,0 @@
-/*
- * Copyright (C) 2011 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;
-
-import android.util.Slog;
-import com.google.android.collect.Lists;
-
-import java.io.FileDescriptor;
-import java.util.ArrayList;
-
-/**
- * Parsed event from native side of {@link NativeDaemonConnector}.
- */
-public class NativeDaemonEvent {
-
- // TODO: keep class ranges in sync with ResponseCode.h
- // TODO: swap client and server error ranges to roughly mirror HTTP spec
-
- private final int mCmdNumber;
- private final int mCode;
- private final String mMessage;
- private final String mRawEvent;
- private final String mLogMessage;
- private String[] mParsed;
- private FileDescriptor[] mFdList;
-
- private NativeDaemonEvent(int cmdNumber, int code, String message,
- String rawEvent, String logMessage, FileDescriptor[] fdList) {
- mCmdNumber = cmdNumber;
- mCode = code;
- mMessage = message;
- mRawEvent = rawEvent;
- mLogMessage = logMessage;
- mParsed = null;
- mFdList = fdList;
- }
-
- static public final String SENSITIVE_MARKER = "{{sensitive}}";
-
- public int getCmdNumber() {
- return mCmdNumber;
- }
-
- public int getCode() {
- return mCode;
- }
-
- public String getMessage() {
- return mMessage;
- }
-
- public FileDescriptor[] getFileDescriptors() {
- return mFdList;
- }
-
- @Deprecated
- public String getRawEvent() {
- return mRawEvent;
- }
-
- @Override
- public String toString() {
- return mLogMessage;
- }
-
- /**
- * Test if event represents a partial response which is continued in
- * additional subsequent events.
- */
- public boolean isClassContinue() {
- return mCode >= 100 && mCode < 200;
- }
-
- /**
- * Test if event represents a command success.
- */
- public boolean isClassOk() {
- return mCode >= 200 && mCode < 300;
- }
-
- /**
- * Test if event represents a remote native daemon error.
- */
- public boolean isClassServerError() {
- return mCode >= 400 && mCode < 500;
- }
-
- /**
- * Test if event represents a command syntax or argument error.
- */
- public boolean isClassClientError() {
- return mCode >= 500 && mCode < 600;
- }
-
- /**
- * Test if event represents an unsolicited event from native daemon.
- */
- public boolean isClassUnsolicited() {
- return isClassUnsolicited(mCode);
- }
-
- private static boolean isClassUnsolicited(int code) {
- return code >= 600 && code < 700;
- }
-
- /**
- * Verify this event matches the given code.
- *
- * @throws IllegalStateException if {@link #getCode()} doesn't match.
- */
- public void checkCode(int code) {
- if (mCode != code) {
- throw new IllegalStateException("Expected " + code + " but was: " + this);
- }
- }
-
- /**
- * Parse the given raw event into {@link NativeDaemonEvent} instance.
- *
- * @throws IllegalArgumentException when line doesn't match format expected
- * from native side.
- */
- public static NativeDaemonEvent parseRawEvent(String rawEvent, FileDescriptor[] fdList) {
- final String[] parsed = rawEvent.split(" ");
- if (parsed.length < 2) {
- throw new IllegalArgumentException("Insufficient arguments");
- }
-
- int skiplength = 0;
-
- final int code;
- try {
- code = Integer.parseInt(parsed[0]);
- skiplength = parsed[0].length() + 1;
- } catch (NumberFormatException e) {
- throw new IllegalArgumentException("problem parsing code", e);
- }
-
- int cmdNumber = -1;
- if (isClassUnsolicited(code) == false) {
- if (parsed.length < 3) {
- throw new IllegalArgumentException("Insufficient arguemnts");
- }
- try {
- cmdNumber = Integer.parseInt(parsed[1]);
- skiplength += parsed[1].length() + 1;
- } catch (NumberFormatException e) {
- throw new IllegalArgumentException("problem parsing cmdNumber", e);
- }
- }
-
- String logMessage = rawEvent;
- if (parsed.length > 2 && parsed[2].equals(SENSITIVE_MARKER)) {
- skiplength += parsed[2].length() + 1;
- logMessage = parsed[0] + " " + parsed[1] + " {}";
- }
-
- final String message = rawEvent.substring(skiplength);
-
- return new NativeDaemonEvent(cmdNumber, code, message, rawEvent, logMessage, fdList);
- }
-
- /**
- * Filter the given {@link NativeDaemonEvent} list, returning
- * {@link #getMessage()} for any events matching the requested code.
- */
- public static String[] filterMessageList(NativeDaemonEvent[] events, int matchCode) {
- final ArrayList<String> result = Lists.newArrayList();
- for (NativeDaemonEvent event : events) {
- if (event.getCode() == matchCode) {
- result.add(event.getMessage());
- }
- }
- return result.toArray(new String[result.size()]);
- }
-
- /**
- * Find the Nth field of the event.
- *
- * This ignores and code or cmdNum, the first return value is given for N=0.
- * Also understands "\"quoted\" multiword responses" and tries them as a single field
- */
- public String getField(int n) {
- if (mParsed == null) {
- mParsed = unescapeArgs(mRawEvent);
- }
- n += 2; // skip code and command#
- if (n > mParsed.length) return null;
- return mParsed[n];
- }
-
- public static String[] unescapeArgs(String rawEvent) {
- final boolean DEBUG_ROUTINE = false;
- final String LOGTAG = "unescapeArgs";
- final ArrayList<String> parsed = new ArrayList<String>();
- final int length = rawEvent.length();
- int current = 0;
- int wordEnd = -1;
- boolean quoted = false;
-
- if (DEBUG_ROUTINE) Slog.e(LOGTAG, "parsing '" + rawEvent + "'");
- if (rawEvent.charAt(current) == '\"') {
- quoted = true;
- current++;
- }
- while (current < length) {
- // find the end of the word
- char terminator = quoted ? '\"' : ' ';
- wordEnd = current;
- while (wordEnd < length && rawEvent.charAt(wordEnd) != terminator) {
- if (rawEvent.charAt(wordEnd) == '\\') {
- // skip the escaped char
- ++wordEnd;
- }
- ++wordEnd;
- }
- if (wordEnd > length) wordEnd = length;
- String word = rawEvent.substring(current, wordEnd);
- current += word.length();
- if (!quoted) {
- word = word.trim();
- } else {
- current++; // skip the trailing quote
- }
- // unescape stuff within the word
- word = word.replace("\\\\", "\\");
- word = word.replace("\\\"", "\"");
-
- if (DEBUG_ROUTINE) Slog.e(LOGTAG, "found '" + word + "'");
- parsed.add(word);
-
- // find the beginning of the next word - either of these options
- int nextSpace = rawEvent.indexOf(' ', current);
- int nextQuote = rawEvent.indexOf(" \"", current);
- if (DEBUG_ROUTINE) {
- Slog.e(LOGTAG, "nextSpace=" + nextSpace + ", nextQuote=" + nextQuote);
- }
- if (nextQuote > -1 && nextQuote <= nextSpace) {
- quoted = true;
- current = nextQuote + 2;
- } else {
- quoted = false;
- if (nextSpace > -1) {
- current = nextSpace + 1;
- }
- } // else we just start the next word after the current and read til the end
- if (DEBUG_ROUTINE) {
- Slog.e(LOGTAG, "next loop - current=" + current +
- ", length=" + length + ", quoted=" + quoted);
- }
- }
- return parsed.toArray(new String[parsed.size()]);
- }
-}
diff --git a/services/core/java/com/android/server/NativeDaemonTimeoutException.java b/services/core/java/com/android/server/NativeDaemonTimeoutException.java
deleted file mode 100644
index 658f7d6264eb..000000000000
--- a/services/core/java/com/android/server/NativeDaemonTimeoutException.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2015 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;
-
-/**
- * An exception that indicates there was a timeout with a
- * {@link NativeDaemonConnector} operation.
- */
-public class NativeDaemonTimeoutException extends NativeDaemonConnectorException {
- public NativeDaemonTimeoutException(String command, NativeDaemonEvent event) {
- super(command, event);
- }
-}
-
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 3ea0ce173745..b59cd4c0f212 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -20,30 +20,29 @@ import static android.Manifest.permission.CONNECTIVITY_INTERNAL;
import static android.Manifest.permission.NETWORK_SETTINGS;
import static android.Manifest.permission.OBSERVE_NETWORK_POLICY;
import static android.Manifest.permission.SHUTDOWN;
+import static android.net.ConnectivityManager.FIREWALL_CHAIN_DOZABLE;
+import static android.net.ConnectivityManager.FIREWALL_CHAIN_LOW_POWER_STANDBY;
+import static android.net.ConnectivityManager.FIREWALL_CHAIN_POWERSAVE;
+import static android.net.ConnectivityManager.FIREWALL_CHAIN_RESTRICTED;
+import static android.net.ConnectivityManager.FIREWALL_CHAIN_STANDBY;
import static android.net.INetd.FIREWALL_ALLOWLIST;
-import static android.net.INetd.FIREWALL_CHAIN_DOZABLE;
import static android.net.INetd.FIREWALL_CHAIN_NONE;
-import static android.net.INetd.FIREWALL_CHAIN_POWERSAVE;
-import static android.net.INetd.FIREWALL_CHAIN_RESTRICTED;
-import static android.net.INetd.FIREWALL_CHAIN_STANDBY;
import static android.net.INetd.FIREWALL_DENYLIST;
import static android.net.INetd.FIREWALL_RULE_ALLOW;
import static android.net.INetd.FIREWALL_RULE_DENY;
import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_DOZABLE;
+import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_LOW_POWER_STANDBY;
import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_POWERSAVE;
import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_RESTRICTED;
import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_STANDBY;
import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT;
-import static android.net.NetworkStats.SET_DEFAULT;
-import static android.net.NetworkStats.STATS_PER_UID;
-import static android.net.NetworkStats.TAG_NONE;
-import static android.net.TrafficStats.UID_TETHERING;
-import static com.android.server.NetworkManagementSocketTagger.PROP_QTAGUID_ENABLED;
+import static com.android.net.module.util.NetworkStatsUtils.LIMIT_GLOBAL_ALERT;
import android.annotation.NonNull;
import android.app.ActivityManager;
import android.content.Context;
+import android.net.ConnectivityManager;
import android.net.INetd;
import android.net.INetdUnsolicitedEventListener;
import android.net.INetworkManagementEventObserver;
@@ -57,11 +56,7 @@ import android.net.NetworkPolicyManager;
import android.net.NetworkStack;
import android.net.NetworkStats;
import android.net.RouteInfo;
-import android.net.TetherStatsParcel;
import android.net.UidRangeParcel;
-import android.net.shared.NetdUtils;
-import android.net.shared.RouteUtils;
-import android.net.shared.RouteUtils.ModifyOperation;
import android.net.util.NetdService;
import android.os.BatteryStats;
import android.os.Binder;
@@ -75,7 +70,6 @@ import android.os.ServiceManager;
import android.os.ServiceSpecificException;
import android.os.StrictMode;
import android.os.SystemClock;
-import android.os.SystemProperties;
import android.os.Trace;
import android.text.TextUtils;
import android.util.Log;
@@ -88,6 +82,8 @@ import com.android.internal.app.IBatteryStats;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.HexDump;
import com.android.internal.util.Preconditions;
+import com.android.net.module.util.NetdUtils;
+import com.android.net.module.util.NetdUtils.ModifyOperation;
import com.google.android.collect.Maps;
@@ -137,12 +133,6 @@ public class NetworkManagementService extends INetworkManagementService.Stub {
private static final int MAX_UID_RANGES_PER_COMMAND = 10;
- /**
- * Name representing {@link #setGlobalAlert(long)} limit when delivered to
- * {@link INetworkManagementEventObserver#limitReached(String, String)}.
- */
- public static final String LIMIT_GLOBAL_ALERT = "globalAlert";
-
static final int DAEMON_MSG_MOBILE_CONN_REAL_TIME_INFO = 1;
static final boolean MODIFY_OPERATION_ADD = true;
@@ -218,6 +208,11 @@ public class NetworkManagementService extends INetworkManagementService.Stub {
*/
@GuardedBy("mRulesLock")
private SparseIntArray mUidFirewallRestrictedRules = new SparseIntArray();
+ /**
+ * Contains the per-UID firewall rules that are used when Low Power Standby is enabled.
+ */
+ @GuardedBy("mRulesLock")
+ private SparseIntArray mUidFirewallLowPowerStandbyRules = new SparseIntArray();
/** Set of states for the child firewall chains. True if the chain is active. */
@GuardedBy("mRulesLock")
final SparseBooleanArray mFirewallChainStates = new SparseBooleanArray();
@@ -447,9 +442,6 @@ public class NetworkManagementService extends INetworkManagementService.Stub {
// push any existing quota or UID rules
synchronized (mQuotaLock) {
- // Netd unconditionally enable bandwidth control
- SystemProperties.set(PROP_QTAGUID_ENABLED, "1");
-
mStrictEnabled = true;
setDataSaverModeEnabled(mDataSaverMode);
@@ -521,12 +513,14 @@ public class NetworkManagementService extends INetworkManagementService.Stub {
syncFirewallChainLocked(FIREWALL_CHAIN_DOZABLE, "dozable ");
syncFirewallChainLocked(FIREWALL_CHAIN_POWERSAVE, "powersave ");
syncFirewallChainLocked(FIREWALL_CHAIN_RESTRICTED, "restricted ");
+ syncFirewallChainLocked(FIREWALL_CHAIN_LOW_POWER_STANDBY, "low power standby ");
final int[] chains = {
FIREWALL_CHAIN_STANDBY,
FIREWALL_CHAIN_DOZABLE,
FIREWALL_CHAIN_POWERSAVE,
- FIREWALL_CHAIN_RESTRICTED
+ FIREWALL_CHAIN_RESTRICTED,
+ FIREWALL_CHAIN_LOW_POWER_STANDBY
};
for (int chain : chains) {
@@ -831,13 +825,13 @@ public class NetworkManagementService extends INetworkManagementService.Stub {
@Override
public void addRoute(int netId, RouteInfo route) {
NetworkStack.checkNetworkStackPermission(mContext);
- RouteUtils.modifyRoute(mNetdService, ModifyOperation.ADD, netId, route);
+ NetdUtils.modifyRoute(mNetdService, ModifyOperation.ADD, netId, route);
}
@Override
public void removeRoute(int netId, RouteInfo route) {
NetworkStack.checkNetworkStackPermission(mContext);
- RouteUtils.modifyRoute(mNetdService, ModifyOperation.REMOVE, netId, route);
+ NetdUtils.modifyRoute(mNetdService, ModifyOperation.REMOVE, netId, route);
}
private ArrayList<String> readRouteList(String filename) {
@@ -1169,19 +1163,12 @@ public class NetworkManagementService extends INetworkManagementService.Stub {
}
Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "inetd bandwidth");
+ final ConnectivityManager cm = mContext.getSystemService(ConnectivityManager.class);
try {
if (allowlist) {
- if (enable) {
- mNetdService.bandwidthAddNiceApp(uid);
- } else {
- mNetdService.bandwidthRemoveNiceApp(uid);
- }
+ cm.updateMeteredNetworkAllowList(uid, enable);
} else {
- if (enable) {
- mNetdService.bandwidthAddNaughtyApp(uid);
- } else {
- mNetdService.bandwidthRemoveNaughtyApp(uid);
- }
+ cm.updateMeteredNetworkDenyList(uid, enable);
}
synchronized (mRulesLock) {
if (enable) {
@@ -1190,7 +1177,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub {
quotaList.delete(uid);
}
}
- } catch (RemoteException | ServiceSpecificException e) {
+ } catch (RuntimeException e) {
throw new IllegalStateException(e);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_NETWORK);
@@ -1303,40 +1290,9 @@ public class NetworkManagementService extends INetworkManagementService.Stub {
private class NetdTetheringStatsProvider extends ITetheringStatsProvider.Stub {
@Override
public NetworkStats getTetherStats(int how) {
- // We only need to return per-UID stats. Per-device stats are already counted by
- // interface counters.
- if (how != STATS_PER_UID) {
- return new NetworkStats(SystemClock.elapsedRealtime(), 0);
- }
-
- final TetherStatsParcel[] tetherStatsVec;
- try {
- tetherStatsVec = mNetdService.tetherGetStats();
- } catch (RemoteException | ServiceSpecificException e) {
- throw new IllegalStateException("problem parsing tethering stats: ", e);
- }
-
- final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(),
- tetherStatsVec.length);
- final NetworkStats.Entry entry = new NetworkStats.Entry();
-
- for (TetherStatsParcel tetherStats : tetherStatsVec) {
- try {
- entry.iface = tetherStats.iface;
- entry.uid = UID_TETHERING;
- entry.set = SET_DEFAULT;
- entry.tag = TAG_NONE;
- entry.rxBytes = tetherStats.rxBytes;
- entry.rxPackets = tetherStats.rxPackets;
- entry.txBytes = tetherStats.txBytes;
- entry.txPackets = tetherStats.txPackets;
- stats.combineValues(entry);
- } catch (ArrayIndexOutOfBoundsException e) {
- throw new IllegalStateException("invalid tethering stats " + e);
- }
- }
-
- return stats;
+ // Remove the implementation of NetdTetheringStatsProvider#getTetherStats
+ // since all callers are migrated to use INetd#tetherGetStats directly.
+ throw new UnsupportedOperationException();
}
@Override
@@ -1347,20 +1303,9 @@ public class NetworkManagementService extends INetworkManagementService.Stub {
@Override
public NetworkStats getNetworkStatsTethering(int how) {
- NetworkStack.checkNetworkStackPermission(mContext);
-
- final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 1);
- synchronized (mTetheringStatsProviders) {
- for (ITetheringStatsProvider provider: mTetheringStatsProviders.keySet()) {
- try {
- stats.combineAllValues(provider.getTetherStats(how));
- } catch (RemoteException e) {
- Log.e(TAG, "Problem reading tethering stats from " +
- mTetheringStatsProviders.get(provider) + ": " + e);
- }
- }
- }
- return stats;
+ // Remove the implementation of getNetworkStatsTethering since all callers are migrated
+ // to use INetd#tetherGetStats directly.
+ throw new UnsupportedOperationException();
}
@Override
@@ -1475,9 +1420,10 @@ public class NetworkManagementService extends INetworkManagementService.Stub {
throw new IllegalArgumentException("Bad child chain: " + chainName);
}
+ final ConnectivityManager cm = mContext.getSystemService(ConnectivityManager.class);
try {
- mNetdService.firewallEnableChildChain(chain, enable);
- } catch (RemoteException | ServiceSpecificException e) {
+ cm.setFirewallChainEnabled(chain, enable);
+ } catch (RuntimeException e) {
throw new IllegalStateException(e);
}
@@ -1501,6 +1447,8 @@ public class NetworkManagementService extends INetworkManagementService.Stub {
return FIREWALL_CHAIN_NAME_POWERSAVE;
case FIREWALL_CHAIN_RESTRICTED:
return FIREWALL_CHAIN_NAME_RESTRICTED;
+ case FIREWALL_CHAIN_LOW_POWER_STANDBY:
+ return FIREWALL_CHAIN_NAME_LOW_POWER_STANDBY;
default:
throw new IllegalArgumentException("Bad child chain: " + chain);
}
@@ -1516,6 +1464,8 @@ public class NetworkManagementService extends INetworkManagementService.Stub {
return FIREWALL_ALLOWLIST;
case FIREWALL_CHAIN_RESTRICTED:
return FIREWALL_ALLOWLIST;
+ case FIREWALL_CHAIN_LOW_POWER_STANDBY:
+ return FIREWALL_ALLOWLIST;
default:
return isFirewallEnabled() ? FIREWALL_ALLOWLIST : FIREWALL_DENYLIST;
}
@@ -1549,25 +1499,10 @@ public class NetworkManagementService extends INetworkManagementService.Stub {
updateFirewallUidRuleLocked(chain, uid, FIREWALL_RULE_DEFAULT);
}
}
+ final ConnectivityManager cm = mContext.getSystemService(ConnectivityManager.class);
try {
- switch (chain) {
- case FIREWALL_CHAIN_DOZABLE:
- mNetdService.firewallReplaceUidChain("fw_dozable", true, uids);
- break;
- case FIREWALL_CHAIN_STANDBY:
- mNetdService.firewallReplaceUidChain("fw_standby", false, uids);
- break;
- case FIREWALL_CHAIN_POWERSAVE:
- mNetdService.firewallReplaceUidChain("fw_powersave", true, uids);
- break;
- case FIREWALL_CHAIN_RESTRICTED:
- mNetdService.firewallReplaceUidChain("fw_restricted", true, uids);
- break;
- case FIREWALL_CHAIN_NONE:
- default:
- Slog.d(TAG, "setFirewallUidRules() called on invalid chain: " + chain);
- }
- } catch (RemoteException e) {
+ cm.replaceFirewallChain(chain, uids);
+ } catch (RuntimeException e) {
Slog.w(TAG, "Error flushing firewall chain " + chain, e);
}
}
@@ -1583,10 +1518,10 @@ public class NetworkManagementService extends INetworkManagementService.Stub {
private void setFirewallUidRuleLocked(int chain, int uid, int rule) {
if (updateFirewallUidRuleLocked(chain, uid, rule)) {
- final int ruleType = getFirewallRuleType(chain, rule);
+ final ConnectivityManager cm = mContext.getSystemService(ConnectivityManager.class);
try {
- mNetdService.firewallSetUidRule(chain, uid, ruleType);
- } catch (RemoteException | ServiceSpecificException e) {
+ cm.updateFirewallRule(chain, uid, isFirewallRuleAllow(chain, rule));
+ } catch (RuntimeException e) {
throw new IllegalStateException(e);
}
}
@@ -1649,6 +1584,8 @@ public class NetworkManagementService extends INetworkManagementService.Stub {
return mUidFirewallPowerSaveRules;
case FIREWALL_CHAIN_RESTRICTED:
return mUidFirewallRestrictedRules;
+ case FIREWALL_CHAIN_LOW_POWER_STANDBY:
+ return mUidFirewallLowPowerStandbyRules;
case FIREWALL_CHAIN_NONE:
return mUidFirewallRules;
default:
@@ -1656,12 +1593,12 @@ public class NetworkManagementService extends INetworkManagementService.Stub {
}
}
- private int getFirewallRuleType(int chain, int rule) {
+ // There are only two type of firewall rule: FIREWALL_RULE_ALLOW or FIREWALL_RULE_DENY.
+ private boolean isFirewallRuleAllow(int chain, int rule) {
if (rule == NetworkPolicyManager.FIREWALL_RULE_DEFAULT) {
- return getFirewallType(chain) == FIREWALL_ALLOWLIST
- ? INetd.FIREWALL_RULE_DENY : INetd.FIREWALL_RULE_ALLOW;
+ return getFirewallType(chain) == FIREWALL_DENYLIST;
}
- return rule;
+ return rule == INetd.FIREWALL_RULE_ALLOW;
}
private void enforceSystemUid() {
@@ -1704,6 +1641,11 @@ public class NetworkManagementService extends INetworkManagementService.Stub {
pw.println(getFirewallChainState(FIREWALL_CHAIN_RESTRICTED));
dumpUidFirewallRule(pw, FIREWALL_CHAIN_NAME_RESTRICTED,
mUidFirewallRestrictedRules);
+
+ pw.print("UID firewall low power standby chain enabled: ");
+ pw.println(getFirewallChainState(FIREWALL_CHAIN_LOW_POWER_STANDBY));
+ dumpUidFirewallRule(pw, FIREWALL_CHAIN_NAME_LOW_POWER_STANDBY,
+ mUidFirewallLowPowerStandbyRules);
}
pw.print("Firewall enabled: "); pw.println(mFirewallEnabled);
@@ -1785,7 +1727,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub {
public void addInterfaceToLocalNetwork(String iface, List<RouteInfo> routes) {
modifyInterfaceInNetwork(MODIFY_OPERATION_ADD, INetd.LOCAL_NET_ID, iface);
// modifyInterfaceInNetwork already check calling permission.
- RouteUtils.addRoutesToLocalNetwork(mNetdService, iface, routes);
+ NetdUtils.addRoutesToLocalNetwork(mNetdService, iface, routes);
}
@Override
@@ -1796,7 +1738,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub {
@Override
public int removeRoutesFromLocalNetwork(List<RouteInfo> routes) {
NetworkStack.checkNetworkStackPermission(mContext);
- return RouteUtils.removeRoutesFromLocalNetwork(mNetdService, routes);
+ return NetdUtils.removeRoutesFromLocalNetwork(mNetdService, routes);
}
@Override
@@ -1827,6 +1769,11 @@ public class NetworkManagementService extends INetworkManagementService.Stub {
if (DBG) Slog.d(TAG, "Uid " + uid + " restricted because of restricted mode");
return true;
}
+ if (getFirewallChainState(FIREWALL_CHAIN_LOW_POWER_STANDBY)
+ && mUidFirewallLowPowerStandbyRules.get(uid) != FIREWALL_RULE_ALLOW) {
+ if (DBG) Slog.d(TAG, "Uid " + uid + " restricted because of low power standby");
+ return true;
+ }
if (mUidRejectOnMetered.get(uid)) {
if (DBG) Slog.d(TAG, "Uid " + uid + " restricted because of no metered data"
+ " in the background");
diff --git a/services/core/java/com/android/server/NsdService.java b/services/core/java/com/android/server/NsdService.java
deleted file mode 100644
index c9608a55170e..000000000000
--- a/services/core/java/com/android/server/NsdService.java
+++ /dev/null
@@ -1,984 +0,0 @@
-/*
- * Copyright (C) 2010 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;
-
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.database.ContentObserver;
-import android.net.NetworkStack;
-import android.net.Uri;
-import android.net.nsd.INsdManager;
-import android.net.nsd.NsdManager;
-import android.net.nsd.NsdServiceInfo;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Message;
-import android.os.Messenger;
-import android.os.UserHandle;
-import android.provider.Settings;
-import android.util.Base64;
-import android.util.Slog;
-import android.util.SparseArray;
-import android.util.SparseIntArray;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.AsyncChannel;
-import com.android.internal.util.DumpUtils;
-import com.android.internal.util.State;
-import com.android.internal.util.StateMachine;
-import com.android.net.module.util.DnsSdTxtRecord;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.net.InetAddress;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.concurrent.CountDownLatch;
-
-/**
- * Network Service Discovery Service handles remote service discovery operation requests by
- * implementing the INsdManager interface.
- *
- * @hide
- */
-public class NsdService extends INsdManager.Stub {
- private static final String TAG = "NsdService";
- private static final String MDNS_TAG = "mDnsConnector";
-
- private static final boolean DBG = true;
- private static final long CLEANUP_DELAY_MS = 10000;
-
- private final Context mContext;
- private final NsdSettings mNsdSettings;
- private final NsdStateMachine mNsdStateMachine;
- private final DaemonConnection mDaemon;
- private final NativeCallbackReceiver mDaemonCallback;
-
- /**
- * Clients receiving asynchronous messages
- */
- private final HashMap<Messenger, ClientInfo> mClients = new HashMap<>();
-
- /* A map from unique id to client info */
- private final SparseArray<ClientInfo> mIdToClientInfoMap= new SparseArray<>();
-
- private final AsyncChannel mReplyChannel = new AsyncChannel();
- private final long mCleanupDelayMs;
-
- private static final int INVALID_ID = 0;
- private int mUniqueId = 1;
- // The count of the connected legacy clients.
- private int mLegacyClientCount = 0;
-
- private class NsdStateMachine extends StateMachine {
-
- private final DefaultState mDefaultState = new DefaultState();
- private final DisabledState mDisabledState = new DisabledState();
- private final EnabledState mEnabledState = new EnabledState();
-
- @Override
- protected String getWhatToString(int what) {
- return NsdManager.nameOf(what);
- }
-
- private void maybeStartDaemon() {
- mDaemon.maybeStart();
- maybeScheduleStop();
- }
-
- private boolean isAnyRequestActive() {
- return mIdToClientInfoMap.size() != 0;
- }
-
- private void scheduleStop() {
- sendMessageDelayed(NsdManager.DAEMON_CLEANUP, mCleanupDelayMs);
- }
- private void maybeScheduleStop() {
- // The native daemon should stay alive and can't be cleanup
- // if any legacy client connected.
- if (!isAnyRequestActive() && mLegacyClientCount == 0) {
- scheduleStop();
- }
- }
-
- private void cancelStop() {
- this.removeMessages(NsdManager.DAEMON_CLEANUP);
- }
-
- /**
- * Observes the NSD on/off setting, and takes action when changed.
- */
- private void registerForNsdSetting() {
- final ContentObserver contentObserver = new ContentObserver(this.getHandler()) {
- @Override
- public void onChange(boolean selfChange) {
- notifyEnabled(isNsdEnabled());
- }
- };
-
- final Uri uri = Settings.Global.getUriFor(Settings.Global.NSD_ON);
- mNsdSettings.registerContentObserver(uri, contentObserver);
- }
-
- NsdStateMachine(String name, Handler handler) {
- super(name, handler);
- addState(mDefaultState);
- addState(mDisabledState, mDefaultState);
- addState(mEnabledState, mDefaultState);
- State initialState = isNsdEnabled() ? mEnabledState : mDisabledState;
- setInitialState(initialState);
- setLogRecSize(25);
- registerForNsdSetting();
- }
-
- class DefaultState extends State {
- @Override
- public boolean processMessage(Message msg) {
- ClientInfo cInfo = null;
- switch (msg.what) {
- case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
- if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
- AsyncChannel c = (AsyncChannel) msg.obj;
- if (DBG) Slog.d(TAG, "New client listening to asynchronous messages");
- c.sendMessage(AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED);
- cInfo = new ClientInfo(c, msg.replyTo);
- mClients.put(msg.replyTo, cInfo);
- } else {
- Slog.e(TAG, "Client connection failure, error=" + msg.arg1);
- }
- break;
- case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
- switch (msg.arg1) {
- case AsyncChannel.STATUS_SEND_UNSUCCESSFUL:
- Slog.e(TAG, "Send failed, client connection lost");
- break;
- case AsyncChannel.STATUS_REMOTE_DISCONNECTION:
- if (DBG) Slog.d(TAG, "Client disconnected");
- break;
- default:
- if (DBG) Slog.d(TAG, "Client connection lost with reason: " + msg.arg1);
- break;
- }
-
- cInfo = mClients.get(msg.replyTo);
- if (cInfo != null) {
- cInfo.expungeAllRequests();
- mClients.remove(msg.replyTo);
- if (cInfo.isLegacy()) {
- mLegacyClientCount -= 1;
- }
- }
- maybeScheduleStop();
- break;
- case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION:
- AsyncChannel ac = new AsyncChannel();
- ac.connect(mContext, getHandler(), msg.replyTo);
- break;
- case NsdManager.DISCOVER_SERVICES:
- replyToMessage(msg, NsdManager.DISCOVER_SERVICES_FAILED,
- NsdManager.FAILURE_INTERNAL_ERROR);
- break;
- case NsdManager.STOP_DISCOVERY:
- replyToMessage(msg, NsdManager.STOP_DISCOVERY_FAILED,
- NsdManager.FAILURE_INTERNAL_ERROR);
- break;
- case NsdManager.REGISTER_SERVICE:
- replyToMessage(msg, NsdManager.REGISTER_SERVICE_FAILED,
- NsdManager.FAILURE_INTERNAL_ERROR);
- break;
- case NsdManager.UNREGISTER_SERVICE:
- replyToMessage(msg, NsdManager.UNREGISTER_SERVICE_FAILED,
- NsdManager.FAILURE_INTERNAL_ERROR);
- break;
- case NsdManager.RESOLVE_SERVICE:
- replyToMessage(msg, NsdManager.RESOLVE_SERVICE_FAILED,
- NsdManager.FAILURE_INTERNAL_ERROR);
- break;
- case NsdManager.DAEMON_CLEANUP:
- mDaemon.maybeStop();
- break;
- // This event should be only sent by the legacy (target SDK < S) clients.
- // Mark the sending client as legacy.
- case NsdManager.DAEMON_STARTUP:
- cInfo = mClients.get(msg.replyTo);
- if (cInfo != null) {
- cancelStop();
- cInfo.setLegacy();
- mLegacyClientCount += 1;
- maybeStartDaemon();
- }
- break;
- case NsdManager.NATIVE_DAEMON_EVENT:
- default:
- Slog.e(TAG, "Unhandled " + msg);
- return NOT_HANDLED;
- }
- return HANDLED;
- }
- }
-
- class DisabledState extends State {
- @Override
- public void enter() {
- sendNsdStateChangeBroadcast(false);
- }
-
- @Override
- public boolean processMessage(Message msg) {
- switch (msg.what) {
- case NsdManager.ENABLE:
- transitionTo(mEnabledState);
- break;
- default:
- return NOT_HANDLED;
- }
- return HANDLED;
- }
- }
-
- class EnabledState extends State {
- @Override
- public void enter() {
- sendNsdStateChangeBroadcast(true);
- }
-
- @Override
- public void exit() {
- // TODO: it is incorrect to stop the daemon without expunging all requests
- // and sending error callbacks to clients.
- scheduleStop();
- }
-
- private boolean requestLimitReached(ClientInfo clientInfo) {
- if (clientInfo.mClientIds.size() >= ClientInfo.MAX_LIMIT) {
- if (DBG) Slog.d(TAG, "Exceeded max outstanding requests " + clientInfo);
- return true;
- }
- return false;
- }
-
- private void storeRequestMap(int clientId, int globalId, ClientInfo clientInfo, int what) {
- clientInfo.mClientIds.put(clientId, globalId);
- clientInfo.mClientRequests.put(clientId, what);
- mIdToClientInfoMap.put(globalId, clientInfo);
- // Remove the cleanup event because here comes a new request.
- cancelStop();
- }
-
- private void removeRequestMap(int clientId, int globalId, ClientInfo clientInfo) {
- clientInfo.mClientIds.delete(clientId);
- clientInfo.mClientRequests.delete(clientId);
- mIdToClientInfoMap.remove(globalId);
- maybeScheduleStop();
- }
-
- @Override
- public boolean processMessage(Message msg) {
- ClientInfo clientInfo;
- NsdServiceInfo servInfo;
- int id;
- switch (msg.what) {
- case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
- return NOT_HANDLED;
- case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
- return NOT_HANDLED;
- case NsdManager.DISABLE:
- //TODO: cleanup clients
- transitionTo(mDisabledState);
- break;
- case NsdManager.DISCOVER_SERVICES:
- if (DBG) Slog.d(TAG, "Discover services");
- servInfo = (NsdServiceInfo) msg.obj;
- clientInfo = mClients.get(msg.replyTo);
-
- if (requestLimitReached(clientInfo)) {
- replyToMessage(msg, NsdManager.DISCOVER_SERVICES_FAILED,
- NsdManager.FAILURE_MAX_LIMIT);
- break;
- }
-
- maybeStartDaemon();
- id = getUniqueId();
- if (discoverServices(id, servInfo.getServiceType())) {
- if (DBG) {
- Slog.d(TAG, "Discover " + msg.arg2 + " " + id +
- servInfo.getServiceType());
- }
- storeRequestMap(msg.arg2, id, clientInfo, msg.what);
- replyToMessage(msg, NsdManager.DISCOVER_SERVICES_STARTED, servInfo);
- } else {
- stopServiceDiscovery(id);
- replyToMessage(msg, NsdManager.DISCOVER_SERVICES_FAILED,
- NsdManager.FAILURE_INTERNAL_ERROR);
- }
- break;
- case NsdManager.STOP_DISCOVERY:
- if (DBG) Slog.d(TAG, "Stop service discovery");
- clientInfo = mClients.get(msg.replyTo);
-
- try {
- id = clientInfo.mClientIds.get(msg.arg2);
- } catch (NullPointerException e) {
- replyToMessage(msg, NsdManager.STOP_DISCOVERY_FAILED,
- NsdManager.FAILURE_INTERNAL_ERROR);
- break;
- }
- removeRequestMap(msg.arg2, id, clientInfo);
- if (stopServiceDiscovery(id)) {
- replyToMessage(msg, NsdManager.STOP_DISCOVERY_SUCCEEDED);
- } else {
- replyToMessage(msg, NsdManager.STOP_DISCOVERY_FAILED,
- NsdManager.FAILURE_INTERNAL_ERROR);
- }
- break;
- case NsdManager.REGISTER_SERVICE:
- if (DBG) Slog.d(TAG, "Register service");
- clientInfo = mClients.get(msg.replyTo);
- if (requestLimitReached(clientInfo)) {
- replyToMessage(msg, NsdManager.REGISTER_SERVICE_FAILED,
- NsdManager.FAILURE_MAX_LIMIT);
- break;
- }
-
- maybeStartDaemon();
- id = getUniqueId();
- if (registerService(id, (NsdServiceInfo) msg.obj)) {
- if (DBG) Slog.d(TAG, "Register " + msg.arg2 + " " + id);
- storeRequestMap(msg.arg2, id, clientInfo, msg.what);
- // Return success after mDns reports success
- } else {
- unregisterService(id);
- replyToMessage(msg, NsdManager.REGISTER_SERVICE_FAILED,
- NsdManager.FAILURE_INTERNAL_ERROR);
- }
- break;
- case NsdManager.UNREGISTER_SERVICE:
- if (DBG) Slog.d(TAG, "unregister service");
- clientInfo = mClients.get(msg.replyTo);
- try {
- id = clientInfo.mClientIds.get(msg.arg2);
- } catch (NullPointerException e) {
- replyToMessage(msg, NsdManager.UNREGISTER_SERVICE_FAILED,
- NsdManager.FAILURE_INTERNAL_ERROR);
- break;
- }
- removeRequestMap(msg.arg2, id, clientInfo);
- if (unregisterService(id)) {
- replyToMessage(msg, NsdManager.UNREGISTER_SERVICE_SUCCEEDED);
- } else {
- replyToMessage(msg, NsdManager.UNREGISTER_SERVICE_FAILED,
- NsdManager.FAILURE_INTERNAL_ERROR);
- }
- break;
- case NsdManager.RESOLVE_SERVICE:
- if (DBG) Slog.d(TAG, "Resolve service");
- servInfo = (NsdServiceInfo) msg.obj;
- clientInfo = mClients.get(msg.replyTo);
-
-
- if (clientInfo.mResolvedService != null) {
- replyToMessage(msg, NsdManager.RESOLVE_SERVICE_FAILED,
- NsdManager.FAILURE_ALREADY_ACTIVE);
- break;
- }
-
- maybeStartDaemon();
- id = getUniqueId();
- if (resolveService(id, servInfo)) {
- clientInfo.mResolvedService = new NsdServiceInfo();
- storeRequestMap(msg.arg2, id, clientInfo, msg.what);
- } else {
- replyToMessage(msg, NsdManager.RESOLVE_SERVICE_FAILED,
- NsdManager.FAILURE_INTERNAL_ERROR);
- }
- break;
- case NsdManager.NATIVE_DAEMON_EVENT:
- NativeEvent event = (NativeEvent) msg.obj;
- if (!handleNativeEvent(event.code, event.raw, event.cooked)) {
- return NOT_HANDLED;
- }
- break;
- default:
- return NOT_HANDLED;
- }
- return HANDLED;
- }
-
- private boolean handleNativeEvent(int code, String raw, String[] cooked) {
- NsdServiceInfo servInfo;
- int id = Integer.parseInt(cooked[1]);
- ClientInfo clientInfo = mIdToClientInfoMap.get(id);
- if (clientInfo == null) {
- String name = NativeResponseCode.nameOf(code);
- Slog.e(TAG, String.format("id %d for %s has no client mapping", id, name));
- return false;
- }
-
- /* This goes in response as msg.arg2 */
- int clientId = clientInfo.getClientId(id);
- if (clientId < 0) {
- // This can happen because of race conditions. For example,
- // SERVICE_FOUND may race with STOP_SERVICE_DISCOVERY,
- // and we may get in this situation.
- String name = NativeResponseCode.nameOf(code);
- Slog.d(TAG, String.format(
- "Notification %s for listener id %d that is no longer active",
- name, id));
- return false;
- }
- if (DBG) {
- String name = NativeResponseCode.nameOf(code);
- Slog.d(TAG, String.format("Native daemon message %s: %s", name, raw));
- }
- switch (code) {
- case NativeResponseCode.SERVICE_FOUND:
- /* NNN uniqueId serviceName regType domain */
- servInfo = new NsdServiceInfo(cooked[2], cooked[3]);
- clientInfo.mChannel.sendMessage(NsdManager.SERVICE_FOUND, 0,
- clientId, servInfo);
- break;
- case NativeResponseCode.SERVICE_LOST:
- /* NNN uniqueId serviceName regType domain */
- servInfo = new NsdServiceInfo(cooked[2], cooked[3]);
- clientInfo.mChannel.sendMessage(NsdManager.SERVICE_LOST, 0,
- clientId, servInfo);
- break;
- case NativeResponseCode.SERVICE_DISCOVERY_FAILED:
- /* NNN uniqueId errorCode */
- clientInfo.mChannel.sendMessage(NsdManager.DISCOVER_SERVICES_FAILED,
- NsdManager.FAILURE_INTERNAL_ERROR, clientId);
- break;
- case NativeResponseCode.SERVICE_REGISTERED:
- /* NNN regId serviceName regType */
- servInfo = new NsdServiceInfo(cooked[2], null);
- clientInfo.mChannel.sendMessage(NsdManager.REGISTER_SERVICE_SUCCEEDED,
- id, clientId, servInfo);
- break;
- case NativeResponseCode.SERVICE_REGISTRATION_FAILED:
- /* NNN regId errorCode */
- clientInfo.mChannel.sendMessage(NsdManager.REGISTER_SERVICE_FAILED,
- NsdManager.FAILURE_INTERNAL_ERROR, clientId);
- break;
- case NativeResponseCode.SERVICE_UPDATED:
- /* NNN regId */
- break;
- case NativeResponseCode.SERVICE_UPDATE_FAILED:
- /* NNN regId errorCode */
- break;
- case NativeResponseCode.SERVICE_RESOLVED:
- /* NNN resolveId fullName hostName port txtlen txtdata */
- int index = 0;
- while (index < cooked[2].length() && cooked[2].charAt(index) != '.') {
- if (cooked[2].charAt(index) == '\\') {
- ++index;
- }
- ++index;
- }
- if (index >= cooked[2].length()) {
- Slog.e(TAG, "Invalid service found " + raw);
- break;
- }
- String name = cooked[2].substring(0, index);
- String rest = cooked[2].substring(index);
- String type = rest.replace(".local.", "");
-
- name = unescape(name);
-
- clientInfo.mResolvedService.setServiceName(name);
- clientInfo.mResolvedService.setServiceType(type);
- clientInfo.mResolvedService.setPort(Integer.parseInt(cooked[4]));
- clientInfo.mResolvedService.setTxtRecords(cooked[6]);
-
- stopResolveService(id);
- removeRequestMap(clientId, id, clientInfo);
-
- int id2 = getUniqueId();
- if (getAddrInfo(id2, cooked[3])) {
- storeRequestMap(clientId, id2, clientInfo, NsdManager.RESOLVE_SERVICE);
- } else {
- clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_FAILED,
- NsdManager.FAILURE_INTERNAL_ERROR, clientId);
- clientInfo.mResolvedService = null;
- }
- break;
- case NativeResponseCode.SERVICE_RESOLUTION_FAILED:
- /* NNN resolveId errorCode */
- stopResolveService(id);
- removeRequestMap(clientId, id, clientInfo);
- clientInfo.mResolvedService = null;
- clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_FAILED,
- NsdManager.FAILURE_INTERNAL_ERROR, clientId);
- break;
- case NativeResponseCode.SERVICE_GET_ADDR_FAILED:
- /* NNN resolveId errorCode */
- stopGetAddrInfo(id);
- removeRequestMap(clientId, id, clientInfo);
- clientInfo.mResolvedService = null;
- clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_FAILED,
- NsdManager.FAILURE_INTERNAL_ERROR, clientId);
- break;
- case NativeResponseCode.SERVICE_GET_ADDR_SUCCESS:
- /* NNN resolveId hostname ttl addr */
- try {
- clientInfo.mResolvedService.setHost(InetAddress.getByName(cooked[4]));
- clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_SUCCEEDED,
- 0, clientId, clientInfo.mResolvedService);
- } catch (java.net.UnknownHostException e) {
- clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_FAILED,
- NsdManager.FAILURE_INTERNAL_ERROR, clientId);
- }
- stopGetAddrInfo(id);
- removeRequestMap(clientId, id, clientInfo);
- clientInfo.mResolvedService = null;
- break;
- default:
- return false;
- }
- return true;
- }
- }
- }
-
- private String unescape(String s) {
- StringBuilder sb = new StringBuilder(s.length());
- for (int i = 0; i < s.length(); ++i) {
- char c = s.charAt(i);
- if (c == '\\') {
- if (++i >= s.length()) {
- Slog.e(TAG, "Unexpected end of escape sequence in: " + s);
- break;
- }
- c = s.charAt(i);
- if (c != '.' && c != '\\') {
- if (i + 2 >= s.length()) {
- Slog.e(TAG, "Unexpected end of escape sequence in: " + s);
- break;
- }
- c = (char) ((c-'0') * 100 + (s.charAt(i+1)-'0') * 10 + (s.charAt(i+2)-'0'));
- i += 2;
- }
- }
- sb.append(c);
- }
- return sb.toString();
- }
-
- @VisibleForTesting
- NsdService(Context ctx, NsdSettings settings, Handler handler,
- DaemonConnectionSupplier fn, long cleanupDelayMs) {
- mCleanupDelayMs = cleanupDelayMs;
- mContext = ctx;
- mNsdSettings = settings;
- mNsdStateMachine = new NsdStateMachine(TAG, handler);
- mNsdStateMachine.start();
- mDaemonCallback = new NativeCallbackReceiver();
- mDaemon = fn.get(mDaemonCallback);
- }
-
- public static NsdService create(Context context) throws InterruptedException {
- NsdSettings settings = NsdSettings.makeDefault(context);
- HandlerThread thread = new HandlerThread(TAG);
- thread.start();
- Handler handler = new Handler(thread.getLooper());
- NsdService service = new NsdService(context, settings, handler,
- DaemonConnection::new, CLEANUP_DELAY_MS);
- service.mDaemonCallback.awaitConnection();
- return service;
- }
-
- public Messenger getMessenger() {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INTERNET, "NsdService");
- return new Messenger(mNsdStateMachine.getHandler());
- }
-
- public void setEnabled(boolean isEnabled) {
- NetworkStack.checkNetworkStackPermission(mContext);
- mNsdSettings.putEnabledStatus(isEnabled);
- notifyEnabled(isEnabled);
- }
-
- private void notifyEnabled(boolean isEnabled) {
- mNsdStateMachine.sendMessage(isEnabled ? NsdManager.ENABLE : NsdManager.DISABLE);
- }
-
- private void sendNsdStateChangeBroadcast(boolean isEnabled) {
- final Intent intent = new Intent(NsdManager.ACTION_NSD_STATE_CHANGED);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
- int nsdState = isEnabled ? NsdManager.NSD_STATE_ENABLED : NsdManager.NSD_STATE_DISABLED;
- intent.putExtra(NsdManager.EXTRA_NSD_STATE, nsdState);
- mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
- }
-
- private boolean isNsdEnabled() {
- boolean ret = mNsdSettings.isEnabled();
- if (DBG) {
- Slog.d(TAG, "Network service discovery is " + (ret ? "enabled" : "disabled"));
- }
- return ret;
- }
-
- private int getUniqueId() {
- if (++mUniqueId == INVALID_ID) return ++mUniqueId;
- return mUniqueId;
- }
-
- /* These should be in sync with system/netd/server/ResponseCode.h */
- static final class NativeResponseCode {
- public static final int SERVICE_DISCOVERY_FAILED = 602;
- public static final int SERVICE_FOUND = 603;
- public static final int SERVICE_LOST = 604;
-
- public static final int SERVICE_REGISTRATION_FAILED = 605;
- public static final int SERVICE_REGISTERED = 606;
-
- public static final int SERVICE_RESOLUTION_FAILED = 607;
- public static final int SERVICE_RESOLVED = 608;
-
- public static final int SERVICE_UPDATED = 609;
- public static final int SERVICE_UPDATE_FAILED = 610;
-
- public static final int SERVICE_GET_ADDR_FAILED = 611;
- public static final int SERVICE_GET_ADDR_SUCCESS = 612;
-
- private static final SparseArray<String> CODE_NAMES = new SparseArray<>();
- static {
- CODE_NAMES.put(SERVICE_DISCOVERY_FAILED, "SERVICE_DISCOVERY_FAILED");
- CODE_NAMES.put(SERVICE_FOUND, "SERVICE_FOUND");
- CODE_NAMES.put(SERVICE_LOST, "SERVICE_LOST");
- CODE_NAMES.put(SERVICE_REGISTRATION_FAILED, "SERVICE_REGISTRATION_FAILED");
- CODE_NAMES.put(SERVICE_REGISTERED, "SERVICE_REGISTERED");
- CODE_NAMES.put(SERVICE_RESOLUTION_FAILED, "SERVICE_RESOLUTION_FAILED");
- CODE_NAMES.put(SERVICE_RESOLVED, "SERVICE_RESOLVED");
- CODE_NAMES.put(SERVICE_UPDATED, "SERVICE_UPDATED");
- CODE_NAMES.put(SERVICE_UPDATE_FAILED, "SERVICE_UPDATE_FAILED");
- CODE_NAMES.put(SERVICE_GET_ADDR_FAILED, "SERVICE_GET_ADDR_FAILED");
- CODE_NAMES.put(SERVICE_GET_ADDR_SUCCESS, "SERVICE_GET_ADDR_SUCCESS");
- }
-
- static String nameOf(int code) {
- String name = CODE_NAMES.get(code);
- if (name == null) {
- return Integer.toString(code);
- }
- return name;
- }
- }
-
- private class NativeEvent {
- final int code;
- final String raw;
- final String[] cooked;
-
- NativeEvent(int code, String raw, String[] cooked) {
- this.code = code;
- this.raw = raw;
- this.cooked = cooked;
- }
- }
-
- class NativeCallbackReceiver implements INativeDaemonConnectorCallbacks {
- private final CountDownLatch connected = new CountDownLatch(1);
-
- public void awaitConnection() throws InterruptedException {
- connected.await();
- }
-
- @Override
- public void onDaemonConnected() {
- connected.countDown();
- }
-
- @Override
- public boolean onCheckHoldWakeLock(int code) {
- return false;
- }
-
- @Override
- public boolean onEvent(int code, String raw, String[] cooked) {
- // TODO: NDC translates a message to a callback, we could enhance NDC to
- // directly interact with a state machine through messages
- NativeEvent event = new NativeEvent(code, raw, cooked);
- mNsdStateMachine.sendMessage(NsdManager.NATIVE_DAEMON_EVENT, event);
- return true;
- }
- }
-
- interface DaemonConnectionSupplier {
- DaemonConnection get(NativeCallbackReceiver callback);
- }
-
- @VisibleForTesting
- public static class DaemonConnection {
- final NativeDaemonConnector mNativeConnector;
- boolean mIsStarted = false;
-
- DaemonConnection(NativeCallbackReceiver callback) {
- mNativeConnector = new NativeDaemonConnector(callback, "mdns", 10, MDNS_TAG, 25, null);
- new Thread(mNativeConnector, MDNS_TAG).start();
- }
-
- /**
- * Executes the specified cmd on the daemon.
- */
- public boolean execute(Object... args) {
- if (DBG) {
- Slog.d(TAG, "mdnssd " + Arrays.toString(args));
- }
- try {
- mNativeConnector.execute("mdnssd", args);
- } catch (NativeDaemonConnectorException e) {
- Slog.e(TAG, "Failed to execute mdnssd " + Arrays.toString(args), e);
- return false;
- }
- return true;
- }
-
- /**
- * Starts the daemon if it is not already started.
- */
- public void maybeStart() {
- if (mIsStarted) {
- return;
- }
- execute("start-service");
- mIsStarted = true;
- }
-
- /**
- * Stops the daemon if it is started.
- */
- public void maybeStop() {
- if (!mIsStarted) {
- return;
- }
- execute("stop-service");
- mIsStarted = false;
- }
- }
-
- private boolean registerService(int regId, NsdServiceInfo service) {
- if (DBG) {
- Slog.d(TAG, "registerService: " + regId + " " + service);
- }
- String name = service.getServiceName();
- String type = service.getServiceType();
- int port = service.getPort();
- byte[] textRecord = service.getTxtRecord();
- String record = Base64.encodeToString(textRecord, Base64.DEFAULT).replace("\n", "");
- return mDaemon.execute("register", regId, name, type, port, record);
- }
-
- private boolean unregisterService(int regId) {
- return mDaemon.execute("stop-register", regId);
- }
-
- private boolean updateService(int regId, DnsSdTxtRecord t) {
- if (t == null) {
- return false;
- }
- return mDaemon.execute("update", regId, t.size(), t.getRawData());
- }
-
- private boolean discoverServices(int discoveryId, String serviceType) {
- return mDaemon.execute("discover", discoveryId, serviceType);
- }
-
- private boolean stopServiceDiscovery(int discoveryId) {
- return mDaemon.execute("stop-discover", discoveryId);
- }
-
- private boolean resolveService(int resolveId, NsdServiceInfo service) {
- String name = service.getServiceName();
- String type = service.getServiceType();
- return mDaemon.execute("resolve", resolveId, name, type, "local.");
- }
-
- private boolean stopResolveService(int resolveId) {
- return mDaemon.execute("stop-resolve", resolveId);
- }
-
- private boolean getAddrInfo(int resolveId, String hostname) {
- return mDaemon.execute("getaddrinfo", resolveId, hostname);
- }
-
- private boolean stopGetAddrInfo(int resolveId) {
- return mDaemon.execute("stop-getaddrinfo", resolveId);
- }
-
- @Override
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
-
- for (ClientInfo client : mClients.values()) {
- pw.println("Client Info");
- pw.println(client);
- }
-
- mNsdStateMachine.dump(fd, pw, args);
- }
-
- /* arg2 on the source message has an id that needs to be retained in replies
- * see NsdManager for details */
- private Message obtainMessage(Message srcMsg) {
- Message msg = Message.obtain();
- msg.arg2 = srcMsg.arg2;
- return msg;
- }
-
- private void replyToMessage(Message msg, int what) {
- if (msg.replyTo == null) return;
- Message dstMsg = obtainMessage(msg);
- dstMsg.what = what;
- mReplyChannel.replyToMessage(msg, dstMsg);
- }
-
- private void replyToMessage(Message msg, int what, int arg1) {
- if (msg.replyTo == null) return;
- Message dstMsg = obtainMessage(msg);
- dstMsg.what = what;
- dstMsg.arg1 = arg1;
- mReplyChannel.replyToMessage(msg, dstMsg);
- }
-
- private void replyToMessage(Message msg, int what, Object obj) {
- if (msg.replyTo == null) return;
- Message dstMsg = obtainMessage(msg);
- dstMsg.what = what;
- dstMsg.obj = obj;
- mReplyChannel.replyToMessage(msg, dstMsg);
- }
-
- /* Information tracked per client */
- private class ClientInfo {
-
- private static final int MAX_LIMIT = 10;
- private final AsyncChannel mChannel;
- private final Messenger mMessenger;
- /* Remembers a resolved service until getaddrinfo completes */
- private NsdServiceInfo mResolvedService;
-
- /* A map from client id to unique id sent to mDns */
- private final SparseIntArray mClientIds = new SparseIntArray();
-
- /* A map from client id to the type of the request we had received */
- private final SparseIntArray mClientRequests = new SparseIntArray();
-
- // The target SDK of this client < Build.VERSION_CODES.S
- private boolean mIsLegacy = false;
-
- private ClientInfo(AsyncChannel c, Messenger m) {
- mChannel = c;
- mMessenger = m;
- if (DBG) Slog.d(TAG, "New client, channel: " + c + " messenger: " + m);
- }
-
- @Override
- public String toString() {
- StringBuilder sb = new StringBuilder();
- sb.append("mChannel ").append(mChannel).append("\n");
- sb.append("mMessenger ").append(mMessenger).append("\n");
- sb.append("mResolvedService ").append(mResolvedService).append("\n");
- sb.append("mIsLegacy ").append(mIsLegacy).append("\n");
- for(int i = 0; i< mClientIds.size(); i++) {
- int clientID = mClientIds.keyAt(i);
- sb.append("clientId ").append(clientID).
- append(" mDnsId ").append(mClientIds.valueAt(i)).
- append(" type ").append(mClientRequests.get(clientID)).append("\n");
- }
- return sb.toString();
- }
-
- private boolean isLegacy() {
- return mIsLegacy;
- }
-
- private void setLegacy() {
- mIsLegacy = true;
- }
-
- // Remove any pending requests from the global map when we get rid of a client,
- // and send cancellations to the daemon.
- private void expungeAllRequests() {
- int globalId, clientId, i;
- // TODO: to keep handler responsive, do not clean all requests for that client at once.
- for (i = 0; i < mClientIds.size(); i++) {
- clientId = mClientIds.keyAt(i);
- globalId = mClientIds.valueAt(i);
- mIdToClientInfoMap.remove(globalId);
- if (DBG) Slog.d(TAG, "Terminating client-ID " + clientId +
- " global-ID " + globalId + " type " + mClientRequests.get(clientId));
- switch (mClientRequests.get(clientId)) {
- case NsdManager.DISCOVER_SERVICES:
- stopServiceDiscovery(globalId);
- break;
- case NsdManager.RESOLVE_SERVICE:
- stopResolveService(globalId);
- break;
- case NsdManager.REGISTER_SERVICE:
- unregisterService(globalId);
- break;
- default:
- break;
- }
- }
- mClientIds.clear();
- mClientRequests.clear();
- }
-
- // mClientIds is a sparse array of listener id -> mDnsClient id. For a given mDnsClient id,
- // return the corresponding listener id. mDnsClient id is also called a global id.
- private int getClientId(final int globalId) {
- int idx = mClientIds.indexOfValue(globalId);
- if (idx < 0) {
- return idx;
- }
- return mClientIds.keyAt(idx);
- }
- }
-
- /**
- * Interface which encapsulates dependencies of NsdService that are hard to mock, hard to
- * override, or have side effects on global state in unit tests.
- */
- @VisibleForTesting
- public interface NsdSettings {
- boolean isEnabled();
- void putEnabledStatus(boolean isEnabled);
- void registerContentObserver(Uri uri, ContentObserver observer);
-
- static NsdSettings makeDefault(Context context) {
- final ContentResolver resolver = context.getContentResolver();
- return new NsdSettings() {
- @Override
- public boolean isEnabled() {
- return Settings.Global.getInt(resolver, Settings.Global.NSD_ON, 1) == 1;
- }
-
- @Override
- public void putEnabledStatus(boolean isEnabled) {
- Settings.Global.putInt(resolver, Settings.Global.NSD_ON, isEnabled ? 1 : 0);
- }
-
- @Override
- public void registerContentObserver(Uri uri, ContentObserver observer) {
- resolver.registerContentObserver(uri, false, observer);
- }
- };
- }
- }
-}
diff --git a/services/core/java/com/android/server/OWNERS b/services/core/java/com/android/server/OWNERS
index 95dc66727730..71b463a4db8c 100644
--- a/services/core/java/com/android/server/OWNERS
+++ b/services/core/java/com/android/server/OWNERS
@@ -16,19 +16,23 @@ per-file SensorPrivacyService.java = file:platform/frameworks/native:/libs/senso
# ServiceWatcher
per-file ServiceWatcher.java = sooniln@google.com
+# Health
+per-file BatteryService.java = file:platform/hardware/interfaces:/health/aidl/OWNERS
+
per-file *Alarm* = file:/apex/jobscheduler/OWNERS
per-file *AppOp* = file:/core/java/android/permission/OWNERS
per-file *Battery* = file:/BATTERY_STATS_OWNERS
per-file *Binder* = file:/core/java/com/android/internal/os/BINDER_OWNERS
per-file *Bluetooth* = file:/core/java/android/bluetooth/OWNERS
per-file *Gnss* = file:/services/core/java/com/android/server/location/OWNERS
+per-file **IpSec* = file:/services/core/java/com/android/server/net/OWNERS
+per-file **IpSec* = file:/services/core/java/com/android/server/vcn/OWNERS
per-file *Location* = file:/services/core/java/com/android/server/location/OWNERS
per-file *Network* = file:/services/core/java/com/android/server/net/OWNERS
per-file *Storage* = file:/core/java/android/os/storage/OWNERS
per-file *TimeUpdate* = file:/core/java/android/app/timezone/OWNERS
per-file DynamicSystemService.java = file:/packages/DynamicSystemInstallationService/OWNERS
per-file GestureLauncherService.java = file:platform/packages/apps/EmergencyInfo:/OWNERS
-per-file IpSecService.java = file:/services/core/java/com/android/server/net/OWNERS
per-file MmsServiceBroker.java = file:/telephony/OWNERS
per-file NetIdManager.java = file:/services/core/java/com/android/server/net/OWNERS
per-file PackageWatchdog.java, RescueParty.java = file:/services/core/java/com/android/server/rollback/OWNERS
diff --git a/services/core/java/com/android/server/SerialService.java b/services/core/java/com/android/server/SerialService.java
index 1abe4588261a..e915fa1522a1 100644
--- a/services/core/java/com/android/server/SerialService.java
+++ b/services/core/java/com/android/server/SerialService.java
@@ -16,6 +16,7 @@
package com.android.server;
+import android.annotation.EnforcePermission;
import android.content.Context;
import android.hardware.ISerialManager;
import android.os.ParcelFileDescriptor;
@@ -34,9 +35,8 @@ public class SerialService extends ISerialManager.Stub {
com.android.internal.R.array.config_serialPorts);
}
+ @EnforcePermission(android.Manifest.permission.SERIAL_PORT)
public String[] getSerialPorts() {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.SERIAL_PORT, null);
-
ArrayList<String> ports = new ArrayList<String>();
for (int i = 0; i < mSerialPorts.length; i++) {
String path = mSerialPorts[i];
@@ -49,8 +49,8 @@ public class SerialService extends ISerialManager.Stub {
return result;
}
+ @EnforcePermission(android.Manifest.permission.SERIAL_PORT)
public ParcelFileDescriptor openSerialPort(String path) {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.SERIAL_PORT, null);
for (int i = 0; i < mSerialPorts.length; i++) {
if (mSerialPorts[i].equals(path)) {
return native_open(path);
diff --git a/services/core/java/com/android/server/SmartStorageMaintIdler.java b/services/core/java/com/android/server/SmartStorageMaintIdler.java
new file mode 100644
index 000000000000..2dff72fdc344
--- /dev/null
+++ b/services/core/java/com/android/server/SmartStorageMaintIdler.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2014 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;
+
+import android.app.job.JobInfo;
+import android.app.job.JobParameters;
+import android.app.job.JobScheduler;
+import android.app.job.JobService;
+import android.content.ComponentName;
+import android.content.Context;
+import android.util.Slog;
+
+import java.util.concurrent.TimeUnit;
+
+public class SmartStorageMaintIdler extends JobService {
+ private static final String TAG = "SmartStorageMaintIdler";
+
+ private static final ComponentName SMART_STORAGE_MAINT_SERVICE =
+ new ComponentName("android", SmartStorageMaintIdler.class.getName());
+
+ private static final int SMART_MAINT_JOB_ID = 2808;
+
+ private boolean mStarted;
+ private JobParameters mJobParams;
+ private final Runnable mFinishCallback = new Runnable() {
+ @Override
+ public void run() {
+ Slog.i(TAG, "Got smart storage maintenance service completion callback");
+ if (mStarted) {
+ jobFinished(mJobParams, false);
+ mStarted = false;
+ }
+ // ... and try again in a next period
+ scheduleSmartIdlePass(SmartStorageMaintIdler.this,
+ StorageManagerService.SMART_IDLE_MAINT_PERIOD);
+ }
+ };
+
+ @Override
+ public boolean onStartJob(JobParameters params) {
+ mJobParams = params;
+ StorageManagerService ms = StorageManagerService.sSelf;
+ if (ms != null) {
+ mStarted = true;
+ ms.runSmartIdleMaint(mFinishCallback);
+ }
+ return ms != null;
+ }
+
+ @Override
+ public boolean onStopJob(JobParameters params) {
+ mStarted = false;
+ return false;
+ }
+
+ /**
+ * Schedule the smart storage idle maintenance job
+ */
+ public static void scheduleSmartIdlePass(Context context, int nHours) {
+ StorageManagerService ms = StorageManagerService.sSelf;
+ if ((ms == null) || ms.isPassedLifetimeThresh()) {
+ return;
+ }
+
+ JobScheduler tm = context.getSystemService(JobScheduler.class);
+
+ long nextScheduleTime = TimeUnit.HOURS.toMillis(nHours);
+
+ JobInfo.Builder builder = new JobInfo.Builder(SMART_MAINT_JOB_ID,
+ SMART_STORAGE_MAINT_SERVICE);
+
+ builder.setMinimumLatency(nextScheduleTime);
+ tm.schedule(builder.build());
+ }
+}
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 8727932a87f7..9546496fd8e8 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -79,6 +79,7 @@ import android.content.res.Configuration;
import android.content.res.ObbInfo;
import android.database.ContentObserver;
import android.net.Uri;
+import android.os.BatteryManager;
import android.os.Binder;
import android.os.DropBoxManager;
import android.os.Environment;
@@ -158,6 +159,8 @@ import com.android.server.storage.StorageSessionController.ExternalStorageServic
import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.ActivityTaskManagerInternal.ScreenObserver;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
import libcore.io.IoUtils;
import libcore.util.EmptyArray;
@@ -339,7 +342,44 @@ class StorageManagerService extends IStorageManager.Stub
@Nullable public static String sMediaStoreAuthorityProcessName;
+ // Run period in hour for smart idle maintenance
+ static final int SMART_IDLE_MAINT_PERIOD = 1;
+
private final AtomicFile mSettingsFile;
+ private final AtomicFile mHourlyWriteFile;
+
+ private static final int MAX_HOURLY_WRITE_RECORDS = 72;
+
+ /**
+ * Default config values for smart idle maintenance
+ * Actual values will be controlled by DeviceConfig
+ */
+ // Decide whether smart idle maintenance is enabled or not
+ private static final boolean DEFAULT_SMART_IDLE_MAINT_ENABLED = false;
+ // Storage lifetime percentage threshold to decide to turn off the feature
+ private static final int DEFAULT_LIFETIME_PERCENT_THRESHOLD = 70;
+ // Minimum required number of dirty + free segments to trigger GC
+ private static final int DEFAULT_MIN_SEGMENTS_THRESHOLD = 512;
+ // Determine how much portion of current dirty segments will be GCed
+ private static final float DEFAULT_DIRTY_RECLAIM_RATE = 0.5F;
+ // Multiplier to amplify the target segment number for GC
+ private static final float DEFAULT_SEGMENT_RECLAIM_WEIGHT = 1.0F;
+ // Low battery level threshold to decide to turn off the feature
+ private static final float DEFAULT_LOW_BATTERY_LEVEL = 20F;
+ // Decide whether charging is required to turn on the feature
+ private static final boolean DEFAULT_CHARGING_REQUIRED = true;
+
+ private volatile int mLifetimePercentThreshold;
+ private volatile int mMinSegmentsThreshold;
+ private volatile float mDirtyReclaimRate;
+ private volatile float mSegmentReclaimWeight;
+ private volatile float mLowBatteryLevel;
+ private volatile boolean mChargingRequired;
+ private volatile boolean mNeedGC;
+
+ private volatile boolean mPassedLifetimeThresh;
+ // Tracking storage hourly write amounts
+ private volatile int[] mStorageHourlyWrites;
/**
* <em>Never</em> hold the lock while performing downcalls into vold, since
@@ -901,6 +941,10 @@ class StorageManagerService extends IStorageManager.Stub
}
private void handleSystemReady() {
+ if (prepareSmartIdleMaint()) {
+ SmartStorageMaintIdler.scheduleSmartIdlePass(mContext, SMART_IDLE_MAINT_PERIOD);
+ }
+
// Start scheduling nominally-daily fstrim operations
MountServiceIdler.scheduleIdlePass(mContext);
@@ -1233,7 +1277,7 @@ class StorageManagerService extends IStorageManager.Stub
}
for (int i = 0; i < mVolumes.size(); i++) {
final VolumeInfo vol = mVolumes.valueAt(i);
- if (vol.isVisibleForRead(userId) && vol.isMountedReadable()) {
+ if (vol.isVisibleForUser(userId) && vol.isMountedReadable()) {
final StorageVolume userVol = vol.buildStorageVolume(mContext, userId, false);
mHandler.obtainMessage(H_VOLUME_BROADCAST, userVol).sendToTarget();
@@ -1561,13 +1605,13 @@ class StorageManagerService extends IStorageManager.Stub
&& VolumeInfo.ID_PRIVATE_INTERNAL.equals(privateVol.id)) {
Slog.v(TAG, "Found primary storage at " + vol);
vol.mountFlags |= VolumeInfo.MOUNT_FLAG_PRIMARY;
- vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE;
+ vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE_FOR_WRITE;
mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget();
} else if (Objects.equals(privateVol.fsUuid, mPrimaryStorageUuid)) {
Slog.v(TAG, "Found primary storage at " + vol);
vol.mountFlags |= VolumeInfo.MOUNT_FLAG_PRIMARY;
- vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE;
+ vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE_FOR_WRITE;
mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget();
}
@@ -1577,13 +1621,13 @@ class StorageManagerService extends IStorageManager.Stub
&& vol.disk.isDefaultPrimary()) {
Slog.v(TAG, "Found primary storage at " + vol);
vol.mountFlags |= VolumeInfo.MOUNT_FLAG_PRIMARY;
- vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE;
+ vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE_FOR_WRITE;
}
// Adoptable public disks are visible to apps, since they meet
// public API requirement of being in a stable location.
if (vol.disk.isAdoptable()) {
- vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE;
+ vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE_FOR_WRITE;
}
vol.mountUserId = mCurrentUserId;
@@ -1594,7 +1638,9 @@ class StorageManagerService extends IStorageManager.Stub
} else if (vol.type == VolumeInfo.TYPE_STUB) {
if (vol.disk.isStubVisible()) {
- vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE;
+ vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE_FOR_WRITE;
+ } else {
+ vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE_FOR_READ;
}
vol.mountUserId = mCurrentUserId;
mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget();
@@ -1741,7 +1787,7 @@ class StorageManagerService extends IStorageManager.Stub
// started after this point will trigger additional
// user-specific broadcasts.
for (int userId : mSystemUnlockedUsers) {
- if (vol.isVisibleForRead(userId)) {
+ if (vol.isVisibleForUser(userId)) {
final StorageVolume userVol = vol.buildStorageVolume(mContext, userId,
false);
mHandler.obtainMessage(H_VOLUME_BROADCAST, userVol).sendToTarget();
@@ -1905,6 +1951,10 @@ class StorageManagerService extends IStorageManager.Stub
mSettingsFile = new AtomicFile(
new File(Environment.getDataSystemDirectory(), "storage.xml"), "storage-settings");
+ mHourlyWriteFile = new AtomicFile(
+ new File(Environment.getDataSystemDirectory(), "storage-hourly-writes"));
+
+ mStorageHourlyWrites = new int[MAX_HOURLY_WRITE_RECORDS];
synchronized (mLock) {
readSettingsLocked();
@@ -2570,7 +2620,7 @@ class StorageManagerService extends IStorageManager.Stub
// fstrim time is still updated. If file based checkpoints are used, we run
// idle maintenance (GC + fstrim) regardless of checkpoint status.
if (!needsCheckpoint() || !supportsBlockCheckpoint()) {
- mVold.runIdleMaint(new IVoldTaskListener.Stub() {
+ mVold.runIdleMaint(mNeedGC, new IVoldTaskListener.Stub() {
@Override
public void onStatus(int status, PersistableBundle extras) {
// Not currently used
@@ -2621,6 +2671,176 @@ class StorageManagerService extends IStorageManager.Stub
abortIdleMaint(null);
}
+ private boolean prepareSmartIdleMaint() {
+ /**
+ * We can choose whether going with a new storage smart idle maintenance job
+ * or falling back to the traditional way using DeviceConfig
+ */
+ boolean smartIdleMaintEnabled = DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
+ "smart_idle_maint_enabled",
+ DEFAULT_SMART_IDLE_MAINT_ENABLED);
+ if (smartIdleMaintEnabled) {
+ mLifetimePercentThreshold = DeviceConfig.getInt(
+ DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
+ "lifetime_threshold", DEFAULT_LIFETIME_PERCENT_THRESHOLD);
+ mMinSegmentsThreshold = DeviceConfig.getInt(DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
+ "min_segments_threshold", DEFAULT_MIN_SEGMENTS_THRESHOLD);
+ mDirtyReclaimRate = DeviceConfig.getFloat(DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
+ "dirty_reclaim_rate", DEFAULT_DIRTY_RECLAIM_RATE);
+ mSegmentReclaimWeight = DeviceConfig.getFloat(
+ DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
+ "segment_reclaim_weight", DEFAULT_SEGMENT_RECLAIM_WEIGHT);
+ mLowBatteryLevel = DeviceConfig.getFloat(DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
+ "low_battery_level", DEFAULT_LOW_BATTERY_LEVEL);
+ mChargingRequired = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
+ "charging_required", DEFAULT_CHARGING_REQUIRED);
+
+ // If we use the smart idle maintenance, we need to turn off GC in the traditional idle
+ // maintenance to avoid the conflict
+ mNeedGC = false;
+
+ loadStorageHourlyWrites();
+ try {
+ mVold.refreshLatestWrite();
+ } catch (Exception e) {
+ Slog.wtf(TAG, e);
+ }
+ refreshLifetimeConstraint();
+ }
+ return smartIdleMaintEnabled;
+ }
+
+ // Return whether storage lifetime exceeds the threshold
+ public boolean isPassedLifetimeThresh() {
+ return mPassedLifetimeThresh;
+ }
+
+ private void loadStorageHourlyWrites() {
+ FileInputStream fis = null;
+
+ try {
+ fis = mHourlyWriteFile.openRead();
+ ObjectInputStream ois = new ObjectInputStream(fis);
+ mStorageHourlyWrites = (int[])ois.readObject();
+ } catch (FileNotFoundException e) {
+ // Missing data is okay, probably first boot
+ } catch (Exception e) {
+ Slog.wtf(TAG, "Failed reading write records", e);
+ } finally {
+ IoUtils.closeQuietly(fis);
+ }
+ }
+
+ private int getAverageHourlyWrite() {
+ return Arrays.stream(mStorageHourlyWrites).sum() / MAX_HOURLY_WRITE_RECORDS;
+ }
+
+ private void updateStorageHourlyWrites(int latestWrite) {
+ FileOutputStream fos = null;
+
+ System.arraycopy(mStorageHourlyWrites,0, mStorageHourlyWrites, 1,
+ MAX_HOURLY_WRITE_RECORDS - 1);
+ mStorageHourlyWrites[0] = latestWrite;
+ try {
+ fos = mHourlyWriteFile.startWrite();
+ ObjectOutputStream oos = new ObjectOutputStream(fos);
+ oos.writeObject(mStorageHourlyWrites);
+ mHourlyWriteFile.finishWrite(fos);
+ } catch (IOException e) {
+ if (fos != null) {
+ mHourlyWriteFile.failWrite(fos);
+ }
+ }
+ }
+
+ private boolean checkChargeStatus() {
+ IntentFilter ifilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
+ Intent batteryStatus = mContext.registerReceiver(null, ifilter);
+
+ if (mChargingRequired) {
+ int status = batteryStatus.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
+ if (status != BatteryManager.BATTERY_STATUS_CHARGING &&
+ status != BatteryManager.BATTERY_STATUS_FULL) {
+ Slog.w(TAG, "Battery is not being charged");
+ return false;
+ }
+ }
+
+ int level = batteryStatus.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
+ int scale = batteryStatus.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
+ float chargePercent = level * 100f / (float)scale;
+
+ if (chargePercent < mLowBatteryLevel) {
+ Slog.w(TAG, "Battery level is " + chargePercent + ", which is lower than threshold: " +
+ mLowBatteryLevel);
+ return false;
+ }
+ return true;
+ }
+
+ private boolean refreshLifetimeConstraint() {
+ int storageLifeTime = 0;
+
+ try {
+ storageLifeTime = mVold.getStorageLifeTime();
+ } catch (Exception e) {
+ Slog.wtf(TAG, e);
+ return false;
+ }
+
+ if (storageLifeTime == -1) {
+ Slog.w(TAG, "Failed to get storage lifetime");
+ return false;
+ } else if (storageLifeTime > mLifetimePercentThreshold) {
+ Slog.w(TAG, "Ended smart idle maintenance, because of lifetime(" + storageLifeTime +
+ ")" + ", lifetime threshold(" + mLifetimePercentThreshold + ")");
+ mPassedLifetimeThresh = true;
+ return false;
+ }
+ Slog.i(TAG, "Storage lifetime: " + storageLifeTime);
+ return true;
+ }
+
+ void runSmartIdleMaint(Runnable callback) {
+ enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);
+
+ try {
+ // Block based checkpoint process runs fstrim. So, if checkpoint is in progress
+ // (first boot after OTA), We skip the smart idle maintenance
+ if (!needsCheckpoint() || !supportsBlockCheckpoint()) {
+ if (!refreshLifetimeConstraint() || !checkChargeStatus()) {
+ return;
+ }
+
+ int latestHourlyWrite = mVold.getWriteAmount();
+ if (latestHourlyWrite == -1) {
+ Slog.w(TAG, "Failed to get storage hourly write");
+ return;
+ }
+
+ updateStorageHourlyWrites(latestHourlyWrite);
+ int avgHourlyWrite = getAverageHourlyWrite();
+
+ Slog.i(TAG, "Set smart idle maintenance: " + "latest hourly write: " +
+ latestHourlyWrite + ", average hourly write: " + avgHourlyWrite +
+ ", min segment threshold: " + mMinSegmentsThreshold +
+ ", dirty reclaim rate: " + mDirtyReclaimRate +
+ ", segment reclaim weight:" + mSegmentReclaimWeight);
+ mVold.setGCUrgentPace(avgHourlyWrite, mMinSegmentsThreshold, mDirtyReclaimRate,
+ mSegmentReclaimWeight);
+ } else {
+ Slog.i(TAG, "Skipping smart idle maintenance - block based checkpoint in progress");
+ }
+ } catch (Exception e) {
+ Slog.wtf(TAG, e);
+ } finally {
+ if (callback != null) {
+ callback.run();
+ }
+ }
+ }
+
@Override
public void setDebugFlags(int flags, int mask) {
enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
@@ -3400,8 +3620,12 @@ class StorageManagerService extends IStorageManager.Stub
mInstaller.tryMountDataMirror(volumeUuid);
}
}
- } catch (Exception e) {
+ } catch (RemoteException | Installer.InstallerException e) {
Slog.wtf(TAG, e);
+ // Make sure to re-throw this exception; we must not ignore failure
+ // to prepare the user storage as it could indicate that encryption
+ // wasn't successfully set up.
+ throw new RuntimeException(e);
}
}
@@ -3563,24 +3787,24 @@ class StorageManagerService extends IStorageManager.Stub
}
@Override
- public ParcelFileDescriptor open() throws NativeDaemonConnectorException {
+ public ParcelFileDescriptor open() throws AppFuseMountException {
try {
final FileDescriptor fd = mVold.mountAppFuse(uid, mountId);
mMounted = true;
return new ParcelFileDescriptor(fd);
} catch (Exception e) {
- throw new NativeDaemonConnectorException("Failed to mount", e);
+ throw new AppFuseMountException("Failed to mount", e);
}
}
@Override
public ParcelFileDescriptor openFile(int mountId, int fileId, int flags)
- throws NativeDaemonConnectorException {
+ throws AppFuseMountException {
try {
return new ParcelFileDescriptor(
mVold.openAppFuseFile(uid, mountId, fileId, flags));
} catch (Exception e) {
- throw new NativeDaemonConnectorException("Failed to open", e);
+ throw new AppFuseMountException("Failed to open", e);
}
}
@@ -3620,7 +3844,7 @@ class StorageManagerService extends IStorageManager.Stub
// It seems the thread of mAppFuseBridge has already been terminated.
mAppFuseBridge = null;
}
- } catch (NativeDaemonConnectorException e) {
+ } catch (AppFuseMountException e) {
throw e.rethrowAsParcelableException();
}
}
@@ -3769,7 +3993,7 @@ class StorageManagerService extends IStorageManager.Stub
if (forWrite) {
match = vol.isVisibleForWrite(userId);
} else {
- match = vol.isVisibleForRead(userId)
+ match = vol.isVisibleForUser(userId)
|| (includeInvisible && vol.getPath() != null);
}
if (!match) continue;
diff --git a/services/core/java/com/android/server/SystemServiceManager.java b/services/core/java/com/android/server/SystemServiceManager.java
index cb6e73af56ac..3cc8e7672557 100644
--- a/services/core/java/com/android/server/SystemServiceManager.java
+++ b/services/core/java/com/android/server/SystemServiceManager.java
@@ -21,18 +21,16 @@ import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.content.Context;
import android.content.pm.UserInfo;
-import android.os.Build;
import android.os.Environment;
import android.os.SystemClock;
import android.os.Trace;
-import android.util.ArrayMap;
import android.util.EventLog;
import android.util.IndentingPrintWriter;
import android.util.Slog;
import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
-import com.android.internal.os.ClassLoaderFactory;
+import com.android.internal.os.SystemServerClassLoaderFactory;
import com.android.internal.util.Preconditions;
import com.android.server.SystemService.TargetUser;
import com.android.server.am.EventLogTags;
@@ -77,9 +75,6 @@ public final class SystemServiceManager implements Dumpable {
// Services that should receive lifecycle events.
private final ArrayList<SystemService> mServices = new ArrayList<SystemService>();
- // Map of paths to PathClassLoader, so we don't load the same path multiple times.
- private final ArrayMap<String, PathClassLoader> mLoadedPaths = new ArrayMap<>();
-
private int mCurrentPhase = -1;
private UserManagerInternal mUserManagerInternal;
@@ -119,16 +114,8 @@ public final class SystemServiceManager implements Dumpable {
* @return The service instance.
*/
public SystemService startServiceFromJar(String className, String path) {
- PathClassLoader pathClassLoader = mLoadedPaths.get(path);
- if (pathClassLoader == null) {
- // NB: the parent class loader should always be the system server class loader.
- // Changing it has implications that require discussion with the mainline team.
- pathClassLoader = (PathClassLoader) ClassLoaderFactory.createClassLoader(
- path, null /* librarySearchPath */, null /* libraryPermittedPath */,
- this.getClass().getClassLoader(), Build.VERSION.SDK_INT,
- true /* isNamespaceShared */, null /* classLoaderName */);
- mLoadedPaths.put(path, pathClassLoader);
- }
+ PathClassLoader pathClassLoader = SystemServerClassLoaderFactory.getOrCreateClassLoader(
+ path, this.getClass().getClassLoader());
final Class<SystemService> serviceClass = loadClassFromLoader(className, pathClassLoader);
return startService(serviceClass);
}
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index ab220b5e42e4..811f2f5e5283 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -91,6 +91,7 @@ import android.util.Pair;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IBatteryStats;
+import com.android.internal.telephony.ICarrierPrivilegesListener;
import com.android.internal.telephony.IOnSubscriptionsChangedListener;
import com.android.internal.telephony.IPhoneStateListener;
import com.android.internal.telephony.ITelephonyRegistry;
@@ -106,6 +107,7 @@ import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -149,9 +151,12 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
IPhoneStateListener callback;
IOnSubscriptionsChangedListener onSubscriptionsChangedListenerCallback;
IOnSubscriptionsChangedListener onOpportunisticSubscriptionsChangedListenerCallback;
+ ICarrierPrivilegesListener carrierPrivilegesListener;
int callerUid;
int callerPid;
+ boolean renounceFineLocationAccess;
+ boolean renounceCoarseLocationAccess;
Set<Integer> eventList;
@@ -171,6 +176,10 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
return (onOpportunisticSubscriptionsChangedListenerCallback != null);
}
+ boolean matchCarrierPrivilegesListener() {
+ return carrierPrivilegesListener != null;
+ }
+
boolean canReadCallLog() {
try {
return TelephonyPermissions.checkReadCallLog(
@@ -187,8 +196,9 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
+ " onSubscriptionsChangedListenererCallback="
+ onSubscriptionsChangedListenerCallback
+ " onOpportunisticSubscriptionsChangedListenererCallback="
- + onOpportunisticSubscriptionsChangedListenerCallback + " subId=" + subId
- + " phoneId=" + phoneId + " events=" + eventList + "}";
+ + onOpportunisticSubscriptionsChangedListenerCallback
+ + " carrierPrivilegesListener=" + carrierPrivilegesListener
+ + " subId=" + subId + " phoneId=" + phoneId + " events=" + eventList + "}";
}
}
@@ -364,7 +374,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
private List<BarringInfo> mBarringInfo = null;
- private boolean mCarrierNetworkChangeState = false;
+ private boolean[] mCarrierNetworkChangeState = null;
private PhoneCapability mPhoneCapability = null;
@@ -386,6 +396,10 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
private int[] mAllowedNetworkTypeReason;
private long[] mAllowedNetworkTypeValue;
+ private static final List<LinkCapacityEstimate> INVALID_LCE_LIST =
+ new ArrayList<LinkCapacityEstimate>(Arrays.asList(new LinkCapacityEstimate(
+ LinkCapacityEstimate.LCE_TYPE_COMBINED,
+ LinkCapacityEstimate.INVALID, LinkCapacityEstimate.INVALID)));
private List<List<LinkCapacityEstimate>> mLinkCapacityEstimateLists;
/**
@@ -396,6 +410,10 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
*/
private List<Map<Pair<Integer, ApnSetting>, PreciseDataConnectionState>>
mPreciseDataConnectionStates;
+
+ /** Per-phoneId snapshot of privileged packages (names + UIDs). */
+ private List<Pair<List<String>, int[]>> mCarrierPrivilegeStates;
+
/**
* Support backward compatibility for {@link android.telephony.TelephonyDisplayInfo}.
*/
@@ -669,6 +687,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
mOutgoingCallEmergencyNumber = copyOf(mOutgoingCallEmergencyNumber, mNumPhones);
mOutgoingSmsEmergencyNumber = copyOf(mOutgoingSmsEmergencyNumber, mNumPhones);
mTelephonyDisplayInfos = copyOf(mTelephonyDisplayInfos, mNumPhones);
+ mCarrierNetworkChangeState = copyOf(mCarrierNetworkChangeState, mNumPhones);
mIsDataEnabled= copyOf(mIsDataEnabled, mNumPhones);
mDataEnabledReason = copyOf(mDataEnabledReason, mNumPhones);
mAllowedNetworkTypeReason = copyOf(mAllowedNetworkTypeReason, mNumPhones);
@@ -682,6 +701,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
cutListToSize(mBarringInfo, mNumPhones);
cutListToSize(mPhysicalChannelConfigs, mNumPhones);
cutListToSize(mLinkCapacityEstimateLists, mNumPhones);
+ cutListToSize(mCarrierPrivilegeStates, mNumPhones);
return;
}
@@ -714,13 +734,15 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
mBackgroundCallState[i] = PreciseCallState.PRECISE_CALL_STATE_IDLE;
mPreciseDataConnectionStates.add(new ArrayMap<>());
mBarringInfo.add(i, new BarringInfo());
+ mCarrierNetworkChangeState[i] = false;
mTelephonyDisplayInfos[i] = null;
mIsDataEnabled[i] = false;
mDataEnabledReason[i] = TelephonyManager.DATA_ENABLED_REASON_USER;
mPhysicalChannelConfigs.add(i, new ArrayList<>());
mAllowedNetworkTypeReason[i] = -1;
mAllowedNetworkTypeValue[i] = -1;
- mLinkCapacityEstimateLists.add(i, new ArrayList<>());
+ mLinkCapacityEstimateLists.add(i, INVALID_LCE_LIST);
+ mCarrierPrivilegeStates.add(i, new Pair<>(Collections.emptyList(), new int[0]));
}
}
@@ -778,6 +800,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
mOutgoingCallEmergencyNumber = new EmergencyNumber[numPhones];
mOutgoingSmsEmergencyNumber = new EmergencyNumber[numPhones];
mBarringInfo = new ArrayList<>();
+ mCarrierNetworkChangeState = new boolean[numPhones];
mTelephonyDisplayInfos = new TelephonyDisplayInfo[numPhones];
mPhysicalChannelConfigs = new ArrayList<>();
mAllowedNetworkTypeReason = new int[numPhones];
@@ -785,6 +808,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
mIsDataEnabled = new boolean[numPhones];
mDataEnabledReason = new int[numPhones];
mLinkCapacityEstimateLists = new ArrayList<>();
+ mCarrierPrivilegeStates = new ArrayList<>();
for (int i = 0; i < numPhones; i++) {
mCallState[i] = TelephonyManager.CALL_STATE_IDLE;
@@ -814,13 +838,15 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
mBackgroundCallState[i] = PreciseCallState.PRECISE_CALL_STATE_IDLE;
mPreciseDataConnectionStates.add(new ArrayMap<>());
mBarringInfo.add(i, new BarringInfo());
+ mCarrierNetworkChangeState[i] = false;
mTelephonyDisplayInfos[i] = null;
mIsDataEnabled[i] = false;
mDataEnabledReason[i] = TelephonyManager.DATA_ENABLED_REASON_USER;
mPhysicalChannelConfigs.add(i, new ArrayList<>());
mAllowedNetworkTypeReason[i] = -1;
mAllowedNetworkTypeValue[i] = -1;
- mLinkCapacityEstimateLists.add(i, new ArrayList<>());
+ mLinkCapacityEstimateLists.add(i, INVALID_LCE_LIST);
+ mCarrierPrivilegeStates.add(i, new Pair<>(Collections.emptyList(), new int[0]));
}
mAppOps = mContext.getSystemService(AppOpsManager.class);
@@ -991,14 +1017,25 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
@Override
- public void listenWithEventList(int subId, String callingPackage, String callingFeatureId,
- IPhoneStateListener callback, int[] events, boolean notifyNow) {
+ public void listenWithEventList(boolean renounceFineLocationAccess,
+ boolean renounceCoarseLocationAccess, int subId, String callingPackage,
+ String callingFeatureId, IPhoneStateListener callback,
+ int[] events, boolean notifyNow) {
Set<Integer> eventList = Arrays.stream(events).boxed().collect(Collectors.toSet());
- listen(callingPackage, callingFeatureId, callback, eventList, notifyNow, subId);
+ listen(renounceFineLocationAccess, renounceFineLocationAccess, callingPackage,
+ callingFeatureId, callback, eventList, notifyNow, subId);
}
private void listen(String callingPackage, @Nullable String callingFeatureId,
IPhoneStateListener callback, Set<Integer> events, boolean notifyNow, int subId) {
+ listen(false, false, callingPackage,
+ callingFeatureId, callback, events, notifyNow, subId);
+ }
+
+ private void listen(boolean renounceFineLocationAccess,
+ boolean renounceCoarseLocationAccess, String callingPackage,
+ @Nullable String callingFeatureId, IPhoneStateListener callback,
+ Set<Integer> events, boolean notifyNow, int subId) {
int callerUserId = UserHandle.getCallingUserId();
mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
String str = "listen: E pkg=" + pii(callingPackage) + " uid=" + Binder.getCallingUid()
@@ -1043,6 +1080,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
r.callback = callback;
r.callingPackage = callingPackage;
r.callingFeatureId = callingFeatureId;
+ r.renounceCoarseLocationAccess = renounceCoarseLocationAccess;
+ r.renounceFineLocationAccess = renounceFineLocationAccess;
r.callerUid = Binder.getCallingUid();
r.callerPid = Binder.getCallingPid();
// Legacy applications pass SubscriptionManager.DEFAULT_SUB_ID,
@@ -1161,17 +1200,6 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
remove(r.binder);
}
}
- if (events.contains(
- TelephonyCallback.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED)) {
- updateReportSignalStrengthDecision(r.subId);
- try {
- if (mSignalStrength[r.phoneId] != null) {
- r.callback.onSignalStrengthsChanged(mSignalStrength[r.phoneId]);
- }
- } catch (RemoteException ex) {
- remove(r.binder);
- }
- }
if (validateEventAndUserLocked(
r, TelephonyCallback.EVENT_CELL_INFO_CHANGED)) {
try {
@@ -1222,7 +1250,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
if (events.contains(TelephonyCallback.EVENT_CARRIER_NETWORK_CHANGED)) {
try {
- r.callback.onCarrierNetworkChange(mCarrierNetworkChangeState);
+ r.callback.onCarrierNetworkChange(mCarrierNetworkChangeState[r.phoneId]);
} catch (RemoteException ex) {
remove(r.binder);
}
@@ -1350,27 +1378,6 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
}
- private void updateReportSignalStrengthDecision(int subscriptionId) {
- synchronized (mRecords) {
- TelephonyManager telephonyManager = (TelephonyManager) mContext
- .getSystemService(Context.TELEPHONY_SERVICE);
- for (Record r : mRecords) {
- // If any of the system clients wants to always listen to signal strength,
- // we need to set it on.
- if (r.matchTelephonyCallbackEvent(
- TelephonyCallback.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED)) {
- telephonyManager.createForSubscriptionId(subscriptionId)
- .setAlwaysReportSignalStrength(true);
- return;
- }
- }
- // If none of the system clients wants to always listen to signal strength,
- // we need to set it off.
- telephonyManager.createForSubscriptionId(subscriptionId)
- .setAlwaysReportSignalStrength(false);
- }
- }
-
private String getCallIncomingNumber(Record record, int phoneId) {
// Only reveal the incoming number if the record has read call log permission.
return record.canReadCallLog() ? mCallIncomingNumber[phoneId] : "";
@@ -1454,14 +1461,6 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
mRecords.remove(i);
-
- // Every time a client that is registrating to always receive the signal
- // strength is removed from registry records, we need to check if
- // the signal strength decision needs to update on its slot.
- if (r.matchTelephonyCallbackEvent(
- TelephonyCallback.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED)) {
- updateReportSignalStrengthDecision(r.subId);
- }
return;
}
}
@@ -1693,10 +1692,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
log("notifySignalStrengthForPhoneId: r=" + r + " subId=" + subId
+ " phoneId=" + phoneId + " ss=" + signalStrength);
}
- if ((r.matchTelephonyCallbackEvent(
+ if (r.matchTelephonyCallbackEvent(
TelephonyCallback.EVENT_SIGNAL_STRENGTHS_CHANGED)
- || r.matchTelephonyCallbackEvent(
- TelephonyCallback.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED))
&& idMatch(r, subId, phoneId)) {
try {
if (DBG) {
@@ -1747,23 +1744,37 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
throw new SecurityException("notifyCarrierNetworkChange without carrier privilege");
}
+ for (int subId : subIds) {
+ notifyCarrierNetworkChangeWithPermission(subId, active);
+ }
+ }
+
+ @Override
+ public void notifyCarrierNetworkChangeWithSubId(int subId, boolean active) {
+ if (!TelephonyPermissions.checkCarrierPrivilegeForSubId(mContext, subId)) {
+ throw new SecurityException(
+ "notifyCarrierNetworkChange without carrier privilege on subId " + subId);
+ }
+
+ notifyCarrierNetworkChangeWithPermission(subId, active);
+ }
+
+ private void notifyCarrierNetworkChangeWithPermission(int subId, boolean active) {
synchronized (mRecords) {
- mCarrierNetworkChangeState = active;
- for (int subId : subIds) {
- int phoneId = getPhoneIdFromSubId(subId);
+ int phoneId = getPhoneIdFromSubId(subId);
+ mCarrierNetworkChangeState[phoneId] = active;
- if (VDBG) {
- log("notifyCarrierNetworkChange: active=" + active + "subId: " + subId);
- }
- for (Record r : mRecords) {
- if (r.matchTelephonyCallbackEvent(
- TelephonyCallback.EVENT_CARRIER_NETWORK_CHANGED)
- && idMatch(r, subId, phoneId)) {
- try {
- r.callback.onCarrierNetworkChange(active);
- } catch (RemoteException ex) {
- mRemoveList.add(r.binder);
- }
+ if (VDBG) {
+ log("notifyCarrierNetworkChange: active=" + active + "subId: " + subId);
+ }
+ for (Record r : mRecords) {
+ if (r.matchTelephonyCallbackEvent(
+ TelephonyCallback.EVENT_CARRIER_NETWORK_CHANGED)
+ && idMatch(r, subId, phoneId)) {
+ try {
+ r.callback.onCarrierNetworkChange(active);
+ } catch (RemoteException ex) {
+ mRemoveList.add(r.binder);
}
}
}
@@ -2771,6 +2782,104 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
@Override
+ public void addCarrierPrivilegesListener(
+ int phoneId,
+ ICarrierPrivilegesListener callback,
+ String callingPackage,
+ String callingFeatureId) {
+ int callerUserId = UserHandle.getCallingUserId();
+ mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+ "addCarrierPrivilegesListener");
+ if (VDBG) {
+ log(
+ "listen carrier privs: E pkg=" + pii(callingPackage) + " phoneId=" + phoneId
+ + " uid=" + Binder.getCallingUid()
+ + " myUserId=" + UserHandle.myUserId() + " callerUserId=" + callerUserId
+ + " callback=" + callback
+ + " callback.asBinder=" + callback.asBinder());
+ }
+ if (!validatePhoneId(phoneId)) {
+ throw new IllegalArgumentException("Invalid slot index: " + phoneId);
+ }
+
+ synchronized (mRecords) {
+ Record r = add(
+ callback.asBinder(), Binder.getCallingUid(), Binder.getCallingPid(), false);
+
+ if (r == null) return;
+
+ r.context = mContext;
+ r.carrierPrivilegesListener = callback;
+ r.callingPackage = callingPackage;
+ r.callingFeatureId = callingFeatureId;
+ r.callerUid = Binder.getCallingUid();
+ r.callerPid = Binder.getCallingPid();
+ r.phoneId = phoneId;
+ r.eventList = new ArraySet<>();
+ if (DBG) {
+ log("listen carrier privs: Register r=" + r);
+ }
+
+ Pair<List<String>, int[]> state = mCarrierPrivilegeStates.get(phoneId);
+ try {
+ r.carrierPrivilegesListener.onCarrierPrivilegesChanged(
+ Collections.unmodifiableList(state.first),
+ Arrays.copyOf(state.second, state.second.length));
+ } catch (RemoteException ex) {
+ remove(r.binder);
+ }
+ }
+ }
+
+ @Override
+ public void removeCarrierPrivilegesListener(
+ ICarrierPrivilegesListener callback, String callingPackage) {
+ mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+ "removeCarrierPrivilegesListener");
+ remove(callback.asBinder());
+ }
+
+ @Override
+ public void notifyCarrierPrivilegesChanged(
+ int phoneId, List<String> privilegedPackageNames, int[] privilegedUids) {
+ if (!checkNotifyPermission("notifyCarrierPrivilegesChanged")) {
+ return;
+ }
+ if (!validatePhoneId(phoneId)) return;
+ if (VDBG) {
+ log(
+ "notifyCarrierPrivilegesChanged: phoneId=" + phoneId
+ + ", <packages=" + pii(privilegedPackageNames)
+ + ", uids=" + Arrays.toString(privilegedUids) + ">");
+ }
+ synchronized (mRecords) {
+ mCarrierPrivilegeStates.set(
+ phoneId, new Pair<>(privilegedPackageNames, privilegedUids));
+ for (Record r : mRecords) {
+ // Listeners are per-slot, not per-subscription. This is to provide a stable
+ // view across SIM profile switches.
+ if (!r.matchCarrierPrivilegesListener()
+ || !idMatch(r, SubscriptionManager.INVALID_SUBSCRIPTION_ID, phoneId)) {
+ continue;
+ }
+ try {
+ // Make sure even in-process listeners can't modify the values.
+ r.carrierPrivilegesListener.onCarrierPrivilegesChanged(
+ Collections.unmodifiableList(privilegedPackageNames),
+ Arrays.copyOf(privilegedUids, privilegedUids.length));
+ } catch (RemoteException ex) {
+ mRemoveList.add(r.binder);
+ }
+ }
+ handleRemoveListLocked();
+ }
+ }
+
+ @Override
public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ");
@@ -2811,6 +2920,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
pw.println("mOutgoingCallEmergencyNumber=" + mOutgoingCallEmergencyNumber[i]);
pw.println("mOutgoingSmsEmergencyNumber=" + mOutgoingSmsEmergencyNumber[i]);
pw.println("mBarringInfo=" + mBarringInfo.get(i));
+ pw.println("mCarrierNetworkChangeState=" + mCarrierNetworkChangeState[i]);
pw.println("mTelephonyDisplayInfo=" + mTelephonyDisplayInfos[i]);
pw.println("mIsDataEnabled=" + mIsDataEnabled);
pw.println("mDataEnabledReason=" + mDataEnabledReason);
@@ -2818,9 +2928,14 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
pw.println("mAllowedNetworkTypeValue=" + mAllowedNetworkTypeValue[i]);
pw.println("mPhysicalChannelConfigs=" + mPhysicalChannelConfigs.get(i));
pw.println("mLinkCapacityEstimateList=" + mLinkCapacityEstimateLists.get(i));
+ // We need to obfuscate package names, and primitive arrays' native toString is ugly
+ Pair<List<String>, int[]> carrierPrivilegeState = mCarrierPrivilegeStates.get(i);
+ pw.println(
+ "mCarrierPrivilegeState=<packages=" + pii(carrierPrivilegeState.first)
+ + ", uids=" + Arrays.toString(carrierPrivilegeState.second) + ">");
pw.decreaseIndent();
}
- pw.println("mCarrierNetworkChangeState=" + mCarrierNetworkChangeState);
+
pw.println("mPhoneCapability=" + mPhoneCapability);
pw.println("mActiveDataSubId=" + mActiveDataSubId);
pw.println("mRadioPowerState=" + mRadioPowerState);
@@ -3150,11 +3265,6 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION, null);
}
- if ((events.contains(TelephonyCallback.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED))) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH, null);
- }
-
if (isPrivilegedPhoneStatePermissionRequired(events)) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, null);
@@ -3242,6 +3352,9 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
* If you don't need app compat logic, use {@link #checkFineLocationAccess(Record)}.
*/
private boolean checkFineLocationAccess(Record r, int minSdk) {
+ if (r.renounceFineLocationAccess) {
+ return false;
+ }
LocationAccessPolicy.LocationPermissionQuery query =
new LocationAccessPolicy.LocationPermissionQuery.Builder()
.setCallingPackage(r.callingPackage)
@@ -3268,6 +3381,9 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
* If you don't need app compat logic, use {@link #checkCoarseLocationAccess(Record)}.
*/
private boolean checkCoarseLocationAccess(Record r, int minSdk) {
+ if (r.renounceCoarseLocationAccess) {
+ return false;
+ }
LocationAccessPolicy.LocationPermissionQuery query =
new LocationAccessPolicy.LocationPermissionQuery.Builder()
.setCallingPackage(r.callingPackage)
@@ -3315,9 +3431,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
}
- if (events.contains(TelephonyCallback.EVENT_SIGNAL_STRENGTHS_CHANGED)
- || events.contains(
- TelephonyCallback.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED)) {
+ if (events.contains(TelephonyCallback.EVENT_SIGNAL_STRENGTHS_CHANGED)) {
try {
if (mSignalStrength[phoneId] != null) {
SignalStrength signalStrength = mSignalStrength[phoneId];
@@ -3545,4 +3659,10 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
private static String pii(String packageName) {
return Build.IS_DEBUGGABLE ? packageName : "***";
}
+
+ /** Redacts an entire list of package names if necessary. */
+ private static String pii(List<String> packageNames) {
+ if (packageNames.isEmpty() || Build.IS_DEBUGGABLE) return packageNames.toString();
+ return "[***, size=" + packageNames.size() + "]";
+ }
}
diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java
index ce74f1f4a1ac..2d328d8b0949 100644
--- a/services/core/java/com/android/server/VcnManagementService.java
+++ b/services/core/java/com/android/server/VcnManagementService.java
@@ -24,7 +24,6 @@ import static android.net.vcn.VcnManager.VCN_STATUS_CODE_ACTIVE;
import static android.net.vcn.VcnManager.VCN_STATUS_CODE_INACTIVE;
import static android.net.vcn.VcnManager.VCN_STATUS_CODE_NOT_CONFIGURED;
import static android.net.vcn.VcnManager.VCN_STATUS_CODE_SAFE_MODE;
-import static android.telephony.SubscriptionManager.isValidSubscriptionId;
import static com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
import static com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionTrackerCallback;
@@ -141,7 +140,7 @@ import java.util.concurrent.TimeUnit;
* | or its properties
* v |
* +-----------------------------------------------------------------------+
- * | UnderlyingNetworkTracker |
+ * | UnderlyingNetworkController |
* | |
* | Manages lifecycle of underlying physical networks, filing requests to |
* | bring them up, and releasing them as they become no longer necessary |
@@ -164,10 +163,6 @@ public class VcnManagementService extends IVcnManagementService.Stub {
@VisibleForTesting(visibility = Visibility.PRIVATE)
static final String VCN_CONFIG_FILE = "/data/system/vcn/configs.xml";
- // TODO(b/176956496): Directly use CarrierServiceBindHelper.UNBIND_DELAY_MILLIS
- @VisibleForTesting(visibility = Visibility.PRIVATE)
- static final long CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS = TimeUnit.SECONDS.toMillis(30);
-
/* Binder context for this service */
@NonNull private final Context mContext;
@NonNull private final Dependencies mDeps;
@@ -372,12 +367,15 @@ public class VcnManagementService extends IVcnManagementService.Stub {
/** Notifies the VcnManagementService that external dependencies can be set up. */
public void systemReady() {
- mNetworkProvider.register();
- mContext.getSystemService(ConnectivityManager.class)
- .registerNetworkCallback(
- new NetworkRequest.Builder().clearCapabilities().build(),
- mTrackingNetworkCallback);
- mTelephonySubscriptionTracker.register();
+ // Always run on the handler thread to ensure consistency.
+ mHandler.post(() -> {
+ mNetworkProvider.register();
+ mContext.getSystemService(ConnectivityManager.class)
+ .registerNetworkCallback(
+ new NetworkRequest.Builder().clearCapabilities().build(),
+ mTrackingNetworkCallback);
+ mTelephonySubscriptionTracker.register();
+ });
}
private void enforcePrimaryUser() {
@@ -471,22 +469,15 @@ public class VcnManagementService extends IVcnManagementService.Stub {
if (!mVcns.containsKey(subGrp)) {
startVcnLocked(subGrp, entry.getValue());
}
-
- // Cancel any scheduled teardowns for active subscriptions
- mHandler.removeCallbacksAndMessages(mVcns.get(subGrp));
}
}
- // Schedule teardown of any VCN instances that have lost carrier privileges (after a
- // delay)
+ // Schedule teardown of any VCN instances that have lost carrier privileges
for (Entry<ParcelUuid, Vcn> entry : mVcns.entrySet()) {
final ParcelUuid subGrp = entry.getKey();
final VcnConfig config = mConfigs.get(subGrp);
final boolean isActiveSubGrp = isActiveSubGroup(subGrp, snapshot);
- final boolean isValidActiveDataSubIdNotInVcnSubGrp =
- isValidSubscriptionId(snapshot.getActiveDataSubscriptionId())
- && !isActiveSubGroup(subGrp, snapshot);
// TODO(b/193687515): Support multiple VCNs active at the same time
if (config == null
@@ -496,31 +487,12 @@ public class VcnManagementService extends IVcnManagementService.Stub {
final ParcelUuid uuidToTeardown = subGrp;
final Vcn instanceToTeardown = entry.getValue();
- // TODO(b/193687515): Support multiple VCNs active at the same time
- // If directly switching to a subscription not in the current group,
- // teardown immediately to prevent other subscription's network from being
- // outscored by the VCN. Otherwise, teardown after a delay to ensure that
- // SIM profile switches do not trigger the VCN to cycle.
- final long teardownDelayMs =
- isValidActiveDataSubIdNotInVcnSubGrp
- ? 0
- : CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS;
- mHandler.postDelayed(() -> {
- synchronized (mLock) {
- // Guard against case where this is run after a old instance was
- // torn down, and a new instance was started. Verify to ensure
- // correct instance is torn down. This could happen as a result of a
- // Carrier App manually removing/adding a VcnConfig.
- if (mVcns.get(uuidToTeardown) == instanceToTeardown) {
- stopVcnLocked(uuidToTeardown);
-
- // TODO(b/181789060): invoke asynchronously after Vcn notifies
- // through VcnCallback
- notifyAllPermissionedStatusCallbacksLocked(
- uuidToTeardown, VCN_STATUS_CODE_INACTIVE);
- }
- }
- }, instanceToTeardown, teardownDelayMs);
+ stopVcnLocked(uuidToTeardown);
+
+ // TODO(b/181789060): invoke asynchronously after Vcn notifies
+ // through VcnCallback
+ notifyAllPermissionedStatusCallbacksLocked(
+ uuidToTeardown, VCN_STATUS_CODE_INACTIVE);
} else {
// If this VCN's status has not changed, update it with the new snapshot
entry.getValue().updateSubscriptionSnapshot(mLastSnapshot);
diff --git a/services/core/java/com/android/server/VpnManagerService.java b/services/core/java/com/android/server/VpnManagerService.java
index a03425c0bb75..7b8cce54c8a7 100644
--- a/services/core/java/com/android/server/VpnManagerService.java
+++ b/services/core/java/com/android/server/VpnManagerService.java
@@ -340,17 +340,18 @@ public class VpnManagerService extends IVpnManager.Stub {
* <p>This is designed to serve the VpnManager only; settings-based VPN profiles are managed
* exclusively by the Settings app, and passed into the platform at startup time.
*
+ * @return A unique key corresponding to this session.
* @throws IllegalArgumentException if no profile was found for the given package name.
* @hide
*/
@Override
- public void startVpnProfile(@NonNull String packageName) {
+ public String startVpnProfile(@NonNull String packageName) {
final int callingUid = Binder.getCallingUid();
verifyCallingUidAndPackage(packageName, callingUid);
final int user = UserHandle.getUserId(callingUid);
synchronized (mVpns) {
throwIfLockdownEnabled();
- mVpns.get(user).startVpnProfile(packageName);
+ return mVpns.get(user).startVpnProfile(packageName);
}
}
@@ -681,7 +682,7 @@ public class VpnManagerService extends IVpnManager.Stub {
intentFilter = new IntentFilter();
intentFilter.addAction(LockdownVpnTracker.ACTION_LOCKDOWN_RESET);
mUserAllContext.registerReceiver(
- mIntentReceiver, intentFilter, NETWORK_STACK, mHandler);
+ mIntentReceiver, intentFilter, NETWORK_STACK, mHandler, Context.RECEIVER_EXPORTED);
}
private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 400b084ee966..ab2147dff853 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -67,6 +67,7 @@ import android.content.pm.ResolveInfo;
import android.content.pm.Signature;
import android.content.pm.UserInfo;
import android.database.Cursor;
+import android.database.sqlite.SQLiteFullException;
import android.database.sqlite.SQLiteStatement;
import android.os.Binder;
import android.os.Bundle;
@@ -5250,7 +5251,7 @@ public class AccountManagerService
logStatement.bindLong(6, userDebugDbInsertionPoint);
try {
logStatement.execute();
- } catch (IllegalStateException e) {
+ } catch (IllegalStateException | SQLiteFullException e) {
// Guard against crash, DB can already be closed
// since this statement is executed on a handler thread
Slog.w(TAG, "Failed to insert a log record. accountId=" + accountId
diff --git a/services/core/java/com/android/server/accounts/OWNERS b/services/core/java/com/android/server/accounts/OWNERS
index 8dcc04a27af6..df1b4f432cd6 100644
--- a/services/core/java/com/android/server/accounts/OWNERS
+++ b/services/core/java/com/android/server/accounts/OWNERS
@@ -1,9 +1 @@
-carlosvaldivia@google.com
-dementyev@google.com
-sandrakwan@google.com
-hackbod@google.com
-svetoslavganov@google.com
-fkupolov@google.com
-yamasani@google.com
-omakoto@google.com
-
+include /core/java/android/accounts/OWNERS
diff --git a/services/core/java/com/android/server/adb/AdbDebuggingManager.java b/services/core/java/com/android/server/adb/AdbDebuggingManager.java
index d91537c8afc6..297d28dadde3 100644
--- a/services/core/java/com/android/server/adb/AdbDebuggingManager.java
+++ b/services/core/java/com/android/server/adb/AdbDebuggingManager.java
@@ -1369,7 +1369,10 @@ public class AdbDebuggingManager {
if (args.length > 1) {
hostname = args[1];
}
- PairDevice device = new PairDevice(fingerprints, hostname, false);
+ PairDevice device = new PairDevice();
+ device.name = fingerprints;
+ device.guid = hostname;
+ device.connected = false;
intent.putExtra(AdbManager.WIRELESS_PAIR_DEVICE_EXTRA, device);
AdbDebuggingManager.sendBroadcastWithDebugPermission(mContext, intent,
UserHandle.ALL);
@@ -1852,8 +1855,11 @@ public class AdbDebuggingManager {
if (args.length > 1) {
hostname = args[1];
}
- pairedDevices.put(keyEntry.getKey(), new PairDevice(
- hostname, fingerprints, mWifiConnectedKeys.contains(keyEntry.getKey())));
+ PairDevice pairDevice = new PairDevice();
+ pairDevice.name = hostname;
+ pairDevice.guid = fingerprints;
+ pairDevice.connected = mWifiConnectedKeys.contains(keyEntry.getKey());
+ pairedDevices.put(keyEntry.getKey(), pairDevice);
}
return pairedDevices;
}
diff --git a/services/core/java/com/android/server/adb/AdbService.java b/services/core/java/com/android/server/adb/AdbService.java
index 5b16daa5e835..2845fbfc6ebf 100644
--- a/services/core/java/com/android/server/adb/AdbService.java
+++ b/services/core/java/com/android/server/adb/AdbService.java
@@ -27,6 +27,8 @@ import android.database.ContentObserver;
import android.debug.AdbManager;
import android.debug.AdbManagerInternal;
import android.debug.AdbTransportType;
+import android.debug.FingerprintAndPairDevice;
+import android.debug.IAdbCallback;
import android.debug.IAdbManager;
import android.debug.IAdbTransport;
import android.debug.PairDevice;
@@ -35,6 +37,7 @@ import android.net.Uri;
import android.os.Binder;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
+import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.SystemProperties;
import android.os.UserHandle;
@@ -87,6 +90,7 @@ public class AdbService extends IAdbManager.Stub {
private final AdbConnectionPortListener mPortListener = new AdbConnectionPortListener();
private AdbDebuggingManager.AdbConnectionPortPoller mConnectionPortPoller;
+ private final RemoteCallbackList<IAdbCallback> mCallbacks = new RemoteCallbackList<>();
/**
* Manages the service lifecycle for {@code AdbService} in {@code SystemServer}.
*/
@@ -348,12 +352,21 @@ public class AdbService extends IAdbManager.Stub {
}
@Override
- public Map<String, PairDevice> getPairedDevices() {
+ public FingerprintAndPairDevice[] getPairedDevices() {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING, null);
- if (mDebuggingManager != null) {
- return mDebuggingManager.getPairedDevices();
- }
- return null;
+ if (mDebuggingManager == null) {
+ return null;
+ }
+ Map<String, PairDevice> map = mDebuggingManager.getPairedDevices();
+ FingerprintAndPairDevice[] ret = new FingerprintAndPairDevice[map.size()];
+ int i = 0;
+ for (Map.Entry<String, PairDevice> entry : map.entrySet()) {
+ ret[i] = new FingerprintAndPairDevice();
+ ret[i].keyFingerprint = entry.getKey();
+ ret[i].device = entry.getValue();
+ i++;
+ }
+ return ret;
}
@Override
@@ -401,6 +414,21 @@ public class AdbService extends IAdbManager.Stub {
return mConnectionPort.get();
}
+ @Override
+ public void registerCallback(IAdbCallback callback) throws RemoteException {
+ if (DEBUG) {
+ Slog.d(TAG, "Registering callback " + callback);
+ }
+ mCallbacks.register(callback);
+ }
+
+ @Override
+ public void unregisterCallback(IAdbCallback callback) throws RemoteException {
+ if (DEBUG) {
+ Slog.d(TAG, "Unregistering callback " + callback);
+ }
+ mCallbacks.unregister(callback);
+ }
/**
* This listener is only used when ro.adb.secure=0. Otherwise, AdbDebuggingManager will
* do this.
@@ -507,6 +535,23 @@ public class AdbService extends IAdbManager.Stub {
if (mDebuggingManager != null) {
mDebuggingManager.setAdbEnabled(enable, transportType);
}
+
+ if (DEBUG) {
+ Slog.d(TAG, "Broadcasting enable = " + enable + ", type = " + transportType);
+ }
+ mCallbacks.broadcast((callback) -> {
+ if (DEBUG) {
+ Slog.d(TAG, "Sending enable = " + enable + ", type = " + transportType
+ + " to " + callback);
+ }
+ try {
+ callback.onDebuggingChanged(enable, transportType);
+ } catch (RemoteException ex) {
+ if (DEBUG) {
+ Slog.d(TAG, "Unable to send onDebuggingChanged:", ex);
+ }
+ }
+ });
}
@Override
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 9a371f3b0b31..09398f377d92 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -4910,7 +4910,9 @@ public final class ActiveServices {
sr.setProcess(null, null, 0, null);
sr.isolatedProc = null;
sr.executeNesting = 0;
- sr.forceClearTracker();
+ synchronized (mAm.mProcessStats.mLock) {
+ sr.forceClearTracker();
+ }
if (mDestroyingServices.remove(sr)) {
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "killServices remove destroying " + sr);
}
@@ -5060,7 +5062,9 @@ public final class ActiveServices {
i--;
ServiceRecord sr = mDestroyingServices.get(i);
if (sr.app == app) {
- sr.forceClearTracker();
+ synchronized (mAm.mProcessStats.mLock) {
+ sr.forceClearTracker();
+ }
mDestroyingServices.remove(i);
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "killServices remove destroying " + sr);
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 330d2ddc0a94..9f59a5fc7253 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -3317,13 +3317,19 @@ public class ActivityManagerService extends IActivityManager.Stub
final int max = SystemProperties.getInt("tombstoned.max_anr_count", 64);
final long now = System.currentTimeMillis();
- Arrays.sort(files, Comparator.comparingLong(File::lastModified).reversed());
- for (int i = 0; i < files.length; ++i) {
- if (i > max || (now - files[i].lastModified()) > DAY_IN_MILLIS) {
- if (!files[i].delete()) {
- Slog.w(TAG, "Unable to prune stale trace file: " + files[i]);
+ try {
+ Arrays.sort(files, Comparator.comparingLong(File::lastModified).reversed());
+ for (int i = 0; i < files.length; ++i) {
+ if (i > max || (now - files[i].lastModified()) > DAY_IN_MILLIS) {
+ if (!files[i].delete()) {
+ Slog.w(TAG, "Unable to prune stale trace file: " + files[i]);
+ }
}
}
+ } catch (IllegalArgumentException e) {
+ // The modification times changed while we were sorting. Bail...
+ // https://issuetracker.google.com/169836837
+ Slog.w(TAG, "tombstone modification times changed while sorting; not pruning", e);
}
}
@@ -3376,7 +3382,7 @@ public class ActivityManagerService extends IActivityManager.Stub
// control of all writes to the file in question.
// We must complete all stack dumps within 20 seconds.
- long remainingTime = 20 * 1000;
+ long remainingTime = 20 * 1000 * Build.HW_TIMEOUT_MULTIPLIER;
// As applications are usually interested with the ANR stack traces, but we can't share with
// them the stack traces other than their own stacks. So after the very first PID is
@@ -5012,8 +5018,9 @@ public class ActivityManagerService extends IActivityManager.Stub
}
@Override
- public void registerIntentSenderCancelListener(IIntentSender sender, IResultReceiver receiver) {
- mPendingIntentController.registerIntentSenderCancelListener(sender, receiver);
+ public boolean registerIntentSenderCancelListenerEx(
+ IIntentSender sender, IResultReceiver receiver) {
+ return mPendingIntentController.registerIntentSenderCancelListener(sender, receiver);
}
@Override
@@ -8374,11 +8381,13 @@ public class ActivityManagerService extends IActivityManager.Stub
if (lines > 0) {
sb.append("\n");
- // Merge several logcat streams, and take the last N lines
InputStreamReader input = null;
try {
java.lang.Process logcat = new ProcessBuilder(
- "/system/bin/timeout", "-k", "15s", "10s",
+ // Time out after 10s, but kill logcat with SEGV
+ // so we can investigate why it didn't finish.
+ "/system/bin/timeout", "-s", "SEGV", "10s",
+ // Merge several logcat streams, and take the last N lines.
"/system/bin/logcat", "-v", "threadtime", "-b", "events", "-b", "system",
"-b", "main", "-b", "crash", "-t", String.valueOf(lines))
.redirectErrorStream(true).start();
diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
index cf4c8a356662..b3b74a109083 100644
--- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
+++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
@@ -16,6 +16,7 @@
package com.android.server.am;
import android.annotation.Nullable;
+import android.app.usage.NetworkStatsManager;
import android.bluetooth.BluetoothActivityEnergyInfo;
import android.bluetooth.BluetoothAdapter;
import android.content.Context;
@@ -711,8 +712,10 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync {
if (wifiInfo.isValid()) {
final long wifiChargeUC = measuredEnergyDeltas != null ?
measuredEnergyDeltas.wifiChargeUC : MeasuredEnergySnapshot.UNAVAILABLE;
- mStats.updateWifiState(
- extractDeltaLocked(wifiInfo), wifiChargeUC, elapsedRealtime, uptime);
+ final NetworkStatsManager networkStatsManager = mInjector.getSystemService(
+ NetworkStatsManager.class);
+ mStats.updateWifiState(extractDeltaLocked(wifiInfo),
+ wifiChargeUC, elapsedRealtime, uptime, networkStatsManager);
} else {
Slog.w(TAG, "wifi info is invalid: " + wifiInfo);
}
@@ -721,8 +724,10 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync {
if (modemInfo != null) {
final long mobileRadioChargeUC = measuredEnergyDeltas != null
? measuredEnergyDeltas.mobileRadioChargeUC : MeasuredEnergySnapshot.UNAVAILABLE;
+ final NetworkStatsManager networkStatsManager = mInjector.getSystemService(
+ NetworkStatsManager.class);
mStats.noteModemControllerActivity(modemInfo, mobileRadioChargeUC, elapsedRealtime,
- uptime);
+ uptime, networkStatsManager);
}
if (updateFlags == UPDATE_ALL) {
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 8d10d562520a..2030b19b6b9a 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -22,6 +22,7 @@ import static android.os.BatteryStats.POWER_DATA_UNAVAILABLE;
import android.annotation.NonNull;
import android.app.StatsManager;
+import android.app.usage.NetworkStatsManager;
import android.bluetooth.BluetoothActivityEnergyInfo;
import android.content.ContentResolver;
import android.content.Context;
@@ -1984,7 +1985,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub
}
@Override
- public void noteResetBleScan() {
+ public void noteBleScanReset() {
enforceCallingPermission();
synchronized (mLock) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
@@ -2025,8 +2026,11 @@ public final class BatteryStatsService extends IBatteryStats.Stub
synchronized (mLock) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
final long uptime = SystemClock.uptimeMillis();
+ final NetworkStatsManager networkStatsManager = mContext.getSystemService(
+ NetworkStatsManager.class);
mHandler.post(() -> {
- mStats.updateWifiState(info, POWER_DATA_UNAVAILABLE, elapsedRealtime, uptime);
+ mStats.updateWifiState(info, POWER_DATA_UNAVAILABLE, elapsedRealtime, uptime,
+ networkStatsManager);
});
}
}
@@ -2063,9 +2067,11 @@ public final class BatteryStatsService extends IBatteryStats.Stub
synchronized (mLock) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
final long uptime = SystemClock.uptimeMillis();
+ final NetworkStatsManager networkStatsManager = mContext.getSystemService(
+ NetworkStatsManager.class);
mHandler.post(() -> {
mStats.noteModemControllerActivity(info, POWER_DATA_UNAVAILABLE, elapsedRealtime,
- uptime);
+ uptime, networkStatsManager);
});
}
}
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index 6bb5aa148e59..c9b07801ff67 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -191,7 +191,9 @@ public final class CachedAppOptimizer {
} else if (KEY_COMPACT_THROTTLE_1.equals(name)
|| KEY_COMPACT_THROTTLE_2.equals(name)
|| KEY_COMPACT_THROTTLE_3.equals(name)
- || KEY_COMPACT_THROTTLE_4.equals(name)) {
+ || KEY_COMPACT_THROTTLE_4.equals(name)
+ || KEY_COMPACT_THROTTLE_5.equals(name)
+ || KEY_COMPACT_THROTTLE_6.equals(name)) {
updateCompactionThrottles();
} else if (KEY_COMPACT_STATSD_SAMPLE_RATE.equals(name)) {
updateCompactStatsdSampleRate();
@@ -647,21 +649,25 @@ public final class CachedAppOptimizer {
char state = (char) fr.read();
if (state == '1' || state == '0') {
+ // Also check freezer binder ioctl
+ getBinderFreezeInfo(Process.myPid());
supported = true;
} else {
Slog.e(TAG_AM, "unexpected value in cgroup.freeze");
}
} catch (java.io.FileNotFoundException e) {
- Slog.d(TAG_AM, "cgroup.freeze not present");
+ Slog.w(TAG_AM, "cgroup.freeze not present");
+ } catch (RuntimeException e) {
+ Slog.w(TAG_AM, "unable to read freezer info");
} catch (Exception e) {
- Slog.d(TAG_AM, "unable to read cgroup.freeze: " + e.toString());
+ Slog.w(TAG_AM, "unable to read cgroup.freeze: " + e.toString());
}
if (fr != null) {
try {
fr.close();
} catch (java.io.IOException e) {
- Slog.e(TAG_AM, "Exception closing freezer.killable: " + e.toString());
+ Slog.e(TAG_AM, "Exception closing cgroup.freeze: " + e.toString());
}
}
diff --git a/services/core/java/com/android/server/am/DataConnectionStats.java b/services/core/java/com/android/server/am/DataConnectionStats.java
index 6e39a4c802d9..651e98c602d9 100644
--- a/services/core/java/com/android/server/am/DataConnectionStats.java
+++ b/services/core/java/com/android/server/am/DataConnectionStats.java
@@ -73,7 +73,8 @@ public class DataConnectionStats extends BroadcastReceiver {
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_SIM_STATE_CHANGED);
- mContext.registerReceiver(this, filter, null /* broadcastPermission */, mListenerHandler);
+ mContext.registerReceiver(this, filter, null /* broadcastPermission */, mListenerHandler,
+ Context.RECEIVER_NOT_EXPORTED);
}
@Override
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 88ca8a5e8356..956f34ffbed8 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -2863,29 +2863,18 @@ public class OomAdjuster {
void setAttachingSchedGroupLSP(ProcessRecord app) {
int initialSchedGroup = ProcessList.SCHED_GROUP_DEFAULT;
final ProcessStateRecord state = app.mState;
- // If the process has been marked as foreground via Zygote.START_FLAG_USE_TOP_APP_PRIORITY,
- // then verify that the top priority is actually is applied.
+ // If the process has been marked as foreground, it is starting as the top app (with
+ // Zygote#START_AS_TOP_APP_ARG), so boost the thread priority of its default UI thread.
if (state.hasForegroundActivities()) {
- String fallbackReason = null;
try {
// The priority must be the same as how does {@link #applyOomAdjLSP} set for
// {@link ProcessList.SCHED_GROUP_TOP_APP}. We don't check render thread because it
// is not ready when attaching.
- if (Process.getProcessGroup(app.getPid()) == THREAD_GROUP_TOP_APP) {
- app.getWindowProcessController().onTopProcChanged();
- setThreadPriority(app.getPid(), THREAD_PRIORITY_TOP_APP_BOOST);
- } else {
- fallbackReason = "not expected top priority";
- }
- } catch (Exception e) {
- fallbackReason = e.toString();
- }
- if (fallbackReason == null) {
+ app.getWindowProcessController().onTopProcChanged();
+ setThreadPriority(app.getPid(), THREAD_PRIORITY_TOP_APP_BOOST);
initialSchedGroup = ProcessList.SCHED_GROUP_TOP_APP;
- } else {
- // The real scheduling group will depend on if there is any component of the process
- // did something during attaching.
- Slog.w(TAG, "Fallback pre-set sched group to default: " + fallbackReason);
+ } catch (Exception e) {
+ Slog.w(TAG, "Failed to pre-set top priority to " + app + " " + e);
}
}
diff --git a/services/core/java/com/android/server/am/OomAdjuster.md b/services/core/java/com/android/server/am/OomAdjuster.md
index eda511ab7369..febc37b6f457 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.md
+++ b/services/core/java/com/android/server/am/OomAdjuster.md
@@ -59,6 +59,7 @@ The update procedure basically consists of 3 parts:
* The next two factors are either it was the previous process with visible UI to the user, or it's a backup agent.
* And then it goes to the massive searches against the service connections and the content providers, each of the clients will be evaluated, and the Oom Adj score could get updated according to its clients' scores. However there are a bunch of service binding flags which could impact the result:
* Below table captures the results with given various service binding states:
+
| Conditon #1 | Condition #2 | Condition #3 | Condition #4 | Result |
|---------------------------------|------------------------------------------------------------|----------------------------------------------|---------------------------------------------------|--------------------------|
| `BIND_WAIVE_PRIORITY` not set | `BIND_ALLOW_OOM_MANAGEMENT` set | Shown UI && Not Home | | Use the app's own Adj |
@@ -83,6 +84,7 @@ The update procedure basically consists of 3 parts:
| | | `BIND_NOT_FOREGROUND` not set | `BIND_IMPORTANT` is set | Sched = top app bound |
| | | | `BIND_IMPORTANT` is NOT set | Sched = default |
* Below table captures the results with given various content provider binding states:
+
| Conditon #1 | Condition #2 | Condition #3 | Result |
|---------------------------------|------------------------------------------------------------|----------------------------------------------|--------------------------|
| Client's process state >= cached| | | Client ProcState = empty |
@@ -95,6 +97,7 @@ The update procedure basically consists of 3 parts:
| Still within retain time | Adj > previous app Adj | | adj = previuos app adj |
| | Process state > last activity | | ProcState = last activity|
* Some additional tweaks after the above ones:
+
| Conditon #1 | Condition #2 | Condition #3 | Result |
|---------------------------------|------------------------------------------------------------|----------------------------------------------|------------------------------------|
| Process state >= cached empty | Has client activities | | ProcState = cached activity client |
diff --git a/services/core/java/com/android/server/am/PendingIntentController.java b/services/core/java/com/android/server/am/PendingIntentController.java
index 534bd84a91a3..c49e6969c2c5 100644
--- a/services/core/java/com/android/server/am/PendingIntentController.java
+++ b/services/core/java/com/android/server/am/PendingIntentController.java
@@ -271,9 +271,11 @@ public class PendingIntentController {
}
}
- void registerIntentSenderCancelListener(IIntentSender sender, IResultReceiver receiver) {
+ boolean registerIntentSenderCancelListener(IIntentSender sender, IResultReceiver receiver) {
if (!(sender instanceof PendingIntentRecord)) {
- return;
+ Slog.w(TAG, "registerIntentSenderCancelListener called on non-PendingIntentRecord");
+ // In this case, it's not "success", but we don't know if it's canceld either.
+ return true;
}
boolean isCancelled;
synchronized (mLock) {
@@ -281,12 +283,9 @@ public class PendingIntentController {
isCancelled = pendingIntent.canceled;
if (!isCancelled) {
pendingIntent.registerCancelListenerLocked(receiver);
- }
- }
- if (isCancelled) {
- try {
- receiver.send(Activity.RESULT_CANCELED, null);
- } catch (RemoteException e) {
+ return true;
+ } else {
+ return false;
}
}
}
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index 2f20efbf5730..c5ac3907ecfe 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -87,12 +87,15 @@ public class SettingsToPropertiesMapper {
DeviceConfig.NAMESPACE_LMKD_NATIVE,
DeviceConfig.NAMESPACE_MEDIA_NATIVE,
DeviceConfig.NAMESPACE_NETD_NATIVE,
+ DeviceConfig.NAMESPACE_NNAPI_NATIVE,
DeviceConfig.NAMESPACE_PROFCOLLECT_NATIVE_BOOT,
DeviceConfig.NAMESPACE_RUNTIME_NATIVE,
DeviceConfig.NAMESPACE_RUNTIME_NATIVE_BOOT,
DeviceConfig.NAMESPACE_STATSD_NATIVE,
DeviceConfig.NAMESPACE_STATSD_NATIVE_BOOT,
DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
+ DeviceConfig.NAMESPACE_TETHERING,
+ DeviceConfig.NAMESPACE_VIRTUALIZATION_FRAMEWORK_NATIVE,
DeviceConfig.NAMESPACE_WINDOW_MANAGER_NATIVE_BOOT,
};
diff --git a/services/core/java/com/android/server/app/OWNERS b/services/core/java/com/android/server/app/OWNERS
new file mode 100644
index 000000000000..aaebbfa8e253
--- /dev/null
+++ b/services/core/java/com/android/server/app/OWNERS
@@ -0,0 +1 @@
+per-file GameManager* = file:/GAME_MANAGER_OWNERS
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index f053e942fe6b..406ff9b00686 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -17,10 +17,8 @@ package com.android.server.audio;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.bluetooth.BluetoothA2dp;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHeadset;
-import android.bluetooth.BluetoothHearingAid;
import android.bluetooth.BluetoothProfile;
import android.content.ContentResolver;
import android.content.Context;
@@ -31,6 +29,7 @@ import android.media.AudioDeviceInfo;
import android.media.AudioManager;
import android.media.AudioRoutesInfo;
import android.media.AudioSystem;
+import android.media.BluetoothProfileConnectionInfo;
import android.media.IAudioRoutesObserver;
import android.media.ICapturePresetDevicesRoleDispatcher;
import android.media.ICommunicationDeviceDispatcher;
@@ -503,29 +502,94 @@ import java.util.concurrent.atomic.AtomicBoolean;
}
}
- /*package*/ static final class BtDeviceConnectionInfo {
+ /*package*/ static final class BleVolumeInfo {
+ final int mIndex;
+ final int mMaxIndex;
+ final int mStreamType;
+
+ BleVolumeInfo(int index, int maxIndex, int streamType) {
+ mIndex = index;
+ mMaxIndex = maxIndex;
+ mStreamType = streamType;
+ }
+ };
+
+ /*package*/ static final class BtDeviceChangedData {
+ final @Nullable BluetoothDevice mNewDevice;
+ final @Nullable BluetoothDevice mPreviousDevice;
+ final @NonNull BluetoothProfileConnectionInfo mInfo;
+ final @NonNull String mEventSource;
+
+ BtDeviceChangedData(@Nullable BluetoothDevice newDevice,
+ @Nullable BluetoothDevice previousDevice,
+ @NonNull BluetoothProfileConnectionInfo info, @NonNull String eventSource) {
+ mNewDevice = newDevice;
+ mPreviousDevice = previousDevice;
+ mInfo = info;
+ mEventSource = eventSource;
+ }
+
+ @Override
+ public String toString() {
+ return "BtDeviceChangedData profile=" + BluetoothProfile.getProfileName(
+ mInfo.getProfile())
+ + ", switch device: [" + mPreviousDevice + "] -> [" + mNewDevice + "]";
+ }
+ }
+
+ /*package*/ static final class BtDeviceInfo {
final @NonNull BluetoothDevice mDevice;
final @AudioService.BtProfileConnectionState int mState;
- final int mProfile;
+ final @AudioService.BtProfile int mProfile;
final boolean mSupprNoisy;
final int mVolume;
+ final boolean mIsLeOutput;
+ final @NonNull String mEventSource;
+ final @AudioSystem.AudioFormatNativeEnumForBtCodec int mCodec;
+ final int mAudioSystemDevice;
+ final int mMusicDevice;
- BtDeviceConnectionInfo(@NonNull BluetoothDevice device,
- @AudioService.BtProfileConnectionState int state,
- int profile, boolean suppressNoisyIntent, int vol) {
+ BtDeviceInfo(@NonNull BtDeviceChangedData d, @NonNull BluetoothDevice device, int state,
+ int audioDevice, @AudioSystem.AudioFormatNativeEnumForBtCodec int codec) {
mDevice = device;
mState = state;
+ mProfile = d.mInfo.getProfile();
+ mSupprNoisy = d.mInfo.isSuppressNoisyIntent();
+ mVolume = d.mInfo.getVolume();
+ mIsLeOutput = d.mInfo.isLeOutput();
+ mEventSource = d.mEventSource;
+ mAudioSystemDevice = audioDevice;
+ mMusicDevice = AudioSystem.DEVICE_NONE;
+ mCodec = codec;
+ }
+
+ // constructor used by AudioDeviceBroker to search similar message
+ BtDeviceInfo(@NonNull BluetoothDevice device, int profile) {
+ mDevice = device;
mProfile = profile;
- mSupprNoisy = suppressNoisyIntent;
- mVolume = vol;
- }
-
- BtDeviceConnectionInfo(@NonNull BtDeviceConnectionInfo info) {
- mDevice = info.mDevice;
- mState = info.mState;
- mProfile = info.mProfile;
- mSupprNoisy = info.mSupprNoisy;
- mVolume = info.mVolume;
+ mEventSource = "";
+ mMusicDevice = AudioSystem.DEVICE_NONE;
+ mCodec = AudioSystem.AUDIO_FORMAT_DEFAULT;
+ mAudioSystemDevice = 0;
+ mState = 0;
+ mSupprNoisy = false;
+ mVolume = -1;
+ mIsLeOutput = false;
+ }
+
+ // constructor used by AudioDeviceInventory when config change failed
+ BtDeviceInfo(@NonNull BluetoothDevice device, int profile, int state, int musicDevice,
+ int audioSystemDevice) {
+ mDevice = device;
+ mProfile = profile;
+ mEventSource = "";
+ mMusicDevice = musicDevice;
+ mCodec = AudioSystem.AUDIO_FORMAT_DEFAULT;
+ mAudioSystemDevice = audioSystemDevice;
+ mState = state;
+ mSupprNoisy = false;
+ mVolume = -1;
+ mIsLeOutput = false;
}
// redefine equality op so we can match messages intended for this device
@@ -537,16 +601,52 @@ import java.util.concurrent.atomic.AtomicBoolean;
if (this == o) {
return true;
}
- if (o instanceof BtDeviceConnectionInfo) {
- return mDevice.equals(((BtDeviceConnectionInfo) o).mDevice);
+ if (o instanceof BtDeviceInfo) {
+ return mProfile == ((BtDeviceInfo) o).mProfile
+ && mDevice.equals(((BtDeviceInfo) o).mDevice);
}
return false;
}
+ }
- @Override
- public String toString() {
- return "BtDeviceConnectionInfo dev=" + mDevice.toString();
+ BtDeviceInfo createBtDeviceInfo(@NonNull BtDeviceChangedData d, @NonNull BluetoothDevice device,
+ int state) {
+ int audioDevice;
+ int codec = AudioSystem.AUDIO_FORMAT_DEFAULT;
+ switch (d.mInfo.getProfile()) {
+ case BluetoothProfile.A2DP_SINK:
+ audioDevice = AudioSystem.DEVICE_IN_BLUETOOTH_A2DP;
+ break;
+ case BluetoothProfile.A2DP:
+ audioDevice = AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP;
+ synchronized (mDeviceStateLock) {
+ codec = mBtHelper.getA2dpCodec(device);
+ }
+ break;
+ case BluetoothProfile.HEARING_AID:
+ audioDevice = AudioSystem.DEVICE_OUT_HEARING_AID;
+ break;
+ case BluetoothProfile.LE_AUDIO:
+ if (d.mInfo.isLeOutput()) {
+ audioDevice = AudioSystem.DEVICE_OUT_BLE_HEADSET;
+ } else {
+ audioDevice = AudioSystem.DEVICE_IN_BLE_HEADSET;
+ }
+ break;
+ default: throw new IllegalArgumentException("Invalid profile " + d.mInfo.getProfile());
}
+ return new BtDeviceInfo(d, device, state, audioDevice, codec);
+ }
+
+ private void btMediaMetricRecord(@NonNull BluetoothDevice device, String state,
+ @NonNull BtDeviceChangedData data) {
+ final String name = TextUtils.emptyIfNull(device.getName());
+ new MediaMetrics.Item(MediaMetrics.Name.AUDIO_DEVICE + MediaMetrics.SEPARATOR
+ + "queueOnBluetoothActiveDeviceChanged")
+ .set(MediaMetrics.Property.STATE, state)
+ .set(MediaMetrics.Property.STATUS, data.mInfo.getProfile())
+ .set(MediaMetrics.Property.NAME, name)
+ .record();
}
/**
@@ -554,85 +654,37 @@ import java.util.concurrent.atomic.AtomicBoolean;
* not just a simple message post
* @param info struct with the (dis)connection information
*/
- /*package*/ void queueBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
- @NonNull BtDeviceConnectionInfo info) {
- final String name = TextUtils.emptyIfNull(info.mDevice.getName());
- new MediaMetrics.Item(MediaMetrics.Name.AUDIO_DEVICE + MediaMetrics.SEPARATOR
- + "postBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent")
- .set(MediaMetrics.Property.STATE, info.mState == BluetoothProfile.STATE_CONNECTED
- ? MediaMetrics.Value.CONNECTED : MediaMetrics.Value.DISCONNECTED)
- .set(MediaMetrics.Property.INDEX, info.mVolume)
- .set(MediaMetrics.Property.NAME, name)
- .record();
-
- // operations of removing and posting messages related to A2DP device state change must be
- // mutually exclusive
- synchronized (mDeviceStateLock) {
- // when receiving a request to change the connection state of a device, this last
- // request is the source of truth, so cancel all previous requests that are already in
- // the handler
- removeScheduledA2dpEvents(info.mDevice);
-
- sendLMsgNoDelay(
- info.mState == BluetoothProfile.STATE_CONNECTED
- ? MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_CONNECTION
- : MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_DISCONNECTION,
- SENDMSG_QUEUE, info);
- }
- }
-
- /** remove all previously scheduled connection and state change events for the given device */
- @GuardedBy("mDeviceStateLock")
- private void removeScheduledA2dpEvents(@NonNull BluetoothDevice device) {
- mBrokerHandler.removeEqualMessages(MSG_L_A2DP_DEVICE_CONFIG_CHANGE, device);
-
- final BtDeviceConnectionInfo connectionInfoToRemove = new BtDeviceConnectionInfo(device,
- // the next parameters of the constructor will be ignored when finding the message
- // to remove as the equality of the message's object is tested on the device itself
- // (see BtDeviceConnectionInfo.equals() method override)
- BluetoothProfile.STATE_CONNECTED, 0, false, -1);
- mBrokerHandler.removeEqualMessages(MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_DISCONNECTION,
- connectionInfoToRemove);
- mBrokerHandler.removeEqualMessages(MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_CONNECTION,
- connectionInfoToRemove);
-
- final BtHelper.BluetoothA2dpDeviceInfo devInfoToRemove =
- new BtHelper.BluetoothA2dpDeviceInfo(device);
- mBrokerHandler.removeEqualMessages(MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_CONNECTED,
- devInfoToRemove);
- mBrokerHandler.removeEqualMessages(MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED,
- devInfoToRemove);
- mBrokerHandler.removeEqualMessages(MSG_L_A2DP_ACTIVE_DEVICE_CHANGE,
- devInfoToRemove);
- }
-
- private static final class HearingAidDeviceConnectionInfo {
- final @NonNull BluetoothDevice mDevice;
- final @AudioService.BtProfileConnectionState int mState;
- final boolean mSupprNoisy;
- final int mMusicDevice;
- final @NonNull String mEventSource;
-
- HearingAidDeviceConnectionInfo(@NonNull BluetoothDevice device,
- @AudioService.BtProfileConnectionState int state,
- boolean suppressNoisyIntent, int musicDevice, @NonNull String eventSource) {
- mDevice = device;
- mState = state;
- mSupprNoisy = suppressNoisyIntent;
- mMusicDevice = musicDevice;
- mEventSource = eventSource;
+ /*package*/ void queueOnBluetoothActiveDeviceChanged(@NonNull BtDeviceChangedData data) {
+ if (data.mInfo.getProfile() == BluetoothProfile.A2DP && data.mPreviousDevice != null
+ && data.mPreviousDevice.equals(data.mNewDevice)) {
+ final String name = TextUtils.emptyIfNull(data.mNewDevice.getName());
+ new MediaMetrics.Item(MediaMetrics.Name.AUDIO_DEVICE + MediaMetrics.SEPARATOR
+ + "queueOnBluetoothActiveDeviceChanged_update")
+ .set(MediaMetrics.Property.NAME, name)
+ .set(MediaMetrics.Property.STATUS, data.mInfo.getProfile())
+ .record();
+ synchronized (mDeviceStateLock) {
+ postBluetoothA2dpDeviceConfigChange(data.mNewDevice);
+ }
+ } else {
+ synchronized (mDeviceStateLock) {
+ if (data.mPreviousDevice != null) {
+ btMediaMetricRecord(data.mPreviousDevice, MediaMetrics.Value.DISCONNECTED,
+ data);
+ sendLMsgNoDelay(MSG_L_BT_ACTIVE_DEVICE_CHANGE_EXT, SENDMSG_QUEUE,
+ createBtDeviceInfo(data, data.mPreviousDevice,
+ BluetoothProfile.STATE_DISCONNECTED));
+ }
+ if (data.mNewDevice != null) {
+ btMediaMetricRecord(data.mNewDevice, MediaMetrics.Value.CONNECTED, data);
+ sendLMsgNoDelay(MSG_L_BT_ACTIVE_DEVICE_CHANGE_EXT, SENDMSG_QUEUE,
+ createBtDeviceInfo(data, data.mNewDevice,
+ BluetoothProfile.STATE_CONNECTED));
+ }
+ }
}
}
- /*package*/ void postBluetoothHearingAidDeviceConnectionState(
- @NonNull BluetoothDevice device, @AudioService.BtProfileConnectionState int state,
- boolean suppressNoisyIntent, int musicDevice, @NonNull String eventSource) {
- final HearingAidDeviceConnectionInfo info = new HearingAidDeviceConnectionInfo(
- device, state, suppressNoisyIntent, musicDevice, eventSource);
- sendLMsgNoDelay(MSG_L_HEARING_AID_DEVICE_CONNECTION_CHANGE_EXT, SENDMSG_QUEUE, info);
- }
-
-
/**
* Current Bluetooth SCO audio active state indicated by BtHelper via setBluetoothScoOn().
*/
@@ -680,6 +732,11 @@ import java.util.concurrent.atomic.AtomicBoolean;
sendIIMsgNoDelay(MSG_II_SET_HEARING_AID_VOLUME, SENDMSG_REPLACE, index, streamType);
}
+ /*package*/ void postSetLeAudioVolumeIndex(int index, int maxIndex, int streamType) {
+ BleVolumeInfo info = new BleVolumeInfo(index, maxIndex, streamType);
+ sendLMsgNoDelay(MSG_II_SET_LE_AUDIO_OUT_VOLUME, SENDMSG_REPLACE, info);
+ }
+
/*package*/ void postSetModeOwnerPid(int pid, int mode) {
sendIIMsgNoDelay(MSG_I_SET_MODE_OWNER_PID, SENDMSG_REPLACE, pid, mode);
}
@@ -820,6 +877,10 @@ import java.util.concurrent.atomic.AtomicBoolean;
return mAudioService.getVssVolumeForDevice(streamType, device);
}
+ /*package*/ int getMaxVssVolumeForStream(int streamType) {
+ return mAudioService.getMaxVssVolumeForStream(streamType);
+ }
+
/*package*/ int getDeviceForStream(int streamType) {
return mAudioService.getDeviceForStream(streamType);
}
@@ -873,19 +934,8 @@ import java.util.concurrent.atomic.AtomicBoolean;
}
@GuardedBy("mDeviceStateLock")
- /*package*/ void postA2dpSinkConnection(@AudioService.BtProfileConnectionState int state,
- @NonNull BtHelper.BluetoothA2dpDeviceInfo btDeviceInfo, int delay) {
- sendILMsg(state == BluetoothA2dp.STATE_CONNECTED
- ? MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_CONNECTED
- : MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED,
- SENDMSG_QUEUE,
- state, btDeviceInfo, delay);
- }
-
- /*package*/ void postA2dpSourceConnection(@AudioService.BtProfileConnectionState int state,
- @NonNull BtHelper.BluetoothA2dpDeviceInfo btDeviceInfo, int delay) {
- sendILMsg(MSG_IL_SET_A2DP_SOURCE_CONNECTION_STATE, SENDMSG_QUEUE,
- state, btDeviceInfo, delay);
+ /*package*/ void postBluetoothActiveDevice(BtDeviceInfo info, int delay) {
+ sendLMsg(MSG_L_SET_BT_ACTIVE_DEVICE, SENDMSG_QUEUE, info, delay);
}
/*package*/ void postSetWiredDeviceConnectionState(
@@ -893,46 +943,12 @@ import java.util.concurrent.atomic.AtomicBoolean;
sendLMsg(MSG_L_SET_WIRED_DEVICE_CONNECTION_STATE, SENDMSG_QUEUE, connectionState, delay);
}
- /*package*/ void postSetHearingAidConnectionState(
- @AudioService.BtProfileConnectionState int state,
- @NonNull BluetoothDevice device, int delay) {
- sendILMsg(MSG_IL_SET_HEARING_AID_CONNECTION_STATE, SENDMSG_QUEUE,
- state,
- device,
- delay);
+ /*package*/ void postBtProfileDisconnected(int profile) {
+ sendIMsgNoDelay(MSG_I_BT_SERVICE_DISCONNECTED_PROFILE, SENDMSG_QUEUE, profile);
}
- /*package*/ void postDisconnectA2dp() {
- sendMsgNoDelay(MSG_DISCONNECT_A2DP, SENDMSG_QUEUE);
- }
-
- /*package*/ void postDisconnectA2dpSink() {
- sendMsgNoDelay(MSG_DISCONNECT_A2DP_SINK, SENDMSG_QUEUE);
- }
-
- /*package*/ void postDisconnectHearingAid() {
- sendMsgNoDelay(MSG_DISCONNECT_BT_HEARING_AID, SENDMSG_QUEUE);
- }
-
- /*package*/ void postDisconnectHeadset() {
- sendMsgNoDelay(MSG_DISCONNECT_BT_HEADSET, SENDMSG_QUEUE);
- }
-
- /*package*/ void postBtA2dpProfileConnected(BluetoothA2dp a2dpProfile) {
- sendLMsgNoDelay(MSG_L_BT_SERVICE_CONNECTED_PROFILE_A2DP, SENDMSG_QUEUE, a2dpProfile);
- }
-
- /*package*/ void postBtA2dpSinkProfileConnected(BluetoothProfile profile) {
- sendLMsgNoDelay(MSG_L_BT_SERVICE_CONNECTED_PROFILE_A2DP_SINK, SENDMSG_QUEUE, profile);
- }
-
- /*package*/ void postBtHeasetProfileConnected(BluetoothHeadset headsetProfile) {
- sendLMsgNoDelay(MSG_L_BT_SERVICE_CONNECTED_PROFILE_HEADSET, SENDMSG_QUEUE, headsetProfile);
- }
-
- /*package*/ void postBtHearingAidProfileConnected(BluetoothHearingAid hearingAidProfile) {
- sendLMsgNoDelay(MSG_L_BT_SERVICE_CONNECTED_PROFILE_HEARING_AID, SENDMSG_QUEUE,
- hearingAidProfile);
+ /*package*/ void postBtProfileConnected(int profile, BluetoothProfile proxy) {
+ sendILMsgNoDelay(MSG_IL_BT_SERVICE_CONNECTED_PROFILE, SENDMSG_QUEUE, profile, proxy);
}
/*package*/ void postCommunicationRouteClientDied(CommunicationRouteClient client) {
@@ -990,13 +1006,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
}
}
- /*package*/ void postSetA2dpSourceConnectionState(@BluetoothProfile.BtProfileState int state,
- @NonNull BtHelper.BluetoothA2dpDeviceInfo btDeviceInfo) {
- final int intState = (state == BluetoothA2dp.STATE_CONNECTED) ? 1 : 0;
- sendILMsgNoDelay(MSG_IL_SET_A2DP_SOURCE_CONNECTION_STATE, SENDMSG_QUEUE, state,
- btDeviceInfo);
- }
-
/*package*/ void handleFailureToConnectToBtHeadsetService(int delay) {
sendMsg(MSG_BT_HEADSET_CNCT_FAILED, SENDMSG_REPLACE, delay);
}
@@ -1009,19 +1018,10 @@ import java.util.concurrent.atomic.AtomicBoolean;
sendMsgNoDelay(fromA2dp ? MSG_REPORT_NEW_ROUTES_A2DP : MSG_REPORT_NEW_ROUTES, SENDMSG_NOOP);
}
- /*package*/ void postA2dpActiveDeviceChange(
- @NonNull BtHelper.BluetoothA2dpDeviceInfo btDeviceInfo) {
- sendLMsgNoDelay(MSG_L_A2DP_ACTIVE_DEVICE_CHANGE, SENDMSG_QUEUE, btDeviceInfo);
- }
-
// must be called synchronized on mConnectedDevices
- /*package*/ boolean hasScheduledA2dpSinkConnectionState(BluetoothDevice btDevice) {
- final BtHelper.BluetoothA2dpDeviceInfo devInfoToCheck =
- new BtHelper.BluetoothA2dpDeviceInfo(btDevice);
- return (mBrokerHandler.hasEqualMessages(
- MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_CONNECTED, devInfoToCheck)
- || mBrokerHandler.hasEqualMessages(
- MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED, devInfoToCheck));
+ /*package*/ boolean hasScheduledA2dpConnection(BluetoothDevice btDevice) {
+ final BtDeviceInfo devInfoToCheck = new BtDeviceInfo(btDevice, BluetoothProfile.A2DP);
+ return mBrokerHandler.hasEqualMessages(MSG_L_SET_BT_ACTIVE_DEVICE, devInfoToCheck);
}
/*package*/ void setA2dpTimeout(String address, int a2dpCodec, int delayMs) {
@@ -1045,12 +1045,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
}
}
- /*package*/ int getA2dpCodec(@NonNull BluetoothDevice device) {
- synchronized (mDeviceStateLock) {
- return mBtHelper.getA2dpCodec(device);
- }
- }
-
/*package*/ void broadcastStickyIntentToCurrentProfileGroup(Intent intent) {
mSystemServer.broadcastStickyIntentToCurrentProfileGroup(intent);
}
@@ -1206,24 +1200,10 @@ import java.util.concurrent.atomic.AtomicBoolean;
mDeviceInventory.onReportNewRoutes();
}
break;
- case MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_CONNECTED:
- case MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED:
- synchronized (mDeviceStateLock) {
- mDeviceInventory.onSetA2dpSinkConnectionState(
- (BtHelper.BluetoothA2dpDeviceInfo) msg.obj, msg.arg1);
- }
- break;
- case MSG_IL_SET_A2DP_SOURCE_CONNECTION_STATE:
+ case MSG_L_SET_BT_ACTIVE_DEVICE:
synchronized (mDeviceStateLock) {
- mDeviceInventory.onSetA2dpSourceConnectionState(
- (BtHelper.BluetoothA2dpDeviceInfo) msg.obj, msg.arg1);
- }
- break;
- case MSG_IL_SET_HEARING_AID_CONNECTION_STATE:
- synchronized (mDeviceStateLock) {
- mDeviceInventory.onSetHearingAidConnectionState(
- (BluetoothDevice) msg.obj, msg.arg1,
- mAudioService.getHearingAidStreamType());
+ mDeviceInventory.onSetBtActiveDevice((BtDeviceInfo) msg.obj,
+ mAudioService.getBluetoothContextualVolumeStream());
}
break;
case MSG_BT_HEADSET_CNCT_FAILED:
@@ -1240,14 +1220,10 @@ import java.util.concurrent.atomic.AtomicBoolean;
}
break;
case MSG_L_A2DP_DEVICE_CONFIG_CHANGE:
- final int a2dpCodec;
final BluetoothDevice btDevice = (BluetoothDevice) msg.obj;
synchronized (mDeviceStateLock) {
- a2dpCodec = mBtHelper.getA2dpCodec(btDevice);
- // TODO: name of method being called on AudioDeviceInventory is currently
- // misleading (config change vs active device change), to be
- // reconciliated once the BT side has been updated.
- mDeviceInventory.onBluetoothA2dpActiveDeviceChange(
+ final int a2dpCodec = mBtHelper.getA2dpCodec(btDevice);
+ mDeviceInventory.onBluetoothA2dpDeviceConfigChange(
new BtHelper.BluetoothA2dpDeviceInfo(btDevice, -1, a2dpCodec),
BtHelper.EVENT_DEVICE_CONFIG_CHANGE);
}
@@ -1260,6 +1236,12 @@ import java.util.concurrent.atomic.AtomicBoolean;
mBtHelper.setHearingAidVolume(msg.arg1, msg.arg2);
}
break;
+ case MSG_II_SET_LE_AUDIO_OUT_VOLUME: {
+ final BleVolumeInfo info = (BleVolumeInfo) msg.obj;
+ synchronized (mDeviceStateLock) {
+ mBtHelper.setLeAudioVolume(info.mIndex, info.mMaxIndex, info.mStreamType);
+ }
+ } break;
case MSG_I_SET_AVRCP_ABSOLUTE_VOLUME:
synchronized (mDeviceStateLock) {
mBtHelper.setAvrcpAbsoluteVolumeIndex(msg.arg1);
@@ -1294,85 +1276,47 @@ import java.util.concurrent.atomic.AtomicBoolean;
mDeviceInventory.onToggleHdmi();
}
break;
- case MSG_L_A2DP_ACTIVE_DEVICE_CHANGE:
- synchronized (mDeviceStateLock) {
- mDeviceInventory.onBluetoothA2dpActiveDeviceChange(
- (BtHelper.BluetoothA2dpDeviceInfo) msg.obj,
- BtHelper.EVENT_ACTIVE_DEVICE_CHANGE);
- }
- break;
- case MSG_DISCONNECT_A2DP:
- synchronized (mDeviceStateLock) {
- mDeviceInventory.disconnectA2dp();
- }
- break;
- case MSG_DISCONNECT_A2DP_SINK:
- synchronized (mDeviceStateLock) {
- mDeviceInventory.disconnectA2dpSink();
- }
- break;
- case MSG_DISCONNECT_BT_HEARING_AID:
- synchronized (mDeviceStateLock) {
- mDeviceInventory.disconnectHearingAid();
- }
- break;
- case MSG_DISCONNECT_BT_HEADSET:
- synchronized (mSetModeLock) {
+ case MSG_I_BT_SERVICE_DISCONNECTED_PROFILE:
+ if (msg.arg1 != BluetoothProfile.HEADSET) {
synchronized (mDeviceStateLock) {
- mBtHelper.disconnectHeadset();
+ mDeviceInventory.onBtProfileDisconnected(msg.arg1);
+ }
+ } else {
+ synchronized (mSetModeLock) {
+ synchronized (mDeviceStateLock) {
+ mBtHelper.disconnectHeadset();
+ }
}
}
break;
- case MSG_L_BT_SERVICE_CONNECTED_PROFILE_A2DP:
- synchronized (mDeviceStateLock) {
- mBtHelper.onA2dpProfileConnected((BluetoothA2dp) msg.obj);
- }
- break;
- case MSG_L_BT_SERVICE_CONNECTED_PROFILE_A2DP_SINK:
- synchronized (mDeviceStateLock) {
- mBtHelper.onA2dpSinkProfileConnected((BluetoothProfile) msg.obj);
- }
- break;
- case MSG_L_BT_SERVICE_CONNECTED_PROFILE_HEARING_AID:
- synchronized (mDeviceStateLock) {
- mBtHelper.onHearingAidProfileConnected((BluetoothHearingAid) msg.obj);
- }
- break;
- case MSG_L_BT_SERVICE_CONNECTED_PROFILE_HEADSET:
- synchronized (mSetModeLock) {
+ case MSG_IL_BT_SERVICE_CONNECTED_PROFILE:
+ if (msg.arg1 != BluetoothProfile.HEADSET) {
synchronized (mDeviceStateLock) {
- mBtHelper.onHeadsetProfileConnected((BluetoothHeadset) msg.obj);
+ mBtHelper.onBtProfileConnected(msg.arg1, (BluetoothProfile) msg.obj);
+ }
+ } else {
+ synchronized (mSetModeLock) {
+ synchronized (mDeviceStateLock) {
+ mBtHelper.onHeadsetProfileConnected((BluetoothHeadset) msg.obj);
+ }
}
}
break;
- case MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_CONNECTION:
- case MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_DISCONNECTION: {
- final BtDeviceConnectionInfo info = (BtDeviceConnectionInfo) msg.obj;
+ case MSG_L_BT_ACTIVE_DEVICE_CHANGE_EXT: {
+ final BtDeviceInfo info = (BtDeviceInfo) msg.obj;
+ if (info.mDevice == null) break;
AudioService.sDeviceLogger.log((new AudioEventLogger.StringEvent(
- "msg: setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent "
+ "msg: onBluetoothActiveDeviceChange "
+ " state=" + info.mState
// only querying address as this is the only readily available
// field on the device
+ " addr=" + info.mDevice.getAddress()
- + " prof=" + info.mProfile + " supprNoisy=" + info.mSupprNoisy
- + " vol=" + info.mVolume)).printLog(TAG));
- synchronized (mDeviceStateLock) {
- mDeviceInventory.setBluetoothA2dpDeviceConnectionState(
- info.mDevice, info.mState, info.mProfile, info.mSupprNoisy,
- AudioSystem.DEVICE_NONE, info.mVolume);
- }
- } break;
- case MSG_L_HEARING_AID_DEVICE_CONNECTION_CHANGE_EXT: {
- final HearingAidDeviceConnectionInfo info =
- (HearingAidDeviceConnectionInfo) msg.obj;
- AudioService.sDeviceLogger.log((new AudioEventLogger.StringEvent(
- "msg: setHearingAidDeviceConnectionState state=" + info.mState
- + " addr=" + info.mDevice.getAddress()
+ + " prof=" + info.mProfile
+ " supprNoisy=" + info.mSupprNoisy
- + " src=" + info.mEventSource)).printLog(TAG));
+ + " src=" + info.mEventSource
+ )).printLog(TAG));
synchronized (mDeviceStateLock) {
- mDeviceInventory.setBluetoothHearingAidDeviceConnectionState(
- info.mDevice, info.mState, info.mSupprNoisy, info.mMusicDevice);
+ mDeviceInventory.setBluetoothActiveDevice(info);
}
} break;
case MSG_IL_SAVE_PREF_DEVICES_FOR_STRATEGY: {
@@ -1445,8 +1389,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
private static final int MSG_IIL_SET_FORCE_USE = 4;
private static final int MSG_IIL_SET_FORCE_BT_A2DP_USE = 5;
private static final int MSG_TOGGLE_HDMI = 6;
- private static final int MSG_IL_SET_A2DP_SOURCE_CONNECTION_STATE = 7;
- private static final int MSG_IL_SET_HEARING_AID_CONNECTION_STATE = 8;
+ private static final int MSG_L_SET_BT_ACTIVE_DEVICE = 7;
private static final int MSG_BT_HEADSET_CNCT_FAILED = 9;
private static final int MSG_IL_BTA2DP_TIMEOUT = 10;
@@ -1459,25 +1402,11 @@ import java.util.concurrent.atomic.AtomicBoolean;
private static final int MSG_I_SET_AVRCP_ABSOLUTE_VOLUME = 15;
private static final int MSG_I_SET_MODE_OWNER_PID = 16;
- // process active A2DP device change, obj is BtHelper.BluetoothA2dpDeviceInfo
- private static final int MSG_L_A2DP_ACTIVE_DEVICE_CHANGE = 18;
-
- private static final int MSG_DISCONNECT_A2DP = 19;
- private static final int MSG_DISCONNECT_A2DP_SINK = 20;
- private static final int MSG_DISCONNECT_BT_HEARING_AID = 21;
- private static final int MSG_DISCONNECT_BT_HEADSET = 22;
- private static final int MSG_L_BT_SERVICE_CONNECTED_PROFILE_A2DP = 23;
- private static final int MSG_L_BT_SERVICE_CONNECTED_PROFILE_A2DP_SINK = 24;
- private static final int MSG_L_BT_SERVICE_CONNECTED_PROFILE_HEARING_AID = 25;
- private static final int MSG_L_BT_SERVICE_CONNECTED_PROFILE_HEADSET = 26;
-
- // process change of state, obj is BtHelper.BluetoothA2dpDeviceInfo
- private static final int MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_CONNECTED = 27;
- private static final int MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED = 28;
+ private static final int MSG_I_BT_SERVICE_DISCONNECTED_PROFILE = 22;
+ private static final int MSG_IL_BT_SERVICE_CONNECTED_PROFILE = 23;
// process external command to (dis)connect an A2DP device, obj is BtDeviceConnectionInfo
- private static final int MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_CONNECTION = 29;
- private static final int MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_DISCONNECTION = 30;
+ private static final int MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT = 29;
// process external command to (dis)connect a hearing aid device
private static final int MSG_L_HEARING_AID_DEVICE_CONNECTION_CHANGE_EXT = 31;
@@ -1496,19 +1425,19 @@ import java.util.concurrent.atomic.AtomicBoolean;
private static final int MSG_IL_SET_PREF_DEVICES_FOR_STRATEGY = 40;
private static final int MSG_I_REMOVE_PREF_DEVICES_FOR_STRATEGY = 41;
+ private static final int MSG_L_BT_ACTIVE_DEVICE_CHANGE_EXT = 45;
+ //
+ // process set volume for Le Audio, obj is BleVolumeInfo
+ private static final int MSG_II_SET_LE_AUDIO_OUT_VOLUME = 46;
+
private static boolean isMessageHandledUnderWakelock(int msgId) {
switch(msgId) {
case MSG_L_SET_WIRED_DEVICE_CONNECTION_STATE:
- case MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_CONNECTED:
- case MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED:
- case MSG_IL_SET_A2DP_SOURCE_CONNECTION_STATE:
- case MSG_IL_SET_HEARING_AID_CONNECTION_STATE:
+ case MSG_L_SET_BT_ACTIVE_DEVICE:
case MSG_IL_BTA2DP_TIMEOUT:
case MSG_L_A2DP_DEVICE_CONFIG_CHANGE:
case MSG_TOGGLE_HDMI:
- case MSG_L_A2DP_ACTIVE_DEVICE_CHANGE:
- case MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_CONNECTION:
- case MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_DISCONNECTION:
+ case MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT:
case MSG_L_HEARING_AID_DEVICE_CONNECTION_CHANGE_EXT:
case MSG_CHECK_MUTE_MUSIC:
return true;
@@ -1593,14 +1522,10 @@ import java.util.concurrent.atomic.AtomicBoolean;
long time = SystemClock.uptimeMillis() + delay;
switch (msg) {
- case MSG_IL_SET_A2DP_SOURCE_CONNECTION_STATE:
- case MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_CONNECTED:
- case MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED:
- case MSG_IL_SET_HEARING_AID_CONNECTION_STATE:
+ case MSG_L_SET_BT_ACTIVE_DEVICE:
case MSG_L_SET_WIRED_DEVICE_CONNECTION_STATE:
case MSG_IL_BTA2DP_TIMEOUT:
case MSG_L_A2DP_DEVICE_CONFIG_CHANGE:
- case MSG_L_A2DP_ACTIVE_DEVICE_CHANGE:
if (sLastDeviceConnectMsgTime >= time) {
// add a little delay to make sure messages are ordered as expected
time = sLastDeviceConnectMsgTime + 30;
@@ -1619,12 +1544,9 @@ import java.util.concurrent.atomic.AtomicBoolean;
private static final Set<Integer> MESSAGES_MUTE_MUSIC;
static {
MESSAGES_MUTE_MUSIC = new HashSet<>();
- MESSAGES_MUTE_MUSIC.add(MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_CONNECTED);
- MESSAGES_MUTE_MUSIC.add(MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED);
+ MESSAGES_MUTE_MUSIC.add(MSG_L_SET_BT_ACTIVE_DEVICE);
MESSAGES_MUTE_MUSIC.add(MSG_L_A2DP_DEVICE_CONFIG_CHANGE);
- MESSAGES_MUTE_MUSIC.add(MSG_L_A2DP_ACTIVE_DEVICE_CHANGE);
- MESSAGES_MUTE_MUSIC.add(MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_CONNECTION);
- MESSAGES_MUTE_MUSIC.add(MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_DISCONNECTION);
+ MESSAGES_MUTE_MUSIC.add(MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT);
MESSAGES_MUTE_MUSIC.add(MSG_IIL_SET_FORCE_BT_A2DP_USE);
MESSAGES_MUTE_MUSIC.add(MSG_REPORT_NEW_ROUTES_A2DP);
}
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index 5288a04ce86e..de4161163723 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -16,10 +16,8 @@
package com.android.server.audio;
import android.annotation.NonNull;
-import android.bluetooth.BluetoothA2dp;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
-import android.bluetooth.BluetoothHearingAid;
import android.bluetooth.BluetoothProfile;
import android.content.Intent;
import android.media.AudioDeviceAttributes;
@@ -288,147 +286,102 @@ public class AudioDeviceInventory {
}
}
- // only public for mocking/spying
@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
- @VisibleForTesting
- public void onSetA2dpSinkConnectionState(@NonNull BtHelper.BluetoothA2dpDeviceInfo btInfo,
- @AudioService.BtProfileConnectionState int state) {
- final BluetoothDevice btDevice = btInfo.getBtDevice();
- int a2dpVolume = btInfo.getVolume();
+ void onSetBtActiveDevice(@NonNull AudioDeviceBroker.BtDeviceInfo btInfo, int streamType) {
if (AudioService.DEBUG_DEVICES) {
- Log.d(TAG, "onSetA2dpSinkConnectionState btDevice=" + btDevice + " state="
- + state + " vol=" + a2dpVolume);
- }
- String address = btDevice.getAddress();
- if (address == null) {
- address = "";
+ Log.d(TAG, "onSetBtActiveDevice"
+ + " btDevice=" + btInfo.mDevice
+ + " profile=" + BluetoothProfile.getProfileName(btInfo.mProfile)
+ + " state=" + BluetoothProfile.getConnectionStateName(btInfo.mState));
}
+ String address = btInfo.mDevice.getAddress();
if (!BluetoothAdapter.checkBluetoothAddress(address)) {
address = "";
}
- final @AudioSystem.AudioFormatNativeEnumForBtCodec int a2dpCodec = btInfo.getCodec();
+ AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent("BT connected:"
+ + " addr=" + address
+ + " profile=" + btInfo.mProfile
+ + " state=" + btInfo.mState
+ + " codec=" + AudioSystem.audioFormatToString(btInfo.mCodec)));
- AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(
- "A2DP sink connected: device addr=" + address + " state=" + state
- + " codec=" + AudioSystem.audioFormatToString(a2dpCodec)
- + " vol=" + a2dpVolume));
-
- new MediaMetrics.Item(mMetricsId + "a2dp")
+ new MediaMetrics.Item(mMetricsId + "onSetBtActiveDevice")
+ .set(MediaMetrics.Property.STATUS, btInfo.mProfile)
+ .set(MediaMetrics.Property.DEVICE,
+ AudioSystem.getDeviceName(btInfo.mAudioSystemDevice))
.set(MediaMetrics.Property.ADDRESS, address)
- .set(MediaMetrics.Property.ENCODING, AudioSystem.audioFormatToString(a2dpCodec))
- .set(MediaMetrics.Property.EVENT, "onSetA2dpSinkConnectionState")
- .set(MediaMetrics.Property.INDEX, a2dpVolume)
+ .set(MediaMetrics.Property.ENCODING,
+ AudioSystem.audioFormatToString(btInfo.mCodec))
+ .set(MediaMetrics.Property.EVENT, "onSetBtActiveDevice")
+ .set(MediaMetrics.Property.STREAM_TYPE,
+ AudioSystem.streamToString(streamType))
.set(MediaMetrics.Property.STATE,
- state == BluetoothProfile.STATE_CONNECTED
+ btInfo.mState == BluetoothProfile.STATE_CONNECTED
? MediaMetrics.Value.CONNECTED : MediaMetrics.Value.DISCONNECTED)
.record();
synchronized (mDevicesLock) {
- final String key = DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
- btDevice.getAddress());
+ final String key = DeviceInfo.makeDeviceListKey(btInfo.mAudioSystemDevice, address);
final DeviceInfo di = mConnectedDevices.get(key);
- boolean isConnected = di != null;
-
- if (isConnected) {
- if (state == BluetoothProfile.STATE_CONNECTED) {
- // device is already connected, but we are receiving a connection again,
- // it could be for a codec change
- if (a2dpCodec != di.mDeviceCodecFormat) {
- mDeviceBroker.postBluetoothA2dpDeviceConfigChange(btDevice);
- }
- } else {
- makeA2dpDeviceUnavailableNow(address, di.mDeviceCodecFormat);
- }
- } else if (state == BluetoothProfile.STATE_CONNECTED) {
- // device is not already connected
- if (a2dpVolume != -1) {
- mDeviceBroker.postSetVolumeIndexOnDevice(AudioSystem.STREAM_MUSIC,
- // convert index to internal representation in VolumeStreamState
- a2dpVolume * 10,
- AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, "onSetA2dpSinkConnectionState");
- }
- makeA2dpDeviceAvailable(address, BtHelper.getName(btDevice),
- "onSetA2dpSinkConnectionState", a2dpCodec);
- }
- }
- }
-
- /*package*/ void onSetA2dpSourceConnectionState(
- @NonNull BtHelper.BluetoothA2dpDeviceInfo btInfo, int state) {
- final BluetoothDevice btDevice = btInfo.getBtDevice();
- if (AudioService.DEBUG_DEVICES) {
- Log.d(TAG, "onSetA2dpSourceConnectionState btDevice=" + btDevice + " state="
- + state);
- }
- String address = btDevice.getAddress();
- if (!BluetoothAdapter.checkBluetoothAddress(address)) {
- address = "";
- }
- synchronized (mDevicesLock) {
- final String key = DeviceInfo.makeDeviceListKey(
- AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, address);
- final DeviceInfo di = mConnectedDevices.get(key);
- boolean isConnected = di != null;
+ final boolean isConnected = di != null;
- new MediaMetrics.Item(mMetricsId + "onSetA2dpSourceConnectionState")
- .set(MediaMetrics.Property.ADDRESS, address)
- .set(MediaMetrics.Property.DEVICE,
- AudioSystem.getDeviceName(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP))
- .set(MediaMetrics.Property.STATE,
- state == BluetoothProfile.STATE_CONNECTED
- ? MediaMetrics.Value.CONNECTED : MediaMetrics.Value.DISCONNECTED)
- .record();
+ final boolean switchToUnavailable = isConnected
+ && btInfo.mState != BluetoothProfile.STATE_CONNECTED;
+ final boolean switchToAvailable = !isConnected
+ && btInfo.mState == BluetoothProfile.STATE_CONNECTED;
- if (isConnected && state != BluetoothProfile.STATE_CONNECTED) {
- makeA2dpSrcUnavailable(address);
- } else if (!isConnected && state == BluetoothProfile.STATE_CONNECTED) {
- makeA2dpSrcAvailable(address);
+ switch (btInfo.mProfile) {
+ case BluetoothProfile.A2DP_SINK:
+ if (switchToUnavailable) {
+ makeA2dpSrcUnavailable(address);
+ } else if (switchToAvailable) {
+ makeA2dpSrcAvailable(address);
+ }
+ break;
+ case BluetoothProfile.A2DP:
+ if (switchToUnavailable) {
+ makeA2dpDeviceUnavailableNow(address, di.mDeviceCodecFormat);
+ } else if (switchToAvailable) {
+ // device is not already connected
+ if (btInfo.mVolume != -1) {
+ mDeviceBroker.postSetVolumeIndexOnDevice(AudioSystem.STREAM_MUSIC,
+ // convert index to internal representation in VolumeStreamState
+ btInfo.mVolume * 10, btInfo.mAudioSystemDevice,
+ "onSetBtActiveDevice");
+ }
+ makeA2dpDeviceAvailable(address, BtHelper.getName(btInfo.mDevice),
+ "onSetBtActiveDevice", btInfo.mCodec);
+ }
+ break;
+ case BluetoothProfile.HEARING_AID:
+ if (switchToUnavailable) {
+ makeHearingAidDeviceUnavailable(address);
+ } else if (switchToAvailable) {
+ makeHearingAidDeviceAvailable(address, BtHelper.getName(btInfo.mDevice),
+ streamType, "onSetBtActiveDevice");
+ }
+ break;
+ case BluetoothProfile.LE_AUDIO:
+ if (switchToUnavailable) {
+ makeLeAudioDeviceUnavailable(address, btInfo.mAudioSystemDevice);
+ } else if (switchToAvailable) {
+ makeLeAudioDeviceAvailable(address, BtHelper.getName(btInfo.mDevice),
+ streamType, btInfo.mAudioSystemDevice, "onSetBtActiveDevice");
+ }
+ break;
+ default: throw new IllegalArgumentException("Invalid profile "
+ + BluetoothProfile.getProfileName(btInfo.mProfile));
}
}
}
- /*package*/ void onSetHearingAidConnectionState(BluetoothDevice btDevice,
- @AudioService.BtProfileConnectionState int state, int streamType) {
- String address = btDevice.getAddress();
- if (!BluetoothAdapter.checkBluetoothAddress(address)) {
- address = "";
- }
- AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(
- "onSetHearingAidConnectionState addr=" + address));
-
- new MediaMetrics.Item(mMetricsId + "onSetHearingAidConnectionState")
- .set(MediaMetrics.Property.ADDRESS, address)
- .set(MediaMetrics.Property.DEVICE,
- AudioSystem.getDeviceName(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP))
- .set(MediaMetrics.Property.STATE,
- state == BluetoothProfile.STATE_CONNECTED
- ? MediaMetrics.Value.CONNECTED : MediaMetrics.Value.DISCONNECTED)
- .set(MediaMetrics.Property.STREAM_TYPE,
- AudioSystem.streamToString(streamType))
- .record();
-
- synchronized (mDevicesLock) {
- final String key = DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_HEARING_AID,
- btDevice.getAddress());
- final DeviceInfo di = mConnectedDevices.get(key);
- boolean isConnected = di != null;
-
- if (isConnected && state != BluetoothProfile.STATE_CONNECTED) {
- makeHearingAidDeviceUnavailable(address);
- } else if (!isConnected && state == BluetoothProfile.STATE_CONNECTED) {
- makeHearingAidDeviceAvailable(address, BtHelper.getName(btDevice), streamType,
- "onSetHearingAidConnectionState");
- }
- }
- }
@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
- /*package*/ void onBluetoothA2dpActiveDeviceChange(
+ /*package*/ void onBluetoothA2dpDeviceConfigChange(
@NonNull BtHelper.BluetoothA2dpDeviceInfo btInfo, int event) {
MediaMetrics.Item mmi = new MediaMetrics.Item(mMetricsId
- + "onBluetoothA2dpActiveDeviceChange")
+ + "onBluetoothA2dpDeviceConfigChange")
.set(MediaMetrics.Property.EVENT, BtHelper.a2dpDeviceEventToString(event));
final BluetoothDevice btDevice = btInfo.getBtDevice();
@@ -437,7 +390,7 @@ public class AudioDeviceInventory {
return;
}
if (AudioService.DEBUG_DEVICES) {
- Log.d(TAG, "onBluetoothA2dpActiveDeviceChange btDevice=" + btDevice);
+ Log.d(TAG, "onBluetoothA2dpDeviceConfigChange btDevice=" + btDevice);
}
int a2dpVolume = btInfo.getVolume();
@AudioSystem.AudioFormatNativeEnumForBtCodec final int a2dpCodec = btInfo.getCodec();
@@ -447,11 +400,11 @@ public class AudioDeviceInventory {
address = "";
}
AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(
- "onBluetoothA2dpActiveDeviceChange addr=" + address
+ "onBluetoothA2dpDeviceConfigChange addr=" + address
+ " event=" + BtHelper.a2dpDeviceEventToString(event)));
synchronized (mDevicesLock) {
- if (mDeviceBroker.hasScheduledA2dpSinkConnectionState(btDevice)) {
+ if (mDeviceBroker.hasScheduledA2dpConnection(btDevice)) {
AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(
"A2dp config change ignored (scheduled connection change)")
.printLog(TAG));
@@ -463,7 +416,7 @@ public class AudioDeviceInventory {
AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address);
final DeviceInfo di = mConnectedDevices.get(key);
if (di == null) {
- Log.e(TAG, "invalid null DeviceInfo in onBluetoothA2dpActiveDeviceChange");
+ Log.e(TAG, "invalid null DeviceInfo in onBluetoothA2dpDeviceConfigChange");
mmi.set(MediaMetrics.Property.EARLY_RETURN, "null DeviceInfo").record();
return;
}
@@ -481,7 +434,7 @@ public class AudioDeviceInventory {
// convert index to internal representation in VolumeStreamState
a2dpVolume * 10,
AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
- "onBluetoothA2dpActiveDeviceChange");
+ "onBluetoothA2dpDeviceConfigChange");
}
} else if (event == BtHelper.EVENT_DEVICE_CONFIG_CHANGE) {
if (di.mDeviceCodecFormat != a2dpCodec) {
@@ -502,10 +455,9 @@ public class AudioDeviceInventory {
int musicDevice = mDeviceBroker.getDeviceForStream(AudioSystem.STREAM_MUSIC);
// force A2DP device disconnection in case of error so that AudioService state is
// consistent with audio policy manager state
- setBluetoothA2dpDeviceConnectionState(
- btDevice, BluetoothA2dp.STATE_DISCONNECTED, BluetoothProfile.A2DP,
- false /* suppressNoisyIntent */, musicDevice,
- -1 /* a2dpVolume */);
+ setBluetoothActiveDevice(new AudioDeviceBroker.BtDeviceInfo(btDevice,
+ BluetoothProfile.A2DP, BluetoothProfile.STATE_DISCONNECTED,
+ musicDevice, AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP));
} else {
AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(
"APM handleDeviceConfigChange success for A2DP device addr=" + address
@@ -791,7 +743,7 @@ public class AudioDeviceInventory {
}
- /*package*/ void disconnectA2dp() {
+ private void disconnectA2dp() {
synchronized (mDevicesLock) {
final ArraySet<String> toRemove = new ArraySet<>();
// Disconnect ALL DEVICE_OUT_BLUETOOTH_A2DP devices
@@ -813,7 +765,7 @@ public class AudioDeviceInventory {
}
}
- /*package*/ void disconnectA2dpSink() {
+ private void disconnectA2dpSink() {
synchronized (mDevicesLock) {
final ArraySet<String> toRemove = new ArraySet<>();
// Disconnect ALL DEVICE_IN_BLUETOOTH_A2DP devices
@@ -828,7 +780,7 @@ public class AudioDeviceInventory {
}
}
- /*package*/ void disconnectHearingAid() {
+ private void disconnectHearingAid() {
synchronized (mDevicesLock) {
final ArraySet<String> toRemove = new ArraySet<>();
// Disconnect ALL DEVICE_OUT_HEARING_AID devices
@@ -850,6 +802,50 @@ public class AudioDeviceInventory {
}
}
+ /*package*/ synchronized void onBtProfileDisconnected(int profile) {
+ switch (profile) {
+ case BluetoothProfile.A2DP:
+ disconnectA2dp();
+ break;
+ case BluetoothProfile.A2DP_SINK:
+ disconnectA2dpSink();
+ break;
+ case BluetoothProfile.HEARING_AID:
+ disconnectHearingAid();
+ break;
+ case BluetoothProfile.LE_AUDIO:
+ disconnectLeAudio();
+ break;
+ default:
+ // Not a valid profile to disconnect
+ Log.e(TAG, "onBtProfileDisconnected: Not a valid profile to disconnect "
+ + BluetoothProfile.getProfileName(profile));
+ break;
+ }
+ }
+
+ /*package*/ void disconnectLeAudio() {
+ synchronized (mDevicesLock) {
+ final ArraySet<String> toRemove = new ArraySet<>();
+ // Disconnect ALL DEVICE_OUT_BLE_HEADSET devices
+ mConnectedDevices.values().forEach(deviceInfo -> {
+ if (deviceInfo.mDeviceType == AudioSystem.DEVICE_OUT_BLE_HEADSET) {
+ toRemove.add(deviceInfo.mDeviceAddress);
+ }
+ });
+ new MediaMetrics.Item(mMetricsId + "disconnectLeAudio")
+ .record();
+ if (toRemove.size() > 0) {
+ final int delay = checkSendBecomingNoisyIntentInt(
+ AudioSystem.DEVICE_OUT_BLE_HEADSET, 0, AudioSystem.DEVICE_NONE);
+ toRemove.stream().forEach(deviceAddress ->
+ makeLeAudioDeviceUnavailable(deviceAddress,
+ AudioSystem.DEVICE_OUT_BLE_HEADSET)
+ );
+ }
+ }
+ }
+
// must be called before removing the device from mConnectedDevices
// musicDevice argument is used when not AudioSystem.DEVICE_NONE instead of querying
// from AudioSystem
@@ -875,46 +871,39 @@ public class AudioDeviceInventory {
// only public for mocking/spying
@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
@VisibleForTesting
- public void setBluetoothA2dpDeviceConnectionState(
- @NonNull BluetoothDevice device, @AudioService.BtProfileConnectionState int state,
- int profile, boolean suppressNoisyIntent, int musicDevice, int a2dpVolume) {
+ public int setBluetoothActiveDevice(@NonNull AudioDeviceBroker.BtDeviceInfo info) {
int delay;
- if (profile != BluetoothProfile.A2DP && profile != BluetoothProfile.A2DP_SINK) {
- throw new IllegalArgumentException("invalid profile " + profile);
- }
synchronized (mDevicesLock) {
- if (profile == BluetoothProfile.A2DP && !suppressNoisyIntent) {
+ if (!info.mSupprNoisy
+ && ((info.mProfile == BluetoothProfile.LE_AUDIO && info.mIsLeOutput)
+ || info.mProfile == BluetoothProfile.HEARING_AID
+ || info.mProfile == BluetoothProfile.A2DP)) {
@AudioService.ConnectionState int asState =
- (state == BluetoothA2dp.STATE_CONNECTED)
+ (info.mState == BluetoothProfile.STATE_CONNECTED)
? AudioService.CONNECTION_STATE_CONNECTED
: AudioService.CONNECTION_STATE_DISCONNECTED;
- delay = checkSendBecomingNoisyIntentInt(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
- asState, musicDevice);
+ delay = checkSendBecomingNoisyIntentInt(info.mAudioSystemDevice, asState,
+ info.mMusicDevice);
} else {
delay = 0;
}
- final int a2dpCodec = mDeviceBroker.getA2dpCodec(device);
-
if (AudioService.DEBUG_DEVICES) {
- Log.i(TAG, "setBluetoothA2dpDeviceConnectionState device: " + device
- + " state: " + state + " delay(ms): " + delay
- + " codec:" + Integer.toHexString(a2dpCodec)
- + " suppressNoisyIntent: " + suppressNoisyIntent);
+ Log.i(TAG, "setBluetoothActiveDevice device: " + info.mDevice
+ + " profile: " + BluetoothProfile.getProfileName(info.mProfile)
+ + " state: " + BluetoothProfile.getConnectionStateName(info.mState)
+ + " delay(ms): " + delay
+ + " codec:" + Integer.toHexString(info.mCodec)
+ + " suppressNoisyIntent: " + info.mSupprNoisy);
}
-
- final BtHelper.BluetoothA2dpDeviceInfo a2dpDeviceInfo =
- new BtHelper.BluetoothA2dpDeviceInfo(device, a2dpVolume, a2dpCodec);
- if (profile == BluetoothProfile.A2DP) {
- mDeviceBroker.postA2dpSinkConnection(state,
- a2dpDeviceInfo,
- delay);
- } else { //profile == BluetoothProfile.A2DP_SINK
- mDeviceBroker.postA2dpSourceConnection(state,
- a2dpDeviceInfo,
- delay);
+ mDeviceBroker.postBluetoothActiveDevice(info, delay);
+ if (info.mProfile == BluetoothProfile.HEARING_AID
+ && info.mState == BluetoothProfile.STATE_CONNECTED) {
+ mDeviceBroker.setForceUse_Async(AudioSystem.FOR_MEDIA, AudioSystem.FORCE_NONE,
+ "HEARING_AID set to CONNECTED");
}
}
+ return delay;
}
/*package*/ int setWiredDeviceConnectionState(int type, @AudioService.ConnectionState int state,
@@ -928,28 +917,6 @@ public class AudioDeviceInventory {
}
}
- /*package*/ int setBluetoothHearingAidDeviceConnectionState(
- @NonNull BluetoothDevice device, @AudioService.BtProfileConnectionState int state,
- boolean suppressNoisyIntent, int musicDevice) {
- int delay;
- synchronized (mDevicesLock) {
- if (!suppressNoisyIntent) {
- int intState = (state == BluetoothHearingAid.STATE_CONNECTED) ? 1 : 0;
- delay = checkSendBecomingNoisyIntentInt(AudioSystem.DEVICE_OUT_HEARING_AID,
- intState, musicDevice);
- } else {
- delay = 0;
- }
- mDeviceBroker.postSetHearingAidConnectionState(state, device, delay);
- if (state == BluetoothHearingAid.STATE_CONNECTED) {
- mDeviceBroker.setForceUse_Async(AudioSystem.FOR_MEDIA, AudioSystem.FORCE_NONE,
- "HEARING_AID set to CONNECTED");
- }
- return delay;
- }
- }
-
-
//-------------------------------------------------------------------
// Internal utilities
@@ -1121,6 +1088,46 @@ public class AudioDeviceInventory {
}
@GuardedBy("mDevicesLock")
+ private void makeLeAudioDeviceAvailable(String address, String name, int streamType, int device,
+ String eventSource) {
+ if (device != AudioSystem.DEVICE_NONE) {
+
+ /* Audio Policy sees Le Audio similar to A2DP. Let's make sure
+ * AUDIO_POLICY_FORCE_NO_BT_A2DP is not set
+ */
+ mDeviceBroker.setBluetoothA2dpOnInt(true, false /*fromA2dp*/, eventSource);
+
+ AudioSystem.setDeviceConnectionState(device, AudioSystem.DEVICE_STATE_AVAILABLE,
+ address, name, AudioSystem.AUDIO_FORMAT_DEFAULT);
+ mConnectedDevices.put(DeviceInfo.makeDeviceListKey(device, address),
+ new DeviceInfo(device, name, address, AudioSystem.AUDIO_FORMAT_DEFAULT));
+ mDeviceBroker.postAccessoryPlugMediaUnmute(device);
+ }
+
+ if (streamType == AudioSystem.STREAM_DEFAULT) {
+ // No need to update volume for input devices
+ return;
+ }
+
+ final int leAudioVolIndex = mDeviceBroker.getVssVolumeForDevice(streamType,
+ AudioSystem.DEVICE_OUT_BLE_HEADSET);
+ final int maxIndex = mDeviceBroker.getMaxVssVolumeForStream(streamType);
+ mDeviceBroker.postSetLeAudioVolumeIndex(leAudioVolIndex, maxIndex, streamType);
+ mDeviceBroker.postApplyVolumeOnDevice(streamType, device, "makeLeAudioDeviceAvailable");
+ }
+
+ @GuardedBy("mDevicesLock")
+ private void makeLeAudioDeviceUnavailable(String address, int device) {
+ if (device != AudioSystem.DEVICE_NONE) {
+ AudioSystem.setDeviceConnectionState(device, AudioSystem.DEVICE_STATE_UNAVAILABLE,
+ address, "", AudioSystem.AUDIO_FORMAT_DEFAULT);
+ mConnectedDevices.remove(DeviceInfo.makeDeviceListKey(device, address));
+ }
+
+ setCurrentAudioRouteNameIfPossible(null, false /*fromA2dp*/);
+ }
+
+ @GuardedBy("mDevicesLock")
private void setCurrentAudioRouteNameIfPossible(String name, boolean fromA2dp) {
synchronized (mCurAudioRoutes) {
if (TextUtils.equals(mCurAudioRoutes.bluetoothName, name)) {
@@ -1154,8 +1161,10 @@ public class AudioDeviceInventory {
BECOMING_NOISY_INTENT_DEVICES_SET.add(AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET);
BECOMING_NOISY_INTENT_DEVICES_SET.add(AudioSystem.DEVICE_OUT_LINE);
BECOMING_NOISY_INTENT_DEVICES_SET.add(AudioSystem.DEVICE_OUT_HEARING_AID);
+ BECOMING_NOISY_INTENT_DEVICES_SET.add(AudioSystem.DEVICE_OUT_BLE_HEADSET);
BECOMING_NOISY_INTENT_DEVICES_SET.addAll(AudioSystem.DEVICE_OUT_ALL_A2DP_SET);
BECOMING_NOISY_INTENT_DEVICES_SET.addAll(AudioSystem.DEVICE_OUT_ALL_USB_SET);
+ BECOMING_NOISY_INTENT_DEVICES_SET.addAll(AudioSystem.DEVICE_OUT_ALL_BLE_SET);
}
// must be called before removing the device from mConnectedDevices
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 2e02e4977094..dadf2a4dbc0a 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -83,6 +83,7 @@ import android.media.AudioPlaybackConfiguration;
import android.media.AudioRecordingConfiguration;
import android.media.AudioRoutesInfo;
import android.media.AudioSystem;
+import android.media.BluetoothProfileConnectionInfo;
import android.media.IAudioFocusDispatcher;
import android.media.IAudioModeDispatcher;
import android.media.IAudioRoutesObserver;
@@ -315,8 +316,8 @@ public class AudioService extends IAudioService.Stub
private static final int MSG_UPDATE_A11Y_SERVICE_UIDS = 35;
private static final int MSG_UPDATE_AUDIO_MODE = 36;
private static final int MSG_RECORDING_CONFIG_CHANGE = 37;
- private static final int MSG_SET_A2DP_DEV_CONNECTION_STATE = 38;
- private static final int MSG_A2DP_DEV_CONFIG_CHANGE = 39;
+ private static final int MSG_BT_DEV_CHANGED = 38;
+
private static final int MSG_DISPATCH_AUDIO_MODE = 40;
private static final int MSG_ROUTING_UPDATED = 41;
private static final int MSG_INIT_HEADTRACKING_SENSORS = 42;
@@ -344,6 +345,10 @@ public class AudioService extends IAudioService.Stub
return mStreamStates[stream].getIndex(device);
}
+ /*package*/ int getMaxVssVolumeForStream(int stream) {
+ return mStreamStates[stream].getMaxIndex();
+ }
+
private SettingsObserver mSettingsObserver;
private AtomicInteger mMode = new AtomicInteger(AudioSystem.MODE_NORMAL);
@@ -2852,8 +2857,9 @@ public class AudioService extends IAudioService.Stub
int step;
// skip a2dp absolute volume control request when the device
- // is not an a2dp device
- if (!AudioSystem.DEVICE_OUT_ALL_A2DP_SET.contains(device)
+ // is neither an a2dp device nor BLE device
+ if ((!AudioSystem.DEVICE_OUT_ALL_A2DP_SET.contains(device)
+ && !AudioSystem.DEVICE_OUT_ALL_BLE_SET.contains(device))
&& (flags & AudioManager.FLAG_BLUETOOTH_ABS_VOLUME) != 0) {
return;
}
@@ -2992,11 +2998,22 @@ public class AudioService extends IAudioService.Stub
mDeviceBroker.postSetAvrcpAbsoluteVolumeIndex(newIndex / 10);
}
+ if (device == AudioSystem.DEVICE_OUT_BLE_HEADSET
+ && streamType == getBluetoothContextualVolumeStream()
+ && (flags & AudioManager.FLAG_BLUETOOTH_ABS_VOLUME) == 0) {
+ if (DEBUG_VOL) {
+ Log.d(TAG, "adjustSreamVolume postSetLeAudioVolumeIndex index="
+ + newIndex + " stream=" + streamType);
+ }
+ mDeviceBroker.postSetLeAudioVolumeIndex(newIndex,
+ mStreamStates[streamType].getMaxIndex(), streamType);
+ }
+
// Check if volume update should be send to Hearing Aid
if (device == AudioSystem.DEVICE_OUT_HEARING_AID) {
// only modify the hearing aid attenuation when the stream to modify matches
// the one expected by the hearing aid
- if (streamType == getHearingAidStreamType()) {
+ if (streamType == getBluetoothContextualVolumeStream()) {
if (DEBUG_VOL) {
Log.d(TAG, "adjustSreamVolume postSetHearingAidVolumeIndex index="
+ newIndex + " stream=" + streamType);
@@ -3218,6 +3235,13 @@ public class AudioService extends IAudioService.Stub
}
}
+ private void enforceQueryStatePermission() {
+ if (mContext.checkCallingOrSelfPermission(Manifest.permission.QUERY_AUDIO_STATE)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("Missing QUERY_AUDIO_STATE permissions");
+ }
+ }
+
private void enforceQueryStateOrModifyRoutingPermission() {
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
!= PackageManager.PERMISSION_GRANTED
@@ -3346,11 +3370,11 @@ public class AudioService extends IAudioService.Stub
}
}
- /*package*/ int getHearingAidStreamType() {
- return getHearingAidStreamType(mMode.get());
+ /*package*/ int getBluetoothContextualVolumeStream() {
+ return getBluetoothContextualVolumeStream(mMode.get());
}
- private int getHearingAidStreamType(int mode) {
+ private int getBluetoothContextualVolumeStream(int mode) {
switch (mode) {
case AudioSystem.MODE_IN_COMMUNICATION:
case AudioSystem.MODE_IN_CALL:
@@ -3507,7 +3531,7 @@ public class AudioService extends IAudioService.Stub
}
private void updateHearingAidVolumeOnVoiceActivityUpdate() {
- final int streamType = getHearingAidStreamType();
+ final int streamType = getBluetoothContextualVolumeStream();
final int index = getStreamVolume(streamType);
sVolumeLogger.log(new VolumeEvent(VolumeEvent.VOL_VOICE_ACTIVITY_HEARING_AID,
mVoicePlaybackActive.get(), streamType, index));
@@ -3539,7 +3563,7 @@ public class AudioService extends IAudioService.Stub
return;
}
- int streamType = getHearingAidStreamType(newMode);
+ int streamType = getBluetoothContextualVolumeStream(newMode);
final Set<Integer> deviceTypes = AudioSystem.generateAudioDeviceTypesSet(
mAudioSystem.getDevicesForStream(streamType));
@@ -3577,8 +3601,9 @@ public class AudioService extends IAudioService.Stub
int oldIndex;
// skip a2dp absolute volume control request when the device
- // is not an a2dp device
- if (!AudioSystem.DEVICE_OUT_ALL_A2DP_SET.contains(device)
+ // is neither an a2dp device nor BLE device
+ if ((!AudioSystem.DEVICE_OUT_ALL_A2DP_SET.contains(device)
+ && !AudioSystem.DEVICE_OUT_ALL_BLE_SET.contains(device))
&& (flags & AudioManager.FLAG_BLUETOOTH_ABS_VOLUME) != 0) {
return;
}
@@ -3619,8 +3644,19 @@ public class AudioService extends IAudioService.Stub
mDeviceBroker.postSetAvrcpAbsoluteVolumeIndex(index / 10);
}
+ if (device == AudioSystem.DEVICE_OUT_BLE_HEADSET
+ && streamType == getBluetoothContextualVolumeStream()
+ && (flags & AudioManager.FLAG_BLUETOOTH_ABS_VOLUME) == 0) {
+ if (DEBUG_VOL) {
+ Log.d(TAG, "adjustSreamVolume postSetLeAudioVolumeIndex index="
+ + index + " stream=" + streamType);
+ }
+ mDeviceBroker.postSetLeAudioVolumeIndex(index,
+ mStreamStates[streamType].getMaxIndex(), streamType);
+ }
+
if (device == AudioSystem.DEVICE_OUT_HEARING_AID
- && streamType == getHearingAidStreamType()) {
+ && streamType == getBluetoothContextualVolumeStream()) {
Log.i(TAG, "setStreamVolume postSetHearingAidVolumeIndex index=" + index
+ " stream=" + streamType);
mDeviceBroker.postSetHearingAidVolumeIndex(index, streamType);
@@ -4109,6 +4145,7 @@ public class AudioService extends IAudioService.Stub
/** Get last audible volume before stream was muted. */
public int getLastAudibleStreamVolume(int streamType) {
+ enforceQueryStatePermission();
ensureValidStreamType(streamType);
int device = getDeviceForStream(streamType);
return (mStreamStates[streamType].getIndex(device) + 5) / 10;
@@ -6161,6 +6198,11 @@ public class AudioService extends IAudioService.Stub
if (pkgName == null) {
pkgName = "";
}
+ if (device.getType() == AudioDeviceInfo.TYPE_BLUETOOTH_A2DP) {
+ avrcpSupportsAbsoluteVolume(device.getAddress(),
+ deviceVolumeBehavior == AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE);
+ return;
+ }
int audioSystemDeviceOut = AudioDeviceInfo.convertDeviceTypeToInternalDevice(
device.getType());
@@ -6286,45 +6328,44 @@ public class AudioService extends IAudioService.Stub
public @interface BtProfileConnectionState {}
/**
- * See AudioManager.setBluetoothHearingAidDeviceConnectionState()
+ * @hide
+ * The profiles that can be used with AudioService.handleBluetoothActiveDeviceChanged()
*/
- public void setBluetoothHearingAidDeviceConnectionState(
- @NonNull BluetoothDevice device, @BtProfileConnectionState int state,
- boolean suppressNoisyIntent, int musicDevice)
- {
- if (device == null) {
- throw new IllegalArgumentException("Illegal null device");
- }
- if (state != BluetoothProfile.STATE_CONNECTED
- && state != BluetoothProfile.STATE_DISCONNECTED) {
- throw new IllegalArgumentException("Illegal BluetoothProfile state for device "
- + " (dis)connection, got " + state);
- }
- mDeviceBroker.postBluetoothHearingAidDeviceConnectionState(
- device, state, suppressNoisyIntent, musicDevice, "AudioService");
- }
+ @IntDef({
+ BluetoothProfile.HEARING_AID,
+ BluetoothProfile.A2DP,
+ BluetoothProfile.A2DP_SINK,
+ BluetoothProfile.LE_AUDIO,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface BtProfile {}
+
/**
- * See AudioManager.setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent()
+ * See AudioManager.handleBluetoothActiveDeviceChanged(...)
*/
- public void setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
- @NonNull BluetoothDevice device, @BtProfileConnectionState int state,
- int profile, boolean suppressNoisyIntent, int a2dpVolume) {
- if (device == null) {
- throw new IllegalArgumentException("Illegal null device");
+ public void handleBluetoothActiveDeviceChanged(BluetoothDevice newDevice,
+ BluetoothDevice previousDevice, @NonNull BluetoothProfileConnectionInfo info) {
+ if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.BLUETOOTH_STACK)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("Bluetooth is the only caller allowed");
}
- if (state != BluetoothProfile.STATE_CONNECTED
- && state != BluetoothProfile.STATE_DISCONNECTED) {
- throw new IllegalArgumentException("Illegal BluetoothProfile state for device "
- + " (dis)connection, got " + state);
+ if (info == null) {
+ throw new IllegalArgumentException("Illegal null BluetoothProfileConnectionInfo for"
+ + " device " + previousDevice + " -> " + newDevice);
}
-
- AudioDeviceBroker.BtDeviceConnectionInfo info =
- new AudioDeviceBroker.BtDeviceConnectionInfo(device, state,
- profile, suppressNoisyIntent, a2dpVolume);
- sendMsg(mAudioHandler, MSG_SET_A2DP_DEV_CONNECTION_STATE, SENDMSG_QUEUE,
- 0 /*arg1*/, 0 /*arg2*/,
- /*obj*/ info, 0 /*delay*/);
+ final int profile = info.getProfile();
+ if (profile != BluetoothProfile.A2DP && profile != BluetoothProfile.A2DP_SINK
+ && profile != BluetoothProfile.LE_AUDIO
+ && profile != BluetoothProfile.HEARING_AID) {
+ throw new IllegalArgumentException("Illegal BluetoothProfile profile for device "
+ + previousDevice + " -> " + newDevice + ". Got: " + profile);
+ }
+ AudioDeviceBroker.BtDeviceChangedData data =
+ new AudioDeviceBroker.BtDeviceChangedData(newDevice, previousDevice, info,
+ "AudioService");
+ sendMsg(mAudioHandler, MSG_BT_DEV_CHANGED, SENDMSG_QUEUE, 0, 0,
+ /*obj*/ data, /*delay*/ 0);
}
/** only public for mocking/spying, do not call outside of AudioService */
@@ -6333,19 +6374,6 @@ public class AudioService extends IAudioService.Stub
mStreamStates[AudioSystem.STREAM_MUSIC].muteInternally(mute);
}
- /**
- * See AudioManager.handleBluetoothA2dpDeviceConfigChange()
- * @param device
- */
- public void handleBluetoothA2dpDeviceConfigChange(BluetoothDevice device)
- {
- if (device == null) {
- throw new IllegalArgumentException("Illegal null device");
- }
- sendMsg(mAudioHandler, MSG_A2DP_DEV_CONFIG_CHANGE, SENDMSG_QUEUE, 0, 0,
- /*obj*/ device, /*delay*/ 0);
- }
-
private static final Set<Integer> DEVICE_MEDIA_UNMUTED_ON_PLUG_SET;
static {
DEVICE_MEDIA_UNMUTED_ON_PLUG_SET = new HashSet<>();
@@ -7710,13 +7738,9 @@ public class AudioService extends IAudioService.Stub
}
break;
- case MSG_SET_A2DP_DEV_CONNECTION_STATE:
- mDeviceBroker.queueBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
- (AudioDeviceBroker.BtDeviceConnectionInfo) msg.obj);
- break;
-
- case MSG_A2DP_DEV_CONFIG_CHANGE:
- mDeviceBroker.postBluetoothA2dpDeviceConfigChange((BluetoothDevice) msg.obj);
+ case MSG_BT_DEV_CHANGED:
+ mDeviceBroker.queueOnBluetoothActiveDeviceChanged(
+ (AudioDeviceBroker.BtDeviceChangedData) msg.obj);
break;
case MSG_DISPATCH_AUDIO_MODE:
@@ -7806,7 +7830,7 @@ public class AudioService extends IAudioService.Stub
}
}
- public void avrcpSupportsAbsoluteVolume(String address, boolean support) {
+ private void avrcpSupportsAbsoluteVolume(String address, boolean support) {
// address is not used for now, but may be used when multiple a2dp devices are supported
sVolumeLogger.log(new AudioEventLogger.StringEvent("avrcpSupportsAbsoluteVolume addr="
+ address + " support=" + support));
diff --git a/services/core/java/com/android/server/audio/AudioServiceEvents.java b/services/core/java/com/android/server/audio/AudioServiceEvents.java
index 0eb5a5d1fb48..3137fa5784d2 100644
--- a/services/core/java/com/android/server/audio/AudioServiceEvents.java
+++ b/services/core/java/com/android/server/audio/AudioServiceEvents.java
@@ -155,6 +155,7 @@ public class AudioServiceEvents {
static final int VOL_MODE_CHANGE_HEARING_AID = 7;
static final int VOL_SET_GROUP_VOL = 8;
static final int VOL_MUTE_STREAM_INT = 9;
+ static final int VOL_SET_LE_AUDIO_VOL = 10;
final int mOp;
final int mStream;
@@ -310,6 +311,13 @@ public class AudioServiceEvents {
.set(MediaMetrics.Property.INDEX, mVal1)
.record();
return;
+ case VOL_SET_LE_AUDIO_VOL:
+ new MediaMetrics.Item(mMetricsId)
+ .set(MediaMetrics.Property.EVENT, "setLeAudioVolume")
+ .set(MediaMetrics.Property.INDEX, mVal1)
+ .set(MediaMetrics.Property.MAX_INDEX, mVal2)
+ .record();
+ return;
case VOL_SET_AVRCP_VOL:
new MediaMetrics.Item(mMetricsId)
.set(MediaMetrics.Property.EVENT, "setAvrcpVolume")
@@ -382,6 +390,11 @@ public class AudioServiceEvents {
.append(" index:").append(mVal1)
.append(" gain dB:").append(mVal2)
.toString();
+ case VOL_SET_LE_AUDIO_VOL:
+ return new StringBuilder("setLeAudioVolume:")
+ .append(" index:").append(mVal1)
+ .append(" gain dB:").append(mVal2)
+ .toString();
case VOL_SET_AVRCP_VOL:
return new StringBuilder("setAvrcpVolume:")
.append(" index:").append(mVal1)
diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java
index 52e8edff5ffa..f813ae011fee 100644
--- a/services/core/java/com/android/server/audio/BtHelper.java
+++ b/services/core/java/com/android/server/audio/BtHelper.java
@@ -25,11 +25,13 @@ import android.bluetooth.BluetoothCodecStatus;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHeadset;
import android.bluetooth.BluetoothHearingAid;
+import android.bluetooth.BluetoothLeAudio;
import android.bluetooth.BluetoothProfile;
import android.content.Intent;
import android.media.AudioDeviceAttributes;
import android.media.AudioManager;
import android.media.AudioSystem;
+import android.media.BluetoothProfileConnectionInfo;
import android.os.Binder;
import android.os.UserHandle;
import android.provider.Settings;
@@ -63,6 +65,8 @@ public class BtHelper {
private @Nullable BluetoothHearingAid mHearingAid;
+ private @Nullable BluetoothLeAudio mLeAudio;
+
// Reference to BluetoothA2dp to query for AbsoluteVolume.
private @Nullable BluetoothA2dp mA2dp;
@@ -106,6 +110,8 @@ public class BtHelper {
private static final int SCO_MODE_MAX = 2;
private static final int BT_HEARING_AID_GAIN_MIN = -128;
+ private static final int BT_LE_AUDIO_MIN_VOL = 0;
+ private static final int BT_LE_AUDIO_MAX_VOL = 255;
/**
* Returns a string representation of the scoAudioMode.
@@ -235,6 +241,8 @@ public class BtHelper {
mBluetoothProfileServiceListener, BluetoothProfile.A2DP);
adapter.getProfileProxy(mDeviceBroker.getContext(),
mBluetoothProfileServiceListener, BluetoothProfile.HEARING_AID);
+ adapter.getProfileProxy(mDeviceBroker.getContext(),
+ mBluetoothProfileServiceListener, BluetoothProfile.LE_AUDIO);
}
}
@@ -389,6 +397,26 @@ public class BtHelper {
return requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED, SCO_MODE_VIRTUAL_CALL);
}
+ /*package*/ synchronized void setLeAudioVolume(int index, int maxIndex, int streamType) {
+ if (mLeAudio == null) {
+ if (AudioService.DEBUG_VOL) {
+ Log.i(TAG, "setLeAudioVolume: null mLeAudio");
+ }
+ return;
+ }
+ /* leaudio expect volume value in range 0 to 255
+ */
+ int volume = (index * (BT_LE_AUDIO_MAX_VOL - BT_LE_AUDIO_MIN_VOL)) / maxIndex ;
+
+ if (AudioService.DEBUG_VOL) {
+ Log.i(TAG, "setLeAudioVolume: calling mLeAudio.setVolume idx="
+ + index + " volume=" + volume);
+ }
+ AudioService.sVolumeLogger.log(new AudioServiceEvents.VolumeEvent(
+ AudioServiceEvents.VolumeEvent.VOL_SET_LE_AUDIO_VOL, index, maxIndex));
+ mLeAudio.setVolume(volume);
+ }
+
/*package*/ synchronized void setHearingAidVolume(int index, int streamType) {
if (mHearingAid == null) {
if (AudioService.DEBUG_VOL) {
@@ -424,10 +452,11 @@ public class BtHelper {
}
/*package*/ synchronized void disconnectAllBluetoothProfiles() {
- mDeviceBroker.postDisconnectA2dp();
- mDeviceBroker.postDisconnectA2dpSink();
- mDeviceBroker.postDisconnectHeadset();
- mDeviceBroker.postDisconnectHearingAid();
+ mDeviceBroker.postBtProfileDisconnected(BluetoothProfile.A2DP);
+ mDeviceBroker.postBtProfileDisconnected(BluetoothProfile.A2DP_SINK);
+ mDeviceBroker.postBtProfileDisconnected(BluetoothProfile.HEADSET);
+ mDeviceBroker.postBtProfileDisconnected(BluetoothProfile.HEARING_AID);
+ mDeviceBroker.postBtProfileDisconnected(BluetoothProfile.LE_AUDIO);
}
// @GuardedBy("AudioDeviceBroker.mSetModeLock")
@@ -446,46 +475,32 @@ public class BtHelper {
mBluetoothHeadset = null;
}
- /*package*/ synchronized void onA2dpProfileConnected(BluetoothA2dp a2dp) {
- mA2dp = a2dp;
- final List<BluetoothDevice> deviceList = mA2dp.getConnectedDevices();
- if (deviceList.isEmpty()) {
+ /*package*/ synchronized void onBtProfileConnected(int profile, BluetoothProfile proxy) {
+ if (profile == BluetoothProfile.HEADSET) {
+ onHeadsetProfileConnected((BluetoothHeadset) proxy);
return;
}
- final BluetoothDevice btDevice = deviceList.get(0);
- // the device is guaranteed CONNECTED
- mDeviceBroker.queueBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
- new AudioDeviceBroker.BtDeviceConnectionInfo(btDevice,
- BluetoothA2dp.STATE_CONNECTED, BluetoothProfile.A2DP_SINK,
- true, -1));
- }
-
- /*package*/ synchronized void onA2dpSinkProfileConnected(BluetoothProfile profile) {
- final List<BluetoothDevice> deviceList = profile.getConnectedDevices();
- if (deviceList.isEmpty()) {
- return;
+ if (profile == BluetoothProfile.A2DP) {
+ mA2dp = (BluetoothA2dp) proxy;
+ } else if (profile == BluetoothProfile.LE_AUDIO) {
+ mLeAudio = (BluetoothLeAudio) proxy;
}
- final BluetoothDevice btDevice = deviceList.get(0);
- final @BluetoothProfile.BtProfileState int state =
- profile.getConnectionState(btDevice);
- mDeviceBroker.postSetA2dpSourceConnectionState(
- state, new BluetoothA2dpDeviceInfo(btDevice));
- }
-
- /*package*/ synchronized void onHearingAidProfileConnected(BluetoothHearingAid hearingAid) {
- mHearingAid = hearingAid;
- final List<BluetoothDevice> deviceList = mHearingAid.getConnectedDevices();
+ final List<BluetoothDevice> deviceList = proxy.getConnectedDevices();
if (deviceList.isEmpty()) {
return;
}
final BluetoothDevice btDevice = deviceList.get(0);
- final @BluetoothProfile.BtProfileState int state =
- mHearingAid.getConnectionState(btDevice);
- mDeviceBroker.postBluetoothHearingAidDeviceConnectionState(
- btDevice, state,
- /*suppressNoisyIntent*/ false,
- /*musicDevice*/ android.media.AudioSystem.DEVICE_NONE,
- /*eventSource*/ "mBluetoothProfileServiceListener");
+ if (proxy.getConnectionState(btDevice) == BluetoothProfile.STATE_CONNECTED) {
+ mDeviceBroker.queueOnBluetoothActiveDeviceChanged(
+ new AudioDeviceBroker.BtDeviceChangedData(btDevice, null,
+ new BluetoothProfileConnectionInfo(profile),
+ "mBluetoothProfileServiceListener"));
+ } else {
+ mDeviceBroker.queueOnBluetoothActiveDeviceChanged(
+ new AudioDeviceBroker.BtDeviceChangedData(null, btDevice,
+ new BluetoothProfileConnectionInfo(profile),
+ "mBluetoothProfileServiceListener"));
+ }
}
// @GuardedBy("AudioDeviceBroker.mSetModeLock")
@@ -632,29 +647,16 @@ public class BtHelper {
public void onServiceConnected(int profile, BluetoothProfile proxy) {
switch(profile) {
case BluetoothProfile.A2DP:
- AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(
- "BT profile service: connecting A2DP profile"));
- mDeviceBroker.postBtA2dpProfileConnected((BluetoothA2dp) proxy);
- break;
-
case BluetoothProfile.A2DP_SINK:
- AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(
- "BT profile service: connecting A2DP_SINK profile"));
- mDeviceBroker.postBtA2dpSinkProfileConnected(proxy);
- break;
-
case BluetoothProfile.HEADSET:
- AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(
- "BT profile service: connecting HEADSET profile"));
- mDeviceBroker.postBtHeasetProfileConnected((BluetoothHeadset) proxy);
- break;
-
case BluetoothProfile.HEARING_AID:
+ case BluetoothProfile.LE_AUDIO:
AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(
- "BT profile service: connecting HEARING_AID profile"));
- mDeviceBroker.postBtHearingAidProfileConnected(
- (BluetoothHearingAid) proxy);
+ "BT profile service: connecting "
+ + BluetoothProfile.getProfileName(profile) + " profile"));
+ mDeviceBroker.postBtProfileConnected(profile, proxy);
break;
+
default:
break;
}
@@ -663,19 +665,11 @@ public class BtHelper {
switch (profile) {
case BluetoothProfile.A2DP:
- mDeviceBroker.postDisconnectA2dp();
- break;
-
case BluetoothProfile.A2DP_SINK:
- mDeviceBroker.postDisconnectA2dpSink();
- break;
-
case BluetoothProfile.HEADSET:
- mDeviceBroker.postDisconnectHeadset();
- break;
-
case BluetoothProfile.HEARING_AID:
- mDeviceBroker.postDisconnectHearingAid();
+ case BluetoothProfile.LE_AUDIO:
+ mDeviceBroker.postBtProfileDisconnected(profile);
break;
default:
@@ -899,6 +893,7 @@ public class BtHelper {
pw.println(prefix + "mScoAudioState: " + scoAudioStateToString(mScoAudioState));
pw.println(prefix + "mScoAudioMode: " + scoAudioModeToString(mScoAudioMode));
pw.println("\n" + prefix + "mHearingAid: " + mHearingAid);
+ pw.println("\n" + prefix + "mLeAudio: " + mLeAudio);
pw.println(prefix + "mA2dp: " + mA2dp);
pw.println(prefix + "mAvrcpAbsVolSupported: " + mAvrcpAbsVolSupported);
}
diff --git a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
index 40c8f1940bda..47c73822ed46 100644
--- a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
+++ b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
@@ -786,7 +786,7 @@ public final class PlaybackActivityMonitor
// the same time if we still have a public client.
while (clientIterator.hasNext()) {
PlayMonitorClient pmc = clientIterator.next();
- if (pcdb.equals(pmc.mDispatcherCb)) {
+ if (pcdb.asBinder().equals(pmc.mDispatcherCb.asBinder())) {
pmc.release();
clientIterator.remove();
} else {
diff --git a/services/core/java/com/android/server/audio/TEST_MAPPING b/services/core/java/com/android/server/audio/TEST_MAPPING
index 90246f849491..5a6c6a51bd97 100644
--- a/services/core/java/com/android/server/audio/TEST_MAPPING
+++ b/services/core/java/com/android/server/audio/TEST_MAPPING
@@ -1,13 +1,13 @@
{
"presubmit-large": [
{
- "name": "CtsMediaTestCases",
+ "name": "CtsMediaAudioTestCases",
"options": [
{
- "include-filter": "android.media.cts.AudioManagerTest"
+ "include-filter": "android.media.audio.cts.AudioManagerTest"
},
{
- "include-filter": "android.media.cts.AudioFocusTest"
+ "include-filter": "android.media.audio.cts.AudioFocusTest"
}
]
}
diff --git a/services/core/java/com/android/server/biometrics/AuthService.java b/services/core/java/com/android/server/biometrics/AuthService.java
index 9c8ccd946b7f..b5c8cd16a355 100644
--- a/services/core/java/com/android/server/biometrics/AuthService.java
+++ b/services/core/java/com/android/server/biometrics/AuthService.java
@@ -61,6 +61,7 @@ import android.os.Build;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.SystemProperties;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.Slog;
@@ -71,6 +72,7 @@ import com.android.internal.util.ArrayUtils;
import com.android.server.SystemService;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
/**
@@ -82,6 +84,8 @@ public class AuthService extends SystemService {
private static final String SETTING_HIDL_DISABLED =
"com.android.server.biometrics.AuthService.hidlDisabled";
private static final int DEFAULT_HIDL_DISABLED = 0;
+ private static final String SYSPROP_FIRST_API_LEVEL = "ro.board.first_api_level";
+ private static final String SYSPROP_API_LEVEL = "ro.board.api_level";
private final Injector mInjector;
@@ -624,7 +628,16 @@ public class AuthService extends SystemService {
final SensorConfig[] hidlConfigs;
if (!mInjector.isHidlDisabled(getContext())) {
- final String[] configStrings = mInjector.getConfiguration(getContext());
+ final int firstApiLevel = SystemProperties.getInt(SYSPROP_FIRST_API_LEVEL, 0);
+ final int apiLevel = SystemProperties.getInt(SYSPROP_API_LEVEL, firstApiLevel);
+ String[] configStrings = mInjector.getConfiguration(getContext());
+ if (configStrings.length == 0 && apiLevel == Build.VERSION_CODES.R) {
+ // For backwards compatibility with R where biometrics could work without being
+ // configured in config_biometric_sensors. In the absence of a vendor provided
+ // configuration, we assume the weakest biometric strength (i.e. convenience).
+ Slog.w(TAG, "Found R vendor partition without config_biometric_sensors");
+ configStrings = generateRSdkCompatibleConfiguration();
+ }
hidlConfigs = new SensorConfig[configStrings.length];
for (int i = 0; i < configStrings.length; ++i) {
hidlConfigs[i] = new SensorConfig(configStrings[i]);
@@ -640,6 +653,31 @@ public class AuthService extends SystemService {
}
/**
+ * Generates an array of string configs with entries that correspond to the biometric features
+ * declared on the device. Returns an empty array if no biometric features are declared.
+ * Biometrics are assumed to be of the weakest strength class, i.e. convenience.
+ */
+ private @NonNull String[] generateRSdkCompatibleConfiguration() {
+ final PackageManager pm = getContext().getPackageManager();
+ final ArrayList<String> modalities = new ArrayList<>();
+ if (pm.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
+ modalities.add(String.valueOf(BiometricAuthenticator.TYPE_FINGERPRINT));
+ }
+ if (pm.hasSystemFeature(PackageManager.FEATURE_FACE)) {
+ modalities.add(String.valueOf(BiometricAuthenticator.TYPE_FACE));
+ }
+ final String strength = String.valueOf(Authenticators.BIOMETRIC_CONVENIENCE);
+ final String[] configStrings = new String[modalities.size()];
+ for (int i = 0; i < modalities.size(); ++i) {
+ final String id = String.valueOf(i);
+ final String modality = modalities.get(i);
+ configStrings[i] = String.join(":" /* delimiter */, id, modality, strength);
+ }
+ Slog.d(TAG, "Generated config_biometric_sensors: " + Arrays.toString(configStrings));
+ return configStrings;
+ }
+
+ /**
* Registers HIDL and AIDL authenticators for all of the available modalities.
*
* @param hidlSensors Array of {@link SensorConfig} configuration for all of the HIDL sensors
diff --git a/services/core/java/com/android/server/biometrics/OWNERS b/services/core/java/com/android/server/biometrics/OWNERS
index 4eac97286880..f05f40353e30 100644
--- a/services/core/java/com/android/server/biometrics/OWNERS
+++ b/services/core/java/com/android/server/biometrics/OWNERS
@@ -6,3 +6,4 @@ curtislb@google.com
ilyamaty@google.com
joshmccloskey@google.com
jbolinger@google.com
+graciecheng@google.com
diff --git a/services/core/java/com/android/server/broadcastradio/hal1/Tuner.java b/services/core/java/com/android/server/broadcastradio/hal1/Tuner.java
index f2e44ee132ed..e7118ad72bf4 100644
--- a/services/core/java/com/android/server/broadcastradio/hal1/Tuner.java
+++ b/services/core/java/com/android/server/broadcastradio/hal1/Tuner.java
@@ -28,10 +28,8 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
-import java.util.Collections;
import java.util.List;
import java.util.Map;
-import java.util.Objects;
class Tuner extends ITuner.Stub {
private static final String TAG = "BroadcastRadioService.Tuner";
@@ -292,12 +290,12 @@ class Tuner extends ITuner.Stub {
}
@Override
- public Map setParameters(Map parameters) {
+ public Map<String, String> setParameters(Map<String, String> parameters) {
throw new UnsupportedOperationException("Not supported by HAL 1.x");
}
@Override
- public Map getParameters(List<String> keys) {
+ public Map<String, String> getParameters(List<String> keys) {
throw new UnsupportedOperationException("Not supported by HAL 1.x");
}
}
diff --git a/services/core/java/com/android/server/broadcastradio/hal1/TunerCallback.java b/services/core/java/com/android/server/broadcastradio/hal1/TunerCallback.java
index 7a6d9647bf43..867d5b4b6f49 100644
--- a/services/core/java/com/android/server/broadcastradio/hal1/TunerCallback.java
+++ b/services/core/java/com/android/server/broadcastradio/hal1/TunerCallback.java
@@ -18,12 +18,10 @@ package com.android.server.broadcastradio.hal1;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.hardware.radio.ITuner;
import android.hardware.radio.ITunerCallback;
import android.hardware.radio.ProgramList;
import android.hardware.radio.ProgramSelector;
import android.hardware.radio.RadioManager;
-import android.hardware.radio.RadioMetadata;
import android.hardware.radio.RadioTuner;
import android.os.IBinder;
import android.os.RemoteException;
@@ -31,7 +29,6 @@ import android.util.Slog;
import java.util.List;
import java.util.Map;
-import java.util.Objects;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
@@ -176,7 +173,7 @@ class TunerCallback implements ITunerCallback {
}
@Override
- public void onParametersUpdated(Map parameters) {
+ public void onParametersUpdated(Map<String, String> parameters) {
Slog.e(TAG, "Not applicable for HAL 1.x");
}
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java b/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java
index 200af2fb1da7..d476fd60b3ee 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java
@@ -301,7 +301,7 @@ class TunerSession extends ITuner.Stub {
}
@Override
- public Map setParameters(Map parameters) {
+ public Map<String, String> setParameters(Map<String, String> parameters) {
synchronized (mLock) {
checkNotClosedLocked();
return Convert.vendorInfoFromHal(Utils.maybeRethrow(
@@ -310,7 +310,7 @@ class TunerSession extends ITuner.Stub {
}
@Override
- public Map getParameters(List<String> keys) {
+ public Map<String, String> getParameters(List<String> keys) {
synchronized (mLock) {
checkNotClosedLocked();
return Convert.vendorInfoFromHal(Utils.maybeRethrow(
diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java
index 093ecd57124f..a0c5aa364f0d 100644
--- a/services/core/java/com/android/server/clipboard/ClipboardService.java
+++ b/services/core/java/com/android/server/clipboard/ClipboardService.java
@@ -535,9 +535,6 @@ public class ClipboardService extends SystemService {
mEmulatorClipboardMonitor.accept(clip);
final int userId = UserHandle.getUserId(uid);
- if (clip != null) {
- startClassificationLocked(clip, userId);
- }
// Update this user
setPrimaryClipInternalLocked(getClipboardLocked(userId), clip, uid, sourcePackage);
@@ -574,7 +571,7 @@ public class ClipboardService extends SystemService {
final boolean canCopyIntoProfile = !hasRestriction(
UserManager.DISALLOW_SHARE_INTO_MANAGED_PROFILE, id);
if (canCopyIntoProfile) {
- setPrimaryClipInternalLocked(
+ setPrimaryClipInternalNoClassifyLocked(
getClipboardLocked(id), clip, uid, sourcePackage);
}
}
@@ -593,6 +590,17 @@ public class ClipboardService extends SystemService {
@GuardedBy("mLock")
private void setPrimaryClipInternalLocked(PerUserClipboard clipboard, @Nullable ClipData clip,
int uid, @Nullable String sourcePackage) {
+ final int userId = UserHandle.getUserId(uid);
+ if (clip != null) {
+ startClassificationLocked(clip, userId);
+ }
+
+ setPrimaryClipInternalNoClassifyLocked(clipboard, clip, uid, sourcePackage);
+ }
+
+ @GuardedBy("mLock")
+ private void setPrimaryClipInternalNoClassifyLocked(PerUserClipboard clipboard,
+ @Nullable ClipData clip, int uid, @Nullable String sourcePackage) {
revokeUris(clipboard);
clipboard.activePermissionOwners.clear();
if (clip == null && clipboard.primaryClip == null) {
@@ -631,7 +639,7 @@ public class ClipboardService extends SystemService {
clipboard.primaryClipListeners.getBroadcastItem(i)
.dispatchPrimaryClipChanged();
}
- } catch (RemoteException e) {
+ } catch (RemoteException | SecurityException e) {
// The RemoteCallbackList will take care of removing
// the dead object for us.
}
diff --git a/services/core/java/com/android/server/clipboard/OWNERS b/services/core/java/com/android/server/clipboard/OWNERS
index 5449df908051..0d5dbf9acac3 100644
--- a/services/core/java/com/android/server/clipboard/OWNERS
+++ b/services/core/java/com/android/server/clipboard/OWNERS
@@ -1 +1,3 @@
per-file EmulatorClipboardMonitor.java = bohu@google.com,lfy@google.com,rkir@google.com
+
+olilan@google.com
diff --git a/services/core/java/com/android/server/compat/OWNERS b/services/core/java/com/android/server/compat/OWNERS
index cfd0a4b079ad..ee3086ab2fdb 100644
--- a/services/core/java/com/android/server/compat/OWNERS
+++ b/services/core/java/com/android/server/compat/OWNERS
@@ -1,6 +1 @@
-# Use this reviewer by default.
-platform-compat-eng+reviews@google.com
-
-andreionea@google.com
-mathewi@google.com
-satayev@google.com
+include tools/platform-compat:/OWNERS
diff --git a/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java b/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java
index a56a8ea993f0..603f20633cfb 100644
--- a/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java
+++ b/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java
@@ -18,16 +18,12 @@ package com.android.server.connectivity;
import static android.net.ConnectivityManager.MULTIPATH_PREFERENCE_HANDOVER;
import static android.net.ConnectivityManager.MULTIPATH_PREFERENCE_RELIABILITY;
-import static android.net.ConnectivityManager.TYPE_MOBILE;
import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkPolicy.LIMIT_DISABLED;
import static android.net.NetworkPolicy.WARNING_DISABLED;
-import static android.net.NetworkTemplate.NETWORK_TYPE_ALL;
-import static android.net.NetworkTemplate.OEM_MANAGED_ALL;
-import static android.net.NetworkTemplate.SUBSCRIBER_ID_MATCH_RULE_EXACT;
import static android.provider.Settings.Global.NETWORK_DEFAULT_DAILY_MULTIPATH_QUOTA_BYTES;
import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
@@ -70,13 +66,14 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.LocalServices;
import com.android.server.net.NetworkPolicyManagerInternal;
-import com.android.server.net.NetworkStatsManagerInternal;
import java.time.Clock;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit;
+import java.util.Objects;
+import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
@@ -96,6 +93,7 @@ public class MultipathPolicyTracker {
private static String TAG = MultipathPolicyTracker.class.getSimpleName();
private static final boolean DBG = false;
+ private static final long MIN_THRESHOLD_BYTES = 2 * 1_048_576L; // 2MiB
// This context is for the current user.
private final Context mContext;
@@ -200,6 +198,7 @@ public class MultipathPolicyTracker {
private final NetworkTemplate mNetworkTemplate;
private final UsageCallback mUsageCallback;
private NetworkCapabilities mNetworkCapabilities;
+ private final NetworkStatsManager mStatsManager;
public MultipathTracker(Network network, NetworkCapabilities nc) {
this.network = network;
@@ -224,12 +223,13 @@ public class MultipathPolicyTracker {
"Can't get TelephonyManager for subId %d", subId));
}
- subscriberId = tele.getSubscriberId();
- mNetworkTemplate = new NetworkTemplate(
- NetworkTemplate.MATCH_MOBILE, subscriberId, new String[] { subscriberId },
- null, NetworkStats.METERED_YES, NetworkStats.ROAMING_ALL,
- NetworkStats.DEFAULT_NETWORK_NO, NETWORK_TYPE_ALL, OEM_MANAGED_ALL,
- SUBSCRIBER_ID_MATCH_RULE_EXACT);
+ subscriberId = Objects.requireNonNull(tele.getSubscriberId(),
+ "Null subscriber Id for subId " + subId);
+ mNetworkTemplate = new NetworkTemplate.Builder(NetworkTemplate.MATCH_MOBILE)
+ .setSubscriberIds(Set.of(subscriberId))
+ .setMeteredness(NetworkStats.METERED_YES)
+ .setDefaultNetworkStatus(NetworkStats.DEFAULT_NETWORK_NO)
+ .build();
mUsageCallback = new UsageCallback() {
@Override
public void onThresholdReached(int networkType, String subscriberId) {
@@ -238,6 +238,13 @@ public class MultipathPolicyTracker {
updateMultipathBudget();
}
};
+ mStatsManager = mContext.getSystemService(NetworkStatsManager.class);
+ // Query stats from NetworkStatsService will trigger a poll by default.
+ // But since MultipathPolicyTracker listens NPMS events that triggered by
+ // stats updated event, and will query stats
+ // after the event. A polling -> updated -> query -> polling loop will be introduced
+ // if polls on open. Hence, set flag to false to prevent a polling loop.
+ mStatsManager.setPollOnOpen(false);
updateMultipathBudget();
}
@@ -261,8 +268,9 @@ public class MultipathPolicyTracker {
private long getNetworkTotalBytes(long start, long end) {
try {
- return LocalServices.getService(NetworkStatsManagerInternal.class)
- .getNetworkTotalBytes(mNetworkTemplate, start, end);
+ final android.app.usage.NetworkStats.Bucket ret =
+ mStatsManager.querySummaryForDevice(mNetworkTemplate, start, end);
+ return ret.getRxBytes() + ret.getTxBytes();
} catch (RuntimeException e) {
Log.w(TAG, "Failed to get data usage: " + e);
return -1;
@@ -270,15 +278,11 @@ public class MultipathPolicyTracker {
}
private NetworkIdentity getTemplateMatchingNetworkIdentity(NetworkCapabilities nc) {
- return new NetworkIdentity(
- ConnectivityManager.TYPE_MOBILE,
- 0 /* subType, unused for template matching */,
- subscriberId,
- null /* networkId, unused for matching mobile networks */,
- !nc.hasCapability(NET_CAPABILITY_NOT_ROAMING),
- !nc.hasCapability(NET_CAPABILITY_NOT_METERED),
- false /* defaultNetwork, templates should have DEFAULT_NETWORK_ALL */,
- OEM_MANAGED_ALL);
+ return new NetworkIdentity.Builder().setType(ConnectivityManager.TYPE_MOBILE)
+ .setSubscriberId(subscriberId)
+ .setRoaming(!nc.hasCapability(NET_CAPABILITY_NOT_ROAMING))
+ .setMetered(!nc.hasCapability(NET_CAPABILITY_NOT_METERED))
+ .build();
}
private long getRemainingDailyBudget(long limitBytes,
@@ -367,7 +371,7 @@ public class MultipathPolicyTracker {
// This will only be called if the total quota for the day changed, not if usage changed
// since last time, so even if this is called very often the budget will not snap to 0
// as soon as there are less than 2MB left for today.
- if (budget > NetworkStatsManager.MIN_THRESHOLD_BYTES) {
+ if (budget > MIN_THRESHOLD_BYTES) {
if (DBG) {
Log.d(TAG, "Setting callback for " + budget + " bytes on network " + network);
}
@@ -400,8 +404,8 @@ public class MultipathPolicyTracker {
private void registerUsageCallback(long budget) {
maybeUnregisterUsageCallback();
- mStatsManager.registerUsageCallback(mNetworkTemplate, TYPE_MOBILE, budget,
- mUsageCallback, mHandler);
+ mStatsManager.registerUsageCallback(mNetworkTemplate, budget,
+ (command) -> mHandler.post(command), mUsageCallback);
mMultipathBudget = budget;
}
diff --git a/services/core/java/com/android/server/connectivity/NetdEventListenerService.java b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
index 6ea84ce35002..ee5bda3abc91 100644
--- a/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
+++ b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
@@ -171,25 +171,28 @@ public class NetdEventListenerService extends INetdEventListener.Stub {
}
private NetworkMetrics getMetricsForNetwork(long timeMs, int netId) {
- collectPendingMetricsSnapshot(timeMs);
NetworkMetrics metrics = mNetworkMetrics.get(netId);
- if (metrics == null) {
- // TODO: allow to change transport for a given netid.
- metrics = new NetworkMetrics(netId, getTransports(netId), mConnectTb);
+ final NetworkCapabilities nc = mCallback.getNetworkCapabilities(netId);
+ final long transports = (nc != null) ? BitUtils.packBits(nc.getTransportTypes()) : 0;
+ final boolean forceCollect =
+ (metrics != null && nc != null && metrics.transports != transports);
+ collectPendingMetricsSnapshot(timeMs, forceCollect);
+ if (metrics == null || forceCollect) {
+ metrics = new NetworkMetrics(netId, transports, mConnectTb);
mNetworkMetrics.put(netId, metrics);
}
return metrics;
}
private NetworkMetricsSnapshot[] getNetworkMetricsSnapshots() {
- collectPendingMetricsSnapshot(System.currentTimeMillis());
+ collectPendingMetricsSnapshot(System.currentTimeMillis(), false /* forceCollect */);
return mNetworkMetricsSnapshots.toArray();
}
- private void collectPendingMetricsSnapshot(long timeMs) {
+ private void collectPendingMetricsSnapshot(long timeMs, boolean forceCollect) {
// Detects time differences larger than the snapshot collection period.
// This is robust against clock jumps and long inactivity periods.
- if (Math.abs(timeMs - mLastSnapshot) <= METRICS_SNAPSHOT_SPAN_MS) {
+ if (!forceCollect && Math.abs(timeMs - mLastSnapshot) <= METRICS_SNAPSHOT_SPAN_MS) {
return;
}
mLastSnapshot = projectSnapshotTime(timeMs);
@@ -394,14 +397,6 @@ public class NetdEventListenerService extends INetdEventListener.Stub {
return list;
}
- private long getTransports(int netId) {
- final NetworkCapabilities nc = mCallback.getNetworkCapabilities(netId);
- if (nc == null) {
- return 0;
- }
- return BitUtils.packBits(nc.getTransportTypes());
- }
-
/** Helper class for buffering summaries of NetworkMetrics at regular time intervals */
static class NetworkMetricsSnapshot {
diff --git a/services/core/java/com/android/server/connectivity/OWNERS b/services/core/java/com/android/server/connectivity/OWNERS
index 7311eee32a4c..62c5737a2e8e 100644
--- a/services/core/java/com/android/server/connectivity/OWNERS
+++ b/services/core/java/com/android/server/connectivity/OWNERS
@@ -1,8 +1,2 @@
set noparent
-
-codewiz@google.com
-ek@google.com
-jchalard@google.com
-lorenzo@google.com
-reminv@google.com
-satk@google.com
+file:platform/packages/modules/Connectivity:master:/OWNERS_core_networking
diff --git a/services/core/java/com/android/server/connectivity/PacProxyService.java b/services/core/java/com/android/server/connectivity/PacProxyService.java
index 00703390a118..3a97765246c1 100644
--- a/services/core/java/com/android/server/connectivity/PacProxyService.java
+++ b/services/core/java/com/android/server/connectivity/PacProxyService.java
@@ -34,7 +34,6 @@ import android.net.ProxyInfo;
import android.net.TrafficStats;
import android.net.Uri;
import android.os.Handler;
-import android.os.HandlerExecutor;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.RemoteCallbackList;
@@ -42,6 +41,7 @@ import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.os.UserHandle;
import android.provider.Settings;
import android.util.Log;
@@ -192,7 +192,7 @@ public class PacProxyService extends IPacProxyManager.Stub {
}
/**
- * Updates the PAC Proxy Installer with current Proxy information. This is called by
+ * Updates the PAC Proxy Service with current Proxy information. This is called by
* the ProxyTracker through PacProxyManager before a broadcast takes place to allow
* the PacProxyService to indicate that the broadcast should not be sent and the
* PacProxyService will trigger a new broadcast when it is ready.
@@ -357,8 +357,9 @@ public class PacProxyService extends IPacProxyManager.Stub {
}
}
};
- mContext.bindService(intent, mConnection,
- Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND | Context.BIND_NOT_VISIBLE);
+ mContext.bindServiceAsUser(intent, mConnection,
+ Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND | Context.BIND_NOT_VISIBLE,
+ UserHandle.SYSTEM);
intent = new Intent();
intent.setClassName(PROXY_PACKAGE, PROXY_SERVICE);
@@ -398,9 +399,9 @@ public class PacProxyService extends IPacProxyManager.Stub {
}
}
};
- mContext.bindService(intent,
+ mContext.bindServiceAsUser(intent, mProxyConnection,
Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND | Context.BIND_NOT_VISIBLE,
- new HandlerExecutor(mNetThreadHandler), mProxyConnection);
+ mNetThreadHandler, UserHandle.SYSTEM);
}
private void unbind() {
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 3762ccaae13b..ea054a5b280c 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -26,8 +26,7 @@ import static android.net.VpnManager.NOTIFICATION_CHANNEL_VPN;
import static android.os.PowerWhitelistManager.REASON_VPN;
import static android.os.UserHandle.PER_USER_RANGE;
-import static com.android.internal.util.Preconditions.checkArgument;
-import static com.android.internal.util.Preconditions.checkNotNull;
+import static java.util.Objects.requireNonNull;
import android.Manifest;
import android.annotation.NonNull;
@@ -129,6 +128,7 @@ import com.android.server.net.BaseNetworkObserver;
import libcore.io.IoUtils;
import java.io.File;
+import java.io.FileDescriptor;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -153,6 +153,7 @@ import java.util.Objects;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
+import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
@@ -198,12 +199,14 @@ public class Vpn {
private final Context mContext;
private final ConnectivityManager mConnectivityManager;
+ private final AppOpsManager mAppOpsManager;
// The context is for specific user which is created from mUserId
private final Context mUserIdContext;
@VisibleForTesting final Dependencies mDeps;
private final NetworkInfo mNetworkInfo;
private int mLegacyState;
@VisibleForTesting protected String mPackage;
+ private String mSessionKey;
private int mOwnerUID;
private boolean mIsPackageTargetingAtLeastQ;
@VisibleForTesting
@@ -410,6 +413,46 @@ public class Vpn {
public boolean isInterfacePresent(final Vpn vpn, final String iface) {
return vpn.jniCheck(iface) != 0;
}
+
+ /**
+ * @see ParcelFileDescriptor#adoptFd(int)
+ */
+ public ParcelFileDescriptor adoptFd(Vpn vpn, int mtu) {
+ return ParcelFileDescriptor.adoptFd(jniCreate(vpn, mtu));
+ }
+
+ /**
+ * Call native method to create the VPN interface and return the FileDescriptor of /dev/tun.
+ */
+ public int jniCreate(Vpn vpn, int mtu) {
+ return vpn.jniCreate(mtu);
+ }
+
+ /**
+ * Call native method to get the interface name of VPN.
+ */
+ public String jniGetName(Vpn vpn, int fd) {
+ return vpn.jniGetName(fd);
+ }
+
+ /**
+ * Call native method to set the VPN addresses and return the number of addresses.
+ */
+ public int jniSetAddresses(Vpn vpn, String interfaze, String addresses) {
+ return vpn.jniSetAddresses(interfaze, addresses);
+ }
+
+ /**
+ * @see IoUtils#setBlocking(FileDescriptor, boolean)
+ */
+ public void setBlocking(FileDescriptor fd, boolean blocking) {
+ try {
+ IoUtils.setBlocking(fd, blocking);
+ } catch (IOException e) {
+ throw new IllegalStateException(
+ "Cannot set tunnel's fd as blocking=" + blocking, e);
+ }
+ }
}
public Vpn(Looper looper, Context context, INetworkManagementService netService, INetd netd,
@@ -434,6 +477,7 @@ public class Vpn {
mVpnProfileStore = vpnProfileStore;
mContext = context;
mConnectivityManager = mContext.getSystemService(ConnectivityManager.class);
+ mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
mUserIdContext = context.createContextAsUser(UserHandle.of(userId), 0 /* flags */);
mDeps = deps;
mNms = netService;
@@ -829,7 +873,6 @@ public class Vpn {
VpnProfile profile = getVpnProfilePrivileged(alwaysOnPackage);
if (profile != null) {
startVpnProfilePrivileged(profile, alwaysOnPackage);
-
// If the above startVpnProfilePrivileged() call returns, the Ikev2VpnProfile was
// correctly parsed, and the VPN has started running in a different thread. The only
// other possibility is that the above call threw an exception, which will be
@@ -990,9 +1033,15 @@ public class Vpn {
} catch (Exception e) {
// ignore
}
+ mAppOpsManager.finishOp(
+ AppOpsManager.OPSTR_ESTABLISH_VPN_SERVICE, mOwnerUID, mPackage, null);
mContext.unbindService(mConnection);
cleanupVpnStateLocked();
} else if (mVpnRunner != null) {
+ if (!VpnConfig.LEGACY_VPN.equals(mPackage)) {
+ mAppOpsManager.finishOp(
+ AppOpsManager.OPSTR_ESTABLISH_VPN_MANAGER, mOwnerUID, mPackage, null);
+ }
// cleanupVpnStateLocked() is called from mVpnRunner.exit()
mVpnRunner.exit();
}
@@ -1057,10 +1106,8 @@ public class Vpn {
return false;
}
- final AppOpsManager appOpMgr =
- (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
for (final String appOpStr : toChange) {
- appOpMgr.setMode(
+ mAppOpsManager.setMode(
appOpStr,
uid,
packageName,
@@ -1113,13 +1160,14 @@ public class Vpn {
return Process.myUid();
}
PackageManager pm = mContext.getPackageManager();
- return Binder.withCleanCallingIdentity(() -> {
- try {
- return pm.getPackageUidAsUser(app, userId);
- } catch (NameNotFoundException e) {
- return -1;
- }
- });
+ final long token = Binder.clearCallingIdentity();
+ try {
+ return pm.getPackageUidAsUser(app, userId);
+ } catch (NameNotFoundException e) {
+ return -1;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
private boolean doesPackageTargetAtLeastQ(String packageName) {
@@ -1177,8 +1225,11 @@ public class Vpn {
for (RouteInfo route : mConfig.routes) {
lp.addRoute(route);
InetAddress address = route.getDestination().getAddress();
- allowIPv4 |= address instanceof Inet4Address;
- allowIPv6 |= address instanceof Inet6Address;
+
+ if (route.getType() == RouteInfo.RTN_UNICAST) {
+ allowIPv4 |= address instanceof Inet4Address;
+ allowIPv6 |= address instanceof Inet6Address;
+ }
}
}
@@ -1268,6 +1319,7 @@ public class Vpn {
.setLegacyType(ConnectivityManager.TYPE_VPN)
.setLegacyTypeName("VPN")
.setBypassableVpn(mConfig.allowBypass && !mLockdown)
+ .setVpnRequiresValidation(mConfig.requiresInternetValidation)
.build();
capsBuilder.setOwnerUid(mOwnerUID);
@@ -1285,6 +1337,9 @@ public class Vpn {
capsBuilder.addCapability(NET_CAPABILITY_NOT_METERED);
}
+ capsBuilder.setUnderlyingNetworks((mConfig.underlyingNetworks != null)
+ ? Arrays.asList(mConfig.underlyingNetworks) : null);
+
mNetworkCapabilities = capsBuilder.build();
mNetworkAgent = new NetworkAgent(mContext, mLooper, NETWORKTYPE /* logtag */,
mNetworkCapabilities, lp,
@@ -1295,17 +1350,16 @@ public class Vpn {
// We are user controlled, not driven by NetworkRequest.
}
};
- Binder.withCleanCallingIdentity(() -> {
- try {
- mNetworkAgent.register();
- } catch (final Exception e) {
- // If register() throws, don't keep an unregistered agent.
- mNetworkAgent = null;
- throw e;
- }
- });
- mNetworkAgent.setUnderlyingNetworks((mConfig.underlyingNetworks != null)
- ? Arrays.asList(mConfig.underlyingNetworks) : null);
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mNetworkAgent.register();
+ } catch (final Exception e) {
+ // If register() throws, don't keep an unregistered agent.
+ mNetworkAgent = null;
+ throw e;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
updateState(DetailedState.CONNECTED, "agentConnect");
}
@@ -1379,9 +1433,9 @@ public class Vpn {
Set<Range<Integer>> oldUsers = mNetworkCapabilities.getUids();
// Configure the interface. Abort if any of these steps fails.
- ParcelFileDescriptor tun = ParcelFileDescriptor.adoptFd(jniCreate(config.mtu));
+ final ParcelFileDescriptor tun = mDeps.adoptFd(this, config.mtu);
try {
- String interfaze = jniGetName(tun.getFd());
+ final String interfaze = mDeps.jniGetName(this, tun.getFd());
// TEMP use the old jni calls until there is support for netd address setting
StringBuilder builder = new StringBuilder();
@@ -1389,7 +1443,7 @@ public class Vpn {
builder.append(" ");
builder.append(address);
}
- if (jniSetAddresses(interfaze, builder.toString()) < 1) {
+ if (mDeps.jniSetAddresses(this, interfaze, builder.toString()) < 1) {
throw new IllegalArgumentException("At least one address must be specified");
}
Connection connection = new Connection();
@@ -1413,7 +1467,10 @@ public class Vpn {
// parameters. If that fails, disconnect.
if (oldConfig != null
&& updateLinkPropertiesInPlaceIfPossible(mNetworkAgent, oldConfig)) {
- // Keep mNetworkAgent unchanged
+ // Update underlying networks if it is changed.
+ if (!Arrays.equals(oldConfig.underlyingNetworks, config.underlyingNetworks)) {
+ setUnderlyingNetworks(config.underlyingNetworks);
+ }
} else {
// Initialize the state for a new agent, while keeping the old one connected
// in case this new connection fails.
@@ -1435,11 +1492,11 @@ public class Vpn {
jniReset(oldInterface);
}
- try {
- IoUtils.setBlocking(tun.getFileDescriptor(), config.blocking);
- } catch (IOException e) {
- throw new IllegalStateException(
- "Cannot set tunnel's fd as blocking=" + config.blocking, e);
+ mDeps.setBlocking(tun.getFileDescriptor(), config.blocking);
+ // Record that the VPN connection is established by an app which uses VpnService API.
+ if (oldNetworkAgent != mNetworkAgent) {
+ mAppOpsManager.startOp(
+ AppOpsManager.OPSTR_ESTABLISH_VPN_SERVICE, mOwnerUID, mPackage, null, null);
}
} catch (RuntimeException e) {
IoUtils.closeQuietly(tun);
@@ -1794,9 +1851,17 @@ public class Vpn {
synchronized (Vpn.this) {
if (interfaze.equals(mInterface) && jniCheck(interfaze) == 0) {
if (mConnection != null) {
+ mAppOpsManager.finishOp(
+ AppOpsManager.OPSTR_ESTABLISH_VPN_SERVICE, mOwnerUID, mPackage,
+ null);
mContext.unbindService(mConnection);
cleanupVpnStateLocked();
} else if (mVpnRunner != null) {
+ if (!VpnConfig.LEGACY_VPN.equals(mPackage)) {
+ mAppOpsManager.finishOp(
+ AppOpsManager.OPSTR_ESTABLISH_VPN_MANAGER, mOwnerUID, mPackage,
+ null);
+ }
// cleanupVpnStateLocked() is called from mVpnRunner.exit()
mVpnRunner.exit();
}
@@ -2040,13 +2105,16 @@ public class Vpn {
}
private void enforceNotRestrictedUser() {
- Binder.withCleanCallingIdentity(() -> {
+ final long token = Binder.clearCallingIdentity();
+ try {
final UserInfo user = mUserManager.getUserInfo(mUserId);
if (user.isRestricted()) {
throw new SecurityException("Restricted users cannot configure VPNs");
}
- });
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
/**
@@ -2452,6 +2520,7 @@ public class Vpn {
mProfile = profile;
mIpSecManager = (IpSecManager) mContext.getSystemService(Context.IPSEC_SERVICE);
mNetworkCallback = new VpnIkev2Utils.Ikev2VpnNetworkCallback(TAG, this);
+ mSessionKey = UUID.randomUUID().toString();
}
@Override
@@ -2773,6 +2842,7 @@ public class Vpn {
*/
private void disconnectVpnRunner() {
mActiveNetwork = null;
+ mSessionKey = null;
mIsRunning = false;
resetIkeState();
@@ -2839,8 +2909,10 @@ public class Vpn {
LegacyVpnRunner(VpnConfig config, String[] racoon, String[] mtpd, VpnProfile profile) {
super(TAG);
- checkArgument(racoon != null || mtpd != null, "Arguments to racoon and mtpd "
- + "must not both be null");
+ if (racoon == null && mtpd == null) {
+ throw new IllegalArgumentException(
+ "Arguments to racoon and mtpd must not both be null");
+ }
mConfig = config;
mDaemons = new String[] {"racoon", "mtpd"};
// TODO: clear arguments from memory once launched
@@ -3166,8 +3238,8 @@ public class Vpn {
*/
public synchronized boolean provisionVpnProfile(
@NonNull String packageName, @NonNull VpnProfile profile) {
- checkNotNull(packageName, "No package name provided");
- checkNotNull(profile, "No profile provided");
+ requireNonNull(packageName, "No package name provided");
+ requireNonNull(profile, "No profile provided");
verifyCallingUidAndPackage(packageName);
enforceNotRestrictedUser();
@@ -3184,12 +3256,12 @@ public class Vpn {
}
// Permissions checked during startVpnProfile()
- Binder.withCleanCallingIdentity(
- () -> {
- getVpnProfileStore().put(
- getProfileNameForPackage(packageName),
- encodedProfile);
- });
+ final long token = Binder.clearCallingIdentity();
+ try {
+ getVpnProfileStore().put(getProfileNameForPackage(packageName), encodedProfile);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
// TODO: if package has CONTROL_VPN, grant the ACTIVATE_PLATFORM_VPN appop.
// This mirrors the prepareAndAuthorize that is used by VpnService.
@@ -3209,26 +3281,28 @@ public class Vpn {
*/
public synchronized void deleteVpnProfile(
@NonNull String packageName) {
- checkNotNull(packageName, "No package name provided");
+ requireNonNull(packageName, "No package name provided");
verifyCallingUidAndPackage(packageName);
enforceNotRestrictedUser();
- Binder.withCleanCallingIdentity(
- () -> {
- // If this profile is providing the current VPN, turn it off, disabling
- // always-on as well if enabled.
- if (isCurrentIkev2VpnLocked(packageName)) {
- if (mAlwaysOn) {
- // Will transitively call prepareInternal(VpnConfig.LEGACY_VPN).
- setAlwaysOnPackage(null, false, null);
- } else {
- prepareInternal(VpnConfig.LEGACY_VPN);
- }
- }
+ final long token = Binder.clearCallingIdentity();
+ try {
+ // If this profile is providing the current VPN, turn it off, disabling
+ // always-on as well if enabled.
+ if (isCurrentIkev2VpnLocked(packageName)) {
+ if (mAlwaysOn) {
+ // Will transitively call prepareInternal(VpnConfig.LEGACY_VPN).
+ setAlwaysOnPackage(null, false, null);
+ } else {
+ prepareInternal(VpnConfig.LEGACY_VPN);
+ }
+ }
- getVpnProfileStore().remove(getProfileNameForPackage(packageName));
- });
+ getVpnProfileStore().remove(getProfileNameForPackage(packageName));
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
/**
@@ -3260,9 +3334,8 @@ public class Vpn {
*
* @param packageName the package name of the app provisioning this profile
*/
- public synchronized void startVpnProfile(
- @NonNull String packageName) {
- checkNotNull(packageName, "No package name provided");
+ public synchronized String startVpnProfile(@NonNull String packageName) {
+ requireNonNull(packageName, "No package name provided");
enforceNotRestrictedUser();
@@ -3271,15 +3344,18 @@ public class Vpn {
throw new SecurityException("User consent not granted for package " + packageName);
}
- Binder.withCleanCallingIdentity(
- () -> {
- final VpnProfile profile = getVpnProfilePrivileged(packageName);
- if (profile == null) {
- throw new IllegalArgumentException("No profile found for " + packageName);
- }
+ final long token = Binder.clearCallingIdentity();
+ try {
+ final VpnProfile profile = getVpnProfilePrivileged(packageName);
+ if (profile == null) {
+ throw new IllegalArgumentException("No profile found for " + packageName);
+ }
- startVpnProfilePrivileged(profile, packageName);
- });
+ startVpnProfilePrivileged(profile, packageName);
+ return mSessionKey;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
private synchronized void startVpnProfilePrivileged(
@@ -3308,6 +3384,7 @@ public class Vpn {
}
mConfig.startTime = SystemClock.elapsedRealtime();
mConfig.proxyInfo = profile.proxy;
+ mConfig.requiresInternetValidation = profile.requiresInternetValidation;
switch (profile.type) {
case VpnProfile.TYPE_IKEV2_IPSEC_USER_PASS:
@@ -3322,6 +3399,13 @@ public class Vpn {
Log.d(TAG, "Unknown VPN profile type: " + profile.type);
break;
}
+
+ // Record that the VPN connection is established by an app which uses VpnManager API.
+ if (!VpnConfig.LEGACY_VPN.equals(packageName)) {
+ mAppOpsManager.startOp(
+ AppOpsManager.OPSTR_ESTABLISH_VPN_MANAGER, mOwnerUID, mPackage, null,
+ null);
+ }
} catch (GeneralSecurityException e) {
// Reset mConfig
mConfig = null;
@@ -3340,7 +3424,7 @@ public class Vpn {
* @param packageName the package name of the app provisioning this profile
*/
public synchronized void stopVpnProfile(@NonNull String packageName) {
- checkNotNull(packageName, "No package name provided");
+ requireNonNull(packageName, "No package name provided");
enforceNotRestrictedUser();
diff --git a/services/core/java/com/android/server/criticalevents/OWNERS b/services/core/java/com/android/server/criticalevents/OWNERS
new file mode 100644
index 000000000000..9c3136c592f1
--- /dev/null
+++ b/services/core/java/com/android/server/criticalevents/OWNERS
@@ -0,0 +1,2 @@
+benmiles@google.com
+gaillard@google.com
diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java
index a4f7c85dee29..5088daaaa036 100644
--- a/services/core/java/com/android/server/display/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/DisplayModeDirector.java
@@ -525,7 +525,10 @@ public class DisplayModeDirector {
*/
public void setShouldAlwaysRespectAppRequestedMode(boolean enabled) {
synchronized (mLock) {
- mAlwaysRespectAppRequest = enabled;
+ if (mAlwaysRespectAppRequest != enabled) {
+ mAlwaysRespectAppRequest = enabled;
+ notifyDesiredDisplayModeSpecsChangedLocked();
+ }
}
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecKeycode.java b/services/core/java/com/android/server/hdmi/HdmiCecKeycode.java
index 23b5c1411b0e..698ee0bb6220 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecKeycode.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecKeycode.java
@@ -481,4 +481,113 @@ final class HdmiCecKeycode {
// return muting ? CEC_KEYCODE_MUTE_FUNCTION : CEC_KEYCODE_RESTORE_VOLUME_FUNCTION;
return CEC_KEYCODE_MUTE;
}
+
+ public static String getKeycodeType(byte keycode) {
+ switch (keycode) {
+ case CEC_KEYCODE_UP:
+ case CEC_KEYCODE_DOWN:
+ case CEC_KEYCODE_LEFT:
+ case CEC_KEYCODE_RIGHT:
+ case CEC_KEYCODE_RIGHT_UP:
+ case CEC_KEYCODE_RIGHT_DOWN:
+ case CEC_KEYCODE_LEFT_UP:
+ case CEC_KEYCODE_LEFT_DOWN:
+ case CEC_KEYCODE_PAGE_UP:
+ case CEC_KEYCODE_PAGE_DOWN:
+ case CEC_KEYCODE_EXIT:
+ return "Navigation";
+ case CEC_KEYCODE_ROOT_MENU:
+ case CEC_KEYCODE_SETUP_MENU:
+ case CEC_KEYCODE_CONTENTS_MENU:
+ case CEC_KEYCODE_FAVORITE_MENU:
+ case CEC_KEYCODE_MEDIA_TOP_MENU:
+ case CEC_KEYCODE_MEDIA_CONTEXT_SENSITIVE_MENU:
+ return "Menu";
+ case CEC_KEYCODE_VOLUME_UP:
+ return "Volume up";
+ case CEC_KEYCODE_VOLUME_DOWN:
+ return "Volume down";
+ case CEC_KEYCODE_MUTE:
+ return "Volume mute";
+ case CEC_KEYCODE_POWER:
+ return "Power";
+ case CEC_KEYCODE_POWER_TOGGLE_FUNCTION:
+ return "Power toggle";
+ case CEC_KEYCODE_POWER_OFF_FUNCTION:
+ return "Power off";
+ case CEC_KEYCODE_POWER_ON_FUNCTION:
+ return "Power on";
+ case CEC_KEYCODE_F1_BLUE:
+ case CEC_KEYCODE_F2_RED:
+ case CEC_KEYCODE_F3_GREEN:
+ case CEC_KEYCODE_F4_YELLOW:
+ case CEC_KEYCODE_F5:
+ return "Function key";
+ case CEC_KEYCODE_NEXT_FAVORITE:
+ case CEC_KEYCODE_CHANNEL_UP:
+ case CEC_KEYCODE_CHANNEL_DOWN:
+ case CEC_KEYCODE_PREVIOUS_CHANNEL:
+ return "Channel";
+ case CEC_KEYCODE_NUMBER_11:
+ case CEC_KEYCODE_NUMBER_12:
+ case CEC_KEYCODE_NUMBER_0_OR_NUMBER_10:
+ case CEC_KEYCODE_NUMBERS_1:
+ case CEC_KEYCODE_NUMBERS_2:
+ case CEC_KEYCODE_NUMBERS_3:
+ case CEC_KEYCODE_NUMBERS_4:
+ case CEC_KEYCODE_NUMBERS_5:
+ case CEC_KEYCODE_NUMBERS_6:
+ case CEC_KEYCODE_NUMBERS_7:
+ case CEC_KEYCODE_NUMBERS_8:
+ case CEC_KEYCODE_NUMBERS_9:
+ return "Number";
+ case CEC_KEYCODE_PLAY:
+ case CEC_KEYCODE_STOP:
+ case CEC_KEYCODE_PAUSE:
+ case CEC_KEYCODE_RECORD:
+ case CEC_KEYCODE_REWIND:
+ case CEC_KEYCODE_FAST_FORWARD:
+ case CEC_KEYCODE_EJECT:
+ case CEC_KEYCODE_FORWARD:
+ case CEC_KEYCODE_BACKWARD:
+ case CEC_KEYCODE_STOP_RECORD:
+ case CEC_KEYCODE_PAUSE_RECORD:
+ case CEC_KEYCODE_ANGLE:
+ case CEC_KEYCODE_SUB_PICTURE:
+ case CEC_KEYCODE_VIDEO_ON_DEMAND:
+ return "Media";
+ case CEC_KEYCODE_PLAY_FUNCTION:
+ case CEC_KEYCODE_PAUSE_PLAY_FUNCTION:
+ case CEC_KEYCODE_RECORD_FUNCTION:
+ case CEC_KEYCODE_PAUSE_RECORD_FUNCTION:
+ case CEC_KEYCODE_STOP_FUNCTION:
+ case CEC_KEYCODE_MUTE_FUNCTION:
+ case CEC_KEYCODE_RESTORE_VOLUME_FUNCTION:
+ case CEC_KEYCODE_TUNE_FUNCTION:
+ case CEC_KEYCODE_SELECT_MEDIA_FUNCTION:
+ case CEC_KEYCODE_SELECT_AV_INPUT_FUNCTION:
+ case CEC_KEYCODE_SELECT_AUDIO_INPUT_FUNCTION:
+ return "Functional";
+ case CEC_KEYCODE_ELECTRONIC_PROGRAM_GUIDE:
+ case CEC_KEYCODE_TIMER_PROGRAMMING:
+ return "Timer";
+ case CEC_KEYCODE_SOUND_SELECT:
+ case CEC_KEYCODE_SELECT_SOUND_PRESENTATION:
+ case CEC_KEYCODE_SELECT_BROADCAST_TYPE:
+ case CEC_KEYCODE_INPUT_SELECT:
+ case CEC_KEYCODE_SELECT:
+ return "Select";
+ case CEC_KEYCODE_NUMBER_ENTRY_MODE:
+ case CEC_KEYCODE_DOT:
+ case CEC_KEYCODE_CLEAR:
+ case CEC_KEYCODE_ENTER:
+ case CEC_KEYCODE_DISPLAY_INFORMATION:
+ case CEC_KEYCODE_HELP:
+ case CEC_KEYCODE_DATA:
+ case CEC_KEYCODE_INITIAL_CONFIGURATION:
+ return "General";
+ default:
+ return "Unknown";
+ }
+ }
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
index a2cb78d27a57..c12522b43f2a 100755
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
@@ -75,6 +75,8 @@ abstract class HdmiCecLocalDevice {
protected int mLastKeycode = HdmiCecKeycode.UNSUPPORTED_KEYCODE;
protected int mLastKeyRepeatCount = 0;
+ HdmiCecStandbyModeHandler mStandbyHandler;
+
// Stores recent changes to the active source in the CEC network.
private final ArrayBlockingQueue<HdmiCecController.Dumpable> mActiveSourceHistory =
new ArrayBlockingQueue<>(MAX_HDMI_ACTIVE_SOURCE_HISTORY);
@@ -209,6 +211,12 @@ abstract class HdmiCecLocalDevice {
void init() {
assertRunOnServiceThread();
mPreferredAddress = getPreferredAddress();
+ if (mHandler.hasMessages(MSG_DISABLE_DEVICE_TIMEOUT)) {
+ // Remove and trigger the queued message for clearing all actions when going to standby.
+ // This is necessary because the device may wake up before the message is triggered.
+ mHandler.removeMessages(MSG_DISABLE_DEVICE_TIMEOUT);
+ handleDisableDeviceTimeout();
+ }
mPendingActionClearedCallback = null;
}
@@ -257,6 +265,11 @@ abstract class HdmiCecLocalDevice {
if (dest != mAddress && dest != Constants.ADDR_BROADCAST) {
return Constants.NOT_HANDLED;
}
+ if (mService.isPowerStandby()
+ && !mService.isWakeUpMessageReceived()
+ && mStandbyHandler.handleCommand(message)) {
+ return Constants.HANDLED;
+ }
// Cache incoming message if it is included in the list of cacheable opcodes.
mCecMessageCache.cacheMessage(message);
return onMessage(message);
@@ -823,17 +836,12 @@ abstract class HdmiCecLocalDevice {
protected int handleVendorCommandWithId(HdmiCecMessage message) {
byte[] params = message.getParams();
int vendorId = HdmiUtils.threeBytesToInt(params);
- if (vendorId == mService.getVendorId()) {
- if (!mService.invokeVendorCommandListenersOnReceived(
- mDeviceType, message.getSource(), message.getDestination(), params, true)) {
- return Constants.ABORT_REFUSED;
- }
- } else if (message.getDestination() != Constants.ADDR_BROADCAST
- && message.getSource() != Constants.ADDR_UNREGISTERED) {
- Slog.v(TAG, "Wrong direct vendor command. Replying with <Feature Abort>");
- return Constants.ABORT_UNRECOGNIZED_OPCODE;
- } else {
+ if (message.getDestination() == Constants.ADDR_BROADCAST
+ || message.getSource() == Constants.ADDR_UNREGISTERED) {
Slog.v(TAG, "Wrong broadcast vendor command. Ignoring");
+ } else if (!mService.invokeVendorCommandListenersOnReceived(
+ mDeviceType, message.getSource(), message.getDestination(), params, true)) {
+ return Constants.ABORT_REFUSED;
}
return Constants.HANDLED;
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
index 0bb128536b42..c549b5598b67 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
@@ -112,6 +112,7 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
mService.readBooleanSetting(Global.HDMI_CEC_SWITCH_ENABLED, false);
mSystemAudioControlFeatureEnabled =
mService.readBooleanSetting(Global.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED, true);
+ mStandbyHandler = new HdmiCecStandbyModeHandler(service, this);
}
private static final String SHORT_AUDIO_DESCRIPTOR_CONFIG_PATH = "/vendor/etc/sadConfig.xml";
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
index 37ee76b7c615..40718585c484 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
@@ -64,6 +64,7 @@ public class HdmiCecLocalDevicePlayback extends HdmiCecLocalDeviceSource {
HdmiCecLocalDevicePlayback(HdmiControlService service) {
super(service, HdmiDeviceInfo.DEVICE_PLAYBACK);
+ mStandbyHandler = new HdmiCecStandbyModeHandler(service, this);
}
@Override
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index acfeb6c77db2..3d218cffc5df 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -91,8 +91,6 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
@GuardedBy("mLock")
private boolean mSystemAudioMute = false;
- private final HdmiCecStandbyModeHandler mStandbyHandler;
-
// If true, do not do routing control/send active source for internal source.
// Set to true when the device was woken up by <Text/Image View On>.
private boolean mSkipRoutingControl;
@@ -556,10 +554,8 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
}
}
- if (!mService.isPowerStandbyOrTransient()) {
- addAndStartAction(new NewDeviceAction(this, activeSource.logicalAddress,
- activeSource.physicalAddress, deviceType));
- }
+ addAndStartAction(new NewDeviceAction(this, activeSource.logicalAddress,
+ activeSource.physicalAddress, deviceType));
}
private boolean handleNewDeviceAtTheTailOfActivePath(int path) {
@@ -711,14 +707,12 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
@ServiceThreadOnly
void onNewAvrAdded(HdmiDeviceInfo avr) {
assertRunOnServiceThread();
- if (!mService.isPowerStandbyOrTransient()) {
- addAndStartAction(new SystemAudioAutoInitiationAction(this, avr.getLogicalAddress()));
- if (!isDirectConnectAddress(avr.getPhysicalAddress())) {
- startArcAction(false);
- } else if (isConnected(avr.getPortId()) && isArcFeatureEnabled(avr.getPortId())
- && !hasAction(SetArcTransmissionStateAction.class)) {
- startArcAction(true);
- }
+ addAndStartAction(new SystemAudioAutoInitiationAction(this, avr.getLogicalAddress()));
+ if (!isDirectConnectAddress(avr.getPhysicalAddress())) {
+ startArcAction(false);
+ } else if (isConnected(avr.getPortId()) && isArcFeatureEnabled(avr.getPortId())
+ && !hasAction(SetArcTransmissionStateAction.class)) {
+ startArcAction(true);
}
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessage.java b/services/core/java/com/android/server/hdmi/HdmiCecMessage.java
index c85fd50c62fe..15a316739134 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecMessage.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecMessage.java
@@ -119,6 +119,10 @@ public final class HdmiCecMessage {
if (mParams.length > 0) {
if (filterMessageParameters(mOpcode)) {
s.append(String.format(" <Redacted len=%d>", mParams.length));
+ } else if (isUserControlPressedMessage(mOpcode)) {
+ s.append(
+ String.format(
+ " <Keycode type = %s>", HdmiCecKeycode.getKeycodeType(mParams[0])));
} else {
for (byte data : mParams) {
s.append(String.format(":%02X", data));
@@ -287,7 +291,6 @@ public final class HdmiCecMessage {
private static boolean filterMessageParameters(int opcode) {
switch (opcode) {
- case Constants.MESSAGE_USER_CONTROL_PRESSED:
case Constants.MESSAGE_USER_CONTROL_RELEASED:
case Constants.MESSAGE_SET_OSD_NAME:
case Constants.MESSAGE_SET_OSD_STRING:
@@ -300,5 +303,9 @@ public final class HdmiCecMessage {
return false;
}
}
+
+ private static boolean isUserControlPressedMessage(int opcode) {
+ return Constants.MESSAGE_USER_CONTROL_PRESSED == opcode;
+ }
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java b/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
index 5802e53c67f1..8a727c6ffb24 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
@@ -254,7 +254,7 @@ public class HdmiCecMessageValidator {
mValidationInfo.append(opcode, new ValidationInfo(validator, addrType));
}
- int isValid(HdmiCecMessage message) {
+ int isValid(HdmiCecMessage message, boolean isMessageReceived) {
int opcode = message.getOpcode();
ValidationInfo info = mValidationInfo.get(opcode);
if (info == null) {
@@ -268,6 +268,22 @@ public class HdmiCecMessageValidator {
HdmiLogger.warning("Unexpected source: " + message);
return ERROR_SOURCE;
}
+
+ if (isMessageReceived) {
+ // Check if the source's logical address and local device's logical
+ // address are the same.
+ for (HdmiCecLocalDevice device : mService.getAllLocalDevices()) {
+ synchronized (device.mLock) {
+ if (message.getSource() == device.getDeviceInfo().getLogicalAddress()
+ && message.getSource() != Constants.ADDR_UNREGISTERED) {
+ HdmiLogger.warning(
+ "Unexpected source: message sent from device itself, " + message);
+ return ERROR_SOURCE;
+ }
+ }
+ }
+ }
+
// Check the destination field.
if (message.getDestination() == Constants.ADDR_BROADCAST) {
if ((info.addressType & DEST_BROADCAST) == 0) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecStandbyModeHandler.java b/services/core/java/com/android/server/hdmi/HdmiCecStandbyModeHandler.java
index 6f7473d60121..1c296e5b5640 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecStandbyModeHandler.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecStandbyModeHandler.java
@@ -16,6 +16,7 @@
package com.android.server.hdmi;
+import android.hardware.hdmi.HdmiDeviceInfo;
import android.util.SparseArray;
/**
@@ -56,7 +57,8 @@ public final class HdmiCecStandbyModeHandler {
private final class AutoOnHandler implements CecMessageHandler {
@Override
public boolean handle(HdmiCecMessage message) {
- if (!mTv.getAutoWakeup()) {
+ HdmiCecLocalDeviceTv tv = (HdmiCecLocalDeviceTv) mDevice;
+ if (!tv.getAutoWakeup()) {
mAborterRefused.handle(message);
return true;
}
@@ -78,7 +80,7 @@ public final class HdmiCecStandbyModeHandler {
}
private final HdmiControlService mService;
- private final HdmiCecLocalDeviceTv mTv;
+ private final HdmiCecLocalDevice mDevice;
private final SparseArray<CecMessageHandler> mCecMessageHandlers = new SparseArray<>();
private final CecMessageHandler mDefaultHandler = new Aborter(
@@ -92,13 +94,7 @@ public final class HdmiCecStandbyModeHandler {
private final UserControlProcessedHandler
mUserControlProcessedHandler = new UserControlProcessedHandler();
- public HdmiCecStandbyModeHandler(HdmiControlService service, HdmiCecLocalDeviceTv tv) {
- mService = service;
- mTv = tv;
-
- addHandler(Constants.MESSAGE_IMAGE_VIEW_ON, mAutoOnHandler);
- addHandler(Constants.MESSAGE_TEXT_VIEW_ON, mAutoOnHandler);
-
+ private void addCommonHandlers() {
addHandler(Constants.MESSAGE_ACTIVE_SOURCE, mBystander);
addHandler(Constants.MESSAGE_REQUEST_ACTIVE_SOURCE, mBystander);
addHandler(Constants.MESSAGE_ROUTING_CHANGE, mBystander);
@@ -112,19 +108,6 @@ public final class HdmiCecStandbyModeHandler {
addHandler(Constants.MESSAGE_SYSTEM_AUDIO_MODE_STATUS, mBystander);
addHandler(Constants.MESSAGE_REPORT_AUDIO_STATUS, mBystander);
- // If TV supports the following messages during power-on, ignore them and do nothing,
- // else reply with <Feature Abort>["Unrecognized Opcode"]
- // <Deck Status>, <Tuner Device Status>, <Tuner Cleared Status>, <Timer Status>
- addHandler(Constants.MESSAGE_RECORD_STATUS, mBystander);
-
- // If TV supports the following messages during power-on, reply with <Feature Abort>["Not
- // in correct mode to respond"], else reply with <Feature Abort>["Unrecognized Opcode"]
- // <Give Tuner Device Status>, <Select Digital Service>, <Tuner Step Decrement>,
- // <Tuner Stem Increment>, <Menu Status>.
- addHandler(Constants.MESSAGE_RECORD_TV_SCREEN, mAborterIncorrectMode);
- addHandler(Constants.MESSAGE_INITIATE_ARC, mAborterIncorrectMode);
- addHandler(Constants.MESSAGE_TERMINATE_ARC, mAborterIncorrectMode);
-
addHandler(Constants.MESSAGE_GIVE_PHYSICAL_ADDRESS, mBypasser);
addHandler(Constants.MESSAGE_GET_MENU_LANGUAGE, mBypasser);
addHandler(Constants.MESSAGE_REPORT_PHYSICAL_ADDRESS, mBypasser);
@@ -133,6 +116,7 @@ public final class HdmiCecStandbyModeHandler {
addHandler(Constants.MESSAGE_SET_OSD_NAME, mBypasser);
addHandler(Constants.MESSAGE_DEVICE_VENDOR_ID, mBypasser);
addHandler(Constants.MESSAGE_REPORT_POWER_STATUS, mBypasser);
+ addHandler(Constants.MESSAGE_GIVE_FEATURES, mBypasser);
addHandler(Constants.MESSAGE_USER_CONTROL_PRESSED, mUserControlProcessedHandler);
@@ -144,6 +128,34 @@ public final class HdmiCecStandbyModeHandler {
addHandler(Constants.MESSAGE_SET_SYSTEM_AUDIO_MODE, mAborterIncorrectMode);
}
+ private void addTvHandlers() {
+ addHandler(Constants.MESSAGE_IMAGE_VIEW_ON, mAutoOnHandler);
+ addHandler(Constants.MESSAGE_TEXT_VIEW_ON, mAutoOnHandler);
+
+ // If TV supports the following messages during power-on, ignore them and do nothing,
+ // else reply with <Feature Abort>["Unrecognized Opcode"]
+ // <Deck Status>, <Tuner Device Status>, <Tuner Cleared Status>, <Timer Status>
+ addHandler(Constants.MESSAGE_RECORD_STATUS, mBystander);
+
+ // If TV supports the following messages during power-on, reply with <Feature Abort>["Not
+ // in correct mode to respond"], else reply with <Feature Abort>["Unrecognized Opcode"]
+ // <Give Tuner Device Status>, <Select Digital Service>, <Tuner Step Decrement>,
+ // <Tuner Stem Increment>, <Menu Status>.
+ addHandler(Constants.MESSAGE_RECORD_TV_SCREEN, mAborterIncorrectMode);
+ addHandler(Constants.MESSAGE_INITIATE_ARC, mAborterIncorrectMode);
+ addHandler(Constants.MESSAGE_TERMINATE_ARC, mAborterIncorrectMode);
+ }
+
+ public HdmiCecStandbyModeHandler(HdmiControlService service, HdmiCecLocalDevice device) {
+ mService = service;
+ mDevice = device;
+
+ addCommonHandlers();
+ if (mDevice.getType() == HdmiDeviceInfo.DEVICE_TV) {
+ addTvHandlers();
+ }
+ }
+
private void addHandler(int opcode, CecMessageHandler handler) {
mCecMessageHandlers.put(opcode, handler);
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index a086bdaa6ec9..8dadf5a8d20d 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -634,6 +634,12 @@ public class HdmiControlService extends SystemService {
// on boot, if device is interactive, set HDMI CEC state as powered on as well
if (mPowerManager.isInteractive() && isPowerStandbyOrTransient()) {
mPowerStatusController.setPowerStatus(HdmiControlManager.POWER_STATUS_ON);
+ // Start all actions that were queued because the device was in standby
+ if (mAddressAllocated) {
+ for (HdmiCecLocalDevice localDevice : getAllLocalDevices()) {
+ localDevice.startQueuedActions();
+ }
+ }
}
}
@@ -1132,7 +1138,7 @@ public class HdmiControlService extends SystemService {
@ServiceThreadOnly
void sendCecCommand(HdmiCecMessage command, @Nullable SendMessageCallback callback) {
assertRunOnServiceThread();
- if (mMessageValidator.isValid(command) == HdmiCecMessageValidator.OK) {
+ if (mMessageValidator.isValid(command, false) == HdmiCecMessageValidator.OK) {
mCecController.sendCommand(command, callback);
} else {
HdmiLogger.error("Invalid message type:" + command);
@@ -1165,7 +1171,7 @@ public class HdmiControlService extends SystemService {
@Constants.HandleMessageResult
protected int handleCecCommand(HdmiCecMessage message) {
assertRunOnServiceThread();
- int errorCode = mMessageValidator.isValid(message);
+ int errorCode = mMessageValidator.isValid(message, true);
if (errorCode != HdmiCecMessageValidator.OK) {
// We'll not response on the messages with the invalid source or destination
// or with parameter length shorter than specified in the standard.
@@ -1576,11 +1582,11 @@ public class HdmiControlService extends SystemService {
class VendorCommandListenerRecord implements IBinder.DeathRecipient {
private final IHdmiVendorCommandListener mListener;
- private final int mDeviceType;
+ private final int mVendorId;
- public VendorCommandListenerRecord(IHdmiVendorCommandListener listener, int deviceType) {
+ VendorCommandListenerRecord(IHdmiVendorCommandListener listener, int vendorId) {
mListener = listener;
- mDeviceType = deviceType;
+ mVendorId = vendorId;
}
@Override
@@ -2071,10 +2077,10 @@ public class HdmiControlService extends SystemService {
}
@Override
- public void addVendorCommandListener(final IHdmiVendorCommandListener listener,
- final int deviceType) {
+ public void addVendorCommandListener(
+ final IHdmiVendorCommandListener listener, final int vendorId) {
initBinderCall();
- HdmiControlService.this.addVendorCommandListener(listener, deviceType);
+ HdmiControlService.this.addVendorCommandListener(listener, vendorId);
}
@Override
@@ -3125,7 +3131,7 @@ public class HdmiControlService extends SystemService {
Slog.v(TAG, "On standby-action cleared:" + device.mDeviceType);
devices.remove(device);
if (devices.isEmpty()) {
- onStandbyCompleted(standbyAction);
+ onPendingActionsCleared(standbyAction);
// We will not clear local devices here, since some OEM/SOC will keep passing
// the received packets until the application processor enters to the sleep
// actually.
@@ -3187,10 +3193,17 @@ public class HdmiControlService extends SystemService {
mHdmiCecNetwork.clearLocalDevices();
}
+ /**
+ * Normally called after all devices have cleared their pending actions, to execute the final
+ * phase of the standby flow.
+ *
+ * This can also be called during wakeup, when pending actions are cleared after failing to be
+ * cleared during standby. In this case, it does not execute the standby flow.
+ */
@ServiceThreadOnly
- private void onStandbyCompleted(int standbyAction) {
+ private void onPendingActionsCleared(int standbyAction) {
assertRunOnServiceThread();
- Slog.v(TAG, "onStandbyCompleted");
+ Slog.v(TAG, "onPendingActionsCleared");
if (!mPowerStatusController.isPowerStatusTransientToStandby()) {
return;
@@ -3206,8 +3219,9 @@ public class HdmiControlService extends SystemService {
}
}
- private void addVendorCommandListener(IHdmiVendorCommandListener listener, int deviceType) {
- VendorCommandListenerRecord record = new VendorCommandListenerRecord(listener, deviceType);
+ @VisibleForTesting
+ void addVendorCommandListener(IHdmiVendorCommandListener listener, int vendorId) {
+ VendorCommandListenerRecord record = new VendorCommandListenerRecord(listener, vendorId);
try {
listener.asBinder().linkToDeath(record, 0);
} catch (RemoteException e) {
@@ -3226,8 +3240,14 @@ public class HdmiControlService extends SystemService {
return false;
}
for (VendorCommandListenerRecord record : mVendorCommandListenerRecords) {
- if (record.mDeviceType != deviceType) {
- continue;
+ if (hasVendorId) {
+ int vendorId =
+ ((params[0] & 0xFF) << 16)
+ + ((params[1] & 0xFF) << 8)
+ + (params[2] & 0xFF);
+ if (record.mVendorId != vendorId) {
+ continue;
+ }
}
try {
record.mListener.onReceived(srcAddress, destAddress, params, hasVendorId);
@@ -3549,8 +3569,8 @@ public class HdmiControlService extends SystemService {
invokeInputChangeListener(info);
}
- void setMhlInputChangeEnabled(boolean enabled) {
- mMhlController.setOption(OPTION_MHL_INPUT_SWITCHING, toInt(enabled));
+ void setMhlInputChangeEnabled(boolean enabled) {
+ mMhlController.setOption(OPTION_MHL_INPUT_SWITCHING, toInt(enabled));
synchronized (mLock) {
mMhlInputChangeEnabled = enabled;
diff --git a/services/core/java/com/android/server/health/HealthHalCallbackHidl.java b/services/core/java/com/android/server/health/HealthHalCallbackHidl.java
new file mode 100644
index 000000000000..7a6698085c0d
--- /dev/null
+++ b/services/core/java/com/android/server/health/HealthHalCallbackHidl.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.health;
+
+import static android.hardware.health.Translate.h2aTranslate;
+
+import android.annotation.NonNull;
+import android.hardware.health.V2_0.IHealth;
+import android.hardware.health.V2_0.Result;
+import android.hardware.health.V2_1.BatteryCapacityLevel;
+import android.hardware.health.V2_1.Constants;
+import android.hardware.health.V2_1.IHealthInfoCallback;
+import android.os.RemoteException;
+import android.os.Trace;
+import android.util.Slog;
+
+/**
+ * On service registration, {@link HealthServiceWrapperHidl.Callback#onRegistration} is called,
+ * which registers {@code this}, a {@link IHealthInfoCallback}, to the health service.
+ *
+ * <p>When the health service has updates to health info, {@link HealthInfoCallback#update} is
+ * called.
+ *
+ * @hide
+ */
+class HealthHalCallbackHidl extends IHealthInfoCallback.Stub
+ implements HealthServiceWrapperHidl.Callback {
+
+ private static final String TAG = HealthHalCallbackHidl.class.getSimpleName();
+
+ private static void traceBegin(String name) {
+ Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, name);
+ }
+
+ private static void traceEnd() {
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
+ }
+
+ private HealthInfoCallback mCallback;
+
+ HealthHalCallbackHidl(@NonNull HealthInfoCallback callback) {
+ mCallback = callback;
+ }
+
+ @Override
+ public void healthInfoChanged(android.hardware.health.V2_0.HealthInfo props) {
+ android.hardware.health.V2_1.HealthInfo propsLatest =
+ new android.hardware.health.V2_1.HealthInfo();
+ propsLatest.legacy = props;
+
+ propsLatest.batteryCapacityLevel = BatteryCapacityLevel.UNSUPPORTED;
+ propsLatest.batteryChargeTimeToFullNowSeconds =
+ Constants.BATTERY_CHARGE_TIME_TO_FULL_NOW_SECONDS_UNSUPPORTED;
+
+ mCallback.update(h2aTranslate(propsLatest));
+ }
+
+ @Override
+ public void healthInfoChanged_2_1(android.hardware.health.V2_1.HealthInfo props) {
+ mCallback.update(h2aTranslate(props));
+ }
+
+ // on new service registered
+ @Override
+ public void onRegistration(IHealth oldService, IHealth newService, String instance) {
+ if (newService == null) return;
+
+ traceBegin("HealthUnregisterCallback");
+ try {
+ if (oldService != null) {
+ int r = oldService.unregisterCallback(this);
+ if (r != Result.SUCCESS) {
+ Slog.w(
+ TAG,
+ "health: cannot unregister previous callback: " + Result.toString(r));
+ }
+ }
+ } catch (RemoteException ex) {
+ Slog.w(
+ TAG,
+ "health: cannot unregister previous callback (transaction error): "
+ + ex.getMessage());
+ } finally {
+ traceEnd();
+ }
+
+ traceBegin("HealthRegisterCallback");
+ try {
+ int r = newService.registerCallback(this);
+ if (r != Result.SUCCESS) {
+ Slog.w(TAG, "health: cannot register callback: " + Result.toString(r));
+ return;
+ }
+ // registerCallback does NOT guarantee that update is called
+ // immediately, so request a manual update here.
+ newService.update();
+ } catch (RemoteException ex) {
+ Slog.e(TAG, "health: cannot register callback (transaction error): " + ex.getMessage());
+ } finally {
+ traceEnd();
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/INativeDaemonConnectorCallbacks.java b/services/core/java/com/android/server/health/HealthInfoCallback.java
index 0cf9dcde012d..c2a77fc862fa 100644
--- a/services/core/java/com/android/server/INativeDaemonConnectorCallbacks.java
+++ b/services/core/java/com/android/server/health/HealthInfoCallback.java
@@ -1,6 +1,5 @@
-
/*
- * Copyright (C) 2007 The Android Open Source Project
+ * Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,11 +14,18 @@
* limitations under the License.
*/
-package com.android.server;
-
-interface INativeDaemonConnectorCallbacks {
+package com.android.server.health;
- void onDaemonConnected();
- boolean onCheckHoldWakeLock(int code);
- boolean onEvent(int code, String raw, String[] cooked);
+/**
+ * A wrapper over HIDL / AIDL IHealthInfoCallback.
+ *
+ * @hide
+ */
+public interface HealthInfoCallback {
+ /**
+ * Signals to the client that health info is changed.
+ *
+ * @param props the new health info.
+ */
+ void update(android.hardware.health.HealthInfo props);
}
diff --git a/services/core/java/com/android/server/health/HealthRegCallbackAidl.java b/services/core/java/com/android/server/health/HealthRegCallbackAidl.java
new file mode 100644
index 000000000000..629011a86bb4
--- /dev/null
+++ b/services/core/java/com/android/server/health/HealthRegCallbackAidl.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.health;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.hardware.health.HealthInfo;
+import android.hardware.health.IHealth;
+import android.hardware.health.IHealthInfoCallback;
+import android.os.RemoteException;
+import android.os.Trace;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * On service registration, {@link #onRegistration} is called, which registers {@code this}, an
+ * {@link IHealthInfoCallback}, to the health service.
+ *
+ * <p>When the health service has updates to health info via {@link IHealthInfoCallback}, {@link
+ * HealthInfoCallback#update} is called.
+ *
+ * <p>AIDL variant of {@link HealthHalCallbackHidl}.
+ *
+ * @hide
+ */
+// It is made public so Mockito can access this class. It should have been package private if not
+// for testing.
+@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+public class HealthRegCallbackAidl {
+ private static final String TAG = "HealthRegCallbackAidl";
+ private final HealthInfoCallback mServiceInfoCallback;
+ private final IHealthInfoCallback mHalInfoCallback = new HalInfoCallback();
+
+ HealthRegCallbackAidl(@Nullable HealthInfoCallback healthInfoCallback) {
+ mServiceInfoCallback = healthInfoCallback;
+ }
+
+ /**
+ * Called when the service manager sees {@code newService} replacing {@code oldService}.
+ * This unregisters the health info callback from the old service (ignoring errors), then
+ * registers the health info callback to the new service.
+ *
+ * @param oldService the old IHealth service
+ * @param newService the new IHealth service
+ */
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ public void onRegistration(@Nullable IHealth oldService, @NonNull IHealth newService) {
+ if (mServiceInfoCallback == null) return;
+
+ Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "HealthUnregisterCallbackAidl");
+ try {
+ unregisterCallback(oldService, mHalInfoCallback);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
+ }
+
+ Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "HealthRegisterCallbackAidl");
+ try {
+ registerCallback(newService, mHalInfoCallback);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
+ }
+ }
+
+ private static void unregisterCallback(@Nullable IHealth oldService, IHealthInfoCallback cb) {
+ if (oldService == null) return;
+ try {
+ oldService.unregisterCallback(cb);
+ } catch (RemoteException e) {
+ // Ignore errors. The service might have died.
+ Slog.w(
+ TAG,
+ "health: cannot unregister previous callback (transaction error): "
+ + e.getMessage());
+ }
+ }
+
+ private static void registerCallback(@NonNull IHealth newService, IHealthInfoCallback cb) {
+ try {
+ newService.registerCallback(cb);
+ } catch (RemoteException e) {
+ Slog.e(
+ TAG,
+ "health: cannot register callback, framework may cease to"
+ + " receive updates on health / battery info!",
+ e);
+ return;
+ }
+ // registerCallback does NOT guarantee that update is called immediately, so request a
+ // manual update here.
+ try {
+ newService.update();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "health: cannot update after registering health info callback", e);
+ }
+ }
+
+ private class HalInfoCallback extends IHealthInfoCallback.Stub {
+ @Override
+ public void healthInfoChanged(HealthInfo healthInfo) throws RemoteException {
+ mServiceInfoCallback.update(healthInfo);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/health/HealthServiceWrapper.java b/services/core/java/com/android/server/health/HealthServiceWrapper.java
new file mode 100644
index 000000000000..25d1a885bc18
--- /dev/null
+++ b/services/core/java/com/android/server/health/HealthServiceWrapper.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.health;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.BatteryProperty;
+import android.os.HandlerThread;
+import android.os.IBatteryPropertiesRegistrar;
+import android.os.RemoteException;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.NoSuchElementException;
+
+/**
+ * HealthServiceWrapper wraps the internal IHealth service and refreshes the service when necessary.
+ * This is essentially a wrapper over IHealth that is useful for BatteryService.
+ *
+ * <p>The implementation may be backed by a HIDL or AIDL HAL.
+ *
+ * <p>On new registration of IHealth service, the internal service is refreshed. On death of an
+ * existing IHealth service, the internal service is NOT cleared to avoid race condition between
+ * death notification and new service notification. Hence, a caller must check for transaction
+ * errors when calling into the service.
+ *
+ * @hide Should only be used internally.
+ */
+public abstract class HealthServiceWrapper {
+ /** @return the handler thread. Exposed for testing. */
+ @VisibleForTesting
+ abstract HandlerThread getHandlerThread();
+
+ /**
+ * Calls into get*() functions in the health HAL. This reads into the kernel interfaces
+ * directly.
+ *
+ * @see IBatteryPropertiesRegistrar#getProperty
+ */
+ public abstract int getProperty(int id, BatteryProperty prop) throws RemoteException;
+
+ /**
+ * Calls update() in the health HAL.
+ *
+ * @see IBatteryPropertiesRegistrar#scheduleUpdate
+ */
+ public abstract void scheduleUpdate() throws RemoteException;
+
+ /**
+ * Calls into getHealthInfo() in the health HAL. This returns a cached value in the health HAL
+ * implementation.
+ *
+ * @return health info. {@code null} if no health HAL service. {@code null} if any
+ * service-specific error when calling {@code getHealthInfo}, e.g. it is unsupported.
+ * @throws RemoteException for any transaction-level errors
+ */
+ public abstract android.hardware.health.HealthInfo getHealthInfo() throws RemoteException;
+
+ /**
+ * Create a new HealthServiceWrapper instance.
+ *
+ * @param healthInfoCallback the callback to call when health info changes
+ * @return the new HealthServiceWrapper instance, which may be backed by HIDL or AIDL service.
+ * @throws RemoteException transaction errors
+ * @throws NoSuchElementException no HIDL or AIDL service is available
+ */
+ public static HealthServiceWrapper create(@Nullable HealthInfoCallback healthInfoCallback)
+ throws RemoteException, NoSuchElementException {
+ return create(
+ healthInfoCallback == null ? null : new HealthRegCallbackAidl(healthInfoCallback),
+ new HealthServiceWrapperAidl.ServiceManagerStub() {},
+ healthInfoCallback == null ? null : new HealthHalCallbackHidl(healthInfoCallback),
+ new HealthServiceWrapperHidl.IServiceManagerSupplier() {},
+ new HealthServiceWrapperHidl.IHealthSupplier() {});
+ }
+
+ /**
+ * Create a new HealthServiceWrapper instance for testing.
+ *
+ * @param aidlRegCallback callback for AIDL service registration, or {@code null} if the client
+ * does not care about AIDL service registration notifications
+ * @param aidlServiceManager Stub for AIDL ServiceManager
+ * @param hidlRegCallback callback for HIDL service registration, or {@code null} if the client
+ * does not care about HIDL service registration notifications
+ * @param hidlServiceManagerSupplier supplier of HIDL service manager
+ * @param hidlHealthSupplier supplier of HIDL health HAL
+ * @return the new HealthServiceWrapper instance, which may be backed by HIDL or AIDL service.
+ */
+ @VisibleForTesting
+ static @NonNull HealthServiceWrapper create(
+ @Nullable HealthRegCallbackAidl aidlRegCallback,
+ @NonNull HealthServiceWrapperAidl.ServiceManagerStub aidlServiceManager,
+ @Nullable HealthServiceWrapperHidl.Callback hidlRegCallback,
+ @NonNull HealthServiceWrapperHidl.IServiceManagerSupplier hidlServiceManagerSupplier,
+ @NonNull HealthServiceWrapperHidl.IHealthSupplier hidlHealthSupplier)
+ throws RemoteException, NoSuchElementException {
+ try {
+ return new HealthServiceWrapperAidl(aidlRegCallback, aidlServiceManager);
+ } catch (NoSuchElementException e) {
+ // Ignore, try HIDL
+ }
+ return new HealthServiceWrapperHidl(
+ hidlRegCallback, hidlServiceManagerSupplier, hidlHealthSupplier);
+ }
+}
diff --git a/services/core/java/com/android/server/health/HealthServiceWrapperAidl.java b/services/core/java/com/android/server/health/HealthServiceWrapperAidl.java
new file mode 100644
index 000000000000..4f2ed68974fa
--- /dev/null
+++ b/services/core/java/com/android/server/health/HealthServiceWrapperAidl.java
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.health;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.hardware.health.HealthInfo;
+import android.hardware.health.IHealth;
+import android.os.BatteryManager;
+import android.os.BatteryProperty;
+import android.os.Binder;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.IServiceCallback;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.ServiceSpecificException;
+import android.os.Trace;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.NoSuchElementException;
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * Implement {@link HealthServiceWrapper} backed by the AIDL HAL.
+ *
+ * @hide
+ */
+class HealthServiceWrapperAidl extends HealthServiceWrapper {
+ private static final String TAG = "HealthServiceWrapperAidl";
+ @VisibleForTesting static final String SERVICE_NAME = IHealth.DESCRIPTOR + "/default";
+ private final HandlerThread mHandlerThread = new HandlerThread("HealthServiceBinder");
+ private final AtomicReference<IHealth> mLastService = new AtomicReference<>();
+ private final IServiceCallback mServiceCallback = new ServiceCallback();
+ private final HealthRegCallbackAidl mRegCallback;
+
+ /** Stub interface into {@link ServiceManager} for testing. */
+ interface ServiceManagerStub {
+ default @Nullable IHealth waitForDeclaredService(@NonNull String name) {
+ return IHealth.Stub.asInterface(ServiceManager.waitForDeclaredService(name));
+ }
+
+ default void registerForNotifications(
+ @NonNull String name, @NonNull IServiceCallback callback) throws RemoteException {
+ ServiceManager.registerForNotifications(name, callback);
+ }
+ }
+
+ HealthServiceWrapperAidl(
+ @Nullable HealthRegCallbackAidl regCallback, @NonNull ServiceManagerStub serviceManager)
+ throws RemoteException, NoSuchElementException {
+
+ traceBegin("HealthInitGetServiceAidl");
+ IHealth newService;
+ try {
+ newService = serviceManager.waitForDeclaredService(SERVICE_NAME);
+ } finally {
+ traceEnd();
+ }
+ if (newService == null) {
+ throw new NoSuchElementException(
+ "IHealth service instance isn't available. Perhaps no permission?");
+ }
+ mLastService.set(newService);
+ mRegCallback = regCallback;
+ if (mRegCallback != null) {
+ mRegCallback.onRegistration(null /* oldService */, newService);
+ }
+
+ traceBegin("HealthInitRegisterNotificationAidl");
+ mHandlerThread.start();
+ try {
+ serviceManager.registerForNotifications(SERVICE_NAME, mServiceCallback);
+ } finally {
+ traceEnd();
+ }
+ Slog.i(TAG, "health: HealthServiceWrapper listening to AIDL HAL");
+ }
+
+ @Override
+ @VisibleForTesting
+ public HandlerThread getHandlerThread() {
+ return mHandlerThread;
+ }
+
+ @Override
+ public int getProperty(int id, BatteryProperty prop) throws RemoteException {
+ traceBegin("HealthGetPropertyAidl");
+ try {
+ return getPropertyInternal(id, prop);
+ } finally {
+ traceEnd();
+ }
+ }
+
+ private int getPropertyInternal(int id, BatteryProperty prop) throws RemoteException {
+ IHealth service = mLastService.get();
+ if (service == null) throw new RemoteException("no health service");
+ try {
+ switch (id) {
+ case BatteryManager.BATTERY_PROPERTY_CHARGE_COUNTER:
+ prop.setLong(service.getChargeCounterUah());
+ break;
+ case BatteryManager.BATTERY_PROPERTY_CURRENT_NOW:
+ prop.setLong(service.getCurrentNowMicroamps());
+ break;
+ case BatteryManager.BATTERY_PROPERTY_CURRENT_AVERAGE:
+ prop.setLong(service.getCurrentAverageMicroamps());
+ break;
+ case BatteryManager.BATTERY_PROPERTY_CAPACITY:
+ prop.setLong(service.getCapacity());
+ break;
+ case BatteryManager.BATTERY_PROPERTY_STATUS:
+ prop.setLong(service.getChargeStatus());
+ break;
+ case BatteryManager.BATTERY_PROPERTY_ENERGY_COUNTER:
+ prop.setLong(service.getEnergyCounterNwh());
+ break;
+ }
+ } catch (UnsupportedOperationException e) {
+ // Leave prop untouched.
+ return -1;
+ } catch (ServiceSpecificException e) {
+ // Leave prop untouched.
+ return -2;
+ }
+ // throws RemoteException as-is. BatteryManager wraps it into a RuntimeException
+ // and throw it to apps.
+
+ // If no error, return 0.
+ return 0;
+ }
+
+ @Override
+ public void scheduleUpdate() throws RemoteException {
+ getHandlerThread()
+ .getThreadHandler()
+ .post(
+ () -> {
+ traceBegin("HealthScheduleUpdate");
+ try {
+ IHealth service = mLastService.get();
+ if (service == null) {
+ Slog.e(TAG, "no health service");
+ return;
+ }
+ service.update();
+ } catch (RemoteException | ServiceSpecificException ex) {
+ Slog.e(TAG, "Cannot call update on health AIDL HAL", ex);
+ } finally {
+ traceEnd();
+ }
+ });
+ }
+
+ @Override
+ public HealthInfo getHealthInfo() throws RemoteException {
+ IHealth service = mLastService.get();
+ if (service == null) return null;
+ try {
+ return service.getHealthInfo();
+ } catch (UnsupportedOperationException | ServiceSpecificException ex) {
+ return null;
+ }
+ }
+
+ private static void traceBegin(String name) {
+ Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, name);
+ }
+
+ private static void traceEnd() {
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
+ }
+
+ private class ServiceCallback extends IServiceCallback.Stub {
+ @Override
+ public void onRegistration(String name, @NonNull final IBinder newBinder)
+ throws RemoteException {
+ if (!SERVICE_NAME.equals(name)) return;
+ // This runnable only runs on mHandlerThread and ordering is ensured, hence
+ // no locking is needed inside the runnable.
+ getHandlerThread()
+ .getThreadHandler()
+ .post(
+ () -> {
+ IHealth newService =
+ IHealth.Stub.asInterface(Binder.allowBlocking(newBinder));
+ IHealth oldService = mLastService.getAndSet(newService);
+ IBinder oldBinder =
+ oldService != null ? oldService.asBinder() : null;
+ if (Objects.equals(newBinder, oldBinder)) return;
+
+ Slog.i(TAG, "New health AIDL HAL service registered");
+ mRegCallback.onRegistration(oldService, newService);
+ });
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/health/HealthServiceWrapperHidl.java b/services/core/java/com/android/server/health/HealthServiceWrapperHidl.java
new file mode 100644
index 000000000000..0301174a45c6
--- /dev/null
+++ b/services/core/java/com/android/server/health/HealthServiceWrapperHidl.java
@@ -0,0 +1,313 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.health;
+
+import static android.hardware.health.Translate.h2aTranslate;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.hardware.health.HealthInfo;
+import android.hardware.health.V2_0.IHealth;
+import android.hardware.health.V2_0.Result;
+import android.hidl.manager.V1_0.IServiceManager;
+import android.hidl.manager.V1_0.IServiceNotification;
+import android.os.BatteryManager;
+import android.os.BatteryProperty;
+import android.os.HandlerThread;
+import android.os.RemoteException;
+import android.os.Trace;
+import android.util.MutableInt;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.NoSuchElementException;
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * Implement {@link HealthServiceWrapper} backed by the HIDL HAL.
+ *
+ * @hide
+ */
+final class HealthServiceWrapperHidl extends HealthServiceWrapper {
+ private static final String TAG = "HealthServiceWrapperHidl";
+ public static final String INSTANCE_VENDOR = "default";
+
+ private final IServiceNotification mNotification = new Notification();
+ private final HandlerThread mHandlerThread = new HandlerThread("HealthServiceHwbinder");
+ // These variables are fixed after init.
+ private Callback mCallback;
+ private IHealthSupplier mHealthSupplier;
+ private String mInstanceName;
+
+ // Last IHealth service received.
+ private final AtomicReference<IHealth> mLastService = new AtomicReference<>();
+
+ private static void traceBegin(String name) {
+ Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, name);
+ }
+
+ private static void traceEnd() {
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
+ }
+
+ @Override
+ public int getProperty(int id, final BatteryProperty prop) throws RemoteException {
+ traceBegin("HealthGetProperty");
+ try {
+ IHealth service = mLastService.get();
+ if (service == null) throw new RemoteException("no health service");
+ final MutableInt outResult = new MutableInt(Result.NOT_SUPPORTED);
+ switch (id) {
+ case BatteryManager.BATTERY_PROPERTY_CHARGE_COUNTER:
+ service.getChargeCounter(
+ (int result, int value) -> {
+ outResult.value = result;
+ if (result == Result.SUCCESS) prop.setLong(value);
+ });
+ break;
+ case BatteryManager.BATTERY_PROPERTY_CURRENT_NOW:
+ service.getCurrentNow(
+ (int result, int value) -> {
+ outResult.value = result;
+ if (result == Result.SUCCESS) prop.setLong(value);
+ });
+ break;
+ case BatteryManager.BATTERY_PROPERTY_CURRENT_AVERAGE:
+ service.getCurrentAverage(
+ (int result, int value) -> {
+ outResult.value = result;
+ if (result == Result.SUCCESS) prop.setLong(value);
+ });
+ break;
+ case BatteryManager.BATTERY_PROPERTY_CAPACITY:
+ service.getCapacity(
+ (int result, int value) -> {
+ outResult.value = result;
+ if (result == Result.SUCCESS) prop.setLong(value);
+ });
+ break;
+ case BatteryManager.BATTERY_PROPERTY_STATUS:
+ service.getChargeStatus(
+ (int result, int value) -> {
+ outResult.value = result;
+ if (result == Result.SUCCESS) prop.setLong(value);
+ });
+ break;
+ case BatteryManager.BATTERY_PROPERTY_ENERGY_COUNTER:
+ service.getEnergyCounter(
+ (int result, long value) -> {
+ outResult.value = result;
+ if (result == Result.SUCCESS) prop.setLong(value);
+ });
+ break;
+ }
+ return outResult.value;
+ } finally {
+ traceEnd();
+ }
+ }
+
+ @Override
+ public void scheduleUpdate() throws RemoteException {
+ getHandlerThread()
+ .getThreadHandler()
+ .post(
+ () -> {
+ traceBegin("HealthScheduleUpdate");
+ try {
+ IHealth service = mLastService.get();
+ if (service == null) {
+ Slog.e(TAG, "no health service");
+ return;
+ }
+ service.update();
+ } catch (RemoteException ex) {
+ Slog.e(TAG, "Cannot call update on health HAL", ex);
+ } finally {
+ traceEnd();
+ }
+ });
+ }
+
+ private static class Mutable<T> {
+ public T value;
+ }
+
+ @Override
+ public HealthInfo getHealthInfo() throws RemoteException {
+ IHealth service = mLastService.get();
+ if (service == null) return null;
+ final Mutable<HealthInfo> ret = new Mutable<>();
+ service.getHealthInfo(
+ (result, value) -> {
+ if (result == Result.SUCCESS) {
+ ret.value = h2aTranslate(value.legacy);
+ }
+ });
+ return ret.value;
+ }
+
+ /**
+ * Start monitoring registration of new IHealth services. Only instance {@link #INSTANCE_VENDOR}
+ * and in device / framework manifest are used. This function should only be called once.
+ *
+ * <p>mCallback.onRegistration() is called synchronously (aka in init thread) before this method
+ * returns if callback is not null.
+ *
+ * @throws RemoteException transaction error when talking to IServiceManager
+ * @throws NoSuchElementException if one of the following cases: - No service manager; - {@link
+ * #INSTANCE_VENDOR} is not in manifests (i.e. not available on this device), or none of
+ * these instances are available to current process.
+ * @throws NullPointerException when supplier is null
+ */
+ @VisibleForTesting
+ HealthServiceWrapperHidl(
+ @Nullable Callback callback,
+ @NonNull IServiceManagerSupplier managerSupplier,
+ @NonNull IHealthSupplier healthSupplier)
+ throws RemoteException, NoSuchElementException, NullPointerException {
+ if (managerSupplier == null || healthSupplier == null) {
+ throw new NullPointerException();
+ }
+ mHealthSupplier = healthSupplier;
+
+ // Initialize mLastService and call callback for the first time (in init thread)
+ IHealth newService = null;
+ traceBegin("HealthInitGetService_" + INSTANCE_VENDOR);
+ try {
+ newService = healthSupplier.get(INSTANCE_VENDOR);
+ } catch (NoSuchElementException ex) {
+ /* ignored, handled below */
+ } finally {
+ traceEnd();
+ }
+ if (newService != null) {
+ mInstanceName = INSTANCE_VENDOR;
+ mLastService.set(newService);
+ }
+
+ if (mInstanceName == null || newService == null) {
+ throw new NoSuchElementException(
+ String.format(
+ "IHealth service instance %s isn't available. Perhaps no permission?",
+ INSTANCE_VENDOR));
+ }
+
+ if (callback != null) {
+ mCallback = callback;
+ mCallback.onRegistration(null, newService, mInstanceName);
+ }
+
+ // Register for future service registrations
+ traceBegin("HealthInitRegisterNotification");
+ mHandlerThread.start();
+ try {
+ managerSupplier
+ .get()
+ .registerForNotifications(IHealth.kInterfaceName, mInstanceName, mNotification);
+ } finally {
+ traceEnd();
+ }
+ Slog.i(TAG, "health: HealthServiceWrapper listening to instance " + mInstanceName);
+ }
+
+ @VisibleForTesting
+ public HandlerThread getHandlerThread() {
+ return mHandlerThread;
+ }
+
+ /** Service registration callback. */
+ interface Callback {
+ /**
+ * This function is invoked asynchronously when a new and related IServiceNotification is
+ * received.
+ *
+ * @param service the recently retrieved service from IServiceManager. Can be a dead service
+ * before service notification of a new service is delivered. Implementation must handle
+ * cases for {@link RemoteException}s when calling into service.
+ * @param instance instance name.
+ */
+ void onRegistration(IHealth oldService, IHealth newService, String instance);
+ }
+
+ /**
+ * Supplier of services. Must not return null; throw {@link NoSuchElementException} if a service
+ * is not available.
+ */
+ interface IServiceManagerSupplier {
+ default IServiceManager get() throws NoSuchElementException, RemoteException {
+ return IServiceManager.getService();
+ }
+ }
+
+ /**
+ * Supplier of services. Must not return null; throw {@link NoSuchElementException} if a service
+ * is not available.
+ */
+ interface IHealthSupplier {
+ default IHealth get(String name) throws NoSuchElementException, RemoteException {
+ return IHealth.getService(name, true /* retry */);
+ }
+ }
+
+ private class Notification extends IServiceNotification.Stub {
+ @Override
+ public final void onRegistration(
+ String interfaceName, String instanceName, boolean preexisting) {
+ if (!IHealth.kInterfaceName.equals(interfaceName)) return;
+ if (!mInstanceName.equals(instanceName)) return;
+
+ // This runnable only runs on mHandlerThread and ordering is ensured, hence
+ // no locking is needed inside the runnable.
+ mHandlerThread
+ .getThreadHandler()
+ .post(
+ new Runnable() {
+ @Override
+ public void run() {
+ try {
+ IHealth newService = mHealthSupplier.get(mInstanceName);
+ IHealth oldService = mLastService.getAndSet(newService);
+
+ // preexisting may be inaccurate (race). Check for equality
+ // here.
+ if (Objects.equals(newService, oldService)) return;
+
+ Slog.i(
+ TAG,
+ "health: new instance registered " + mInstanceName);
+ // #init() may be called with null callback. Skip null
+ // callbacks.
+ if (mCallback == null) return;
+ mCallback.onRegistration(
+ oldService, newService, mInstanceName);
+ } catch (NoSuchElementException | RemoteException ex) {
+ Slog.e(
+ TAG,
+ "health: Cannot get instance '"
+ + mInstanceName
+ + "': "
+ + ex.getMessage()
+ + ". Perhaps no permission?");
+ }
+ }
+ });
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/health/OWNERS b/services/core/java/com/android/server/health/OWNERS
new file mode 100644
index 000000000000..81522fcaa09f
--- /dev/null
+++ b/services/core/java/com/android/server/health/OWNERS
@@ -0,0 +1 @@
+file:platform/hardware/interfaces:/health/aidl/OWNERS
diff --git a/services/core/java/com/android/server/health/Utils.java b/services/core/java/com/android/server/health/Utils.java
new file mode 100644
index 000000000000..a8c978c50e42
--- /dev/null
+++ b/services/core/java/com/android/server/health/Utils.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.health;
+
+/**
+ * Utils for {@link om.android.server.BatteryService} to deal with health info structs.
+ *
+ * @hide
+ */
+public class Utils {
+ private Utils() {}
+
+ /**
+ * Copy health info struct.
+ *
+ * @param dst destination
+ * @param src source
+ */
+ public static void copy(
+ android.hardware.health.V1_0.HealthInfo dst,
+ android.hardware.health.V1_0.HealthInfo src) {
+ dst.chargerAcOnline = src.chargerAcOnline;
+ dst.chargerUsbOnline = src.chargerUsbOnline;
+ dst.chargerWirelessOnline = src.chargerWirelessOnline;
+ dst.maxChargingCurrent = src.maxChargingCurrent;
+ dst.maxChargingVoltage = src.maxChargingVoltage;
+ dst.batteryStatus = src.batteryStatus;
+ dst.batteryHealth = src.batteryHealth;
+ dst.batteryPresent = src.batteryPresent;
+ dst.batteryLevel = src.batteryLevel;
+ dst.batteryVoltage = src.batteryVoltage;
+ dst.batteryTemperature = src.batteryTemperature;
+ dst.batteryCurrent = src.batteryCurrent;
+ dst.batteryCycleCount = src.batteryCycleCount;
+ dst.batteryFullCharge = src.batteryFullCharge;
+ dst.batteryChargeCounter = src.batteryChargeCounter;
+ dst.batteryTechnology = src.batteryTechnology;
+ }
+
+ /**
+ * Copy battery fields of {@link android.hardware.health.HealthInfo} V1. This excludes
+ * non-battery fields like {@link android.hardware.health.HealthInfo#diskStats diskStats} and
+ * {@link android.hardware.health.HealthInfo#storageInfos storageInfos}
+ *
+ * @param dst destination
+ * @param src source
+ */
+ public static void copyV1Battery(
+ android.hardware.health.HealthInfo dst, android.hardware.health.HealthInfo src) {
+ dst.chargerAcOnline = src.chargerAcOnline;
+ dst.chargerUsbOnline = src.chargerUsbOnline;
+ dst.chargerWirelessOnline = src.chargerWirelessOnline;
+ dst.maxChargingCurrentMicroamps = src.maxChargingCurrentMicroamps;
+ dst.maxChargingVoltageMicrovolts = src.maxChargingVoltageMicrovolts;
+ dst.batteryStatus = src.batteryStatus;
+ dst.batteryHealth = src.batteryHealth;
+ dst.batteryPresent = src.batteryPresent;
+ dst.batteryLevel = src.batteryLevel;
+ dst.batteryVoltageMillivolts = src.batteryVoltageMillivolts;
+ dst.batteryTemperatureTenthsCelsius = src.batteryTemperatureTenthsCelsius;
+ dst.batteryCurrentMicroamps = src.batteryCurrentMicroamps;
+ dst.batteryCycleCount = src.batteryCycleCount;
+ dst.batteryFullChargeUah = src.batteryFullChargeUah;
+ dst.batteryChargeCounterUah = src.batteryChargeCounterUah;
+ dst.batteryTechnology = src.batteryTechnology;
+ dst.batteryCurrentAverageMicroamps = src.batteryCurrentAverageMicroamps;
+ dst.batteryCapacityLevel = src.batteryCapacityLevel;
+ dst.batteryChargeTimeToFullNowSeconds = src.batteryChargeTimeToFullNowSeconds;
+ dst.batteryFullChargeDesignCapacityUah = src.batteryFullChargeDesignCapacityUah;
+ }
+}
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index fae7e451b529..0bf110acdedd 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -2872,8 +2872,7 @@ public class InputManagerService extends IInputManager.Stub
};
for (File baseDir: baseDirs) {
File confFile = new File(baseDir, EXCLUDED_DEVICES_PATH);
- try {
- InputStream stream = new FileInputStream(confFile);
+ try (InputStream stream = new FileInputStream(confFile)) {
names.addAll(ConfigurationProcessor.processExcludedDeviceNames(stream));
} catch (FileNotFoundException e) {
// It's ok if the file does not exist.
@@ -2906,8 +2905,7 @@ public class InputManagerService extends IInputManager.Stub
final File baseDir = Environment.getVendorDirectory();
final File confFile = new File(baseDir, PORT_ASSOCIATIONS_PATH);
- try {
- final InputStream stream = new FileInputStream(confFile);
+ try (final InputStream stream = new FileInputStream(confFile)) {
return ConfigurationProcessor.processInputPortAssociations(stream);
} catch (FileNotFoundException e) {
// Most of the time, file will not exist, which is expected.
@@ -3047,10 +3045,10 @@ public class InputManagerService extends IInputManager.Stub
@Override
public void visitKeyboardLayout(Resources resources,
int keyboardLayoutResId, KeyboardLayout layout) {
- try {
+ try (final InputStreamReader stream = new InputStreamReader(
+ resources.openRawResource(keyboardLayoutResId))) {
result[0] = layout.getDescriptor();
- result[1] = Streams.readFully(new InputStreamReader(
- resources.openRawResource(keyboardLayoutResId)));
+ result[1] = Streams.readFully(stream);
} catch (IOException ex) {
} catch (NotFoundException ex) {
}
diff --git a/services/core/java/com/android/server/inputmethod/OWNERS b/services/core/java/com/android/server/inputmethod/OWNERS
index c09ade9e075f..00cd700541c0 100644
--- a/services/core/java/com/android/server/inputmethod/OWNERS
+++ b/services/core/java/com/android/server/inputmethod/OWNERS
@@ -5,3 +5,4 @@ yukawa@google.com
tarandeep@google.com
lumark@google.com
roosa@google.com
+wilsonwu@google.com
diff --git a/services/core/java/com/android/server/location/listeners/PendingIntentListenerRegistration.java b/services/core/java/com/android/server/location/listeners/PendingIntentListenerRegistration.java
index 0aafb2929d56..240ac0144293 100644
--- a/services/core/java/com/android/server/location/listeners/PendingIntentListenerRegistration.java
+++ b/services/core/java/com/android/server/location/listeners/PendingIntentListenerRegistration.java
@@ -80,7 +80,7 @@ public abstract class PendingIntentListenerRegistration<TRequest, TListener> ext
}
@Override
- public void onCancelled(PendingIntent intent) {
+ public void onCanceled(PendingIntent intent) {
if (Log.isLoggable(getOwner().getTag(), Log.DEBUG)) {
Log.d(getOwner().getTag(),
"pending intent registration " + getIdentity() + " canceled");
diff --git a/services/core/java/com/android/server/location/provider/LocationProviderManager.java b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
index 9ed63b5ce2da..39c20ef93228 100644
--- a/services/core/java/com/android/server/location/provider/LocationProviderManager.java
+++ b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
@@ -1132,9 +1132,9 @@ public class LocationProviderManager extends
}
@Override
- public void onCancelled(PendingIntent intent) {
+ public void onCanceled(PendingIntent intent) {
if (D) {
- Log.d(TAG, mName + " provider registration " + getIdentity() + " cancelled");
+ Log.d(TAG, mName + " provider registration " + getIdentity() + " canceled");
}
synchronized (mLock) {
diff --git a/services/core/java/com/android/server/media/BluetoothRouteProvider.java b/services/core/java/com/android/server/media/BluetoothRouteProvider.java
index 7afa81aa047d..91de9e559e13 100644
--- a/services/core/java/com/android/server/media/BluetoothRouteProvider.java
+++ b/services/core/java/com/android/server/media/BluetoothRouteProvider.java
@@ -26,6 +26,7 @@ import android.bluetooth.BluetoothA2dp;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHearingAid;
+import android.bluetooth.BluetoothLeAudio;
import android.bluetooth.BluetoothProfile;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -50,12 +51,14 @@ import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.Set;
class BluetoothRouteProvider {
private static final String TAG = "BTRouteProvider";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private static final String HEARING_AID_ROUTE_ID_PREFIX = "HEARING_AID_";
+ private static final String LE_AUDIO_ROUTE_ID_PREFIX = "LE_AUDIO_";
@SuppressWarnings("WeakerAccess") /* synthetic access */
// Maps hardware address to BluetoothRouteInfo
@@ -66,6 +69,8 @@ class BluetoothRouteProvider {
BluetoothA2dp mA2dpProfile;
@SuppressWarnings("WeakerAccess") /* synthetic access */
BluetoothHearingAid mHearingAidProfile;
+ @SuppressWarnings("WeakerAccess") /* synthetic access */
+ BluetoothLeAudio mLeAudioProfile;
// Route type -> volume map
private final SparseIntArray mVolumeMap = new SparseIntArray();
@@ -108,6 +113,7 @@ class BluetoothRouteProvider {
public void start(UserHandle user) {
mBluetoothAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.A2DP);
mBluetoothAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.HEARING_AID);
+ mBluetoothAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.LE_AUDIO);
// Bluetooth on/off broadcasts
addEventReceiver(BluetoothAdapter.ACTION_STATE_CHANGED, new AdapterStateChangedReceiver());
@@ -119,6 +125,10 @@ class BluetoothRouteProvider {
deviceStateChangedReceiver);
addEventReceiver(BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED,
deviceStateChangedReceiver);
+ addEventReceiver(BluetoothLeAudio.ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED,
+ deviceStateChangedReceiver);
+ addEventReceiver(BluetoothLeAudio.ACTION_LE_AUDIO_ACTIVE_DEVICE_CHANGED,
+ deviceStateChangedReceiver);
mContext.registerReceiverAsUser(mBroadcastReceiver, user,
mIntentFilter, null, null);
@@ -165,8 +175,9 @@ class BluetoothRouteProvider {
private void buildBluetoothRoutes() {
mBluetoothRoutes.clear();
- if (mBluetoothAdapter.getBondedDevices() != null) {
- for (BluetoothDevice device : mBluetoothAdapter.getBondedDevices()) {
+ Set<BluetoothDevice> bondedDevices = mBluetoothAdapter.getBondedDevices();
+ if (bondedDevices != null) {
+ for (BluetoothDevice device : bondedDevices) {
if (device.isConnected()) {
BluetoothRouteInfo newBtRoute = createBluetoothRoute(device);
if (newBtRoute.connectedProfiles.size() > 0) {
@@ -240,6 +251,8 @@ class BluetoothRouteProvider {
| AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES
| AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER)) != 0) {
routeType = MediaRoute2Info.TYPE_BLUETOOTH_A2DP;
+ } else if ((devices & (AudioManager.DEVICE_OUT_BLE_HEADSET)) != 0) {
+ routeType = MediaRoute2Info.TYPE_BLE_HEADSET;
} else {
return false;
}
@@ -288,6 +301,12 @@ class BluetoothRouteProvider {
routeId = HEARING_AID_ROUTE_ID_PREFIX + mHearingAidProfile.getHiSyncId(device);
type = MediaRoute2Info.TYPE_HEARING_AID;
}
+ if (mLeAudioProfile != null
+ && mLeAudioProfile.getConnectedDevices().contains(device)) {
+ newBtRoute.connectedProfiles.put(BluetoothProfile.LE_AUDIO, true);
+ routeId = LE_AUDIO_ROUTE_ID_PREFIX + mLeAudioProfile.getGroupId(device);
+ type = MediaRoute2Info.TYPE_BLE_HEADSET;
+ }
// Current volume will be set when connected.
newBtRoute.route = new MediaRoute2Info.Builder(routeId, deviceName)
@@ -325,10 +344,19 @@ class BluetoothRouteProvider {
}
private void addActiveRoute(BluetoothRouteInfo btRoute) {
+ if (btRoute == null) {
+ if (DEBUG) {
+ Log.d(TAG, " btRoute is null");
+ }
+ return;
+ }
if (DEBUG) {
Log.d(TAG, "Adding active route: " + btRoute.route);
}
- if (btRoute == null || mActiveRoutes.contains(btRoute)) {
+ if (mActiveRoutes.contains(btRoute)) {
+ if (DEBUG) {
+ Log.d(TAG, " btRoute is already added.");
+ }
return;
}
setRouteConnectionState(btRoute, STATE_CONNECTED);
@@ -358,11 +386,7 @@ class BluetoothRouteProvider {
}
}
- private void addActiveHearingAidDevices(BluetoothDevice device) {
- if (DEBUG) {
- Log.d(TAG, "Setting active hearing aid devices. device=" + device);
- }
-
+ private void addActiveDevices(BluetoothDevice device) {
// Let the given device be the first active device
BluetoothRouteInfo activeBtRoute = mBluetoothRoutes.get(device.getAddress());
addActiveRoute(activeBtRoute);
@@ -376,6 +400,21 @@ class BluetoothRouteProvider {
}
}
}
+ private void addActiveHearingAidDevices(BluetoothDevice device) {
+ if (DEBUG) {
+ Log.d(TAG, "Setting active hearing aid devices. device=" + device);
+ }
+
+ addActiveDevices(device);
+ }
+
+ private void addActiveLeAudioDevices(BluetoothDevice device) {
+ if (DEBUG) {
+ Log.d(TAG, "Setting active le audio devices. device=" + device);
+ }
+
+ addActiveDevices(device);
+ }
interface BluetoothRoutesUpdatedListener {
void onBluetoothRoutesUpdated(@NonNull List<MediaRoute2Info> routes);
@@ -392,6 +431,11 @@ class BluetoothRouteProvider {
if (connectedProfiles.get(BluetoothProfile.HEARING_AID, false)) {
return MediaRoute2Info.TYPE_HEARING_AID;
}
+
+ if (connectedProfiles.get(BluetoothProfile.LE_AUDIO, false)) {
+ return MediaRoute2Info.TYPE_BLE_HEADSET;
+ }
+
return MediaRoute2Info.TYPE_BLUETOOTH_A2DP;
}
}
@@ -410,6 +454,10 @@ class BluetoothRouteProvider {
mHearingAidProfile = (BluetoothHearingAid) proxy;
activeDevices = mHearingAidProfile.getActiveDevices();
break;
+ case BluetoothProfile.LE_AUDIO:
+ mLeAudioProfile = (BluetoothLeAudio) proxy;
+ activeDevices = mLeAudioProfile.getActiveDevices();
+ break;
default:
return;
}
@@ -434,6 +482,9 @@ class BluetoothRouteProvider {
case BluetoothProfile.HEARING_AID:
mHearingAidProfile = null;
break;
+ case BluetoothProfile.LE_AUDIO:
+ mLeAudioProfile = null;
+ break;
default:
return;
}
@@ -490,12 +541,22 @@ class BluetoothRouteProvider {
}
notifyBluetoothRoutesUpdated();
break;
+ case BluetoothLeAudio.ACTION_LE_AUDIO_ACTIVE_DEVICE_CHANGED:
+ clearActiveRoutesWithType(MediaRoute2Info.TYPE_BLE_HEADSET);
+ if (device != null) {
+ addActiveLeAudioDevices(device);
+ }
+ notifyBluetoothRoutesUpdated();
+ break;
case BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED:
handleConnectionStateChanged(BluetoothProfile.A2DP, intent, device);
break;
case BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED:
handleConnectionStateChanged(BluetoothProfile.HEARING_AID, intent, device);
break;
+ case BluetoothLeAudio.ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED:
+ handleConnectionStateChanged(BluetoothProfile.LE_AUDIO, intent, device);
+ break;
}
}
diff --git a/services/core/java/com/android/server/media/OWNERS b/services/core/java/com/android/server/media/OWNERS
index 2e2d812c058e..8097f4e9b329 100644
--- a/services/core/java/com/android/server/media/OWNERS
+++ b/services/core/java/com/android/server/media/OWNERS
@@ -1,8 +1,6 @@
+# Bug component: 137631
elaurent@google.com
-hdmoon@google.com
-insun@google.com
-jaewan@google.com
-jinpark@google.com
-klhyun@google.com
lajos@google.com
-sungsoo@google.com
+
+# go/android-fwk-media-solutions for info on areas of ownership.
+include platform/frameworks/av:/media/janitors/media_solutions_OWNERS
diff --git a/services/core/java/com/android/server/net/DelayedDiskWrite.java b/services/core/java/com/android/server/net/DelayedDiskWrite.java
deleted file mode 100644
index 8f09eb7c19ab..000000000000
--- a/services/core/java/com/android/server/net/DelayedDiskWrite.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright (C) 2014 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.net;
-
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.text.TextUtils;
-import android.util.Log;
-
-import java.io.BufferedOutputStream;
-import java.io.DataOutputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-
-public class DelayedDiskWrite {
- private HandlerThread mDiskWriteHandlerThread;
- private Handler mDiskWriteHandler;
- /* Tracks multiple writes on the same thread */
- private int mWriteSequence = 0;
- private final String TAG = "DelayedDiskWrite";
-
- public interface Writer {
- public void onWriteCalled(DataOutputStream out) throws IOException;
- }
-
- public void write(final String filePath, final Writer w) {
- write(filePath, w, true);
- }
-
- public void write(final String filePath, final Writer w, final boolean open) {
- if (TextUtils.isEmpty(filePath)) {
- throw new IllegalArgumentException("empty file path");
- }
-
- /* Do a delayed write to disk on a separate handler thread */
- synchronized (this) {
- if (++mWriteSequence == 1) {
- mDiskWriteHandlerThread = new HandlerThread("DelayedDiskWriteThread");
- mDiskWriteHandlerThread.start();
- mDiskWriteHandler = new Handler(mDiskWriteHandlerThread.getLooper());
- }
- }
-
- mDiskWriteHandler.post(new Runnable() {
- @Override
- public void run() {
- doWrite(filePath, w, open);
- }
- });
- }
-
- private void doWrite(String filePath, Writer w, boolean open) {
- DataOutputStream out = null;
- try {
- if (open) {
- out = new DataOutputStream(new BufferedOutputStream(
- new FileOutputStream(filePath)));
- }
- w.onWriteCalled(out);
- } catch (IOException e) {
- loge("Error writing data file " + filePath);
- } finally {
- if (out != null) {
- try {
- out.close();
- } catch (Exception e) {}
- }
-
- // Quit if no more writes sent
- synchronized (this) {
- if (--mWriteSequence == 0) {
- mDiskWriteHandler.getLooper().quit();
- mDiskWriteHandler = null;
- mDiskWriteHandlerThread = null;
- }
- }
- }
- }
-
- private void loge(String s) {
- Log.e(TAG, s);
- }
-}
-
diff --git a/services/core/java/com/android/server/net/IpConfigStore.java b/services/core/java/com/android/server/net/IpConfigStore.java
deleted file mode 100644
index df1eb6d9fe3c..000000000000
--- a/services/core/java/com/android/server/net/IpConfigStore.java
+++ /dev/null
@@ -1,434 +0,0 @@
-/*
- * Copyright (C) 2014 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.net;
-
-import android.net.InetAddresses;
-import android.net.IpConfiguration;
-import android.net.IpConfiguration.IpAssignment;
-import android.net.IpConfiguration.ProxySettings;
-import android.net.LinkAddress;
-import android.net.ProxyInfo;
-import android.net.StaticIpConfiguration;
-import android.net.Uri;
-import android.util.ArrayMap;
-import android.util.Log;
-import android.util.SparseArray;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.net.module.util.ProxyUtils;
-
-import java.io.BufferedInputStream;
-import java.io.DataInputStream;
-import java.io.DataOutputStream;
-import java.io.EOFException;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.Inet4Address;
-import java.net.InetAddress;
-import java.util.ArrayList;
-import java.util.List;
-
-public class IpConfigStore {
- private static final String TAG = "IpConfigStore";
- private static final boolean DBG = false;
-
- protected final DelayedDiskWrite mWriter;
-
- /* IP and proxy configuration keys */
- protected static final String ID_KEY = "id";
- protected static final String IP_ASSIGNMENT_KEY = "ipAssignment";
- protected static final String LINK_ADDRESS_KEY = "linkAddress";
- protected static final String GATEWAY_KEY = "gateway";
- protected static final String DNS_KEY = "dns";
- protected static final String PROXY_SETTINGS_KEY = "proxySettings";
- protected static final String PROXY_HOST_KEY = "proxyHost";
- protected static final String PROXY_PORT_KEY = "proxyPort";
- protected static final String PROXY_PAC_FILE = "proxyPac";
- protected static final String EXCLUSION_LIST_KEY = "exclusionList";
- protected static final String EOS = "eos";
-
- protected static final int IPCONFIG_FILE_VERSION = 3;
-
- public IpConfigStore(DelayedDiskWrite writer) {
- mWriter = writer;
- }
-
- public IpConfigStore() {
- this(new DelayedDiskWrite());
- }
-
- private static boolean writeConfig(DataOutputStream out, String configKey,
- IpConfiguration config) throws IOException {
- return writeConfig(out, configKey, config, IPCONFIG_FILE_VERSION);
- }
-
- @VisibleForTesting
- public static boolean writeConfig(DataOutputStream out, String configKey,
- IpConfiguration config, int version) throws IOException {
- boolean written = false;
-
- try {
- switch (config.getIpAssignment()) {
- case STATIC:
- out.writeUTF(IP_ASSIGNMENT_KEY);
- out.writeUTF(config.getIpAssignment().toString());
- StaticIpConfiguration staticIpConfiguration = config.getStaticIpConfiguration();
- if (staticIpConfiguration != null) {
- if (staticIpConfiguration.getIpAddress() != null) {
- LinkAddress ipAddress = staticIpConfiguration.getIpAddress();
- out.writeUTF(LINK_ADDRESS_KEY);
- out.writeUTF(ipAddress.getAddress().getHostAddress());
- out.writeInt(ipAddress.getPrefixLength());
- }
- if (staticIpConfiguration.getGateway() != null) {
- out.writeUTF(GATEWAY_KEY);
- out.writeInt(0); // Default route.
- out.writeInt(1); // Have a gateway.
- out.writeUTF(staticIpConfiguration.getGateway().getHostAddress());
- }
- for (InetAddress inetAddr : staticIpConfiguration.getDnsServers()) {
- out.writeUTF(DNS_KEY);
- out.writeUTF(inetAddr.getHostAddress());
- }
- }
- written = true;
- break;
- case DHCP:
- out.writeUTF(IP_ASSIGNMENT_KEY);
- out.writeUTF(config.getIpAssignment().toString());
- written = true;
- break;
- case UNASSIGNED:
- /* Ignore */
- break;
- default:
- loge("Ignore invalid ip assignment while writing");
- break;
- }
-
- switch (config.getProxySettings()) {
- case STATIC:
- ProxyInfo proxyProperties = config.getHttpProxy();
- String exclusionList = ProxyUtils.exclusionListAsString(
- proxyProperties.getExclusionList());
- out.writeUTF(PROXY_SETTINGS_KEY);
- out.writeUTF(config.getProxySettings().toString());
- out.writeUTF(PROXY_HOST_KEY);
- out.writeUTF(proxyProperties.getHost());
- out.writeUTF(PROXY_PORT_KEY);
- out.writeInt(proxyProperties.getPort());
- if (exclusionList != null) {
- out.writeUTF(EXCLUSION_LIST_KEY);
- out.writeUTF(exclusionList);
- }
- written = true;
- break;
- case PAC:
- ProxyInfo proxyPacProperties = config.getHttpProxy();
- out.writeUTF(PROXY_SETTINGS_KEY);
- out.writeUTF(config.getProxySettings().toString());
- out.writeUTF(PROXY_PAC_FILE);
- out.writeUTF(proxyPacProperties.getPacFileUrl().toString());
- written = true;
- break;
- case NONE:
- out.writeUTF(PROXY_SETTINGS_KEY);
- out.writeUTF(config.getProxySettings().toString());
- written = true;
- break;
- case UNASSIGNED:
- /* Ignore */
- break;
- default:
- loge("Ignore invalid proxy settings while writing");
- break;
- }
-
- if (written) {
- out.writeUTF(ID_KEY);
- if (version < 3) {
- out.writeInt(Integer.valueOf(configKey));
- } else {
- out.writeUTF(configKey);
- }
- }
- } catch (NullPointerException e) {
- loge("Failure in writing " + config + e);
- }
- out.writeUTF(EOS);
-
- return written;
- }
-
- /**
- * @Deprecated use {@link #writeIpConfigurations(String, ArrayMap)} instead.
- * New method uses string as network identifier which could be interface name or MAC address or
- * other token.
- */
- @Deprecated
- public void writeIpAndProxyConfigurationsToFile(String filePath,
- final SparseArray<IpConfiguration> networks) {
- mWriter.write(filePath, out -> {
- out.writeInt(IPCONFIG_FILE_VERSION);
- for(int i = 0; i < networks.size(); i++) {
- writeConfig(out, String.valueOf(networks.keyAt(i)), networks.valueAt(i));
- }
- });
- }
-
- public void writeIpConfigurations(String filePath,
- ArrayMap<String, IpConfiguration> networks) {
- mWriter.write(filePath, out -> {
- out.writeInt(IPCONFIG_FILE_VERSION);
- for(int i = 0; i < networks.size(); i++) {
- writeConfig(out, networks.keyAt(i), networks.valueAt(i));
- }
- });
- }
-
- public static ArrayMap<String, IpConfiguration> readIpConfigurations(String filePath) {
- BufferedInputStream bufferedInputStream;
- try {
- bufferedInputStream = new BufferedInputStream(new FileInputStream(filePath));
- } catch (FileNotFoundException e) {
- // Return an empty array here because callers expect an empty array when the file is
- // not present.
- loge("Error opening configuration file: " + e);
- return new ArrayMap<>(0);
- }
- return readIpConfigurations(bufferedInputStream);
- }
-
- /** @Deprecated use {@link #readIpConfigurations(String)} */
- @Deprecated
- public static SparseArray<IpConfiguration> readIpAndProxyConfigurations(String filePath) {
- BufferedInputStream bufferedInputStream;
- try {
- bufferedInputStream = new BufferedInputStream(new FileInputStream(filePath));
- } catch (FileNotFoundException e) {
- // Return an empty array here because callers expect an empty array when the file is
- // not present.
- loge("Error opening configuration file: " + e);
- return new SparseArray<>();
- }
- return readIpAndProxyConfigurations(bufferedInputStream);
- }
-
- /** @Deprecated use {@link #readIpConfigurations(InputStream)} */
- @Deprecated
- public static SparseArray<IpConfiguration> readIpAndProxyConfigurations(
- InputStream inputStream) {
- ArrayMap<String, IpConfiguration> networks = readIpConfigurations(inputStream);
- if (networks == null) {
- return null;
- }
-
- SparseArray<IpConfiguration> networksById = new SparseArray<>();
- for (int i = 0; i < networks.size(); i++) {
- int id = Integer.valueOf(networks.keyAt(i));
- networksById.put(id, networks.valueAt(i));
- }
-
- return networksById;
- }
-
- /** Returns a map of network identity token and {@link IpConfiguration}. */
- public static ArrayMap<String, IpConfiguration> readIpConfigurations(
- InputStream inputStream) {
- ArrayMap<String, IpConfiguration> networks = new ArrayMap<>();
- DataInputStream in = null;
- try {
- in = new DataInputStream(inputStream);
-
- int version = in.readInt();
- if (version != 3 && version != 2 && version != 1) {
- loge("Bad version on IP configuration file, ignore read");
- return null;
- }
-
- while (true) {
- String uniqueToken = null;
- // Default is DHCP with no proxy
- IpAssignment ipAssignment = IpAssignment.DHCP;
- ProxySettings proxySettings = ProxySettings.NONE;
- StaticIpConfiguration staticIpConfiguration = new StaticIpConfiguration();
- LinkAddress linkAddress = null;
- InetAddress gatewayAddress = null;
- String proxyHost = null;
- String pacFileUrl = null;
- int proxyPort = -1;
- String exclusionList = null;
- String key;
- final List<InetAddress> dnsServers = new ArrayList<>();
-
- do {
- key = in.readUTF();
- try {
- if (key.equals(ID_KEY)) {
- if (version < 3) {
- int id = in.readInt();
- uniqueToken = String.valueOf(id);
- } else {
- uniqueToken = in.readUTF();
- }
- } else if (key.equals(IP_ASSIGNMENT_KEY)) {
- ipAssignment = IpAssignment.valueOf(in.readUTF());
- } else if (key.equals(LINK_ADDRESS_KEY)) {
- LinkAddress parsedLinkAddress =
- new LinkAddress(
- InetAddresses.parseNumericAddress(in.readUTF()),
- in.readInt());
- if (parsedLinkAddress.getAddress() instanceof Inet4Address
- && linkAddress == null) {
- linkAddress = parsedLinkAddress;
- } else {
- loge("Non-IPv4 or duplicate address: " + parsedLinkAddress);
- }
- } else if (key.equals(GATEWAY_KEY)) {
- LinkAddress dest = null;
- InetAddress gateway = null;
- if (version == 1) {
- // only supported default gateways - leave the dest/prefix empty
- gateway = InetAddresses.parseNumericAddress(in.readUTF());
- if (gatewayAddress == null) {
- gatewayAddress = gateway;
- } else {
- loge("Duplicate gateway: " + gateway.getHostAddress());
- }
- } else {
- if (in.readInt() == 1) {
- dest =
- new LinkAddress(
- InetAddresses.parseNumericAddress(in.readUTF()),
- in.readInt());
- }
- if (in.readInt() == 1) {
- gateway = InetAddresses.parseNumericAddress(in.readUTF());
- }
- // If the destination is a default IPv4 route, use the gateway
- // address unless already set.
- if (dest.getAddress() instanceof Inet4Address
- && dest.getPrefixLength() == 0 && gatewayAddress == null) {
- gatewayAddress = gateway;
- } else {
- loge("Non-IPv4 default or duplicate route: "
- + dest.getAddress());
- }
- }
- } else if (key.equals(DNS_KEY)) {
- dnsServers.add(InetAddresses.parseNumericAddress(in.readUTF()));
- } else if (key.equals(PROXY_SETTINGS_KEY)) {
- proxySettings = ProxySettings.valueOf(in.readUTF());
- } else if (key.equals(PROXY_HOST_KEY)) {
- proxyHost = in.readUTF();
- } else if (key.equals(PROXY_PORT_KEY)) {
- proxyPort = in.readInt();
- } else if (key.equals(PROXY_PAC_FILE)) {
- pacFileUrl = in.readUTF();
- } else if (key.equals(EXCLUSION_LIST_KEY)) {
- exclusionList = in.readUTF();
- } else if (key.equals(EOS)) {
- break;
- } else {
- loge("Ignore unknown key " + key + "while reading");
- }
- } catch (IllegalArgumentException e) {
- loge("Ignore invalid address while reading" + e);
- }
- } while (true);
-
- staticIpConfiguration = new StaticIpConfiguration.Builder()
- .setIpAddress(linkAddress)
- .setGateway(gatewayAddress)
- .setDnsServers(dnsServers)
- .build();
-
- if (uniqueToken != null) {
- IpConfiguration config = new IpConfiguration();
- networks.put(uniqueToken, config);
-
- switch (ipAssignment) {
- case STATIC:
- config.setStaticIpConfiguration(staticIpConfiguration);
- config.setIpAssignment(ipAssignment);
- break;
- case DHCP:
- config.setIpAssignment(ipAssignment);
- break;
- case UNASSIGNED:
- loge("BUG: Found UNASSIGNED IP on file, use DHCP");
- config.setIpAssignment(IpAssignment.DHCP);
- break;
- default:
- loge("Ignore invalid ip assignment while reading.");
- config.setIpAssignment(IpAssignment.UNASSIGNED);
- break;
- }
-
- switch (proxySettings) {
- case STATIC:
- ProxyInfo proxyInfo = ProxyInfo.buildDirectProxy(proxyHost, proxyPort,
- ProxyUtils.exclusionStringAsList(exclusionList));
- config.setProxySettings(proxySettings);
- config.setHttpProxy(proxyInfo);
- break;
- case PAC:
- ProxyInfo proxyPacProperties =
- ProxyInfo.buildPacProxy(Uri.parse(pacFileUrl));
- config.setProxySettings(proxySettings);
- config.setHttpProxy(proxyPacProperties);
- break;
- case NONE:
- config.setProxySettings(proxySettings);
- break;
- case UNASSIGNED:
- loge("BUG: Found UNASSIGNED proxy on file, use NONE");
- config.setProxySettings(ProxySettings.NONE);
- break;
- default:
- loge("Ignore invalid proxy settings while reading");
- config.setProxySettings(ProxySettings.UNASSIGNED);
- break;
- }
- } else {
- if (DBG) log("Missing id while parsing configuration");
- }
- }
- } catch (EOFException ignore) {
- } catch (IOException e) {
- loge("Error parsing configuration: " + e);
- } finally {
- if (in != null) {
- try {
- in.close();
- } catch (Exception e) {}
- }
- }
-
- return networks;
- }
-
- protected static void loge(String s) {
- Log.e(TAG, s);
- }
-
- protected static void log(String s) {
- Log.d(TAG, s);
- }
-}
diff --git a/services/core/java/com/android/server/net/NetworkIdentitySet.java b/services/core/java/com/android/server/net/NetworkIdentitySet.java
deleted file mode 100644
index 22ed781da92d..000000000000
--- a/services/core/java/com/android/server/net/NetworkIdentitySet.java
+++ /dev/null
@@ -1,191 +0,0 @@
-/*
- * Copyright (C) 2011 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.net;
-
-import android.net.NetworkIdentity;
-import android.service.NetworkIdentitySetProto;
-import android.util.proto.ProtoOutputStream;
-
-import java.io.DataInput;
-import java.io.DataOutput;
-import java.io.IOException;
-import java.util.HashSet;
-
-import static android.net.ConnectivityManager.TYPE_MOBILE;
-
-/**
- * Identity of a {@code iface}, defined by the set of {@link NetworkIdentity}
- * active on that interface.
- *
- * @hide
- */
-public class NetworkIdentitySet extends HashSet<NetworkIdentity> implements
- Comparable<NetworkIdentitySet> {
- private static final int VERSION_INIT = 1;
- private static final int VERSION_ADD_ROAMING = 2;
- private static final int VERSION_ADD_NETWORK_ID = 3;
- private static final int VERSION_ADD_METERED = 4;
- private static final int VERSION_ADD_DEFAULT_NETWORK = 5;
- private static final int VERSION_ADD_OEM_MANAGED_NETWORK = 6;
-
- public NetworkIdentitySet() {
- }
-
- public NetworkIdentitySet(DataInput in) throws IOException {
- final int version = in.readInt();
- final int size = in.readInt();
- for (int i = 0; i < size; i++) {
- if (version <= VERSION_INIT) {
- final int ignored = in.readInt();
- }
- final int type = in.readInt();
- final int subType = in.readInt();
- final String subscriberId = readOptionalString(in);
- final String networkId;
- if (version >= VERSION_ADD_NETWORK_ID) {
- networkId = readOptionalString(in);
- } else {
- networkId = null;
- }
- final boolean roaming;
- if (version >= VERSION_ADD_ROAMING) {
- roaming = in.readBoolean();
- } else {
- roaming = false;
- }
-
- final boolean metered;
- if (version >= VERSION_ADD_METERED) {
- metered = in.readBoolean();
- } else {
- // If this is the old data and the type is mobile, treat it as metered. (Note that
- // if this is a mobile network, TYPE_MOBILE is the only possible type that could be
- // used.)
- metered = (type == TYPE_MOBILE);
- }
-
- final boolean defaultNetwork;
- if (version >= VERSION_ADD_DEFAULT_NETWORK) {
- defaultNetwork = in.readBoolean();
- } else {
- defaultNetwork = true;
- }
-
- final int oemNetCapabilities;
- if (version >= VERSION_ADD_OEM_MANAGED_NETWORK) {
- oemNetCapabilities = in.readInt();
- } else {
- oemNetCapabilities = NetworkIdentity.OEM_NONE;
- }
-
- add(new NetworkIdentity(type, subType, subscriberId, networkId, roaming, metered,
- defaultNetwork, oemNetCapabilities));
- }
- }
-
- public void writeToStream(DataOutput out) throws IOException {
- out.writeInt(VERSION_ADD_OEM_MANAGED_NETWORK);
- out.writeInt(size());
- for (NetworkIdentity ident : this) {
- out.writeInt(ident.getType());
- out.writeInt(ident.getSubType());
- writeOptionalString(out, ident.getSubscriberId());
- writeOptionalString(out, ident.getNetworkId());
- out.writeBoolean(ident.getRoaming());
- out.writeBoolean(ident.getMetered());
- out.writeBoolean(ident.getDefaultNetwork());
- out.writeInt(ident.getOemManaged());
- }
- }
-
- /** @return whether any {@link NetworkIdentity} in this set is considered metered. */
- public boolean isAnyMemberMetered() {
- if (isEmpty()) {
- return false;
- }
- for (NetworkIdentity ident : this) {
- if (ident.getMetered()) {
- return true;
- }
- }
- return false;
- }
-
- /** @return whether any {@link NetworkIdentity} in this set is considered roaming. */
- public boolean isAnyMemberRoaming() {
- if (isEmpty()) {
- return false;
- }
- for (NetworkIdentity ident : this) {
- if (ident.getRoaming()) {
- return true;
- }
- }
- return false;
- }
-
- /** @return whether any {@link NetworkIdentity} in this set is considered on the default
- network. */
- public boolean areAllMembersOnDefaultNetwork() {
- if (isEmpty()) {
- return true;
- }
- for (NetworkIdentity ident : this) {
- if (!ident.getDefaultNetwork()) {
- return false;
- }
- }
- return true;
- }
-
- private static void writeOptionalString(DataOutput out, String value) throws IOException {
- if (value != null) {
- out.writeByte(1);
- out.writeUTF(value);
- } else {
- out.writeByte(0);
- }
- }
-
- private static String readOptionalString(DataInput in) throws IOException {
- if (in.readByte() != 0) {
- return in.readUTF();
- } else {
- return null;
- }
- }
-
- @Override
- public int compareTo(NetworkIdentitySet another) {
- if (isEmpty()) return -1;
- if (another.isEmpty()) return 1;
-
- final NetworkIdentity ident = iterator().next();
- final NetworkIdentity anotherIdent = another.iterator().next();
- return ident.compareTo(anotherIdent);
- }
-
- public void dumpDebug(ProtoOutputStream proto, long tag) {
- final long start = proto.start(tag);
-
- for (NetworkIdentity ident : this) {
- ident.dumpDebug(proto, NetworkIdentitySetProto.IDENTITIES);
- }
-
- proto.end(start);
- }
-}
diff --git a/services/core/java/com/android/server/net/NetworkPolicyLogger.java b/services/core/java/com/android/server/net/NetworkPolicyLogger.java
index 654b17fb9754..33ac6cdb269e 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyLogger.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyLogger.java
@@ -15,13 +15,17 @@
*/
package com.android.server.net;
-import static android.net.INetd.FIREWALL_CHAIN_DOZABLE;
-import static android.net.INetd.FIREWALL_CHAIN_POWERSAVE;
-import static android.net.INetd.FIREWALL_CHAIN_RESTRICTED;
-import static android.net.INetd.FIREWALL_CHAIN_STANDBY;
+import static android.net.ConnectivityManager.BLOCKED_REASON_NONE;
+import static android.net.ConnectivityManager.FIREWALL_CHAIN_DOZABLE;
+import static android.net.ConnectivityManager.FIREWALL_CHAIN_LOW_POWER_STANDBY;
+import static android.net.ConnectivityManager.FIREWALL_CHAIN_POWERSAVE;
+import static android.net.ConnectivityManager.FIREWALL_CHAIN_RESTRICTED;
+import static android.net.ConnectivityManager.FIREWALL_CHAIN_STANDBY;
import static android.net.INetd.FIREWALL_RULE_ALLOW;
import static android.net.INetd.FIREWALL_RULE_DENY;
+import static android.net.NetworkPolicyManager.ALLOWED_REASON_NONE;
import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_DOZABLE;
+import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_LOW_POWER_STANDBY;
import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_POWERSAVE;
import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_RESTRICTED;
import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_STANDBY;
@@ -29,6 +33,7 @@ import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT;
import static android.os.PowerExemptionManager.reasonCodeToString;
import static android.os.Process.INVALID_UID;
+import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityManager.ProcessCapability;
import android.net.NetworkPolicyManager;
@@ -39,6 +44,7 @@ import android.util.Slog;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.RingBuffer;
import com.android.server.am.ProcessList;
+import com.android.server.net.NetworkPolicyManagerService.UidBlockedState;
import java.text.SimpleDateFormat;
import java.util.Arrays;
@@ -72,16 +78,6 @@ public class NetworkPolicyLogger {
private static final int EVENT_UPDATE_METERED_RESTRICTED_PKGS = 13;
private static final int EVENT_APP_IDLE_WL_CHANGED = 14;
- static final int NTWK_BLOCKED_POWER = 0;
- static final int NTWK_ALLOWED_NON_METERED = 1;
- static final int NTWK_BLOCKED_DENYLIST = 2;
- static final int NTWK_ALLOWED_ALLOWLIST = 3;
- static final int NTWK_ALLOWED_TMP_ALLOWLIST = 4;
- static final int NTWK_BLOCKED_BG_RESTRICT = 5;
- static final int NTWK_ALLOWED_DEFAULT = 6;
- static final int NTWK_ALLOWED_SYSTEM = 7;
- static final int NTWK_BLOCKED_RESTRICTED_MODE = 8;
-
private final LogBuffer mNetworkBlockedBuffer = new LogBuffer(MAX_NETWORK_BLOCKED_LOG_SIZE);
private final LogBuffer mUidStateChangeBuffer = new LogBuffer(MAX_LOG_SIZE);
private final LogBuffer mEventsBuffer = new LogBuffer(MAX_LOG_SIZE);
@@ -90,12 +86,18 @@ public class NetworkPolicyLogger {
private final Object mLock = new Object();
- void networkBlocked(int uid, int reason) {
+ void networkBlocked(int uid, @Nullable UidBlockedState uidBlockedState) {
synchronized (mLock) {
if (LOGD || uid == mDebugUid) {
- Slog.d(TAG, uid + " is " + getBlockedReason(reason));
+ Slog.d(TAG, "Blocked state of uid: " + uidBlockedState.toString());
+ }
+ if (uidBlockedState == null) {
+ mNetworkBlockedBuffer.networkBlocked(uid, BLOCKED_REASON_NONE, ALLOWED_REASON_NONE,
+ BLOCKED_REASON_NONE);
+ } else {
+ mNetworkBlockedBuffer.networkBlocked(uid, uidBlockedState.blockedReasons,
+ uidBlockedState.allowedReasons, uidBlockedState.effectiveBlockedReasons);
}
- mNetworkBlockedBuffer.networkBlocked(uid, reason);
}
}
@@ -269,29 +271,6 @@ public class NetworkPolicyLogger {
}
}
- private static String getBlockedReason(int reason) {
- switch (reason) {
- case NTWK_BLOCKED_POWER:
- return "blocked by power restrictions";
- case NTWK_ALLOWED_NON_METERED:
- return "allowed on unmetered network";
- case NTWK_BLOCKED_DENYLIST:
- return "denylisted on metered network";
- case NTWK_ALLOWED_ALLOWLIST:
- return "allowlisted on metered network";
- case NTWK_ALLOWED_TMP_ALLOWLIST:
- return "temporary allowlisted on metered network";
- case NTWK_BLOCKED_BG_RESTRICT:
- return "blocked when background is restricted";
- case NTWK_ALLOWED_DEFAULT:
- return "allowed by default";
- case NTWK_BLOCKED_RESTRICTED_MODE:
- return "blocked by restricted networking mode";
- default:
- return String.valueOf(reason);
- }
- }
-
private static String getPolicyChangedLog(int uid, int oldPolicy, int newPolicy) {
return "Policy for " + uid + " changed from "
+ NetworkPolicyManager.uidPoliciesToString(oldPolicy) + " to "
@@ -351,6 +330,8 @@ public class NetworkPolicyLogger {
return FIREWALL_CHAIN_NAME_POWERSAVE;
case FIREWALL_CHAIN_RESTRICTED:
return FIREWALL_CHAIN_NAME_RESTRICTED;
+ case FIREWALL_CHAIN_LOW_POWER_STANDBY:
+ return FIREWALL_CHAIN_NAME_LOW_POWER_STANDBY;
default:
return String.valueOf(chain);
}
@@ -402,14 +383,17 @@ public class NetworkPolicyLogger {
data.timeStamp = System.currentTimeMillis();
}
- public void networkBlocked(int uid, int reason) {
+ public void networkBlocked(int uid, int blockedReasons, int allowedReasons,
+ int effectiveBlockedReasons) {
final Data data = getNextSlot();
if (data == null) return;
data.reset();
data.type = EVENT_NETWORK_BLOCKED;
data.ifield1 = uid;
- data.ifield2 = reason;
+ data.ifield2 = blockedReasons;
+ data.ifield3 = allowedReasons;
+ data.ifield4 = effectiveBlockedReasons;
data.timeStamp = System.currentTimeMillis();
}
@@ -554,7 +538,8 @@ public class NetworkPolicyLogger {
case EVENT_TYPE_GENERIC:
return data.sfield1;
case EVENT_NETWORK_BLOCKED:
- return data.ifield1 + "-" + getBlockedReason(data.ifield2);
+ return data.ifield1 + "-" + UidBlockedState.toString(
+ data.ifield2, data.ifield3, data.ifield4);
case EVENT_UID_STATE_CHANGED:
return data.ifield1 + ":" + ProcessList.makeProcStateString(data.ifield2)
+ ":" + ActivityManager.getCapabilitiesSummary(data.ifield3)
@@ -593,17 +578,24 @@ public class NetworkPolicyLogger {
}
}
- public final static class Data {
- int type;
- long timeStamp;
-
- int ifield1;
- int ifield2;
- int ifield3;
- long lfield1;
- boolean bfield1;
- boolean bfield2;
- String sfield1;
+ /**
+ * Container class for all networkpolicy events data.
+ *
+ * Note: This class needs to be public for RingBuffer class to be able to create
+ * new instances of this.
+ */
+ public static final class Data {
+ public int type;
+ public long timeStamp;
+
+ public int ifield1;
+ public int ifield2;
+ public int ifield3;
+ public int ifield4;
+ public long lfield1;
+ public boolean bfield1;
+ public boolean bfield2;
+ public String sfield1;
public void reset(){
sfield1 = null;
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
index 03a63b9058a8..8ef42ff97aad 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
@@ -16,11 +16,8 @@
package com.android.server.net;
-import android.annotation.NonNull;
import android.annotation.Nullable;
import android.net.Network;
-import android.net.NetworkTemplate;
-import android.net.netstats.provider.NetworkStatsProvider;
import android.os.PowerExemptionManager.ReasonCode;
import android.telephony.SubscriptionPlan;
@@ -56,11 +53,6 @@ public abstract class NetworkPolicyManagerInternal {
*/
public abstract SubscriptionPlan getSubscriptionPlan(Network network);
- /**
- * Return the active {@link SubscriptionPlan} for the given template.
- */
- public abstract SubscriptionPlan getSubscriptionPlan(NetworkTemplate template);
-
public static final int QUOTA_TYPE_JOBS = 1;
public static final int QUOTA_TYPE_MULTIPATH = 2;
@@ -99,13 +91,4 @@ public abstract class NetworkPolicyManagerInternal {
*/
public abstract void setMeteredRestrictedPackagesAsync(
Set<String> packageNames, int userId);
-
- /**
- * Notifies that the specified {@link NetworkStatsProvider} has reached its quota
- * which was set through {@link NetworkStatsProvider#onSetLimit(String, long)} or
- * {@link NetworkStatsProvider#onSetWarningAndLimit(String, long, long)}.
- *
- * @param tag the human readable identifier of the custom network stats provider.
- */
- public abstract void onStatsProviderWarningOrLimitReached(@NonNull String tag);
}
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index cfefffcdd2e8..6a00d06440b9 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -24,7 +24,6 @@ import static android.Manifest.permission.MANAGE_SUBSCRIPTION_PLANS;
import static android.Manifest.permission.NETWORK_SETTINGS;
import static android.Manifest.permission.NETWORK_STACK;
import static android.Manifest.permission.OBSERVE_NETWORK_POLICY;
-import static android.Manifest.permission.READ_NETWORK_USAGE_HISTORY;
import static android.Manifest.permission.READ_PHONE_STATE;
import static android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE;
import static android.app.PendingIntent.FLAG_IMMUTABLE;
@@ -63,7 +62,6 @@ import static android.net.INetd.FIREWALL_RULE_DENY;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
-import static android.net.NetworkIdentity.OEM_NONE;
import static android.net.NetworkPolicy.LIMIT_DISABLED;
import static android.net.NetworkPolicy.SNOOZE_NEVER;
import static android.net.NetworkPolicy.WARNING_DISABLED;
@@ -79,14 +77,10 @@ import static android.net.NetworkPolicyManager.ALLOWED_REASON_RESTRICTED_MODE_PE
import static android.net.NetworkPolicyManager.ALLOWED_REASON_SYSTEM;
import static android.net.NetworkPolicyManager.EXTRA_NETWORK_TEMPLATE;
import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT;
-import static android.net.NetworkPolicyManager.MASK_ALL_NETWORKS;
-import static android.net.NetworkPolicyManager.MASK_METERED_NETWORKS;
-import static android.net.NetworkPolicyManager.MASK_RESTRICTED_MODE_NETWORKS;
import static android.net.NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND;
import static android.net.NetworkPolicyManager.POLICY_NONE;
import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND;
import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL;
-import static android.net.NetworkPolicyManager.RULE_ALLOW_METERED;
import static android.net.NetworkPolicyManager.RULE_NONE;
import static android.net.NetworkPolicyManager.RULE_REJECT_ALL;
import static android.net.NetworkPolicyManager.RULE_REJECT_METERED;
@@ -104,9 +98,6 @@ import static android.net.NetworkStats.METERED_YES;
import static android.net.NetworkTemplate.MATCH_CARRIER;
import static android.net.NetworkTemplate.MATCH_MOBILE;
import static android.net.NetworkTemplate.MATCH_WIFI;
-import static android.net.NetworkTemplate.buildTemplateCarrierMetered;
-import static android.net.NetworkTemplate.buildTemplateMobileAll;
-import static android.net.TrafficStats.MB_IN_BYTES;
import static android.net.netstats.provider.NetworkStatsProvider.QUOTA_UNLIMITED;
import static android.os.Trace.TRACE_TAG_NETWORK;
import static android.provider.Settings.Global.NETPOLICY_OVERRIDE_ENABLED;
@@ -134,17 +125,7 @@ import static com.android.internal.util.XmlUtils.writeIntArrayXml;
import static com.android.internal.util.XmlUtils.writeIntAttribute;
import static com.android.internal.util.XmlUtils.writeLongAttribute;
import static com.android.internal.util.XmlUtils.writeStringAttribute;
-import static com.android.server.NetworkManagementService.LIMIT_GLOBAL_ALERT;
-import static com.android.server.net.NetworkPolicyLogger.NTWK_ALLOWED_ALLOWLIST;
-import static com.android.server.net.NetworkPolicyLogger.NTWK_ALLOWED_DEFAULT;
-import static com.android.server.net.NetworkPolicyLogger.NTWK_ALLOWED_NON_METERED;
-import static com.android.server.net.NetworkPolicyLogger.NTWK_ALLOWED_SYSTEM;
-import static com.android.server.net.NetworkPolicyLogger.NTWK_ALLOWED_TMP_ALLOWLIST;
-import static com.android.server.net.NetworkPolicyLogger.NTWK_BLOCKED_BG_RESTRICT;
-import static com.android.server.net.NetworkPolicyLogger.NTWK_BLOCKED_DENYLIST;
-import static com.android.server.net.NetworkPolicyLogger.NTWK_BLOCKED_POWER;
-import static com.android.server.net.NetworkPolicyLogger.NTWK_BLOCKED_RESTRICTED_MODE;
-import static com.android.server.net.NetworkStatsService.ACTION_NETWORK_STATS_UPDATED;
+import static com.android.net.module.util.NetworkStatsUtils.LIMIT_GLOBAL_ALERT;
import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
import static org.xmlpull.v1.XmlPullParser.END_TAG;
@@ -164,6 +145,8 @@ import android.app.IUidObserver;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
+import android.app.usage.NetworkStats;
+import android.app.usage.NetworkStatsManager;
import android.app.usage.UsageStatsManagerInternal;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -183,7 +166,6 @@ import android.net.ConnectivityManager.NetworkCallback;
import android.net.INetworkManagementEventObserver;
import android.net.INetworkPolicyListener;
import android.net.INetworkPolicyManager;
-import android.net.INetworkStatsService;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkIdentity;
@@ -194,10 +176,8 @@ import android.net.NetworkRequest;
import android.net.NetworkSpecifier;
import android.net.NetworkStack;
import android.net.NetworkStateSnapshot;
-import android.net.NetworkStats;
import android.net.NetworkTemplate;
import android.net.TelephonyNetworkSpecifier;
-import android.net.TrafficStats;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiManager;
import android.os.BestClock;
@@ -268,6 +248,7 @@ import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.StatLogger;
import com.android.internal.util.XmlUtils;
import com.android.net.module.util.NetworkIdentityUtils;
+import com.android.net.module.util.NetworkStatsUtils;
import com.android.net.module.util.PermissionUtils;
import com.android.server.EventLogTags;
import com.android.server.LocalServices;
@@ -455,7 +436,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
private final Context mContext;
private final IActivityManager mActivityManager;
- private NetworkStatsManagerInternal mNetworkStats;
+ private NetworkStatsManager mNetworkStats;
private final INetworkManagementService mNetworkManager;
private UsageStatsManagerInternal mUsageStats;
private AppStandbyInternal mAppStandby;
@@ -467,6 +448,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
private ConnectivityManager mConnManager;
private PowerManagerInternal mPowerManagerInternal;
private PowerWhitelistManager mPowerWhitelistManager;
+ @NonNull
+ private final Dependencies mDeps;
/** Current cached value of the current Battery Saver mode's setting for restrict background. */
@GuardedBy("mUidRulesFirstLock")
@@ -518,8 +501,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
/** Defined UID policies. */
@GuardedBy("mUidRulesFirstLock") final SparseIntArray mUidPolicy = new SparseIntArray();
- /** Currently derived rules for each UID. */
- @GuardedBy("mUidRulesFirstLock") final SparseIntArray mUidRules = new SparseIntArray();
@GuardedBy("mUidRulesFirstLock")
final SparseIntArray mUidFirewallStandbyRules = new SparseIntArray();
@@ -598,6 +579,10 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
@GuardedBy("mUidRulesFirstLock")
private final SparseArray<UidBlockedState> mUidBlockedState = new SparseArray<>();
+ /** Objects used temporarily while computing the new blocked state for each uid. */
+ @GuardedBy("mUidRulesFirstLock")
+ private final SparseArray<UidBlockedState> mTmpUidBlockedState = new SparseArray<>();
+
/** Map from network ID to last observed meteredness state */
@GuardedBy("mNetworkPoliciesSecondLock")
private final SparseBooleanArray mNetworkMetered = new SparseBooleanArray();
@@ -716,7 +701,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
public NetworkPolicyManagerService(Context context, IActivityManager activityManager,
INetworkManagementService networkManagement) {
this(context, activityManager, networkManagement, AppGlobals.getPackageManager(),
- getDefaultClock(), getDefaultSystemDir(), false);
+ getDefaultClock(), getDefaultSystemDir(), false, new Dependencies(context));
}
private static @NonNull File getDefaultSystemDir() {
@@ -728,9 +713,59 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
Clock.systemUTC());
}
+ static class Dependencies {
+ final Context mContext;
+ final NetworkStatsManager mNetworkStatsManager;
+ Dependencies(Context context) {
+ mContext = context;
+ mNetworkStatsManager = mContext.getSystemService(NetworkStatsManager.class);
+ // Query stats from NetworkStatsService will trigger a poll by default.
+ // But since NPMS listens stats updated event, and will query stats
+ // after the event. A polling -> updated -> query -> polling loop will be introduced
+ // if polls on open. Hence, while NPMS manages it's poll requests explicitly, set
+ // flag to false to prevent a polling loop.
+ mNetworkStatsManager.setPollOnOpen(false);
+ }
+
+ long getNetworkTotalBytes(NetworkTemplate template, long start, long end) {
+ Trace.traceBegin(TRACE_TAG_NETWORK, "getNetworkTotalBytes");
+ try {
+ final NetworkStats.Bucket ret = mNetworkStatsManager
+ .querySummaryForDevice(template, start, end);
+ return ret.getRxBytes() + ret.getTxBytes();
+ } catch (RuntimeException e) {
+ Slog.w(TAG, "Failed to read network stats: " + e);
+ return 0;
+ } finally {
+ Trace.traceEnd(TRACE_TAG_NETWORK);
+ }
+ }
+
+ @NonNull
+ List<NetworkStats.Bucket> getNetworkUidBytes(
+ @NonNull NetworkTemplate template, long start, long end) {
+ Trace.traceBegin(TRACE_TAG_NETWORK, "getNetworkUidBytes");
+ final List<NetworkStats.Bucket> buckets = new ArrayList<>();
+ try {
+ final NetworkStats stats = mNetworkStatsManager.querySummary(template, start, end);
+ while (stats.hasNextBucket()) {
+ final NetworkStats.Bucket bucket = new NetworkStats.Bucket();
+ stats.getNextBucket(bucket);
+ buckets.add(bucket);
+ }
+ } catch (RuntimeException e) {
+ Slog.w(TAG, "Failed to read network stats: " + e);
+ } finally {
+ Trace.traceEnd(TRACE_TAG_NETWORK);
+ }
+ return buckets;
+ }
+ }
+
+ @VisibleForTesting
public NetworkPolicyManagerService(Context context, IActivityManager activityManager,
INetworkManagementService networkManagement, IPackageManager pm, Clock clock,
- File systemDir, boolean suppressDefaultPolicy) {
+ File systemDir, boolean suppressDefaultPolicy, Dependencies deps) {
mContext = Objects.requireNonNull(context, "missing context");
mActivityManager = Objects.requireNonNull(activityManager, "missing activityManager");
mNetworkManager = Objects.requireNonNull(networkManagement, "missing networkManagement");
@@ -751,10 +786,12 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
mUidEventHandler = new Handler(mUidEventThread.getLooper(), mUidEventHandlerCallback);
mSuppressDefaultPolicy = suppressDefaultPolicy;
+ mDeps = Objects.requireNonNull(deps, "missing Dependencies");
mPolicyFile = new AtomicFile(new File(systemDir, "netpolicy.xml"), "net-policy");
mAppOps = context.getSystemService(AppOpsManager.class);
+ mNetworkStats = context.getSystemService(NetworkStatsManager.class);
mMultipathPolicyTracker = new MultipathPolicyTracker(mContext, mHandler);
// Expose private service for system components to use.
LocalServices.addService(NetworkPolicyManagerInternal.class,
@@ -854,7 +891,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
mUsageStats = LocalServices.getService(UsageStatsManagerInternal.class);
mAppStandby = LocalServices.getService(AppStandbyInternal.class);
- mNetworkStats = LocalServices.getService(NetworkStatsManagerInternal.class);
synchronized (mUidRulesFirstLock) {
synchronized (mNetworkPoliciesSecondLock) {
@@ -969,10 +1005,11 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
userFilter.addAction(ACTION_USER_REMOVED);
mContext.registerReceiver(mUserReceiver, userFilter, null, mHandler);
- // listen for stats update events
- final IntentFilter statsFilter = new IntentFilter(ACTION_NETWORK_STATS_UPDATED);
- mContext.registerReceiver(
- mStatsReceiver, statsFilter, READ_NETWORK_USAGE_HISTORY, mHandler);
+ // listen for stats updated callbacks for interested network types.
+ mNetworkStats.registerUsageCallback(new NetworkTemplate.Builder(MATCH_MOBILE).build(),
+ 0 /* thresholdBytes */, new HandlerExecutor(mHandler), mStatsCallback);
+ mNetworkStats.registerUsageCallback(new NetworkTemplate.Builder(MATCH_WIFI).build(),
+ 0 /* thresholdBytes */, new HandlerExecutor(mHandler), mStatsCallback);
// listen for restrict background changes from notifications
final IntentFilter allowFilter = new IntentFilter(ACTION_ALLOW_BACKGROUND);
@@ -1177,14 +1214,16 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
};
/**
- * Receiver that watches for {@link INetworkStatsService} updates, which we
- * use to check against {@link NetworkPolicy#warningBytes}.
+ * Listener that watches for {@link NetworkStatsManager} updates, which
+ * NetworkPolicyManagerService uses to check against {@link NetworkPolicy#warningBytes}.
*/
- final private BroadcastReceiver mStatsReceiver = new BroadcastReceiver() {
+ private final StatsCallback mStatsCallback = new StatsCallback();
+ private class StatsCallback extends NetworkStatsManager.UsageCallback {
+ private boolean mIsAnyCallbackReceived = false;
+
@Override
- public void onReceive(Context context, Intent intent) {
- // on background handler thread, and verified
- // READ_NETWORK_USAGE_HISTORY permission above.
+ public void onThresholdReached(int networkType, String subscriberId) {
+ mIsAnyCallbackReceived = true;
synchronized (mNetworkPoliciesSecondLock) {
updateNetworkRulesNL();
@@ -1192,6 +1231,14 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
updateNotificationsNL();
}
}
+
+ /**
+ * Return whether any callback is received.
+ * Used to determine if NetworkStatsService is ready.
+ */
+ public boolean isAnyCallbackReceived() {
+ return mIsAnyCallbackReceived;
+ }
};
/**
@@ -1294,7 +1341,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
};
/**
- * Check {@link NetworkPolicy} against current {@link INetworkStatsService}
+ * Check {@link NetworkPolicy} against current {@link NetworkStatsManager}
* to show visible notifications as needed.
*/
@GuardedBy("mNetworkPoliciesSecondLock")
@@ -1415,15 +1462,17 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
long maxBytes = 0;
int maxUid = 0;
- final NetworkStats stats = getNetworkUidBytes(template, start, end);
- NetworkStats.Entry entry = null;
- for (int i = 0; i < stats.size(); i++) {
- entry = stats.getValues(i, entry);
- final long bytes = entry.rxBytes + entry.txBytes;
+ // Skip if not ready. NetworkStatsService will block public API calls until it is
+ // ready. To prevent NPMS be blocked on that, skip and fail fast instead.
+ if (!mStatsCallback.isAnyCallbackReceived()) return null;
+
+ final List<NetworkStats.Bucket> stats = mDeps.getNetworkUidBytes(template, start, end);
+ for (final NetworkStats.Bucket entry : stats) {
+ final long bytes = entry.getRxBytes() + entry.getTxBytes();
totalBytes += bytes;
if (bytes > maxBytes) {
maxBytes = bytes;
- maxUid = entry.uid;
+ maxUid = entry.getUid();
}
}
@@ -1459,13 +1508,11 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
for (int i = 0; i < mSubIdToSubscriberId.size(); i++) {
final int subId = mSubIdToSubscriberId.keyAt(i);
final String subscriberId = mSubIdToSubscriberId.valueAt(i);
- final NetworkIdentity probeIdent = new NetworkIdentity(TYPE_MOBILE,
- TelephonyManager.NETWORK_TYPE_UNKNOWN, subscriberId, null, false, true,
- true, OEM_NONE);
- /* While OEM_NONE indicates "any non OEM managed network", OEM_NONE is meant to be a
- * placeholder value here. The probeIdent is matched against a NetworkTemplate which
- * should have its OEM managed value set to OEM_MANAGED_ALL, which will cause the
- * template to match probeIdent without regard to OEM managed status. */
+ final NetworkIdentity probeIdent = new NetworkIdentity.Builder()
+ .setType(TYPE_MOBILE)
+ .setSubscriberId(subscriberId)
+ .setMetered(true)
+ .setDefaultNetwork(true).build();
if (template.matches(probeIdent)) {
return subId;
}
@@ -1698,9 +1745,11 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
// find and update the carrier NetworkPolicy for this subscriber id
boolean policyUpdated = false;
- final NetworkIdentity probeIdent = new NetworkIdentity(TYPE_MOBILE,
- TelephonyManager.NETWORK_TYPE_UNKNOWN, subscriberId, null, false, true, true,
- OEM_NONE);
+ final NetworkIdentity probeIdent = new NetworkIdentity.Builder()
+ .setType(TYPE_MOBILE)
+ .setSubscriberId(subscriberId)
+ .setMetered(true)
+ .setDefaultNetwork(true).build();
for (int i = mNetworkPolicy.size() - 1; i >= 0; i--) {
final NetworkTemplate template = mNetworkPolicy.keyAt(i);
if (template.matches(probeIdent)) {
@@ -1928,10 +1977,11 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
for (int i = 0; i < mSubIdToSubscriberId.size(); i++) {
final int subId = mSubIdToSubscriberId.keyAt(i);
final String subscriberId = mSubIdToSubscriberId.valueAt(i);
-
- final NetworkIdentity probeIdent = new NetworkIdentity(TYPE_MOBILE,
- TelephonyManager.NETWORK_TYPE_UNKNOWN, subscriberId, null, false, true,
- true, OEM_NONE);
+ final NetworkIdentity probeIdent = new NetworkIdentity.Builder()
+ .setType(TYPE_MOBILE)
+ .setSubscriberId(subscriberId)
+ .setMetered(true)
+ .setDefaultNetwork(true).build();
// Template is matched when subscriber id matches.
if (template.matches(probeIdent)) {
matchingSubIds.add(subId);
@@ -2035,11 +2085,9 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
for (final NetworkStateSnapshot snapshot : snapshots) {
mNetIdToSubId.put(snapshot.getNetwork().getNetId(), parseSubId(snapshot));
- // Policies matched by NPMS only match by subscriber ID or by ssid. Thus subtype
- // in the object created here is never used and its value doesn't matter, so use
- // NETWORK_TYPE_UNKNOWN.
- final NetworkIdentity ident = NetworkIdentity.buildNetworkIdentity(mContext, snapshot,
- true, TelephonyManager.NETWORK_TYPE_UNKNOWN /* subType */);
+ // Policies matched by NPMS only match by subscriber ID or by network ID.
+ final NetworkIdentity ident = new NetworkIdentity.Builder()
+ .setNetworkStateSnapshot(snapshot).setDefaultNetwork(true).build();
identified.put(snapshot, ident);
}
@@ -2236,9 +2284,11 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
@GuardedBy("mNetworkPoliciesSecondLock")
private boolean ensureActiveCarrierPolicyAL(int subId, String subscriberId) {
// Poke around to see if we already have a policy
- final NetworkIdentity probeIdent = new NetworkIdentity(TYPE_MOBILE,
- TelephonyManager.NETWORK_TYPE_UNKNOWN, subscriberId, null, false, true, true,
- OEM_NONE);
+ final NetworkIdentity probeIdent = new NetworkIdentity.Builder()
+ .setType(TYPE_MOBILE)
+ .setSubscriberId(subscriberId)
+ .setMetered(true)
+ .setDefaultNetwork(true).build();
for (int i = mNetworkPolicy.size() - 1; i >= 0; i--) {
final NetworkTemplate template = mNetworkPolicy.keyAt(i);
if (template.matches(probeIdent)) {
@@ -2264,7 +2314,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
if (dataWarningConfig == WARNING_DISABLED) {
return WARNING_DISABLED;
} else {
- return dataWarningConfig * MB_IN_BYTES;
+ return DataUnit.MEBIBYTES.toBytes(dataWarningConfig);
}
}
@@ -2289,6 +2339,18 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
}
/**
+ * Template to match all metered carrier networks with the given IMSI.
+ *
+ * @hide
+ */
+ public static NetworkTemplate buildTemplateCarrierMetered(@NonNull String subscriberId) {
+ Objects.requireNonNull(subscriberId);
+ return new NetworkTemplate.Builder(MATCH_CARRIER)
+ .setSubscriberIds(Set.of(subscriberId))
+ .setMeteredness(METERED_YES).build();
+ }
+
+ /**
* Update the given {@link NetworkPolicy} based on any carrier-provided
* defaults via {@link SubscriptionPlan} or {@link CarrierConfigManager}.
* Leaves policy untouched if the user has modified it.
@@ -2399,7 +2461,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
templateMeteredness = readIntAttribute(in, ATTR_TEMPLATE_METERED);
} else {
- subscriberIdMatchRule = NetworkTemplate.SUBSCRIBER_ID_MATCH_RULE_EXACT;
+ subscriberIdMatchRule =
+ NetworkStatsUtils.SUBSCRIBER_ID_MATCH_RULE_EXACT;
if (templateType == MATCH_MOBILE) {
Log.d(TAG, "Update template match rule from mobile to carrier and"
+ " force to metered");
@@ -2462,12 +2525,20 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
} else {
inferred = false;
}
- final NetworkTemplate template = new NetworkTemplate(templateType,
- subscriberId, new String[] { subscriberId },
- networkId, templateMeteredness, NetworkStats.ROAMING_ALL,
- NetworkStats.DEFAULT_NETWORK_ALL, NetworkTemplate.NETWORK_TYPE_ALL,
- NetworkTemplate.OEM_MANAGED_ALL, subscriberIdMatchRule);
- if (template.isPersistable()) {
+ final NetworkTemplate.Builder builder =
+ new NetworkTemplate.Builder(templateType)
+ .setMeteredness(templateMeteredness);
+ if (subscriberIdMatchRule
+ == NetworkStatsUtils.SUBSCRIBER_ID_MATCH_RULE_EXACT) {
+ final ArraySet<String> ids = new ArraySet<>();
+ ids.add(subscriberId);
+ builder.setSubscriberIds(ids);
+ }
+ if (networkId != null) {
+ builder.setWifiNetworkKeys(Set.of(networkId));
+ }
+ final NetworkTemplate template = builder.build();
+ if (NetworkPolicy.isTemplatePersistable(template)) {
mNetworkPolicy.put(template, new NetworkPolicy(template, cycleRule,
warningBytes, limitBytes, lastWarningSnooze,
lastLimitSnooze, metered, inferred));
@@ -2616,35 +2687,39 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
* into {@link WifiConfiguration}.
*/
private void upgradeWifiMeteredOverride() {
- final ArrayMap<String, Boolean> wifiNetworkIds = new ArrayMap<>();
+ final ArrayMap<String, Boolean> wifiNetworkKeys = new ArrayMap<>();
synchronized (mNetworkPoliciesSecondLock) {
for (int i = 0; i < mNetworkPolicy.size();) {
final NetworkPolicy policy = mNetworkPolicy.valueAt(i);
if (policy.template.getMatchRule() == NetworkTemplate.MATCH_WIFI
&& !policy.inferred) {
mNetworkPolicy.removeAt(i);
- wifiNetworkIds.put(policy.template.getNetworkId(), policy.metered);
+ final Set<String> keys = policy.template.getWifiNetworkKeys();
+ wifiNetworkKeys.put(keys.isEmpty() ? null : keys.iterator().next(),
+ policy.metered);
} else {
i++;
}
}
}
- if (wifiNetworkIds.isEmpty()) {
+ if (wifiNetworkKeys.isEmpty()) {
return;
}
final WifiManager wm = mContext.getSystemService(WifiManager.class);
final List<WifiConfiguration> configs = wm.getConfiguredNetworks();
for (int i = 0; i < configs.size(); ++i) {
final WifiConfiguration config = configs.get(i);
- final String networkId = resolveNetworkId(config);
- final Boolean metered = wifiNetworkIds.get(networkId);
- if (metered != null) {
- Slog.d(TAG, "Found network " + networkId + "; upgrading metered hint");
- config.meteredOverride = metered
- ? WifiConfiguration.METERED_OVERRIDE_METERED
- : WifiConfiguration.METERED_OVERRIDE_NOT_METERED;
- wm.updateNetwork(config);
+ for (String key : config.getAllNetworkKeys()) {
+ final Boolean metered = wifiNetworkKeys.get(key);
+ if (metered != null) {
+ Slog.d(TAG, "Found network " + key + "; upgrading metered hint");
+ config.meteredOverride = metered
+ ? WifiConfiguration.METERED_OVERRIDE_METERED
+ : WifiConfiguration.METERED_OVERRIDE_NOT_METERED;
+ wm.updateNetwork(config);
+ break;
+ }
}
}
@@ -2674,19 +2749,22 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
for (int i = 0; i < mNetworkPolicy.size(); i++) {
final NetworkPolicy policy = mNetworkPolicy.valueAt(i);
final NetworkTemplate template = policy.template;
- if (!template.isPersistable()) continue;
+ if (!NetworkPolicy.isTemplatePersistable(template)) continue;
out.startTag(null, TAG_NETWORK_POLICY);
writeIntAttribute(out, ATTR_NETWORK_TEMPLATE, template.getMatchRule());
- final String subscriberId = template.getSubscriberId();
+ final String subscriberId = template.getSubscriberIds().isEmpty() ? null
+ : template.getSubscriberIds().iterator().next();
if (subscriberId != null) {
out.attribute(null, ATTR_SUBSCRIBER_ID, subscriberId);
}
- writeIntAttribute(out, ATTR_SUBSCRIBER_ID_MATCH_RULE,
- template.getSubscriberIdMatchRule());
- final String networkId = template.getNetworkId();
- if (networkId != null) {
- out.attribute(null, ATTR_NETWORK_ID, networkId);
+ final int subscriberIdMatchRule = template.getSubscriberIds().isEmpty()
+ ? NetworkStatsUtils.SUBSCRIBER_ID_MATCH_RULE_ALL
+ : NetworkStatsUtils.SUBSCRIBER_ID_MATCH_RULE_EXACT;
+ writeIntAttribute(out, ATTR_SUBSCRIBER_ID_MATCH_RULE, subscriberIdMatchRule);
+ if (!template.getWifiNetworkKeys().isEmpty()) {
+ out.attribute(null, ATTR_NETWORK_ID,
+ template.getWifiNetworkKeys().iterator().next());
}
writeIntAttribute(out, ATTR_TEMPLATE_METERED,
template.getMeteredness());
@@ -3053,7 +3131,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
}
// When two normalized templates conflict, prefer the most
// restrictive policy
- policy.template = NetworkTemplate.normalize(policy.template, mMergedSubscriberIds);
+ policy.template = normalizeTemplate(policy.template, mMergedSubscriberIds);
final NetworkPolicy existing = mNetworkPolicy.get(policy.template);
if (existing == null || existing.compareTo(policy) > 0) {
if (existing != null) {
@@ -3064,6 +3142,46 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
}
}
+ /**
+ * Examine the given template and normalize it.
+ * We pick the "lowest" merged subscriber as the primary
+ * for key purposes, and expand the template to match all other merged
+ * subscribers.
+ *
+ * There can be multiple merged subscriberIds for multi-SIM devices.
+ *
+ * <p>
+ * For example, given an incoming template matching B, and the currently
+ * active merge set [A,B], we'd return a new template that primarily matches
+ * A, but also matches B.
+ */
+ private static NetworkTemplate normalizeTemplate(@NonNull NetworkTemplate template,
+ @NonNull List<String[]> mergedList) {
+ // Now there are several types of network which uses Subscriber Id to store network
+ // information. For instance:
+ // 1. A merged carrier wifi network which has TYPE_WIFI with a Subscriber Id.
+ // 2. A typical cellular network could have TYPE_MOBILE with a Subscriber Id.
+
+ if (template.getSubscriberIds().isEmpty()) return template;
+
+ for (final String[] merged : mergedList) {
+ // TODO: Handle incompatible subscriberIds if that happens in practice.
+ for (final String subscriberId : template.getSubscriberIds()) {
+ if (com.android.net.module.util.CollectionUtils.contains(merged, subscriberId)) {
+ // Requested template subscriber is part of the merged group; return
+ // a template that matches all merged subscribers.
+ return new NetworkTemplate.Builder(template.getMatchRule())
+ .setWifiNetworkKeys(template.getWifiNetworkKeys())
+ .setSubscriberIds(Set.of(merged))
+ .setMeteredness(template.getMeteredness())
+ .build();
+ }
+ }
+ }
+
+ return template;
+ }
+
@Override
public void snoozeLimit(NetworkTemplate template) {
mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
@@ -3378,6 +3496,35 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
return result;
}
+ /**
+ * Get subscription plan for the given networkTemplate.
+ *
+ * @param template the networkTemplate to get the subscription plan for.
+ */
+ @Override
+ public SubscriptionPlan getSubscriptionPlan(@NonNull NetworkTemplate template) {
+ enforceAnyPermissionOf(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
+ synchronized (mNetworkPoliciesSecondLock) {
+ final int subId = findRelevantSubIdNL(template);
+ return getPrimarySubscriptionPlanLocked(subId);
+ }
+ }
+
+ /**
+ * Notifies that the specified {@link NetworkStatsProvider} has reached its quota
+ * which was set through {@link NetworkStatsProvider#onSetLimit(String, long)} or
+ * {@link NetworkStatsProvider#onSetWarningAndLimit(String, long, long)}.
+ */
+ @Override
+ public void notifyStatsProviderWarningOrLimitReached() {
+ enforceAnyPermissionOf(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
+ // This API may be called before the system is ready.
+ synchronized (mNetworkPoliciesSecondLock) {
+ if (!mSystemReady) return;
+ }
+ mHandler.obtainMessage(MSG_STATS_PROVIDER_WARNING_OR_LIMIT_REACHED).sendToTarget();
+ }
+
@Override
public SubscriptionPlan[] getSubscriptionPlans(int subId, String callingPackage) {
enforceSubscriptionPlanAccess(subId, Binder.getCallingUid(), callingPackage);
@@ -3389,9 +3536,9 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
plans.add(SubscriptionPlan.Builder
.createRecurringMonthly(ZonedDateTime.parse("2007-03-14T00:00:00.000Z"))
.setTitle("G-Mobile")
- .setDataLimit(5 * TrafficStats.GB_IN_BYTES,
+ .setDataLimit(DataUnit.GIBIBYTES.toBytes(5),
SubscriptionPlan.LIMIT_BEHAVIOR_BILLED)
- .setDataUsage(1 * TrafficStats.GB_IN_BYTES,
+ .setDataUsage(DataUnit.GIBIBYTES.toBytes(1),
ZonedDateTime.now().minusHours(36).toInstant().toEpochMilli())
.build());
plans.add(SubscriptionPlan.Builder
@@ -3399,15 +3546,15 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
.setTitle("G-Mobile Happy")
.setDataLimit(SubscriptionPlan.BYTES_UNLIMITED,
SubscriptionPlan.LIMIT_BEHAVIOR_BILLED)
- .setDataUsage(5 * TrafficStats.GB_IN_BYTES,
+ .setDataUsage(DataUnit.GIBIBYTES.toBytes(5),
ZonedDateTime.now().minusHours(36).toInstant().toEpochMilli())
.build());
plans.add(SubscriptionPlan.Builder
.createRecurringMonthly(ZonedDateTime.parse("2017-03-14T00:00:00.000Z"))
.setTitle("G-Mobile, Charged after limit")
- .setDataLimit(5 * TrafficStats.GB_IN_BYTES,
+ .setDataLimit(DataUnit.GIBIBYTES.toBytes(5),
SubscriptionPlan.LIMIT_BEHAVIOR_BILLED)
- .setDataUsage(5 * TrafficStats.GB_IN_BYTES,
+ .setDataUsage(DataUnit.GIBIBYTES.toBytes(5),
ZonedDateTime.now().minusHours(36).toInstant().toEpochMilli())
.build());
} else if ("month_soft".equals(fake)) {
@@ -3416,25 +3563,25 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
.setTitle("G-Mobile is the carriers name who this plan belongs to")
.setSummary("Crazy unlimited bandwidth plan with incredibly long title "
+ "that should be cut off to prevent UI from looking terrible")
- .setDataLimit(5 * TrafficStats.GB_IN_BYTES,
+ .setDataLimit(DataUnit.GIBIBYTES.toBytes(5),
SubscriptionPlan.LIMIT_BEHAVIOR_THROTTLED)
- .setDataUsage(1 * TrafficStats.GB_IN_BYTES,
+ .setDataUsage(DataUnit.GIBIBYTES.toBytes(1),
ZonedDateTime.now().minusHours(1).toInstant().toEpochMilli())
.build());
plans.add(SubscriptionPlan.Builder
.createRecurringMonthly(ZonedDateTime.parse("2017-03-14T00:00:00.000Z"))
.setTitle("G-Mobile, Throttled after limit")
- .setDataLimit(5 * TrafficStats.GB_IN_BYTES,
+ .setDataLimit(DataUnit.GIBIBYTES.toBytes(5),
SubscriptionPlan.LIMIT_BEHAVIOR_THROTTLED)
- .setDataUsage(5 * TrafficStats.GB_IN_BYTES,
+ .setDataUsage(DataUnit.GIBIBYTES.toBytes(5),
ZonedDateTime.now().minusHours(1).toInstant().toEpochMilli())
.build());
plans.add(SubscriptionPlan.Builder
.createRecurringMonthly(ZonedDateTime.parse("2017-03-14T00:00:00.000Z"))
.setTitle("G-Mobile, No data connection after limit")
- .setDataLimit(5 * TrafficStats.GB_IN_BYTES,
+ .setDataLimit(DataUnit.GIBIBYTES.toBytes(5),
SubscriptionPlan.LIMIT_BEHAVIOR_DISABLED)
- .setDataUsage(5 * TrafficStats.GB_IN_BYTES,
+ .setDataUsage(DataUnit.GIBIBYTES.toBytes(5),
ZonedDateTime.now().minusHours(1).toInstant().toEpochMilli())
.build());
@@ -3442,25 +3589,25 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
plans.add(SubscriptionPlan.Builder
.createRecurringMonthly(ZonedDateTime.parse("2007-03-14T00:00:00.000Z"))
.setTitle("G-Mobile is the carriers name who this plan belongs to")
- .setDataLimit(5 * TrafficStats.GB_IN_BYTES,
+ .setDataLimit(DataUnit.GIBIBYTES.toBytes(5),
SubscriptionPlan.LIMIT_BEHAVIOR_THROTTLED)
- .setDataUsage(6 * TrafficStats.GB_IN_BYTES,
+ .setDataUsage(DataUnit.GIBIBYTES.toBytes(6),
ZonedDateTime.now().minusHours(1).toInstant().toEpochMilli())
.build());
plans.add(SubscriptionPlan.Builder
.createRecurringMonthly(ZonedDateTime.parse("2017-03-14T00:00:00.000Z"))
.setTitle("G-Mobile, Throttled after limit")
- .setDataLimit(5 * TrafficStats.GB_IN_BYTES,
+ .setDataLimit(DataUnit.GIBIBYTES.toBytes(5),
SubscriptionPlan.LIMIT_BEHAVIOR_THROTTLED)
- .setDataUsage(5 * TrafficStats.GB_IN_BYTES,
+ .setDataUsage(DataUnit.GIBIBYTES.toBytes(5),
ZonedDateTime.now().minusHours(1).toInstant().toEpochMilli())
.build());
plans.add(SubscriptionPlan.Builder
.createRecurringMonthly(ZonedDateTime.parse("2017-03-14T00:00:00.000Z"))
.setTitle("G-Mobile, No data connection after limit")
- .setDataLimit(5 * TrafficStats.GB_IN_BYTES,
+ .setDataLimit(DataUnit.GIBIBYTES.toBytes(5),
SubscriptionPlan.LIMIT_BEHAVIOR_DISABLED)
- .setDataUsage(5 * TrafficStats.GB_IN_BYTES,
+ .setDataUsage(DataUnit.GIBIBYTES.toBytes(5),
ZonedDateTime.now().minusHours(1).toInstant().toEpochMilli())
.build());
@@ -3474,9 +3621,9 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
.createNonrecurring(ZonedDateTime.now().minusDays(20),
ZonedDateTime.now().plusDays(10))
.setTitle("G-Mobile")
- .setDataLimit(512 * TrafficStats.MB_IN_BYTES,
+ .setDataLimit(DataUnit.MEBIBYTES.toBytes(512),
SubscriptionPlan.LIMIT_BEHAVIOR_DISABLED)
- .setDataUsage(100 * TrafficStats.MB_IN_BYTES,
+ .setDataUsage(DataUnit.MEBIBYTES.toBytes(100),
ZonedDateTime.now().minusHours(3).toInstant().toEpochMilli())
.build());
} else if ("prepaid_crazy".equals(fake)) {
@@ -3484,9 +3631,9 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
.createNonrecurring(ZonedDateTime.now().minusDays(20),
ZonedDateTime.now().plusDays(10))
.setTitle("G-Mobile Anytime")
- .setDataLimit(512 * TrafficStats.MB_IN_BYTES,
+ .setDataLimit(DataUnit.MEBIBYTES.toBytes(512),
SubscriptionPlan.LIMIT_BEHAVIOR_DISABLED)
- .setDataUsage(100 * TrafficStats.MB_IN_BYTES,
+ .setDataUsage(DataUnit.MEBIBYTES.toBytes(100),
ZonedDateTime.now().minusHours(3).toInstant().toEpochMilli())
.build());
plans.add(SubscriptionPlan.Builder
@@ -3494,9 +3641,9 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
ZonedDateTime.now().plusDays(20))
.setTitle("G-Mobile Nickel Nights")
.setSummary("5¢/GB between 1-5AM")
- .setDataLimit(5 * TrafficStats.GB_IN_BYTES,
+ .setDataLimit(DataUnit.GIBIBYTES.toBytes(5),
SubscriptionPlan.LIMIT_BEHAVIOR_THROTTLED)
- .setDataUsage(15 * TrafficStats.MB_IN_BYTES,
+ .setDataUsage(DataUnit.MEBIBYTES.toBytes(15),
ZonedDateTime.now().minusHours(30).toInstant().toEpochMilli())
.build());
plans.add(SubscriptionPlan.Builder
@@ -3504,9 +3651,9 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
ZonedDateTime.now().plusDays(20))
.setTitle("G-Mobile Bonus 3G")
.setSummary("Unlimited 3G data")
- .setDataLimit(1 * TrafficStats.GB_IN_BYTES,
+ .setDataLimit(DataUnit.GIBIBYTES.toBytes(1),
SubscriptionPlan.LIMIT_BEHAVIOR_THROTTLED)
- .setDataUsage(300 * TrafficStats.MB_IN_BYTES,
+ .setDataUsage(DataUnit.MEBIBYTES.toBytes(300),
ZonedDateTime.now().minusHours(1).toInstant().toEpochMilli())
.build());
} else if ("unlimited".equals(fake)) {
@@ -3516,7 +3663,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
.setTitle("G-Mobile Awesome")
.setDataLimit(SubscriptionPlan.BYTES_UNLIMITED,
SubscriptionPlan.LIMIT_BEHAVIOR_THROTTLED)
- .setDataUsage(50 * TrafficStats.MB_IN_BYTES,
+ .setDataUsage(DataUnit.MEBIBYTES.toBytes(50),
ZonedDateTime.now().minusHours(3).toInstant().toEpochMilli())
.build());
}
@@ -3825,7 +3972,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
final SparseBooleanArray knownUids = new SparseBooleanArray();
collectKeys(mUidState, knownUids);
- collectKeys(mUidRules, knownUids);
+ collectKeys(mUidBlockedState, knownUids);
fout.println("Status for all known UIDs:");
fout.increaseIndent();
@@ -3843,23 +3990,13 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
fout.print(uidState.toString());
}
- final int uidRules = mUidRules.get(uid, RULE_NONE);
- fout.print(" rules=");
- fout.print(uidRulesToString(uidRules));
- fout.println();
- }
- fout.decreaseIndent();
-
- fout.println("Status for just UIDs with rules:");
- fout.increaseIndent();
- size = mUidRules.size();
- for (int i = 0; i < size; i++) {
- final int uid = mUidRules.keyAt(i);
- fout.print("UID=");
- fout.print(uid);
- final int uidRules = mUidRules.get(uid, RULE_NONE);
- fout.print(" rules=");
- fout.print(uidRulesToString(uidRules));
+ final UidBlockedState uidBlockedState = mUidBlockedState.get(uid);
+ if (uidBlockedState == null) {
+ fout.print(" blocked_state={null}");
+ } else {
+ fout.print(" blocked_state=");
+ fout.print(uidBlockedState.toString());
+ }
fout.println();
}
fout.decreaseIndent();
@@ -4010,22 +4147,17 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
void updateRestrictedModeAllowlistUL() {
mUidFirewallRestrictedModeRules.clear();
forEachUid("updateRestrictedModeAllowlist", uid -> {
- final int oldUidRule = mUidRules.get(uid);
- final int newUidRule = getNewRestrictedModeUidRule(uid, oldUidRule);
- final boolean hasUidRuleChanged = oldUidRule != newUidRule;
- final int newFirewallRule = getRestrictedModeFirewallRule(newUidRule);
-
- // setUidFirewallRulesUL will allowlist all uids that are passed to it, so only add
- // non-default rules.
- if (newFirewallRule != FIREWALL_RULE_DEFAULT) {
- mUidFirewallRestrictedModeRules.append(uid, newFirewallRule);
- }
-
- if (hasUidRuleChanged) {
- mUidRules.put(uid, newUidRule);
- mHandler.obtainMessage(MSG_RULES_CHANGED, uid, newUidRule).sendToTarget();
+ synchronized (mUidRulesFirstLock) {
+ final UidBlockedState uidBlockedState = updateBlockedReasonsForRestrictedModeUL(
+ uid);
+ final int newFirewallRule = getRestrictedModeFirewallRule(uidBlockedState);
+
+ // setUidFirewallRulesUL will allowlist all uids that are passed to it, so only add
+ // non-default rules.
+ if (newFirewallRule != FIREWALL_RULE_DEFAULT) {
+ mUidFirewallRestrictedModeRules.append(uid, newFirewallRule);
+ }
}
- updateBlockedReasonsForRestrictedModeUL(uid);
});
if (mRestrictedNetworkingMode) {
// firewall rules only need to be set when this mode is being enabled.
@@ -4038,15 +4170,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
@VisibleForTesting
@GuardedBy("mUidRulesFirstLock")
void updateRestrictedModeForUidUL(int uid) {
- final int oldUidRule = mUidRules.get(uid);
- final int newUidRule = getNewRestrictedModeUidRule(uid, oldUidRule);
- final boolean hasUidRuleChanged = oldUidRule != newUidRule;
-
- if (hasUidRuleChanged) {
- mUidRules.put(uid, newUidRule);
- mHandler.obtainMessage(MSG_RULES_CHANGED, uid, newUidRule).sendToTarget();
- }
- updateBlockedReasonsForRestrictedModeUL(uid);
+ final UidBlockedState uidBlockedState = updateBlockedReasonsForRestrictedModeUL(uid);
// if restricted networking mode is on, and the app has an access exemption, the uid rule
// will not change, but the firewall rule will have to be updated.
@@ -4054,16 +4178,14 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
// Note: setUidFirewallRule also updates mUidFirewallRestrictedModeRules.
// In this case, default firewall rules can also be added.
setUidFirewallRule(FIREWALL_CHAIN_RESTRICTED, uid,
- getRestrictedModeFirewallRule(newUidRule));
+ getRestrictedModeFirewallRule(uidBlockedState));
}
}
- private void updateBlockedReasonsForRestrictedModeUL(int uid) {
- UidBlockedState uidBlockedState = mUidBlockedState.get(uid);
- if (uidBlockedState == null) {
- uidBlockedState = new UidBlockedState();
- mUidBlockedState.put(uid, uidBlockedState);
- }
+ @GuardedBy("mUidRulesFirstLock")
+ private UidBlockedState updateBlockedReasonsForRestrictedModeUL(int uid) {
+ final UidBlockedState uidBlockedState = getOrCreateUidBlockedStateForUid(
+ mUidBlockedState, uid);
final int oldEffectiveBlockedReasons = uidBlockedState.effectiveBlockedReasons;
if (mRestrictedNetworkingMode) {
uidBlockedState.blockedReasons |= BLOCKED_REASON_RESTRICTED_MODE;
@@ -4077,23 +4199,16 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
}
uidBlockedState.updateEffectiveBlockedReasons();
if (oldEffectiveBlockedReasons != uidBlockedState.effectiveBlockedReasons) {
- mHandler.obtainMessage(MSG_BLOCKED_REASON_CHANGED, uid,
- uidBlockedState.effectiveBlockedReasons, oldEffectiveBlockedReasons)
- .sendToTarget();
- }
- }
+ postBlockedReasonsChangedMsg(uid,
+ uidBlockedState.effectiveBlockedReasons, oldEffectiveBlockedReasons);
- private int getNewRestrictedModeUidRule(int uid, int oldUidRule) {
- int newRule = oldUidRule;
- newRule &= ~MASK_RESTRICTED_MODE_NETWORKS;
- if (mRestrictedNetworkingMode && !hasRestrictedModeAccess(uid)) {
- newRule |= RULE_REJECT_RESTRICTED_MODE;
+ postUidRulesChangedMsg(uid, uidBlockedState.deriveUidRules());
}
- return newRule;
+ return uidBlockedState;
}
- private static int getRestrictedModeFirewallRule(int uidRule) {
- if ((uidRule & RULE_REJECT_RESTRICTED_MODE) != 0) {
+ private static int getRestrictedModeFirewallRule(UidBlockedState uidBlockedState) {
+ if ((uidBlockedState.effectiveBlockedReasons & BLOCKED_REASON_RESTRICTED_MODE) != 0) {
// rejected in restricted mode, this is the default behavior.
return FIREWALL_RULE_DEFAULT;
} else {
@@ -4301,16 +4416,12 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
if (!isUidValidForDenylistRulesUL(uid)) {
continue;
}
- int oldRules = mUidRules.get(uid);
- if (enableChain) {
- // Chain wasn't enabled before and the other power-related
- // chains are allowlists, so we can clear the
- // MASK_ALL_NETWORKS part of the rules and re-inform listeners if
- // the effective rules result in blocking network access.
- oldRules &= MASK_METERED_NETWORKS;
- } else {
- // Skip if it had no restrictions to begin with
- if ((oldRules & MASK_ALL_NETWORKS) == 0) continue;
+ final UidBlockedState uidBlockedState = getOrCreateUidBlockedStateForUid(
+ mUidBlockedState, uid);
+ if (!enableChain && (uidBlockedState.blockedReasons & ~BLOCKED_METERED_REASON_MASK)
+ == BLOCKED_REASON_NONE) {
+ // Chain isn't enabled and the uid had no restrictions to begin with.
+ continue;
}
final boolean isUidIdle = !paroled && isUidIdle(uid);
if (isUidIdle && !mPowerSaveTempWhitelistAppIds.get(UserHandle.getAppId(uid))
@@ -4320,13 +4431,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
} else {
mUidFirewallStandbyRules.put(uid, FIREWALL_RULE_DEFAULT);
}
- final int newUidRules = updateRulesForPowerRestrictionsUL(uid, oldRules,
- isUidIdle);
- if (newUidRules == RULE_NONE) {
- mUidRules.delete(uid);
- } else {
- mUidRules.put(uid, newUidRules);
- }
+ updateRulesForPowerRestrictionsUL(uid, isUidIdle);
}
setUidFirewallRulesUL(FIREWALL_CHAIN_STANDBY, blockedUids,
enableChain ? CHAIN_TOGGLE_ENABLE : CHAIN_TOGGLE_DISABLE);
@@ -4544,6 +4649,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
mInternetPermissionMap.put(uid, hasPermission);
return hasPermission;
} catch (RemoteException e) {
+ // ignored; service lives in system_server
}
return true;
}
@@ -4554,7 +4660,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
@GuardedBy("mUidRulesFirstLock")
private void onUidDeletedUL(int uid) {
// First cleanup in-memory state synchronously...
- mUidRules.delete(uid);
+ mUidBlockedState.delete(uid);
mUidPolicy.delete(uid);
mUidFirewallStandbyRules.delete(uid);
mUidFirewallDozableRules.delete(uid);
@@ -4640,7 +4746,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
* permission, since there is no need to change the {@code iptables} rule if the app does not
* have permission to use the internet.
*
- * <p>The {@link #mUidRules} map is used to define the transtion of states of an UID.
+ * <p>The {@link #mUidBlockedState} map is used to define the transition of states of an UID.
*
*/
private void updateRulesForDataUsageRestrictionsUL(int uid) {
@@ -4655,6 +4761,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
}
}
+ @GuardedBy("mUidRulesFirstLock")
private void updateRulesForDataUsageRestrictionsULInner(int uid) {
if (!isUidValidForAllowlistRulesUL(uid)) {
if (LOGD) Slog.d(TAG, "no need to update restrict data rules for uid " + uid);
@@ -4662,38 +4769,17 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
}
final int uidPolicy = mUidPolicy.get(uid, POLICY_NONE);
- final int oldUidRules = mUidRules.get(uid, RULE_NONE);
final boolean isForeground = isUidForegroundOnRestrictBackgroundUL(uid);
final boolean isRestrictedByAdmin = isRestrictedByAdminUL(uid);
- UidBlockedState uidBlockedState = mUidBlockedState.get(uid);
- if (uidBlockedState == null) {
- uidBlockedState = new UidBlockedState();
- mUidBlockedState.put(uid, uidBlockedState);
- }
+ final UidBlockedState uidBlockedState = getOrCreateUidBlockedStateForUid(
+ mUidBlockedState, uid);
+ final UidBlockedState previousUidBlockedState = getOrCreateUidBlockedStateForUid(
+ mTmpUidBlockedState, uid);
+ previousUidBlockedState.copyFrom(uidBlockedState);
final boolean isDenied = (uidPolicy & POLICY_REJECT_METERED_BACKGROUND) != 0;
final boolean isAllowed = (uidPolicy & POLICY_ALLOW_METERED_BACKGROUND) != 0;
- // copy oldUidRules and clear out METERED_NETWORKS rules.
- int newUidRules = oldUidRules & (~MASK_METERED_NETWORKS);
-
- // First step: define the new rule based on user restrictions and foreground state.
- if (isRestrictedByAdmin) {
- newUidRules |= RULE_REJECT_METERED;
- } else if (isForeground) {
- if (isDenied || (mRestrictBackground && !isAllowed)) {
- newUidRules |= RULE_TEMPORARY_ALLOW_METERED;
- } else if (isAllowed) {
- newUidRules |= RULE_ALLOW_METERED;
- }
- } else {
- if (isDenied) {
- newUidRules |= RULE_REJECT_METERED;
- } else if (mRestrictBackground && isAllowed) {
- newUidRules |= RULE_ALLOW_METERED;
- }
- }
-
int newBlockedReasons = BLOCKED_REASON_NONE;
int newAllowedReasons = ALLOWED_REASON_NONE;
newBlockedReasons |= (isRestrictedByAdmin ? BLOCKED_METERED_REASON_ADMIN_DISABLED : 0);
@@ -4704,16 +4790,48 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
newAllowedReasons |= (isForeground ? ALLOWED_METERED_REASON_FOREGROUND : 0);
newAllowedReasons |= (isAllowed ? ALLOWED_METERED_REASON_USER_EXEMPTED : 0);
+ uidBlockedState.blockedReasons = (uidBlockedState.blockedReasons
+ & ~BLOCKED_METERED_REASON_MASK) | newBlockedReasons;
+ uidBlockedState.allowedReasons = (uidBlockedState.allowedReasons
+ & ~ALLOWED_METERED_REASON_MASK) | newAllowedReasons;
+ uidBlockedState.updateEffectiveBlockedReasons();
+ final int oldEffectiveBlockedReasons = previousUidBlockedState.effectiveBlockedReasons;
+ final int newEffectiveBlockedReasons = uidBlockedState.effectiveBlockedReasons;
+ if (oldEffectiveBlockedReasons != newEffectiveBlockedReasons) {
+ postBlockedReasonsChangedMsg(uid,
+ newEffectiveBlockedReasons, oldEffectiveBlockedReasons);
+
+ postUidRulesChangedMsg(uid, uidBlockedState.deriveUidRules());
+ }
+
+ // Note that the conditionals below are for avoiding unnecessary calls to netd.
+ // TODO: Measure the performance for doing a no-op call to netd so that we can
+ // remove the conditionals to simplify the logic below. We can also further reduce
+ // some calls to netd if they turn out to be costly.
+ final int denylistReasons = BLOCKED_METERED_REASON_ADMIN_DISABLED
+ | BLOCKED_METERED_REASON_USER_RESTRICTED;
+ if ((oldEffectiveBlockedReasons & denylistReasons) != BLOCKED_REASON_NONE
+ || (newEffectiveBlockedReasons & denylistReasons) != BLOCKED_REASON_NONE) {
+ setMeteredNetworkDenylist(uid,
+ (newEffectiveBlockedReasons & denylistReasons) != BLOCKED_REASON_NONE);
+ }
+ final int allowlistReasons = ALLOWED_METERED_REASON_FOREGROUND
+ | ALLOWED_METERED_REASON_USER_EXEMPTED;
+ final int oldAllowedReasons = previousUidBlockedState.allowedReasons;
+ if ((oldAllowedReasons & allowlistReasons) != ALLOWED_REASON_NONE
+ || (newAllowedReasons & allowlistReasons) != ALLOWED_REASON_NONE) {
+ setMeteredNetworkAllowlist(uid,
+ (newAllowedReasons & allowlistReasons) != ALLOWED_REASON_NONE);
+ }
+
if (LOGV) {
Log.v(TAG, "updateRuleForRestrictBackgroundUL(" + uid + ")"
+ ": isForeground=" +isForeground
+ ", isDenied=" + isDenied
+ ", isAllowed=" + isAllowed
+ ", isRestrictedByAdmin=" + isRestrictedByAdmin
- + ", oldRule=" + uidRulesToString(oldUidRules & MASK_METERED_NETWORKS)
- + ", newRule=" + uidRulesToString(newUidRules & MASK_METERED_NETWORKS)
- + ", newUidRules=" + uidRulesToString(newUidRules)
- + ", oldUidRules=" + uidRulesToString(oldUidRules)
+ + ", oldBlockedState=" + previousUidBlockedState.toString()
+ + ", newBlockedState="
+ ", oldBlockedMeteredReasons=" + NetworkPolicyManager.blockedReasonsToString(
uidBlockedState.blockedReasons & BLOCKED_METERED_REASON_MASK)
+ ", oldBlockedMeteredEffectiveReasons="
@@ -4722,84 +4840,11 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
+ ", oldAllowedMeteredReasons=" + NetworkPolicyManager.blockedReasonsToString(
uidBlockedState.allowedReasons & BLOCKED_METERED_REASON_MASK));
}
-
- if (newUidRules == RULE_NONE) {
- mUidRules.delete(uid);
- } else {
- mUidRules.put(uid, newUidRules);
- }
-
- // Second step: apply bw changes based on change of state.
- if (newUidRules != oldUidRules) {
- if (hasRule(newUidRules, RULE_TEMPORARY_ALLOW_METERED)) {
- // Temporarily allow foreground app, removing from denylist if necessary
- // (since bw_penalty_box prevails over bw_happy_box).
-
- setMeteredNetworkAllowlist(uid, true);
- // TODO: if statement below is used to avoid an unnecessary call to netd / iptables,
- // but ideally it should be just:
- // setMeteredNetworkDenylist(uid, isDenied);
- if (isDenied) {
- setMeteredNetworkDenylist(uid, false);
- }
- } else if (hasRule(oldUidRules, RULE_TEMPORARY_ALLOW_METERED)) {
- // Remove temporary exemption from app that is not on foreground anymore.
-
- // TODO: if statements below are used to avoid unnecessary calls to netd / iptables,
- // but ideally they should be just:
- // setMeteredNetworkAllowlist(uid, isAllowed);
- // setMeteredNetworkDenylist(uid, isDenied);
- if (!isAllowed) {
- setMeteredNetworkAllowlist(uid, false);
- }
- if (isDenied || isRestrictedByAdmin) {
- setMeteredNetworkDenylist(uid, true);
- }
- } else if (hasRule(newUidRules, RULE_REJECT_METERED)
- || hasRule(oldUidRules, RULE_REJECT_METERED)) {
- // Flip state because app was explicitly added or removed to denylist.
- setMeteredNetworkDenylist(uid, (isDenied || isRestrictedByAdmin));
- if (hasRule(oldUidRules, RULE_REJECT_METERED) && isAllowed) {
- // Since denial prevails over allowance, we need to handle the special case
- // where app is allowed and denied at the same time (although such
- // scenario should be blocked by the UI), then it is removed from the denylist.
- setMeteredNetworkAllowlist(uid, isAllowed);
- }
- } else if (hasRule(newUidRules, RULE_ALLOW_METERED)
- || hasRule(oldUidRules, RULE_ALLOW_METERED)) {
- // Flip state because app was explicitly added or removed to allowlist.
- setMeteredNetworkAllowlist(uid, isAllowed);
- } else {
- // All scenarios should have been covered above.
- Log.wtf(TAG, "Unexpected change of metered UID state for " + uid
- + ": foreground=" + isForeground
- + ", allowlisted=" + isAllowed
- + ", denylisted=" + isDenied
- + ", isRestrictedByAdmin=" + isRestrictedByAdmin
- + ", newRule=" + uidRulesToString(newUidRules)
- + ", oldRule=" + uidRulesToString(oldUidRules));
- }
-
- // Dispatch changed rule to existing listeners.
- mHandler.obtainMessage(MSG_RULES_CHANGED, uid, newUidRules).sendToTarget();
- }
-
- final int oldEffectiveBlockedReasons = uidBlockedState.effectiveBlockedReasons;
- uidBlockedState.blockedReasons = (uidBlockedState.blockedReasons
- & ~BLOCKED_METERED_REASON_MASK) | newBlockedReasons;
- uidBlockedState.allowedReasons = (uidBlockedState.allowedReasons
- & ~ALLOWED_METERED_REASON_MASK) | newAllowedReasons;
- uidBlockedState.updateEffectiveBlockedReasons();
- if (oldEffectiveBlockedReasons != uidBlockedState.effectiveBlockedReasons) {
- mHandler.obtainMessage(MSG_BLOCKED_REASON_CHANGED, uid,
- uidBlockedState.effectiveBlockedReasons, oldEffectiveBlockedReasons)
- .sendToTarget();
- }
}
/**
- * Updates the power-related part of the {@link #mUidRules} for a given map, and notify external
- * listeners in case of change.
+ * Updates the power-related part of the {@link #mUidBlockedState} for a given map, and
+ * notify external listeners in case of change.
* <p>
* There are 3 power-related rules that affects whether an app has background access on
* non-metered networks, and when the condition applies and the UID is not allowed for power
@@ -4810,23 +4855,15 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
* <li>Battery Saver Mode is on: {@code fw_powersave} firewall chain.
* </ul>
* <p>
- * This method updates the power-related part of the {@link #mUidRules} for a given uid based on
- * these modes, the UID process state (foreground or not), and the UID allowlist state.
+ * This method updates the power-related part of the {@link #mUidBlockedState} for a given
+ * uid based on these modes, the UID process state (foreground or not), and the UID
+ * allowlist state.
* <p>
* <strong>NOTE: </strong>This method does not update the firewall rules on {@code netd}.
*/
@GuardedBy("mUidRulesFirstLock")
private void updateRulesForPowerRestrictionsUL(int uid) {
- final int oldUidRules = mUidRules.get(uid, RULE_NONE);
-
- final int newUidRules = updateRulesForPowerRestrictionsUL(uid, oldUidRules,
- isUidIdle(uid));
-
- if (newUidRules == RULE_NONE) {
- mUidRules.delete(uid);
- } else {
- mUidRules.put(uid, newUidRules);
- }
+ updateRulesForPowerRestrictionsUL(uid, isUidIdle(uid));
}
/**
@@ -4835,56 +4872,37 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
* @param uid the uid of the app to update rules for
* @param oldUidRules the current rules for the uid, in order to determine if there's a change
* @param isUidIdle whether uid is idle or not
- *
- * @return the new computed rules for the uid
*/
@GuardedBy("mUidRulesFirstLock")
- private int updateRulesForPowerRestrictionsUL(int uid, int oldUidRules, boolean isUidIdle) {
+ private void updateRulesForPowerRestrictionsUL(int uid, boolean isUidIdle) {
if (Trace.isTagEnabled(Trace.TRACE_TAG_NETWORK)) {
Trace.traceBegin(Trace.TRACE_TAG_NETWORK,
- "updateRulesForPowerRestrictionsUL: " + uid + "/" + oldUidRules + "/"
+ "updateRulesForPowerRestrictionsUL: " + uid + "/"
+ (isUidIdle ? "I" : "-"));
}
try {
- return updateRulesForPowerRestrictionsULInner(uid, oldUidRules, isUidIdle);
+ updateRulesForPowerRestrictionsULInner(uid, isUidIdle);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_NETWORK);
}
}
@GuardedBy("mUidRulesFirstLock")
- private int updateRulesForPowerRestrictionsULInner(int uid, int oldUidRules,
- boolean isUidIdle) {
+ private void updateRulesForPowerRestrictionsULInner(int uid, boolean isUidIdle) {
if (!isUidValidForDenylistRulesUL(uid)) {
if (LOGD) Slog.d(TAG, "no need to update restrict power rules for uid " + uid);
- return RULE_NONE;
+ return;
}
- final boolean restrictMode = isUidIdle || mRestrictPower || mDeviceIdleMode;
final boolean isForeground = isUidForegroundOnRestrictPowerUL(uid);
final boolean isWhitelisted = isWhitelistedFromPowerSaveUL(uid, mDeviceIdleMode);
- // Copy existing uid rules and clear ALL_NETWORK rules.
- int newUidRules = oldUidRules & (~MASK_ALL_NETWORKS);
-
- UidBlockedState uidBlockedState = mUidBlockedState.get(uid);
- if (uidBlockedState == null) {
- uidBlockedState = new UidBlockedState();
- mUidBlockedState.put(uid, uidBlockedState);
- }
-
- // First step: define the new rule based on user restrictions and foreground state.
-
- // NOTE: if statements below could be inlined, but it's easier to understand the logic
- // by considering the foreground and non-foreground states.
- if (isForeground) {
- if (restrictMode) {
- newUidRules |= RULE_ALLOW_ALL;
- }
- } else if (restrictMode) {
- newUidRules |= isWhitelisted ? RULE_ALLOW_ALL : RULE_REJECT_ALL;
- }
+ final UidBlockedState uidBlockedState = getOrCreateUidBlockedStateForUid(
+ mUidBlockedState, uid);
+ final UidBlockedState previousUidBlockedState = getOrCreateUidBlockedStateForUid(
+ mTmpUidBlockedState, uid);
+ previousUidBlockedState.copyFrom(uidBlockedState);
int newBlockedReasons = BLOCKED_REASON_NONE;
int newAllowedReasons = ALLOWED_REASON_NONE;
@@ -4902,6 +4920,20 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
newAllowedReasons |= (uidBlockedState.allowedReasons
& ALLOWED_REASON_RESTRICTED_MODE_PERMISSIONS);
+ uidBlockedState.blockedReasons = (uidBlockedState.blockedReasons
+ & BLOCKED_METERED_REASON_MASK) | newBlockedReasons;
+ uidBlockedState.allowedReasons = (uidBlockedState.allowedReasons
+ & ALLOWED_METERED_REASON_MASK) | newAllowedReasons;
+ uidBlockedState.updateEffectiveBlockedReasons();
+ if (previousUidBlockedState.effectiveBlockedReasons
+ != uidBlockedState.effectiveBlockedReasons) {
+ postBlockedReasonsChangedMsg(uid,
+ uidBlockedState.effectiveBlockedReasons,
+ previousUidBlockedState.effectiveBlockedReasons);
+
+ postUidRulesChangedMsg(uid, uidBlockedState.deriveUidRules());
+ }
+
if (LOGV) {
Log.v(TAG, "updateRulesForPowerRestrictionsUL(" + uid + ")"
+ ", isIdle: " + isUidIdle
@@ -4909,43 +4941,9 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
+ ", mDeviceIdleMode: " + mDeviceIdleMode
+ ", isForeground=" + isForeground
+ ", isWhitelisted=" + isWhitelisted
- + ", oldRule=" + uidRulesToString(oldUidRules & MASK_ALL_NETWORKS)
- + ", newRule=" + uidRulesToString(newUidRules & MASK_ALL_NETWORKS)
- + ", newUidRules=" + uidRulesToString(newUidRules)
- + ", oldUidRules=" + uidRulesToString(oldUidRules));
- }
-
- // Second step: notify listeners if state changed.
- if (newUidRules != oldUidRules) {
- if ((newUidRules & MASK_ALL_NETWORKS) == RULE_NONE || hasRule(newUidRules,
- RULE_ALLOW_ALL)) {
- if (LOGV) Log.v(TAG, "Allowing non-metered access for UID " + uid);
- } else if (hasRule(newUidRules, RULE_REJECT_ALL)) {
- if (LOGV) Log.v(TAG, "Rejecting non-metered access for UID " + uid);
- } else {
- // All scenarios should have been covered above
- Log.wtf(TAG, "Unexpected change of non-metered UID state for " + uid
- + ": foreground=" + isForeground
- + ", whitelisted=" + isWhitelisted
- + ", newRule=" + uidRulesToString(newUidRules)
- + ", oldRule=" + uidRulesToString(oldUidRules));
- }
- mHandler.obtainMessage(MSG_RULES_CHANGED, uid, newUidRules).sendToTarget();
+ + ", oldUidBlockedState=" + previousUidBlockedState.toString()
+ + ", newUidBlockedState=" + uidBlockedState.toString());
}
-
- final int oldEffectiveBlockedReasons = uidBlockedState.effectiveBlockedReasons;
- uidBlockedState.blockedReasons = (uidBlockedState.blockedReasons
- & BLOCKED_METERED_REASON_MASK) | newBlockedReasons;
- uidBlockedState.allowedReasons = (uidBlockedState.allowedReasons
- & ALLOWED_METERED_REASON_MASK) | newAllowedReasons;
- uidBlockedState.updateEffectiveBlockedReasons();
- if (oldEffectiveBlockedReasons != uidBlockedState.effectiveBlockedReasons) {
- mHandler.obtainMessage(MSG_BLOCKED_REASON_CHANGED, uid,
- uidBlockedState.effectiveBlockedReasons, oldEffectiveBlockedReasons)
- .sendToTarget();
- }
-
- return newUidRules;
}
private class NetPolicyAppIdleStateChangeListener extends AppIdleStateChangeListener {
@@ -4973,10 +4971,23 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
}
}
+ private void postBlockedReasonsChangedMsg(int uid, int newEffectiveBlockedReasons,
+ int oldEffectiveBlockedReasons) {
+ mHandler.obtainMessage(MSG_BLOCKED_REASON_CHANGED, uid,
+ newEffectiveBlockedReasons, oldEffectiveBlockedReasons)
+ .sendToTarget();
+ }
+
+ private void postUidRulesChangedMsg(int uid, int uidRules) {
+ mHandler.obtainMessage(MSG_RULES_CHANGED, uid, uidRules)
+ .sendToTarget();
+ }
+
private void dispatchUidRulesChanged(INetworkPolicyListener listener, int uid, int uidRules) {
try {
listener.onUidRulesChanged(uid, uidRules);
} catch (RemoteException ignored) {
+ // Ignore if there is an error sending the callback to the client.
}
}
@@ -4985,6 +4996,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
try {
listener.onMeteredIfacesChanged(meteredIfaces);
} catch (RemoteException ignored) {
+ // Ignore if there is an error sending the callback to the client.
}
}
@@ -4993,6 +5005,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
try {
listener.onRestrictBackgroundChanged(restrictBackground);
} catch (RemoteException ignored) {
+ // Ignore if there is an error sending the callback to the client.
}
}
@@ -5001,6 +5014,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
try {
listener.onUidPoliciesChanged(uid, uidPolicies);
} catch (RemoteException ignored) {
+ // Ignore if there is an error sending the callback to the client.
}
}
@@ -5009,6 +5023,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
try {
listener.onSubscriptionOverride(subId, overrideMask, overrideValue, networkTypes);
} catch (RemoteException ignored) {
+ // Ignore if there is an error sending the callback to the client.
}
}
@@ -5017,6 +5032,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
try {
listener.onSubscriptionPlansChanged(subId, plans);
} catch (RemoteException ignored) {
+ // Ignore if there is an error sending the callback to the client.
}
}
@@ -5025,6 +5041,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
try {
listener.onBlockedReasonChanged(uid, oldBlockedReasons, newBlockedReasons);
} catch (RemoteException ignored) {
+ // Ignore if there is an error sending the callback to the client.
}
}
@@ -5035,6 +5052,10 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
case MSG_RULES_CHANGED: {
final int uid = msg.arg1;
final int uidRules = msg.arg2;
+ if (LOGV) {
+ Slog.v(TAG, "Dispatching rules=" + uidRulesToString(uidRules)
+ + " for uid=" + uid);
+ }
final int length = mListeners.beginBroadcast();
for (int i = 0; i < length; i++) {
final INetworkPolicyListener listener = mListeners.getBroadcastItem(i);
@@ -5121,7 +5142,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
// make sure stats are recorded frequently enough; we aim
// for 2MB threshold for 2GB/month rules.
final long persistThreshold = lowestRule / 1000;
- mNetworkStats.advisePersistThreshold(persistThreshold);
+ // TODO: Sync internal naming with the API surface.
+ mNetworkStats.setDefaultGlobalAlert(persistThreshold);
return true;
}
case MSG_UPDATE_INTERFACE_QUOTAS: {
@@ -5490,25 +5512,10 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
@Deprecated
private long getTotalBytes(NetworkTemplate template, long start, long end) {
- return getNetworkTotalBytes(template, start, end);
- }
-
- private long getNetworkTotalBytes(NetworkTemplate template, long start, long end) {
- try {
- return mNetworkStats.getNetworkTotalBytes(template, start, end);
- } catch (RuntimeException e) {
- Slog.w(TAG, "Failed to read network stats: " + e);
- return 0;
- }
- }
-
- private NetworkStats getNetworkUidBytes(NetworkTemplate template, long start, long end) {
- try {
- return mNetworkStats.getNetworkUidBytes(template, start, end);
- } catch (RuntimeException e) {
- Slog.w(TAG, "Failed to read network stats: " + e);
- return new NetworkStats(SystemClock.elapsedRealtime(), 0);
- }
+ // Skip if not ready. NetworkStatsService will block public API calls until it is
+ // ready. To prevent NPMS be blocked on that, skip and fail fast instead.
+ if (!mStatsCallback.isAnyCallbackReceived()) return 0;
+ return mDeps.getNetworkTotalBytes(template, start, end);
}
private boolean isBandwidthControlEnabled() {
@@ -5607,7 +5614,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
}
}
- private static void collectKeys(SparseArray<UidState> source, SparseBooleanArray target) {
+ private static <T> void collectKeys(SparseArray<T> source, SparseBooleanArray target) {
final int size = source.size();
for (int i = 0; i < size; i++) {
target.put(source.keyAt(i), true);
@@ -5626,7 +5633,11 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
NetworkPolicy[] policies = getNetworkPolicies(mContext.getOpPackageName());
NetworkTemplate templateCarrier = subscriber != null
? buildTemplateCarrierMetered(subscriber) : null;
- NetworkTemplate templateMobile = buildTemplateMobileAll(subscriber);
+ NetworkTemplate templateMobile = subscriber != null
+ ? new NetworkTemplate.Builder(MATCH_MOBILE)
+ .setSubscriberIds(Set.of(subscriber))
+ .setMeteredness(android.net.NetworkStats.METERED_YES)
+ .build() : null;
for (NetworkPolicy policy : policies) {
// All policies loaded from disk will be carrier templates, and setting will also only
// set carrier templates, but we clear mobile templates just in case one is set by
@@ -5655,90 +5666,38 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
final long startTime = mStatLogger.getTime();
mContext.enforceCallingOrSelfPermission(OBSERVE_NETWORK_POLICY, TAG);
- final int uidRules;
- final boolean isBackgroundRestricted;
+ int blockedReasons;
synchronized (mUidRulesFirstLock) {
- uidRules = mUidRules.get(uid, RULE_NONE);
- isBackgroundRestricted = mRestrictBackground;
+ final UidBlockedState uidBlockedState = mUidBlockedState.get(uid);
+ blockedReasons = uidBlockedState == null
+ ? BLOCKED_REASON_NONE : uidBlockedState.effectiveBlockedReasons;
+ if (!isNetworkMetered) {
+ blockedReasons &= ~BLOCKED_METERED_REASON_MASK;
+ }
+ mLogger.networkBlocked(uid, uidBlockedState);
}
- final boolean ret = isUidNetworkingBlockedInternal(uid, uidRules, isNetworkMetered,
- isBackgroundRestricted, mLogger);
mStatLogger.logDurationStat(Stats.IS_UID_NETWORKING_BLOCKED, startTime);
- return ret;
+ return blockedReasons != BLOCKED_REASON_NONE;
}
@Override
public boolean isUidRestrictedOnMeteredNetworks(int uid) {
mContext.enforceCallingOrSelfPermission(OBSERVE_NETWORK_POLICY, TAG);
- final int uidRules;
- final boolean isBackgroundRestricted;
synchronized (mUidRulesFirstLock) {
- uidRules = mUidRules.get(uid, RULE_ALLOW_ALL);
- isBackgroundRestricted = mRestrictBackground;
+ final UidBlockedState uidBlockedState = mUidBlockedState.get(uid);
+ int blockedReasons = uidBlockedState == null
+ ? BLOCKED_REASON_NONE : uidBlockedState.effectiveBlockedReasons;
+ blockedReasons &= BLOCKED_METERED_REASON_MASK;
+ return blockedReasons != BLOCKED_REASON_NONE;
}
- // TODO(b/177490332): The logic here might not be correct because it doesn't consider
- // RULE_REJECT_METERED condition. And it could be replaced by
- // isUidNetworkingBlockedInternal().
- return isBackgroundRestricted
- && !hasRule(uidRules, RULE_ALLOW_METERED)
- && !hasRule(uidRules, RULE_TEMPORARY_ALLOW_METERED);
}
private static boolean isSystem(int uid) {
return uid < Process.FIRST_APPLICATION_UID;
}
- static boolean isUidNetworkingBlockedInternal(int uid, int uidRules, boolean isNetworkMetered,
- boolean isBackgroundRestricted, @Nullable NetworkPolicyLogger logger) {
- final int reason;
- // Networks are never blocked for system components
- if (isSystem(uid)) {
- reason = NTWK_ALLOWED_SYSTEM;
- } else if (hasRule(uidRules, RULE_REJECT_RESTRICTED_MODE)) {
- reason = NTWK_BLOCKED_RESTRICTED_MODE;
- } else if (hasRule(uidRules, RULE_REJECT_ALL)) {
- reason = NTWK_BLOCKED_POWER;
- } else if (!isNetworkMetered) {
- reason = NTWK_ALLOWED_NON_METERED;
- } else if (hasRule(uidRules, RULE_REJECT_METERED)) {
- reason = NTWK_BLOCKED_DENYLIST;
- } else if (hasRule(uidRules, RULE_ALLOW_METERED)) {
- reason = NTWK_ALLOWED_ALLOWLIST;
- } else if (hasRule(uidRules, RULE_TEMPORARY_ALLOW_METERED)) {
- reason = NTWK_ALLOWED_TMP_ALLOWLIST;
- } else if (isBackgroundRestricted) {
- reason = NTWK_BLOCKED_BG_RESTRICT;
- } else {
- reason = NTWK_ALLOWED_DEFAULT;
- }
-
- final boolean blocked;
- switch(reason) {
- case NTWK_ALLOWED_DEFAULT:
- case NTWK_ALLOWED_NON_METERED:
- case NTWK_ALLOWED_TMP_ALLOWLIST:
- case NTWK_ALLOWED_ALLOWLIST:
- case NTWK_ALLOWED_SYSTEM:
- blocked = false;
- break;
- case NTWK_BLOCKED_RESTRICTED_MODE:
- case NTWK_BLOCKED_POWER:
- case NTWK_BLOCKED_DENYLIST:
- case NTWK_BLOCKED_BG_RESTRICT:
- blocked = true;
- break;
- default:
- throw new IllegalArgumentException();
- }
- if (logger != null) {
- logger.networkBlocked(uid, reason);
- }
-
- return blocked;
- }
-
private class NetworkPolicyManagerInternalImpl extends NetworkPolicyManagerInternal {
@Override
@@ -5780,14 +5739,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
}
@Override
- public SubscriptionPlan getSubscriptionPlan(NetworkTemplate template) {
- synchronized (mNetworkPoliciesSecondLock) {
- final int subId = findRelevantSubIdNL(template);
- return getPrimarySubscriptionPlanLocked(subId);
- }
- }
-
- @Override
public long getSubscriptionOpportunisticQuota(Network network, int quotaType) {
final long quotaBytes;
synchronized (mNetworkPoliciesSecondLock) {
@@ -5829,12 +5780,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
mHandler.obtainMessage(MSG_METERED_RESTRICTED_PACKAGES_CHANGED,
userId, 0, packageNames).sendToTarget();
}
-
- @Override
- public void onStatsProviderWarningOrLimitReached(@NonNull String tag) {
- Log.v(TAG, "onStatsProviderWarningOrLimitReached: " + tag);
- mHandler.obtainMessage(MSG_STATS_PROVIDER_WARNING_OR_LIMIT_REACHED).sendToTarget();
- }
}
private void setMeteredRestrictedPackagesInternal(Set<String> packageNames, int userId) {
@@ -5947,6 +5892,16 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
return (bundle != null) ? bundle.getBoolean(key, defaultValue) : defaultValue;
}
+ private static UidBlockedState getOrCreateUidBlockedStateForUid(
+ SparseArray<UidBlockedState> uidBlockedStates, int uid) {
+ UidBlockedState uidBlockedState = uidBlockedStates.get(uid);
+ if (uidBlockedState == null) {
+ uidBlockedState = new UidBlockedState();
+ uidBlockedStates.put(uid, uidBlockedState);
+ }
+ return uidBlockedState;
+ }
+
@VisibleForTesting
static final class UidBlockedState {
public int blockedReasons;
@@ -6010,9 +5965,180 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
}
return effectiveBlockedReasons;
}
+
+ @Override
+ public String toString() {
+ return toString(blockedReasons, allowedReasons, effectiveBlockedReasons);
+ }
+
+ public static String toString(int blockedReasons, int allowedReasons,
+ int effectiveBlockedReasons) {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("{");
+ sb.append("blocked=").append(blockedReasonsToString(blockedReasons)).append(",");
+ sb.append("allowed=").append(allowedReasonsToString(allowedReasons)).append(",");
+ sb.append("effective=").append(blockedReasonsToString(effectiveBlockedReasons));
+ sb.append("}");
+ return sb.toString();
+ }
+
+ private static final int[] BLOCKED_REASONS = {
+ BLOCKED_REASON_BATTERY_SAVER,
+ BLOCKED_REASON_DOZE,
+ BLOCKED_REASON_APP_STANDBY,
+ BLOCKED_REASON_RESTRICTED_MODE,
+ BLOCKED_METERED_REASON_DATA_SAVER,
+ BLOCKED_METERED_REASON_USER_RESTRICTED,
+ BLOCKED_METERED_REASON_ADMIN_DISABLED,
+ };
+
+ private static final int[] ALLOWED_REASONS = {
+ ALLOWED_REASON_SYSTEM,
+ ALLOWED_REASON_FOREGROUND,
+ ALLOWED_REASON_POWER_SAVE_ALLOWLIST,
+ ALLOWED_REASON_POWER_SAVE_EXCEPT_IDLE_ALLOWLIST,
+ ALLOWED_REASON_RESTRICTED_MODE_PERMISSIONS,
+ ALLOWED_METERED_REASON_USER_EXEMPTED,
+ ALLOWED_METERED_REASON_SYSTEM,
+ ALLOWED_METERED_REASON_FOREGROUND,
+ };
+
+ private static String blockedReasonToString(int blockedReason) {
+ switch (blockedReason) {
+ case BLOCKED_REASON_NONE:
+ return "NONE";
+ case BLOCKED_REASON_BATTERY_SAVER:
+ return "BATTERY_SAVER";
+ case BLOCKED_REASON_DOZE:
+ return "DOZE";
+ case BLOCKED_REASON_APP_STANDBY:
+ return "APP_STANDBY";
+ case BLOCKED_REASON_RESTRICTED_MODE:
+ return "RESTRICTED_MODE";
+ case BLOCKED_METERED_REASON_DATA_SAVER:
+ return "DATA_SAVER";
+ case BLOCKED_METERED_REASON_USER_RESTRICTED:
+ return "METERED_USER_RESTRICTED";
+ case BLOCKED_METERED_REASON_ADMIN_DISABLED:
+ return "METERED_ADMIN_DISABLED";
+ default:
+ Slog.wtfStack(TAG, "Unknown blockedReason: " + blockedReason);
+ return String.valueOf(blockedReason);
+ }
+ }
+
+ private static String allowedReasonToString(int allowedReason) {
+ switch (allowedReason) {
+ case ALLOWED_REASON_NONE:
+ return "NONE";
+ case ALLOWED_REASON_SYSTEM:
+ return "SYSTEM";
+ case ALLOWED_REASON_FOREGROUND:
+ return "FOREGROUND";
+ case ALLOWED_REASON_POWER_SAVE_ALLOWLIST:
+ return "POWER_SAVE_ALLOWLIST";
+ case ALLOWED_REASON_POWER_SAVE_EXCEPT_IDLE_ALLOWLIST:
+ return "POWER_SAVE_EXCEPT_IDLE_ALLOWLIST";
+ case ALLOWED_REASON_RESTRICTED_MODE_PERMISSIONS:
+ return "RESTRICTED_MODE_PERMISSIONS";
+ case ALLOWED_METERED_REASON_USER_EXEMPTED:
+ return "METERED_USER_EXEMPTED";
+ case ALLOWED_METERED_REASON_SYSTEM:
+ return "METERED_SYSTEM";
+ case ALLOWED_METERED_REASON_FOREGROUND:
+ return "METERED_FOREGROUND";
+ default:
+ Slog.wtfStack(TAG, "Unknown allowedReason: " + allowedReason);
+ return String.valueOf(allowedReason);
+ }
+ }
+
+ public static String blockedReasonsToString(int blockedReasons) {
+ if (blockedReasons == BLOCKED_REASON_NONE) {
+ return blockedReasonToString(BLOCKED_REASON_NONE);
+ }
+ final StringBuilder sb = new StringBuilder();
+ for (int reason : BLOCKED_REASONS) {
+ if ((blockedReasons & reason) != 0) {
+ sb.append(sb.length() == 0 ? "" : "|");
+ sb.append(blockedReasonToString(reason));
+ blockedReasons &= ~reason;
+ }
+ }
+ if (blockedReasons != 0) {
+ sb.append(sb.length() == 0 ? "" : "|");
+ sb.append(String.valueOf(blockedReasons));
+ Slog.wtfStack(TAG, "Unknown blockedReasons: " + blockedReasons);
+ }
+ return sb.toString();
+ }
+
+ public static String allowedReasonsToString(int allowedReasons) {
+ if (allowedReasons == ALLOWED_REASON_NONE) {
+ return allowedReasonToString(ALLOWED_REASON_NONE);
+ }
+ final StringBuilder sb = new StringBuilder();
+ for (int reason : ALLOWED_REASONS) {
+ if ((allowedReasons & reason) != 0) {
+ sb.append(sb.length() == 0 ? "" : "|");
+ sb.append(allowedReasonToString(reason));
+ allowedReasons &= ~reason;
+ }
+ }
+ if (allowedReasons != 0) {
+ sb.append(sb.length() == 0 ? "" : "|");
+ sb.append(String.valueOf(allowedReasons));
+ Slog.wtfStack(TAG, "Unknown allowedReasons: " + allowedReasons);
+ }
+ return sb.toString();
+ }
+
+ public void copyFrom(UidBlockedState uidBlockedState) {
+ blockedReasons = uidBlockedState.blockedReasons;
+ allowedReasons = uidBlockedState.allowedReasons;
+ effectiveBlockedReasons = uidBlockedState.effectiveBlockedReasons;
+ }
+
+ public int deriveUidRules() {
+ int uidRule = RULE_NONE;
+ if ((effectiveBlockedReasons & BLOCKED_REASON_RESTRICTED_MODE) != 0) {
+ uidRule |= RULE_REJECT_RESTRICTED_MODE;
+ }
+
+ int powerBlockedReasons = BLOCKED_REASON_APP_STANDBY
+ | BLOCKED_REASON_DOZE
+ | BLOCKED_REASON_BATTERY_SAVER;
+ if ((effectiveBlockedReasons & powerBlockedReasons) != 0) {
+ uidRule |= RULE_REJECT_ALL;
+ } else if ((blockedReasons & powerBlockedReasons) != 0) {
+ uidRule |= RULE_ALLOW_ALL;
+ }
+
+ // UidRule doesn't include RestrictBackground (DataSaver) state, so not including in
+ // metered blocked reasons below.
+ int meteredBlockedReasons = BLOCKED_METERED_REASON_ADMIN_DISABLED
+ | BLOCKED_METERED_REASON_USER_RESTRICTED;
+ if ((effectiveBlockedReasons & meteredBlockedReasons) != 0) {
+ uidRule |= RULE_REJECT_METERED;
+ } else if ((blockedReasons & BLOCKED_METERED_REASON_USER_RESTRICTED) != 0
+ && (allowedReasons & ALLOWED_METERED_REASON_FOREGROUND) != 0) {
+ uidRule |= RULE_TEMPORARY_ALLOW_METERED;
+ } else if ((blockedReasons & BLOCKED_METERED_REASON_DATA_SAVER) != 0) {
+ if ((allowedReasons & ALLOWED_METERED_REASON_USER_EXEMPTED) != 0) {
+ uidRule |= RULE_ALLOW_ALL;
+ } else if ((allowedReasons & ALLOWED_METERED_REASON_FOREGROUND) != 0) {
+ uidRule |= RULE_TEMPORARY_ALLOW_METERED;
+ }
+ }
+ if (LOGV) {
+ Slog.v(TAG, "uidBlockedState=" + this.toString()
+ + " -> uidRule=" + uidRulesToString(uidRule));
+ }
+ return uidRule;
+ }
}
- private class NotificationId {
+ private static class NotificationId {
private final String mTag;
private final int mId;
diff --git a/services/core/java/com/android/server/net/NetworkStatsAccess.java b/services/core/java/com/android/server/net/NetworkStatsAccess.java
deleted file mode 100644
index d25eae409d40..000000000000
--- a/services/core/java/com/android/server/net/NetworkStatsAccess.java
+++ /dev/null
@@ -1,199 +0,0 @@
-/*
- * Copyright (C) 2015 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.net;
-
-import static android.Manifest.permission.READ_NETWORK_USAGE_HISTORY;
-import static android.net.NetworkStats.UID_ALL;
-import static android.net.TrafficStats.UID_REMOVED;
-import static android.net.TrafficStats.UID_TETHERING;
-
-import android.Manifest;
-import android.annotation.IntDef;
-import android.app.AppOpsManager;
-import android.app.admin.DevicePolicyManagerInternal;
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.os.Binder;
-import android.os.Process;
-import android.os.UserHandle;
-import android.telephony.TelephonyManager;
-
-import com.android.server.LocalServices;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/** Utility methods for controlling access to network stats APIs. */
-public final class NetworkStatsAccess {
- private NetworkStatsAccess() {}
-
- /**
- * Represents an access level for the network usage history and statistics APIs.
- *
- * <p>Access levels are in increasing order; that is, it is reasonable to check access by
- * verifying that the caller's access level is at least the minimum required level.
- */
- @IntDef({
- Level.DEFAULT,
- Level.USER,
- Level.DEVICESUMMARY,
- Level.DEVICE,
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface Level {
- /**
- * Default, unprivileged access level.
- *
- * <p>Can only access usage for one's own UID.
- *
- * <p>Every app will have at least this access level.
- */
- int DEFAULT = 0;
-
- /**
- * Access level for apps which can access usage for any app running in the same user.
- *
- * <p>Granted to:
- * <ul>
- * <li>Profile owners.
- * </ul>
- */
- int USER = 1;
-
- /**
- * Access level for apps which can access usage summary of device. Device summary includes
- * usage by apps running in any profiles/users, however this access level does not
- * allow querying usage of individual apps running in other profiles/users.
- *
- * <p>Granted to:
- * <ul>
- * <li>Apps with the PACKAGE_USAGE_STATS permission granted. Note that this is an AppOps bit
- * so it is not necessarily sufficient to declare this in the manifest.
- * <li>Apps with the (signature/privileged) READ_NETWORK_USAGE_HISTORY permission.
- * </ul>
- */
- int DEVICESUMMARY = 2;
-
- /**
- * Access level for apps which can access usage for any app on the device, including apps
- * running on other users/profiles.
- *
- * <p>Granted to:
- * <ul>
- * <li>Device owners.
- * <li>Carrier-privileged applications.
- * <li>The system UID.
- * </ul>
- */
- int DEVICE = 3;
- }
-
- /** Returns the {@link NetworkStatsAccess.Level} for the given caller. */
- public static @NetworkStatsAccess.Level int checkAccessLevel(
- Context context, int callingUid, String callingPackage) {
- final DevicePolicyManagerInternal dpmi = LocalServices.getService(
- DevicePolicyManagerInternal.class);
- final TelephonyManager tm = (TelephonyManager)
- context.getSystemService(Context.TELEPHONY_SERVICE);
- boolean hasCarrierPrivileges;
- final long token = Binder.clearCallingIdentity();
- try {
- hasCarrierPrivileges = tm != null
- && tm.checkCarrierPrivilegesForPackageAnyPhone(callingPackage)
- == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
- } finally {
- Binder.restoreCallingIdentity(token);
- }
-
- final boolean isDeviceOwner = dpmi != null && dpmi.isActiveDeviceOwner(callingUid);
- final int appId = UserHandle.getAppId(callingUid);
- if (hasCarrierPrivileges || isDeviceOwner
- || appId == Process.SYSTEM_UID || appId == Process.NETWORK_STACK_UID) {
- // Carrier-privileged apps and device owners, and the system (including the
- // network stack) can access data usage for all apps on the device.
- return NetworkStatsAccess.Level.DEVICE;
- }
-
- boolean hasAppOpsPermission = hasAppOpsPermission(context, callingUid, callingPackage);
- if (hasAppOpsPermission || context.checkCallingOrSelfPermission(
- READ_NETWORK_USAGE_HISTORY) == PackageManager.PERMISSION_GRANTED) {
- return NetworkStatsAccess.Level.DEVICESUMMARY;
- }
-
- //TODO(b/169395065) Figure out if this flow makes sense in Device Owner mode.
- boolean isProfileOwner = dpmi != null && (dpmi.isActiveProfileOwner(callingUid)
- || dpmi.isActiveDeviceOwner(callingUid));
- if (isProfileOwner) {
- // Apps with the AppOps permission, profile owners, and apps with the privileged
- // permission can access data usage for all apps in this user/profile.
- return NetworkStatsAccess.Level.USER;
- }
-
- // Everyone else gets default access (only to their own UID).
- return NetworkStatsAccess.Level.DEFAULT;
- }
-
- /**
- * Returns whether the given caller should be able to access the given UID when the caller has
- * the given {@link NetworkStatsAccess.Level}.
- */
- public static boolean isAccessibleToUser(int uid, int callerUid,
- @NetworkStatsAccess.Level int accessLevel) {
- switch (accessLevel) {
- case NetworkStatsAccess.Level.DEVICE:
- // Device-level access - can access usage for any uid.
- return true;
- case NetworkStatsAccess.Level.DEVICESUMMARY:
- // Can access usage for any app running in the same user, along
- // with some special uids (system, removed, or tethering) and
- // anonymized uids
- return uid == android.os.Process.SYSTEM_UID || uid == UID_REMOVED
- || uid == UID_TETHERING || uid == UID_ALL
- || UserHandle.getUserId(uid) == UserHandle.getUserId(callerUid);
- case NetworkStatsAccess.Level.USER:
- // User-level access - can access usage for any app running in the same user, along
- // with some special uids (system, removed, or tethering).
- return uid == android.os.Process.SYSTEM_UID || uid == UID_REMOVED
- || uid == UID_TETHERING
- || UserHandle.getUserId(uid) == UserHandle.getUserId(callerUid);
- case NetworkStatsAccess.Level.DEFAULT:
- default:
- // Default access level - can only access one's own usage.
- return uid == callerUid;
- }
- }
-
- private static boolean hasAppOpsPermission(
- Context context, int callingUid, String callingPackage) {
- if (callingPackage != null) {
- AppOpsManager appOps = (AppOpsManager) context.getSystemService(
- Context.APP_OPS_SERVICE);
-
- final int mode = appOps.noteOp(AppOpsManager.OP_GET_USAGE_STATS,
- callingUid, callingPackage);
- if (mode == AppOpsManager.MODE_DEFAULT) {
- // The default behavior here is to check if PackageManager has given the app
- // permission.
- final int permissionCheck = context.checkCallingPermission(
- Manifest.permission.PACKAGE_USAGE_STATS);
- return permissionCheck == PackageManager.PERMISSION_GRANTED;
- }
- return (mode == AppOpsManager.MODE_ALLOWED);
- }
- return false;
- }
-}
diff --git a/services/core/java/com/android/server/net/NetworkStatsCollection.java b/services/core/java/com/android/server/net/NetworkStatsCollection.java
deleted file mode 100644
index df372b1459af..000000000000
--- a/services/core/java/com/android/server/net/NetworkStatsCollection.java
+++ /dev/null
@@ -1,815 +0,0 @@
-/*
- * Copyright (C) 2012 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.net;
-
-import static android.net.NetworkStats.DEFAULT_NETWORK_NO;
-import static android.net.NetworkStats.DEFAULT_NETWORK_YES;
-import static android.net.NetworkStats.IFACE_ALL;
-import static android.net.NetworkStats.METERED_NO;
-import static android.net.NetworkStats.METERED_YES;
-import static android.net.NetworkStats.ROAMING_NO;
-import static android.net.NetworkStats.ROAMING_YES;
-import static android.net.NetworkStats.SET_ALL;
-import static android.net.NetworkStats.SET_DEFAULT;
-import static android.net.NetworkStats.TAG_NONE;
-import static android.net.NetworkStats.UID_ALL;
-import static android.net.TrafficStats.UID_REMOVED;
-import static android.text.format.DateUtils.WEEK_IN_MILLIS;
-
-import static com.android.internal.net.NetworkUtilsInternal.multiplySafeByRational;
-import static com.android.server.net.NetworkStatsService.TAG;
-
-import android.net.NetworkIdentity;
-import android.net.NetworkStats;
-import android.net.NetworkStatsHistory;
-import android.net.NetworkTemplate;
-import android.net.TrafficStats;
-import android.os.Binder;
-import android.service.NetworkStatsCollectionKeyProto;
-import android.service.NetworkStatsCollectionProto;
-import android.service.NetworkStatsCollectionStatsProto;
-import android.telephony.SubscriptionPlan;
-import android.text.format.DateUtils;
-import android.util.ArrayMap;
-import android.util.AtomicFile;
-import android.util.IntArray;
-import android.util.MathUtils;
-import android.util.Range;
-import android.util.Slog;
-import android.util.proto.ProtoOutputStream;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.FastDataInput;
-import com.android.internal.util.FastDataOutput;
-import com.android.internal.util.FileRotator;
-import com.android.internal.util.IndentingPrintWriter;
-
-import libcore.io.IoUtils;
-
-import com.google.android.collect.Lists;
-import com.google.android.collect.Maps;
-
-import java.io.BufferedInputStream;
-import java.io.DataInput;
-import java.io.DataInputStream;
-import java.io.DataOutput;
-import java.io.DataOutputStream;
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.PrintWriter;
-import java.net.ProtocolException;
-import java.time.ZonedDateTime;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Objects;
-
-/**
- * Collection of {@link NetworkStatsHistory}, stored based on combined key of
- * {@link NetworkIdentitySet}, UID, set, and tag. Knows how to persist itself.
- */
-public class NetworkStatsCollection implements FileRotator.Reader, FileRotator.Writer {
- /** File header magic number: "ANET" */
- private static final int FILE_MAGIC = 0x414E4554;
-
- /** Default buffer size from BufferedInputStream */
- private static final int BUFFER_SIZE = 8192;
-
- private static final int VERSION_NETWORK_INIT = 1;
-
- private static final int VERSION_UID_INIT = 1;
- private static final int VERSION_UID_WITH_IDENT = 2;
- private static final int VERSION_UID_WITH_TAG = 3;
- private static final int VERSION_UID_WITH_SET = 4;
-
- private static final int VERSION_UNIFIED_INIT = 16;
-
- private ArrayMap<Key, NetworkStatsHistory> mStats = new ArrayMap<>();
-
- private final long mBucketDuration;
-
- private long mStartMillis;
- private long mEndMillis;
- private long mTotalBytes;
- private boolean mDirty;
-
- public NetworkStatsCollection(long bucketDuration) {
- mBucketDuration = bucketDuration;
- reset();
- }
-
- public void clear() {
- reset();
- }
-
- public void reset() {
- mStats.clear();
- mStartMillis = Long.MAX_VALUE;
- mEndMillis = Long.MIN_VALUE;
- mTotalBytes = 0;
- mDirty = false;
- }
-
- public long getStartMillis() {
- return mStartMillis;
- }
-
- /**
- * Return first atomic bucket in this collection, which is more conservative
- * than {@link #mStartMillis}.
- */
- public long getFirstAtomicBucketMillis() {
- if (mStartMillis == Long.MAX_VALUE) {
- return Long.MAX_VALUE;
- } else {
- return mStartMillis + mBucketDuration;
- }
- }
-
- public long getEndMillis() {
- return mEndMillis;
- }
-
- public long getTotalBytes() {
- return mTotalBytes;
- }
-
- public boolean isDirty() {
- return mDirty;
- }
-
- public void clearDirty() {
- mDirty = false;
- }
-
- public boolean isEmpty() {
- return mStartMillis == Long.MAX_VALUE && mEndMillis == Long.MIN_VALUE;
- }
-
- @VisibleForTesting
- public long roundUp(long time) {
- if (time == Long.MIN_VALUE || time == Long.MAX_VALUE
- || time == SubscriptionPlan.TIME_UNKNOWN) {
- return time;
- } else {
- final long mod = time % mBucketDuration;
- if (mod > 0) {
- time -= mod;
- time += mBucketDuration;
- }
- return time;
- }
- }
-
- @VisibleForTesting
- public long roundDown(long time) {
- if (time == Long.MIN_VALUE || time == Long.MAX_VALUE
- || time == SubscriptionPlan.TIME_UNKNOWN) {
- return time;
- } else {
- final long mod = time % mBucketDuration;
- if (mod > 0) {
- time -= mod;
- }
- return time;
- }
- }
-
- public int[] getRelevantUids(@NetworkStatsAccess.Level int accessLevel) {
- return getRelevantUids(accessLevel, Binder.getCallingUid());
- }
-
- public int[] getRelevantUids(@NetworkStatsAccess.Level int accessLevel,
- final int callerUid) {
- IntArray uids = new IntArray();
- for (int i = 0; i < mStats.size(); i++) {
- final Key key = mStats.keyAt(i);
- if (NetworkStatsAccess.isAccessibleToUser(key.uid, callerUid, accessLevel)) {
- int j = uids.binarySearch(key.uid);
-
- if (j < 0) {
- j = ~j;
- uids.add(j, key.uid);
- }
- }
- }
- return uids.toArray();
- }
-
- /**
- * Combine all {@link NetworkStatsHistory} in this collection which match
- * the requested parameters.
- */
- public NetworkStatsHistory getHistory(NetworkTemplate template, SubscriptionPlan augmentPlan,
- int uid, int set, int tag, int fields, long start, long end,
- @NetworkStatsAccess.Level int accessLevel, int callerUid) {
- if (!NetworkStatsAccess.isAccessibleToUser(uid, callerUid, accessLevel)) {
- throw new SecurityException("Network stats history of uid " + uid
- + " is forbidden for caller " + callerUid);
- }
-
- // 180 days of history should be enough for anyone; if we end up needing
- // more, we'll dynamically grow the history object.
- final int bucketEstimate = (int) MathUtils.constrain(((end - start) / mBucketDuration), 0,
- (180 * DateUtils.DAY_IN_MILLIS) / mBucketDuration);
- final NetworkStatsHistory combined = new NetworkStatsHistory(
- mBucketDuration, bucketEstimate, fields);
-
- // shortcut when we know stats will be empty
- if (start == end) return combined;
-
- // Figure out the window of time that we should be augmenting (if any)
- long augmentStart = SubscriptionPlan.TIME_UNKNOWN;
- long augmentEnd = (augmentPlan != null) ? augmentPlan.getDataUsageTime()
- : SubscriptionPlan.TIME_UNKNOWN;
- // And if augmenting, we might need to collect more data to adjust with
- long collectStart = start;
- long collectEnd = end;
-
- if (augmentEnd != SubscriptionPlan.TIME_UNKNOWN) {
- final Iterator<Range<ZonedDateTime>> it = augmentPlan.cycleIterator();
- while (it.hasNext()) {
- final Range<ZonedDateTime> cycle = it.next();
- final long cycleStart = cycle.getLower().toInstant().toEpochMilli();
- final long cycleEnd = cycle.getUpper().toInstant().toEpochMilli();
- if (cycleStart <= augmentEnd && augmentEnd < cycleEnd) {
- augmentStart = cycleStart;
- collectStart = Long.min(collectStart, augmentStart);
- collectEnd = Long.max(collectEnd, augmentEnd);
- break;
- }
- }
- }
-
- if (augmentStart != SubscriptionPlan.TIME_UNKNOWN) {
- // Shrink augmentation window so we don't risk undercounting.
- augmentStart = roundUp(augmentStart);
- augmentEnd = roundDown(augmentEnd);
- // Grow collection window so we get all the stats needed.
- collectStart = roundDown(collectStart);
- collectEnd = roundUp(collectEnd);
- }
-
- for (int i = 0; i < mStats.size(); i++) {
- final Key key = mStats.keyAt(i);
- if (key.uid == uid && NetworkStats.setMatches(set, key.set) && key.tag == tag
- && templateMatches(template, key.ident)) {
- final NetworkStatsHistory value = mStats.valueAt(i);
- combined.recordHistory(value, collectStart, collectEnd);
- }
- }
-
- if (augmentStart != SubscriptionPlan.TIME_UNKNOWN) {
- final NetworkStatsHistory.Entry entry = combined.getValues(
- augmentStart, augmentEnd, null);
-
- // If we don't have any recorded data for this time period, give
- // ourselves something to scale with.
- if (entry.rxBytes == 0 || entry.txBytes == 0) {
- combined.recordData(augmentStart, augmentEnd,
- new NetworkStats.Entry(1, 0, 1, 0, 0));
- combined.getValues(augmentStart, augmentEnd, entry);
- }
-
- final long rawBytes = (entry.rxBytes + entry.txBytes) == 0 ? 1 :
- (entry.rxBytes + entry.txBytes);
- final long rawRxBytes = entry.rxBytes == 0 ? 1 : entry.rxBytes;
- final long rawTxBytes = entry.txBytes == 0 ? 1 : entry.txBytes;
- final long targetBytes = augmentPlan.getDataUsageBytes();
-
- final long targetRxBytes = multiplySafeByRational(targetBytes, rawRxBytes, rawBytes);
- final long targetTxBytes = multiplySafeByRational(targetBytes, rawTxBytes, rawBytes);
-
-
- // Scale all matching buckets to reach anchor target
- final long beforeTotal = combined.getTotalBytes();
- for (int i = 0; i < combined.size(); i++) {
- combined.getValues(i, entry);
- if (entry.bucketStart >= augmentStart
- && entry.bucketStart + entry.bucketDuration <= augmentEnd) {
- entry.rxBytes = multiplySafeByRational(
- targetRxBytes, entry.rxBytes, rawRxBytes);
- entry.txBytes = multiplySafeByRational(
- targetTxBytes, entry.txBytes, rawTxBytes);
- // We purposefully clear out packet counters to indicate
- // that this data has been augmented.
- entry.rxPackets = 0;
- entry.txPackets = 0;
- combined.setValues(i, entry);
- }
- }
-
- final long deltaTotal = combined.getTotalBytes() - beforeTotal;
- if (deltaTotal != 0) {
- Slog.d(TAG, "Augmented network usage by " + deltaTotal + " bytes");
- }
-
- // Finally we can slice data as originally requested
- final NetworkStatsHistory sliced = new NetworkStatsHistory(
- mBucketDuration, bucketEstimate, fields);
- sliced.recordHistory(combined, start, end);
- return sliced;
- } else {
- return combined;
- }
- }
-
- /**
- * Summarize all {@link NetworkStatsHistory} in this collection which match
- * the requested parameters.
- */
- public NetworkStats getSummary(NetworkTemplate template, long start, long end,
- @NetworkStatsAccess.Level int accessLevel, int callerUid) {
- final long now = System.currentTimeMillis();
-
- final NetworkStats stats = new NetworkStats(end - start, 24);
-
- // shortcut when we know stats will be empty
- if (start == end) return stats;
-
- final NetworkStats.Entry entry = new NetworkStats.Entry();
- NetworkStatsHistory.Entry historyEntry = null;
-
- for (int i = 0; i < mStats.size(); i++) {
- final Key key = mStats.keyAt(i);
- if (templateMatches(template, key.ident)
- && NetworkStatsAccess.isAccessibleToUser(key.uid, callerUid, accessLevel)
- && key.set < NetworkStats.SET_DEBUG_START) {
- final NetworkStatsHistory value = mStats.valueAt(i);
- historyEntry = value.getValues(start, end, now, historyEntry);
-
- entry.iface = IFACE_ALL;
- entry.uid = key.uid;
- entry.set = key.set;
- entry.tag = key.tag;
- entry.defaultNetwork = key.ident.areAllMembersOnDefaultNetwork() ?
- DEFAULT_NETWORK_YES : DEFAULT_NETWORK_NO;
- entry.metered = key.ident.isAnyMemberMetered() ? METERED_YES : METERED_NO;
- entry.roaming = key.ident.isAnyMemberRoaming() ? ROAMING_YES : ROAMING_NO;
- entry.rxBytes = historyEntry.rxBytes;
- entry.rxPackets = historyEntry.rxPackets;
- entry.txBytes = historyEntry.txBytes;
- entry.txPackets = historyEntry.txPackets;
- entry.operations = historyEntry.operations;
-
- if (!entry.isEmpty()) {
- stats.combineValues(entry);
- }
- }
- }
-
- return stats;
- }
-
- /**
- * Record given {@link android.net.NetworkStats.Entry} into this collection.
- */
- public void recordData(NetworkIdentitySet ident, int uid, int set, int tag, long start,
- long end, NetworkStats.Entry entry) {
- final NetworkStatsHistory history = findOrCreateHistory(ident, uid, set, tag);
- history.recordData(start, end, entry);
- noteRecordedHistory(history.getStart(), history.getEnd(), entry.rxBytes + entry.txBytes);
- }
-
- /**
- * Record given {@link NetworkStatsHistory} into this collection.
- */
- private void recordHistory(Key key, NetworkStatsHistory history) {
- if (history.size() == 0) return;
- noteRecordedHistory(history.getStart(), history.getEnd(), history.getTotalBytes());
-
- NetworkStatsHistory target = mStats.get(key);
- if (target == null) {
- target = new NetworkStatsHistory(history.getBucketDuration());
- mStats.put(key, target);
- }
- target.recordEntireHistory(history);
- }
-
- /**
- * Record all {@link NetworkStatsHistory} contained in the given collection
- * into this collection.
- */
- public void recordCollection(NetworkStatsCollection another) {
- for (int i = 0; i < another.mStats.size(); i++) {
- final Key key = another.mStats.keyAt(i);
- final NetworkStatsHistory value = another.mStats.valueAt(i);
- recordHistory(key, value);
- }
- }
-
- private NetworkStatsHistory findOrCreateHistory(
- NetworkIdentitySet ident, int uid, int set, int tag) {
- final Key key = new Key(ident, uid, set, tag);
- final NetworkStatsHistory existing = mStats.get(key);
-
- // update when no existing, or when bucket duration changed
- NetworkStatsHistory updated = null;
- if (existing == null) {
- updated = new NetworkStatsHistory(mBucketDuration, 10);
- } else if (existing.getBucketDuration() != mBucketDuration) {
- updated = new NetworkStatsHistory(existing, mBucketDuration);
- }
-
- if (updated != null) {
- mStats.put(key, updated);
- return updated;
- } else {
- return existing;
- }
- }
-
- @Override
- public void read(InputStream in) throws IOException {
- final FastDataInput dataIn = new FastDataInput(in, BUFFER_SIZE);
- read(dataIn);
- }
-
- private void read(DataInput in) throws IOException {
- // verify file magic header intact
- final int magic = in.readInt();
- if (magic != FILE_MAGIC) {
- throw new ProtocolException("unexpected magic: " + magic);
- }
-
- final int version = in.readInt();
- switch (version) {
- case VERSION_UNIFIED_INIT: {
- // uid := size *(NetworkIdentitySet size *(uid set tag NetworkStatsHistory))
- final int identSize = in.readInt();
- for (int i = 0; i < identSize; i++) {
- final NetworkIdentitySet ident = new NetworkIdentitySet(in);
-
- final int size = in.readInt();
- for (int j = 0; j < size; j++) {
- final int uid = in.readInt();
- final int set = in.readInt();
- final int tag = in.readInt();
-
- final Key key = new Key(ident, uid, set, tag);
- final NetworkStatsHistory history = new NetworkStatsHistory(in);
- recordHistory(key, history);
- }
- }
- break;
- }
- default: {
- throw new ProtocolException("unexpected version: " + version);
- }
- }
- }
-
- @Override
- public void write(OutputStream out) throws IOException {
- final FastDataOutput dataOut = new FastDataOutput(out, BUFFER_SIZE);
- write(dataOut);
- dataOut.flush();
- }
-
- private void write(DataOutput out) throws IOException {
- // cluster key lists grouped by ident
- final HashMap<NetworkIdentitySet, ArrayList<Key>> keysByIdent = Maps.newHashMap();
- for (Key key : mStats.keySet()) {
- ArrayList<Key> keys = keysByIdent.get(key.ident);
- if (keys == null) {
- keys = Lists.newArrayList();
- keysByIdent.put(key.ident, keys);
- }
- keys.add(key);
- }
-
- out.writeInt(FILE_MAGIC);
- out.writeInt(VERSION_UNIFIED_INIT);
-
- out.writeInt(keysByIdent.size());
- for (NetworkIdentitySet ident : keysByIdent.keySet()) {
- final ArrayList<Key> keys = keysByIdent.get(ident);
- ident.writeToStream(out);
-
- out.writeInt(keys.size());
- for (Key key : keys) {
- final NetworkStatsHistory history = mStats.get(key);
- out.writeInt(key.uid);
- out.writeInt(key.set);
- out.writeInt(key.tag);
- history.writeToStream(out);
- }
- }
- }
-
- @Deprecated
- public void readLegacyNetwork(File file) throws IOException {
- final AtomicFile inputFile = new AtomicFile(file);
-
- DataInputStream in = null;
- try {
- in = new DataInputStream(new BufferedInputStream(inputFile.openRead()));
-
- // verify file magic header intact
- final int magic = in.readInt();
- if (magic != FILE_MAGIC) {
- throw new ProtocolException("unexpected magic: " + magic);
- }
-
- final int version = in.readInt();
- switch (version) {
- case VERSION_NETWORK_INIT: {
- // network := size *(NetworkIdentitySet NetworkStatsHistory)
- final int size = in.readInt();
- for (int i = 0; i < size; i++) {
- final NetworkIdentitySet ident = new NetworkIdentitySet(in);
- final NetworkStatsHistory history = new NetworkStatsHistory(in);
-
- final Key key = new Key(ident, UID_ALL, SET_ALL, TAG_NONE);
- recordHistory(key, history);
- }
- break;
- }
- default: {
- throw new ProtocolException("unexpected version: " + version);
- }
- }
- } catch (FileNotFoundException e) {
- // missing stats is okay, probably first boot
- } finally {
- IoUtils.closeQuietly(in);
- }
- }
-
- @Deprecated
- public void readLegacyUid(File file, boolean onlyTags) throws IOException {
- final AtomicFile inputFile = new AtomicFile(file);
-
- DataInputStream in = null;
- try {
- in = new DataInputStream(new BufferedInputStream(inputFile.openRead()));
-
- // verify file magic header intact
- final int magic = in.readInt();
- if (magic != FILE_MAGIC) {
- throw new ProtocolException("unexpected magic: " + magic);
- }
-
- final int version = in.readInt();
- switch (version) {
- case VERSION_UID_INIT: {
- // uid := size *(UID NetworkStatsHistory)
-
- // drop this data version, since we don't have a good
- // mapping into NetworkIdentitySet.
- break;
- }
- case VERSION_UID_WITH_IDENT: {
- // uid := size *(NetworkIdentitySet size *(UID NetworkStatsHistory))
-
- // drop this data version, since this version only existed
- // for a short time.
- break;
- }
- case VERSION_UID_WITH_TAG:
- case VERSION_UID_WITH_SET: {
- // uid := size *(NetworkIdentitySet size *(uid set tag NetworkStatsHistory))
- final int identSize = in.readInt();
- for (int i = 0; i < identSize; i++) {
- final NetworkIdentitySet ident = new NetworkIdentitySet(in);
-
- final int size = in.readInt();
- for (int j = 0; j < size; j++) {
- final int uid = in.readInt();
- final int set = (version >= VERSION_UID_WITH_SET) ? in.readInt()
- : SET_DEFAULT;
- final int tag = in.readInt();
-
- final Key key = new Key(ident, uid, set, tag);
- final NetworkStatsHistory history = new NetworkStatsHistory(in);
-
- if ((tag == TAG_NONE) != onlyTags) {
- recordHistory(key, history);
- }
- }
- }
- break;
- }
- default: {
- throw new ProtocolException("unexpected version: " + version);
- }
- }
- } catch (FileNotFoundException e) {
- // missing stats is okay, probably first boot
- } finally {
- IoUtils.closeQuietly(in);
- }
- }
-
- /**
- * Remove any {@link NetworkStatsHistory} attributed to the requested UID,
- * moving any {@link NetworkStats#TAG_NONE} series to
- * {@link TrafficStats#UID_REMOVED}.
- */
- public void removeUids(int[] uids) {
- final ArrayList<Key> knownKeys = Lists.newArrayList();
- knownKeys.addAll(mStats.keySet());
-
- // migrate all UID stats into special "removed" bucket
- for (Key key : knownKeys) {
- if (ArrayUtils.contains(uids, key.uid)) {
- // only migrate combined TAG_NONE history
- if (key.tag == TAG_NONE) {
- final NetworkStatsHistory uidHistory = mStats.get(key);
- final NetworkStatsHistory removedHistory = findOrCreateHistory(
- key.ident, UID_REMOVED, SET_DEFAULT, TAG_NONE);
- removedHistory.recordEntireHistory(uidHistory);
- }
- mStats.remove(key);
- mDirty = true;
- }
- }
- }
-
- private void noteRecordedHistory(long startMillis, long endMillis, long totalBytes) {
- if (startMillis < mStartMillis) mStartMillis = startMillis;
- if (endMillis > mEndMillis) mEndMillis = endMillis;
- mTotalBytes += totalBytes;
- mDirty = true;
- }
-
- private int estimateBuckets() {
- return (int) (Math.min(mEndMillis - mStartMillis, WEEK_IN_MILLIS * 5)
- / mBucketDuration);
- }
-
- private ArrayList<Key> getSortedKeys() {
- final ArrayList<Key> keys = Lists.newArrayList();
- keys.addAll(mStats.keySet());
- Collections.sort(keys);
- return keys;
- }
-
- public void dump(IndentingPrintWriter pw) {
- for (Key key : getSortedKeys()) {
- pw.print("ident="); pw.print(key.ident.toString());
- pw.print(" uid="); pw.print(key.uid);
- pw.print(" set="); pw.print(NetworkStats.setToString(key.set));
- pw.print(" tag="); pw.println(NetworkStats.tagToString(key.tag));
-
- final NetworkStatsHistory history = mStats.get(key);
- pw.increaseIndent();
- history.dump(pw, true);
- pw.decreaseIndent();
- }
- }
-
- public void dumpDebug(ProtoOutputStream proto, long tag) {
- final long start = proto.start(tag);
-
- for (Key key : getSortedKeys()) {
- final long startStats = proto.start(NetworkStatsCollectionProto.STATS);
-
- // Key
- final long startKey = proto.start(NetworkStatsCollectionStatsProto.KEY);
- key.ident.dumpDebug(proto, NetworkStatsCollectionKeyProto.IDENTITY);
- proto.write(NetworkStatsCollectionKeyProto.UID, key.uid);
- proto.write(NetworkStatsCollectionKeyProto.SET, key.set);
- proto.write(NetworkStatsCollectionKeyProto.TAG, key.tag);
- proto.end(startKey);
-
- // Value
- final NetworkStatsHistory history = mStats.get(key);
- history.dumpDebug(proto, NetworkStatsCollectionStatsProto.HISTORY);
- proto.end(startStats);
- }
-
- proto.end(start);
- }
-
- public void dumpCheckin(PrintWriter pw, long start, long end) {
- dumpCheckin(pw, start, end, NetworkTemplate.buildTemplateMobileWildcard(), "cell");
- dumpCheckin(pw, start, end, NetworkTemplate.buildTemplateWifiWildcard(), "wifi");
- dumpCheckin(pw, start, end, NetworkTemplate.buildTemplateEthernet(), "eth");
- dumpCheckin(pw, start, end, NetworkTemplate.buildTemplateBluetooth(), "bt");
- }
-
- /**
- * Dump all contained stats that match requested parameters, but group
- * together all matching {@link NetworkTemplate} under a single prefix.
- */
- private void dumpCheckin(PrintWriter pw, long start, long end, NetworkTemplate groupTemplate,
- String groupPrefix) {
- final ArrayMap<Key, NetworkStatsHistory> grouped = new ArrayMap<>();
-
- // Walk through all history, grouping by matching network templates
- for (int i = 0; i < mStats.size(); i++) {
- final Key key = mStats.keyAt(i);
- final NetworkStatsHistory value = mStats.valueAt(i);
-
- if (!templateMatches(groupTemplate, key.ident)) continue;
- if (key.set >= NetworkStats.SET_DEBUG_START) continue;
-
- final Key groupKey = new Key(null, key.uid, key.set, key.tag);
- NetworkStatsHistory groupHistory = grouped.get(groupKey);
- if (groupHistory == null) {
- groupHistory = new NetworkStatsHistory(value.getBucketDuration());
- grouped.put(groupKey, groupHistory);
- }
- groupHistory.recordHistory(value, start, end);
- }
-
- for (int i = 0; i < grouped.size(); i++) {
- final Key key = grouped.keyAt(i);
- final NetworkStatsHistory value = grouped.valueAt(i);
-
- if (value.size() == 0) continue;
-
- pw.print("c,");
- pw.print(groupPrefix); pw.print(',');
- pw.print(key.uid); pw.print(',');
- pw.print(NetworkStats.setToCheckinString(key.set)); pw.print(',');
- pw.print(key.tag);
- pw.println();
-
- value.dumpCheckin(pw);
- }
- }
-
- /**
- * Test if given {@link NetworkTemplate} matches any {@link NetworkIdentity}
- * in the given {@link NetworkIdentitySet}.
- */
- private static boolean templateMatches(NetworkTemplate template, NetworkIdentitySet identSet) {
- for (NetworkIdentity ident : identSet) {
- if (template.matches(ident)) {
- return true;
- }
- }
- return false;
- }
-
- private static class Key implements Comparable<Key> {
- public final NetworkIdentitySet ident;
- public final int uid;
- public final int set;
- public final int tag;
-
- private final int hashCode;
-
- public Key(NetworkIdentitySet ident, int uid, int set, int tag) {
- this.ident = ident;
- this.uid = uid;
- this.set = set;
- this.tag = tag;
- hashCode = Objects.hash(ident, uid, set, tag);
- }
-
- @Override
- public int hashCode() {
- return hashCode;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (obj instanceof Key) {
- final Key key = (Key) obj;
- return uid == key.uid && set == key.set && tag == key.tag
- && Objects.equals(ident, key.ident);
- }
- return false;
- }
-
- @Override
- public int compareTo(Key another) {
- int res = 0;
- if (ident != null && another.ident != null) {
- res = ident.compareTo(another.ident);
- }
- if (res == 0) {
- res = Integer.compare(uid, another.uid);
- }
- if (res == 0) {
- res = Integer.compare(set, another.set);
- }
- if (res == 0) {
- res = Integer.compare(tag, another.tag);
- }
- return res;
- }
- }
-}
diff --git a/services/core/java/com/android/server/net/NetworkStatsFactory.java b/services/core/java/com/android/server/net/NetworkStatsFactory.java
deleted file mode 100644
index 431b00914f02..000000000000
--- a/services/core/java/com/android/server/net/NetworkStatsFactory.java
+++ /dev/null
@@ -1,489 +0,0 @@
-/*
- * Copyright (C) 2011 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.net;
-
-import static android.net.NetworkStats.INTERFACES_ALL;
-import static android.net.NetworkStats.SET_ALL;
-import static android.net.NetworkStats.TAG_ALL;
-import static android.net.NetworkStats.TAG_NONE;
-import static android.net.NetworkStats.UID_ALL;
-
-import static com.android.server.NetworkManagementSocketTagger.kernelToTag;
-
-import android.annotation.Nullable;
-import android.net.INetd;
-import android.net.NetworkStats;
-import android.net.UnderlyingNetworkInfo;
-import android.net.util.NetdService;
-import android.os.RemoteException;
-import android.os.StrictMode;
-import android.os.SystemClock;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.ProcFileReader;
-
-import libcore.io.IoUtils;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.net.ProtocolException;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-
-/**
- * Creates {@link NetworkStats} instances by parsing various {@code /proc/}
- * files as needed.
- *
- * @hide
- */
-public class NetworkStatsFactory {
- private static final String TAG = "NetworkStatsFactory";
-
- private static final boolean USE_NATIVE_PARSING = true;
- private static final boolean VALIDATE_NATIVE_STATS = false;
-
- /** Path to {@code /proc/net/xt_qtaguid/iface_stat_all}. */
- private final File mStatsXtIfaceAll;
- /** Path to {@code /proc/net/xt_qtaguid/iface_stat_fmt}. */
- private final File mStatsXtIfaceFmt;
- /** Path to {@code /proc/net/xt_qtaguid/stats}. */
- private final File mStatsXtUid;
-
- private final boolean mUseBpfStats;
-
- private INetd mNetdService;
-
- /**
- * Guards persistent data access in this class
- *
- * <p>In order to prevent deadlocks, critical sections protected by this lock SHALL NOT call out
- * to other code that will acquire other locks within the system server. See b/134244752.
- */
- private final Object mPersistentDataLock = new Object();
-
- /** Set containing info about active VPNs and their underlying networks. */
- private volatile UnderlyingNetworkInfo[] mUnderlyingNetworkInfos = new UnderlyingNetworkInfo[0];
-
- // A persistent snapshot of cumulative stats since device start
- @GuardedBy("mPersistentDataLock")
- private NetworkStats mPersistSnapshot;
-
- // The persistent snapshot of tun and 464xlat adjusted stats since device start
- @GuardedBy("mPersistentDataLock")
- private NetworkStats mTunAnd464xlatAdjustedStats;
-
- /**
- * (Stacked interface) -> (base interface) association for all connected ifaces since boot.
- *
- * Because counters must never roll backwards, once a given interface is stacked on top of an
- * underlying interface, the stacked interface can never be stacked on top of
- * another interface. */
- private final ConcurrentHashMap<String, String> mStackedIfaces
- = new ConcurrentHashMap<>();
-
- /** Informs the factory of a new stacked interface. */
- public void noteStackedIface(String stackedIface, String baseIface) {
- if (stackedIface != null && baseIface != null) {
- mStackedIfaces.put(stackedIface, baseIface);
- }
- }
-
- /**
- * Set active VPN information for data usage migration purposes
- *
- * <p>Traffic on TUN-based VPNs inherently all appear to be originated from the VPN providing
- * app's UID. This method is used to support migration of VPN data usage, ensuring data is
- * accurately billed to the real owner of the traffic.
- *
- * @param vpnArray The snapshot of the currently-running VPNs.
- */
- public void updateUnderlyingNetworkInfos(UnderlyingNetworkInfo[] vpnArray) {
- mUnderlyingNetworkInfos = vpnArray.clone();
- }
-
- /**
- * Get a set of interfaces containing specified ifaces and stacked interfaces.
- *
- * <p>The added stacked interfaces are ifaces stacked on top of the specified ones, or ifaces
- * on which the specified ones are stacked. Stacked interfaces are those noted with
- * {@link #noteStackedIface(String, String)}, but only interfaces noted before this method
- * is called are guaranteed to be included.
- */
- public String[] augmentWithStackedInterfaces(@Nullable String[] requiredIfaces) {
- if (requiredIfaces == NetworkStats.INTERFACES_ALL) {
- return null;
- }
-
- HashSet<String> relatedIfaces = new HashSet<>(Arrays.asList(requiredIfaces));
- // ConcurrentHashMap's EntrySet iterators are "guaranteed to traverse
- // elements as they existed upon construction exactly once, and may
- // (but are not guaranteed to) reflect any modifications subsequent to construction".
- // This is enough here.
- for (Map.Entry<String, String> entry : mStackedIfaces.entrySet()) {
- if (relatedIfaces.contains(entry.getKey())) {
- relatedIfaces.add(entry.getValue());
- } else if (relatedIfaces.contains(entry.getValue())) {
- relatedIfaces.add(entry.getKey());
- }
- }
-
- String[] outArray = new String[relatedIfaces.size()];
- return relatedIfaces.toArray(outArray);
- }
-
- /**
- * Applies 464xlat adjustments with ifaces noted with {@link #noteStackedIface(String, String)}.
- * @see NetworkStats#apply464xlatAdjustments(NetworkStats, NetworkStats, Map)
- */
- public void apply464xlatAdjustments(NetworkStats baseTraffic, NetworkStats stackedTraffic) {
- NetworkStats.apply464xlatAdjustments(baseTraffic, stackedTraffic, mStackedIfaces);
- }
-
- public NetworkStatsFactory() {
- this(new File("/proc/"), new File("/sys/fs/bpf/map_netd_app_uid_stats_map").exists());
- }
-
- @VisibleForTesting
- public NetworkStatsFactory(File procRoot, boolean useBpfStats) {
- mStatsXtIfaceAll = new File(procRoot, "net/xt_qtaguid/iface_stat_all");
- mStatsXtIfaceFmt = new File(procRoot, "net/xt_qtaguid/iface_stat_fmt");
- mStatsXtUid = new File(procRoot, "net/xt_qtaguid/stats");
- mUseBpfStats = useBpfStats;
- synchronized (mPersistentDataLock) {
- mPersistSnapshot = new NetworkStats(SystemClock.elapsedRealtime(), -1);
- mTunAnd464xlatAdjustedStats = new NetworkStats(SystemClock.elapsedRealtime(), -1);
- }
- }
-
- public NetworkStats readBpfNetworkStatsDev() throws IOException {
- final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 6);
- if (nativeReadNetworkStatsDev(stats) != 0) {
- throw new IOException("Failed to parse bpf iface stats");
- }
- return stats;
- }
-
- /**
- * Parse and return interface-level summary {@link NetworkStats} measured
- * using {@code /proc/net/dev} style hooks, which may include non IP layer
- * traffic. Values monotonically increase since device boot, and may include
- * details about inactive interfaces.
- *
- * @throws IllegalStateException when problem parsing stats.
- */
- public NetworkStats readNetworkStatsSummaryDev() throws IOException {
-
- // Return xt_bpf stats if switched to bpf module.
- if (mUseBpfStats)
- return readBpfNetworkStatsDev();
-
- final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskReads();
-
- final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 6);
- final NetworkStats.Entry entry = new NetworkStats.Entry();
-
- ProcFileReader reader = null;
- try {
- reader = new ProcFileReader(new FileInputStream(mStatsXtIfaceAll));
-
- while (reader.hasMoreData()) {
- entry.iface = reader.nextString();
- entry.uid = UID_ALL;
- entry.set = SET_ALL;
- entry.tag = TAG_NONE;
-
- final boolean active = reader.nextInt() != 0;
-
- // always include snapshot values
- entry.rxBytes = reader.nextLong();
- entry.rxPackets = reader.nextLong();
- entry.txBytes = reader.nextLong();
- entry.txPackets = reader.nextLong();
-
- // fold in active numbers, but only when active
- if (active) {
- entry.rxBytes += reader.nextLong();
- entry.rxPackets += reader.nextLong();
- entry.txBytes += reader.nextLong();
- entry.txPackets += reader.nextLong();
- }
-
- stats.insertEntry(entry);
- reader.finishLine();
- }
- } catch (NullPointerException|NumberFormatException e) {
- throw protocolExceptionWithCause("problem parsing stats", e);
- } finally {
- IoUtils.closeQuietly(reader);
- StrictMode.setThreadPolicy(savedPolicy);
- }
- return stats;
- }
-
- /**
- * Parse and return interface-level summary {@link NetworkStats}. Designed
- * to return only IP layer traffic. Values monotonically increase since
- * device boot, and may include details about inactive interfaces.
- *
- * @throws IllegalStateException when problem parsing stats.
- */
- public NetworkStats readNetworkStatsSummaryXt() throws IOException {
-
- // Return xt_bpf stats if qtaguid module is replaced.
- if (mUseBpfStats)
- return readBpfNetworkStatsDev();
-
- final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskReads();
-
- // return null when kernel doesn't support
- if (!mStatsXtIfaceFmt.exists()) return null;
-
- final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 6);
- final NetworkStats.Entry entry = new NetworkStats.Entry();
-
- ProcFileReader reader = null;
- try {
- // open and consume header line
- reader = new ProcFileReader(new FileInputStream(mStatsXtIfaceFmt));
- reader.finishLine();
-
- while (reader.hasMoreData()) {
- entry.iface = reader.nextString();
- entry.uid = UID_ALL;
- entry.set = SET_ALL;
- entry.tag = TAG_NONE;
-
- entry.rxBytes = reader.nextLong();
- entry.rxPackets = reader.nextLong();
- entry.txBytes = reader.nextLong();
- entry.txPackets = reader.nextLong();
-
- stats.insertEntry(entry);
- reader.finishLine();
- }
- } catch (NullPointerException|NumberFormatException e) {
- throw protocolExceptionWithCause("problem parsing stats", e);
- } finally {
- IoUtils.closeQuietly(reader);
- StrictMode.setThreadPolicy(savedPolicy);
- }
- return stats;
- }
-
- public NetworkStats readNetworkStatsDetail() throws IOException {
- return readNetworkStatsDetail(UID_ALL, INTERFACES_ALL, TAG_ALL);
- }
-
- @GuardedBy("mPersistentDataLock")
- private void requestSwapActiveStatsMapLocked() throws RemoteException {
- // Ask netd to do a active map stats swap. When the binder call successfully returns,
- // the system server should be able to safely read and clean the inactive map
- // without race problem.
- if (mNetdService == null) {
- mNetdService = NetdService.getInstance();
- }
- mNetdService.trafficSwapActiveStatsMap();
- }
-
- /**
- * Reads the detailed UID stats based on the provided parameters
- *
- * @param limitUid the UID to limit this query to
- * @param limitIfaces the interfaces to limit this query to. Use {@link
- * NetworkStats.INTERFACES_ALL} to select all interfaces
- * @param limitTag the tags to limit this query to
- * @return the NetworkStats instance containing network statistics at the present time.
- */
- public NetworkStats readNetworkStatsDetail(
- int limitUid, String[] limitIfaces, int limitTag) throws IOException {
- // In order to prevent deadlocks, anything protected by this lock MUST NOT call out to other
- // code that will acquire other locks within the system server. See b/134244752.
- synchronized (mPersistentDataLock) {
- // Take a reference. If this gets swapped out, we still have the old reference.
- final UnderlyingNetworkInfo[] vpnArray = mUnderlyingNetworkInfos;
- // Take a defensive copy. mPersistSnapshot is mutated in some cases below
- final NetworkStats prev = mPersistSnapshot.clone();
-
- if (USE_NATIVE_PARSING) {
- final NetworkStats stats =
- new NetworkStats(SystemClock.elapsedRealtime(), 0 /* initialSize */);
- if (mUseBpfStats) {
- try {
- requestSwapActiveStatsMapLocked();
- } catch (RemoteException e) {
- throw new IOException(e);
- }
- // Stats are always read from the inactive map, so they must be read after the
- // swap
- if (nativeReadNetworkStatsDetail(stats, mStatsXtUid.getAbsolutePath(), UID_ALL,
- INTERFACES_ALL, TAG_ALL, mUseBpfStats) != 0) {
- throw new IOException("Failed to parse network stats");
- }
-
- // BPF stats are incremental; fold into mPersistSnapshot.
- mPersistSnapshot.setElapsedRealtime(stats.getElapsedRealtime());
- mPersistSnapshot.combineAllValues(stats);
- } else {
- if (nativeReadNetworkStatsDetail(stats, mStatsXtUid.getAbsolutePath(), UID_ALL,
- INTERFACES_ALL, TAG_ALL, mUseBpfStats) != 0) {
- throw new IOException("Failed to parse network stats");
- }
- if (VALIDATE_NATIVE_STATS) {
- final NetworkStats javaStats = javaReadNetworkStatsDetail(mStatsXtUid,
- UID_ALL, INTERFACES_ALL, TAG_ALL);
- assertEquals(javaStats, stats);
- }
-
- mPersistSnapshot = stats;
- }
- } else {
- mPersistSnapshot = javaReadNetworkStatsDetail(mStatsXtUid, UID_ALL, INTERFACES_ALL,
- TAG_ALL);
- }
-
- NetworkStats adjustedStats = adjustForTunAnd464Xlat(mPersistSnapshot, prev, vpnArray);
-
- // Filter return values
- adjustedStats.filter(limitUid, limitIfaces, limitTag);
- return adjustedStats;
- }
- }
-
- @GuardedBy("mPersistentDataLock")
- private NetworkStats adjustForTunAnd464Xlat(NetworkStats uidDetailStats,
- NetworkStats previousStats, UnderlyingNetworkInfo[] vpnArray) {
- // Calculate delta from last snapshot
- final NetworkStats delta = uidDetailStats.subtract(previousStats);
-
- // Apply 464xlat adjustments before VPN adjustments. If VPNs are using v4 on a v6 only
- // network, the overhead is their fault.
- // No locking here: apply464xlatAdjustments behaves fine with an add-only
- // ConcurrentHashMap.
- delta.apply464xlatAdjustments(mStackedIfaces);
-
- // Migrate data usage over a VPN to the TUN network.
- for (UnderlyingNetworkInfo info : vpnArray) {
- delta.migrateTun(info.getOwnerUid(), info.getInterface(),
- info.getUnderlyingInterfaces());
- // Filter out debug entries as that may lead to over counting.
- delta.filterDebugEntries();
- }
-
- // Update mTunAnd464xlatAdjustedStats with migrated delta.
- mTunAnd464xlatAdjustedStats.combineAllValues(delta);
- mTunAnd464xlatAdjustedStats.setElapsedRealtime(uidDetailStats.getElapsedRealtime());
-
- return mTunAnd464xlatAdjustedStats.clone();
- }
-
- /**
- * Parse and return {@link NetworkStats} with UID-level details. Values are
- * expected to monotonically increase since device boot.
- */
- @VisibleForTesting
- public static NetworkStats javaReadNetworkStatsDetail(File detailPath, int limitUid,
- String[] limitIfaces, int limitTag)
- throws IOException {
- final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskReads();
-
- final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 24);
- final NetworkStats.Entry entry = new NetworkStats.Entry();
-
- int idx = 1;
- int lastIdx = 1;
-
- ProcFileReader reader = null;
- try {
- // open and consume header line
- reader = new ProcFileReader(new FileInputStream(detailPath));
- reader.finishLine();
-
- while (reader.hasMoreData()) {
- idx = reader.nextInt();
- if (idx != lastIdx + 1) {
- throw new ProtocolException(
- "inconsistent idx=" + idx + " after lastIdx=" + lastIdx);
- }
- lastIdx = idx;
-
- entry.iface = reader.nextString();
- entry.tag = kernelToTag(reader.nextString());
- entry.uid = reader.nextInt();
- entry.set = reader.nextInt();
- entry.rxBytes = reader.nextLong();
- entry.rxPackets = reader.nextLong();
- entry.txBytes = reader.nextLong();
- entry.txPackets = reader.nextLong();
-
- if ((limitIfaces == null || ArrayUtils.contains(limitIfaces, entry.iface))
- && (limitUid == UID_ALL || limitUid == entry.uid)
- && (limitTag == TAG_ALL || limitTag == entry.tag)) {
- stats.insertEntry(entry);
- }
-
- reader.finishLine();
- }
- } catch (NullPointerException|NumberFormatException e) {
- throw protocolExceptionWithCause("problem parsing idx " + idx, e);
- } finally {
- IoUtils.closeQuietly(reader);
- StrictMode.setThreadPolicy(savedPolicy);
- }
-
- return stats;
- }
-
- public void assertEquals(NetworkStats expected, NetworkStats actual) {
- if (expected.size() != actual.size()) {
- throw new AssertionError(
- "Expected size " + expected.size() + ", actual size " + actual.size());
- }
-
- NetworkStats.Entry expectedRow = null;
- NetworkStats.Entry actualRow = null;
- for (int i = 0; i < expected.size(); i++) {
- expectedRow = expected.getValues(i, expectedRow);
- actualRow = actual.getValues(i, actualRow);
- if (!expectedRow.equals(actualRow)) {
- throw new AssertionError(
- "Expected row " + i + ": " + expectedRow + ", actual row " + actualRow);
- }
- }
- }
-
- /**
- * Parse statistics from file into given {@link NetworkStats} object. Values
- * are expected to monotonically increase since device boot.
- */
- @VisibleForTesting
- public static native int nativeReadNetworkStatsDetail(NetworkStats stats, String path,
- int limitUid, String[] limitIfaces, int limitTag, boolean useBpfStats);
-
- @VisibleForTesting
- public static native int nativeReadNetworkStatsDev(NetworkStats stats);
-
- private static ProtocolException protocolExceptionWithCause(String message, Throwable cause) {
- ProtocolException pe = new ProtocolException(message);
- pe.initCause(cause);
- return pe;
- }
-}
diff --git a/services/core/java/com/android/server/net/NetworkStatsManagerInternal.java b/services/core/java/com/android/server/net/NetworkStatsManagerInternal.java
deleted file mode 100644
index 0e9a9da6804b..000000000000
--- a/services/core/java/com/android/server/net/NetworkStatsManagerInternal.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2018 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.net;
-
-import android.annotation.NonNull;
-import android.net.NetworkStats;
-import android.net.NetworkTemplate;
-
-public abstract class NetworkStatsManagerInternal {
- /** Return network layer usage total for traffic that matches template. */
- public abstract long getNetworkTotalBytes(NetworkTemplate template, long start, long end);
-
- /** Return network layer usage per-UID for traffic that matches template. */
- public abstract NetworkStats getNetworkUidBytes(NetworkTemplate template, long start, long end);
-
- /** Mark given UID as being in foreground for stats purposes. */
- public abstract void setUidForeground(int uid, boolean uidForeground);
-
- /** Advise persistance threshold; may be overridden internally. */
- public abstract void advisePersistThreshold(long thresholdBytes);
-
- /** Force update of statistics. */
- public abstract void forceUpdate();
-
- /**
- * Set the warning and limit to all registered custom network stats providers.
- * Note that invocation of any interface will be sent to all providers.
- */
- public abstract void setStatsProviderWarningAndLimitAsync(@NonNull String iface, long warning,
- long limit);
-}
diff --git a/services/core/java/com/android/server/net/NetworkStatsObservers.java b/services/core/java/com/android/server/net/NetworkStatsObservers.java
deleted file mode 100644
index 2564daeaa1c0..000000000000
--- a/services/core/java/com/android/server/net/NetworkStatsObservers.java
+++ /dev/null
@@ -1,442 +0,0 @@
-/*
- * Copyright (C) 2016 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.net;
-
-import static android.app.usage.NetworkStatsManager.MIN_THRESHOLD_BYTES;
-
-import static com.android.internal.util.Preconditions.checkArgument;
-
-import android.app.usage.NetworkStatsManager;
-import android.net.DataUsageRequest;
-import android.net.NetworkStats;
-import android.net.NetworkStatsHistory;
-import android.net.NetworkTemplate;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
-import android.os.Messenger;
-import android.os.Process;
-import android.os.RemoteException;
-import android.util.ArrayMap;
-import android.util.Slog;
-import android.util.SparseArray;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.util.concurrent.atomic.AtomicInteger;
-
-/**
- * Manages observers of {@link NetworkStats}. Allows observers to be notified when
- * data usage has been reported in {@link NetworkStatsService}. An observer can set
- * a threshold of how much data it cares about to be notified.
- */
-class NetworkStatsObservers {
- private static final String TAG = "NetworkStatsObservers";
- private static final boolean LOGV = false;
-
- private static final int MSG_REGISTER = 1;
- private static final int MSG_UNREGISTER = 2;
- private static final int MSG_UPDATE_STATS = 3;
-
- // All access to this map must be done from the handler thread.
- // indexed by DataUsageRequest#requestId
- private final SparseArray<RequestInfo> mDataUsageRequests = new SparseArray<>();
-
- // Sequence number of DataUsageRequests
- private final AtomicInteger mNextDataUsageRequestId = new AtomicInteger();
-
- // Lazily instantiated when an observer is registered.
- private volatile Handler mHandler;
-
- /**
- * Creates a wrapper that contains the caller context and a normalized request.
- * The request should be returned to the caller app, and the wrapper should be sent to this
- * object through #addObserver by the service handler.
- *
- * <p>It will register the observer asynchronously, so it is safe to call from any thread.
- *
- * @return the normalized request wrapped within {@link RequestInfo}.
- */
- public DataUsageRequest register(DataUsageRequest inputRequest, Messenger messenger,
- IBinder binder, int callingUid, @NetworkStatsAccess.Level int accessLevel) {
- DataUsageRequest request = buildRequest(inputRequest);
- RequestInfo requestInfo = buildRequestInfo(request, messenger, binder, callingUid,
- accessLevel);
-
- if (LOGV) Slog.v(TAG, "Registering observer for " + request);
- getHandler().sendMessage(mHandler.obtainMessage(MSG_REGISTER, requestInfo));
- return request;
- }
-
- /**
- * Unregister a data usage observer.
- *
- * <p>It will unregister the observer asynchronously, so it is safe to call from any thread.
- */
- public void unregister(DataUsageRequest request, int callingUid) {
- getHandler().sendMessage(mHandler.obtainMessage(MSG_UNREGISTER, callingUid, 0 /* ignore */,
- request));
- }
-
- /**
- * Updates data usage statistics of registered observers and notifies if limits are reached.
- *
- * <p>It will update stats asynchronously, so it is safe to call from any thread.
- */
- public void updateStats(NetworkStats xtSnapshot, NetworkStats uidSnapshot,
- ArrayMap<String, NetworkIdentitySet> activeIfaces,
- ArrayMap<String, NetworkIdentitySet> activeUidIfaces,
- long currentTime) {
- StatsContext statsContext = new StatsContext(xtSnapshot, uidSnapshot, activeIfaces,
- activeUidIfaces, currentTime);
- getHandler().sendMessage(mHandler.obtainMessage(MSG_UPDATE_STATS, statsContext));
- }
-
- private Handler getHandler() {
- if (mHandler == null) {
- synchronized (this) {
- if (mHandler == null) {
- if (LOGV) Slog.v(TAG, "Creating handler");
- mHandler = new Handler(getHandlerLooperLocked(), mHandlerCallback);
- }
- }
- }
- return mHandler;
- }
-
- @VisibleForTesting
- protected Looper getHandlerLooperLocked() {
- HandlerThread handlerThread = new HandlerThread(TAG);
- handlerThread.start();
- return handlerThread.getLooper();
- }
-
- private Handler.Callback mHandlerCallback = new Handler.Callback() {
- @Override
- public boolean handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_REGISTER: {
- handleRegister((RequestInfo) msg.obj);
- return true;
- }
- case MSG_UNREGISTER: {
- handleUnregister((DataUsageRequest) msg.obj, msg.arg1 /* callingUid */);
- return true;
- }
- case MSG_UPDATE_STATS: {
- handleUpdateStats((StatsContext) msg.obj);
- return true;
- }
- default: {
- return false;
- }
- }
- }
- };
-
- /**
- * Adds a {@link RequestInfo} as an observer.
- * Should only be called from the handler thread otherwise there will be a race condition
- * on mDataUsageRequests.
- */
- private void handleRegister(RequestInfo requestInfo) {
- mDataUsageRequests.put(requestInfo.mRequest.requestId, requestInfo);
- }
-
- /**
- * Removes a {@link DataUsageRequest} if the calling uid is authorized.
- * Should only be called from the handler thread otherwise there will be a race condition
- * on mDataUsageRequests.
- */
- private void handleUnregister(DataUsageRequest request, int callingUid) {
- RequestInfo requestInfo;
- requestInfo = mDataUsageRequests.get(request.requestId);
- if (requestInfo == null) {
- if (LOGV) Slog.v(TAG, "Trying to unregister unknown request " + request);
- return;
- }
- if (Process.SYSTEM_UID != callingUid && requestInfo.mCallingUid != callingUid) {
- Slog.w(TAG, "Caller uid " + callingUid + " is not owner of " + request);
- return;
- }
-
- if (LOGV) Slog.v(TAG, "Unregistering " + request);
- mDataUsageRequests.remove(request.requestId);
- requestInfo.unlinkDeathRecipient();
- requestInfo.callCallback(NetworkStatsManager.CALLBACK_RELEASED);
- }
-
- private void handleUpdateStats(StatsContext statsContext) {
- if (mDataUsageRequests.size() == 0) {
- return;
- }
-
- for (int i = 0; i < mDataUsageRequests.size(); i++) {
- RequestInfo requestInfo = mDataUsageRequests.valueAt(i);
- requestInfo.updateStats(statsContext);
- }
- }
-
- private DataUsageRequest buildRequest(DataUsageRequest request) {
- // Cap the minimum threshold to a safe default to avoid too many callbacks
- long thresholdInBytes = Math.max(MIN_THRESHOLD_BYTES, request.thresholdInBytes);
- if (thresholdInBytes < request.thresholdInBytes) {
- Slog.w(TAG, "Threshold was too low for " + request
- + ". Overriding to a safer default of " + thresholdInBytes + " bytes");
- }
- return new DataUsageRequest(mNextDataUsageRequestId.incrementAndGet(),
- request.template, thresholdInBytes);
- }
-
- private RequestInfo buildRequestInfo(DataUsageRequest request,
- Messenger messenger, IBinder binder, int callingUid,
- @NetworkStatsAccess.Level int accessLevel) {
- if (accessLevel <= NetworkStatsAccess.Level.USER) {
- return new UserUsageRequestInfo(this, request, messenger, binder, callingUid,
- accessLevel);
- } else {
- // Safety check in case a new access level is added and we forgot to update this
- checkArgument(accessLevel >= NetworkStatsAccess.Level.DEVICESUMMARY);
- return new NetworkUsageRequestInfo(this, request, messenger, binder, callingUid,
- accessLevel);
- }
- }
-
- /**
- * Tracks information relevant to a data usage observer.
- * It will notice when the calling process dies so we can self-expire.
- */
- private abstract static class RequestInfo implements IBinder.DeathRecipient {
- private final NetworkStatsObservers mStatsObserver;
- protected final DataUsageRequest mRequest;
- private final Messenger mMessenger;
- private final IBinder mBinder;
- protected final int mCallingUid;
- protected final @NetworkStatsAccess.Level int mAccessLevel;
- protected NetworkStatsRecorder mRecorder;
- protected NetworkStatsCollection mCollection;
-
- RequestInfo(NetworkStatsObservers statsObserver, DataUsageRequest request,
- Messenger messenger, IBinder binder, int callingUid,
- @NetworkStatsAccess.Level int accessLevel) {
- mStatsObserver = statsObserver;
- mRequest = request;
- mMessenger = messenger;
- mBinder = binder;
- mCallingUid = callingUid;
- mAccessLevel = accessLevel;
-
- try {
- mBinder.linkToDeath(this, 0);
- } catch (RemoteException e) {
- binderDied();
- }
- }
-
- @Override
- public void binderDied() {
- if (LOGV) Slog.v(TAG, "RequestInfo binderDied("
- + mRequest + ", " + mBinder + ")");
- mStatsObserver.unregister(mRequest, Process.SYSTEM_UID);
- callCallback(NetworkStatsManager.CALLBACK_RELEASED);
- }
-
- @Override
- public String toString() {
- return "RequestInfo from uid:" + mCallingUid
- + " for " + mRequest + " accessLevel:" + mAccessLevel;
- }
-
- private void unlinkDeathRecipient() {
- if (mBinder != null) {
- mBinder.unlinkToDeath(this, 0);
- }
- }
-
- /**
- * Update stats given the samples and interface to identity mappings.
- */
- private void updateStats(StatsContext statsContext) {
- if (mRecorder == null) {
- // First run; establish baseline stats
- resetRecorder();
- recordSample(statsContext);
- return;
- }
- recordSample(statsContext);
-
- if (checkStats()) {
- resetRecorder();
- callCallback(NetworkStatsManager.CALLBACK_LIMIT_REACHED);
- }
- }
-
- private void callCallback(int callbackType) {
- Bundle bundle = new Bundle();
- bundle.putParcelable(DataUsageRequest.PARCELABLE_KEY, mRequest);
- Message msg = Message.obtain();
- msg.what = callbackType;
- msg.setData(bundle);
- try {
- if (LOGV) {
- Slog.v(TAG, "sending notification " + callbackTypeToName(callbackType)
- + " for " + mRequest);
- }
- mMessenger.send(msg);
- } catch (RemoteException e) {
- // May occur naturally in the race of binder death.
- Slog.w(TAG, "RemoteException caught trying to send a callback msg for " + mRequest);
- }
- }
-
- private void resetRecorder() {
- mRecorder = new NetworkStatsRecorder();
- mCollection = mRecorder.getSinceBoot();
- }
-
- protected abstract boolean checkStats();
-
- protected abstract void recordSample(StatsContext statsContext);
-
- private String callbackTypeToName(int callbackType) {
- switch (callbackType) {
- case NetworkStatsManager.CALLBACK_LIMIT_REACHED:
- return "LIMIT_REACHED";
- case NetworkStatsManager.CALLBACK_RELEASED:
- return "RELEASED";
- default:
- return "UNKNOWN";
- }
- }
- }
-
- private static class NetworkUsageRequestInfo extends RequestInfo {
- NetworkUsageRequestInfo(NetworkStatsObservers statsObserver, DataUsageRequest request,
- Messenger messenger, IBinder binder, int callingUid,
- @NetworkStatsAccess.Level int accessLevel) {
- super(statsObserver, request, messenger, binder, callingUid, accessLevel);
- }
-
- @Override
- protected boolean checkStats() {
- long bytesSoFar = getTotalBytesForNetwork(mRequest.template);
- if (LOGV) {
- Slog.v(TAG, bytesSoFar + " bytes so far since notification for "
- + mRequest.template);
- }
- if (bytesSoFar > mRequest.thresholdInBytes) {
- return true;
- }
- return false;
- }
-
- @Override
- protected void recordSample(StatsContext statsContext) {
- // Recorder does not need to be locked in this context since only the handler
- // thread will update it. We pass a null VPN array because usage is aggregated by uid
- // for this snapshot, so VPN traffic can't be reattributed to responsible apps.
- mRecorder.recordSnapshotLocked(statsContext.mXtSnapshot, statsContext.mActiveIfaces,
- statsContext.mCurrentTime);
- }
-
- /**
- * Reads stats matching the given template. {@link NetworkStatsCollection} will aggregate
- * over all buckets, which in this case should be only one since we built it big enough
- * that it will outlive the caller. If it doesn't, then there will be multiple buckets.
- */
- private long getTotalBytesForNetwork(NetworkTemplate template) {
- NetworkStats stats = mCollection.getSummary(template,
- Long.MIN_VALUE /* start */, Long.MAX_VALUE /* end */,
- mAccessLevel, mCallingUid);
- return stats.getTotalBytes();
- }
- }
-
- private static class UserUsageRequestInfo extends RequestInfo {
- UserUsageRequestInfo(NetworkStatsObservers statsObserver, DataUsageRequest request,
- Messenger messenger, IBinder binder, int callingUid,
- @NetworkStatsAccess.Level int accessLevel) {
- super(statsObserver, request, messenger, binder, callingUid, accessLevel);
- }
-
- @Override
- protected boolean checkStats() {
- int[] uidsToMonitor = mCollection.getRelevantUids(mAccessLevel, mCallingUid);
-
- for (int i = 0; i < uidsToMonitor.length; i++) {
- long bytesSoFar = getTotalBytesForNetworkUid(mRequest.template, uidsToMonitor[i]);
- if (bytesSoFar > mRequest.thresholdInBytes) {
- return true;
- }
- }
- return false;
- }
-
- @Override
- protected void recordSample(StatsContext statsContext) {
- // Recorder does not need to be locked in this context since only the handler
- // thread will update it. We pass the VPN info so VPN traffic is reattributed to
- // responsible apps.
- mRecorder.recordSnapshotLocked(statsContext.mUidSnapshot, statsContext.mActiveUidIfaces,
- statsContext.mCurrentTime);
- }
-
- /**
- * Reads all stats matching the given template and uid. Ther history will likely only
- * contain one bucket per ident since we build it big enough that it will outlive the
- * caller lifetime.
- */
- private long getTotalBytesForNetworkUid(NetworkTemplate template, int uid) {
- try {
- NetworkStatsHistory history = mCollection.getHistory(template, null, uid,
- NetworkStats.SET_ALL, NetworkStats.TAG_NONE,
- NetworkStatsHistory.FIELD_ALL,
- Long.MIN_VALUE /* start */, Long.MAX_VALUE /* end */,
- mAccessLevel, mCallingUid);
- return history.getTotalBytes();
- } catch (SecurityException e) {
- if (LOGV) {
- Slog.w(TAG, "CallerUid " + mCallingUid + " may have lost access to uid "
- + uid);
- }
- return 0;
- }
- }
- }
-
- private static class StatsContext {
- NetworkStats mXtSnapshot;
- NetworkStats mUidSnapshot;
- ArrayMap<String, NetworkIdentitySet> mActiveIfaces;
- ArrayMap<String, NetworkIdentitySet> mActiveUidIfaces;
- long mCurrentTime;
-
- StatsContext(NetworkStats xtSnapshot, NetworkStats uidSnapshot,
- ArrayMap<String, NetworkIdentitySet> activeIfaces,
- ArrayMap<String, NetworkIdentitySet> activeUidIfaces,
- long currentTime) {
- mXtSnapshot = xtSnapshot;
- mUidSnapshot = uidSnapshot;
- mActiveIfaces = activeIfaces;
- mActiveUidIfaces = activeUidIfaces;
- mCurrentTime = currentTime;
- }
- }
-}
diff --git a/services/core/java/com/android/server/net/NetworkStatsRecorder.java b/services/core/java/com/android/server/net/NetworkStatsRecorder.java
deleted file mode 100644
index 978ae87d39d5..000000000000
--- a/services/core/java/com/android/server/net/NetworkStatsRecorder.java
+++ /dev/null
@@ -1,505 +0,0 @@
-/*
- * Copyright (C) 2012 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.net;
-
-import static android.net.NetworkStats.TAG_NONE;
-import static android.net.TrafficStats.KB_IN_BYTES;
-import static android.net.TrafficStats.MB_IN_BYTES;
-import static android.text.format.DateUtils.YEAR_IN_MILLIS;
-
-import android.net.NetworkStats;
-import android.net.NetworkStats.NonMonotonicObserver;
-import android.net.NetworkStatsHistory;
-import android.net.NetworkTemplate;
-import android.net.TrafficStats;
-import android.os.Binder;
-import android.os.DropBoxManager;
-import android.service.NetworkStatsRecorderProto;
-import android.util.Log;
-import android.util.MathUtils;
-import android.util.Slog;
-import android.util.proto.ProtoOutputStream;
-
-import com.android.internal.util.FileRotator;
-import com.android.internal.util.IndentingPrintWriter;
-
-import com.google.android.collect.Sets;
-
-import libcore.io.IoUtils;
-
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.PrintWriter;
-import java.lang.ref.WeakReference;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Objects;
-
-/**
- * Logic to record deltas between periodic {@link NetworkStats} snapshots into
- * {@link NetworkStatsHistory} that belong to {@link NetworkStatsCollection}.
- * Keeps pending changes in memory until they pass a specific threshold, in
- * bytes. Uses {@link FileRotator} for persistence logic if present.
- * <p>
- * Not inherently thread safe.
- */
-public class NetworkStatsRecorder {
- private static final String TAG = "NetworkStatsRecorder";
- private static final boolean LOGD = false;
- private static final boolean LOGV = false;
-
- private static final String TAG_NETSTATS_DUMP = "netstats_dump";
-
- /** Dump before deleting in {@link #recoverFromWtf()}. */
- private static final boolean DUMP_BEFORE_DELETE = true;
-
- private final FileRotator mRotator;
- private final NonMonotonicObserver<String> mObserver;
- private final DropBoxManager mDropBox;
- private final String mCookie;
-
- private final long mBucketDuration;
- private final boolean mOnlyTags;
-
- private long mPersistThresholdBytes = 2 * MB_IN_BYTES;
- private NetworkStats mLastSnapshot;
-
- private final NetworkStatsCollection mPending;
- private final NetworkStatsCollection mSinceBoot;
-
- private final CombiningRewriter mPendingRewriter;
-
- private WeakReference<NetworkStatsCollection> mComplete;
-
- /**
- * Non-persisted recorder, with only one bucket. Used by {@link NetworkStatsObservers}.
- */
- public NetworkStatsRecorder() {
- mRotator = null;
- mObserver = null;
- mDropBox = null;
- mCookie = null;
-
- // set the bucket big enough to have all data in one bucket, but allow some
- // slack to avoid overflow
- mBucketDuration = YEAR_IN_MILLIS;
- mOnlyTags = false;
-
- mPending = null;
- mSinceBoot = new NetworkStatsCollection(mBucketDuration);
-
- mPendingRewriter = null;
- }
-
- /**
- * Persisted recorder.
- */
- public NetworkStatsRecorder(FileRotator rotator, NonMonotonicObserver<String> observer,
- DropBoxManager dropBox, String cookie, long bucketDuration, boolean onlyTags) {
- mRotator = Objects.requireNonNull(rotator, "missing FileRotator");
- mObserver = Objects.requireNonNull(observer, "missing NonMonotonicObserver");
- mDropBox = Objects.requireNonNull(dropBox, "missing DropBoxManager");
- mCookie = cookie;
-
- mBucketDuration = bucketDuration;
- mOnlyTags = onlyTags;
-
- mPending = new NetworkStatsCollection(bucketDuration);
- mSinceBoot = new NetworkStatsCollection(bucketDuration);
-
- mPendingRewriter = new CombiningRewriter(mPending);
- }
-
- public void setPersistThreshold(long thresholdBytes) {
- if (LOGV) Slog.v(TAG, "setPersistThreshold() with " + thresholdBytes);
- mPersistThresholdBytes = MathUtils.constrain(
- thresholdBytes, 1 * KB_IN_BYTES, 100 * MB_IN_BYTES);
- }
-
- public void resetLocked() {
- mLastSnapshot = null;
- if (mPending != null) {
- mPending.reset();
- }
- if (mSinceBoot != null) {
- mSinceBoot.reset();
- }
- if (mComplete != null) {
- mComplete.clear();
- }
- }
-
- public NetworkStats.Entry getTotalSinceBootLocked(NetworkTemplate template) {
- return mSinceBoot.getSummary(template, Long.MIN_VALUE, Long.MAX_VALUE,
- NetworkStatsAccess.Level.DEVICE, Binder.getCallingUid()).getTotal(null);
- }
-
- public NetworkStatsCollection getSinceBoot() {
- return mSinceBoot;
- }
-
- /**
- * Load complete history represented by {@link FileRotator}. Caches
- * internally as a {@link WeakReference}, and updated with future
- * {@link #recordSnapshotLocked(NetworkStats, Map, long)} snapshots as long
- * as reference is valid.
- */
- public NetworkStatsCollection getOrLoadCompleteLocked() {
- Objects.requireNonNull(mRotator, "missing FileRotator");
- NetworkStatsCollection res = mComplete != null ? mComplete.get() : null;
- if (res == null) {
- res = loadLocked(Long.MIN_VALUE, Long.MAX_VALUE);
- mComplete = new WeakReference<NetworkStatsCollection>(res);
- }
- return res;
- }
-
- public NetworkStatsCollection getOrLoadPartialLocked(long start, long end) {
- Objects.requireNonNull(mRotator, "missing FileRotator");
- NetworkStatsCollection res = mComplete != null ? mComplete.get() : null;
- if (res == null) {
- res = loadLocked(start, end);
- }
- return res;
- }
-
- private NetworkStatsCollection loadLocked(long start, long end) {
- if (LOGD) Slog.d(TAG, "loadLocked() reading from disk for " + mCookie);
- final NetworkStatsCollection res = new NetworkStatsCollection(mBucketDuration);
- try {
- mRotator.readMatching(res, start, end);
- res.recordCollection(mPending);
- } catch (IOException e) {
- Log.wtf(TAG, "problem completely reading network stats", e);
- recoverFromWtf();
- } catch (OutOfMemoryError e) {
- Log.wtf(TAG, "problem completely reading network stats", e);
- recoverFromWtf();
- }
- return res;
- }
-
- /**
- * Record any delta that occurred since last {@link NetworkStats} snapshot, using the given
- * {@link Map} to identify network interfaces. First snapshot is considered bootstrap, and is
- * not counted as delta.
- */
- public void recordSnapshotLocked(NetworkStats snapshot,
- Map<String, NetworkIdentitySet> ifaceIdent, long currentTimeMillis) {
- final HashSet<String> unknownIfaces = Sets.newHashSet();
-
- // skip recording when snapshot missing
- if (snapshot == null) return;
-
- // assume first snapshot is bootstrap and don't record
- if (mLastSnapshot == null) {
- mLastSnapshot = snapshot;
- return;
- }
-
- final NetworkStatsCollection complete = mComplete != null ? mComplete.get() : null;
-
- final NetworkStats delta = NetworkStats.subtract(
- snapshot, mLastSnapshot, mObserver, mCookie);
- final long end = currentTimeMillis;
- final long start = end - delta.getElapsedRealtime();
-
- NetworkStats.Entry entry = null;
- for (int i = 0; i < delta.size(); i++) {
- entry = delta.getValues(i, entry);
-
- // As a last-ditch check, report any negative values and
- // clamp them so recording below doesn't croak.
- if (entry.isNegative()) {
- if (mObserver != null) {
- mObserver.foundNonMonotonic(delta, i, mCookie);
- }
- entry.rxBytes = Math.max(entry.rxBytes, 0);
- entry.rxPackets = Math.max(entry.rxPackets, 0);
- entry.txBytes = Math.max(entry.txBytes, 0);
- entry.txPackets = Math.max(entry.txPackets, 0);
- entry.operations = Math.max(entry.operations, 0);
- }
-
- final NetworkIdentitySet ident = ifaceIdent.get(entry.iface);
- if (ident == null) {
- unknownIfaces.add(entry.iface);
- continue;
- }
-
- // skip when no delta occurred
- if (entry.isEmpty()) continue;
-
- // only record tag data when requested
- if ((entry.tag == TAG_NONE) != mOnlyTags) {
- if (mPending != null) {
- mPending.recordData(ident, entry.uid, entry.set, entry.tag, start, end, entry);
- }
-
- // also record against boot stats when present
- if (mSinceBoot != null) {
- mSinceBoot.recordData(ident, entry.uid, entry.set, entry.tag, start, end, entry);
- }
-
- // also record against complete dataset when present
- if (complete != null) {
- complete.recordData(ident, entry.uid, entry.set, entry.tag, start, end, entry);
- }
- }
- }
-
- mLastSnapshot = snapshot;
-
- if (LOGV && unknownIfaces.size() > 0) {
- Slog.w(TAG, "unknown interfaces " + unknownIfaces + ", ignoring those stats");
- }
- }
-
- /**
- * Consider persisting any pending deltas, if they are beyond
- * {@link #mPersistThresholdBytes}.
- */
- public void maybePersistLocked(long currentTimeMillis) {
- Objects.requireNonNull(mRotator, "missing FileRotator");
- final long pendingBytes = mPending.getTotalBytes();
- if (pendingBytes >= mPersistThresholdBytes) {
- forcePersistLocked(currentTimeMillis);
- } else {
- mRotator.maybeRotate(currentTimeMillis);
- }
- }
-
- /**
- * Force persisting any pending deltas.
- */
- public void forcePersistLocked(long currentTimeMillis) {
- Objects.requireNonNull(mRotator, "missing FileRotator");
- if (mPending.isDirty()) {
- if (LOGD) Slog.d(TAG, "forcePersistLocked() writing for " + mCookie);
- try {
- mRotator.rewriteActive(mPendingRewriter, currentTimeMillis);
- mRotator.maybeRotate(currentTimeMillis);
- mPending.reset();
- } catch (IOException e) {
- Log.wtf(TAG, "problem persisting pending stats", e);
- recoverFromWtf();
- } catch (OutOfMemoryError e) {
- Log.wtf(TAG, "problem persisting pending stats", e);
- recoverFromWtf();
- }
- }
- }
-
- /**
- * Remove the given UID from all {@link FileRotator} history, migrating it
- * to {@link TrafficStats#UID_REMOVED}.
- */
- public void removeUidsLocked(int[] uids) {
- if (mRotator != null) {
- try {
- // Rewrite all persisted data to migrate UID stats
- mRotator.rewriteAll(new RemoveUidRewriter(mBucketDuration, uids));
- } catch (IOException e) {
- Log.wtf(TAG, "problem removing UIDs " + Arrays.toString(uids), e);
- recoverFromWtf();
- } catch (OutOfMemoryError e) {
- Log.wtf(TAG, "problem removing UIDs " + Arrays.toString(uids), e);
- recoverFromWtf();
- }
- }
-
- // Remove any pending stats
- if (mPending != null) {
- mPending.removeUids(uids);
- }
- if (mSinceBoot != null) {
- mSinceBoot.removeUids(uids);
- }
-
- // Clear UID from current stats snapshot
- if (mLastSnapshot != null) {
- mLastSnapshot.removeUids(uids);
- }
-
- final NetworkStatsCollection complete = mComplete != null ? mComplete.get() : null;
- if (complete != null) {
- complete.removeUids(uids);
- }
- }
-
- /**
- * Rewriter that will combine current {@link NetworkStatsCollection} values
- * with anything read from disk, and write combined set to disk. Clears the
- * original {@link NetworkStatsCollection} when finished writing.
- */
- private static class CombiningRewriter implements FileRotator.Rewriter {
- private final NetworkStatsCollection mCollection;
-
- public CombiningRewriter(NetworkStatsCollection collection) {
- mCollection = Objects.requireNonNull(collection, "missing NetworkStatsCollection");
- }
-
- @Override
- public void reset() {
- // ignored
- }
-
- @Override
- public void read(InputStream in) throws IOException {
- mCollection.read(in);
- }
-
- @Override
- public boolean shouldWrite() {
- return true;
- }
-
- @Override
- public void write(OutputStream out) throws IOException {
- mCollection.write(out);
- mCollection.reset();
- }
- }
-
- /**
- * Rewriter that will remove any {@link NetworkStatsHistory} attributed to
- * the requested UID, only writing data back when modified.
- */
- public static class RemoveUidRewriter implements FileRotator.Rewriter {
- private final NetworkStatsCollection mTemp;
- private final int[] mUids;
-
- public RemoveUidRewriter(long bucketDuration, int[] uids) {
- mTemp = new NetworkStatsCollection(bucketDuration);
- mUids = uids;
- }
-
- @Override
- public void reset() {
- mTemp.reset();
- }
-
- @Override
- public void read(InputStream in) throws IOException {
- mTemp.read(in);
- mTemp.clearDirty();
- mTemp.removeUids(mUids);
- }
-
- @Override
- public boolean shouldWrite() {
- return mTemp.isDirty();
- }
-
- @Override
- public void write(OutputStream out) throws IOException {
- mTemp.write(out);
- }
- }
-
- public void importLegacyNetworkLocked(File file) throws IOException {
- Objects.requireNonNull(mRotator, "missing FileRotator");
-
- // legacy file still exists; start empty to avoid double importing
- mRotator.deleteAll();
-
- final NetworkStatsCollection collection = new NetworkStatsCollection(mBucketDuration);
- collection.readLegacyNetwork(file);
-
- final long startMillis = collection.getStartMillis();
- final long endMillis = collection.getEndMillis();
-
- if (!collection.isEmpty()) {
- // process legacy data, creating active file at starting time, then
- // using end time to possibly trigger rotation.
- mRotator.rewriteActive(new CombiningRewriter(collection), startMillis);
- mRotator.maybeRotate(endMillis);
- }
- }
-
- public void importLegacyUidLocked(File file) throws IOException {
- Objects.requireNonNull(mRotator, "missing FileRotator");
-
- // legacy file still exists; start empty to avoid double importing
- mRotator.deleteAll();
-
- final NetworkStatsCollection collection = new NetworkStatsCollection(mBucketDuration);
- collection.readLegacyUid(file, mOnlyTags);
-
- final long startMillis = collection.getStartMillis();
- final long endMillis = collection.getEndMillis();
-
- if (!collection.isEmpty()) {
- // process legacy data, creating active file at starting time, then
- // using end time to possibly trigger rotation.
- mRotator.rewriteActive(new CombiningRewriter(collection), startMillis);
- mRotator.maybeRotate(endMillis);
- }
- }
-
- public void dumpLocked(IndentingPrintWriter pw, boolean fullHistory) {
- if (mPending != null) {
- pw.print("Pending bytes: "); pw.println(mPending.getTotalBytes());
- }
- if (fullHistory) {
- pw.println("Complete history:");
- getOrLoadCompleteLocked().dump(pw);
- } else {
- pw.println("History since boot:");
- mSinceBoot.dump(pw);
- }
- }
-
- public void dumpDebugLocked(ProtoOutputStream proto, long tag) {
- final long start = proto.start(tag);
- if (mPending != null) {
- proto.write(NetworkStatsRecorderProto.PENDING_TOTAL_BYTES, mPending.getTotalBytes());
- }
- getOrLoadCompleteLocked().dumpDebug(proto, NetworkStatsRecorderProto.COMPLETE_HISTORY);
- proto.end(start);
- }
-
- public void dumpCheckin(PrintWriter pw, long start, long end) {
- // Only load and dump stats from the requested window
- getOrLoadPartialLocked(start, end).dumpCheckin(pw, start, end);
- }
-
- /**
- * Recover from {@link FileRotator} failure by dumping state to
- * {@link DropBoxManager} and deleting contents.
- */
- private void recoverFromWtf() {
- if (DUMP_BEFORE_DELETE) {
- final ByteArrayOutputStream os = new ByteArrayOutputStream();
- try {
- mRotator.dumpAll(os);
- } catch (IOException e) {
- // ignore partial contents
- os.reset();
- } finally {
- IoUtils.closeQuietly(os);
- }
- mDropBox.addData(TAG_NETSTATS_DUMP, os.toByteArray(), 0);
- }
-
- mRotator.deleteAll();
- }
-}
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
deleted file mode 100644
index 097b0711eff7..000000000000
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ /dev/null
@@ -1,2255 +0,0 @@
-/*
- * Copyright (C) 2011 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.net;
-
-import static android.Manifest.permission.NETWORK_STATS_PROVIDER;
-import static android.Manifest.permission.READ_NETWORK_USAGE_HISTORY;
-import static android.Manifest.permission.UPDATE_DEVICE_STATS;
-import static android.content.Intent.ACTION_SHUTDOWN;
-import static android.content.Intent.ACTION_UID_REMOVED;
-import static android.content.Intent.ACTION_USER_REMOVED;
-import static android.content.Intent.EXTRA_UID;
-import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-import static android.net.NetworkIdentity.SUBTYPE_COMBINED;
-import static android.net.NetworkStack.checkNetworkStackPermission;
-import static android.net.NetworkStats.DEFAULT_NETWORK_ALL;
-import static android.net.NetworkStats.IFACE_ALL;
-import static android.net.NetworkStats.IFACE_VT;
-import static android.net.NetworkStats.INTERFACES_ALL;
-import static android.net.NetworkStats.METERED_ALL;
-import static android.net.NetworkStats.ROAMING_ALL;
-import static android.net.NetworkStats.SET_ALL;
-import static android.net.NetworkStats.SET_DEFAULT;
-import static android.net.NetworkStats.SET_FOREGROUND;
-import static android.net.NetworkStats.STATS_PER_IFACE;
-import static android.net.NetworkStats.STATS_PER_UID;
-import static android.net.NetworkStats.TAG_ALL;
-import static android.net.NetworkStats.TAG_NONE;
-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.TetheringManager.ACTION_TETHER_STATE_CHANGED;
-import static android.net.TrafficStats.KB_IN_BYTES;
-import static android.net.TrafficStats.MB_IN_BYTES;
-import static android.net.TrafficStats.UNSUPPORTED;
-import static android.os.Trace.TRACE_TAG_NETWORK;
-import static android.provider.Settings.Global.NETSTATS_AUGMENT_ENABLED;
-import static android.provider.Settings.Global.NETSTATS_COMBINE_SUBTYPE_ENABLED;
-import static android.provider.Settings.Global.NETSTATS_DEV_BUCKET_DURATION;
-import static android.provider.Settings.Global.NETSTATS_DEV_DELETE_AGE;
-import static android.provider.Settings.Global.NETSTATS_DEV_PERSIST_BYTES;
-import static android.provider.Settings.Global.NETSTATS_DEV_ROTATE_AGE;
-import static android.provider.Settings.Global.NETSTATS_GLOBAL_ALERT_BYTES;
-import static android.provider.Settings.Global.NETSTATS_POLL_INTERVAL;
-import static android.provider.Settings.Global.NETSTATS_SAMPLE_ENABLED;
-import static android.provider.Settings.Global.NETSTATS_UID_BUCKET_DURATION;
-import static android.provider.Settings.Global.NETSTATS_UID_DELETE_AGE;
-import static android.provider.Settings.Global.NETSTATS_UID_PERSIST_BYTES;
-import static android.provider.Settings.Global.NETSTATS_UID_ROTATE_AGE;
-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.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
-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;
-import static android.text.format.DateUtils.SECOND_IN_MILLIS;
-
-import static com.android.net.module.util.NetworkCapabilitiesUtils.getDisplayTransport;
-import static com.android.server.NetworkManagementService.LIMIT_GLOBAL_ALERT;
-import static com.android.server.NetworkManagementSocketTagger.resetKernelUidStats;
-import static com.android.server.NetworkManagementSocketTagger.setKernelCounterSet;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.AlarmManager;
-import android.app.PendingIntent;
-import android.app.usage.NetworkStatsManager;
-import android.content.BroadcastReceiver;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.database.ContentObserver;
-import android.net.DataUsageRequest;
-import android.net.INetworkManagementEventObserver;
-import android.net.INetworkStatsService;
-import android.net.INetworkStatsSession;
-import android.net.Network;
-import android.net.NetworkCapabilities;
-import android.net.NetworkIdentity;
-import android.net.NetworkSpecifier;
-import android.net.NetworkStack;
-import android.net.NetworkStateSnapshot;
-import android.net.NetworkStats;
-import android.net.NetworkStats.NonMonotonicObserver;
-import android.net.NetworkStatsHistory;
-import android.net.NetworkTemplate;
-import android.net.TelephonyNetworkSpecifier;
-import android.net.TrafficStats;
-import android.net.UnderlyingNetworkInfo;
-import android.net.Uri;
-import android.net.netstats.provider.INetworkStatsProvider;
-import android.net.netstats.provider.INetworkStatsProviderCallback;
-import android.net.netstats.provider.NetworkStatsProvider;
-import android.os.BestClock;
-import android.os.Binder;
-import android.os.DropBoxManager;
-import android.os.Environment;
-import android.os.Handler;
-import android.os.HandlerExecutor;
-import android.os.HandlerThread;
-import android.os.IBinder;
-import android.os.INetworkManagementService;
-import android.os.Looper;
-import android.os.Message;
-import android.os.Messenger;
-import android.os.PowerManager;
-import android.os.RemoteException;
-import android.os.SystemClock;
-import android.os.Trace;
-import android.os.UserHandle;
-import android.provider.Settings;
-import android.provider.Settings.Global;
-import android.service.NetworkInterfaceProto;
-import android.service.NetworkStatsServiceDumpProto;
-import android.telephony.PhoneStateListener;
-import android.telephony.SubscriptionPlan;
-import android.text.TextUtils;
-import android.text.format.DateUtils;
-import android.util.ArrayMap;
-import android.util.ArraySet;
-import android.util.EventLog;
-import android.util.Log;
-import android.util.MathUtils;
-import android.util.Slog;
-import android.util.SparseIntArray;
-import android.util.proto.ProtoOutputStream;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.DumpUtils;
-import com.android.internal.util.FileRotator;
-import com.android.internal.util.IndentingPrintWriter;
-import com.android.server.EventLogTags;
-import com.android.server.LocalServices;
-
-import java.io.File;
-import java.io.FileDescriptor;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.time.Clock;
-import java.time.ZoneOffset;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Objects;
-import java.util.concurrent.CopyOnWriteArrayList;
-import java.util.concurrent.Executor;
-import java.util.concurrent.Semaphore;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Collect and persist detailed network statistics, and provide this data to
- * other system services.
- */
-public class NetworkStatsService extends INetworkStatsService.Stub {
- static final String TAG = "NetworkStats";
- static final boolean LOGD = Log.isLoggable(TAG, Log.DEBUG);
- static final boolean LOGV = Log.isLoggable(TAG, Log.VERBOSE);
-
- // Perform polling and persist all (FLAG_PERSIST_ALL).
- private static final int MSG_PERFORM_POLL = 1;
- // Perform polling, persist network, and register the global alert again.
- private static final int MSG_PERFORM_POLL_REGISTER_ALERT = 2;
- private static final int MSG_NOTIFY_NETWORK_STATUS = 3;
- // A message for broadcasting ACTION_NETWORK_STATS_UPDATED in handler thread to prevent
- // deadlock.
- private static final int MSG_BROADCAST_NETWORK_STATS_UPDATED = 4;
-
- /** Flags to control detail level of poll event. */
- private static final int FLAG_PERSIST_NETWORK = 0x1;
- private static final int FLAG_PERSIST_UID = 0x2;
- private static final int FLAG_PERSIST_ALL = FLAG_PERSIST_NETWORK | FLAG_PERSIST_UID;
- private static final int FLAG_PERSIST_FORCE = 0x100;
-
- /**
- * When global alert quota is high, wait for this delay before processing each polling,
- * and do not schedule further polls once there is already one queued.
- * This avoids firing the global alert too often on devices with high transfer speeds and
- * high quota.
- */
- private static final int DEFAULT_PERFORM_POLL_DELAY_MS = 1000;
-
- private static final String TAG_NETSTATS_ERROR = "netstats_error";
-
- private final Context mContext;
- private final INetworkManagementService mNetworkManager;
- private final NetworkStatsFactory mStatsFactory;
- private final AlarmManager mAlarmManager;
- private final Clock mClock;
- private final NetworkStatsSettings mSettings;
- private final NetworkStatsObservers mStatsObservers;
-
- private final File mSystemDir;
- private final File mBaseDir;
-
- private final PowerManager.WakeLock mWakeLock;
-
- private final boolean mUseBpfTrafficStats;
-
- private final ContentObserver mContentObserver;
- private final ContentResolver mContentResolver;
-
- @VisibleForTesting
- public static final String ACTION_NETWORK_STATS_POLL =
- "com.android.server.action.NETWORK_STATS_POLL";
- public static final String ACTION_NETWORK_STATS_UPDATED =
- "com.android.server.action.NETWORK_STATS_UPDATED";
-
- private PendingIntent mPollIntent;
-
- private static final String PREFIX_DEV = "dev";
- private static final String PREFIX_XT = "xt";
- private static final String PREFIX_UID = "uid";
- private static final String PREFIX_UID_TAG = "uid_tag";
-
- /**
- * Settings that can be changed externally.
- */
- public interface NetworkStatsSettings {
- long getPollInterval();
- long getPollDelay();
- boolean getSampleEnabled();
- boolean getAugmentEnabled();
- /**
- * When enabled, all mobile data is reported under {@link NetworkIdentity#SUBTYPE_COMBINED}.
- * When disabled, mobile data is broken down by a granular subtype representative of the
- * actual subtype. {@see NetworkTemplate#getCollapsedRatType}.
- * Enabling this decreases the level of detail but saves performance, disk space and
- * amount of data logged.
- */
- boolean getCombineSubtypeEnabled();
-
- class Config {
- public final long bucketDuration;
- public final long rotateAgeMillis;
- public final long deleteAgeMillis;
-
- public Config(long bucketDuration, long rotateAgeMillis, long deleteAgeMillis) {
- this.bucketDuration = bucketDuration;
- this.rotateAgeMillis = rotateAgeMillis;
- this.deleteAgeMillis = deleteAgeMillis;
- }
- }
-
- Config getDevConfig();
- Config getXtConfig();
- Config getUidConfig();
- Config getUidTagConfig();
-
- long getGlobalAlertBytes(long def);
- long getDevPersistBytes(long def);
- long getXtPersistBytes(long def);
- long getUidPersistBytes(long def);
- long getUidTagPersistBytes(long def);
- }
-
- private final Object mStatsLock = new Object();
-
- /** Set of currently active ifaces. */
- @GuardedBy("mStatsLock")
- private final ArrayMap<String, NetworkIdentitySet> mActiveIfaces = new ArrayMap<>();
-
- /** Set of currently active ifaces for UID stats. */
- @GuardedBy("mStatsLock")
- private final ArrayMap<String, NetworkIdentitySet> mActiveUidIfaces = new ArrayMap<>();
-
- /** Current default active iface. */
- @GuardedBy("mStatsLock")
- private String mActiveIface;
-
- /** Set of any ifaces associated with mobile networks since boot. */
- private volatile String[] mMobileIfaces = new String[0];
-
- /** Set of all ifaces currently used by traffic that does not explicitly specify a Network. */
- @GuardedBy("mStatsLock")
- private Network[] mDefaultNetworks = new Network[0];
-
- /** Last states of all networks sent from ConnectivityService. */
- @GuardedBy("mStatsLock")
- @Nullable
- private NetworkStateSnapshot[] mLastNetworkStateSnapshots = null;
-
- private final DropBoxNonMonotonicObserver mNonMonotonicObserver =
- new DropBoxNonMonotonicObserver();
-
- private static final int MAX_STATS_PROVIDER_POLL_WAIT_TIME_MS = 100;
- private final CopyOnWriteArrayList<NetworkStatsProviderCallbackImpl> mStatsProviderCbList =
- new CopyOnWriteArrayList<>();
- /** Semaphore used to wait for stats provider to respond to request stats update. */
- private final Semaphore mStatsProviderSem = new Semaphore(0, true);
-
- @GuardedBy("mStatsLock")
- private NetworkStatsRecorder mDevRecorder;
- @GuardedBy("mStatsLock")
- private NetworkStatsRecorder mXtRecorder;
- @GuardedBy("mStatsLock")
- private NetworkStatsRecorder mUidRecorder;
- @GuardedBy("mStatsLock")
- private NetworkStatsRecorder mUidTagRecorder;
-
- /** Cached {@link #mXtRecorder} stats. */
- @GuardedBy("mStatsLock")
- private NetworkStatsCollection mXtStatsCached;
-
- /** Current counter sets for each UID. */
- private SparseIntArray mActiveUidCounterSet = new SparseIntArray();
-
- /** Data layer operation counters for splicing into other structures. */
- private NetworkStats mUidOperations = new NetworkStats(0L, 10);
-
- @NonNull
- private final Handler mHandler;
-
- private volatile boolean mSystemReady;
- private long mPersistThreshold = 2 * MB_IN_BYTES;
- private long mGlobalAlertBytes;
-
- private static final long POLL_RATE_LIMIT_MS = 15_000;
-
- private long mLastStatsSessionPoll;
-
- /** Map from UID to number of opened sessions */
- @GuardedBy("mOpenSessionCallsPerUid")
- private final SparseIntArray mOpenSessionCallsPerUid = new SparseIntArray();
-
- private final static int DUMP_STATS_SESSION_COUNT = 20;
-
- @NonNull
- private final Dependencies mDeps;
-
- @NonNull
- private final NetworkStatsSubscriptionsMonitor mNetworkStatsSubscriptionsMonitor;
-
- private static @NonNull File getDefaultSystemDir() {
- return new File(Environment.getDataDirectory(), "system");
- }
-
- private static @NonNull File getDefaultBaseDir() {
- File baseDir = new File(getDefaultSystemDir(), "netstats");
- baseDir.mkdirs();
- return baseDir;
- }
-
- private static @NonNull Clock getDefaultClock() {
- return new BestClock(ZoneOffset.UTC, SystemClock.currentNetworkTimeClock(),
- Clock.systemUTC());
- }
-
- private final class NetworkStatsHandler extends Handler {
- NetworkStatsHandler(@NonNull Looper looper) {
- super(looper);
- }
-
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_PERFORM_POLL: {
- performPoll(FLAG_PERSIST_ALL);
- break;
- }
- case MSG_NOTIFY_NETWORK_STATUS: {
- // If no cached states, ignore.
- if (mLastNetworkStateSnapshots == null) break;
- // TODO (b/181642673): Protect mDefaultNetworks from concurrent accessing.
- handleNotifyNetworkStatus(
- mDefaultNetworks, mLastNetworkStateSnapshots, mActiveIface);
- break;
- }
- case MSG_PERFORM_POLL_REGISTER_ALERT: {
- performPoll(FLAG_PERSIST_NETWORK);
- registerGlobalAlert();
- break;
- }
- case MSG_BROADCAST_NETWORK_STATS_UPDATED: {
- final Intent updatedIntent = new Intent(ACTION_NETWORK_STATS_UPDATED);
- updatedIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
- mContext.sendBroadcastAsUser(updatedIntent, UserHandle.ALL,
- READ_NETWORK_USAGE_HISTORY);
- break;
- }
- }
- }
- }
-
- public static NetworkStatsService create(Context context,
- INetworkManagementService networkManager) {
- AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
- PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
- PowerManager.WakeLock wakeLock =
- powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
-
- final NetworkStatsService service = new NetworkStatsService(context, networkManager,
- alarmManager, wakeLock, getDefaultClock(),
- new DefaultNetworkStatsSettings(context), new NetworkStatsFactory(),
- new NetworkStatsObservers(), getDefaultSystemDir(), getDefaultBaseDir(),
- new Dependencies());
- service.registerLocalService();
-
- return service;
- }
-
- // This must not be called outside of tests, even within the same package, as this constructor
- // does not register the local service. Use the create() helper above.
- @VisibleForTesting
- NetworkStatsService(Context context, INetworkManagementService networkManager,
- AlarmManager alarmManager, PowerManager.WakeLock wakeLock, Clock clock,
- 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");
- mAlarmManager = Objects.requireNonNull(alarmManager, "missing AlarmManager");
- mClock = Objects.requireNonNull(clock, "missing Clock");
- mSettings = Objects.requireNonNull(settings, "missing NetworkStatsSettings");
- mWakeLock = Objects.requireNonNull(wakeLock, "missing WakeLock");
- mStatsFactory = Objects.requireNonNull(factory, "missing factory");
- mStatsObservers = Objects.requireNonNull(statsObservers, "missing NetworkStatsObservers");
- mSystemDir = Objects.requireNonNull(systemDir, "missing systemDir");
- mBaseDir = Objects.requireNonNull(baseDir, "missing baseDir");
- mUseBpfTrafficStats = new File("/sys/fs/bpf/map_netd_app_uid_stats_map").exists();
- mDeps = Objects.requireNonNull(deps, "missing Dependencies");
-
- final HandlerThread handlerThread = mDeps.makeHandlerThread();
- handlerThread.start();
- mHandler = new NetworkStatsHandler(handlerThread.getLooper());
- mNetworkStatsSubscriptionsMonitor = deps.makeSubscriptionsMonitor(mContext,
- mHandler.getLooper(), new HandlerExecutor(mHandler), this);
- mContentResolver = mContext.getContentResolver();
- mContentObserver = mDeps.makeContentObserver(mHandler, mSettings,
- mNetworkStatsSubscriptionsMonitor);
- }
-
- /**
- * Dependencies of NetworkStatsService, for injection in tests.
- */
- // TODO: Move more stuff into dependencies object.
- @VisibleForTesting
- public static class Dependencies {
- /**
- * Create a HandlerThread to use in NetworkStatsService.
- */
- @NonNull
- 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 Looper looper, @NonNull Executor executor,
- @NonNull NetworkStatsService service) {
- // TODO: Update RatType passively in NSS, instead of querying into the monitor
- // when notifyNetworkStatus.
- return new NetworkStatsSubscriptionsMonitor(context, looper, executor,
- (subscriberId, type) -> service.handleOnCollapsedRatTypeChanged());
- }
-
- /**
- * Create a ContentObserver instance which is used to observe settings changes,
- * and dispatch onChange events on handler thread.
- */
- public @NonNull ContentObserver makeContentObserver(@NonNull Handler handler,
- @NonNull NetworkStatsSettings settings,
- @NonNull NetworkStatsSubscriptionsMonitor monitor) {
- return new ContentObserver(handler) {
- @Override
- public void onChange(boolean selfChange, @NonNull Uri uri) {
- if (!settings.getCombineSubtypeEnabled()) {
- monitor.start();
- } else {
- monitor.stop();
- }
- }
- };
- }
- }
-
- private void registerLocalService() {
- LocalServices.addService(NetworkStatsManagerInternal.class,
- new NetworkStatsManagerInternalImpl());
- }
-
- public void systemReady() {
- synchronized (mStatsLock) {
- mSystemReady = true;
-
- // create data recorders along with historical rotators
- mDevRecorder = buildRecorder(PREFIX_DEV, mSettings.getDevConfig(), false);
- mXtRecorder = buildRecorder(PREFIX_XT, mSettings.getXtConfig(), false);
- mUidRecorder = buildRecorder(PREFIX_UID, mSettings.getUidConfig(), false);
- mUidTagRecorder = buildRecorder(PREFIX_UID_TAG, mSettings.getUidTagConfig(), true);
-
- updatePersistThresholdsLocked();
-
- // upgrade any legacy stats, migrating them to rotated files
- maybeUpgradeLegacyStatsLocked();
-
- // read historical network stats from disk, since policy service
- // might need them right away.
- mXtStatsCached = mXtRecorder.getOrLoadCompleteLocked();
-
- // bootstrap initial stats to prevent double-counting later
- bootstrapStatsLocked();
- }
-
- // watch for tethering changes
- final IntentFilter tetherFilter = new IntentFilter(ACTION_TETHER_STATE_CHANGED);
- mContext.registerReceiver(mTetherReceiver, tetherFilter, null, mHandler);
-
- // listen for periodic polling events
- final IntentFilter pollFilter = new IntentFilter(ACTION_NETWORK_STATS_POLL);
- mContext.registerReceiver(mPollReceiver, pollFilter, READ_NETWORK_USAGE_HISTORY, mHandler);
-
- // listen for uid removal to clean stats
- final IntentFilter removedFilter = new IntentFilter(ACTION_UID_REMOVED);
- mContext.registerReceiver(mRemovedReceiver, removedFilter, null, mHandler);
-
- // listen for user changes to clean stats
- final IntentFilter userFilter = new IntentFilter(ACTION_USER_REMOVED);
- mContext.registerReceiver(mUserReceiver, userFilter, null, mHandler);
-
- // persist stats during clean shutdown
- final IntentFilter shutdownFilter = new IntentFilter(ACTION_SHUTDOWN);
- mContext.registerReceiver(mShutdownReceiver, shutdownFilter);
-
- try {
- mNetworkManager.registerObserver(mAlertObserver);
- } catch (RemoteException e) {
- // ignored; service lives in system_server
- }
-
- // schedule periodic pall alarm based on {@link NetworkStatsSettings#getPollInterval()}.
- final PendingIntent pollIntent =
- PendingIntent.getBroadcast(mContext, 0, new Intent(ACTION_NETWORK_STATS_POLL),
- PendingIntent.FLAG_IMMUTABLE);
-
- final long currentRealtime = SystemClock.elapsedRealtime();
- mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME, currentRealtime,
- mSettings.getPollInterval(), pollIntent);
-
- mContentResolver.registerContentObserver(Settings.Global
- .getUriFor(Settings.Global.NETSTATS_COMBINE_SUBTYPE_ENABLED),
- false /* notifyForDescendants */, mContentObserver);
-
- // Post a runnable on handler thread to call onChange(). It's for getting current value of
- // NETSTATS_COMBINE_SUBTYPE_ENABLED to decide start or stop monitoring RAT type changes.
- mHandler.post(() -> mContentObserver.onChange(false, Settings.Global
- .getUriFor(Settings.Global.NETSTATS_COMBINE_SUBTYPE_ENABLED)));
-
- registerGlobalAlert();
- }
-
- private NetworkStatsRecorder buildRecorder(
- String prefix, NetworkStatsSettings.Config config, boolean includeTags) {
- final DropBoxManager dropBox = (DropBoxManager) mContext.getSystemService(
- Context.DROPBOX_SERVICE);
- return new NetworkStatsRecorder(new FileRotator(
- mBaseDir, prefix, config.rotateAgeMillis, config.deleteAgeMillis),
- mNonMonotonicObserver, dropBox, prefix, config.bucketDuration, includeTags);
- }
-
- @GuardedBy("mStatsLock")
- private void shutdownLocked() {
- mContext.unregisterReceiver(mTetherReceiver);
- mContext.unregisterReceiver(mPollReceiver);
- mContext.unregisterReceiver(mRemovedReceiver);
- mContext.unregisterReceiver(mUserReceiver);
- mContext.unregisterReceiver(mShutdownReceiver);
-
- if (!mSettings.getCombineSubtypeEnabled()) {
- mNetworkStatsSubscriptionsMonitor.stop();
- }
-
- mContentResolver.unregisterContentObserver(mContentObserver);
-
- final long currentTime = mClock.millis();
-
- // persist any pending stats
- mDevRecorder.forcePersistLocked(currentTime);
- mXtRecorder.forcePersistLocked(currentTime);
- mUidRecorder.forcePersistLocked(currentTime);
- mUidTagRecorder.forcePersistLocked(currentTime);
-
- mSystemReady = false;
- }
-
- @GuardedBy("mStatsLock")
- private void maybeUpgradeLegacyStatsLocked() {
- File file;
- try {
- file = new File(mSystemDir, "netstats.bin");
- if (file.exists()) {
- mDevRecorder.importLegacyNetworkLocked(file);
- file.delete();
- }
-
- file = new File(mSystemDir, "netstats_xt.bin");
- if (file.exists()) {
- file.delete();
- }
-
- file = new File(mSystemDir, "netstats_uid.bin");
- if (file.exists()) {
- mUidRecorder.importLegacyUidLocked(file);
- mUidTagRecorder.importLegacyUidLocked(file);
- file.delete();
- }
- } catch (IOException e) {
- Log.wtf(TAG, "problem during legacy upgrade", e);
- } catch (OutOfMemoryError e) {
- Log.wtf(TAG, "problem during legacy upgrade", e);
- }
- }
-
- /**
- * Register for a global alert that is delivered through {@link INetworkManagementEventObserver}
- * or {@link NetworkStatsProviderCallback#onAlertReached()} once a threshold amount of data has
- * been transferred.
- */
- private void registerGlobalAlert() {
- try {
- mNetworkManager.setGlobalAlert(mGlobalAlertBytes);
- } catch (IllegalStateException e) {
- Slog.w(TAG, "problem registering for global alert: " + e);
- } catch (RemoteException e) {
- // ignored; service lives in system_server
- }
- invokeForAllStatsProviderCallbacks((cb) -> cb.mProvider.onSetAlert(mGlobalAlertBytes));
- }
-
- @Override
- public INetworkStatsSession openSession() {
- // NOTE: if callers want to get non-augmented data, they should go
- // through the public API
- return openSessionInternal(NetworkStatsManager.FLAG_AUGMENT_WITH_SUBSCRIPTION_PLAN, null);
- }
-
- @Override
- public INetworkStatsSession openSessionForUsageStats(int flags, String callingPackage) {
- return openSessionInternal(flags, callingPackage);
- }
-
- private boolean isRateLimitedForPoll(int callingUid) {
- if (callingUid == android.os.Process.SYSTEM_UID) {
- return false;
- }
-
- final long lastCallTime;
- final long now = SystemClock.elapsedRealtime();
- synchronized (mOpenSessionCallsPerUid) {
- int calls = mOpenSessionCallsPerUid.get(callingUid, 0);
- mOpenSessionCallsPerUid.put(callingUid, calls + 1);
- lastCallTime = mLastStatsSessionPoll;
- mLastStatsSessionPoll = now;
- }
-
- return now - lastCallTime < POLL_RATE_LIMIT_MS;
- }
-
- private INetworkStatsSession openSessionInternal(final int flags, final String callingPackage) {
- final int callingUid = Binder.getCallingUid();
- final int usedFlags = isRateLimitedForPoll(callingUid)
- ? flags & (~NetworkStatsManager.FLAG_POLL_ON_OPEN)
- : flags;
- if ((usedFlags & (NetworkStatsManager.FLAG_POLL_ON_OPEN
- | NetworkStatsManager.FLAG_POLL_FORCE)) != 0) {
- final long ident = Binder.clearCallingIdentity();
- try {
- performPoll(FLAG_PERSIST_ALL);
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
-
- // return an IBinder which holds strong references to any loaded stats
- // for its lifetime; when caller closes only weak references remain.
-
- return new INetworkStatsSession.Stub() {
- private final int mCallingUid = callingUid;
- private final String mCallingPackage = callingPackage;
- private final @NetworkStatsAccess.Level int mAccessLevel = checkAccessLevel(
- callingPackage);
-
- private NetworkStatsCollection mUidComplete;
- private NetworkStatsCollection mUidTagComplete;
-
- private NetworkStatsCollection getUidComplete() {
- synchronized (mStatsLock) {
- if (mUidComplete == null) {
- mUidComplete = mUidRecorder.getOrLoadCompleteLocked();
- }
- return mUidComplete;
- }
- }
-
- private NetworkStatsCollection getUidTagComplete() {
- synchronized (mStatsLock) {
- if (mUidTagComplete == null) {
- mUidTagComplete = mUidTagRecorder.getOrLoadCompleteLocked();
- }
- return mUidTagComplete;
- }
- }
-
- @Override
- public int[] getRelevantUids() {
- return getUidComplete().getRelevantUids(mAccessLevel);
- }
-
- @Override
- public NetworkStats getDeviceSummaryForNetwork(
- NetworkTemplate template, long start, long end) {
- return internalGetSummaryForNetwork(template, usedFlags, start, end, mAccessLevel,
- mCallingUid);
- }
-
- @Override
- public NetworkStats getSummaryForNetwork(
- NetworkTemplate template, long start, long end) {
- return internalGetSummaryForNetwork(template, usedFlags, start, end, mAccessLevel,
- mCallingUid);
- }
-
- @Override
- public NetworkStatsHistory getHistoryForNetwork(NetworkTemplate template, int fields) {
- return internalGetHistoryForNetwork(template, usedFlags, fields, mAccessLevel,
- mCallingUid);
- }
-
- @Override
- public NetworkStats getSummaryForAllUid(
- NetworkTemplate template, long start, long end, boolean includeTags) {
- try {
- final NetworkStats stats = getUidComplete()
- .getSummary(template, start, end, mAccessLevel, mCallingUid);
- if (includeTags) {
- final NetworkStats tagStats = getUidTagComplete()
- .getSummary(template, start, end, mAccessLevel, mCallingUid);
- stats.combineAllValues(tagStats);
- }
- return stats;
- } catch (NullPointerException e) {
- // TODO: Track down and fix the cause of this crash and remove this catch block.
- Slog.wtf(TAG, "NullPointerException in getSummaryForAllUid", e);
- throw e;
- }
- }
-
- @Override
- public NetworkStatsHistory getHistoryForUid(
- NetworkTemplate template, int uid, int set, int tag, int fields) {
- // NOTE: We don't augment UID-level statistics
- if (tag == TAG_NONE) {
- return getUidComplete().getHistory(template, null, uid, set, tag, fields,
- Long.MIN_VALUE, Long.MAX_VALUE, mAccessLevel, mCallingUid);
- } else {
- return getUidTagComplete().getHistory(template, null, uid, set, tag, fields,
- Long.MIN_VALUE, Long.MAX_VALUE, mAccessLevel, mCallingUid);
- }
- }
-
- @Override
- public NetworkStatsHistory getHistoryIntervalForUid(
- NetworkTemplate template, int uid, int set, int tag, int fields,
- long start, long end) {
- // NOTE: We don't augment UID-level statistics
- if (tag == TAG_NONE) {
- return getUidComplete().getHistory(template, null, uid, set, tag, fields,
- start, end, mAccessLevel, mCallingUid);
- } else if (uid == Binder.getCallingUid()) {
- return getUidTagComplete().getHistory(template, null, uid, set, tag, fields,
- start, end, mAccessLevel, mCallingUid);
- } else {
- throw new SecurityException("Calling package " + mCallingPackage
- + " cannot access tag information from a different uid");
- }
- }
-
- @Override
- public void close() {
- mUidComplete = null;
- mUidTagComplete = null;
- }
- };
- }
-
- private @NetworkStatsAccess.Level int checkAccessLevel(String callingPackage) {
- return NetworkStatsAccess.checkAccessLevel(
- mContext, Binder.getCallingUid(), callingPackage);
- }
-
- /**
- * Find the most relevant {@link SubscriptionPlan} for the given
- * {@link NetworkTemplate} and flags. This is typically used to augment
- * local measurement results to match a known anchor from the carrier.
- */
- private SubscriptionPlan resolveSubscriptionPlan(NetworkTemplate template, int flags) {
- SubscriptionPlan plan = null;
- if ((flags & NetworkStatsManager.FLAG_AUGMENT_WITH_SUBSCRIPTION_PLAN) != 0
- && mSettings.getAugmentEnabled()) {
- if (LOGD) Slog.d(TAG, "Resolving plan for " + template);
- final long token = Binder.clearCallingIdentity();
- try {
- plan = LocalServices.getService(NetworkPolicyManagerInternal.class)
- .getSubscriptionPlan(template);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- if (LOGD) Slog.d(TAG, "Resolved to plan " + plan);
- }
- return plan;
- }
-
- /**
- * Return network summary, splicing between DEV and XT stats when
- * appropriate.
- */
- private NetworkStats internalGetSummaryForNetwork(NetworkTemplate template, int flags,
- long start, long end, @NetworkStatsAccess.Level int accessLevel, int callingUid) {
- // We've been using pure XT stats long enough that we no longer need to
- // splice DEV and XT together.
- final NetworkStatsHistory history = internalGetHistoryForNetwork(template, flags, FIELD_ALL,
- accessLevel, callingUid);
-
- final long now = System.currentTimeMillis();
- final NetworkStatsHistory.Entry entry = history.getValues(start, end, now, null);
-
- final NetworkStats stats = new NetworkStats(end - start, 1);
- stats.insertEntry(new NetworkStats.Entry(IFACE_ALL, UID_ALL, SET_ALL, TAG_NONE,
- METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, entry.rxBytes, entry.rxPackets,
- entry.txBytes, entry.txPackets, entry.operations));
- return stats;
- }
-
- /**
- * Return network history, splicing between DEV and XT stats when
- * appropriate.
- */
- private NetworkStatsHistory internalGetHistoryForNetwork(NetworkTemplate template,
- int flags, int fields, @NetworkStatsAccess.Level int accessLevel, int callingUid) {
- // We've been using pure XT stats long enough that we no longer need to
- // splice DEV and XT together.
- final SubscriptionPlan augmentPlan = resolveSubscriptionPlan(template, flags);
- synchronized (mStatsLock) {
- return mXtStatsCached.getHistory(template, augmentPlan,
- UID_ALL, SET_ALL, TAG_NONE, fields, Long.MIN_VALUE, Long.MAX_VALUE,
- accessLevel, callingUid);
- }
- }
-
- private long getNetworkTotalBytes(NetworkTemplate template, long start, long end) {
- assertSystemReady();
-
- // NOTE: if callers want to get non-augmented data, they should go
- // through the public API
- return internalGetSummaryForNetwork(template,
- NetworkStatsManager.FLAG_AUGMENT_WITH_SUBSCRIPTION_PLAN, start, end,
- NetworkStatsAccess.Level.DEVICE, Binder.getCallingUid()).getTotalBytes();
- }
-
- private NetworkStats getNetworkUidBytes(NetworkTemplate template, long start, long end) {
- assertSystemReady();
-
- final NetworkStatsCollection uidComplete;
- synchronized (mStatsLock) {
- uidComplete = mUidRecorder.getOrLoadCompleteLocked();
- }
- return uidComplete.getSummary(template, start, end, NetworkStatsAccess.Level.DEVICE,
- android.os.Process.SYSTEM_UID);
- }
-
- @Override
- public NetworkStats getDataLayerSnapshotForUid(int uid) throws RemoteException {
- if (Binder.getCallingUid() != uid) {
- Log.w(TAG, "Snapshots only available for calling UID");
- return new NetworkStats(SystemClock.elapsedRealtime(), 0);
- }
-
- // TODO: switch to data layer stats once kernel exports
- // for now, read network layer stats and flatten across all ifaces
- final NetworkStats networkLayer = readNetworkStatsUidDetail(uid, INTERFACES_ALL, TAG_ALL);
-
- // splice in operation counts
- networkLayer.spliceOperationsFrom(mUidOperations);
-
- final NetworkStats dataLayer = new NetworkStats(
- networkLayer.getElapsedRealtime(), networkLayer.size());
-
- NetworkStats.Entry entry = null;
- for (int i = 0; i < networkLayer.size(); i++) {
- entry = networkLayer.getValues(i, entry);
- entry.iface = IFACE_ALL;
- dataLayer.combineValues(entry);
- }
-
- return dataLayer;
- }
-
- @Override
- public NetworkStats getDetailedUidStats(String[] requiredIfaces) {
- try {
- final String[] ifacesToQuery =
- mStatsFactory.augmentWithStackedInterfaces(requiredIfaces);
- return getNetworkStatsUidDetail(ifacesToQuery);
- } catch (RemoteException e) {
- Log.wtf(TAG, "Error compiling UID stats", e);
- return new NetworkStats(0L, 0);
- }
- }
-
- @Override
- public String[] getMobileIfaces() {
- // TODO (b/192758557): Remove debug log.
- if (ArrayUtils.contains(mMobileIfaces, null)) {
- throw new NullPointerException(
- "null element in mMobileIfaces: " + Arrays.toString(mMobileIfaces));
- }
- return mMobileIfaces.clone();
- }
-
- @Override
- public void incrementOperationCount(int uid, int tag, int operationCount) {
- if (Binder.getCallingUid() != uid) {
- mContext.enforceCallingOrSelfPermission(UPDATE_DEVICE_STATS, TAG);
- }
-
- if (operationCount < 0) {
- throw new IllegalArgumentException("operation count can only be incremented");
- }
- if (tag == TAG_NONE) {
- throw new IllegalArgumentException("operation count must have specific tag");
- }
-
- synchronized (mStatsLock) {
- final int set = mActiveUidCounterSet.get(uid, SET_DEFAULT);
- mUidOperations.combineValues(
- mActiveIface, uid, set, tag, 0L, 0L, 0L, 0L, operationCount);
- mUidOperations.combineValues(
- mActiveIface, uid, set, TAG_NONE, 0L, 0L, 0L, 0L, operationCount);
- }
- }
-
- @VisibleForTesting
- void setUidForeground(int uid, boolean uidForeground) {
- synchronized (mStatsLock) {
- final int set = uidForeground ? SET_FOREGROUND : SET_DEFAULT;
- final int oldSet = mActiveUidCounterSet.get(uid, SET_DEFAULT);
- if (oldSet != set) {
- mActiveUidCounterSet.put(uid, set);
- setKernelCounterSet(uid, set);
- }
- }
- }
-
- /**
- * Notify {@code NetworkStatsService} about network status changed.
- */
- public void notifyNetworkStatus(
- @NonNull Network[] defaultNetworks,
- @NonNull NetworkStateSnapshot[] networkStates,
- @Nullable String activeIface,
- @NonNull UnderlyingNetworkInfo[] underlyingNetworkInfos) {
- checkNetworkStackPermission(mContext);
-
- final long token = Binder.clearCallingIdentity();
- try {
- handleNotifyNetworkStatus(defaultNetworks, networkStates, activeIface);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
-
- // Update the VPN underlying interfaces only after the poll is made and tun data has been
- // migrated. Otherwise the migration would use the new interfaces instead of the ones that
- // were current when the polled data was transferred.
- mStatsFactory.updateUnderlyingNetworkInfos(underlyingNetworkInfos);
- }
-
- @Override
- public void forceUpdate() {
- mContext.enforceCallingOrSelfPermission(READ_NETWORK_USAGE_HISTORY, TAG);
-
- final long token = Binder.clearCallingIdentity();
- try {
- performPoll(FLAG_PERSIST_ALL);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- private void advisePersistThreshold(long thresholdBytes) {
- // clamp threshold into safe range
- mPersistThreshold = MathUtils.constrain(thresholdBytes, 128 * KB_IN_BYTES, 2 * MB_IN_BYTES);
- if (LOGV) {
- Slog.v(TAG, "advisePersistThreshold() given " + thresholdBytes + ", clamped to "
- + mPersistThreshold);
- }
-
- final long oldGlobalAlertBytes = mGlobalAlertBytes;
-
- // update and persist if beyond new thresholds
- final long currentTime = mClock.millis();
- synchronized (mStatsLock) {
- if (!mSystemReady) return;
-
- updatePersistThresholdsLocked();
-
- mDevRecorder.maybePersistLocked(currentTime);
- mXtRecorder.maybePersistLocked(currentTime);
- mUidRecorder.maybePersistLocked(currentTime);
- mUidTagRecorder.maybePersistLocked(currentTime);
- }
-
- if (oldGlobalAlertBytes != mGlobalAlertBytes) {
- registerGlobalAlert();
- }
- }
-
- @Override
- public DataUsageRequest registerUsageCallback(String callingPackage,
- DataUsageRequest request, Messenger messenger, IBinder binder) {
- Objects.requireNonNull(callingPackage, "calling package is null");
- Objects.requireNonNull(request, "DataUsageRequest is null");
- Objects.requireNonNull(request.template, "NetworkTemplate is null");
- Objects.requireNonNull(messenger, "messenger is null");
- Objects.requireNonNull(binder, "binder is null");
-
- int callingUid = Binder.getCallingUid();
- @NetworkStatsAccess.Level int accessLevel = checkAccessLevel(callingPackage);
- DataUsageRequest normalizedRequest;
- final long token = Binder.clearCallingIdentity();
- try {
- normalizedRequest = mStatsObservers.register(request, messenger, binder,
- callingUid, accessLevel);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
-
- // Create baseline stats
- mHandler.sendMessage(mHandler.obtainMessage(MSG_PERFORM_POLL));
-
- return normalizedRequest;
- }
-
- @Override
- public void unregisterUsageRequest(DataUsageRequest request) {
- Objects.requireNonNull(request, "DataUsageRequest is null");
-
- int callingUid = Binder.getCallingUid();
- final long token = Binder.clearCallingIdentity();
- try {
- mStatsObservers.unregister(request, callingUid);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- @Override
- public long getUidStats(int uid, int type) {
- final int callingUid = Binder.getCallingUid();
- if (callingUid != android.os.Process.SYSTEM_UID && callingUid != uid) {
- return UNSUPPORTED;
- }
- return nativeGetUidStat(uid, type, checkBpfStatsEnable());
- }
-
- @Override
- public long getIfaceStats(@NonNull String iface, int type) {
- Objects.requireNonNull(iface);
- long nativeIfaceStats = nativeGetIfaceStat(iface, type, checkBpfStatsEnable());
- if (nativeIfaceStats == -1) {
- return nativeIfaceStats;
- } else {
- // When tethering offload is in use, nativeIfaceStats does not contain usage from
- // offload, add it back here. Note that the included statistics might be stale
- // since polling newest stats from hardware might impact system health and not
- // suitable for TrafficStats API use cases.
- return nativeIfaceStats + getProviderIfaceStats(iface, type);
- }
- }
-
- @Override
- public long getTotalStats(int type) {
- long nativeTotalStats = nativeGetTotalStat(type, checkBpfStatsEnable());
- if (nativeTotalStats == -1) {
- return nativeTotalStats;
- } else {
- // Refer to comment in getIfaceStats
- return nativeTotalStats + getProviderIfaceStats(IFACE_ALL, type);
- }
- }
-
- private long getProviderIfaceStats(@Nullable String iface, int type) {
- final NetworkStats providerSnapshot = getNetworkStatsFromProviders(STATS_PER_IFACE);
- final HashSet<String> limitIfaces;
- if (iface == IFACE_ALL) {
- limitIfaces = null;
- } else {
- limitIfaces = new HashSet<>();
- limitIfaces.add(iface);
- }
- final NetworkStats.Entry entry = providerSnapshot.getTotal(null, limitIfaces);
- switch (type) {
- case TrafficStats.TYPE_RX_BYTES:
- return entry.rxBytes;
- case TrafficStats.TYPE_RX_PACKETS:
- return entry.rxPackets;
- case TrafficStats.TYPE_TX_BYTES:
- return entry.txBytes;
- case TrafficStats.TYPE_TX_PACKETS:
- return entry.txPackets;
- default:
- return 0;
- }
- }
-
- private boolean checkBpfStatsEnable() {
- return mUseBpfTrafficStats;
- }
-
- /**
- * Update {@link NetworkStatsRecorder} and {@link #mGlobalAlertBytes} to
- * reflect current {@link #mPersistThreshold} value. Always defers to
- * {@link Global} values when defined.
- */
- @GuardedBy("mStatsLock")
- private void updatePersistThresholdsLocked() {
- mDevRecorder.setPersistThreshold(mSettings.getDevPersistBytes(mPersistThreshold));
- mXtRecorder.setPersistThreshold(mSettings.getXtPersistBytes(mPersistThreshold));
- mUidRecorder.setPersistThreshold(mSettings.getUidPersistBytes(mPersistThreshold));
- mUidTagRecorder.setPersistThreshold(mSettings.getUidTagPersistBytes(mPersistThreshold));
- mGlobalAlertBytes = mSettings.getGlobalAlertBytes(mPersistThreshold);
- }
-
- /**
- * Receiver that watches for {@link Tethering} to claim interface pairs.
- */
- private BroadcastReceiver mTetherReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- performPoll(FLAG_PERSIST_NETWORK);
- }
- };
-
- private BroadcastReceiver mPollReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- // on background handler thread, and verified UPDATE_DEVICE_STATS
- // permission above.
- performPoll(FLAG_PERSIST_ALL);
-
- // verify that we're watching global alert
- registerGlobalAlert();
- }
- };
-
- private BroadcastReceiver mRemovedReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- // on background handler thread, and UID_REMOVED is protected
- // broadcast.
-
- final int uid = intent.getIntExtra(EXTRA_UID, -1);
- if (uid == -1) return;
-
- synchronized (mStatsLock) {
- mWakeLock.acquire();
- try {
- removeUidsLocked(uid);
- } finally {
- mWakeLock.release();
- }
- }
- }
- };
-
- private BroadcastReceiver mUserReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- // On background handler thread, and USER_REMOVED is protected
- // broadcast.
-
- final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
- if (userId == -1) return;
-
- synchronized (mStatsLock) {
- mWakeLock.acquire();
- try {
- removeUserLocked(userId);
- } finally {
- mWakeLock.release();
- }
- }
- }
- };
-
- private BroadcastReceiver mShutdownReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- // SHUTDOWN is protected broadcast.
- synchronized (mStatsLock) {
- shutdownLocked();
- }
- }
- };
-
- /**
- * Observer that watches for {@link INetworkManagementService} alerts.
- */
- private final INetworkManagementEventObserver mAlertObserver = new BaseNetworkObserver() {
- @Override
- public void limitReached(String limitName, String iface) {
- // only someone like NMS should be calling us
- NetworkStack.checkNetworkStackPermission(mContext);
-
- if (LIMIT_GLOBAL_ALERT.equals(limitName)) {
- // kick off background poll to collect network stats unless there is already
- // such a call pending; UID stats are handled during normal polling interval.
- if (!mHandler.hasMessages(MSG_PERFORM_POLL_REGISTER_ALERT)) {
- mHandler.sendEmptyMessageDelayed(MSG_PERFORM_POLL_REGISTER_ALERT,
- mSettings.getPollDelay());
- }
- }
- }
- };
-
- /**
- * Handle collapsed RAT type changed event.
- */
- @VisibleForTesting
- public void handleOnCollapsedRatTypeChanged() {
- // Protect service from frequently updating. Remove pending messages if any.
- mHandler.removeMessages(MSG_NOTIFY_NETWORK_STATUS);
- mHandler.sendMessageDelayed(
- mHandler.obtainMessage(MSG_NOTIFY_NETWORK_STATUS), mSettings.getPollDelay());
- }
-
- private void handleNotifyNetworkStatus(
- Network[] defaultNetworks,
- NetworkStateSnapshot[] snapshots,
- String activeIface) {
- synchronized (mStatsLock) {
- mWakeLock.acquire();
- try {
- mActiveIface = activeIface;
- handleNotifyNetworkStatusLocked(defaultNetworks, snapshots);
- } finally {
- mWakeLock.release();
- }
- }
- }
-
- /**
- * Inspect all current {@link NetworkStateSnapshot}s to derive mapping from {@code iface} to
- * {@link NetworkStatsHistory}. When multiple networks are active on a single {@code iface},
- * they are combined under a single {@link NetworkIdentitySet}.
- */
- @GuardedBy("mStatsLock")
- private void handleNotifyNetworkStatusLocked(@NonNull Network[] defaultNetworks,
- @NonNull NetworkStateSnapshot[] snapshots) {
- if (!mSystemReady) return;
- if (LOGV) Slog.v(TAG, "handleNotifyNetworkStatusLocked()");
-
- // take one last stats snapshot before updating iface mapping. this
- // isn't perfect, since the kernel may already be counting traffic from
- // the updated network.
-
- // poll, but only persist network stats to keep codepath fast. UID stats
- // will be persisted during next alarm poll event.
- performPollLocked(FLAG_PERSIST_NETWORK);
-
- // Rebuild active interfaces based on connected networks
- mActiveIfaces.clear();
- mActiveUidIfaces.clear();
- // Update the list of default networks.
- mDefaultNetworks = defaultNetworks;
-
- mLastNetworkStateSnapshots = snapshots;
-
- final boolean combineSubtypeEnabled = mSettings.getCombineSubtypeEnabled();
- final ArraySet<String> mobileIfaces = new ArraySet<>();
- for (NetworkStateSnapshot snapshot : snapshots) {
- final int displayTransport =
- getDisplayTransport(snapshot.getNetworkCapabilities().getTransportTypes());
- final boolean isMobile = (NetworkCapabilities.TRANSPORT_CELLULAR == displayTransport);
- final boolean isDefault = ArrayUtils.contains(mDefaultNetworks, snapshot.getNetwork());
- final int subType = combineSubtypeEnabled ? SUBTYPE_COMBINED
- : getSubTypeForStateSnapshot(snapshot);
- final NetworkIdentity ident = NetworkIdentity.buildNetworkIdentity(mContext, snapshot,
- isDefault, subType);
-
- // Traffic occurring on the base interface is always counted for
- // both total usage and UID details.
- final String baseIface = snapshot.getLinkProperties().getInterfaceName();
- if (baseIface != null) {
- findOrCreateNetworkIdentitySet(mActiveIfaces, baseIface).add(ident);
- findOrCreateNetworkIdentitySet(mActiveUidIfaces, baseIface).add(ident);
-
- // Build a separate virtual interface for VT (Video Telephony) data usage.
- // Only do this when IMS is not metered, but VT is metered.
- // If IMS is metered, then the IMS network usage has already included VT usage.
- // VT is considered always metered in framework's layer. If VT is not metered
- // per carrier's policy, modem will report 0 usage for VT calls.
- if (snapshot.getNetworkCapabilities().hasCapability(
- NetworkCapabilities.NET_CAPABILITY_IMS) && !ident.getMetered()) {
-
- // Copy the identify from IMS one but mark it as metered.
- NetworkIdentity vtIdent = new NetworkIdentity(ident.getType(),
- ident.getSubType(), ident.getSubscriberId(), ident.getNetworkId(),
- ident.getRoaming(), true /* metered */,
- true /* onDefaultNetwork */, ident.getOemManaged());
- final String ifaceVt = IFACE_VT + getSubIdForMobile(snapshot);
- findOrCreateNetworkIdentitySet(mActiveIfaces, ifaceVt).add(vtIdent);
- findOrCreateNetworkIdentitySet(mActiveUidIfaces, ifaceVt).add(vtIdent);
- }
-
- if (isMobile) {
- mobileIfaces.add(baseIface);
- }
- }
-
- // Traffic occurring on stacked interfaces is usually clatd.
- //
- // UID stats are always counted on the stacked interface and never on the base
- // interface, because the packets on the base interface do not actually match
- // application sockets (they're not IPv4) and thus the app uid is not known.
- // For receive this is obvious: packets must be translated from IPv6 to IPv4
- // before the application socket can be found.
- // For transmit: either they go through the clat daemon which by virtue of going
- // through userspace strips the original socket association during the IPv4 to
- // IPv6 translation process, or they are offloaded by eBPF, which doesn't:
- // However, on an ebpf device the accounting is done in cgroup ebpf hooks,
- // which don't trigger again post ebpf translation.
- // (as such stats accounted to the clat uid are ignored)
- //
- // Interface stats are more complicated.
- //
- // eBPF offloaded 464xlat'ed packets never hit base interface ip6tables, and thus
- // *all* statistics are collected by iptables on the stacked v4-* interface.
- //
- // Additionally for ingress all packets bound for the clat IPv6 address are dropped
- // in ip6tables raw prerouting and thus even non-offloaded packets are only
- // accounted for on the stacked interface.
- //
- // For egress, packets subject to eBPF offload never appear on the base interface
- // and only appear on the stacked interface. Thus to ensure packets increment
- // interface stats, we must collate data from stacked interfaces. For xt_qtaguid
- // (or non eBPF offloaded) TX they would appear on both, however egress interface
- // accounting is explicitly bypassed for traffic from the clat uid.
- //
- // TODO: This code might be combined to above code.
- for (String iface : snapshot.getLinkProperties().getAllInterfaceNames()) {
- // baseIface has been handled, so ignore it.
- if (TextUtils.equals(baseIface, iface)) continue;
- if (iface != null) {
- findOrCreateNetworkIdentitySet(mActiveIfaces, iface).add(ident);
- findOrCreateNetworkIdentitySet(mActiveUidIfaces, iface).add(ident);
- if (isMobile) {
- mobileIfaces.add(iface);
- }
-
- mStatsFactory.noteStackedIface(iface, baseIface);
- }
- }
- }
-
- mMobileIfaces = mobileIfaces.toArray(new String[0]);
- // TODO (b/192758557): Remove debug log.
- if (ArrayUtils.contains(mMobileIfaces, null)) {
- throw new NullPointerException(
- "null element in mMobileIfaces: " + Arrays.toString(mMobileIfaces));
- }
- }
-
- private static int getSubIdForMobile(@NonNull NetworkStateSnapshot state) {
- if (!state.getNetworkCapabilities().hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {
- throw new IllegalArgumentException("Mobile state need capability TRANSPORT_CELLULAR");
- }
-
- final NetworkSpecifier spec = state.getNetworkCapabilities().getNetworkSpecifier();
- if (spec instanceof TelephonyNetworkSpecifier) {
- return ((TelephonyNetworkSpecifier) spec).getSubscriptionId();
- } else {
- Slog.wtf(TAG, "getSubIdForState invalid NetworkSpecifier");
- return INVALID_SUBSCRIPTION_ID;
- }
- }
-
- /**
- * For networks with {@code TRANSPORT_CELLULAR}, get subType that was obtained through
- * {@link PhoneStateListener}. Otherwise, return 0 given that other networks with different
- * transport types do not actually fill this value.
- */
- private int getSubTypeForStateSnapshot(@NonNull NetworkStateSnapshot state) {
- if (!state.getNetworkCapabilities().hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {
- return 0;
- }
-
- return mNetworkStatsSubscriptionsMonitor.getRatTypeForSubscriberId(state.getSubscriberId());
- }
-
- private static <K> NetworkIdentitySet findOrCreateNetworkIdentitySet(
- ArrayMap<K, NetworkIdentitySet> map, K key) {
- NetworkIdentitySet ident = map.get(key);
- if (ident == null) {
- ident = new NetworkIdentitySet();
- map.put(key, ident);
- }
- return ident;
- }
-
- @GuardedBy("mStatsLock")
- private void recordSnapshotLocked(long currentTime) throws RemoteException {
- // snapshot and record current counters; read UID stats first to
- // avoid over counting dev stats.
- Trace.traceBegin(TRACE_TAG_NETWORK, "snapshotUid");
- final NetworkStats uidSnapshot = getNetworkStatsUidDetail(INTERFACES_ALL);
- Trace.traceEnd(TRACE_TAG_NETWORK);
- Trace.traceBegin(TRACE_TAG_NETWORK, "snapshotXt");
- final NetworkStats xtSnapshot = readNetworkStatsSummaryXt();
- Trace.traceEnd(TRACE_TAG_NETWORK);
- Trace.traceBegin(TRACE_TAG_NETWORK, "snapshotDev");
- final NetworkStats devSnapshot = readNetworkStatsSummaryDev();
- Trace.traceEnd(TRACE_TAG_NETWORK);
-
- // Snapshot for dev/xt stats from all custom stats providers. Counts per-interface data
- // from stats providers that isn't already counted by dev and XT stats.
- Trace.traceBegin(TRACE_TAG_NETWORK, "snapshotStatsProvider");
- final NetworkStats providersnapshot = getNetworkStatsFromProviders(STATS_PER_IFACE);
- Trace.traceEnd(TRACE_TAG_NETWORK);
- xtSnapshot.combineAllValues(providersnapshot);
- devSnapshot.combineAllValues(providersnapshot);
-
- // For xt/dev, we pass a null VPN array because usage is aggregated by UID, so VPN traffic
- // can't be reattributed to responsible apps.
- Trace.traceBegin(TRACE_TAG_NETWORK, "recordDev");
- mDevRecorder.recordSnapshotLocked(devSnapshot, mActiveIfaces, currentTime);
- Trace.traceEnd(TRACE_TAG_NETWORK);
- Trace.traceBegin(TRACE_TAG_NETWORK, "recordXt");
- mXtRecorder.recordSnapshotLocked(xtSnapshot, mActiveIfaces, currentTime);
- Trace.traceEnd(TRACE_TAG_NETWORK);
-
- // For per-UID stats, pass the VPN info so VPN traffic is reattributed to responsible apps.
- Trace.traceBegin(TRACE_TAG_NETWORK, "recordUid");
- mUidRecorder.recordSnapshotLocked(uidSnapshot, mActiveUidIfaces, currentTime);
- Trace.traceEnd(TRACE_TAG_NETWORK);
- Trace.traceBegin(TRACE_TAG_NETWORK, "recordUidTag");
- mUidTagRecorder.recordSnapshotLocked(uidSnapshot, mActiveUidIfaces, currentTime);
- Trace.traceEnd(TRACE_TAG_NETWORK);
-
- // We need to make copies of member fields that are sent to the observer to avoid
- // a race condition between the service handler thread and the observer's
- mStatsObservers.updateStats(xtSnapshot, uidSnapshot, new ArrayMap<>(mActiveIfaces),
- new ArrayMap<>(mActiveUidIfaces), currentTime);
- }
-
- /**
- * Bootstrap initial stats snapshot, usually during {@link #systemReady()}
- * so we have baseline values without double-counting.
- */
- @GuardedBy("mStatsLock")
- private void bootstrapStatsLocked() {
- final long currentTime = mClock.millis();
-
- try {
- recordSnapshotLocked(currentTime);
- } catch (IllegalStateException e) {
- Slog.w(TAG, "problem reading network stats: " + e);
- } catch (RemoteException e) {
- // ignored; service lives in system_server
- }
- }
-
- private void performPoll(int flags) {
- synchronized (mStatsLock) {
- mWakeLock.acquire();
-
- try {
- performPollLocked(flags);
- } finally {
- mWakeLock.release();
- }
- }
- }
-
- /**
- * Periodic poll operation, reading current statistics and recording into
- * {@link NetworkStatsHistory}.
- */
- @GuardedBy("mStatsLock")
- private void performPollLocked(int flags) {
- if (!mSystemReady) return;
- if (LOGV) Slog.v(TAG, "performPollLocked(flags=0x" + Integer.toHexString(flags) + ")");
- Trace.traceBegin(TRACE_TAG_NETWORK, "performPollLocked");
-
- final boolean persistNetwork = (flags & FLAG_PERSIST_NETWORK) != 0;
- final boolean persistUid = (flags & FLAG_PERSIST_UID) != 0;
- final boolean persistForce = (flags & FLAG_PERSIST_FORCE) != 0;
-
- performPollFromProvidersLocked();
-
- // TODO: consider marking "untrusted" times in historical stats
- final long currentTime = mClock.millis();
-
- try {
- recordSnapshotLocked(currentTime);
- } catch (IllegalStateException e) {
- Log.wtf(TAG, "problem reading network stats", e);
- return;
- } catch (RemoteException e) {
- // ignored; service lives in system_server
- return;
- }
-
- // persist any pending data depending on requested flags
- Trace.traceBegin(TRACE_TAG_NETWORK, "[persisting]");
- if (persistForce) {
- mDevRecorder.forcePersistLocked(currentTime);
- mXtRecorder.forcePersistLocked(currentTime);
- mUidRecorder.forcePersistLocked(currentTime);
- mUidTagRecorder.forcePersistLocked(currentTime);
- } else {
- if (persistNetwork) {
- mDevRecorder.maybePersistLocked(currentTime);
- mXtRecorder.maybePersistLocked(currentTime);
- }
- if (persistUid) {
- mUidRecorder.maybePersistLocked(currentTime);
- mUidTagRecorder.maybePersistLocked(currentTime);
- }
- }
- Trace.traceEnd(TRACE_TAG_NETWORK);
-
- if (mSettings.getSampleEnabled()) {
- // sample stats after each full poll
- performSampleLocked();
- }
-
- // finally, dispatch updated event to any listeners
- mHandler.sendMessage(mHandler.obtainMessage(MSG_BROADCAST_NETWORK_STATS_UPDATED));
-
- Trace.traceEnd(TRACE_TAG_NETWORK);
- }
-
- @GuardedBy("mStatsLock")
- private void performPollFromProvidersLocked() {
- // Request asynchronous stats update from all providers for next poll. And wait a bit of
- // time to allow providers report-in given that normally binder call should be fast. Note
- // that size of list might be changed because addition/removing at the same time. For
- // addition, the stats of the missed provider can only be collected in next poll;
- // for removal, wait might take up to MAX_STATS_PROVIDER_POLL_WAIT_TIME_MS
- // once that happened.
- // TODO: request with a valid token.
- Trace.traceBegin(TRACE_TAG_NETWORK, "provider.requestStatsUpdate");
- final int registeredCallbackCount = mStatsProviderCbList.size();
- mStatsProviderSem.drainPermits();
- invokeForAllStatsProviderCallbacks(
- (cb) -> cb.mProvider.onRequestStatsUpdate(0 /* unused */));
- try {
- mStatsProviderSem.tryAcquire(registeredCallbackCount,
- MAX_STATS_PROVIDER_POLL_WAIT_TIME_MS, TimeUnit.MILLISECONDS);
- } catch (InterruptedException e) {
- // Strictly speaking it's possible a provider happened to deliver between the timeout
- // and the log, and that doesn't matter too much as this is just a debug log.
- Log.d(TAG, "requestStatsUpdate - providers responded "
- + mStatsProviderSem.availablePermits()
- + "/" + registeredCallbackCount + " : " + e);
- }
- Trace.traceEnd(TRACE_TAG_NETWORK);
- }
-
- /**
- * Sample recent statistics summary into {@link EventLog}.
- */
- @GuardedBy("mStatsLock")
- private void performSampleLocked() {
- // TODO: migrate trustedtime fixes to separate binary log events
- final long currentTime = mClock.millis();
-
- NetworkTemplate template;
- NetworkStats.Entry devTotal;
- NetworkStats.Entry xtTotal;
- NetworkStats.Entry uidTotal;
-
- // collect mobile sample
- template = buildTemplateMobileWildcard();
- devTotal = mDevRecorder.getTotalSinceBootLocked(template);
- xtTotal = mXtRecorder.getTotalSinceBootLocked(template);
- uidTotal = mUidRecorder.getTotalSinceBootLocked(template);
-
- EventLogTags.writeNetstatsMobileSample(
- devTotal.rxBytes, devTotal.rxPackets, devTotal.txBytes, devTotal.txPackets,
- xtTotal.rxBytes, xtTotal.rxPackets, xtTotal.txBytes, xtTotal.txPackets,
- uidTotal.rxBytes, uidTotal.rxPackets, uidTotal.txBytes, uidTotal.txPackets,
- currentTime);
-
- // collect wifi sample
- template = buildTemplateWifiWildcard();
- devTotal = mDevRecorder.getTotalSinceBootLocked(template);
- xtTotal = mXtRecorder.getTotalSinceBootLocked(template);
- uidTotal = mUidRecorder.getTotalSinceBootLocked(template);
-
- EventLogTags.writeNetstatsWifiSample(
- devTotal.rxBytes, devTotal.rxPackets, devTotal.txBytes, devTotal.txPackets,
- xtTotal.rxBytes, xtTotal.rxPackets, xtTotal.txBytes, xtTotal.txPackets,
- uidTotal.rxBytes, uidTotal.rxPackets, uidTotal.txBytes, uidTotal.txPackets,
- currentTime);
- }
-
- /**
- * Clean up {@link #mUidRecorder} after UID is removed.
- */
- @GuardedBy("mStatsLock")
- private void removeUidsLocked(int... uids) {
- if (LOGV) Slog.v(TAG, "removeUidsLocked() for UIDs " + Arrays.toString(uids));
-
- // Perform one last poll before removing
- performPollLocked(FLAG_PERSIST_ALL);
-
- mUidRecorder.removeUidsLocked(uids);
- mUidTagRecorder.removeUidsLocked(uids);
-
- // Clear kernel stats associated with UID
- for (int uid : uids) {
- resetKernelUidStats(uid);
- }
- }
-
- /**
- * Clean up {@link #mUidRecorder} after user is removed.
- */
- @GuardedBy("mStatsLock")
- private void removeUserLocked(int userId) {
- if (LOGV) Slog.v(TAG, "removeUserLocked() for userId=" + userId);
-
- // Build list of UIDs that we should clean up
- int[] uids = new int[0];
- final List<ApplicationInfo> apps = mContext.getPackageManager().getInstalledApplications(
- PackageManager.MATCH_ANY_USER
- | PackageManager.MATCH_DISABLED_COMPONENTS);
- for (ApplicationInfo app : apps) {
- final int uid = UserHandle.getUid(userId, app.uid);
- uids = ArrayUtils.appendInt(uids, uid);
- }
-
- removeUidsLocked(uids);
- }
-
- private class NetworkStatsManagerInternalImpl extends NetworkStatsManagerInternal {
- @Override
- public long getNetworkTotalBytes(NetworkTemplate template, long start, long end) {
- Trace.traceBegin(TRACE_TAG_NETWORK, "getNetworkTotalBytes");
- try {
- return NetworkStatsService.this.getNetworkTotalBytes(template, start, end);
- } finally {
- Trace.traceEnd(TRACE_TAG_NETWORK);
- }
- }
-
- @Override
- public NetworkStats getNetworkUidBytes(NetworkTemplate template, long start, long end) {
- Trace.traceBegin(TRACE_TAG_NETWORK, "getNetworkUidBytes");
- try {
- return NetworkStatsService.this.getNetworkUidBytes(template, start, end);
- } finally {
- Trace.traceEnd(TRACE_TAG_NETWORK);
- }
- }
-
- @Override
- public void setUidForeground(int uid, boolean uidForeground) {
- NetworkStatsService.this.setUidForeground(uid, uidForeground);
- }
-
- @Override
- public void advisePersistThreshold(long thresholdBytes) {
- NetworkStatsService.this.advisePersistThreshold(thresholdBytes);
- }
-
- @Override
- public void forceUpdate() {
- NetworkStatsService.this.forceUpdate();
- }
-
- @Override
- public void setStatsProviderWarningAndLimitAsync(
- @NonNull String iface, long warning, long limit) {
- if (LOGV) {
- Slog.v(TAG, "setStatsProviderWarningAndLimitAsync("
- + iface + "," + warning + "," + limit + ")");
- }
- invokeForAllStatsProviderCallbacks((cb) -> cb.mProvider.onSetWarningAndLimit(iface,
- warning, limit));
- }
- }
-
- @Override
- protected void dump(FileDescriptor fd, PrintWriter rawWriter, String[] args) {
- if (!DumpUtils.checkDumpPermission(mContext, TAG, rawWriter)) return;
-
- long duration = DateUtils.DAY_IN_MILLIS;
- final HashSet<String> argSet = new HashSet<String>();
- for (String arg : args) {
- argSet.add(arg);
-
- if (arg.startsWith("--duration=")) {
- try {
- duration = Long.parseLong(arg.substring(11));
- } catch (NumberFormatException ignored) {
- }
- }
- }
-
- // usage: dumpsys netstats --full --uid --tag --poll --checkin
- final boolean poll = argSet.contains("--poll") || argSet.contains("poll");
- final boolean checkin = argSet.contains("--checkin");
- final boolean fullHistory = argSet.contains("--full") || argSet.contains("full");
- final boolean includeUid = argSet.contains("--uid") || argSet.contains("detail");
- final boolean includeTag = argSet.contains("--tag") || argSet.contains("detail");
-
- final IndentingPrintWriter pw = new IndentingPrintWriter(rawWriter, " ");
-
- synchronized (mStatsLock) {
- if (args.length > 0 && "--proto".equals(args[0])) {
- // In this case ignore all other arguments.
- dumpProtoLocked(fd);
- return;
- }
-
- if (poll) {
- performPollLocked(FLAG_PERSIST_ALL | FLAG_PERSIST_FORCE);
- pw.println("Forced poll");
- return;
- }
-
- if (checkin) {
- final long end = System.currentTimeMillis();
- final long start = end - duration;
-
- pw.print("v1,");
- pw.print(start / SECOND_IN_MILLIS); pw.print(',');
- pw.print(end / SECOND_IN_MILLIS); pw.println();
-
- pw.println("xt");
- mXtRecorder.dumpCheckin(rawWriter, start, end);
-
- if (includeUid) {
- pw.println("uid");
- mUidRecorder.dumpCheckin(rawWriter, start, end);
- }
- if (includeTag) {
- pw.println("tag");
- mUidTagRecorder.dumpCheckin(rawWriter, start, end);
- }
- return;
- }
-
- pw.println("Configs:");
- pw.increaseIndent();
- pw.printPair(NETSTATS_COMBINE_SUBTYPE_ENABLED, mSettings.getCombineSubtypeEnabled());
- pw.println();
- pw.decreaseIndent();
-
- pw.println("Active interfaces:");
- pw.increaseIndent();
- for (int i = 0; i < mActiveIfaces.size(); i++) {
- pw.printPair("iface", mActiveIfaces.keyAt(i));
- pw.printPair("ident", mActiveIfaces.valueAt(i));
- pw.println();
- }
- pw.decreaseIndent();
-
- pw.println("Active UID interfaces:");
- pw.increaseIndent();
- for (int i = 0; i < mActiveUidIfaces.size(); i++) {
- pw.printPair("iface", mActiveUidIfaces.keyAt(i));
- pw.printPair("ident", mActiveUidIfaces.valueAt(i));
- pw.println();
- }
- pw.decreaseIndent();
-
- // Get the top openSession callers
- final SparseIntArray calls;
- synchronized (mOpenSessionCallsPerUid) {
- calls = mOpenSessionCallsPerUid.clone();
- }
-
- final int N = calls.size();
- final long[] values = new long[N];
- for (int j = 0; j < N; j++) {
- values[j] = ((long) calls.valueAt(j) << 32) | calls.keyAt(j);
- }
- Arrays.sort(values);
-
- pw.println("Top openSession callers (uid=count):");
- pw.increaseIndent();
- final int end = Math.max(0, N - DUMP_STATS_SESSION_COUNT);
- for (int j = N - 1; j >= end; j--) {
- final int uid = (int) (values[j] & 0xffffffff);
- final int count = (int) (values[j] >> 32);
- pw.print(uid); pw.print("="); pw.println(count);
- }
- pw.decreaseIndent();
- pw.println();
-
- pw.println("Stats Providers:");
- pw.increaseIndent();
- invokeForAllStatsProviderCallbacks((cb) -> {
- pw.println(cb.mTag + " Xt:");
- pw.increaseIndent();
- pw.print(cb.getCachedStats(STATS_PER_IFACE).toString());
- pw.decreaseIndent();
- if (includeUid) {
- pw.println(cb.mTag + " Uid:");
- pw.increaseIndent();
- pw.print(cb.getCachedStats(STATS_PER_UID).toString());
- pw.decreaseIndent();
- }
- });
- pw.decreaseIndent();
-
- pw.println("Dev stats:");
- pw.increaseIndent();
- mDevRecorder.dumpLocked(pw, fullHistory);
- pw.decreaseIndent();
-
- pw.println("Xt stats:");
- pw.increaseIndent();
- mXtRecorder.dumpLocked(pw, fullHistory);
- pw.decreaseIndent();
-
- if (includeUid) {
- pw.println("UID stats:");
- pw.increaseIndent();
- mUidRecorder.dumpLocked(pw, fullHistory);
- pw.decreaseIndent();
- }
-
- if (includeTag) {
- pw.println("UID tag stats:");
- pw.increaseIndent();
- mUidTagRecorder.dumpLocked(pw, fullHistory);
- pw.decreaseIndent();
- }
- }
- }
-
- @GuardedBy("mStatsLock")
- private void dumpProtoLocked(FileDescriptor fd) {
- final ProtoOutputStream proto = new ProtoOutputStream(fd);
-
- // TODO Right now it writes all history. Should it limit to the "since-boot" log?
-
- dumpInterfaces(proto, NetworkStatsServiceDumpProto.ACTIVE_INTERFACES, mActiveIfaces);
- dumpInterfaces(proto, NetworkStatsServiceDumpProto.ACTIVE_UID_INTERFACES, mActiveUidIfaces);
- mDevRecorder.dumpDebugLocked(proto, NetworkStatsServiceDumpProto.DEV_STATS);
- mXtRecorder.dumpDebugLocked(proto, NetworkStatsServiceDumpProto.XT_STATS);
- mUidRecorder.dumpDebugLocked(proto, NetworkStatsServiceDumpProto.UID_STATS);
- mUidTagRecorder.dumpDebugLocked(proto, NetworkStatsServiceDumpProto.UID_TAG_STATS);
-
- proto.flush();
- }
-
- private static void dumpInterfaces(ProtoOutputStream proto, long tag,
- ArrayMap<String, NetworkIdentitySet> ifaces) {
- for (int i = 0; i < ifaces.size(); i++) {
- final long start = proto.start(tag);
-
- proto.write(NetworkInterfaceProto.INTERFACE, ifaces.keyAt(i));
- ifaces.valueAt(i).dumpDebug(proto, NetworkInterfaceProto.IDENTITIES);
-
- proto.end(start);
- }
- }
-
- private NetworkStats readNetworkStatsSummaryDev() {
- try {
- return mStatsFactory.readNetworkStatsSummaryDev();
- } catch (IOException e) {
- throw new IllegalStateException(e);
- }
- }
-
- private NetworkStats readNetworkStatsSummaryXt() {
- try {
- return mStatsFactory.readNetworkStatsSummaryXt();
- } catch (IOException e) {
- throw new IllegalStateException(e);
- }
- }
-
- private NetworkStats readNetworkStatsUidDetail(int uid, String[] ifaces, int tag) {
- try {
- return mStatsFactory.readNetworkStatsDetail(uid, ifaces, tag);
- } catch (IOException e) {
- throw new IllegalStateException(e);
- }
- }
-
- /**
- * Return snapshot of current UID statistics, including any
- * {@link TrafficStats#UID_TETHERING}, video calling data usage, and {@link #mUidOperations}
- * values.
- *
- * @param ifaces A list of interfaces the stats should be restricted to, or
- * {@link NetworkStats#INTERFACES_ALL}.
- */
- private NetworkStats getNetworkStatsUidDetail(String[] ifaces)
- throws RemoteException {
- final NetworkStats uidSnapshot = readNetworkStatsUidDetail(UID_ALL, ifaces, TAG_ALL);
-
- // fold tethering stats and operations into uid snapshot
- final NetworkStats tetherSnapshot = getNetworkStatsTethering(STATS_PER_UID);
- tetherSnapshot.filter(UID_ALL, ifaces, TAG_ALL);
- mStatsFactory.apply464xlatAdjustments(uidSnapshot, tetherSnapshot);
- uidSnapshot.combineAllValues(tetherSnapshot);
-
- // get a stale copy of uid stats snapshot provided by providers.
- final NetworkStats providerStats = getNetworkStatsFromProviders(STATS_PER_UID);
- providerStats.filter(UID_ALL, ifaces, TAG_ALL);
- mStatsFactory.apply464xlatAdjustments(uidSnapshot, providerStats);
- uidSnapshot.combineAllValues(providerStats);
-
- uidSnapshot.combineAllValues(mUidOperations);
-
- return uidSnapshot;
- }
-
- /**
- * Return snapshot of current non-offloaded tethering statistics. Will return empty
- * {@link NetworkStats} if any problems are encountered, or queried by {@code STATS_PER_IFACE}
- * since it is already included by {@link #nativeGetIfaceStat}.
- * See {@code OffloadTetheringStatsProvider} for offloaded tethering stats.
- */
- // TODO: Remove this by implementing {@link NetworkStatsProvider} for non-offloaded
- // tethering stats.
- private NetworkStats getNetworkStatsTethering(int how) throws RemoteException {
- try {
- return mNetworkManager.getNetworkStatsTethering(how);
- } catch (IllegalStateException e) {
- Log.wtf(TAG, "problem reading network stats", e);
- return new NetworkStats(0L, 10);
- }
- }
-
- // TODO: It is copied from ConnectivityService, consider refactor these check permission
- // functions to a proper util.
- private boolean checkAnyPermissionOf(String... permissions) {
- for (String permission : permissions) {
- if (mContext.checkCallingOrSelfPermission(permission) == PERMISSION_GRANTED) {
- return true;
- }
- }
- return false;
- }
-
- private void enforceAnyPermissionOf(String... permissions) {
- if (!checkAnyPermissionOf(permissions)) {
- throw new SecurityException("Requires one of the following permissions: "
- + String.join(", ", permissions) + ".");
- }
- }
-
- /**
- * Registers a custom provider of {@link android.net.NetworkStats} to combine the network
- * statistics that cannot be seen by the kernel to system. To unregister, invoke the
- * {@code unregister()} of the returned callback.
- *
- * @param tag a human readable identifier of the custom network stats provider.
- * @param provider the {@link INetworkStatsProvider} binder corresponding to the
- * {@link NetworkStatsProvider} to be registered.
- *
- * @return a {@link INetworkStatsProviderCallback} binder
- * interface, which can be used to report events to the system.
- */
- public @NonNull INetworkStatsProviderCallback registerNetworkStatsProvider(
- @NonNull String tag, @NonNull INetworkStatsProvider provider) {
- enforceAnyPermissionOf(NETWORK_STATS_PROVIDER,
- NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
- Objects.requireNonNull(provider, "provider is null");
- Objects.requireNonNull(tag, "tag is null");
- try {
- NetworkStatsProviderCallbackImpl callback = new NetworkStatsProviderCallbackImpl(
- tag, provider, mStatsProviderSem, mAlertObserver,
- mStatsProviderCbList);
- mStatsProviderCbList.add(callback);
- Log.d(TAG, "registerNetworkStatsProvider from " + callback.mTag + " uid/pid="
- + getCallingUid() + "/" + getCallingPid());
- return callback;
- } catch (RemoteException e) {
- Log.e(TAG, "registerNetworkStatsProvider failed", e);
- }
- return null;
- }
-
- // Collect stats from local cache of providers.
- private @NonNull NetworkStats getNetworkStatsFromProviders(int how) {
- final NetworkStats ret = new NetworkStats(0L, 0);
- invokeForAllStatsProviderCallbacks((cb) -> ret.combineAllValues(cb.getCachedStats(how)));
- return ret;
- }
-
- @FunctionalInterface
- private interface ThrowingConsumer<S, T extends Throwable> {
- void accept(S s) throws T;
- }
-
- private void invokeForAllStatsProviderCallbacks(
- @NonNull ThrowingConsumer<NetworkStatsProviderCallbackImpl, RemoteException> task) {
- for (final NetworkStatsProviderCallbackImpl cb : mStatsProviderCbList) {
- try {
- task.accept(cb);
- } catch (RemoteException e) {
- Log.e(TAG, "Fail to broadcast to provider: " + cb.mTag, e);
- }
- }
- }
-
- private static class NetworkStatsProviderCallbackImpl extends INetworkStatsProviderCallback.Stub
- implements IBinder.DeathRecipient {
- @NonNull final String mTag;
-
- @NonNull final INetworkStatsProvider mProvider;
- @NonNull private final Semaphore mSemaphore;
- @NonNull final INetworkManagementEventObserver mAlertObserver;
- @NonNull final CopyOnWriteArrayList<NetworkStatsProviderCallbackImpl> mStatsProviderCbList;
-
- @NonNull private final Object mProviderStatsLock = new Object();
-
- @GuardedBy("mProviderStatsLock")
- // Track STATS_PER_IFACE and STATS_PER_UID separately.
- private final NetworkStats mIfaceStats = new NetworkStats(0L, 0);
- @GuardedBy("mProviderStatsLock")
- private final NetworkStats mUidStats = new NetworkStats(0L, 0);
-
- NetworkStatsProviderCallbackImpl(
- @NonNull String tag, @NonNull INetworkStatsProvider provider,
- @NonNull Semaphore semaphore,
- @NonNull INetworkManagementEventObserver alertObserver,
- @NonNull CopyOnWriteArrayList<NetworkStatsProviderCallbackImpl> cbList)
- throws RemoteException {
- mTag = tag;
- mProvider = provider;
- mProvider.asBinder().linkToDeath(this, 0);
- mSemaphore = semaphore;
- mAlertObserver = alertObserver;
- mStatsProviderCbList = cbList;
- }
-
- @NonNull
- public NetworkStats getCachedStats(int how) {
- synchronized (mProviderStatsLock) {
- NetworkStats stats;
- switch (how) {
- case STATS_PER_IFACE:
- stats = mIfaceStats;
- break;
- case STATS_PER_UID:
- stats = mUidStats;
- break;
- default:
- throw new IllegalArgumentException("Invalid type: " + how);
- }
- // Callers might be able to mutate the returned object. Return a defensive copy
- // instead of local reference.
- return stats.clone();
- }
- }
-
- @Override
- public void notifyStatsUpdated(int token, @Nullable NetworkStats ifaceStats,
- @Nullable NetworkStats uidStats) {
- // TODO: 1. Use token to map ifaces to correct NetworkIdentity.
- // 2. Store the difference and store it directly to the recorder.
- synchronized (mProviderStatsLock) {
- if (ifaceStats != null) mIfaceStats.combineAllValues(ifaceStats);
- if (uidStats != null) mUidStats.combineAllValues(uidStats);
- }
- mSemaphore.release();
- }
-
- @Override
- public void notifyAlertReached() throws RemoteException {
- mAlertObserver.limitReached(LIMIT_GLOBAL_ALERT, null /* unused */);
- }
-
- @Override
- public void notifyWarningOrLimitReached() {
- Log.d(TAG, mTag + ": notifyWarningOrLimitReached");
- LocalServices.getService(NetworkPolicyManagerInternal.class)
- .onStatsProviderWarningOrLimitReached(mTag);
- }
-
- @Override
- public void binderDied() {
- Log.d(TAG, mTag + ": binderDied");
- mStatsProviderCbList.remove(this);
- }
-
- @Override
- public void unregister() {
- Log.d(TAG, mTag + ": unregister");
- mStatsProviderCbList.remove(this);
- }
-
- }
-
- private void assertSystemReady() {
- if (!mSystemReady) {
- throw new IllegalStateException("System not ready");
- }
- }
-
- private class DropBoxNonMonotonicObserver implements NonMonotonicObserver<String> {
- @Override
- public void foundNonMonotonic(NetworkStats left, int leftIndex, NetworkStats right,
- int rightIndex, String cookie) {
- Log.w(TAG, "Found non-monotonic values; saving to dropbox");
-
- // record error for debugging
- final StringBuilder builder = new StringBuilder();
- builder.append("found non-monotonic " + cookie + " values at left[" + leftIndex
- + "] - right[" + rightIndex + "]\n");
- builder.append("left=").append(left).append('\n');
- builder.append("right=").append(right).append('\n');
-
- mContext.getSystemService(DropBoxManager.class).addText(TAG_NETSTATS_ERROR,
- builder.toString());
- }
-
- @Override
- public void foundNonMonotonic(
- NetworkStats stats, int statsIndex, String cookie) {
- Log.w(TAG, "Found non-monotonic values; saving to dropbox");
-
- final StringBuilder builder = new StringBuilder();
- builder.append("Found non-monotonic " + cookie + " values at [" + statsIndex + "]\n");
- builder.append("stats=").append(stats).append('\n');
-
- mContext.getSystemService(DropBoxManager.class).addText(TAG_NETSTATS_ERROR,
- builder.toString());
- }
- }
-
- /**
- * Default external settings that read from
- * {@link android.provider.Settings.Global}.
- */
- private static class DefaultNetworkStatsSettings implements NetworkStatsSettings {
- private final ContentResolver mResolver;
-
- public DefaultNetworkStatsSettings(Context context) {
- mResolver = Objects.requireNonNull(context.getContentResolver());
- // TODO: adjust these timings for production builds
- }
-
- private long getGlobalLong(String name, long def) {
- return Settings.Global.getLong(mResolver, name, def);
- }
- private boolean getGlobalBoolean(String name, boolean def) {
- final int defInt = def ? 1 : 0;
- return Settings.Global.getInt(mResolver, name, defInt) != 0;
- }
-
- @Override
- public long getPollInterval() {
- return getGlobalLong(NETSTATS_POLL_INTERVAL, 30 * MINUTE_IN_MILLIS);
- }
- @Override
- public long getPollDelay() {
- return DEFAULT_PERFORM_POLL_DELAY_MS;
- }
- @Override
- public long getGlobalAlertBytes(long def) {
- return getGlobalLong(NETSTATS_GLOBAL_ALERT_BYTES, def);
- }
- @Override
- public boolean getSampleEnabled() {
- return getGlobalBoolean(NETSTATS_SAMPLE_ENABLED, true);
- }
- @Override
- public boolean getAugmentEnabled() {
- return getGlobalBoolean(NETSTATS_AUGMENT_ENABLED, true);
- }
- @Override
- public boolean getCombineSubtypeEnabled() {
- return getGlobalBoolean(NETSTATS_COMBINE_SUBTYPE_ENABLED, false);
- }
- @Override
- public Config getDevConfig() {
- return new Config(getGlobalLong(NETSTATS_DEV_BUCKET_DURATION, HOUR_IN_MILLIS),
- getGlobalLong(NETSTATS_DEV_ROTATE_AGE, 15 * DAY_IN_MILLIS),
- getGlobalLong(NETSTATS_DEV_DELETE_AGE, 90 * DAY_IN_MILLIS));
- }
- @Override
- public Config getXtConfig() {
- return getDevConfig();
- }
- @Override
- public Config getUidConfig() {
- return new Config(getGlobalLong(NETSTATS_UID_BUCKET_DURATION, 2 * HOUR_IN_MILLIS),
- getGlobalLong(NETSTATS_UID_ROTATE_AGE, 15 * DAY_IN_MILLIS),
- getGlobalLong(NETSTATS_UID_DELETE_AGE, 90 * DAY_IN_MILLIS));
- }
- @Override
- public Config getUidTagConfig() {
- return new Config(getGlobalLong(NETSTATS_UID_TAG_BUCKET_DURATION, 2 * HOUR_IN_MILLIS),
- getGlobalLong(NETSTATS_UID_TAG_ROTATE_AGE, 5 * DAY_IN_MILLIS),
- getGlobalLong(NETSTATS_UID_TAG_DELETE_AGE, 15 * DAY_IN_MILLIS));
- }
- @Override
- public long getDevPersistBytes(long def) {
- return getGlobalLong(NETSTATS_DEV_PERSIST_BYTES, def);
- }
- @Override
- public long getXtPersistBytes(long def) {
- return getDevPersistBytes(def);
- }
- @Override
- public long getUidPersistBytes(long def) {
- return getGlobalLong(NETSTATS_UID_PERSIST_BYTES, def);
- }
- @Override
- public long getUidTagPersistBytes(long def) {
- return getGlobalLong(NETSTATS_UID_TAG_PERSIST_BYTES, def);
- }
- }
-
- private static native long nativeGetTotalStat(int type, boolean useBpfStats);
- private static native long nativeGetIfaceStat(String iface, int type, boolean useBpfStats);
- private static native long nativeGetUidStat(int uid, int type, boolean useBpfStats);
-}
diff --git a/services/core/java/com/android/server/net/NetworkStatsSubscriptionsMonitor.java b/services/core/java/com/android/server/net/NetworkStatsSubscriptionsMonitor.java
deleted file mode 100644
index 5646c752fc90..000000000000
--- a/services/core/java/com/android/server/net/NetworkStatsSubscriptionsMonitor.java
+++ /dev/null
@@ -1,243 +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 com.android.server.net;
-
-import static android.net.NetworkTemplate.NETWORK_TYPE_5G_NSA;
-import static android.net.NetworkTemplate.getCollapsedRatType;
-
-import android.annotation.NonNull;
-import android.content.Context;
-import android.os.Looper;
-import android.telephony.Annotation;
-import android.telephony.NetworkRegistrationInfo;
-import android.telephony.PhoneStateListener;
-import android.telephony.ServiceState;
-import android.telephony.SubscriptionManager;
-import android.telephony.TelephonyManager;
-import android.text.TextUtils;
-import android.util.Log;
-import android.util.Pair;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.CollectionUtils;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.CopyOnWriteArrayList;
-import java.util.concurrent.Executor;
-
-/**
- * Helper class that watches for events that are triggered per subscription.
- */
-public class NetworkStatsSubscriptionsMonitor extends
- SubscriptionManager.OnSubscriptionsChangedListener {
-
- /**
- * Interface that this monitor uses to delegate event handling to NetworkStatsService.
- */
- public interface Delegate {
- /**
- * Notify that the collapsed RAT type has been changed for any subscription. The method
- * will also be triggered for any existing sub when start and stop monitoring.
- *
- * @param subscriberId IMSI of the subscription.
- * @param collapsedRatType collapsed RAT type.
- * @see android.net.NetworkTemplate#getCollapsedRatType(int).
- */
- void onCollapsedRatTypeChanged(@NonNull String subscriberId,
- @Annotation.NetworkType int collapsedRatType);
- }
- private final Delegate mDelegate;
-
- /**
- * Receivers that watches for {@link ServiceState} changes for each subscription, to
- * monitor the transitioning between Radio Access Technology(RAT) types for each sub.
- */
- @NonNull
- private final CopyOnWriteArrayList<RatTypeListener> mRatListeners =
- new CopyOnWriteArrayList<>();
-
- @NonNull
- private final SubscriptionManager mSubscriptionManager;
- @NonNull
- private final TelephonyManager mTeleManager;
-
- @NonNull
- private final Executor mExecutor;
-
- NetworkStatsSubscriptionsMonitor(@NonNull Context context, @NonNull Looper looper,
- @NonNull Executor executor, @NonNull Delegate delegate) {
- super(looper);
- mSubscriptionManager = (SubscriptionManager) context.getSystemService(
- Context.TELEPHONY_SUBSCRIPTION_SERVICE);
- mTeleManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
- mExecutor = executor;
- mDelegate = delegate;
- }
-
- @Override
- public void onSubscriptionsChanged() {
- // Collect active subId list, hidden subId such as opportunistic subscriptions are
- // also needed to track CBRS.
- final List<Integer> newSubs = getActiveSubIdList(mSubscriptionManager);
-
- // IMSI is needed for every newly added sub. Listener stores subscriberId into it to
- // prevent binder call to telephony when querying RAT. Keep listener registration with empty
- // IMSI is meaningless since the RAT type changed is ambiguous for multi-SIM if reported
- // with empty IMSI. So filter the subs w/o a valid IMSI to prevent such registration.
- final List<Pair<Integer, String>> filteredNewSubs =
- CollectionUtils.mapNotNull(newSubs, subId -> {
- final String subscriberId = mTeleManager.getSubscriberId(subId);
- return TextUtils.isEmpty(subscriberId) ? null : new Pair(subId, subscriberId);
- });
-
- for (final Pair<Integer, String> sub : filteredNewSubs) {
- // Fully match listener with subId and IMSI, since in some rare cases, IMSI might be
- // suddenly change regardless of subId, such as switch IMSI feature in modem side.
- // If that happens, register new listener with new IMSI and remove old one later.
- if (CollectionUtils.find(mRatListeners,
- it -> it.equalsKey(sub.first, sub.second)) != null) {
- continue;
- }
-
- final RatTypeListener listener =
- new RatTypeListener(mExecutor, this, sub.first, sub.second);
- mRatListeners.add(listener);
-
- // Register listener to the telephony manager that associated with specific sub.
- mTeleManager.createForSubscriptionId(sub.first)
- .listen(listener, PhoneStateListener.LISTEN_SERVICE_STATE);
- Log.d(NetworkStatsService.TAG, "RAT type listener registered for sub " + sub.first);
- }
-
- for (final RatTypeListener listener : new ArrayList<>(mRatListeners)) {
- // If there is no subId and IMSI matched the listener, removes it.
- if (CollectionUtils.find(filteredNewSubs,
- it -> listener.equalsKey(it.first, it.second)) == null) {
- handleRemoveRatTypeListener(listener);
- }
- }
- }
-
- @NonNull
- private List<Integer> getActiveSubIdList(@NonNull SubscriptionManager subscriptionManager) {
- final ArrayList<Integer> ret = new ArrayList<>();
- final int[] ids = subscriptionManager.getCompleteActiveSubscriptionIdList();
- for (int id : ids) ret.add(id);
- return ret;
- }
-
- /**
- * Get a collapsed RatType for the given subscriberId.
- *
- * @param subscriberId the target subscriberId
- * @return collapsed RatType for the given subscriberId
- */
- public int getRatTypeForSubscriberId(@NonNull String subscriberId) {
- final RatTypeListener match = CollectionUtils.find(mRatListeners,
- it -> TextUtils.equals(subscriberId, it.mSubscriberId));
- return match != null ? match.mLastCollapsedRatType : TelephonyManager.NETWORK_TYPE_UNKNOWN;
- }
-
- /**
- * Start monitoring events that triggered per subscription.
- */
- public void start() {
- mSubscriptionManager.addOnSubscriptionsChangedListener(mExecutor, this);
- }
-
- /**
- * Unregister subscription changes and all listeners for each subscription.
- */
- public void stop() {
- mSubscriptionManager.removeOnSubscriptionsChangedListener(this);
-
- for (final RatTypeListener listener : new ArrayList<>(mRatListeners)) {
- handleRemoveRatTypeListener(listener);
- }
- }
-
- private void handleRemoveRatTypeListener(@NonNull RatTypeListener listener) {
- mTeleManager.createForSubscriptionId(listener.mSubId)
- .listen(listener, PhoneStateListener.LISTEN_NONE);
- Log.d(NetworkStatsService.TAG, "RAT type listener unregistered for sub " + listener.mSubId);
- mRatListeners.remove(listener);
-
- // Removal of subscriptions doesn't generate RAT changed event, fire it for every
- // RatTypeListener.
- mDelegate.onCollapsedRatTypeChanged(
- listener.mSubscriberId, TelephonyManager.NETWORK_TYPE_UNKNOWN);
- }
-
- static class RatTypeListener extends PhoneStateListener {
- // Unique id for the subscription. See {@link SubscriptionInfo#getSubscriptionId}.
- @NonNull
- private final int mSubId;
-
- // IMSI to identifying the corresponding network from {@link NetworkState}.
- // See {@link TelephonyManager#getSubscriberId}.
- @NonNull
- private final String mSubscriberId;
-
- private volatile int mLastCollapsedRatType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
- @NonNull
- private final NetworkStatsSubscriptionsMonitor mMonitor;
-
- RatTypeListener(@NonNull Executor executor,
- @NonNull NetworkStatsSubscriptionsMonitor monitor, int subId,
- @NonNull String subscriberId) {
- super(executor);
- mSubId = subId;
- mSubscriberId = subscriberId;
- mMonitor = monitor;
- }
-
- @Override
- public void onServiceStateChanged(@NonNull ServiceState ss) {
- // In 5G SA (Stand Alone) mode, the primary cell itself will be 5G hence telephony
- // would report RAT = 5G_NR.
- // However, in 5G NSA (Non Stand Alone) mode, the primary cell is still LTE and
- // network allocates a secondary 5G cell so telephony reports RAT = LTE along with
- // NR state as connected. In such case, attributes the data usage to NR.
- // See b/160727498.
- final boolean is5GNsa = (ss.getDataNetworkType() == TelephonyManager.NETWORK_TYPE_LTE
- || ss.getDataNetworkType() == TelephonyManager.NETWORK_TYPE_LTE_CA)
- && ss.getNrState() == NetworkRegistrationInfo.NR_STATE_CONNECTED;
-
- final int networkType =
- (is5GNsa ? NETWORK_TYPE_5G_NSA : ss.getDataNetworkType());
- final int collapsedRatType = getCollapsedRatType(networkType);
- if (collapsedRatType == mLastCollapsedRatType) return;
-
- if (NetworkStatsService.LOGD) {
- Log.d(NetworkStatsService.TAG, "subtype changed for sub(" + mSubId + "): "
- + mLastCollapsedRatType + " -> " + collapsedRatType);
- }
- mLastCollapsedRatType = collapsedRatType;
- mMonitor.mDelegate.onCollapsedRatTypeChanged(mSubscriberId, mLastCollapsedRatType);
- }
-
- @VisibleForTesting
- public int getSubId() {
- return mSubId;
- }
-
- boolean equalsKey(int subId, @NonNull String subscriberId) {
- return mSubId == subId && TextUtils.equals(mSubscriberId, subscriberId);
- }
- }
-}
diff --git a/services/core/java/com/android/server/net/OWNERS b/services/core/java/com/android/server/net/OWNERS
index 28ae6a417bd3..9c96d46f15b8 100644
--- a/services/core/java/com/android/server/net/OWNERS
+++ b/services/core/java/com/android/server/net/OWNERS
@@ -1,11 +1,6 @@
set noparent
+file:platform/packages/modules/Connectivity:master:/OWNERS_core_networking
-codewiz@google.com
-jchalard@google.com
jsharkey@android.com
-junyulai@google.com
-lorenzo@google.com
-reminv@google.com
-satk@google.com
sudheersai@google.com
yamasani@google.com
diff --git a/services/core/java/com/android/server/notification/NotificationHistoryDatabase.java b/services/core/java/com/android/server/notification/NotificationHistoryDatabase.java
index 42b7c9d388a0..8a3329913142 100644
--- a/services/core/java/com/android/server/notification/NotificationHistoryDatabase.java
+++ b/services/core/java/com/android/server/notification/NotificationHistoryDatabase.java
@@ -354,6 +354,12 @@ public class NotificationHistoryDatabase {
}
}
+ public void unregisterFileCleanupReceiver() {
+ if(mContext != null) {
+ mContext.unregisterReceiver(mFileCleanupReceiver);
+ }
+ }
+
private static long safeParseLong(String fileName) {
// AtomicFile will create copies of the numeric files with ".new" and ".bak"
// over the course of its processing. If these files still exist on boot we need to clean
diff --git a/services/core/java/com/android/server/notification/NotificationHistoryManager.java b/services/core/java/com/android/server/notification/NotificationHistoryManager.java
index 6da898acdafe..0aacd130c837 100644
--- a/services/core/java/com/android/server/notification/NotificationHistoryManager.java
+++ b/services/core/java/com/android/server/notification/NotificationHistoryManager.java
@@ -288,6 +288,7 @@ public class NotificationHistoryManager {
private void disableHistory(NotificationHistoryDatabase userHistory, @UserIdInt int userId) {
userHistory.disableHistory();
+ userHistory.unregisterFileCleanupReceiver();
mUserPendingHistoryDisables.put(userId, false);
mHistoryEnabled.put(userId, false);
diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java
index 53bf7b8aa8ae..31b186091f39 100644
--- a/services/core/java/com/android/server/pm/ApexManager.java
+++ b/services/core/java/com/android/server/pm/ApexManager.java
@@ -249,6 +249,14 @@ public abstract class ApexManager {
throws PackageManagerException;
/**
+ * Returns {@code ApeInfo} about apex sessions that have been marked ready via
+ * {@link #markStagedSessionReady(int)}
+ *
+ * Returns empty array if there is no staged apex session or if there is any error.
+ */
+ abstract ApexInfo[] getStagedApexInfos(ApexSessionParams params);
+
+ /**
* Mark a staged session previously submitted using {@code submitStagedSession} as ready to be
* applied at next reboot.
*
@@ -757,6 +765,19 @@ public abstract class ApexManager {
}
@Override
+ ApexInfo[] getStagedApexInfos(ApexSessionParams params) {
+ try {
+ return waitForApexService().getStagedApexInfos(params);
+ } catch (RemoteException re) {
+ Slog.w(TAG, "Unable to contact apexservice" + re.getMessage());
+ throw new RuntimeException(re);
+ } catch (Exception e) {
+ Slog.w(TAG, "Failed to collect staged apex infos" + e.getMessage());
+ return new ApexInfo[0];
+ }
+ }
+
+ @Override
void markStagedSessionReady(int sessionId) throws PackageManagerException {
try {
waitForApexService().markStagedSessionReady(sessionId);
@@ -1247,6 +1268,11 @@ public abstract class ApexManager {
}
@Override
+ ApexInfo[] getStagedApexInfos(ApexSessionParams params) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
void markStagedSessionReady(int sessionId) {
throw new UnsupportedOperationException();
}
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index 8fd545f271f3..5a5f9ef3b6aa 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -40,7 +40,6 @@ import com.android.server.SystemService;
import dalvik.system.BlockGuard;
import dalvik.system.VMRuntime;
-import java.io.FileDescriptor;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -681,28 +680,6 @@ public class Installer extends SystemService {
}
}
- public void installApkVerity(String filePath, FileDescriptor verityInput, int contentSize)
- throws InstallerException {
- if (!checkBeforeRemote()) return;
- BlockGuard.getVmPolicy().onPathAccess(filePath);
- try {
- mInstalld.installApkVerity(filePath, verityInput, contentSize);
- } catch (Exception e) {
- throw InstallerException.from(e);
- }
- }
-
- public void assertFsverityRootHashMatches(String filePath, @NonNull byte[] expectedHash)
- throws InstallerException {
- if (!checkBeforeRemote()) return;
- BlockGuard.getVmPolicy().onPathAccess(filePath);
- try {
- mInstalld.assertFsverityRootHashMatches(filePath, expectedHash);
- } catch (Exception e) {
- throw InstallerException.from(e);
- }
- }
-
public boolean reconcileSecondaryDexFile(String apkPath, String packageName, int uid,
String[] isas, @Nullable String volumeUuid, int flags) throws InstallerException {
for (int i = 0; i < isas.length; i++) {
diff --git a/services/core/java/com/android/server/pm/OWNERS b/services/core/java/com/android/server/pm/OWNERS
index 43c5d5e4015e..c219f80ac9c5 100644
--- a/services/core/java/com/android/server/pm/OWNERS
+++ b/services/core/java/com/android/server/pm/OWNERS
@@ -3,27 +3,25 @@ hackbod@google.com
jsharkey@android.com
jsharkey@google.com
narayan@google.com
-patb@google.com
svetoslavganov@android.com
svetoslavganov@google.com
-toddke@android.com
-toddke@google.com
+include /PACKAGE_MANAGER_OWNERS
# apex support
per-file ApexManager.java = dariofreni@google.com, ioffe@google.com, olilan@google.com
per-file StagingManager.java = dariofreni@google.com, ioffe@google.com, olilan@google.com
# dex
-per-file AbstractStatsBase.java = calin@google.com, ngeoffray@google.com
-per-file BackgroundDexOptService.java = calin@google.com, ngeoffray@google.com
-per-file CompilerStats.java = calin@google.com, ngeoffray@google.com
-per-file DynamicCodeLoggingService.java = alanstokes@google.com, calin@google.com, ngeoffray@google.com
-per-file InstructionSets.java = calin@google.com, ngeoffray@google.com
-per-file OtaDexoptService.java = calin@google.com, ngeoffray@google.com
-per-file OtaDexoptShellCommand.java = calin@google.com, ngeoffray@google.com
-per-file PackageDexOptimizer.java = calin@google.com, ngeoffray@google.com
-per-file PackageManagerServiceCompilerMapping.java = calin@google.com, ngeoffray@google.com
-per-file PackageUsage.java = calin@google.com, ngeoffray@google.com
+per-file AbstractStatsBase.java = file:dex/OWNERS
+per-file BackgroundDexOptService.java = file:dex/OWNERS
+per-file CompilerStats.java = file:dex/OWNERS
+per-file DynamicCodeLoggingService.java = file:dex/OWNERS
+per-file InstructionSets.java = file:dex/OWNERS
+per-file OtaDexoptService.java = file:dex/OWNERS
+per-file OtaDexoptShellCommand.java = file:dex/OWNERS
+per-file PackageDexOptimizer.java = file:dex/OWNERS
+per-file PackageManagerServiceCompilerMapping.java = file:dex/OWNERS
+per-file PackageUsage.java = file:dex/OWNERS
# multi user / cross profile
per-file CrossProfileAppsServiceImpl.java = omakoto@google.com, yamasani@google.com
@@ -42,7 +40,7 @@ per-file KeySetHandle.java = cbrubaker@google.com, nnk@google.com
per-file KeySetManagerService.java = cbrubaker@google.com, nnk@google.com
per-file PackageKeySetData.java = cbrubaker@google.com, nnk@google.com
per-file PackageSignatures.java = cbrubaker@google.com, nnk@google.com
-per-file SELinuxMMAC.java = cbrubaker@google.com, jeffv@google.com, jgalenson@google.com, nnk@google.com
+per-file SELinuxMMAC* = alanstokes@google.com, cbrubaker@google.com, jeffv@google.com
# shortcuts
per-file LauncherAppsService.java = omakoto@google.com, yamasani@google.com, sunnygoyal@google.com, mett@google.com, pinyaoting@google.com
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 2f1fa2221e98..7b6c8e3fad62 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -262,6 +262,10 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
new Lifecycle(context, this));
}
+ StagingManager getStagingManager() {
+ return mStagingManager;
+ }
+
boolean okToSendBroadcasts() {
return mOkToSendBroadcasts;
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index c0486443b97e..08f916fb702b 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -186,6 +186,7 @@ import android.content.pm.IPackageManager;
import android.content.pm.IPackageManagerNative;
import android.content.pm.IPackageMoveObserver;
import android.content.pm.IPackageStatsObserver;
+import android.content.pm.IStagedApexObserver;
import android.content.pm.IncrementalStatesInfo;
import android.content.pm.InstallSourceInfo;
import android.content.pm.InstantAppInfo;
@@ -227,6 +228,7 @@ import android.content.pm.ServiceInfo;
import android.content.pm.SharedLibraryInfo;
import android.content.pm.Signature;
import android.content.pm.SigningInfo;
+import android.content.pm.StagedApexInfo;
import android.content.pm.SuspendDialogInfo;
import android.content.pm.TestUtilityService;
import android.content.pm.UserInfo;
@@ -3559,9 +3561,9 @@ public class PackageManagerService extends IPackageManager.Stub
list.addAll(mApexManager.getFactoryPackages());
} else {
list.addAll(mApexManager.getActivePackages());
- }
- if (listUninstalled) {
- list.addAll(mApexManager.getInactivePackages());
+ if (listUninstalled) {
+ list.addAll(mApexManager.getInactivePackages());
+ }
}
}
return new ParceledListSlice<>(list);
@@ -7669,9 +7671,16 @@ public class PackageManagerService extends IPackageManager.Stub
// Parse overlay configuration files to set default enable state, mutability, and
// priority of system overlays.
+ final ArrayMap<String, File> apkInApexPreInstalledPaths = new ArrayMap<>();
+ for (ApexManager.ActiveApexInfo apexInfo : mApexManager.getActiveApexInfos()) {
+ for (String packageName : mApexManager.getApksInApex(apexInfo.apexModuleName)) {
+ apkInApexPreInstalledPaths.put(packageName, apexInfo.preInstalledApexPath);
+ }
+ }
mOverlayConfig = OverlayConfig.initializeSystemInstance(
consumer -> mPmInternal.forEachPackage(
- pkg -> consumer.accept(pkg, pkg.isSystem())));
+ pkg -> consumer.accept(pkg, pkg.isSystem(),
+ apkInApexPreInstalledPaths.get(pkg.getPackageName()))));
// Prune any system packages that no longer exist.
final List<String> possiblyDeletedUpdatedSystemApps = new ArrayList<>();
@@ -12108,14 +12117,14 @@ public class PackageManagerService extends IPackageManager.Stub
* Returns if forced apk verification can be skipped for the whole package, including splits.
*/
private boolean canSkipForcedPackageVerification(AndroidPackage pkg) {
- if (!canSkipForcedApkVerification(pkg.getBaseApkPath())) {
+ if (!VerityUtils.hasFsverity(pkg.getBaseApkPath())) {
return false;
}
// TODO: Allow base and splits to be verified individually.
String[] splitCodePaths = pkg.getSplitCodePaths();
if (!ArrayUtils.isEmpty(splitCodePaths)) {
for (int i = 0; i < splitCodePaths.length; i++) {
- if (!canSkipForcedApkVerification(splitCodePaths[i])) {
+ if (!VerityUtils.hasFsverity(splitCodePaths[i])) {
return false;
}
}
@@ -12124,33 +12133,6 @@ public class PackageManagerService extends IPackageManager.Stub
}
/**
- * Returns if forced apk verification can be skipped, depending on current FSVerity setup and
- * whether the apk contains signed root hash. Note that the signer's certificate still needs to
- * match one in a trusted source, and should be done separately.
- */
- private boolean canSkipForcedApkVerification(String apkPath) {
- if (!PackageManagerServiceUtils.isLegacyApkVerityEnabled()) {
- return VerityUtils.hasFsverity(apkPath);
- }
-
- try {
- final byte[] rootHashObserved = VerityUtils.generateApkVerityRootHash(apkPath);
- if (rootHashObserved == null) {
- return false; // APK does not contain Merkle tree root hash.
- }
- synchronized (mInstallLock) {
- // Returns whether the observed root hash matches what kernel has.
- mInstaller.assertFsverityRootHashMatches(apkPath, rootHashObserved);
- return true;
- }
- } catch (InstallerException | IOException | DigestException |
- NoSuchAlgorithmException e) {
- Slog.w(TAG, "Error in fsverity check. Fallback to full apk verification.", e);
- }
- return false;
- }
-
- /**
* Adds a new package to the internal data structures during platform initialization.
* <p>After adding, the package is known to the system and available for querying.
* <p>For packages located on the device ROM [eg. packages located in /system, /vendor,
@@ -21178,9 +21160,7 @@ public class PackageManagerService extends IPackageManager.Stub
*/
private void setUpFsVerityIfPossible(AndroidPackage pkg) throws InstallerException,
PrepareFailure, IOException, DigestException, NoSuchAlgorithmException {
- final boolean standardMode = PackageManagerServiceUtils.isApkVerityEnabled();
- final boolean legacyMode = PackageManagerServiceUtils.isLegacyApkVerityEnabled();
- if (!standardMode && !legacyMode) {
+ if (!PackageManagerServiceUtils.isApkVerityEnabled()) {
return;
}
@@ -21191,39 +21171,25 @@ public class PackageManagerService extends IPackageManager.Stub
// Collect files we care for fs-verity setup.
ArrayMap<String, String> fsverityCandidates = new ArrayMap<>();
- if (legacyMode) {
- synchronized (mLock) {
- final PackageSetting ps = mSettings.getPackageLPr(pkg.getPackageName());
- if (ps != null && ps.isPrivileged()) {
- fsverityCandidates.put(pkg.getBaseApkPath(), null);
- if (pkg.getSplitCodePaths() != null) {
- for (String splitPath : pkg.getSplitCodePaths()) {
- fsverityCandidates.put(splitPath, null);
- }
- }
- }
- }
- } else {
- // NB: These files will become only accessible if the signing key is loaded in kernel's
- // .fs-verity keyring.
- fsverityCandidates.put(pkg.getBaseApkPath(),
- VerityUtils.getFsveritySignatureFilePath(pkg.getBaseApkPath()));
+ // NB: These files will become only accessible if the signing key is loaded in kernel's
+ // .fs-verity keyring.
+ fsverityCandidates.put(pkg.getBaseApkPath(),
+ VerityUtils.getFsveritySignatureFilePath(pkg.getBaseApkPath()));
- final String dmPath = DexMetadataHelper.buildDexMetadataPathForApk(
- pkg.getBaseApkPath());
- if (new File(dmPath).exists()) {
- fsverityCandidates.put(dmPath, VerityUtils.getFsveritySignatureFilePath(dmPath));
- }
+ final String dmPath = DexMetadataHelper.buildDexMetadataPathForApk(
+ pkg.getBaseApkPath());
+ if (new File(dmPath).exists()) {
+ fsverityCandidates.put(dmPath, VerityUtils.getFsveritySignatureFilePath(dmPath));
+ }
- if (pkg.getSplitCodePaths() != null) {
- for (String path : pkg.getSplitCodePaths()) {
- fsverityCandidates.put(path, VerityUtils.getFsveritySignatureFilePath(path));
+ if (pkg.getSplitCodePaths() != null) {
+ for (String path : pkg.getSplitCodePaths()) {
+ fsverityCandidates.put(path, VerityUtils.getFsveritySignatureFilePath(path));
- final String splitDmPath = DexMetadataHelper.buildDexMetadataPathForApk(path);
- if (new File(splitDmPath).exists()) {
- fsverityCandidates.put(splitDmPath,
- VerityUtils.getFsveritySignatureFilePath(splitDmPath));
- }
+ final String splitDmPath = DexMetadataHelper.buildDexMetadataPathForApk(path);
+ if (new File(splitDmPath).exists()) {
+ fsverityCandidates.put(splitDmPath,
+ VerityUtils.getFsveritySignatureFilePath(splitDmPath));
}
}
}
@@ -21232,40 +21198,14 @@ public class PackageManagerService extends IPackageManager.Stub
final String filePath = entry.getKey();
final String signaturePath = entry.getValue();
- if (!legacyMode) {
- // fs-verity is optional for now. Only set up if signature is provided.
- if (new File(signaturePath).exists() && !VerityUtils.hasFsverity(filePath)) {
- try {
- VerityUtils.setUpFsverity(filePath, signaturePath);
- } catch (IOException e) {
- throw new PrepareFailure(PackageManager.INSTALL_FAILED_BAD_SIGNATURE,
- "Failed to enable fs-verity: " + e);
- }
- }
- continue;
- }
-
- // In legacy mode, fs-verity can only be enabled by process with CAP_SYS_ADMIN.
- final VerityUtils.SetupResult result = VerityUtils.generateApkVeritySetupData(filePath);
- if (result.isOk()) {
- if (Build.IS_DEBUGGABLE) Slog.i(TAG, "Enabling verity to " + filePath);
- final FileDescriptor fd = result.getUnownedFileDescriptor();
+ // fs-verity is optional for now. Only set up if signature is provided.
+ if (new File(signaturePath).exists() && !VerityUtils.hasFsverity(filePath)) {
try {
- final byte[] rootHash = VerityUtils.generateApkVerityRootHash(filePath);
- try {
- // A file may already have fs-verity, e.g. when reused during a split
- // install. If the measurement succeeds, no need to attempt to set up.
- mInstaller.assertFsverityRootHashMatches(filePath, rootHash);
- } catch (InstallerException e) {
- mInstaller.installApkVerity(filePath, fd, result.getContentSize());
- mInstaller.assertFsverityRootHashMatches(filePath, rootHash);
- }
- } finally {
- IoUtils.closeQuietly(fd);
+ VerityUtils.setUpFsverity(filePath, signaturePath);
+ } catch (IOException e) {
+ throw new PrepareFailure(PackageManager.INSTALL_FAILED_BAD_SIGNATURE,
+ "Failed to enable fs-verity: " + e);
}
- } else if (result.isFailed()) {
- throw new PrepareFailure(PackageManager.INSTALL_FAILED_BAD_SIGNATURE,
- "Failed to generate verity");
}
}
}
@@ -22037,8 +21977,10 @@ public class PackageManagerService extends IPackageManager.Stub
ApexManager.ActiveApexInfo apexInfo) {
for (int i = 0, size = SYSTEM_PARTITIONS.size(); i < size; i++) {
ScanPartition sp = SYSTEM_PARTITIONS.get(i);
- if (apexInfo.preInstalledApexPath.getAbsolutePath().startsWith(
- sp.getFolder().getAbsolutePath())) {
+ if (apexInfo.preInstalledApexPath.getAbsolutePath().equals(
+ sp.getFolder().getAbsolutePath())
+ || apexInfo.preInstalledApexPath.getAbsolutePath().startsWith(
+ sp.getFolder().getAbsolutePath() + File.separator)) {
return new ScanPartition(apexInfo.apexDirectory, sp, SCAN_AS_APK_IN_APEX);
}
}
@@ -27136,6 +27078,29 @@ public class PackageManagerService extends IPackageManager.Stub
public boolean hasSystemFeature(String featureName, int version) {
return PackageManagerService.this.hasSystemFeature(featureName, version);
}
+
+ @Override
+ public void registerStagedApexObserver(IStagedApexObserver observer) {
+ mInstallerService.getStagingManager().registerStagedApexObserver(observer);
+ }
+
+ @Override
+ public void unregisterStagedApexObserver(IStagedApexObserver observer) {
+ mInstallerService.getStagingManager().unregisterStagedApexObserver(observer);
+ }
+
+ @Override
+ public String[] getStagedApexModuleNames() {
+ return mInstallerService.getStagingManager()
+ .getStagedApexModuleNames().toArray(new String[0]);
+ }
+
+ @Override
+ @Nullable
+ public StagedApexInfo getStagedApexInfo(String moduleName) {
+ return mInstallerService.getStagingManager().getStagedApexInfo(moduleName);
+ }
+
}
private AndroidPackage getPackage(String packageName) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index 8b5abf3afe51..8970049d85cc 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -591,12 +591,6 @@ public class PackageManagerServiceUtils {
/** Default is to not use fs-verity since it depends on kernel support. */
private static final int FSVERITY_DISABLED = 0;
- /**
- * Experimental implementation targeting priv apps, with Android specific kernel patches to
- * extend fs-verity.
- */
- private static final int FSVERITY_LEGACY = 1;
-
/** Standard fs-verity. */
private static final int FSVERITY_ENABLED = 2;
@@ -607,10 +601,6 @@ public class PackageManagerServiceUtils {
== FSVERITY_ENABLED;
}
- static boolean isLegacyApkVerityEnabled() {
- return SystemProperties.getInt("ro.apk_verity.mode", FSVERITY_DISABLED) == FSVERITY_LEGACY;
- }
-
/** Returns true to force apk verification if the package is considered privileged. */
static boolean isApkVerificationForced(@Nullable PackageSetting ps) {
// TODO(b/154310064): re-enable.
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 1aa80a953d6c..5b4084e9b229 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -2438,6 +2438,15 @@ class PackageManagerShellCommand extends ShellCommand {
}
}
+ private String getApexPackageNameContainingPackage(String pkg) {
+ ApexManager apexManager = ApexManager.getInstance();
+ return apexManager.getActiveApexPackageNameContainingPackage(pkg);
+ }
+
+ private boolean isApexApp(String pkg) {
+ return getApexPackageNameContainingPackage(pkg) != null;
+ }
+
private int runGetPrivappPermissions() {
final String pkg = getNextArg();
if (pkg == null) {
@@ -2453,6 +2462,9 @@ class PackageManagerShellCommand extends ShellCommand {
} else if (isSystemExtApp(pkg)) {
privAppPermissions = SystemConfig.getInstance()
.getSystemExtPrivAppPermissions(pkg);
+ } else if (isApexApp(pkg)) {
+ privAppPermissions = SystemConfig.getInstance()
+ .getApexPrivAppPermissions(getApexPackageNameContainingPackage(pkg), pkg);
} else {
privAppPermissions = SystemConfig.getInstance().getPrivAppPermissions(pkg);
}
@@ -2477,6 +2489,9 @@ class PackageManagerShellCommand extends ShellCommand {
} else if (isSystemExtApp(pkg)) {
privAppPermissions = SystemConfig.getInstance()
.getSystemExtPrivAppDenyPermissions(pkg);
+ } else if (isApexApp(pkg)) {
+ privAppPermissions = SystemConfig.getInstance()
+ .getApexPrivAppDenyPermissions(getApexPackageNameContainingPackage(pkg), pkg);
} else {
privAppPermissions = SystemConfig.getInstance().getPrivAppDenyPermissions(pkg);
}
diff --git a/services/core/java/com/android/server/pm/SELinuxMMAC.java b/services/core/java/com/android/server/pm/SELinuxMMAC.java
index c5fbfba9b049..cecbeed67f68 100644
--- a/services/core/java/com/android/server/pm/SELinuxMMAC.java
+++ b/services/core/java/com/android/server/pm/SELinuxMMAC.java
@@ -17,6 +17,7 @@
package com.android.server.pm;
import android.compat.annotation.ChangeId;
+import android.compat.annotation.Disabled;
import android.compat.annotation.EnabledAfter;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageParser.SigningDetails;
@@ -79,20 +80,20 @@ public final class SELinuxMMAC {
/**
* Allows opt-in to the latest targetSdkVersion enforced changes without changing target SDK.
- * Turning this change off for an app targeting the latest SDK is a no-op.
+ * Turning this change on for an app targeting the latest SDK or higher is a no-op.
*
* <p>Has no effect for apps using shared user id.
*
* TODO(b/143539591): Update description with relevant SELINUX changes this opts in to.
*/
- @EnabledAfter(targetSdkVersion = android.os.Build.VERSION_CODES.R)
+ @Disabled
@ChangeId
static final long SELINUX_LATEST_CHANGES = 143539591L;
/**
* This change gates apps access to untrusted_app_R-targetSDK SELinux domain. Allows opt-in
* to R targetSdkVersion enforced changes without changing target SDK. Turning this change
- * off for an app targeting S is a no-op.
+ * off for an app targeting {@code >= android.os.Build.VERSION_CODES.R} is a no-op.
*
* <p>Has no effect for apps using shared user id.
*
@@ -123,16 +124,10 @@ public final class SELinuxMMAC {
}
// Vendor mac permissions.
- // The filename has been renamed from nonplat_mac_permissions to
- // vendor_mac_permissions. Either of them should exist.
final File vendorMacPermission = new File(
Environment.getVendorDirectory(), "/etc/selinux/vendor_mac_permissions.xml");
if (vendorMacPermission.exists()) {
sMacPermissions.add(vendorMacPermission);
- } else {
- // For backward compatibility.
- sMacPermissions.add(new File(Environment.getVendorDirectory(),
- "/etc/selinux/nonplat_mac_permissions.xml"));
}
// ODM mac permissions (optional).
@@ -364,7 +359,8 @@ public final class SELinuxMMAC {
}
final ApplicationInfo appInfo = pkg.toAppInfoWithoutState();
if (compatibility.isChangeEnabledInternal(SELINUX_LATEST_CHANGES, appInfo)) {
- return android.os.Build.VERSION_CODES.S;
+ return Math.max(
+ android.os.Build.VERSION_CODES.CUR_DEVELOPMENT, pkg.getTargetSdkVersion());
} else if (compatibility.isChangeEnabledInternal(SELINUX_R_CHANGES, appInfo)) {
return Math.max(android.os.Build.VERSION_CODES.R, pkg.getTargetSdkVersion());
}
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index bdbcb277e8d6..97c168d4fb0b 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -17,6 +17,7 @@
package com.android.server.pm;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.apex.ApexInfo;
import android.apex.ApexInfoList;
import android.apex.ApexSessionInfo;
@@ -28,7 +29,9 @@ import android.content.IIntentSender;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentSender;
+import android.content.pm.ApexStagedEvent;
import android.content.pm.ApplicationInfo;
+import android.content.pm.IStagedApexObserver;
import android.content.pm.PackageInfo;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageInstaller.SessionInfo;
@@ -38,6 +41,7 @@ import android.content.pm.PackageManagerInternal;
import android.content.pm.PackageParser.PackageParserException;
import android.content.pm.PackageParser.SigningDetails;
import android.content.pm.PackageParser.SigningDetails.SignatureSchemeVersion;
+import android.content.pm.StagedApexInfo;
import android.content.pm.parsing.PackageInfoWithoutStateUtils;
import android.content.rollback.RollbackInfo;
import android.content.rollback.RollbackManager;
@@ -52,6 +56,7 @@ import android.os.SystemProperties;
import android.os.Trace;
import android.os.UserHandle;
import android.text.TextUtils;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.IntArray;
import android.util.Slog;
@@ -80,7 +85,9 @@ import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
+import java.util.Map;
import java.util.Set;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
@@ -98,7 +105,8 @@ public class StagingManager {
private final ApexManager mApexManager;
private final PowerManager mPowerManager;
private final Context mContext;
- private final PreRebootVerificationHandler mPreRebootVerificationHandler;
+ @VisibleForTesting
+ final PreRebootVerificationHandler mPreRebootVerificationHandler;
private final Supplier<PackageParser2> mPackageParserSupplier;
private final File mFailureReasonFile = new File("/metadata/staged-install/failure_reason.txt");
@@ -114,6 +122,9 @@ public class StagingManager {
@GuardedBy("mSuccessfulStagedSessionIds")
private final List<Integer> mSuccessfulStagedSessionIds = new ArrayList<>();
+ @GuardedBy("mStagedApexObservers")
+ private final List<IStagedApexObserver> mStagedApexObservers = new ArrayList<>();
+
interface StagedSession {
boolean isMultiPackage();
boolean isApexSession();
@@ -198,6 +209,35 @@ public class StagingManager {
mApexManager.markBootCompleted();
}
+ void registerStagedApexObserver(IStagedApexObserver observer) {
+ if (observer == null) {
+ return;
+ }
+ if (observer.asBinder() != null) {
+ try {
+ observer.asBinder().linkToDeath(new IBinder.DeathRecipient() {
+ @Override
+ public void binderDied() {
+ synchronized (mStagedApexObservers) {
+ mStagedApexObservers.remove(observer);
+ }
+ }
+ }, 0);
+ } catch (RemoteException re) {
+ Slog.w(TAG, re.getMessage());
+ }
+ }
+ synchronized (mStagedApexObservers) {
+ mStagedApexObservers.add(observer);
+ }
+ }
+
+ void unregisterStagedApexObserver(IStagedApexObserver observer) {
+ synchronized (mStagedApexObservers) {
+ mStagedApexObservers.remove(observer);
+ }
+ }
+
/**
* Validates the signature used to sign the container of the new apex package
*
@@ -836,6 +876,9 @@ public class StagingManager {
// Also, cleaning up the stageDir prevents the apex from being activated.
Slog.e(TAG, "Failed to abort apex session " + session.sessionId());
}
+ if (session.containsApexSession()) {
+ notifyStagedApexObservers();
+ }
}
// Session was successfully aborted from apexd (if required) and pre-reboot verification
@@ -1134,7 +1177,108 @@ public class StagingManager {
mPreRebootVerificationHandler.notifyPreRebootVerification_Apk_Complete(session);
}
- private final class PreRebootVerificationHandler extends Handler {
+ /**
+ * Returns ApexInfo about APEX contained inside the session as a {@code Map<String, ApexInfo>},
+ * where the key of the map is the module name of the ApexInfo.
+ *
+ * Returns an empty map if there is any error.
+ */
+ @VisibleForTesting
+ @NonNull
+ Map<String, ApexInfo> getStagedApexInfos(@NonNull StagedSession session) {
+ Preconditions.checkArgument(session != null, "Session is null");
+ Preconditions.checkArgument(!session.hasParentSessionId(),
+ session.sessionId() + " session has parent session");
+ Preconditions.checkArgument(session.containsApexSession(),
+ session.sessionId() + " session does not contain apex");
+
+ // Even if caller calls this method on ready session, the session could be abandoned
+ // right after this method is called.
+ if (!session.isSessionReady() || session.isDestroyed()) {
+ return Collections.emptyMap();
+ }
+
+ ApexSessionParams params = new ApexSessionParams();
+ params.sessionId = session.sessionId();
+ final IntArray childSessionIds = new IntArray();
+ if (session.isMultiPackage()) {
+ for (StagedSession s : session.getChildSessions()) {
+ if (s.isApexSession()) {
+ childSessionIds.add(s.sessionId());
+ }
+ }
+ }
+ params.childSessionIds = childSessionIds.toArray();
+
+ ApexInfo[] infos = mApexManager.getStagedApexInfos(params);
+ Map<String, ApexInfo> result = new ArrayMap<>();
+ for (ApexInfo info : infos) {
+ result.put(info.moduleName, info);
+ }
+ return result;
+ }
+
+ /**
+ * Returns apex module names of all packages that are staged ready
+ */
+ List<String> getStagedApexModuleNames() {
+ List<String> result = new ArrayList<>();
+ synchronized (mStagedSessions) {
+ for (int i = 0; i < mStagedSessions.size(); i++) {
+ final StagedSession session = mStagedSessions.valueAt(i);
+ if (!session.isSessionReady() || session.isDestroyed()
+ || session.hasParentSessionId() || !session.containsApexSession()) {
+ continue;
+ }
+ result.addAll(getStagedApexInfos(session).keySet());
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Returns ApexInfo of the {@code moduleInfo} provided if it is staged, otherwise returns null.
+ */
+ @Nullable
+ StagedApexInfo getStagedApexInfo(String moduleName) {
+ synchronized (mStagedSessions) {
+ for (int i = 0; i < mStagedSessions.size(); i++) {
+ final StagedSession session = mStagedSessions.valueAt(i);
+ if (!session.isSessionReady() || session.isDestroyed()
+ || session.hasParentSessionId() || !session.containsApexSession()) {
+ continue;
+ }
+ ApexInfo ai = getStagedApexInfos(session).get(moduleName);
+ if (ai != null) {
+ StagedApexInfo info = new StagedApexInfo();
+ info.moduleName = ai.moduleName;
+ info.diskImagePath = ai.modulePath;
+ info.versionCode = ai.versionCode;
+ info.versionName = ai.versionName;
+ info.hasClassPathJars = ai.hasClassPathJars;
+ return info;
+ }
+ }
+ }
+ return null;
+ }
+
+ private void notifyStagedApexObservers() {
+ synchronized (mStagedApexObservers) {
+ for (IStagedApexObserver observer : mStagedApexObservers) {
+ ApexStagedEvent event = new ApexStagedEvent();
+ event.stagedApexModuleNames = getStagedApexModuleNames().toArray(new String[0]);
+ try {
+ observer.onApexStaged(event);
+ } catch (RemoteException re) {
+ Slog.w(TAG, "Failed to contact the observer " + re.getMessage());
+ }
+ }
+ }
+ }
+
+ @VisibleForTesting
+ final class PreRebootVerificationHandler extends Handler {
// Hold sessions before handler gets ready to do the verification.
private List<StagedSession> mPendingSessions;
private boolean mIsReady;
@@ -1160,7 +1304,8 @@ public class StagingManager {
private static final int MSG_PRE_REBOOT_VERIFICATION_START = 1;
private static final int MSG_PRE_REBOOT_VERIFICATION_APEX = 2;
private static final int MSG_PRE_REBOOT_VERIFICATION_APK = 3;
- private static final int MSG_PRE_REBOOT_VERIFICATION_END = 4;
+ @VisibleForTesting
+ static final int MSG_PRE_REBOOT_VERIFICATION_END = 4;
@Override
public void handleMessage(Message msg) {
@@ -1402,6 +1547,7 @@ public class StagingManager {
if (hasApex) {
try {
mApexManager.markStagedSessionReady(session.sessionId());
+ notifyStagedApexObservers();
} catch (PackageManagerException e) {
session.setSessionFailed(e.error, e.getMessage());
return;
diff --git a/services/core/java/com/android/server/pm/UserDataPreparer.java b/services/core/java/com/android/server/pm/UserDataPreparer.java
index 045a295da965..504769064808 100644
--- a/services/core/java/com/android/server/pm/UserDataPreparer.java
+++ b/services/core/java/com/android/server/pm/UserDataPreparer.java
@@ -22,6 +22,7 @@ import android.content.Context;
import android.content.pm.UserInfo;
import android.os.Environment;
import android.os.FileUtils;
+import android.os.RecoverySystem;
import android.os.storage.StorageManager;
import android.os.storage.VolumeInfo;
import android.os.SystemProperties;
@@ -115,6 +116,13 @@ class UserDataPreparer {
// Try one last time; if we fail again we're really in trouble
prepareUserDataLI(volumeUuid, userId, userSerial,
flags | StorageManager.FLAG_STORAGE_DE, false);
+ } else {
+ try {
+ Log.e(TAG, "prepareUserData failed", e);
+ RecoverySystem.rebootPromptAndWipeUserData(mContext, "prepareUserData failed");
+ } catch (IOException e2) {
+ throw new RuntimeException("error rebooting into recovery", e2);
+ }
}
}
}
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 6d8137e74061..075275ac533c 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -3402,8 +3402,11 @@ public class UserManagerService extends IUserManager.Stub {
Slog.wtf(LOG_TAG, "Seeing both legacy and current local restrictions in xml");
}
} else if (legacyLocalRestrictions != null) {
- mDevicePolicyLocalUserRestrictions.put(id,
- new RestrictionsSet(id, legacyLocalRestrictions));
+ RestrictionsSet legacyLocalRestrictionsSet =
+ legacyLocalRestrictions.isEmpty()
+ ? new RestrictionsSet()
+ : new RestrictionsSet(id, legacyLocalRestrictions);
+ mDevicePolicyLocalUserRestrictions.put(id, legacyLocalRestrictionsSet);
}
if (globalRestrictions != null) {
mDevicePolicyGlobalUserRestrictions.updateRestrictions(id,
diff --git a/services/core/java/com/android/server/pm/dex/ArtManagerService.java b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
index 1859b4c83032..01bf63483829 100644
--- a/services/core/java/com/android/server/pm/dex/ArtManagerService.java
+++ b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
@@ -608,6 +608,8 @@ public class ArtManagerService extends android.content.pm.dex.IArtManager.Stub {
private static final int TRON_COMPILATION_REASON_BOOT_AFTER_OTA = 20;
private static final int TRON_COMPILATION_REASON_POST_BOOT = 21;
private static final int TRON_COMPILATION_REASON_CMDLINE = 22;
+ private static final int TRON_COMPILATION_REASON_PREBUILT = 23;
+ private static final int TRON_COMPILATION_REASON_VDEX = 24;
// The annotation to add as a suffix to the compilation reason when dexopt was
// performed with dex metadata.
@@ -628,6 +630,8 @@ public class ArtManagerService extends android.content.pm.dex.IArtManager.Stub {
case "ab-ota" : return TRON_COMPILATION_REASON_AB_OTA;
case "inactive" : return TRON_COMPILATION_REASON_INACTIVE;
case "shared" : return TRON_COMPILATION_REASON_SHARED;
+ case "prebuilt" : return TRON_COMPILATION_REASON_PREBUILT;
+ case "vdex" : return TRON_COMPILATION_REASON_VDEX;
case "install-fast" :
return TRON_COMPILATION_REASON_INSTALL_FAST;
case "install-bulk" :
diff --git a/services/core/java/com/android/server/pm/dex/OWNERS b/services/core/java/com/android/server/pm/dex/OWNERS
index 5a4431ee8c89..052a4ca52afd 100644
--- a/services/core/java/com/android/server/pm/dex/OWNERS
+++ b/services/core/java/com/android/server/pm/dex/OWNERS
@@ -1,2 +1,3 @@
-calin@google.com
+alanstokes@google.com
+jiakaiz@google.com
ngeoffray@google.com
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index cb28637e8c10..67bc2b2f62e7 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -213,6 +213,7 @@ final class DefaultPermissionGrantPolicy {
NEARBY_DEVICES_PERMISSIONS.add(Manifest.permission.BLUETOOTH_CONNECT);
NEARBY_DEVICES_PERMISSIONS.add(Manifest.permission.BLUETOOTH_SCAN);
NEARBY_DEVICES_PERMISSIONS.add(Manifest.permission.UWB_RANGING);
+ NEARBY_DEVICES_PERMISSIONS.add(Manifest.permission.NEARBY_WIFI_DEVICES);
}
private static final int MSG_READ_DEFAULT_PERMISSION_EXCEPTIONS = 1;
diff --git a/services/core/java/com/android/server/pm/permission/OWNERS b/services/core/java/com/android/server/pm/permission/OWNERS
index 8c1a90c13513..fc0ee23c4859 100644
--- a/services/core/java/com/android/server/pm/permission/OWNERS
+++ b/services/core/java/com/android/server/pm/permission/OWNERS
@@ -1,7 +1,3 @@
include platform/frameworks/base:/core/java/android/permission/OWNERS
-per-file DefaultPermissionGrantPolicy.java = hackbod@android.com
-per-file DefaultPermissionGrantPolicy.java = jsharkey@android.com
-per-file DefaultPermissionGrantPolicy.java = toddke@google.com
-per-file DefaultPermissionGrantPolicy.java = yamasani@google.com
-per-file DefaultPermissionGrantPolicy.java = patb@google.com
+per-file DefaultPermissionGrantPolicy.java = file:platform/frameworks/base:/core/java/android/permission/DEFAULT_PERMISSION_GRANT_POLICY_OWNERS
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 54a6c678e0da..41c716c58ddd 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -3454,10 +3454,15 @@ public class PermissionManagerService extends IPermissionManager.Stub {
return true;
}
final String permissionName = permission.getName();
- if (isInSystemConfigPrivAppPermissions(pkg, permissionName)) {
+ final ApexManager apexManager = ApexManager.getInstance();
+ final String containingApexPackageName =
+ apexManager.getActiveApexPackageNameContainingPackage(packageName);
+ if (isInSystemConfigPrivAppPermissions(pkg, permissionName,
+ containingApexPackageName)) {
return true;
}
- if (isInSystemConfigPrivAppDenyPermissions(pkg, permissionName)) {
+ if (isInSystemConfigPrivAppDenyPermissions(pkg, permissionName,
+ containingApexPackageName)) {
return false;
}
// Updated system apps do not need to be allowlisted
@@ -3474,9 +3479,6 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
// Only enforce the allowlist on boot
if (!mSystemReady) {
- final ApexManager apexManager = ApexManager.getInstance();
- final String containingApexPackageName =
- apexManager.getActiveApexPackageNameContainingPackage(packageName);
final boolean isInUpdatedApex = containingApexPackageName != null
&& !apexManager.isFactory(apexManager.getPackageInfo(containingApexPackageName,
MATCH_ACTIVE_PACKAGE));
@@ -3500,7 +3502,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
private boolean isInSystemConfigPrivAppPermissions(@NonNull AndroidPackage pkg,
- @NonNull String permission) {
+ @NonNull String permission, String containingApexPackageName) {
final SystemConfig systemConfig = SystemConfig.getInstance();
final Set<String> permissions;
if (pkg.isVendor()) {
@@ -3509,6 +3511,26 @@ public class PermissionManagerService extends IPermissionManager.Stub {
permissions = systemConfig.getProductPrivAppPermissions(pkg.getPackageName());
} else if (pkg.isSystemExt()) {
permissions = systemConfig.getSystemExtPrivAppPermissions(pkg.getPackageName());
+ } else if (containingApexPackageName != null) {
+ final Set<String> privAppPermissions = systemConfig.getPrivAppPermissions(
+ pkg.getPackageName());
+ final Set<String> apexPermissions = systemConfig.getApexPrivAppPermissions(
+ containingApexPackageName, pkg.getPackageName());
+ if (privAppPermissions != null) {
+ // TODO(andreionea): Remove check as soon as all apk-in-apex
+ // permission allowlists are migrated.
+ Slog.w(TAG, "Package " + pkg.getPackageName() + " is an APK in APEX,"
+ + " but has permission allowlist on the system image. Please bundle the"
+ + " allowlist in the " + containingApexPackageName + " APEX instead.");
+ if (apexPermissions != null) {
+ permissions = new ArraySet<>(privAppPermissions);
+ permissions.addAll(apexPermissions);
+ } else {
+ permissions = privAppPermissions;
+ }
+ } else {
+ permissions = apexPermissions;
+ }
} else {
permissions = systemConfig.getPrivAppPermissions(pkg.getPackageName());
}
@@ -3516,7 +3538,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
private boolean isInSystemConfigPrivAppDenyPermissions(@NonNull AndroidPackage pkg,
- @NonNull String permission) {
+ @NonNull String permission, String containingApexPackageName) {
final SystemConfig systemConfig = SystemConfig.getInstance();
final Set<String> permissions;
if (pkg.isVendor()) {
@@ -3525,6 +3547,9 @@ public class PermissionManagerService extends IPermissionManager.Stub {
permissions = systemConfig.getProductPrivAppDenyPermissions(pkg.getPackageName());
} else if (pkg.isSystemExt()) {
permissions = systemConfig.getSystemExtPrivAppDenyPermissions(pkg.getPackageName());
+ } else if (containingApexPackageName != null) {
+ permissions = systemConfig.getApexPrivAppDenyPermissions(containingApexPackageName,
+ pkg.getPackageName());
} else {
permissions = systemConfig.getPrivAppDenyPermissions(pkg.getPackageName());
}
diff --git a/services/core/java/com/android/server/policy/DisplayFoldDurationLogger.java b/services/core/java/com/android/server/policy/DisplayFoldDurationLogger.java
index bdcd2cde2e4e..3524d7f51090 100644
--- a/services/core/java/com/android/server/policy/DisplayFoldDurationLogger.java
+++ b/services/core/java/com/android/server/policy/DisplayFoldDurationLogger.java
@@ -44,8 +44,8 @@ class DisplayFoldDurationLogger {
@Retention(RetentionPolicy.SOURCE)
public @interface ScreenState {}
- private @ScreenState int mScreenState = SCREEN_STATE_UNKNOWN;
- private Long mLastChanged = null;
+ private volatile @ScreenState int mScreenState = SCREEN_STATE_UNKNOWN;
+ private volatile Long mLastChanged = null;
private static final int LOG_SUBTYPE_UNFOLDED = 0;
private static final int LOG_SUBTYPE_FOLDED = 1;
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index b7d4d22d3966..07abaad1c72f 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -2691,6 +2691,24 @@ public class PhoneWindowManager implements WindowManagerPolicy {
Slog.wtf(TAG, "KEYCODE_VOICE_ASSIST should be handled in"
+ " interceptKeyBeforeQueueing");
return key_consumed;
+ case KeyEvent.KEYCODE_VIDEO_APP_1:
+ case KeyEvent.KEYCODE_VIDEO_APP_2:
+ case KeyEvent.KEYCODE_VIDEO_APP_3:
+ case KeyEvent.KEYCODE_VIDEO_APP_4:
+ case KeyEvent.KEYCODE_VIDEO_APP_5:
+ case KeyEvent.KEYCODE_VIDEO_APP_6:
+ case KeyEvent.KEYCODE_VIDEO_APP_7:
+ case KeyEvent.KEYCODE_VIDEO_APP_8:
+ case KeyEvent.KEYCODE_FEATURED_APP_1:
+ case KeyEvent.KEYCODE_FEATURED_APP_2:
+ case KeyEvent.KEYCODE_FEATURED_APP_3:
+ case KeyEvent.KEYCODE_FEATURED_APP_4:
+ case KeyEvent.KEYCODE_DEMO_APP_1:
+ case KeyEvent.KEYCODE_DEMO_APP_2:
+ case KeyEvent.KEYCODE_DEMO_APP_3:
+ case KeyEvent.KEYCODE_DEMO_APP_4:
+ Slog.wtf(TAG, "KEYCODE_APP_X should be handled in interceptKeyBeforeQueueing");
+ return key_consumed;
case KeyEvent.KEYCODE_SYSRQ:
if (down && repeatCount == 0) {
mScreenshotRunnable.setScreenshotType(TAKE_SCREENSHOT_FULLSCREEN);
@@ -3787,6 +3805,26 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
break;
}
+ case KeyEvent.KEYCODE_VIDEO_APP_1:
+ case KeyEvent.KEYCODE_VIDEO_APP_2:
+ case KeyEvent.KEYCODE_VIDEO_APP_3:
+ case KeyEvent.KEYCODE_VIDEO_APP_4:
+ case KeyEvent.KEYCODE_VIDEO_APP_5:
+ case KeyEvent.KEYCODE_VIDEO_APP_6:
+ case KeyEvent.KEYCODE_VIDEO_APP_7:
+ case KeyEvent.KEYCODE_VIDEO_APP_8:
+ case KeyEvent.KEYCODE_FEATURED_APP_1:
+ case KeyEvent.KEYCODE_FEATURED_APP_2:
+ case KeyEvent.KEYCODE_FEATURED_APP_3:
+ case KeyEvent.KEYCODE_FEATURED_APP_4:
+ case KeyEvent.KEYCODE_DEMO_APP_1:
+ case KeyEvent.KEYCODE_DEMO_APP_2:
+ case KeyEvent.KEYCODE_DEMO_APP_3:
+ case KeyEvent.KEYCODE_DEMO_APP_4: {
+ // Just drop if keys are not intercepted for direct key.
+ result &= ~ACTION_PASS_TO_USER;
+ break;
+ }
}
// Intercept the Accessibility keychord (CTRL + ALT + Z) for keyboard users.
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 8c7d257d271b..97847565690a 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -4105,7 +4105,10 @@ public final class PowerManagerService extends SystemService
if (sQuiescent) {
// Pass the optional "quiescent" argument to the bootloader to let it know
// that it should not turn the screen/lights on.
- reason = reason + ",quiescent";
+ if (reason != ""){
+ reason += ",";
+ }
+ reason = reason + "quiescent";
}
SystemProperties.set("sys.powerctl", "reboot," + reason);
diff --git a/services/core/java/com/android/server/recoverysystem/RecoverySystemShellCommand.java b/services/core/java/com/android/server/recoverysystem/RecoverySystemShellCommand.java
index 3d78828888da..141d4dcf77d0 100644
--- a/services/core/java/com/android/server/recoverysystem/RecoverySystemShellCommand.java
+++ b/services/core/java/com/android/server/recoverysystem/RecoverySystemShellCommand.java
@@ -44,6 +44,8 @@ public class RecoverySystemShellCommand extends ShellCommand {
return requestLskf();
case "clear-lskf":
return clearLskf();
+ case "is-lskf-captured":
+ return isLskfCaptured();
case "reboot-and-apply":
return rebootAndApply();
default:
@@ -74,6 +76,14 @@ public class RecoverySystemShellCommand extends ShellCommand {
return 0;
}
+ private int isLskfCaptured() throws RemoteException {
+ String packageName = getNextArgRequired();
+ boolean captured = mService.isLskfCaptured(packageName);
+ PrintWriter pw = getOutPrintWriter();
+ pw.printf("%s LSKF capture status: %s\n", packageName, captured ? "true" : "false");
+ return 0;
+ }
+
private int rebootAndApply() throws RemoteException {
String packageName = getNextArgRequired();
String rebootReason = getNextArgRequired();
@@ -90,8 +100,9 @@ public class RecoverySystemShellCommand extends ShellCommand {
public void onHelp() {
PrintWriter pw = getOutPrintWriter();
pw.println("Recovery system commands:");
- pw.println(" request-lskf <token>");
+ pw.println(" request-lskf <package_name>");
pw.println(" clear-lskf");
- pw.println(" reboot-and-apply <token> <reason>");
+ pw.println(" is-lskf-captured <package_name>");
+ pw.println(" reboot-and-apply <package_name> <reason>");
}
}
diff --git a/services/core/java/com/android/server/security/OWNERS b/services/core/java/com/android/server/security/OWNERS
index e6f5826557b5..5c2d5badfeee 100644
--- a/services/core/java/com/android/server/security/OWNERS
+++ b/services/core/java/com/android/server/security/OWNERS
@@ -1,3 +1,4 @@
# Bug component: 36824
+per-file *AttestationVerification* = file:/core/java/android/security/attestationverification/OWNERS
per-file FileIntegrityService.java = victorhsieh@google.com
diff --git a/services/core/java/com/android/server/stats/bootstrap/StatsBootstrapAtomService.java b/services/core/java/com/android/server/stats/bootstrap/StatsBootstrapAtomService.java
new file mode 100644
index 000000000000..0d420a535415
--- /dev/null
+++ b/services/core/java/com/android/server/stats/bootstrap/StatsBootstrapAtomService.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.stats.bootstrap;
+
+import android.content.Context;
+import android.os.IStatsBootstrapAtomService;
+import android.os.StatsBootstrapAtom;
+import android.os.StatsBootstrapAtomValue;
+import android.util.Slog;
+import android.util.StatsEvent;
+import android.util.StatsLog;
+
+import com.android.server.SystemService;
+
+/**
+ * Proxy service for logging pushed atoms to statsd
+ *
+ * @hide
+ */
+public class StatsBootstrapAtomService extends IStatsBootstrapAtomService.Stub {
+
+ private static final String TAG = "StatsBootstrapAtomService";
+ private static final boolean DEBUG = false;
+
+ @Override
+ public void reportBootstrapAtom(StatsBootstrapAtom atom) {
+ if (atom.atomId < 1 || atom.atomId >= 10000) {
+ Slog.e(TAG, "Atom ID " + atom.atomId + " is not a valid atom ID");
+ return;
+ }
+ StatsEvent.Builder builder = StatsEvent.newBuilder().setAtomId(atom.atomId);
+ for (StatsBootstrapAtomValue value : atom.values) {
+ switch (value.getTag()) {
+ case StatsBootstrapAtomValue.boolValue:
+ builder.writeBoolean(value.getBoolValue());
+ break;
+ case StatsBootstrapAtomValue.intValue:
+ builder.writeInt(value.getIntValue());
+ break;
+ case StatsBootstrapAtomValue.longValue:
+ builder.writeLong(value.getLongValue());
+ break;
+ case StatsBootstrapAtomValue.floatValue:
+ builder.writeFloat(value.getFloatValue());
+ break;
+ case StatsBootstrapAtomValue.stringValue:
+ builder.writeString(value.getStringValue());
+ break;
+ case StatsBootstrapAtomValue.bytesValue:
+ builder.writeByteArray(value.getBytesValue());
+ break;
+ default:
+ Slog.e(TAG, "Unexpected value type " + value.getTag()
+ + " when logging atom " + atom.atomId);
+ return;
+
+ }
+ }
+ StatsLog.write(builder.usePooledBuffer().build());
+ }
+
+ /**
+ * Lifecycle and related code
+ */
+ public static final class Lifecycle extends SystemService {
+ private StatsBootstrapAtomService mStatsBootstrapAtomService;
+
+ public Lifecycle(Context context) {
+ super(context);
+ }
+
+ @Override
+ public void onStart() {
+ mStatsBootstrapAtomService = new StatsBootstrapAtomService();
+ try {
+ publishBinderService(Context.STATS_BOOTSTRAP_ATOM_SERVICE,
+ mStatsBootstrapAtomService);
+ if (DEBUG) Slog.d(TAG, "Published " + Context.STATS_BOOTSTRAP_ATOM_SERVICE);
+ } catch (Exception e) {
+ Slog.e(TAG, "Failed to publishBinderService", e);
+ }
+ }
+ }
+
+}
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index dfff76d3a044..0b3192d4f052 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -18,28 +18,17 @@ package com.android.server.stats.pull;
import static android.app.AppOpsManager.OP_FLAG_SELF;
import static android.app.AppOpsManager.OP_FLAG_TRUSTED_PROXIED;
-import static android.app.usage.NetworkStatsManager.FLAG_AUGMENT_WITH_SUBSCRIPTION_PLAN;
-import static android.app.usage.NetworkStatsManager.FLAG_POLL_FORCE;
-import static android.app.usage.NetworkStatsManager.FLAG_POLL_ON_OPEN;
import static android.content.pm.PackageInfo.REQUESTED_PERMISSION_GRANTED;
import static android.content.pm.PermissionInfo.PROTECTION_DANGEROUS;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
-import static android.net.NetworkIdentity.OEM_PAID;
-import static android.net.NetworkIdentity.OEM_PRIVATE;
-import static android.net.NetworkStats.DEFAULT_NETWORK_ALL;
-import static android.net.NetworkStats.METERED_ALL;
import static android.net.NetworkStats.METERED_YES;
-import static android.net.NetworkStats.ROAMING_ALL;
import static android.net.NetworkTemplate.MATCH_ETHERNET;
-import static android.net.NetworkTemplate.MATCH_MOBILE_WILDCARD;
-import static android.net.NetworkTemplate.MATCH_WIFI_WILDCARD;
-import static android.net.NetworkTemplate.NETWORK_TYPE_ALL;
+import static android.net.NetworkTemplate.MATCH_MOBILE;
+import static android.net.NetworkTemplate.MATCH_WIFI;
import static android.net.NetworkTemplate.OEM_MANAGED_ALL;
-import static android.net.NetworkTemplate.buildTemplateMobileWildcard;
-import static android.net.NetworkTemplate.buildTemplateMobileWithRatType;
-import static android.net.NetworkTemplate.buildTemplateWifiWildcard;
-import static android.net.NetworkTemplate.getAllCollapsedRatTypes;
+import static android.net.NetworkTemplate.OEM_MANAGED_PAID;
+import static android.net.NetworkTemplate.OEM_MANAGED_PRIVATE;
import static android.os.Debug.getIonHeapsSizeKb;
import static android.os.Process.LAST_SHARED_APPLICATION_GID;
import static android.os.Process.getUidForPid;
@@ -85,6 +74,7 @@ import android.app.ProcessMemoryState;
import android.app.RuntimeAppOpAccessMessage;
import android.app.StatsManager;
import android.app.StatsManager.PullAtomMetadata;
+import android.app.usage.NetworkStatsManager;
import android.bluetooth.BluetoothActivityEnergyInfo;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.UidTraffic;
@@ -98,10 +88,7 @@ import android.content.pm.UserInfo;
import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.face.FaceManager;
import android.hardware.fingerprint.FingerprintManager;
-import android.hardware.health.V2_0.IHealth;
import android.net.ConnectivityManager;
-import android.net.INetworkStatsService;
-import android.net.INetworkStatsSession;
import android.net.Network;
import android.net.NetworkRequest;
import android.net.NetworkStats;
@@ -195,14 +182,15 @@ import com.android.internal.os.StoragedUidIoStatsReader;
import com.android.internal.os.SystemServerCpuThreadReader.SystemServiceCpuThreadTimes;
import com.android.internal.util.CollectionUtils;
import com.android.internal.util.FrameworkStatsLog;
+import com.android.net.module.util.NetworkStatsUtils;
import com.android.role.RoleManagerLocal;
-import com.android.server.BatteryService;
import com.android.server.BinderCallsStatsService;
import com.android.server.LocalManagerRegistry;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.SystemServiceManager;
import com.android.server.am.MemoryStatUtil.MemoryStat;
+import com.android.server.health.HealthServiceWrapper;
import com.android.server.notification.NotificationManagerService;
import com.android.server.stats.pull.IonMemoryUtil.IonAllocations;
import com.android.server.stats.pull.ProcfsMemoryUtil.MemorySnapshot;
@@ -232,6 +220,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.MissingResourceException;
+import java.util.NoSuchElementException;
import java.util.Random;
import java.util.Set;
import java.util.UUID;
@@ -241,7 +230,7 @@ import java.util.concurrent.Executor;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
-import java.util.function.BiConsumer;
+import java.util.function.Function;
/**
* SystemService containing PullAtomCallbacks that are registered with statsd.
@@ -337,6 +326,7 @@ public class StatsPullAtomService extends SystemService {
private WifiManager mWifiManager;
private TelephonyManager mTelephony;
private SubscriptionManager mSubscriptionManager;
+ private NetworkStatsManager mNetworkStatsManager;
@GuardedBy("mKernelWakelockLock")
private KernelWakelockReader mKernelWakelockReader;
@@ -360,7 +350,7 @@ public class StatsPullAtomService extends SystemService {
private File mBaseDir;
@GuardedBy("mHealthHalLock")
- private BatteryService.HealthServiceWrapper mHealthService;
+ private HealthServiceWrapper mHealthService;
@Nullable
@GuardedBy("mCpuTimePerThreadFreqLock")
@@ -786,7 +776,7 @@ public class StatsPullAtomService extends SystemService {
mContext.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE);
mStatsSubscriptionsListener = new StatsSubscriptionsListener(mSubscriptionManager);
mStorageManager = (StorageManager) mContext.getSystemService(StorageManager.class);
-
+ mNetworkStatsManager = mContext.getSystemService(NetworkStatsManager.class);
// Initialize DiskIO
mStoragedUidIoStatsReader = new StoragedUidIoStatsReader();
@@ -809,10 +799,9 @@ public class StatsPullAtomService extends SystemService {
KernelCpuThreadReaderSettingsObserver.getSettingsModifiedReader(mContext);
// Initialize HealthService
- mHealthService = new BatteryService.HealthServiceWrapper();
try {
- mHealthService.init();
- } catch (RemoteException e) {
+ mHealthService = HealthServiceWrapper.create(null);
+ } catch (RemoteException | NoSuchElementException e) {
Slog.e(TAG, "failed to initialize healthHalWrapper");
}
@@ -979,32 +968,6 @@ public class StatsPullAtomService extends SystemService {
registerOemManagedBytesTransfer();
}
- /**
- * Return the {@code INetworkStatsSession} object that holds the necessary properties needed
- * for the subsequent queries to {@link com.android.server.net.NetworkStatsService}. Or
- * null if the service or binder cannot be obtained. Calling this method will trigger poll
- * in NetworkStatsService with once per 15 seconds rate-limit, unless {@code bypassRateLimit}
- * is set to true. This is needed in {@link #getUidNetworkStatsSnapshotForTemplate}, where
- * bypassing the limit is necessary for perfd to supply realtime stats to developers looking at
- * the network usage of their app.
- */
- @Nullable
- private INetworkStatsSession getNetworkStatsSession(boolean bypassRateLimit) {
- final INetworkStatsService networkStatsService =
- INetworkStatsService.Stub.asInterface(
- ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
- if (networkStatsService == null) return null;
-
- try {
- return networkStatsService.openSessionForUsageStats(
- FLAG_AUGMENT_WITH_SUBSCRIPTION_PLAN | (bypassRateLimit ? FLAG_POLL_FORCE
- : FLAG_POLL_ON_OPEN), mContext.getOpPackageName());
- } catch (RemoteException e) {
- Slog.e(TAG, "Cannot get NetworkStats session", e);
- return null;
- }
- }
-
private IThermalService getIThermalService() {
synchronized (mThermalLock) {
if (mThermalService == null) {
@@ -1135,8 +1098,8 @@ public class StatsPullAtomService extends SystemService {
case FrameworkStatsLog.WIFI_BYTES_TRANSFER: {
final NetworkStats stats = getUidNetworkStatsSnapshotForTransport(TRANSPORT_WIFI);
if (stats != null) {
- ret.add(new NetworkStatsExt(stats.groupedByUid(), new int[] {TRANSPORT_WIFI},
- /*slicedByFgbg=*/false));
+ ret.add(new NetworkStatsExt(sliceNetworkStatsByUid(stats),
+ new int[] {TRANSPORT_WIFI}, /*slicedByFgbg=*/false));
}
break;
}
@@ -1152,7 +1115,7 @@ public class StatsPullAtomService extends SystemService {
final NetworkStats stats =
getUidNetworkStatsSnapshotForTransport(TRANSPORT_CELLULAR);
if (stats != null) {
- ret.add(new NetworkStatsExt(stats.groupedByUid(),
+ ret.add(new NetworkStatsExt(sliceNetworkStatsByUid(stats),
new int[] {TRANSPORT_CELLULAR}, /*slicedByFgbg=*/false));
}
break;
@@ -1168,9 +1131,10 @@ public class StatsPullAtomService extends SystemService {
}
case FrameworkStatsLog.BYTES_TRANSFER_BY_TAG_AND_METERED: {
final NetworkStats wifiStats = getUidNetworkStatsSnapshotForTemplate(
- buildTemplateWifiWildcard(), /*includeTags=*/true);
+ new NetworkTemplate.Builder(MATCH_WIFI).build(), /*includeTags=*/true);
final NetworkStats cellularStats = getUidNetworkStatsSnapshotForTemplate(
- buildTemplateMobileWildcard(), /*includeTags=*/true);
+ new NetworkTemplate.Builder(MATCH_MOBILE)
+ .setMeteredness(METERED_YES).build(), /*includeTags=*/true);
if (wifiStats != null && cellularStats != null) {
final NetworkStats stats = wifiStats.add(cellularStats);
ret.add(new NetworkStatsExt(sliceNetworkStatsByUidTagAndMetered(stats),
@@ -1215,13 +1179,14 @@ public class StatsPullAtomService extends SystemService {
Slog.e(TAG, "baseline is null for " + atomTag + ", return.");
return StatsManager.PULL_SKIP;
}
+
final NetworkStatsExt diff = new NetworkStatsExt(
- item.stats.subtract(baseline.stats).removeEmptyEntries(), item.transports,
+ removeEmptyEntries(item.stats.subtract(baseline.stats)), item.transports,
item.slicedByFgbg, item.slicedByTag, item.slicedByMetered, item.ratType,
item.subInfo, item.oemManaged);
// If no diff, skip.
- if (diff.stats.size() == 0) continue;
+ if (!diff.stats.iterator().hasNext()) continue;
switch (atomTag) {
case FrameworkStatsLog.BYTES_TRANSFER_BY_TAG_AND_METERED:
@@ -1240,25 +1205,32 @@ public class StatsPullAtomService extends SystemService {
return StatsManager.PULL_SUCCESS;
}
+ @NonNull private static NetworkStats removeEmptyEntries(NetworkStats stats) {
+ NetworkStats ret = new NetworkStats(0, 1);
+ for (NetworkStats.Entry e : stats) {
+ if (e.getRxBytes() != 0 || e.getRxPackets() != 0 || e.getTxBytes() != 0
+ || e.getTxPackets() != 0 || e.getOperations() != 0) {
+ ret = ret.addEntry(e);
+ }
+ }
+ return ret;
+ }
+
private void addNetworkStats(int atomTag, @NonNull List<StatsEvent> ret,
@NonNull NetworkStatsExt statsExt) {
- int size = statsExt.stats.size();
- final NetworkStats.Entry entry = new NetworkStats.Entry(); // For recycling
- for (int j = 0; j < size; j++) {
- statsExt.stats.getValues(j, entry);
+ for (NetworkStats.Entry entry : statsExt.stats) {
StatsEvent statsEvent;
-
if (statsExt.slicedByFgbg) {
// MobileBytesTransferByFgBg atom or WifiBytesTransferByFgBg atom.
statsEvent = FrameworkStatsLog.buildStatsEvent(
- atomTag, entry.uid,
- (entry.set > 0), entry.rxBytes, entry.rxPackets, entry.txBytes,
- entry.txPackets);
+ atomTag, entry.getUid(),
+ (entry.getSet() > 0), entry.getRxBytes(), entry.getRxPackets(),
+ entry.getTxBytes(), entry.getTxPackets());
} else {
// MobileBytesTransfer atom or WifiBytesTransfer atom.
statsEvent = FrameworkStatsLog.buildStatsEvent(
- atomTag, entry.uid, entry.rxBytes,
- entry.rxPackets, entry.txBytes, entry.txPackets);
+ atomTag, entry.getUid(), entry.getRxBytes(),
+ entry.getRxPackets(), entry.getTxBytes(), entry.getTxPackets());
}
ret.add(statsEvent);
}
@@ -1266,33 +1238,31 @@ public class StatsPullAtomService extends SystemService {
private void addBytesTransferByTagAndMeteredAtoms(@NonNull NetworkStatsExt statsExt,
@NonNull List<StatsEvent> pulledData) {
- final NetworkStats.Entry entry = new NetworkStats.Entry(); // for recycling
- for (int i = 0; i < statsExt.stats.size(); i++) {
- statsExt.stats.getValues(i, entry);
+ for (NetworkStats.Entry entry : statsExt.stats) {
pulledData.add(FrameworkStatsLog.buildStatsEvent(
- FrameworkStatsLog.BYTES_TRANSFER_BY_TAG_AND_METERED, entry.uid,
- entry.metered == NetworkStats.METERED_YES, entry.tag, entry.rxBytes,
- entry.rxPackets, entry.txBytes, entry.txPackets));
+ FrameworkStatsLog.BYTES_TRANSFER_BY_TAG_AND_METERED, entry.getUid(),
+ entry.getMetered() == NetworkStats.METERED_YES, entry.getTag(),
+ entry.getRxBytes(), entry.getRxPackets(), entry.getTxBytes(),
+ entry.getTxPackets()));
}
}
private void addDataUsageBytesTransferAtoms(@NonNull NetworkStatsExt statsExt,
@NonNull List<StatsEvent> pulledData) {
- // Workaround for 5G NSA mode, see {@link NetworkTemplate#NETWORK_TYPE_5G_NSA}.
+ // Workaround for 5G NSA mode, see {@link NetworkStatsManager#NETWORK_TYPE_5G_NSA}.
// 5G NSA mode means the primary cell is LTE with a secondary connection to an
// NR cell. To mitigate risk, NetworkStats is currently storing this state as
// a fake RAT type rather than storing the boolean separately.
- final boolean is5GNsa = statsExt.ratType == NetworkTemplate.NETWORK_TYPE_5G_NSA;
+ final boolean is5GNsa = statsExt.ratType == NetworkStatsManager.NETWORK_TYPE_5G_NSA;
// Report NR connected in 5G non-standalone mode, or if the RAT type is NR to begin with.
final boolean isNR = is5GNsa || statsExt.ratType == TelephonyManager.NETWORK_TYPE_NR;
- final NetworkStats.Entry entry = new NetworkStats.Entry(); // for recycling
- for (int i = 0; i < statsExt.stats.size(); i++) {
- statsExt.stats.getValues(i, entry);
+ for (NetworkStats.Entry entry : statsExt.stats) {
pulledData.add(FrameworkStatsLog.buildStatsEvent(
- FrameworkStatsLog.DATA_USAGE_BYTES_TRANSFER, entry.set, entry.rxBytes,
- entry.rxPackets, entry.txBytes, entry.txPackets,
+ FrameworkStatsLog.DATA_USAGE_BYTES_TRANSFER,
+ entry.getSet(), entry.getRxBytes(), entry.getRxPackets(),
+ entry.getTxBytes(), entry.getTxPackets(),
is5GNsa ? TelephonyManager.NETWORK_TYPE_LTE : statsExt.ratType,
// Fill information about subscription, these cannot be null since invalid data
// would be filtered when adding into subInfo list.
@@ -1306,38 +1276,35 @@ public class StatsPullAtomService extends SystemService {
private void addOemDataUsageBytesTransferAtoms(@NonNull NetworkStatsExt statsExt,
@NonNull List<StatsEvent> pulledData) {
- final NetworkStats.Entry entry = new NetworkStats.Entry(); // for recycling
final int oemManaged = statsExt.oemManaged;
for (final int transport : statsExt.transports) {
- for (int i = 0; i < statsExt.stats.size(); i++) {
- statsExt.stats.getValues(i, entry);
+ for (NetworkStats.Entry entry : statsExt.stats) {
pulledData.add(FrameworkStatsLog.buildStatsEvent(
- FrameworkStatsLog.OEM_MANAGED_BYTES_TRANSFER, entry.uid, (entry.set > 0),
- oemManaged, transport, entry.rxBytes, entry.rxPackets, entry.txBytes,
- entry.txPackets));
+ FrameworkStatsLog.OEM_MANAGED_BYTES_TRANSFER, entry.getUid(),
+ (entry.getSet() > 0), oemManaged, transport, entry.getRxBytes(),
+ entry.getRxPackets(), entry.getTxBytes(), entry.getTxPackets()));
}
}
}
@NonNull private List<NetworkStatsExt> getDataUsageBytesTransferSnapshotForOemManaged() {
- final int[] transports = new int[] {MATCH_ETHERNET, MATCH_MOBILE_WILDCARD,
- MATCH_WIFI_WILDCARD};
- final int[] oemManagedTypes = new int[] {OEM_PAID | OEM_PRIVATE, OEM_PAID, OEM_PRIVATE};
+ final int[] matchRules = new int[] {MATCH_ETHERNET, MATCH_MOBILE, MATCH_WIFI};
+ final int[] oemManagedTypes = new int[] {OEM_MANAGED_PAID | OEM_MANAGED_PRIVATE,
+ OEM_MANAGED_PAID, OEM_MANAGED_PRIVATE};
final List<NetworkStatsExt> ret = new ArrayList<>();
- for (final int transport : transports) {
+ for (final int matchRule : matchRules) {
for (final int oemManaged : oemManagedTypes) {
- /* A null subscriberId will set wildcard=true, since we aren't trying to select a
- specific ssid or subscriber. */
- final NetworkTemplate template = new NetworkTemplate(transport,
- /*subscriberId=*/null, /*matchSubscriberIds=*/null, /*networkId=*/null,
- METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL,
- oemManaged);
+ // Subscriber Ids and Wifi Network Keys will not be set since the purpose is to
+ // slice statistics of different OEM managed networks among all network types.
+ // Thus, specifying networks through their identifiers are not needed.
+ final NetworkTemplate template = new NetworkTemplate.Builder(matchRule)
+ .setOemManaged(oemManaged).build();
final NetworkStats stats = getUidNetworkStatsSnapshotForTemplate(template, true);
if (stats != null) {
ret.add(new NetworkStatsExt(sliceNetworkStatsByUidTagAndMetered(stats),
- new int[] {transport}, /*slicedByFgbg=*/true, /*slicedByTag=*/true,
+ new int[] {matchRule}, /*slicedByFgbg=*/true, /*slicedByTag=*/true,
/*slicedByMetered=*/true, TelephonyManager.NETWORK_TYPE_UNKNOWN,
/*subInfo=*/null, oemManaged));
}
@@ -1351,10 +1318,18 @@ public class StatsPullAtomService extends SystemService {
* Create a snapshot of NetworkStats for a given transport.
*/
@Nullable private NetworkStats getUidNetworkStatsSnapshotForTransport(int transport) {
- final NetworkTemplate template = (transport == TRANSPORT_CELLULAR)
- ? NetworkTemplate.buildTemplateMobileWithRatType(
- /*subscriptionId=*/null, NETWORK_TYPE_ALL, METERED_YES)
- : NetworkTemplate.buildTemplateWifiWildcard();
+ NetworkTemplate template = null;
+ switch (transport) {
+ case TRANSPORT_CELLULAR:
+ template = new NetworkTemplate.Builder(MATCH_MOBILE)
+ .setMeteredness(METERED_YES).build();
+ break;
+ case TRANSPORT_WIFI:
+ template = new NetworkTemplate.Builder(MATCH_WIFI).build();
+ break;
+ default:
+ Log.wtf(TAG, "Unexpected transport.");
+ }
return getUidNetworkStatsSnapshotForTemplate(template, /*includeTags=*/false);
}
@@ -1370,22 +1345,32 @@ public class StatsPullAtomService extends SystemService {
final long currentTimeInMillis = MICROSECONDS.toMillis(SystemClock.currentTimeMicro());
final long bucketDuration = Settings.Global.getLong(mContext.getContentResolver(),
NETSTATS_UID_BUCKET_DURATION, NETSTATS_UID_DEFAULT_BUCKET_DURATION_MS);
- try {
- // TODO (b/156313635): This is short-term hack to allow perfd gets updated networkStats
- // history when query in every second in order to show realtime statistics. However,
- // this is not a good long-term solution since NetworkStatsService will make frequent
- // I/O and also block main thread when polling.
- // Consider making perfd queries NetworkStatsService directly.
- final NetworkStats stats = getNetworkStatsSession(template.getMatchRule()
- == NetworkTemplate.MATCH_WIFI_WILDCARD).getSummaryForAllUid(template,
- currentTimeInMillis - elapsedMillisSinceBoot - bucketDuration,
- currentTimeInMillis, includeTags);
- return stats;
- } catch (RemoteException | NullPointerException e) {
- Slog.e(TAG, "Pulling netstats for template=" + template + " and includeTags="
- + includeTags + " causes error", e);
+
+ // TODO (b/156313635): This is short-term hack to allow perfd gets updated networkStats
+ // history when query in every second in order to show realtime statistics. However,
+ // this is not a good long-term solution since NetworkStatsService will make frequent
+ // I/O and also block main thread when polling.
+ // Consider making perfd queries NetworkStatsService directly.
+ if (template.getMatchRule() == MATCH_WIFI && template.getSubscriberIds().isEmpty()) {
+ mNetworkStatsManager.forceUpdate();
}
- return null;
+
+ final android.app.usage.NetworkStats queryNonTaggedStats =
+ mNetworkStatsManager.querySummary(
+ template, currentTimeInMillis - elapsedMillisSinceBoot - bucketDuration,
+ currentTimeInMillis);
+
+ final NetworkStats nonTaggedStats =
+ NetworkStatsUtils.fromPublicNetworkStats(queryNonTaggedStats);
+ if (!includeTags) return nonTaggedStats;
+
+ final android.app.usage.NetworkStats queryTaggedStats =
+ mNetworkStatsManager.queryTaggedSummary(template,
+ currentTimeInMillis - elapsedMillisSinceBoot - bucketDuration,
+ currentTimeInMillis);
+ final NetworkStats taggedStats =
+ NetworkStatsUtils.fromPublicNetworkStats(queryTaggedStats);
+ return nonTaggedStats.add(taggedStats);
}
@NonNull private List<NetworkStatsExt> getDataUsageBytesTransferSnapshotForSub(
@@ -1393,8 +1378,10 @@ public class StatsPullAtomService extends SystemService {
final List<NetworkStatsExt> ret = new ArrayList<>();
for (final int ratType : getAllCollapsedRatTypes()) {
final NetworkTemplate template =
- buildTemplateMobileWithRatType(subInfo.subscriberId, ratType,
- METERED_YES);
+ new NetworkTemplate.Builder(MATCH_MOBILE)
+ .setSubscriberIds(Set.of(subInfo.subscriberId))
+ .setRatType(ratType)
+ .setMeteredness(METERED_YES).build();
final NetworkStats stats =
getUidNetworkStatsSnapshotForTemplate(template, /*includeTags=*/false);
if (stats != null) {
@@ -1407,27 +1394,72 @@ public class StatsPullAtomService extends SystemService {
return ret;
}
+ /**
+ * Return all supported collapsed RAT types that could be returned by
+ * {@link android.app.usage.NetworkStatsManager#getCollapsedRatType(int)}.
+ */
+ @NonNull
+ private static int[] getAllCollapsedRatTypes() {
+ final int[] ratTypes = TelephonyManager.getAllNetworkTypes();
+ final HashSet<Integer> collapsedRatTypes = new HashSet<>();
+ for (final int ratType : ratTypes) {
+ collapsedRatTypes.add(NetworkStatsManager.getCollapsedRatType(ratType));
+ }
+ // Add NETWORK_TYPE_5G_NSA to the returned list since 5G NSA is a virtual RAT type and
+ // it is not in TelephonyManager#NETWORK_TYPE_* constants.
+ // See {@link NetworkStatsManager#NETWORK_TYPE_5G_NSA}.
+ collapsedRatTypes.add(
+ NetworkStatsManager.getCollapsedRatType(NetworkStatsManager.NETWORK_TYPE_5G_NSA));
+ // Ensure that unknown type is returned.
+ collapsedRatTypes.add(TelephonyManager.NETWORK_TYPE_UNKNOWN);
+ return com.android.net.module.util.CollectionUtils.toIntArray(collapsedRatTypes);
+ }
+
+ @NonNull private NetworkStats sliceNetworkStatsByUid(@NonNull NetworkStats stats) {
+ return sliceNetworkStats(stats,
+ (entry) -> {
+ return new NetworkStats.Entry(null /* IFACE_ALL */, entry.getUid(),
+ NetworkStats.SET_ALL, NetworkStats.TAG_NONE,
+ NetworkStats.METERED_ALL, NetworkStats.ROAMING_ALL,
+ NetworkStats.DEFAULT_NETWORK_ALL,
+ entry.getRxBytes(), entry.getRxPackets(),
+ entry.getTxBytes(), entry.getTxPackets(), 0);
+ });
+ }
+
@NonNull private NetworkStats sliceNetworkStatsByFgbg(@NonNull NetworkStats stats) {
return sliceNetworkStats(stats,
- (newEntry, oldEntry) -> {
- newEntry.set = oldEntry.set;
+ (entry) -> {
+ return new NetworkStats.Entry(null /* IFACE_ALL */, NetworkStats.UID_ALL,
+ entry.getSet(), NetworkStats.TAG_NONE,
+ NetworkStats.METERED_ALL, NetworkStats.ROAMING_ALL,
+ NetworkStats.DEFAULT_NETWORK_ALL,
+ entry.getRxBytes(), entry.getRxPackets(),
+ entry.getTxBytes(), entry.getTxPackets(), 0);
});
}
@NonNull private NetworkStats sliceNetworkStatsByUidAndFgbg(@NonNull NetworkStats stats) {
return sliceNetworkStats(stats,
- (newEntry, oldEntry) -> {
- newEntry.uid = oldEntry.uid;
- newEntry.set = oldEntry.set;
+ (entry) -> {
+ return new NetworkStats.Entry(null /* IFACE_ALL */, entry.getUid(),
+ entry.getSet(), NetworkStats.TAG_NONE,
+ NetworkStats.METERED_ALL, NetworkStats.ROAMING_ALL,
+ NetworkStats.DEFAULT_NETWORK_ALL,
+ entry.getRxBytes(), entry.getRxPackets(),
+ entry.getTxBytes(), entry.getTxPackets(), 0);
});
}
@NonNull private NetworkStats sliceNetworkStatsByUidTagAndMetered(@NonNull NetworkStats stats) {
return sliceNetworkStats(stats,
- (newEntry, oldEntry) -> {
- newEntry.uid = oldEntry.uid;
- newEntry.tag = oldEntry.tag;
- newEntry.metered = oldEntry.metered;
+ (entry) -> {
+ return new NetworkStats.Entry(null /* IFACE_ALL */, entry.getUid(),
+ NetworkStats.SET_ALL, entry.getTag(),
+ entry.getMetered(), NetworkStats.ROAMING_ALL,
+ NetworkStats.DEFAULT_NETWORK_ALL,
+ entry.getRxBytes(), entry.getRxPackets(),
+ entry.getTxBytes(), entry.getTxPackets(), 0);
});
}
@@ -1437,46 +1469,27 @@ public class StatsPullAtomService extends SystemService {
*
* This function iterates through each NetworkStats.Entry, sets its dimensions equal to the
* default state (with the presumption that we don't want to slice on anything), and then
- * applies the slicer lambda to allow users to control which dimensions to slice on. This is
- * adapted from groupedByUid within NetworkStats.java
+ * applies the slicer lambda to allow users to control which dimensions to slice on.
*
- * @param slicer An operation taking into two parameters, new NetworkStats.Entry and old
- * NetworkStats.Entry, that should be used to copy state from the old to the new.
+ * @param slicer An operation taking one parameter, NetworkStats.Entry, that should be used to
+ * get the state from entry to replace the default value.
* This is useful for slicing by particular dimensions. For example, if we wished
* to slice by uid and tag, we could write the following lambda:
- * (new, old) -> {
- * new.uid = old.uid;
- * new.tag = old.tag;
+ * (entry) -> {
+ * return new NetworkStats.Entry(null, entry.getUid(),
+ * NetworkStats.SET_ALL, entry.getTag(),
+ * NetworkStats.METERED_ALL, NetworkStats.ROAMING_ALL,
+ * NetworkStats.DEFAULT_NETWORK_ALL,
+ * entry.getRxBytes(), entry.getRxPackets(),
+ * entry.getTxBytes(), entry.getTxPackets(), 0);
* }
- * If no slicer is provided, the data is not sliced by any dimensions.
* @return new NeworkStats object appropriately sliced
*/
@NonNull private NetworkStats sliceNetworkStats(@NonNull NetworkStats stats,
- @Nullable BiConsumer<NetworkStats.Entry, NetworkStats.Entry> slicer) {
- final NetworkStats ret = new NetworkStats(stats.getElapsedRealtime(), 1);
-
- final NetworkStats.Entry entry = new NetworkStats.Entry();
- entry.uid = NetworkStats.UID_ALL;
- entry.iface = NetworkStats.IFACE_ALL;
- entry.set = NetworkStats.SET_ALL;
- entry.tag = NetworkStats.TAG_NONE;
- entry.metered = NetworkStats.METERED_ALL;
- entry.roaming = NetworkStats.ROAMING_ALL;
- entry.defaultNetwork = NetworkStats.DEFAULT_NETWORK_ALL;
-
- final NetworkStats.Entry recycle = new NetworkStats.Entry(); // used for retrieving values
- for (int i = 0; i < stats.size(); i++) {
- stats.getValues(i, recycle);
- if (slicer != null) {
- slicer.accept(entry, recycle);
- }
-
- entry.rxBytes = recycle.rxBytes;
- entry.rxPackets = recycle.rxPackets;
- entry.txBytes = recycle.txBytes;
- entry.txPackets = recycle.txPackets;
- // Operations purposefully omitted since we don't use them for statsd.
- ret.combineValues(entry);
+ @NonNull Function<NetworkStats.Entry, NetworkStats.Entry> slicer) {
+ NetworkStats ret = new NetworkStats(0, 1);
+ for (NetworkStats.Entry e : stats) {
+ ret = ret.addEntry(slicer.apply(e));
}
return ret;
}
@@ -1615,7 +1628,7 @@ public class StatsPullAtomService extends SystemService {
int pullBluetoothBytesTransferLocked(int atomTag, List<StatsEvent> pulledData) {
BluetoothActivityEnergyInfo info = fetchBluetoothData();
- if (info == null || info.getUidTraffic() == null) {
+ if (info == null) {
return StatsManager.PULL_SKIP;
}
for (UidTraffic traffic : info.getUidTraffic()) {
@@ -2073,7 +2086,7 @@ public class StatsPullAtomService extends SystemService {
if (info == null) {
return StatsManager.PULL_SKIP;
}
- pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, info.getTimeStamp(),
+ pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, info.getTimestampMillis(),
info.getBluetoothStackState(), info.getControllerTxTimeMillis(),
info.getControllerRxTimeMillis(), info.getControllerIdleTimeMillis(),
info.getControllerEnergyUsed()));
@@ -3987,38 +4000,40 @@ public class StatsPullAtomService extends SystemService {
}
int pullHealthHalLocked(int atomTag, List<StatsEvent> pulledData) {
- IHealth healthService = mHealthService.getLastService();
- if (healthService == null) {
+ if (mHealthService == null) {
return StatsManager.PULL_SKIP;
}
+ android.hardware.health.HealthInfo healthInfo;
try {
- healthService.getHealthInfo((result, value) -> {
- int pulledValue;
- switch(atomTag) {
- case FrameworkStatsLog.BATTERY_LEVEL:
- pulledValue = value.legacy.batteryLevel;
- break;
- case FrameworkStatsLog.REMAINING_BATTERY_CAPACITY:
- pulledValue = value.legacy.batteryChargeCounter;
- break;
- case FrameworkStatsLog.FULL_BATTERY_CAPACITY:
- pulledValue = value.legacy.batteryFullCharge;
- break;
- case FrameworkStatsLog.BATTERY_VOLTAGE:
- pulledValue = value.legacy.batteryVoltage;
- break;
- case FrameworkStatsLog.BATTERY_CYCLE_COUNT:
- pulledValue = value.legacy.batteryCycleCount;
- break;
- default:
- throw new IllegalStateException("Invalid atomTag in healthHal puller: "
- + atomTag);
- }
- pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, pulledValue));
- });
+ healthInfo = mHealthService.getHealthInfo();
} catch (RemoteException | IllegalStateException e) {
return StatsManager.PULL_SKIP;
}
+ if (healthInfo == null) {
+ return StatsManager.PULL_SKIP;
+ }
+
+ int pulledValue;
+ switch (atomTag) {
+ case FrameworkStatsLog.BATTERY_LEVEL:
+ pulledValue = healthInfo.batteryLevel;
+ break;
+ case FrameworkStatsLog.REMAINING_BATTERY_CAPACITY:
+ pulledValue = healthInfo.batteryChargeCounterUah;
+ break;
+ case FrameworkStatsLog.FULL_BATTERY_CAPACITY:
+ pulledValue = healthInfo.batteryFullChargeUah;
+ break;
+ case FrameworkStatsLog.BATTERY_VOLTAGE:
+ pulledValue = healthInfo.batteryVoltageMillivolts;
+ break;
+ case FrameworkStatsLog.BATTERY_CYCLE_COUNT:
+ pulledValue = healthInfo.batteryCycleCount;
+ break;
+ default:
+ return StatsManager.PULL_SKIP;
+ }
+ pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, pulledValue));
return StatsManager.PULL_SUCCESS;
}
diff --git a/services/core/java/com/android/server/storage/AppFuseBridge.java b/services/core/java/com/android/server/storage/AppFuseBridge.java
index b00540fd2a52..292314840efb 100644
--- a/services/core/java/com/android/server/storage/AppFuseBridge.java
+++ b/services/core/java/com/android/server/storage/AppFuseBridge.java
@@ -24,7 +24,7 @@ import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.FuseUnavailableMountException;
import com.android.internal.util.Preconditions;
-import com.android.server.NativeDaemonConnectorException;
+import com.android.server.AppFuseMountException;
import libcore.io.IoUtils;
import java.util.concurrent.CountDownLatch;
@@ -55,7 +55,7 @@ public class AppFuseBridge implements Runnable {
}
public ParcelFileDescriptor addBridge(MountScope mountScope)
- throws FuseUnavailableMountException, NativeDaemonConnectorException {
+ throws FuseUnavailableMountException, AppFuseMountException {
/*
** Dead Lock between Java lock (AppFuseBridge.java) and Native lock (FuseBridgeLoop.cc)
**
@@ -112,7 +112,7 @@ public class AppFuseBridge implements Runnable {
try {
int flags = FileUtils.translateModePfdToPosix(mode);
return scope.openFile(mountId, fileId, flags);
- } catch (NativeDaemonConnectorException error) {
+ } catch (AppFuseMountException error) {
throw new FuseUnavailableMountException(mountId);
}
}
@@ -160,9 +160,9 @@ public class AppFuseBridge implements Runnable {
return mMountResult;
}
- public abstract ParcelFileDescriptor open() throws NativeDaemonConnectorException;
+ public abstract ParcelFileDescriptor open() throws AppFuseMountException;
public abstract ParcelFileDescriptor openFile(int mountId, int fileId, int flags)
- throws NativeDaemonConnectorException;
+ throws AppFuseMountException;
}
private native long native_new();
diff --git a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
index 0f37450c24c9..e7d05b623481 100644
--- a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
+++ b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
@@ -527,12 +527,13 @@ public final class TextClassificationManagerService extends ITextClassifierServi
callback.onFailure();
return;
}
- textClassifierServiceConsumer.accept(serviceState.mService);
+ consumeServiceNoExceptLocked(textClassifierServiceConsumer, serviceState.mService);
} else {
serviceState.mPendingRequests.add(
new PendingRequest(
methodName,
- () -> textClassifierServiceConsumer.accept(serviceState.mService),
+ () -> consumeServiceNoExceptLocked(
+ textClassifierServiceConsumer, serviceState.mService),
callback::onFailure, callback.asBinder(),
this,
serviceState,
@@ -541,6 +542,16 @@ public final class TextClassificationManagerService extends ITextClassifierServi
}
}
+ private static void consumeServiceNoExceptLocked(
+ @NonNull ThrowingConsumer<ITextClassifierService> textClassifierServiceConsumer,
+ @Nullable ITextClassifierService service) {
+ try {
+ textClassifierServiceConsumer.accept(service);
+ } catch (RuntimeException | Error e) {
+ Slog.e(LOG_TAG, "Exception when consume textClassifierService: " + e);
+ }
+ }
+
private static ITextClassifierCallback wrap(ITextClassifierCallback orig) {
return new CallbackWrapper(orig);
}
diff --git a/services/core/java/com/android/server/timedetector/OWNERS b/services/core/java/com/android/server/timedetector/OWNERS
index 8f8089717e3b..67fc9d66d69d 100644
--- a/services/core/java/com/android/server/timedetector/OWNERS
+++ b/services/core/java/com/android/server/timedetector/OWNERS
@@ -1,3 +1,3 @@
# Bug component: 847766
-mingaleev@google.com
-include /core/java/android/app/timedetector/OWNERS
+# This code is maintained by the same OWNERS as timezonedetector.
+include /services/core/java/com/android/server/timezonedetector/OWNERS
diff --git a/services/core/java/com/android/server/timezone/OWNERS b/services/core/java/com/android/server/timezone/OWNERS
index 8f8089717e3b..2d365747473a 100644
--- a/services/core/java/com/android/server/timezone/OWNERS
+++ b/services/core/java/com/android/server/timezone/OWNERS
@@ -1,3 +1,2 @@
-# Bug component: 847766
-mingaleev@google.com
-include /core/java/android/app/timedetector/OWNERS
+# Bug component: 24949
+include platform/libcore:/OWNERS
diff --git a/services/core/java/com/android/server/timezonedetector/OWNERS b/services/core/java/com/android/server/timezonedetector/OWNERS
index 8f8089717e3b..029324246c91 100644
--- a/services/core/java/com/android/server/timezonedetector/OWNERS
+++ b/services/core/java/com/android/server/timezonedetector/OWNERS
@@ -1,3 +1,7 @@
# Bug component: 847766
+# This is the main list for platform time / time zone detection maintainers, for this dir and
+# ultimately referenced by other OWNERS files for components maintained by the same team.
+nfuller@google.com
+jmorace@google.com
mingaleev@google.com
-include /core/java/android/app/timedetector/OWNERS
+narayan@google.com
diff --git a/services/core/java/com/android/server/timezonedetector/ReferenceWithHistory.java b/services/core/java/com/android/server/timezonedetector/ReferenceWithHistory.java
index 4eb1b99183f1..60068cb9a766 100644
--- a/services/core/java/com/android/server/timezonedetector/ReferenceWithHistory.java
+++ b/services/core/java/com/android/server/timezonedetector/ReferenceWithHistory.java
@@ -25,6 +25,7 @@ import android.util.IndentingPrintWriter;
import java.time.Duration;
import java.util.ArrayDeque;
+import java.util.Iterator;
/**
* A class that behaves like the following definition, except it stores the history of values set
@@ -112,9 +113,11 @@ public final class ReferenceWithHistory<V> {
if (mValues == null) {
ipw.println("{Empty}");
} else {
- int i = mSetCount;
- for (TimestampedValue<V> valueHolder : mValues) {
- ipw.print(--i);
+ int i = mSetCount - mValues.size();
+ Iterator<TimestampedValue<V>> reverseIterator = mValues.descendingIterator();
+ while (reverseIterator.hasNext()) {
+ TimestampedValue<V> valueHolder = reverseIterator.next();
+ ipw.print(i++);
ipw.print("@");
ipw.print(Duration.ofMillis(valueHolder.getReferenceTimeMillis()).toString());
ipw.print(": ");
diff --git a/services/core/java/com/android/server/tracing/TracingServiceProxy.java b/services/core/java/com/android/server/tracing/TracingServiceProxy.java
index ff2f08bc4a50..10e868d06766 100644
--- a/services/core/java/com/android/server/tracing/TracingServiceProxy.java
+++ b/services/core/java/com/android/server/tracing/TracingServiceProxy.java
@@ -15,52 +15,99 @@
*/
package com.android.server.tracing;
+import static com.android.internal.util.FrameworkStatsLog.TRACING_SERVICE_REPORT_EVENT;
+import static com.android.internal.util.FrameworkStatsLog.TRACING_SERVICE_REPORT_EVENT__EVENT__TRACING_SERVICE_REPORT_BEGIN;
+import static com.android.internal.util.FrameworkStatsLog.TRACING_SERVICE_REPORT_EVENT__EVENT__TRACING_SERVICE_REPORT_BIND_PERM_INCORRECT;
+import static com.android.internal.util.FrameworkStatsLog.TRACING_SERVICE_REPORT_EVENT__EVENT__TRACING_SERVICE_REPORT_SVC_COMM_ERROR;
+import static com.android.internal.util.FrameworkStatsLog.TRACING_SERVICE_REPORT_EVENT__EVENT__TRACING_SERVICE_REPORT_SVC_HANDOFF;
+import static com.android.internal.util.FrameworkStatsLog.TRACING_SERVICE_REPORT_EVENT__EVENT__TRACING_SERVICE_REPORT_SVC_PERM_MISSING;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ServiceInfo;
import android.os.Binder;
+import android.os.IMessenger;
+import android.os.Message;
+import android.os.ParcelFileDescriptor;
+import android.os.ParcelFileDescriptor.AutoCloseInputStream;
+import android.os.ParcelFileDescriptor.AutoCloseOutputStream;
import android.os.UserHandle;
+import android.service.tracing.TraceReportService;
import android.tracing.ITracingServiceProxy;
+import android.tracing.TraceReportParams;
import android.util.Log;
+import android.util.LruCache;
+import android.util.Slog;
+import com.android.internal.infra.ServiceConnector;
+import com.android.internal.util.FrameworkStatsLog;
import com.android.server.SystemService;
+import java.io.IOException;
+
/**
* TracingServiceProxy is the system_server intermediary between the Perfetto tracing daemon and the
- * system tracing app Traceur.
+ * other components (e.g. system tracing app Traceur, trace reporting apps).
*
* Access to this service is restricted via SELinux. Normal apps do not have access.
*
* @hide
*/
public class TracingServiceProxy extends SystemService {
- private static final String TAG = "TracingServiceProxy";
-
public static final String TRACING_SERVICE_PROXY_BINDER_NAME = "tracing.proxy";
-
+ private static final String TAG = "TracingServiceProxy";
private static final String TRACING_APP_PACKAGE_NAME = "com.android.traceur";
private static final String TRACING_APP_ACTIVITY = "com.android.traceur.StopTraceService";
+ private static final int MAX_CACHED_REPORTER_SERVICES = 8;
+
+ // The maximum size of the trace allowed if the option |usePipeForTesting| is set.
+ // Note: this size MUST be smaller than the buffer size of the pipe (i.e. what you can
+ // write to the pipe without blocking) to avoid system_server blocking on this.
+ // (on Linux, the minimum value is 4K i.e. 1 minimally sized page).
+ private static final int MAX_FILE_SIZE_BYTES_TO_PIPE = 1024;
+
// Keep this in sync with the definitions in TraceService
private static final String INTENT_ACTION_NOTIFY_SESSION_STOPPED =
"com.android.traceur.NOTIFY_SESSION_STOPPED";
private static final String INTENT_ACTION_NOTIFY_SESSION_STOLEN =
"com.android.traceur.NOTIFY_SESSION_STOLEN";
+ private static final int REPORT_BEGIN =
+ TRACING_SERVICE_REPORT_EVENT__EVENT__TRACING_SERVICE_REPORT_BEGIN;
+ private static final int REPORT_SVC_HANDOFF =
+ TRACING_SERVICE_REPORT_EVENT__EVENT__TRACING_SERVICE_REPORT_SVC_HANDOFF;
+ private static final int REPORT_BIND_PERM_INCORRECT =
+ TRACING_SERVICE_REPORT_EVENT__EVENT__TRACING_SERVICE_REPORT_BIND_PERM_INCORRECT;
+ private static final int REPORT_SVC_PERM_MISSING =
+ TRACING_SERVICE_REPORT_EVENT__EVENT__TRACING_SERVICE_REPORT_SVC_PERM_MISSING;
+ private static final int REPORT_SVC_COMM_ERROR =
+ TRACING_SERVICE_REPORT_EVENT__EVENT__TRACING_SERVICE_REPORT_SVC_COMM_ERROR;
+
private final Context mContext;
private final PackageManager mPackageManager;
+ private final LruCache<ComponentName, ServiceConnector<IMessenger>> mCachedReporterServices;
private final ITracingServiceProxy.Stub mTracingServiceProxy = new ITracingServiceProxy.Stub() {
/**
- * Notifies system tracing app that a tracing session has ended. If a session is repurposed
- * for use in a bugreport, sessionStolen can be set to indicate that tracing has ended but
- * there is no buffer available to dump.
- */
+ * Notifies system tracing app that a tracing session has ended. If a session is repurposed
+ * for use in a bugreport, sessionStolen can be set to indicate that tracing has ended but
+ * there is no buffer available to dump.
+ */
@Override
public void notifyTraceSessionEnded(boolean sessionStolen) {
- notifyTraceur(sessionStolen);
+ TracingServiceProxy.this.notifyTraceur(sessionStolen);
+ }
+
+ @Override
+ public void reportTrace(@NonNull TraceReportParams params) {
+ TracingServiceProxy.this.reportTrace(params);
}
};
@@ -68,6 +115,7 @@ public class TracingServiceProxy extends SystemService {
super(context);
mContext = context;
mPackageManager = context.getPackageManager();
+ mCachedReporterServices = new LruCache<>(MAX_CACHED_REPORTER_SERVICES);
}
@Override
@@ -103,4 +151,131 @@ public class TracingServiceProxy extends SystemService {
Log.e(TAG, "Failed to locate Traceur", e);
}
}
+
+ private void reportTrace(@NonNull TraceReportParams params) {
+ FrameworkStatsLog.write(TRACING_SERVICE_REPORT_EVENT, REPORT_BEGIN,
+ params.uuidLsb, params.uuidMsb);
+
+ // We don't need to do any permission checks on the caller because access
+ // to this service is guarded by SELinux.
+ ComponentName component = new ComponentName(params.reporterPackageName,
+ params.reporterClassName);
+ if (!hasBindServicePermission(component)) {
+ FrameworkStatsLog.write(TRACING_SERVICE_REPORT_EVENT, REPORT_BIND_PERM_INCORRECT,
+ params.uuidLsb, params.uuidMsb);
+ return;
+ }
+ boolean hasDumpPermission = hasPermission(component, Manifest.permission.DUMP);
+ boolean hasUsageStatsPermission = hasPermission(component,
+ Manifest.permission.PACKAGE_USAGE_STATS);
+ if (!hasDumpPermission || !hasUsageStatsPermission) {
+ FrameworkStatsLog.write(TRACING_SERVICE_REPORT_EVENT, REPORT_SVC_PERM_MISSING,
+ params.uuidLsb, params.uuidMsb);
+ return;
+ }
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ reportTrace(getOrCreateReporterService(component), params);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ private void reportTrace(
+ @NonNull ServiceConnector<IMessenger> reporterService,
+ @NonNull TraceReportParams params) {
+ reporterService.post(messenger -> {
+ if (params.usePipeForTesting) {
+ ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe();
+ try (AutoCloseInputStream i = new AutoCloseInputStream(params.fd)) {
+ try (AutoCloseOutputStream o = new AutoCloseOutputStream(pipe[1])) {
+ byte[] array = i.readNBytes(MAX_FILE_SIZE_BYTES_TO_PIPE);
+ if (array.length == MAX_FILE_SIZE_BYTES_TO_PIPE) {
+ throw new IllegalArgumentException(
+ "Trace file too large when |usePipeForTesting| is set.");
+ }
+ o.write(array);
+ }
+ }
+ params.fd = pipe[0];
+ }
+
+ Message message = Message.obtain();
+ message.what = TraceReportService.MSG_REPORT_TRACE;
+ message.obj = params;
+ messenger.send(message);
+
+ FrameworkStatsLog.write(TRACING_SERVICE_REPORT_EVENT, REPORT_SVC_HANDOFF,
+ params.uuidLsb, params.uuidMsb);
+ }).whenComplete((res, err) -> {
+ if (err != null) {
+ FrameworkStatsLog.write(TRACING_SERVICE_REPORT_EVENT, REPORT_SVC_COMM_ERROR,
+ params.uuidLsb, params.uuidMsb);
+ Slog.e(TAG, "Failed to report trace", err);
+ }
+ try {
+ params.fd.close();
+ } catch (IOException ignored) {
+ }
+ });
+ }
+
+ private ServiceConnector<IMessenger> getOrCreateReporterService(
+ @NonNull ComponentName component) {
+ ServiceConnector<IMessenger> connector = mCachedReporterServices.get(component);
+ if (connector == null) {
+ Intent intent = new Intent();
+ intent.setComponent(component);
+ connector = new ServiceConnector.Impl<IMessenger>(
+ mContext, intent,
+ Context.BIND_AUTO_CREATE | Context.BIND_WAIVE_PRIORITY,
+ mContext.getUser().getIdentifier(), IMessenger.Stub::asInterface) {
+ private static final long DISCONNECT_TIMEOUT_MS = 15_000;
+ private static final long REQUEST_TIMEOUT_MS = 10_000;
+
+ @Override
+ protected long getAutoDisconnectTimeoutMs() {
+ return DISCONNECT_TIMEOUT_MS;
+ }
+
+ @Override
+ protected long getRequestTimeoutMs() {
+ return REQUEST_TIMEOUT_MS;
+ }
+ };
+ mCachedReporterServices.put(intent.getComponent(), connector);
+ }
+ return connector;
+ }
+
+ private boolean hasPermission(@NonNull ComponentName componentName,
+ @NonNull String permission) throws SecurityException {
+ if (mPackageManager.checkPermission(permission, componentName.getPackageName())
+ != PackageManager.PERMISSION_GRANTED) {
+ Slog.e(TAG,
+ "Trace reporting service " + componentName.toShortString() + " does not have "
+ + permission + " permission");
+ return false;
+ }
+ return true;
+ }
+
+ private boolean hasBindServicePermission(@NonNull ComponentName componentName) {
+ ServiceInfo info;
+ try {
+ info = mPackageManager.getServiceInfo(componentName, 0);
+ } catch (NameNotFoundException ex) {
+ Slog.e(TAG,
+ "Trace reporting service " + componentName.toShortString() + " does not exist");
+ return false;
+ }
+ if (!Manifest.permission.BIND_TRACE_REPORT_SERVICE.equals(info.permission)) {
+ Slog.e(TAG,
+ "Trace reporting service " + componentName.toShortString()
+ + " does not request " + Manifest.permission.BIND_TRACE_REPORT_SERVICE
+ + " permission; instead requests " + info.permission);
+ return false;
+ }
+ return true;
+ }
}
diff --git a/services/core/java/com/android/server/tv/TvInputHardwareManager.java b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
index 83085ccb7d8c..f57a852fe8c5 100755
--- a/services/core/java/com/android/server/tv/TvInputHardwareManager.java
+++ b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
@@ -114,6 +114,8 @@ class TvInputHardwareManager implements TvInputHal.Callback {
private final SparseBooleanArray mHdmiStateMap = new SparseBooleanArray();
private final List<Message> mPendingHdmiDeviceEvents = new LinkedList<>();
+ private final List<Message> mPendingTvinputInfoEvents = new LinkedList<>();
+
// Calls to mListener should happen here.
private final Handler mHandler = new ListenerHandler();
@@ -229,7 +231,16 @@ class TvInputHardwareManager implements TvInputHal.Callback {
connection.getInputStateLocked(), 0, inputId).sendToTarget();
}
}
- }
+ } else {
+ Message msg = mHandler.obtainMessage(ListenerHandler.TVINPUT_INFO_ADDED,
+ deviceId, cableConnectionStatus, connection);
+ for (Iterator<Message> it = mPendingTvinputInfoEvents.iterator(); it.hasNext();) {
+ if (it.next().arg1 == deviceId) {
+ it.remove();
+ }
+ }
+ mPendingTvinputInfoEvents.add(msg);
+ }
ITvInputHardwareCallback callback = connection.getCallbackLocked();
if (callback != null) {
try {
@@ -288,6 +299,8 @@ class TvInputHardwareManager implements TvInputHal.Callback {
}
mHardwareInputIdMap.put(deviceId, info.getId());
mInputMap.put(info.getId(), info);
+ processPendingTvInputInfoEventsLocked();
+ Slog.d(TAG,"deviceId ="+ deviceId+", tvinputinfo = "+info);
// Process pending state changes
@@ -530,6 +543,20 @@ class TvInputHardwareManager implements TvInputHal.Callback {
}
}
+
+ private void processPendingTvInputInfoEventsLocked() {
+ for (Iterator<Message> it = mPendingTvinputInfoEvents.iterator(); it.hasNext(); ) {
+ Message msg = it.next();
+ int deviceId = msg.arg1;
+ String inputId = mHardwareInputIdMap.get(deviceId);
+ if (inputId != null) {
+ msg.sendToTarget();
+ it.remove();
+ }
+ }
+ }
+
+
private void updateVolume() {
mCurrentMaxIndex = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
mCurrentIndex = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
@@ -1048,17 +1075,22 @@ class TvInputHardwareManager implements TvInputHal.Callback {
}
if (shouldRecreateAudioPatch) {
mCommittedVolume = volume;
- if (mAudioPatch != null) {
- mAudioManager.releaseAudioPatch(mAudioPatch);
- }
- mAudioManager.createAudioPatch(
+ // only recreate if something was updated or audioPath is null
+ if (mAudioPatch == null || sinkUpdated ||sourceUpdated ) {
+ if (mAudioPatch != null) {
+ mAudioManager.releaseAudioPatch(mAudioPatch);
+ audioPatchArray[0] = null;
+ }
+ mAudioManager.createAudioPatch(
audioPatchArray,
new AudioPortConfig[] { sourceConfig },
sinkConfigs.toArray(new AudioPortConfig[sinkConfigs.size()]));
- mAudioPatch = audioPatchArray[0];
- if (sourceGainConfig != null) {
- mAudioManager.setAudioPortGain(mAudioSource, sourceGainConfig);
+ mAudioPatch = audioPatchArray[0];
}
+ }
+
+ if (sourceGainConfig != null) {
+ mAudioManager.setAudioPortGain(mAudioSource, sourceGainConfig);
}
}
@@ -1182,6 +1214,7 @@ class TvInputHardwareManager implements TvInputHal.Callback {
private static final int HDMI_DEVICE_ADDED = 4;
private static final int HDMI_DEVICE_REMOVED = 5;
private static final int HDMI_DEVICE_UPDATED = 6;
+ private static final int TVINPUT_INFO_ADDED = 7;
@Override
public final void handleMessage(Message msg) {
@@ -1226,6 +1259,31 @@ class TvInputHardwareManager implements TvInputHal.Callback {
}
break;
}
+ case TVINPUT_INFO_ADDED: {
+ int deviceId = msg.arg1;
+ int cableConnectionStatus = msg.arg2;
+ Connection connection =(Connection)msg.obj;
+
+ int previousConfigsLength = connection.getConfigsLengthLocked();
+ int previousCableConnectionStatus = connection.getInputStateLocked();
+ String inputId = mHardwareInputIdMap.get(deviceId);
+
+ if (inputId != null) {
+ if (connection.updateCableConnectionStatusLocked(cableConnectionStatus)) {
+ if (previousCableConnectionStatus != connection.getInputStateLocked()) {
+ mHandler.obtainMessage(ListenerHandler.STATE_CHANGED,
+ connection.getInputStateLocked(), 0, inputId).sendToTarget();
+ }
+ } else {
+ if ((previousConfigsLength == 0)
+ != (connection.getConfigsLengthLocked() == 0)) {
+ mHandler.obtainMessage(ListenerHandler.STATE_CHANGED,
+ connection.getInputStateLocked(), 0, inputId).sendToTarget();
+ }
+ }
+ }
+ break;
+ }
default: {
Slog.w(TAG, "Unhandled message: " + msg);
break;
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 28947083854b..cb3d00a20f65 100755
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -89,6 +89,7 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.content.PackageMonitor;
import com.android.internal.os.SomeArgs;
+import com.android.internal.util.CollectionUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.IndentingPrintWriter;
@@ -315,6 +316,7 @@ public final class TvInputManagerService extends SystemService {
PackageManager.GET_SERVICES | PackageManager.GET_META_DATA,
userId);
List<TvInputInfo> inputList = new ArrayList<>();
+ List<ComponentName> hardwareComponents = new ArrayList<>();
for (ResolveInfo ri : services) {
ServiceInfo si = ri.serviceInfo;
if (!android.Manifest.permission.BIND_TV_INPUT.equals(si.permission)) {
@@ -325,6 +327,7 @@ public final class TvInputManagerService extends SystemService {
ComponentName component = new ComponentName(si.packageName, si.name);
if (hasHardwarePermission(pm, component)) {
+ hardwareComponents.add(component);
ServiceState serviceState = userState.serviceStateMap.get(component);
if (serviceState == null) {
// New hardware input found. Create a new ServiceState and connect to the
@@ -397,6 +400,15 @@ public final class TvInputManagerService extends SystemService {
}
}
+ // Clean up ServiceState corresponding to the removed hardware inputs
+ Iterator<ServiceState> it = userState.serviceStateMap.values().iterator();
+ while (it.hasNext()) {
+ ServiceState serviceState = it.next();
+ if (serviceState.isHardware && !hardwareComponents.contains(serviceState.component)) {
+ it.remove();
+ }
+ }
+
userState.inputMap.clear();
userState.inputMap = inputMap;
}
@@ -1162,6 +1174,91 @@ public final class TvInputManagerService extends SystemService {
}
@Override
+ public List<String> getAvailableExtensionInterfaceNames(String inputId, int userId) {
+ final int callingUid = Binder.getCallingUid();
+ final int callingPid = Binder.getCallingPid();
+ final int resolvedUserId = resolveCallingUserId(callingPid, callingUid,
+ userId, "getAvailableExtensionInterfaceNames");
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ ITvInputService service = null;
+ synchronized (mLock) {
+ UserState userState = getOrCreateUserStateLocked(resolvedUserId);
+ TvInputState inputState = userState.inputMap.get(inputId);
+ if (inputState != null) {
+ ServiceState serviceState =
+ userState.serviceStateMap.get(inputState.info.getComponent());
+ if (serviceState != null && serviceState.isHardware
+ && serviceState.service != null) {
+ service = serviceState.service;
+ }
+ }
+ }
+ try {
+ if (service != null) {
+ List<String> interfaces = new ArrayList<>();
+ for (final String name : CollectionUtils.emptyIfNull(
+ service.getAvailableExtensionInterfaceNames())) {
+ String permission = service.getExtensionInterfacePermission(name);
+ if (permission == null
+ || mContext.checkPermission(permission, callingPid, callingUid)
+ == PackageManager.PERMISSION_GRANTED) {
+ interfaces.add(name);
+ }
+ }
+ return interfaces;
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "error in getAvailableExtensionInterfaceNames "
+ + "or getExtensionInterfacePermission", e);
+ }
+ return new ArrayList<>();
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
+ public IBinder getExtensionInterface(String inputId, String name, int userId) {
+ final int callingUid = Binder.getCallingUid();
+ final int callingPid = Binder.getCallingPid();
+ final int resolvedUserId = resolveCallingUserId(callingPid, callingUid,
+ userId, "getExtensionInterface");
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ ITvInputService service = null;
+ synchronized (mLock) {
+ UserState userState = getOrCreateUserStateLocked(resolvedUserId);
+ TvInputState inputState = userState.inputMap.get(inputId);
+ if (inputState != null) {
+ ServiceState serviceState =
+ userState.serviceStateMap.get(inputState.info.getComponent());
+ if (serviceState != null && serviceState.isHardware
+ && serviceState.service != null) {
+ service = serviceState.service;
+ }
+ }
+ }
+ try {
+ if (service != null) {
+ String permission = service.getExtensionInterfacePermission(name);
+ if (permission == null
+ || mContext.checkPermission(permission, callingPid, callingUid)
+ == PackageManager.PERMISSION_GRANTED) {
+ return service.getExtensionInterface(name);
+ }
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "error in getExtensionInterfacePermission "
+ + "or getExtensionInterface", e);
+ }
+ return null;
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
public List<TvContentRatingSystemInfo> getTvContentRatingSystemList(int userId) {
if (mContext.checkCallingPermission(
android.Manifest.permission.READ_CONTENT_RATING_SYSTEMS)
@@ -2974,32 +3071,47 @@ public final class TvInputManagerService extends SystemService {
public void addHardwareInput(int deviceId, TvInputInfo inputInfo) {
ensureHardwarePermission();
ensureValidInput(inputInfo);
- synchronized (mLock) {
- mTvInputHardwareManager.addHardwareInput(deviceId, inputInfo);
- addHardwareInputLocked(inputInfo);
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ mTvInputHardwareManager.addHardwareInput(deviceId, inputInfo);
+ addHardwareInputLocked(inputInfo);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
}
}
public void addHdmiInput(int id, TvInputInfo inputInfo) {
ensureHardwarePermission();
ensureValidInput(inputInfo);
- synchronized (mLock) {
- mTvInputHardwareManager.addHdmiInput(id, inputInfo);
- addHardwareInputLocked(inputInfo);
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ mTvInputHardwareManager.addHdmiInput(id, inputInfo);
+ addHardwareInputLocked(inputInfo);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
}
}
public void removeHardwareInput(String inputId) {
ensureHardwarePermission();
- synchronized (mLock) {
- ServiceState serviceState = getServiceStateLocked(mComponent, mUserId);
- boolean removed = serviceState.hardwareInputMap.remove(inputId) != null;
- if (removed) {
- buildTvInputListLocked(mUserId, null);
- mTvInputHardwareManager.removeHardwareInput(inputId);
- } else {
- Slog.e(TAG, "failed to remove input " + inputId);
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ ServiceState serviceState = getServiceStateLocked(mComponent, mUserId);
+ boolean removed = serviceState.hardwareInputMap.remove(inputId) != null;
+ if (removed) {
+ buildTvInputListLocked(mUserId, null);
+ mTvInputHardwareManager.removeHardwareInput(inputId);
+ } else {
+ Slog.e(TAG, "failed to remove input " + inputId);
+ }
}
+ } finally {
+ Binder.restoreCallingIdentity(identity);
}
}
}
diff --git a/services/core/java/com/android/server/vcn/OWNERS b/services/core/java/com/android/server/vcn/OWNERS
index 33b9f0f75f81..2441e772468c 100644
--- a/services/core/java/com/android/server/vcn/OWNERS
+++ b/services/core/java/com/android/server/vcn/OWNERS
@@ -3,5 +3,5 @@ set noparent
benedictwong@google.com
ckesting@google.com
evitayan@google.com
+junyin@google.com
nharold@google.com
-jchalard@google.com \ No newline at end of file
diff --git a/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java b/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java
index a31c56a3b737..30e261725a73 100644
--- a/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java
+++ b/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java
@@ -21,6 +21,7 @@ import static android.telephony.CarrierConfigManager.EXTRA_SLOT_INDEX;
import static android.telephony.CarrierConfigManager.EXTRA_SUBSCRIPTION_INDEX;
import static android.telephony.SubscriptionManager.INVALID_SIM_SLOT_INDEX;
import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+import static android.telephony.TelephonyManager.ACTION_MULTI_SIM_CONFIG_CHANGED;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -38,15 +39,19 @@ import android.telephony.SubscriptionManager;
import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
import android.telephony.TelephonyCallback;
import android.telephony.TelephonyManager;
+import android.telephony.TelephonyManager.CarrierPrivilegesListener;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting.Visibility;
import com.android.internal.util.IndentingPrintWriter;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
@@ -92,6 +97,10 @@ public class TelephonySubscriptionTracker extends BroadcastReceiver {
@NonNull private final Map<Integer, Integer> mReadySubIdsBySlotId = new HashMap<>();
@NonNull private final OnSubscriptionsChangedListener mSubscriptionChangedListener;
+ @NonNull
+ private final List<CarrierPrivilegesListener> mCarrierPrivilegesChangedListeners =
+ new ArrayList<>();
+
@NonNull private TelephonySubscriptionSnapshot mCurrentSnapshot;
public TelephonySubscriptionTracker(
@@ -126,22 +135,71 @@ public class TelephonySubscriptionTracker extends BroadcastReceiver {
};
}
- /** Registers the receivers, and starts tracking subscriptions. */
+ /**
+ * Registers the receivers, and starts tracking subscriptions.
+ *
+ * <p>Must always be run on the VcnManagementService thread.
+ */
public void register() {
final HandlerExecutor executor = new HandlerExecutor(mHandler);
+ final IntentFilter filter = new IntentFilter();
+ filter.addAction(ACTION_CARRIER_CONFIG_CHANGED);
+ filter.addAction(ACTION_MULTI_SIM_CONFIG_CHANGED);
- mContext.registerReceiver(
- this, new IntentFilter(ACTION_CARRIER_CONFIG_CHANGED), null, mHandler);
+ mContext.registerReceiver(this, filter, null, mHandler, Context.RECEIVER_NOT_EXPORTED);
mSubscriptionManager.addOnSubscriptionsChangedListener(
executor, mSubscriptionChangedListener);
mTelephonyManager.registerTelephonyCallback(executor, mActiveDataSubIdListener);
+
+ registerCarrierPrivilegesListeners();
+ }
+
+ private void registerCarrierPrivilegesListeners() {
+ final HandlerExecutor executor = new HandlerExecutor(mHandler);
+ final int modemCount = mTelephonyManager.getActiveModemCount();
+ try {
+ for (int i = 0; i < modemCount; i++) {
+ CarrierPrivilegesListener carrierPrivilegesListener =
+ new CarrierPrivilegesListener() {
+ @Override
+ public void onCarrierPrivilegesChanged(
+ @NonNull List<String> privilegedPackageNames,
+ @NonNull int[] privilegedUids) {
+ // Re-trigger the synchronous check (which is also very cheap due
+ // to caching in CarrierPrivilegesTracker). This allows consistency
+ // with the onSubscriptionsChangedListener and broadcasts.
+ handleSubscriptionsChanged();
+ }
+ };
+
+ mTelephonyManager.addCarrierPrivilegesListener(
+ i, executor, carrierPrivilegesListener);
+ mCarrierPrivilegesChangedListeners.add(carrierPrivilegesListener);
+ }
+ } catch (IllegalArgumentException e) {
+ Slog.wtf(TAG, "Encounted exception registering carrier privileges listeners", e);
+ }
}
- /** Unregisters the receivers, and stops tracking subscriptions. */
+ /**
+ * Unregisters the receivers, and stops tracking subscriptions.
+ *
+ * <p>Must always be run on the VcnManagementService thread.
+ */
public void unregister() {
mContext.unregisterReceiver(this);
mSubscriptionManager.removeOnSubscriptionsChangedListener(mSubscriptionChangedListener);
mTelephonyManager.unregisterTelephonyCallback(mActiveDataSubIdListener);
+
+ unregisterCarrierPrivilegesListeners();
+ }
+
+ private void unregisterCarrierPrivilegesListeners() {
+ for (CarrierPrivilegesListener carrierPrivilegesListener :
+ mCarrierPrivilegesChangedListeners) {
+ mTelephonyManager.removeCarrierPrivilegesListener(carrierPrivilegesListener);
+ }
+ mCarrierPrivilegesChangedListeners.clear();
}
/**
@@ -178,8 +236,6 @@ public class TelephonySubscriptionTracker extends BroadcastReceiver {
// group.
if (subInfo.getSimSlotIndex() != INVALID_SIM_SLOT_INDEX
&& mReadySubIdsBySlotId.values().contains(subInfo.getSubscriptionId())) {
- // TODO (b/172619301): Cache based on callbacks from CarrierPrivilegesTracker
-
final TelephonyManager subIdSpecificTelephonyManager =
mTelephonyManager.createForSubscriptionId(subInfo.getSubscriptionId());
@@ -214,12 +270,39 @@ public class TelephonySubscriptionTracker extends BroadcastReceiver {
*/
@Override
public void onReceive(Context context, Intent intent) {
- // Accept sticky broadcasts; if CARRIER_CONFIG_CHANGED was previously broadcast and it
- // already was for an identified carrier, we can stop waiting for initial load to complete
- if (!ACTION_CARRIER_CONFIG_CHANGED.equals(intent.getAction())) {
- return;
+ switch (intent.getAction()) {
+ case ACTION_CARRIER_CONFIG_CHANGED:
+ handleActionCarrierConfigChanged(context, intent);
+ break;
+ case ACTION_MULTI_SIM_CONFIG_CHANGED:
+ handleActionMultiSimConfigChanged(context, intent);
+ break;
+ default:
+ Slog.v(TAG, "Unknown intent received with action: " + intent.getAction());
}
+ }
+
+ private void handleActionMultiSimConfigChanged(Context context, Intent intent) {
+ unregisterCarrierPrivilegesListeners();
+
+ // Clear invalid slotIds from the mReadySubIdsBySlotId map.
+ final int modemCount = mTelephonyManager.getActiveModemCount();
+ final Iterator<Integer> slotIdIterator = mReadySubIdsBySlotId.keySet().iterator();
+ while (slotIdIterator.hasNext()) {
+ final int slotId = slotIdIterator.next();
+ if (slotId >= modemCount) {
+ slotIdIterator.remove();
+ }
+ }
+
+ registerCarrierPrivilegesListeners();
+ handleSubscriptionsChanged();
+ }
+
+ private void handleActionCarrierConfigChanged(Context context, Intent intent) {
+ // Accept sticky broadcasts; if CARRIER_CONFIG_CHANGED was previously broadcast and it
+ // already was for an identified carrier, we can stop waiting for initial load to complete
final int subId = intent.getIntExtra(EXTRA_SUBSCRIPTION_INDEX, INVALID_SUBSCRIPTION_ID);
final int slotId = intent.getIntExtra(EXTRA_SLOT_INDEX, INVALID_SIM_SLOT_INDEX);
diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
index 055012b1dad4..597f7f284730 100644
--- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
+++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
@@ -78,6 +78,7 @@ import android.os.PowerManager.WakeLock;
import android.os.Process;
import android.os.SystemClock;
import android.provider.Settings;
+import android.telephony.TelephonyManager;
import android.util.ArraySet;
import android.util.Slog;
@@ -88,9 +89,10 @@ import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
import com.android.internal.util.WakeupMessage;
import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
-import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkRecord;
-import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkTrackerCallback;
import com.android.server.vcn.Vcn.VcnGatewayStatusCallback;
+import com.android.server.vcn.routeselection.UnderlyingNetworkController;
+import com.android.server.vcn.routeselection.UnderlyingNetworkController.UnderlyingNetworkControllerCallback;
+import com.android.server.vcn.routeselection.UnderlyingNetworkRecord;
import com.android.server.vcn.util.LogUtils;
import com.android.server.vcn.util.MtuUtils;
import com.android.server.vcn.util.OneWayBoolean;
@@ -162,6 +164,14 @@ import java.util.function.Consumer;
public class VcnGatewayConnection extends StateMachine {
private static final String TAG = VcnGatewayConnection.class.getSimpleName();
+ // Matches DataConnection.NETWORK_TYPE private constant, and magic string from
+ // ConnectivityManager#getNetworkTypeName()
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ static final String NETWORK_INFO_NETWORK_TYPE_STRING = "MOBILE";
+
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ static final String NETWORK_INFO_EXTRA_INFO = "VCN";
+
@VisibleForTesting(visibility = Visibility.PRIVATE)
static final InetAddress DUMMY_ADDR = InetAddresses.parseNumericAddress("192.0.2.0");
@@ -202,7 +212,7 @@ public class VcnGatewayConnection extends StateMachine {
private interface EventInfo {}
/**
- * Sent when there are changes to the underlying network (per the UnderlyingNetworkTracker).
+ * Sent when there are changes to the underlying network (per the UnderlyingNetworkController).
*
* <p>May indicate an entirely new underlying network, OR a change in network properties.
*
@@ -523,11 +533,14 @@ public class VcnGatewayConnection extends StateMachine {
@NonNull private final VcnContext mVcnContext;
@NonNull private final ParcelUuid mSubscriptionGroup;
- @NonNull private final UnderlyingNetworkTracker mUnderlyingNetworkTracker;
+ @NonNull private final UnderlyingNetworkController mUnderlyingNetworkController;
@NonNull private final VcnGatewayConnectionConfig mConnectionConfig;
@NonNull private final VcnGatewayStatusCallback mGatewayStatusCallback;
@NonNull private final Dependencies mDeps;
- @NonNull private final VcnUnderlyingNetworkTrackerCallback mUnderlyingNetworkTrackerCallback;
+
+ @NonNull
+ private final VcnUnderlyingNetworkControllerCallback mUnderlyingNetworkControllerCallback;
+
private final boolean mIsMobileDataEnabled;
@NonNull private final IpSecManager mIpSecManager;
@@ -675,17 +688,18 @@ public class VcnGatewayConnection extends StateMachine {
mLastSnapshot = Objects.requireNonNull(snapshot, "Missing snapshot");
- mUnderlyingNetworkTrackerCallback = new VcnUnderlyingNetworkTrackerCallback();
+ mUnderlyingNetworkControllerCallback = new VcnUnderlyingNetworkControllerCallback();
mWakeLock =
mDeps.newWakeLock(mVcnContext.getContext(), PowerManager.PARTIAL_WAKE_LOCK, TAG);
- mUnderlyingNetworkTracker =
- mDeps.newUnderlyingNetworkTracker(
+ mUnderlyingNetworkController =
+ mDeps.newUnderlyingNetworkController(
mVcnContext,
+ mConnectionConfig,
subscriptionGroup,
mLastSnapshot,
- mUnderlyingNetworkTrackerCallback);
+ mUnderlyingNetworkControllerCallback);
mIpSecManager = mVcnContext.getContext().getSystemService(IpSecManager.class);
addState(mDisconnectedState);
@@ -749,7 +763,7 @@ public class VcnGatewayConnection extends StateMachine {
cancelRetryTimeoutAlarm();
cancelSafeModeAlarm();
- mUnderlyingNetworkTracker.teardown();
+ mUnderlyingNetworkController.teardown();
mGatewayStatusCallback.onQuit();
}
@@ -765,12 +779,13 @@ public class VcnGatewayConnection extends StateMachine {
mVcnContext.ensureRunningOnLooperThread();
mLastSnapshot = snapshot;
- mUnderlyingNetworkTracker.updateSubscriptionSnapshot(mLastSnapshot);
+ mUnderlyingNetworkController.updateSubscriptionSnapshot(mLastSnapshot);
sendMessageAndAcquireWakeLock(EVENT_SUBSCRIPTIONS_CHANGED, TOKEN_ALL);
}
- private class VcnUnderlyingNetworkTrackerCallback implements UnderlyingNetworkTrackerCallback {
+ private class VcnUnderlyingNetworkControllerCallback
+ implements UnderlyingNetworkControllerCallback {
@Override
public void onSelectedUnderlyingNetworkChanged(
@Nullable UnderlyingNetworkRecord underlying) {
@@ -1625,6 +1640,12 @@ public class VcnGatewayConnection extends StateMachine {
final NetworkAgentConfig nac =
new NetworkAgentConfig.Builder()
.setLegacyType(ConnectivityManager.TYPE_MOBILE)
+ .setLegacyTypeName(NETWORK_INFO_NETWORK_TYPE_STRING)
+ .setLegacySubType(TelephonyManager.NETWORK_TYPE_UNKNOWN)
+ .setLegacySubTypeName(
+ TelephonyManager.getNetworkTypeName(
+ TelephonyManager.NETWORK_TYPE_UNKNOWN))
+ .setLegacyExtraInfo(NETWORK_INFO_EXTRA_INFO)
.build();
final VcnNetworkAgent agent =
@@ -1676,8 +1697,6 @@ public class VcnGatewayConnection extends StateMachine {
} /* validationStatusCallback */);
agent.register();
- agent.setUnderlyingNetworks(
- mUnderlying == null ? null : Collections.singletonList(mUnderlying.network));
agent.markConnected();
return agent;
@@ -2052,6 +2071,7 @@ public class VcnGatewayConnection extends StateMachine {
"Unknown transport type or missing TransportInfo/NetworkSpecifier for"
+ " non-null underlying network");
}
+ builder.setUnderlyingNetworks(List.of(underlying.network));
} else {
Slog.wtf(
TAG,
@@ -2277,7 +2297,7 @@ public class VcnGatewayConnection extends StateMachine {
+ (mNetworkAgent == null ? null : mNetworkAgent.getNetwork()));
pw.println();
- mUnderlyingNetworkTracker.dump(pw);
+ mUnderlyingNetworkController.dump(pw);
pw.println();
pw.decreaseIndent();
@@ -2289,8 +2309,8 @@ public class VcnGatewayConnection extends StateMachine {
}
@VisibleForTesting(visibility = Visibility.PRIVATE)
- UnderlyingNetworkTrackerCallback getUnderlyingNetworkTrackerCallback() {
- return mUnderlyingNetworkTrackerCallback;
+ UnderlyingNetworkControllerCallback getUnderlyingNetworkControllerCallback() {
+ return mUnderlyingNetworkControllerCallback;
}
@VisibleForTesting(visibility = Visibility.PRIVATE)
@@ -2369,17 +2389,15 @@ public class VcnGatewayConnection extends StateMachine {
/** External dependencies used by VcnGatewayConnection, for injection in tests */
@VisibleForTesting(visibility = Visibility.PRIVATE)
public static class Dependencies {
- /** Builds a new UnderlyingNetworkTracker. */
- public UnderlyingNetworkTracker newUnderlyingNetworkTracker(
+ /** Builds a new UnderlyingNetworkController. */
+ public UnderlyingNetworkController newUnderlyingNetworkController(
VcnContext vcnContext,
+ VcnGatewayConnectionConfig connectionConfig,
ParcelUuid subscriptionGroup,
TelephonySubscriptionSnapshot snapshot,
- UnderlyingNetworkTrackerCallback callback) {
- return new UnderlyingNetworkTracker(
- vcnContext,
- subscriptionGroup,
- snapshot,
- callback);
+ UnderlyingNetworkControllerCallback callback) {
+ return new UnderlyingNetworkController(
+ vcnContext, connectionConfig, subscriptionGroup, snapshot, callback);
}
/** Builds a new IkeSession. */
diff --git a/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java b/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java
new file mode 100644
index 000000000000..c96c1ee01a6d
--- /dev/null
+++ b/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java
@@ -0,0 +1,339 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.vcn.routeselection;
+
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.NetworkCapabilities.TRANSPORT_TEST;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_FORBIDDEN;
+import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_REQUIRED;
+
+import static com.android.server.VcnManagementService.LOCAL_LOG;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.NetworkCapabilities;
+import android.net.TelephonyNetworkSpecifier;
+import android.net.vcn.VcnCellUnderlyingNetworkTemplate;
+import android.net.vcn.VcnManager;
+import android.net.vcn.VcnUnderlyingNetworkTemplate;
+import android.net.vcn.VcnWifiUnderlyingNetworkTemplate;
+import android.os.ParcelUuid;
+import android.os.PersistableBundle;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.annotations.VisibleForTesting.Visibility;
+import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
+import com.android.server.vcn.VcnContext;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+
+/** @hide */
+class NetworkPriorityClassifier {
+ @NonNull private static final String TAG = NetworkPriorityClassifier.class.getSimpleName();
+ /**
+ * Minimum signal strength for a WiFi network to be eligible for switching to
+ *
+ * <p>A network that satisfies this is eligible to become the selected underlying network with
+ * no additional conditions
+ */
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ static final int WIFI_ENTRY_RSSI_THRESHOLD_DEFAULT = -70;
+ /**
+ * Minimum signal strength to continue using a WiFi network
+ *
+ * <p>A network that satisfies the conditions may ONLY continue to be used if it is already
+ * selected as the underlying network. A WiFi network satisfying this condition, but NOT the
+ * prospective-network RSSI threshold CANNOT be switched to.
+ */
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ static final int WIFI_EXIT_RSSI_THRESHOLD_DEFAULT = -74;
+
+ /** Priority for any other networks (including unvalidated, etc) */
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ static final int PRIORITY_ANY = Integer.MAX_VALUE;
+
+ /** Gives networks a priority class, based on configured VcnGatewayConnectionConfig */
+ public static int calculatePriorityClass(
+ VcnContext vcnContext,
+ UnderlyingNetworkRecord networkRecord,
+ List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates,
+ ParcelUuid subscriptionGroup,
+ TelephonySubscriptionSnapshot snapshot,
+ UnderlyingNetworkRecord currentlySelected,
+ PersistableBundle carrierConfig) {
+ // mRouteSelectionNetworkRequest requires a network be both VALIDATED and NOT_SUSPENDED
+
+ if (networkRecord.isBlocked) {
+ logWtf("Network blocked for System Server: " + networkRecord.network);
+ return PRIORITY_ANY;
+ }
+
+ if (snapshot == null) {
+ logWtf("Got null snapshot");
+ return PRIORITY_ANY;
+ }
+
+ int priorityIndex = 0;
+ for (VcnUnderlyingNetworkTemplate nwPriority : underlyingNetworkTemplates) {
+ if (checkMatchesPriorityRule(
+ vcnContext,
+ nwPriority,
+ networkRecord,
+ subscriptionGroup,
+ snapshot,
+ currentlySelected,
+ carrierConfig)) {
+ return priorityIndex;
+ }
+ priorityIndex++;
+ }
+ return PRIORITY_ANY;
+ }
+
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ public static boolean checkMatchesPriorityRule(
+ VcnContext vcnContext,
+ VcnUnderlyingNetworkTemplate networkPriority,
+ UnderlyingNetworkRecord networkRecord,
+ ParcelUuid subscriptionGroup,
+ TelephonySubscriptionSnapshot snapshot,
+ UnderlyingNetworkRecord currentlySelected,
+ PersistableBundle carrierConfig) {
+ final NetworkCapabilities caps = networkRecord.networkCapabilities;
+ final boolean isSelectedUnderlyingNetwork =
+ currentlySelected != null
+ && Objects.equals(currentlySelected.network, networkRecord.network);
+
+ final int meteredMatch = networkPriority.getMetered();
+ final boolean isMetered = !caps.hasCapability(NET_CAPABILITY_NOT_METERED);
+ if (meteredMatch == MATCH_REQUIRED && !isMetered
+ || meteredMatch == MATCH_FORBIDDEN && isMetered) {
+ return false;
+ }
+
+ // Fails bandwidth requirements if either (a) less than exit threshold, or (b), not
+ // selected, but less than entry threshold
+ if (caps.getLinkUpstreamBandwidthKbps() < networkPriority.getMinExitUpstreamBandwidthKbps()
+ || (caps.getLinkUpstreamBandwidthKbps()
+ < networkPriority.getMinEntryUpstreamBandwidthKbps()
+ && !isSelectedUnderlyingNetwork)) {
+ return false;
+ }
+
+ if (caps.getLinkDownstreamBandwidthKbps()
+ < networkPriority.getMinExitDownstreamBandwidthKbps()
+ || (caps.getLinkDownstreamBandwidthKbps()
+ < networkPriority.getMinEntryDownstreamBandwidthKbps()
+ && !isSelectedUnderlyingNetwork)) {
+ return false;
+ }
+
+ if (vcnContext.isInTestMode() && caps.hasTransport(TRANSPORT_TEST)) {
+ return true;
+ }
+
+ if (networkPriority instanceof VcnWifiUnderlyingNetworkTemplate) {
+ return checkMatchesWifiPriorityRule(
+ (VcnWifiUnderlyingNetworkTemplate) networkPriority,
+ networkRecord,
+ currentlySelected,
+ carrierConfig);
+ }
+
+ if (networkPriority instanceof VcnCellUnderlyingNetworkTemplate) {
+ return checkMatchesCellPriorityRule(
+ vcnContext,
+ (VcnCellUnderlyingNetworkTemplate) networkPriority,
+ networkRecord,
+ subscriptionGroup,
+ snapshot);
+ }
+
+ logWtf(
+ "Got unknown VcnUnderlyingNetworkTemplate class: "
+ + networkPriority.getClass().getSimpleName());
+ return false;
+ }
+
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ public static boolean checkMatchesWifiPriorityRule(
+ VcnWifiUnderlyingNetworkTemplate networkPriority,
+ UnderlyingNetworkRecord networkRecord,
+ UnderlyingNetworkRecord currentlySelected,
+ PersistableBundle carrierConfig) {
+ final NetworkCapabilities caps = networkRecord.networkCapabilities;
+
+ if (!caps.hasTransport(TRANSPORT_WIFI)) {
+ return false;
+ }
+
+ // TODO: Move the Network Quality check to the network metric monitor framework.
+ if (!isWifiRssiAcceptable(networkRecord, currentlySelected, carrierConfig)) {
+ return false;
+ }
+
+ if (!networkPriority.getSsids().isEmpty()
+ && !networkPriority.getSsids().contains(caps.getSsid())) {
+ return false;
+ }
+
+ return true;
+ }
+
+ private static boolean isWifiRssiAcceptable(
+ UnderlyingNetworkRecord networkRecord,
+ UnderlyingNetworkRecord currentlySelected,
+ PersistableBundle carrierConfig) {
+ final NetworkCapabilities caps = networkRecord.networkCapabilities;
+ final boolean isSelectedNetwork =
+ currentlySelected != null
+ && networkRecord.network.equals(currentlySelected.network);
+
+ if (isSelectedNetwork
+ && caps.getSignalStrength() >= getWifiExitRssiThreshold(carrierConfig)) {
+ return true;
+ }
+
+ if (caps.getSignalStrength() >= getWifiEntryRssiThreshold(carrierConfig)) {
+ return true;
+ }
+
+ return false;
+ }
+
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ public static boolean checkMatchesCellPriorityRule(
+ VcnContext vcnContext,
+ VcnCellUnderlyingNetworkTemplate networkPriority,
+ UnderlyingNetworkRecord networkRecord,
+ ParcelUuid subscriptionGroup,
+ TelephonySubscriptionSnapshot snapshot) {
+ final NetworkCapabilities caps = networkRecord.networkCapabilities;
+
+ if (!caps.hasTransport(TRANSPORT_CELLULAR)) {
+ return false;
+ }
+
+ final TelephonyNetworkSpecifier telephonyNetworkSpecifier =
+ ((TelephonyNetworkSpecifier) caps.getNetworkSpecifier());
+ if (telephonyNetworkSpecifier == null) {
+ logWtf("Got null NetworkSpecifier");
+ return false;
+ }
+
+ final int subId = telephonyNetworkSpecifier.getSubscriptionId();
+ final TelephonyManager subIdSpecificTelephonyMgr =
+ vcnContext
+ .getContext()
+ .getSystemService(TelephonyManager.class)
+ .createForSubscriptionId(subId);
+
+ if (!networkPriority.getOperatorPlmnIds().isEmpty()) {
+ final String plmnId = subIdSpecificTelephonyMgr.getNetworkOperator();
+ if (!networkPriority.getOperatorPlmnIds().contains(plmnId)) {
+ return false;
+ }
+ }
+
+ if (!networkPriority.getSimSpecificCarrierIds().isEmpty()) {
+ final int carrierId = subIdSpecificTelephonyMgr.getSimSpecificCarrierId();
+ if (!networkPriority.getSimSpecificCarrierIds().contains(carrierId)) {
+ return false;
+ }
+ }
+
+ final int roamingMatch = networkPriority.getRoaming();
+ final boolean isRoaming = !caps.hasCapability(NET_CAPABILITY_NOT_ROAMING);
+ if (roamingMatch == MATCH_REQUIRED && !isRoaming
+ || roamingMatch == MATCH_FORBIDDEN && isRoaming) {
+ return false;
+ }
+
+ final int opportunisticMatch = networkPriority.getOpportunistic();
+ final boolean isOpportunistic = isOpportunistic(snapshot, caps.getSubscriptionIds());
+ if (opportunisticMatch == MATCH_REQUIRED) {
+ if (!isOpportunistic) {
+ return false;
+ }
+
+ // If this carrier is the active data provider, ensure that opportunistic is only
+ // ever prioritized if it is also the active data subscription. This ensures that
+ // if an opportunistic subscription is still in the process of being switched to,
+ // or switched away from, the VCN does not attempt to continue using it against the
+ // decision made at the telephony layer. Failure to do so may result in the modem
+ // switching back and forth.
+ //
+ // Allow the following two cases:
+ // 1. Active subId is NOT in the group that this VCN is supporting
+ // 2. This opportunistic subscription is for the active subId
+ if (snapshot.getAllSubIdsInGroup(subscriptionGroup)
+ .contains(SubscriptionManager.getActiveDataSubscriptionId())
+ && !caps.getSubscriptionIds()
+ .contains(SubscriptionManager.getActiveDataSubscriptionId())) {
+ return false;
+ }
+ } else if (opportunisticMatch == MATCH_FORBIDDEN && !isOpportunistic) {
+ return false;
+ }
+
+ return true;
+ }
+
+ static boolean isOpportunistic(
+ @NonNull TelephonySubscriptionSnapshot snapshot, Set<Integer> subIds) {
+ if (snapshot == null) {
+ logWtf("Got null snapshot");
+ return false;
+ }
+ for (int subId : subIds) {
+ if (snapshot.isOpportunistic(subId)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ static int getWifiEntryRssiThreshold(@Nullable PersistableBundle carrierConfig) {
+ if (carrierConfig != null) {
+ return carrierConfig.getInt(
+ VcnManager.VCN_NETWORK_SELECTION_WIFI_ENTRY_RSSI_THRESHOLD_KEY,
+ WIFI_ENTRY_RSSI_THRESHOLD_DEFAULT);
+ }
+ return WIFI_ENTRY_RSSI_THRESHOLD_DEFAULT;
+ }
+
+ static int getWifiExitRssiThreshold(@Nullable PersistableBundle carrierConfig) {
+ if (carrierConfig != null) {
+ return carrierConfig.getInt(
+ VcnManager.VCN_NETWORK_SELECTION_WIFI_EXIT_RSSI_THRESHOLD_KEY,
+ WIFI_EXIT_RSSI_THRESHOLD_DEFAULT);
+ }
+ return WIFI_EXIT_RSSI_THRESHOLD_DEFAULT;
+ }
+
+ private static void logWtf(String msg) {
+ Slog.wtf(TAG, msg);
+ LOCAL_LOG.log(TAG + " WTF: " + msg);
+ }
+}
diff --git a/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java
index 7ddd1355a2d6..ca2e449ffc25 100644
--- a/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java
+++ b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java
@@ -14,13 +14,14 @@
* limitations under the License.
*/
-package com.android.server.vcn;
+package com.android.server.vcn.routeselection;
-import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
-import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static android.telephony.TelephonyCallback.ActiveDataSubscriptionIdListener;
import static com.android.server.VcnManagementService.LOCAL_LOG;
+import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.getWifiEntryRssiThreshold;
+import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.getWifiExitRssiThreshold;
+import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.isOpportunistic;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -31,27 +32,25 @@ import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
import android.net.TelephonyNetworkSpecifier;
-import android.net.vcn.VcnManager;
+import android.net.vcn.VcnGatewayConnectionConfig;
+import android.net.vcn.VcnUnderlyingNetworkTemplate;
import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.ParcelUuid;
import android.os.PersistableBundle;
import android.telephony.CarrierConfigManager;
-import android.telephony.SubscriptionManager;
import android.telephony.TelephonyCallback;
import android.telephony.TelephonyManager;
import android.util.ArrayMap;
import android.util.Slog;
-import android.util.SparseArray;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.annotations.VisibleForTesting.Visibility;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
+import com.android.server.vcn.VcnContext;
import java.util.ArrayList;
import java.util.Collections;
-import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -61,68 +60,19 @@ import java.util.TreeSet;
/**
* Tracks a set of Networks underpinning a VcnGatewayConnection.
*
- * <p>A single UnderlyingNetworkTracker is built to serve a SINGLE VCN Gateway Connection, and MUST
- * be torn down with the VcnGatewayConnection in order to ensure underlying networks are allowed to
- * be reaped.
+ * <p>A single UnderlyingNetworkController is built to serve a SINGLE VCN Gateway Connection, and
+ * MUST be torn down with the VcnGatewayConnection in order to ensure underlying networks are
+ * allowed to be reaped.
*
* @hide
*/
-public class UnderlyingNetworkTracker {
- @NonNull private static final String TAG = UnderlyingNetworkTracker.class.getSimpleName();
-
- /**
- * Minimum signal strength for a WiFi network to be eligible for switching to
- *
- * <p>A network that satisfies this is eligible to become the selected underlying network with
- * no additional conditions
- */
- @VisibleForTesting(visibility = Visibility.PRIVATE)
- static final int WIFI_ENTRY_RSSI_THRESHOLD_DEFAULT = -70;
-
- /**
- * Minimum signal strength to continue using a WiFi network
- *
- * <p>A network that satisfies the conditions may ONLY continue to be used if it is already
- * selected as the underlying network. A WiFi network satisfying this condition, but NOT the
- * prospective-network RSSI threshold CANNOT be switched to.
- */
- @VisibleForTesting(visibility = Visibility.PRIVATE)
- static final int WIFI_EXIT_RSSI_THRESHOLD_DEFAULT = -74;
-
- /** Priority for any cellular network for which the subscription is listed as opportunistic */
- @VisibleForTesting(visibility = Visibility.PRIVATE)
- static final int PRIORITY_OPPORTUNISTIC_CELLULAR = 0;
-
- /** Priority for any WiFi network which is in use, and satisfies the in-use RSSI threshold */
- @VisibleForTesting(visibility = Visibility.PRIVATE)
- static final int PRIORITY_WIFI_IN_USE = 1;
-
- /** Priority for any WiFi network which satisfies the prospective-network RSSI threshold */
- @VisibleForTesting(visibility = Visibility.PRIVATE)
- static final int PRIORITY_WIFI_PROSPECTIVE = 2;
-
- /** Priority for any standard macro cellular network */
- @VisibleForTesting(visibility = Visibility.PRIVATE)
- static final int PRIORITY_MACRO_CELLULAR = 3;
-
- /** Priority for any other networks (including unvalidated, etc) */
- @VisibleForTesting(visibility = Visibility.PRIVATE)
- static final int PRIORITY_ANY = Integer.MAX_VALUE;
-
- private static final SparseArray<String> PRIORITY_TO_STRING_MAP = new SparseArray<>();
-
- static {
- PRIORITY_TO_STRING_MAP.put(
- PRIORITY_OPPORTUNISTIC_CELLULAR, "PRIORITY_OPPORTUNISTIC_CELLULAR");
- PRIORITY_TO_STRING_MAP.put(PRIORITY_WIFI_IN_USE, "PRIORITY_WIFI_IN_USE");
- PRIORITY_TO_STRING_MAP.put(PRIORITY_WIFI_PROSPECTIVE, "PRIORITY_WIFI_PROSPECTIVE");
- PRIORITY_TO_STRING_MAP.put(PRIORITY_MACRO_CELLULAR, "PRIORITY_MACRO_CELLULAR");
- PRIORITY_TO_STRING_MAP.put(PRIORITY_ANY, "PRIORITY_ANY");
- }
+public class UnderlyingNetworkController {
+ @NonNull private static final String TAG = UnderlyingNetworkController.class.getSimpleName();
@NonNull private final VcnContext mVcnContext;
+ @NonNull private final VcnGatewayConnectionConfig mConnectionConfig;
@NonNull private final ParcelUuid mSubscriptionGroup;
- @NonNull private final UnderlyingNetworkTrackerCallback mCb;
+ @NonNull private final UnderlyingNetworkControllerCallback mCb;
@NonNull private final Dependencies mDeps;
@NonNull private final Handler mHandler;
@NonNull private final ConnectivityManager mConnectivityManager;
@@ -142,26 +92,24 @@ public class UnderlyingNetworkTracker {
@Nullable private UnderlyingNetworkRecord mCurrentRecord;
@Nullable private UnderlyingNetworkRecord.Builder mRecordInProgress;
- public UnderlyingNetworkTracker(
+ public UnderlyingNetworkController(
@NonNull VcnContext vcnContext,
+ @NonNull VcnGatewayConnectionConfig connectionConfig,
@NonNull ParcelUuid subscriptionGroup,
@NonNull TelephonySubscriptionSnapshot snapshot,
- @NonNull UnderlyingNetworkTrackerCallback cb) {
- this(
- vcnContext,
- subscriptionGroup,
- snapshot,
- cb,
- new Dependencies());
+ @NonNull UnderlyingNetworkControllerCallback cb) {
+ this(vcnContext, connectionConfig, subscriptionGroup, snapshot, cb, new Dependencies());
}
- private UnderlyingNetworkTracker(
+ private UnderlyingNetworkController(
@NonNull VcnContext vcnContext,
+ @NonNull VcnGatewayConnectionConfig connectionConfig,
@NonNull ParcelUuid subscriptionGroup,
@NonNull TelephonySubscriptionSnapshot snapshot,
- @NonNull UnderlyingNetworkTrackerCallback cb,
+ @NonNull UnderlyingNetworkControllerCallback cb,
@NonNull Dependencies deps) {
mVcnContext = Objects.requireNonNull(vcnContext, "Missing vcnContext");
+ mConnectionConfig = Objects.requireNonNull(connectionConfig, "Missing connectionConfig");
mSubscriptionGroup = Objects.requireNonNull(subscriptionGroup, "Missing subscriptionGroup");
mLastSnapshot = Objects.requireNonNull(snapshot, "Missing snapshot");
mCb = Objects.requireNonNull(cb, "Missing cb");
@@ -271,8 +219,8 @@ public class UnderlyingNetworkTracker {
* subscription group, while the VCN networks are excluded by virtue of not having subIds set on
* the VCN-exposed networks.
*
- * <p>If the VCN that this UnderlyingNetworkTracker belongs to is in test-mode, this will return
- * a NetworkRequest that only matches Test Networks.
+ * <p>If the VCN that this UnderlyingNetworkController belongs to is in test-mode, this will
+ * return a NetworkRequest that only matches Test Networks.
*/
private NetworkRequest getRouteSelectionRequest() {
if (mVcnContext.isInTestMode()) {
@@ -373,9 +321,9 @@ public class UnderlyingNetworkTracker {
}
/**
- * Update this UnderlyingNetworkTracker's TelephonySubscriptionSnapshot.
+ * Update this UnderlyingNetworkController's TelephonySubscriptionSnapshot.
*
- * <p>Updating the TelephonySubscriptionSnapshot will cause this UnderlyingNetworkTracker to
+ * <p>Updating the TelephonySubscriptionSnapshot will cause this UnderlyingNetworkController to
* reevaluate its NetworkBringupCallbacks. This may result in NetworkRequests being registered
* or unregistered if the subIds mapped to the this Tracker's SubscriptionGroup change.
*/
@@ -410,7 +358,7 @@ public class UnderlyingNetworkTracker {
private void reevaluateNetworks() {
if (mIsQuitting || mRouteSelectionCallback == null) {
- return; // UnderlyingNetworkTracker has quit.
+ return; // UnderlyingNetworkController has quit.
}
TreeSet<UnderlyingNetworkRecord> sorted =
@@ -424,22 +372,6 @@ public class UnderlyingNetworkTracker {
mCb.onSelectedUnderlyingNetworkChanged(mCurrentRecord);
}
- private static boolean isOpportunistic(
- @NonNull TelephonySubscriptionSnapshot snapshot, Set<Integer> subIds) {
- if (snapshot == null) {
- logWtf("Got null snapshot");
- return false;
- }
-
- for (int subId : subIds) {
- if (snapshot.isOpportunistic(subId)) {
- return true;
- }
- }
-
- return false;
- }
-
/**
* NetworkBringupCallback is used to keep background, VCN-managed Networks from being reaped.
*
@@ -468,6 +400,8 @@ public class UnderlyingNetworkTracker {
TreeSet<UnderlyingNetworkRecord> sorted =
new TreeSet<>(
UnderlyingNetworkRecord.getComparator(
+ mVcnContext,
+ mConnectionConfig.getVcnUnderlyingNetworkPriorities(),
mSubscriptionGroup,
mLastSnapshot,
mCurrentRecord,
@@ -544,230 +478,6 @@ public class UnderlyingNetworkTracker {
}
}
- private static int getWifiEntryRssiThreshold(@Nullable PersistableBundle carrierConfig) {
- if (carrierConfig != null) {
- return carrierConfig.getInt(
- VcnManager.VCN_NETWORK_SELECTION_WIFI_ENTRY_RSSI_THRESHOLD_KEY,
- WIFI_ENTRY_RSSI_THRESHOLD_DEFAULT);
- }
-
- return WIFI_ENTRY_RSSI_THRESHOLD_DEFAULT;
- }
-
- private static int getWifiExitRssiThreshold(@Nullable PersistableBundle carrierConfig) {
- if (carrierConfig != null) {
- return carrierConfig.getInt(
- VcnManager.VCN_NETWORK_SELECTION_WIFI_EXIT_RSSI_THRESHOLD_KEY,
- WIFI_EXIT_RSSI_THRESHOLD_DEFAULT);
- }
-
- return WIFI_EXIT_RSSI_THRESHOLD_DEFAULT;
- }
-
- /** A record of a single underlying network, caching relevant fields. */
- public static class UnderlyingNetworkRecord {
- @NonNull public final Network network;
- @NonNull public final NetworkCapabilities networkCapabilities;
- @NonNull public final LinkProperties linkProperties;
- public final boolean isBlocked;
-
- @VisibleForTesting(visibility = Visibility.PRIVATE)
- UnderlyingNetworkRecord(
- @NonNull Network network,
- @NonNull NetworkCapabilities networkCapabilities,
- @NonNull LinkProperties linkProperties,
- boolean isBlocked) {
- this.network = network;
- this.networkCapabilities = networkCapabilities;
- this.linkProperties = linkProperties;
- this.isBlocked = isBlocked;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (!(o instanceof UnderlyingNetworkRecord)) return false;
- final UnderlyingNetworkRecord that = (UnderlyingNetworkRecord) o;
-
- return network.equals(that.network)
- && networkCapabilities.equals(that.networkCapabilities)
- && linkProperties.equals(that.linkProperties)
- && isBlocked == that.isBlocked;
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(network, networkCapabilities, linkProperties, isBlocked);
- }
-
- /**
- * Gives networks a priority class, based on the following priorities:
- *
- * <ol>
- * <li>Opportunistic cellular
- * <li>Carrier WiFi, signal strength >= WIFI_ENTRY_RSSI_THRESHOLD_DEFAULT
- * <li>Carrier WiFi, active network + signal strength >= WIFI_EXIT_RSSI_THRESHOLD_DEFAULT
- * <li>Macro cellular
- * <li>Any others
- * </ol>
- */
- private int calculatePriorityClass(
- ParcelUuid subscriptionGroup,
- TelephonySubscriptionSnapshot snapshot,
- UnderlyingNetworkRecord currentlySelected,
- PersistableBundle carrierConfig) {
- final NetworkCapabilities caps = networkCapabilities;
-
- // mRouteSelectionNetworkRequest requires a network be both VALIDATED and NOT_SUSPENDED
-
- if (isBlocked) {
- logWtf("Network blocked for System Server: " + network);
- return PRIORITY_ANY;
- }
-
- if (caps.hasTransport(TRANSPORT_CELLULAR)
- && isOpportunistic(snapshot, caps.getSubscriptionIds())) {
- // If this carrier is the active data provider, ensure that opportunistic is only
- // ever prioritized if it is also the active data subscription. This ensures that
- // if an opportunistic subscription is still in the process of being switched to,
- // or switched away from, the VCN does not attempt to continue using it against the
- // decision made at the telephony layer. Failure to do so may result in the modem
- // switching back and forth.
- //
- // Allow the following two cases:
- // 1. Active subId is NOT in the group that this VCN is supporting
- // 2. This opportunistic subscription is for the active subId
- if (!snapshot.getAllSubIdsInGroup(subscriptionGroup)
- .contains(SubscriptionManager.getActiveDataSubscriptionId())
- || caps.getSubscriptionIds()
- .contains(SubscriptionManager.getActiveDataSubscriptionId())) {
- return PRIORITY_OPPORTUNISTIC_CELLULAR;
- }
- }
-
- if (caps.hasTransport(TRANSPORT_WIFI)) {
- if (caps.getSignalStrength() >= getWifiExitRssiThreshold(carrierConfig)
- && currentlySelected != null
- && network.equals(currentlySelected.network)) {
- return PRIORITY_WIFI_IN_USE;
- }
-
- if (caps.getSignalStrength() >= getWifiEntryRssiThreshold(carrierConfig)) {
- return PRIORITY_WIFI_PROSPECTIVE;
- }
- }
-
- // Disallow opportunistic subscriptions from matching PRIORITY_MACRO_CELLULAR, as might
- // be the case when Default Data SubId (CBRS) != Active Data SubId (MACRO), as might be
- // the case if the Default Data SubId does not support certain services (eg voice
- // calling)
- if (caps.hasTransport(TRANSPORT_CELLULAR)
- && !isOpportunistic(snapshot, caps.getSubscriptionIds())) {
- return PRIORITY_MACRO_CELLULAR;
- }
-
- return PRIORITY_ANY;
- }
-
- private static Comparator<UnderlyingNetworkRecord> getComparator(
- ParcelUuid subscriptionGroup,
- TelephonySubscriptionSnapshot snapshot,
- UnderlyingNetworkRecord currentlySelected,
- PersistableBundle carrierConfig) {
- return (left, right) -> {
- return Integer.compare(
- left.calculatePriorityClass(
- subscriptionGroup, snapshot, currentlySelected, carrierConfig),
- right.calculatePriorityClass(
- subscriptionGroup, snapshot, currentlySelected, carrierConfig));
- };
- }
-
- /** Dumps the state of this record for logging and debugging purposes. */
- private void dump(
- IndentingPrintWriter pw,
- ParcelUuid subscriptionGroup,
- TelephonySubscriptionSnapshot snapshot,
- UnderlyingNetworkRecord currentlySelected,
- PersistableBundle carrierConfig) {
- pw.println("UnderlyingNetworkRecord:");
- pw.increaseIndent();
-
- final int priorityClass =
- calculatePriorityClass(
- subscriptionGroup, snapshot, currentlySelected, carrierConfig);
- pw.println(
- "Priority class: " + PRIORITY_TO_STRING_MAP.get(priorityClass) + " ("
- + priorityClass + ")");
- pw.println("mNetwork: " + network);
- pw.println("mNetworkCapabilities: " + networkCapabilities);
- pw.println("mLinkProperties: " + linkProperties);
-
- pw.decreaseIndent();
- }
-
- /** Builder to incrementally construct an UnderlyingNetworkRecord. */
- private static class Builder {
- @NonNull private final Network mNetwork;
-
- @Nullable private NetworkCapabilities mNetworkCapabilities;
- @Nullable private LinkProperties mLinkProperties;
- boolean mIsBlocked;
- boolean mWasIsBlockedSet;
-
- @Nullable private UnderlyingNetworkRecord mCached;
-
- private Builder(@NonNull Network network) {
- mNetwork = network;
- }
-
- @NonNull
- private Network getNetwork() {
- return mNetwork;
- }
-
- private void setNetworkCapabilities(@NonNull NetworkCapabilities networkCapabilities) {
- mNetworkCapabilities = networkCapabilities;
- mCached = null;
- }
-
- @Nullable
- private NetworkCapabilities getNetworkCapabilities() {
- return mNetworkCapabilities;
- }
-
- private void setLinkProperties(@NonNull LinkProperties linkProperties) {
- mLinkProperties = linkProperties;
- mCached = null;
- }
-
- private void setIsBlocked(boolean isBlocked) {
- mIsBlocked = isBlocked;
- mWasIsBlockedSet = true;
- mCached = null;
- }
-
- private boolean isValid() {
- return mNetworkCapabilities != null && mLinkProperties != null && mWasIsBlockedSet;
- }
-
- private UnderlyingNetworkRecord build() {
- if (!isValid()) {
- throw new IllegalArgumentException(
- "Called build before UnderlyingNetworkRecord was valid");
- }
-
- if (mCached == null) {
- mCached =
- new UnderlyingNetworkRecord(
- mNetwork, mNetworkCapabilities, mLinkProperties, mIsBlocked);
- }
-
- return mCached;
- }
- }
- }
-
private static void logWtf(String msg) {
Slog.wtf(TAG, msg);
LOCAL_LOG.log(TAG + " WTF: " + msg);
@@ -780,7 +490,7 @@ public class UnderlyingNetworkTracker {
/** Dumps the state of this record for logging and debugging purposes. */
public void dump(IndentingPrintWriter pw) {
- pw.println("UnderlyingNetworkTracker:");
+ pw.println("UnderlyingNetworkController:");
pw.increaseIndent();
pw.println("Carrier WiFi Entry Threshold: " + getWifiEntryRssiThreshold(mCarrierConfig));
@@ -788,12 +498,31 @@ public class UnderlyingNetworkTracker {
pw.println(
"Currently selected: " + (mCurrentRecord == null ? null : mCurrentRecord.network));
+ pw.println("VcnUnderlyingNetworkTemplate list:");
+ pw.increaseIndent();
+ int index = 0;
+ for (VcnUnderlyingNetworkTemplate priority :
+ mConnectionConfig.getVcnUnderlyingNetworkPriorities()) {
+ pw.println("Priority index: " + index);
+ priority.dump(pw);
+ index++;
+ }
+ pw.decreaseIndent();
+ pw.println();
+
pw.println("Underlying networks:");
pw.increaseIndent();
if (mRouteSelectionCallback != null) {
for (UnderlyingNetworkRecord record :
mRouteSelectionCallback.getSortedUnderlyingNetworks()) {
- record.dump(pw, mSubscriptionGroup, mLastSnapshot, mCurrentRecord, mCarrierConfig);
+ record.dump(
+ mVcnContext,
+ pw,
+ mConnectionConfig.getVcnUnderlyingNetworkPriorities(),
+ mSubscriptionGroup,
+ mLastSnapshot,
+ mCurrentRecord,
+ mCarrierConfig);
}
}
pw.decreaseIndent();
@@ -811,7 +540,7 @@ public class UnderlyingNetworkTracker {
}
/** Callbacks for being notified of the changes in, or to the selected underlying network. */
- public interface UnderlyingNetworkTrackerCallback {
+ public interface UnderlyingNetworkControllerCallback {
/**
* Fired when a new underlying network is selected, or properties have changed.
*
diff --git a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java
new file mode 100644
index 000000000000..c0488b18cb65
--- /dev/null
+++ b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.vcn.routeselection;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.LinkProperties;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.vcn.VcnUnderlyingNetworkTemplate;
+import android.os.ParcelUuid;
+import android.os.PersistableBundle;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.annotations.VisibleForTesting.Visibility;
+import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
+import com.android.server.vcn.VcnContext;
+
+import java.util.Comparator;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * A record of a single underlying network, caching relevant fields.
+ *
+ * @hide
+ */
+public class UnderlyingNetworkRecord {
+ @NonNull public final Network network;
+ @NonNull public final NetworkCapabilities networkCapabilities;
+ @NonNull public final LinkProperties linkProperties;
+ public final boolean isBlocked;
+
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ public UnderlyingNetworkRecord(
+ @NonNull Network network,
+ @NonNull NetworkCapabilities networkCapabilities,
+ @NonNull LinkProperties linkProperties,
+ boolean isBlocked) {
+ this.network = network;
+ this.networkCapabilities = networkCapabilities;
+ this.linkProperties = linkProperties;
+ this.isBlocked = isBlocked;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof UnderlyingNetworkRecord)) return false;
+ final UnderlyingNetworkRecord that = (UnderlyingNetworkRecord) o;
+
+ return network.equals(that.network)
+ && networkCapabilities.equals(that.networkCapabilities)
+ && linkProperties.equals(that.linkProperties)
+ && isBlocked == that.isBlocked;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(network, networkCapabilities, linkProperties, isBlocked);
+ }
+
+ static Comparator<UnderlyingNetworkRecord> getComparator(
+ VcnContext vcnContext,
+ List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates,
+ ParcelUuid subscriptionGroup,
+ TelephonySubscriptionSnapshot snapshot,
+ UnderlyingNetworkRecord currentlySelected,
+ PersistableBundle carrierConfig) {
+ return (left, right) -> {
+ final int leftIndex =
+ NetworkPriorityClassifier.calculatePriorityClass(
+ vcnContext,
+ left,
+ underlyingNetworkTemplates,
+ subscriptionGroup,
+ snapshot,
+ currentlySelected,
+ carrierConfig);
+ final int rightIndex =
+ NetworkPriorityClassifier.calculatePriorityClass(
+ vcnContext,
+ right,
+ underlyingNetworkTemplates,
+ subscriptionGroup,
+ snapshot,
+ currentlySelected,
+ carrierConfig);
+
+ // In the case of networks in the same priority class, prioritize based on other
+ // criteria (eg. actively selected network, link metrics, etc)
+ if (leftIndex == rightIndex) {
+ // TODO: Improve the strategy of network selection when both UnderlyingNetworkRecord
+ // fall into the same priority class.
+ if (isSelected(left, currentlySelected)) {
+ return -1;
+ }
+ if (isSelected(left, currentlySelected)) {
+ return 1;
+ }
+ }
+ return Integer.compare(leftIndex, rightIndex);
+ };
+ }
+
+ private static boolean isSelected(
+ UnderlyingNetworkRecord recordToCheck, UnderlyingNetworkRecord currentlySelected) {
+ if (currentlySelected == null) {
+ return false;
+ }
+ if (currentlySelected.network == recordToCheck.network) {
+ return true;
+ }
+ return false;
+ }
+
+ /** Dumps the state of this record for logging and debugging purposes. */
+ void dump(
+ VcnContext vcnContext,
+ IndentingPrintWriter pw,
+ List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates,
+ ParcelUuid subscriptionGroup,
+ TelephonySubscriptionSnapshot snapshot,
+ UnderlyingNetworkRecord currentlySelected,
+ PersistableBundle carrierConfig) {
+ pw.println("UnderlyingNetworkRecord:");
+ pw.increaseIndent();
+
+ final int priorityIndex =
+ NetworkPriorityClassifier.calculatePriorityClass(
+ vcnContext,
+ this,
+ underlyingNetworkTemplates,
+ subscriptionGroup,
+ snapshot,
+ currentlySelected,
+ carrierConfig);
+
+ pw.println("Priority index:" + priorityIndex);
+ pw.println("mNetwork: " + network);
+ pw.println("mNetworkCapabilities: " + networkCapabilities);
+ pw.println("mLinkProperties: " + linkProperties);
+
+ pw.decreaseIndent();
+ }
+
+ /** Builder to incrementally construct an UnderlyingNetworkRecord. */
+ static class Builder {
+ @NonNull private final Network mNetwork;
+
+ @Nullable private NetworkCapabilities mNetworkCapabilities;
+ @Nullable private LinkProperties mLinkProperties;
+ boolean mIsBlocked;
+ boolean mWasIsBlockedSet;
+
+ @Nullable private UnderlyingNetworkRecord mCached;
+
+ Builder(@NonNull Network network) {
+ mNetwork = network;
+ }
+
+ @NonNull
+ Network getNetwork() {
+ return mNetwork;
+ }
+
+ void setNetworkCapabilities(@NonNull NetworkCapabilities networkCapabilities) {
+ mNetworkCapabilities = networkCapabilities;
+ mCached = null;
+ }
+
+ @Nullable
+ NetworkCapabilities getNetworkCapabilities() {
+ return mNetworkCapabilities;
+ }
+
+ void setLinkProperties(@NonNull LinkProperties linkProperties) {
+ mLinkProperties = linkProperties;
+ mCached = null;
+ }
+
+ void setIsBlocked(boolean isBlocked) {
+ mIsBlocked = isBlocked;
+ mWasIsBlockedSet = true;
+ mCached = null;
+ }
+
+ boolean isValid() {
+ return mNetworkCapabilities != null && mLinkProperties != null && mWasIsBlockedSet;
+ }
+
+ UnderlyingNetworkRecord build() {
+ if (!isValid()) {
+ throw new IllegalArgumentException(
+ "Called build before UnderlyingNetworkRecord was valid");
+ }
+
+ if (mCached == null) {
+ mCached =
+ new UnderlyingNetworkRecord(
+ mNetwork, mNetworkCapabilities, mLinkProperties, mIsBlocked);
+ }
+
+ return mCached;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/vcn/util/PersistableBundleUtils.java b/services/core/java/com/android/server/vcn/util/PersistableBundleUtils.java
index 5c1b5ffb2209..1c675c228554 100644
--- a/services/core/java/com/android/server/vcn/util/PersistableBundleUtils.java
+++ b/services/core/java/com/android/server/vcn/util/PersistableBundleUtils.java
@@ -46,6 +46,7 @@ public class PersistableBundleUtils {
private static final String PARCEL_UUID_KEY = "PARCEL_UUID";
private static final String BYTE_ARRAY_KEY = "BYTE_ARRAY_KEY";
private static final String INTEGER_KEY = "INTEGER_KEY";
+ private static final String STRING_KEY = "STRING_KEY";
/**
* Functional interface to convert an object of the specified type to a PersistableBundle.
@@ -91,6 +92,21 @@ public class PersistableBundleUtils {
return bundle.getInt(INTEGER_KEY);
};
+ /** Serializer to convert s String to a PersistableBundle. */
+ public static final Serializer<String> STRING_SERIALIZER =
+ (i) -> {
+ final PersistableBundle result = new PersistableBundle();
+ result.putString(STRING_KEY, i);
+ return result;
+ };
+
+ /** Deserializer to convert a PersistableBundle to a String. */
+ public static final Deserializer<String> STRING_DESERIALIZER =
+ (bundle) -> {
+ Objects.requireNonNull(bundle, "PersistableBundle is null");
+ return bundle.getString(STRING_KEY);
+ };
+
/**
* Converts a ParcelUuid to a PersistableBundle.
*
diff --git a/services/core/java/com/android/server/vibrator/OWNERS b/services/core/java/com/android/server/vibrator/OWNERS
index 7e7335d68d3b..08f0a90485cc 100644
--- a/services/core/java/com/android/server/vibrator/OWNERS
+++ b/services/core/java/com/android/server/vibrator/OWNERS
@@ -1 +1,3 @@
+lsandrade@google.com
michaelwr@google.com
+sbowden@google.com \ No newline at end of file
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 618e421038c7..e70517f3080b 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -1457,13 +1457,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
final Task rootTask = getRootTask();
- // If we reparent, make sure to remove ourselves from the old animation registry.
- if (mAnimatingActivityRegistry != null) {
- mAnimatingActivityRegistry.notifyFinished(this);
- }
- mAnimatingActivityRegistry = rootTask != null
- ? rootTask.getAnimatingActivityRegistry()
- : null;
+ updateAnimatingActivityRegistry();
if (task == mLastParentBeforePip) {
// Activity's reparented back from pip, clear the links once established
@@ -1496,6 +1490,20 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
}
+ void updateAnimatingActivityRegistry() {
+ final Task rootTask = getRootTask();
+ final AnimatingActivityRegistry registry = rootTask != null
+ ? rootTask.getAnimatingActivityRegistry()
+ : null;
+
+ // If we reparent, make sure to remove ourselves from the old animation registry.
+ if (mAnimatingActivityRegistry != null && mAnimatingActivityRegistry != registry) {
+ mAnimatingActivityRegistry.notifyFinished(this);
+ }
+
+ mAnimatingActivityRegistry = registry;
+ }
+
/**
* Sets {@link #mLastParentBeforePip} to the current parent Task, it's caller's job to ensure
* {@link #getTask()} is set before this is called.
@@ -5186,6 +5194,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
mAtmService.updateActivityUsageStats(this, Event.ACTIVITY_STOPPED);
break;
case DESTROYED:
+ if (app != null && (mVisible || mVisibleRequested)) {
+ // The app may be died while visible (no PAUSED state).
+ mAtmService.updateBatteryStats(this, false);
+ }
mAtmService.updateActivityUsageStats(this, Event.ACTIVITY_DESTROYED);
// Fall through.
case DESTROYING:
diff --git a/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java b/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java
index f61cc94b6181..a83a033985c5 100644
--- a/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java
+++ b/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java
@@ -65,7 +65,16 @@ public class DisplayAreaOrganizerController extends IDisplayAreaOrganizerControl
@Override
public void binderDied() {
synchronized (mGlobalLock) {
- mOrganizersByFeatureIds.remove(mFeature).destroy();
+ IDisplayAreaOrganizer featureOrganizer = getOrganizerByFeature(mFeature);
+ if (featureOrganizer != null) {
+ IBinder organizerBinder = featureOrganizer.asBinder();
+ if (!organizerBinder.equals(mOrganizer.asBinder()) &&
+ organizerBinder.isBinderAlive()) {
+ Slog.d(TAG, "Dead organizer replaced for feature=" + mFeature);
+ return;
+ }
+ mOrganizersByFeatureIds.remove(mFeature).destroy();
+ }
}
}
}
@@ -172,7 +181,7 @@ public class DisplayAreaOrganizerController extends IDisplayAreaOrganizerControl
organizer.asBinder(), uid);
mOrganizersByFeatureIds.entrySet().removeIf((entry) -> {
final boolean matches = entry.getValue().mOrganizer.asBinder()
- == organizer.asBinder();
+ .equals(organizer.asBinder());
if (matches) {
entry.getValue().destroy();
}
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 1ff2ebeb8c14..d69d32effc76 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -1644,11 +1644,15 @@ public class DisplayPolicy {
statusBarBottom);
}
- sTmpRect.set(windowFrames.mFrame);
- sTmpRect.intersect(displayFrames.mDisplayCutoutSafe);
- sTmpRect.top = windowFrames.mFrame.top; // Ignore top display cutout inset
- sTmpRect.bottom = statusBarBottom; // Use collapsed status bar size
- contentFrame.set(sTmpRect);
+ final InsetsState state = displayFrames.mInsetsState;
+ sTmpRect.set(displayFrames.mDisplayCutoutSafe);
+ // The status bar content can extend into regular display cutout insets but not
+ // waterfall insets.
+ sTmpRect.top = Math.max(state.getDisplayCutout().getWaterfallInsets().top, 0);
+
+ contentFrame.set(windowFrames.mFrame);
+ contentFrame.intersect(sTmpRect);
+ contentFrame.bottom = statusBarBottom; // Use collapsed status bar size
}
private int layoutNavigationBar(DisplayFrames displayFrames, Rect contentFrame) {
diff --git a/services/core/java/com/android/server/wm/RefreshRatePolicy.java b/services/core/java/com/android/server/wm/RefreshRatePolicy.java
index b63843dd20d4..7bddb620c94d 100644
--- a/services/core/java/com/android/server/wm/RefreshRatePolicy.java
+++ b/services/core/java/com/android/server/wm/RefreshRatePolicy.java
@@ -164,7 +164,19 @@ class RefreshRatePolicy {
return 0;
}
- return w.mAttrs.preferredMinDisplayRefreshRate;
+ if (w.mAttrs.preferredMinDisplayRefreshRate > 0) {
+ return w.mAttrs.preferredMinDisplayRefreshRate;
+ }
+
+ String packageName = w.getOwningPackage();
+ // If app is using Camera, we set both the min and max refresh rate to the camera's
+ // preferred refresh rate to make sure we don't end up with a refresh rate lower
+ // than the camera capture rate, which will lead to dropping camera frames.
+ if (mNonHighRefreshRatePackages.contains(packageName)) {
+ return mLowRefreshRateMode.getRefreshRate();
+ }
+
+ return 0;
}
float getPreferredMaxRefreshRate(WindowState w) {
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimator.java b/services/core/java/com/android/server/wm/SurfaceAnimator.java
index 50c9b31f425a..53e33781bca6 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimator.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimator.java
@@ -88,6 +88,8 @@ class SurfaceAnimator {
private boolean mAnimationStartDelayed;
+ private boolean mAnimationFinished;
+
/**
* @param animatable The object to animate.
* @param staticAnimationFinishedCallback Callback to invoke when an animation has finished
@@ -137,6 +139,7 @@ class SurfaceAnimator {
|| anim.shouldDeferAnimationFinish(resetAndInvokeFinish))) {
resetAndInvokeFinish.run();
}
+ mAnimationFinished = true;
}
};
}
@@ -302,6 +305,9 @@ class SurfaceAnimator {
Slog.w(TAG, "Unable to transfer animation, surface or parent is null");
cancelAnimation();
return;
+ } else if (from.mAnimationFinished) {
+ Slog.w(TAG, "Unable to transfer animation, because " + from + " animation is finished");
+ return;
}
endDelayingAnimationStart();
final Transaction t = mAnimatable.getPendingTransaction();
@@ -392,6 +398,7 @@ class SurfaceAnimator {
SurfaceControl leash = mLeash;
mLeash = null;
final boolean scheduleAnim = removeLeash(t, mAnimatable, leash, destroyLeash);
+ mAnimationFinished = false;
if (scheduleAnim) {
mService.scheduleAnimationLocked();
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index fd5df6f6c390..632c63708549 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -1255,6 +1255,9 @@ class Task extends TaskFragment {
adjustBoundsForDisplayChangeIfNeeded(getDisplayContent());
mRootWindowContainer.updateUIDsPresentOnDisplay();
+
+ // Ensure all animations are finished at same time in split-screen mode.
+ forAllActivities(ActivityRecord::updateAnimatingActivityRegistry);
}
@Override
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index 141d889c1804..6a6751b7c1fe 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -119,6 +119,11 @@ class TaskSnapshotController {
*/
private final boolean mIsRunningOnWear;
+ /**
+ * Flag indicating if device configuration has disabled app snapshots.
+ */
+ private final boolean mConfigDisableTaskSnapshots;
+
TaskSnapshotController(WindowManagerService service) {
mService = service;
mPersister = new TaskSnapshotPersister(mService, Environment::getDataSystemCeDirectory);
@@ -132,6 +137,8 @@ class TaskSnapshotController {
PackageManager.FEATURE_WATCH);
mHighResTaskSnapshotScale = mService.mContext.getResources().getFloat(
com.android.internal.R.dimen.config_highResTaskSnapshotScale);
+ mConfigDisableTaskSnapshots = mService.mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_disableTaskSnapshots);
}
void systemReady() {
@@ -497,7 +504,8 @@ class TaskSnapshotController {
}
boolean shouldDisableSnapshots() {
- return mIsRunningOnWear || mIsRunningOnTv || mIsRunningOnIoT;
+ return mIsRunningOnWear || mIsRunningOnTv || mIsRunningOnIoT
+ || mConfigDisableTaskSnapshots;
}
/**
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 53401fd47178..e48b5e17739b 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -85,6 +85,7 @@ cc_library_static {
header_libs: [
"bionic_libc_platform_headers",
+ "bpf_connectivity_headers",
],
}
@@ -140,8 +141,8 @@ cc_defaults {
"libutils",
"libhwui",
"libbpf_android",
- "libnetdbpf",
"libnetdutils",
+ "libnetworkstats",
"libpsi",
"libdataloader",
"libincfs",
@@ -160,12 +161,12 @@ cc_defaults {
"android.hardware.input.classifier@1.0",
"android.hardware.ir@1.0",
"android.hardware.light@2.0",
- "android.hardware.memtrack-V1-ndk_platform",
+ "android.hardware.memtrack-V1-ndk",
"android.hardware.power@1.0",
"android.hardware.power@1.1",
"android.hardware.power-V2-cpp",
"android.hardware.power.stats@1.0",
- "android.hardware.power.stats-V1-ndk_platform",
+ "android.hardware.power.stats-V1-ndk",
"android.hardware.thermal@1.0",
"android.hardware.tv.input@1.0",
"android.hardware.vibrator-V2-cpp",
@@ -178,10 +179,10 @@ cc_defaults {
"android.frameworks.schedulerservice@1.0",
"android.frameworks.sensorservice@1.0",
"android.frameworks.stats@1.0",
- "android.frameworks.stats-V1-ndk_platform",
+ "android.frameworks.stats-V1-ndk",
"android.system.suspend.control-V1-cpp",
"android.system.suspend.control.internal-cpp",
- "android.system.suspend@1.0",
+ "android.system.suspend-V1-ndk",
"service.incremental",
],
diff --git a/services/core/jni/BroadcastRadio/types.h b/services/core/jni/BroadcastRadio/types.h
index 910bb7c0a4d1..4d286bf05650 100644
--- a/services/core/jni/BroadcastRadio/types.h
+++ b/services/core/jni/BroadcastRadio/types.h
@@ -30,13 +30,13 @@ namespace BroadcastRadio {
// Keep in sync with STATUS_* constants from RadioManager.java.
enum class Status : jint {
OK = 0,
- ERROR = -0x80000000ll, // Integer.MIN_VALUE
+ ERROR = -0x80000000LL, // Integer.MIN_VALUE
PERMISSION_DENIED = -1, // -EPERM
- NO_INIT = -19, // -ENODEV
- BAD_VALUE = -22, // -EINVAL
- DEAD_OBJECT = -32, // -EPIPE
- INVALID_OPERATION = -38, // -ENOSYS
- TIMED_OUT = -110, // -ETIMEDOUT
+ NO_INIT = -19, // -ENODEV
+ BAD_VALUE = -22, // -EINVAL
+ DEAD_OBJECT = -32, // -EPIPE
+ INVALID_OPERATION = -38, // -ENOSYS
+ TIMED_OUT = -110, // -ETIMEDOUT
};
// Keep in sync with REGION_* constants from RadioManager.java.
diff --git a/services/core/jni/com_android_server_ConsumerIrService.cpp b/services/core/jni/com_android_server_ConsumerIrService.cpp
index 2ca348b3ae46..63daa3503bd5 100644
--- a/services/core/jni/com_android_server_ConsumerIrService.cpp
+++ b/services/core/jni/com_android_server_ConsumerIrService.cpp
@@ -34,7 +34,7 @@ namespace android {
static sp<IConsumerIr> mHal;
-static jboolean halOpen(JNIEnv* /* env */, jobject /* obj */) {
+static jboolean getHidlHalService(JNIEnv * /* env */, jobject /* obj */) {
// TODO(b/31632518)
mHal = IConsumerIr::getService();
return mHal != nullptr;
@@ -84,9 +84,9 @@ static jintArray halGetCarrierFrequencies(JNIEnv *env, jobject /* obj */) {
}
static const JNINativeMethod method_table[] = {
- { "halOpen", "()Z", (void *)halOpen },
- { "halTransmit", "(I[I)I", (void *)halTransmit },
- { "halGetCarrierFrequencies", "()[I", (void *)halGetCarrierFrequencies},
+ {"getHidlHalService", "()Z", (void *)getHidlHalService},
+ {"halTransmit", "(I[I)I", (void *)halTransmit},
+ {"halGetCarrierFrequencies", "()[I", (void *)halGetCarrierFrequencies},
};
int register_android_server_ConsumerIrService(JNIEnv *env) {
diff --git a/services/core/jni/com_android_server_UsbAlsaJackDetector.cpp b/services/core/jni/com_android_server_UsbAlsaJackDetector.cpp
index ccb4f5995330..cba54b39fe9a 100644
--- a/services/core/jni/com_android_server_UsbAlsaJackDetector.cpp
+++ b/services/core/jni/com_android_server_UsbAlsaJackDetector.cpp
@@ -25,7 +25,6 @@
#include <stdio.h>
#include <string.h>
-#include <asm/byteorder.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
diff --git a/services/core/jni/com_android_server_UsbDescriptorParser.cpp b/services/core/jni/com_android_server_UsbDescriptorParser.cpp
index d29d3fcb51d5..9917bcb45c0a 100644
--- a/services/core/jni/com_android_server_UsbDescriptorParser.cpp
+++ b/services/core/jni/com_android_server_UsbDescriptorParser.cpp
@@ -15,16 +15,14 @@
*/
#define LOG_TAG "UsbHostManagerJNI"
-#include "utils/Log.h"
-
+#include <nativehelper/JNIHelp.h>
#include <stdlib.h>
+#include <usbhost/usbhost.h>
+#include <usbhost/usbhost_jni.h>
#include "jni.h"
-#include <nativehelper/JNIHelp.h>
-
-#include <usbhost/usbhost.h>
+#include "utils/Log.h"
-#define MAX_DESCRIPTORS_LENGTH 4096
static const int USB_CONTROL_TRANSFER_TIMEOUT_MS = 200;
// com.android.server.usb.descriptors
@@ -41,26 +39,9 @@ jbyteArray JNICALL Java_com_android_server_usb_descriptors_UsbDescriptorParser_g
}
int fd = usb_device_get_fd(device);
- if (fd < 0) {
- usb_device_close(device);
- return NULL;
- }
-
- // from android_hardware_UsbDeviceConnection_get_desc()
- jbyte buffer[MAX_DESCRIPTORS_LENGTH];
- lseek(fd, 0, SEEK_SET);
- int numBytes = read(fd, buffer, sizeof(buffer));
- jbyteArray ret = NULL;
+ jbyteArray descriptors = usb_jni_read_descriptors(env, fd);
usb_device_close(device);
-
- if (numBytes > 0) {
- ret = env->NewByteArray(numBytes);
- env->SetByteArrayRegion(ret, 0, numBytes, buffer);
- } else {
- ALOGE("error reading descriptors\n");
- }
-
- return ret;
+ return descriptors;
}
jstring JNICALL Java_com_android_server_usb_descriptors_UsbDescriptorParser_getDescriptorString_1native(
diff --git a/services/core/jni/com_android_server_UsbDeviceManager.cpp b/services/core/jni/com_android_server_UsbDeviceManager.cpp
index 3ab5920d8b59..0a9ce2fed7fc 100644
--- a/services/core/jni/com_android_server_UsbDeviceManager.cpp
+++ b/services/core/jni/com_android_server_UsbDeviceManager.cpp
@@ -25,7 +25,6 @@
#include "MtpDescriptors.h"
#include <stdio.h>
-#include <asm/byteorder.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
diff --git a/services/core/jni/com_android_server_UsbHostManager.cpp b/services/core/jni/com_android_server_UsbHostManager.cpp
index a629b69c1c27..e29d2ca663f7 100644
--- a/services/core/jni/com_android_server_UsbHostManager.cpp
+++ b/services/core/jni/com_android_server_UsbHostManager.cpp
@@ -23,7 +23,6 @@
#include "android_runtime/Log.h"
#include <stdio.h>
-#include <asm/byteorder.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
@@ -31,8 +30,6 @@
#include <usbhost/usbhost.h>
-#define MAX_DESCRIPTORS_LENGTH 4096
-
namespace android
{
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index bb9740b60f78..7066d5680f66 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -1585,7 +1585,7 @@ static jobject nativeCreateInputChannel(JNIEnv* env, jclass /* clazz */, jlong p
if (!inputChannel.ok()) {
std::string message = inputChannel.error().message();
- message += StringPrintf(" Status=%d", inputChannel.error().code());
+ message += StringPrintf(" Status=%d", static_cast<int>(inputChannel.error().code()));
jniThrowRuntimeException(env, message.c_str());
return nullptr;
}
@@ -1619,7 +1619,7 @@ static jobject nativeCreateInputMonitor(JNIEnv* env, jclass /* clazz */, jlong p
if (!inputChannel.ok()) {
std::string message = inputChannel.error().message();
- message += StringPrintf(" Status=%d", inputChannel.error().code());
+ message += StringPrintf(" Status=%d", static_cast<int>(inputChannel.error().code()));
jniThrowRuntimeException(env, message.c_str());
return nullptr;
}
diff --git a/services/core/jni/com_android_server_net_NetworkStatsService.cpp b/services/core/jni/com_android_server_net_NetworkStatsService.cpp
index 10b248a70e7e..39cbaf716fc0 100644
--- a/services/core/jni/com_android_server_net_NetworkStatsService.cpp
+++ b/services/core/jni/com_android_server_net_NetworkStatsService.cpp
@@ -16,20 +16,18 @@
#define LOG_TAG "NetworkStatsNative"
+#include <cutils/qtaguid.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-
-#include "core_jni_helpers.h"
#include <jni.h>
#include <nativehelper/ScopedUtfChars.h>
-#include <utils/misc.h>
+#include <sys/stat.h>
+#include <sys/types.h>
#include <utils/Log.h>
+#include <utils/misc.h>
-#include "android-base/unique_fd.h"
#include "bpf/BpfUtils.h"
#include "netdbpf/BpfNetworkStats.h"
@@ -38,9 +36,6 @@ using android::bpf::bpfGetIfaceStats;
namespace android {
-static const char* QTAGUID_IFACE_STATS = "/proc/net/xt_qtaguid/iface_stat_fmt";
-static const char* QTAGUID_UID_STATS = "/proc/net/xt_qtaguid/stats";
-
// NOTE: keep these in sync with TrafficStats.java
static const uint64_t UNKNOWN = -1;
@@ -72,102 +67,17 @@ static uint64_t getStatsType(Stats* stats, StatsType type) {
}
}
-static int parseIfaceStats(const char* iface, Stats* stats) {
- FILE *fp = fopen(QTAGUID_IFACE_STATS, "r");
- if (fp == NULL) {
- return -1;
- }
-
- char buffer[384];
- char cur_iface[32];
- bool foundTcp = false;
- uint64_t rxBytes, rxPackets, txBytes, txPackets, tcpRxPackets, tcpTxPackets;
-
- while (fgets(buffer, sizeof(buffer), fp) != NULL) {
- int matched = sscanf(buffer, "%31s %" SCNu64 " %" SCNu64 " %" SCNu64
- " %" SCNu64 " " "%*u %" SCNu64 " %*u %*u %*u %*u "
- "%*u %" SCNu64 " %*u %*u %*u %*u", cur_iface, &rxBytes,
- &rxPackets, &txBytes, &txPackets, &tcpRxPackets, &tcpTxPackets);
- if (matched >= 5) {
- if (matched == 7) {
- foundTcp = true;
- }
- if (!iface || !strcmp(iface, cur_iface)) {
- stats->rxBytes += rxBytes;
- stats->rxPackets += rxPackets;
- stats->txBytes += txBytes;
- stats->txPackets += txPackets;
- if (matched == 7) {
- stats->tcpRxPackets += tcpRxPackets;
- stats->tcpTxPackets += tcpTxPackets;
- }
- }
- }
- }
-
- if (!foundTcp) {
- stats->tcpRxPackets = UNKNOWN;
- stats->tcpTxPackets = UNKNOWN;
- }
-
- if (fclose(fp) != 0) {
- return -1;
- }
- return 0;
-}
-
-static int parseUidStats(const uint32_t uid, Stats* stats) {
- FILE *fp = fopen(QTAGUID_UID_STATS, "r");
- if (fp == NULL) {
- return -1;
- }
-
- char buffer[384];
- char iface[32];
- uint32_t idx, cur_uid, set;
- uint64_t tag, rxBytes, rxPackets, txBytes, txPackets;
-
- while (fgets(buffer, sizeof(buffer), fp) != NULL) {
- if (sscanf(buffer,
- "%" SCNu32 " %31s 0x%" SCNx64 " %u %u %" SCNu64 " %" SCNu64
- " %" SCNu64 " %" SCNu64 "",
- &idx, iface, &tag, &cur_uid, &set, &rxBytes, &rxPackets,
- &txBytes, &txPackets) == 9) {
- if (uid == cur_uid && tag == 0L) {
- stats->rxBytes += rxBytes;
- stats->rxPackets += rxPackets;
- stats->txBytes += txBytes;
- stats->txPackets += txPackets;
- }
- }
- }
-
- if (fclose(fp) != 0) {
- return -1;
- }
- return 0;
-}
-
-static jlong getTotalStat(JNIEnv* env, jclass clazz, jint type, jboolean useBpfStats) {
+static jlong getTotalStat(JNIEnv* env, jclass clazz, jint type) {
Stats stats = {};
- if (useBpfStats) {
- if (bpfGetIfaceStats(NULL, &stats) == 0) {
- return getStatsType(&stats, (StatsType) type);
- } else {
- return UNKNOWN;
- }
- }
-
- if (parseIfaceStats(NULL, &stats) == 0) {
+ if (bpfGetIfaceStats(NULL, &stats) == 0) {
return getStatsType(&stats, (StatsType) type);
} else {
return UNKNOWN;
}
}
-static jlong getIfaceStat(JNIEnv* env, jclass clazz, jstring iface, jint type,
- jboolean useBpfStats) {
+static jlong getIfaceStat(JNIEnv* env, jclass clazz, jstring iface, jint type) {
ScopedUtfChars iface8(env, iface);
if (iface8.c_str() == NULL) {
return UNKNOWN;
@@ -175,33 +85,17 @@ static jlong getIfaceStat(JNIEnv* env, jclass clazz, jstring iface, jint type,
Stats stats = {};
- if (useBpfStats) {
- if (bpfGetIfaceStats(iface8.c_str(), &stats) == 0) {
- return getStatsType(&stats, (StatsType) type);
- } else {
- return UNKNOWN;
- }
- }
-
- if (parseIfaceStats(iface8.c_str(), &stats) == 0) {
+ if (bpfGetIfaceStats(iface8.c_str(), &stats) == 0) {
return getStatsType(&stats, (StatsType) type);
} else {
return UNKNOWN;
}
}
-static jlong getUidStat(JNIEnv* env, jclass clazz, jint uid, jint type, jboolean useBpfStats) {
+static jlong getUidStat(JNIEnv* env, jclass clazz, jint uid, jint type) {
Stats stats = {};
- if (useBpfStats) {
- if (bpfGetUidStats(uid, &stats) == 0) {
- return getStatsType(&stats, (StatsType) type);
- } else {
- return UNKNOWN;
- }
- }
-
- if (parseUidStats(uid, &stats) == 0) {
+ if (bpfGetUidStats(uid, &stats) == 0) {
return getStatsType(&stats, (StatsType) type);
} else {
return UNKNOWN;
@@ -209,9 +103,9 @@ static jlong getUidStat(JNIEnv* env, jclass clazz, jint uid, jint type, jboolean
}
static const JNINativeMethod gMethods[] = {
- {"nativeGetTotalStat", "(IZ)J", (void*) getTotalStat},
- {"nativeGetIfaceStat", "(Ljava/lang/String;IZ)J", (void*) getIfaceStat},
- {"nativeGetUidStat", "(IIZ)J", (void*) getUidStat},
+ {"nativeGetTotalStat", "(I)J", (void*)getTotalStat},
+ {"nativeGetIfaceStat", "(Ljava/lang/String;I)J", (void*)getIfaceStat},
+ {"nativeGetUidStat", "(II)J", (void*)getUidStat},
};
int register_android_server_net_NetworkStatsService(JNIEnv* env) {
diff --git a/services/core/jni/com_android_server_power_PowerManagerService.cpp b/services/core/jni/com_android_server_power_PowerManagerService.cpp
index ae7ea3cd90e8..fe86ff1ef7ef 100644
--- a/services/core/jni/com_android_server_power_PowerManagerService.cpp
+++ b/services/core/jni/com_android_server_power_PowerManagerService.cpp
@@ -18,11 +18,12 @@
//#define LOG_NDEBUG 0
+#include <aidl/android/system/suspend/ISystemSuspend.h>
+#include <aidl/android/system/suspend/IWakeLock.h>
#include <android/hardware/power/1.1/IPower.h>
#include <android/hardware/power/Boost.h>
#include <android/hardware/power/IPower.h>
#include <android/hardware/power/Mode.h>
-#include <android/system/suspend/1.0/ISystemSuspend.h>
#include <android/system/suspend/ISuspendControlService.h>
#include <android/system/suspend/internal/ISuspendControlServiceInternal.h>
#include <nativehelper/JNIHelp.h>
@@ -34,6 +35,7 @@
#include <limits.h>
#include <android-base/chrono_utils.h>
+#include <android/binder_manager.h>
#include <android_runtime/AndroidRuntime.h>
#include <android_runtime/Log.h>
#include <binder/IServiceManager.h>
@@ -41,20 +43,20 @@
#include <hardware/power.h>
#include <hardware_legacy/power.h>
#include <hidl/ServiceManagement.h>
+#include <utils/Log.h>
+#include <utils/String8.h>
#include <utils/Timers.h>
#include <utils/misc.h>
-#include <utils/String8.h>
-#include <utils/Log.h>
#include "com_android_server_power_PowerManagerService.h"
+using aidl::android::system::suspend::ISystemSuspend;
+using aidl::android::system::suspend::IWakeLock;
+using aidl::android::system::suspend::WakeLockType;
using android::String8;
using android::hardware::power::Boost;
using android::hardware::power::Mode;
using android::system::suspend::ISuspendControlService;
-using android::system::suspend::V1_0::ISystemSuspend;
-using android::system::suspend::V1_0::IWakeLock;
-using android::system::suspend::V1_0::WakeLockType;
using IPowerV1_1 = android::hardware::power::V1_1::IPower;
using IPowerV1_0 = android::hardware::power::V1_0::IPower;
using IPowerAidl = android::hardware::power::IPower;
@@ -133,20 +135,21 @@ void android_server_PowerManagerService_userActivity(nsecs_t eventTime, int32_t
}
}
-static sp<ISystemSuspend> gSuspendHal = nullptr;
+static std::shared_ptr<ISystemSuspend> gSuspendHal = nullptr;
static sp<ISuspendControlService> gSuspendControl = nullptr;
static sp<system::suspend::internal::ISuspendControlServiceInternal> gSuspendControlInternal =
nullptr;
-static sp<IWakeLock> gSuspendBlocker = nullptr;
+static std::shared_ptr<IWakeLock> gSuspendBlocker = nullptr;
static std::mutex gSuspendMutex;
// Assume SystemSuspend HAL is always alive.
// TODO: Force device to restart if SystemSuspend HAL dies.
-sp<ISystemSuspend> getSuspendHal() {
+std::shared_ptr<ISystemSuspend> getSuspendHal() {
static std::once_flag suspendHalFlag;
- std::call_once(suspendHalFlag, [](){
- ::android::hardware::details::waitForHwService(ISystemSuspend::descriptor, "default");
- gSuspendHal = ISystemSuspend::getService();
+ std::call_once(suspendHalFlag, []() {
+ const std::string suspendInstance = std::string() + ISystemSuspend::descriptor + "/default";
+ gSuspendHal = ISystemSuspend::fromBinder(
+ ndk::SpAIBinder(AServiceManager_waitForService(suspendInstance.c_str())));
assert(gSuspendHal != nullptr);
});
return gSuspendHal;
@@ -175,16 +178,17 @@ sp<system::suspend::internal::ISuspendControlServiceInternal> getSuspendControlI
void enableAutoSuspend() {
static bool enabled = false;
if (!enabled) {
+ static sp<IBinder> autosuspendClientToken = new BBinder();
sp<system::suspend::internal::ISuspendControlServiceInternal> suspendControl =
getSuspendControlInternal();
- suspendControl->enableAutosuspend(&enabled);
+ suspendControl->enableAutosuspend(autosuspendClientToken, &enabled);
}
{
std::lock_guard<std::mutex> lock(gSuspendMutex);
if (gSuspendBlocker) {
gSuspendBlocker->release();
- gSuspendBlocker.clear();
+ gSuspendBlocker = nullptr;
}
}
}
@@ -192,9 +196,10 @@ void enableAutoSuspend() {
void disableAutoSuspend() {
std::lock_guard<std::mutex> lock(gSuspendMutex);
if (!gSuspendBlocker) {
- sp<ISystemSuspend> suspendHal = getSuspendHal();
- gSuspendBlocker = suspendHal->acquireWakeLock(WakeLockType::PARTIAL,
- "PowerManager.SuspendLockout");
+ std::shared_ptr<ISystemSuspend> suspendHal = getSuspendHal();
+ suspendHal->acquireWakeLock(WakeLockType::PARTIAL, "PowerManager.SuspendLockout",
+ &gSuspendBlocker);
+ assert(gSuspendBlocker != nullptr);
}
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
index 37a84f3698c1..1c9d58458629 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
@@ -32,6 +32,7 @@ import android.app.admin.DeviceAdminInfo;
import android.app.admin.DevicePolicyManager;
import android.app.admin.FactoryResetProtectionPolicy;
import android.app.admin.PasswordPolicy;
+import android.app.admin.PreferentialNetworkServiceConfig;
import android.graphics.Color;
import android.os.Bundle;
import android.os.PersistableBundle;
@@ -294,6 +295,8 @@ class ActiveAdmin {
public boolean mAdminCanGrantSensorsPermissions;
public boolean mPreferentialNetworkServiceEnabled =
DevicePolicyManager.PREFERENTIAL_NETWORK_SERVICE_ENABLED_DEFAULT;
+ public PreferentialNetworkServiceConfig mPreferentialNetworkServiceConfig =
+ PreferentialNetworkServiceConfig.DEFAULT;
private static final boolean USB_DATA_SIGNALING_ENABLED_DEFAULT = true;
boolean mUsbDataSignalingEnabled = USB_DATA_SIGNALING_ENABLED_DEFAULT;
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index a46ae27ff8d9..dabdff21964f 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -109,6 +109,8 @@ import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES;
import static android.net.ConnectivityManager.PROFILE_NETWORK_PREFERENCE_DEFAULT;
import static android.net.ConnectivityManager.PROFILE_NETWORK_PREFERENCE_ENTERPRISE;
+import static android.net.ConnectivityManager.PROFILE_NETWORK_PREFERENCE_ENTERPRISE_NO_FALLBACK;
+import static android.net.NetworkCapabilities.NET_ENTERPRISE_ID_1;
import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK;
import static android.provider.Settings.Global.PRIVATE_DNS_SPECIFIER;
import static android.provider.Settings.Secure.USER_SETUP_COMPLETE;
@@ -179,6 +181,7 @@ import android.app.admin.NetworkEvent;
import android.app.admin.ParcelableGranteeMap;
import android.app.admin.PasswordMetrics;
import android.app.admin.PasswordPolicy;
+import android.app.admin.PreferentialNetworkServiceConfig;
import android.app.admin.SecurityLog;
import android.app.admin.SecurityLog.SecurityEvent;
import android.app.admin.StartInstallingUpdateCallback;
@@ -231,6 +234,7 @@ import android.media.IAudioService;
import android.net.ConnectivityManager;
import android.net.ConnectivitySettingsManager;
import android.net.IIpConnectivityMetrics;
+import android.net.ProfileNetworkPreference;
import android.net.ProxyInfo;
import android.net.Uri;
import android.net.VpnManager;
@@ -3310,14 +3314,14 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
updatePermissionPolicyCache(userId);
updateAdminCanGrantSensorsPermissionCache(userId);
- final boolean preferentialNetworkServiceEnabled;
+ final PreferentialNetworkServiceConfig preferentialNetworkServiceConfig;
synchronized (getLockObject()) {
ActiveAdmin owner = getDeviceOrProfileOwnerAdminLocked(userId);
- preferentialNetworkServiceEnabled = owner != null
- ? owner.mPreferentialNetworkServiceEnabled
- : DevicePolicyManager.PREFERENTIAL_NETWORK_SERVICE_ENABLED_DEFAULT;
+ preferentialNetworkServiceConfig = owner != null
+ ? owner.mPreferentialNetworkServiceConfig
+ : PreferentialNetworkServiceConfig.DEFAULT;
}
- updateNetworkPreferenceForUser(userId, preferentialNetworkServiceEnabled);
+ updateNetworkPreferenceForUser(userId, preferentialNetworkServiceConfig);
startOwnerService(userId, "start-user");
}
@@ -3334,7 +3338,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
void handleStopUser(int userId) {
- updateNetworkPreferenceForUser(userId, false);
+ updateNetworkPreferenceForUser(userId, PreferentialNetworkServiceConfig.DEFAULT);
stopOwnerService(userId, "stop-user");
}
@@ -10176,7 +10180,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
@Override
- public boolean setPermittedAccessibilityServices(ComponentName who, List packageList) {
+ public boolean setPermittedAccessibilityServices(ComponentName who, List<String> packageList) {
if (!mHasFeature) {
return false;
}
@@ -10228,7 +10232,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
@Override
- public List getPermittedAccessibilityServices(ComponentName who) {
+ public List<String> getPermittedAccessibilityServices(ComponentName who) {
if (!mHasFeature) {
return null;
}
@@ -10243,7 +10247,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
@Override
- public List getPermittedAccessibilityServicesForUser(int userId) {
+ public List<String> getPermittedAccessibilityServicesForUser(int userId) {
if (!mHasFeature) {
return null;
}
@@ -10329,7 +10333,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
@Override
- public boolean setPermittedInputMethods(ComponentName who, List packageList,
+ public boolean setPermittedInputMethods(ComponentName who, List<String> packageList,
boolean calledOnParentInstance) {
if (!mHasFeature) {
return false;
@@ -10393,7 +10397,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
@Override
- public List getPermittedInputMethods(ComponentName who, boolean calledOnParentInstance) {
+ public List<String> getPermittedInputMethods(ComponentName who,
+ boolean calledOnParentInstance) {
if (!mHasFeature) {
return null;
}
@@ -10414,7 +10419,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
@Override
- public List getPermittedInputMethodsForCurrentUser() {
+ public List<String> getPermittedInputMethodsForCurrentUser() {
final CallerIdentity caller = getCallerIdentity();
Preconditions.checkCallAuthorization(canManageUsers(caller));
@@ -12004,7 +12009,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final CallerIdentity caller = getCallerIdentity();
Preconditions.checkCallAuthorization(isProfileOwner(caller),
"Caller is not profile owner;"
- + " only profile owner may control the preferntial network service");
+ + " only profile owner may control the preferential network service");
synchronized (getLockObject()) {
final ActiveAdmin requiredAdmin = getProfileOwnerAdminLocked(
caller.getUserId());
@@ -12041,6 +12046,47 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
@Override
+ public void setPreferentialNetworkServiceConfig(
+ PreferentialNetworkServiceConfig preferentialNetworkServiceConfig) {
+ if (!mHasFeature) {
+ return;
+ }
+ final CallerIdentity caller = getCallerIdentity();
+ Preconditions.checkCallAuthorization(isProfileOwner(caller),
+ "Caller is not profile owner;"
+ + " only profile owner may control the preferential network service");
+ synchronized (getLockObject()) {
+ final ActiveAdmin requiredAdmin = getProfileOwnerAdminLocked(
+ caller.getUserId());
+ if (!requiredAdmin.mPreferentialNetworkServiceConfig.equals(
+ preferentialNetworkServiceConfig)) {
+ requiredAdmin.mPreferentialNetworkServiceConfig = preferentialNetworkServiceConfig;
+ saveSettingsLocked(caller.getUserId());
+ }
+ }
+ updateNetworkPreferenceForUser(caller.getUserId(), preferentialNetworkServiceConfig);
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.SET_PREFERENTIAL_NETWORK_SERVICE_ENABLED)
+ .setBoolean(preferentialNetworkServiceConfig.isEnabled())
+ .write();
+ }
+
+ @Override
+ public PreferentialNetworkServiceConfig getPreferentialNetworkServiceConfig() {
+ if (!mHasFeature) {
+ return PreferentialNetworkServiceConfig.DEFAULT;
+ }
+
+ final CallerIdentity caller = getCallerIdentity();
+ Preconditions.checkCallAuthorization(isProfileOwner(caller),
+ "Caller is not profile owner");
+ synchronized (getLockObject()) {
+ final ActiveAdmin requiredAdmin = getProfileOwnerAdminLocked(caller.getUserId());
+ return requiredAdmin.mPreferentialNetworkServiceConfig;
+ }
+ }
+
+ @Override
public void setLockTaskPackages(ComponentName who, String[] packages)
throws SecurityException {
Objects.requireNonNull(who, "ComponentName is null");
@@ -17787,12 +17833,53 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (!isManagedProfile(userId)) {
return;
}
- int networkPreference = preferentialNetworkServiceEnabled
- ? PROFILE_NETWORK_PREFERENCE_ENTERPRISE : PROFILE_NETWORK_PREFERENCE_DEFAULT;
+ ProfileNetworkPreference.Builder preferenceBuilder =
+ new ProfileNetworkPreference.Builder();
+ if (preferentialNetworkServiceEnabled) {
+ preferenceBuilder.setPreference(PROFILE_NETWORK_PREFERENCE_ENTERPRISE);
+ preferenceBuilder.setPreferenceEnterpriseId(NET_ENTERPRISE_ID_1);
+ } else {
+ preferenceBuilder.setPreference(PROFILE_NETWORK_PREFERENCE_DEFAULT);
+ }
+ List<ProfileNetworkPreference> preferences = new ArrayList<>();
+ preferences.add(preferenceBuilder.build());
+ mInjector.binderWithCleanCallingIdentity(() ->
+ mInjector.getConnectivityManager().setProfileNetworkPreferences(
+ UserHandle.of(userId), preferences,
+ null /* executor */, null /* listener */));
+ }
+
+ private void updateNetworkPreferenceForUser(int userId,
+ PreferentialNetworkServiceConfig preferentialNetworkServiceConfig) {
+ if (!isManagedProfile(userId)) {
+ return;
+ }
+ ProfileNetworkPreference.Builder preferenceBuilder =
+ new ProfileNetworkPreference.Builder();
+ if (preferentialNetworkServiceConfig.isEnabled()) {
+ if (preferentialNetworkServiceConfig.isFallbackToDefaultConnectionAllowed()) {
+ preferenceBuilder.setPreference(PROFILE_NETWORK_PREFERENCE_ENTERPRISE);
+ } else {
+ preferenceBuilder.setPreference(PROFILE_NETWORK_PREFERENCE_ENTERPRISE_NO_FALLBACK);
+ }
+ } else {
+ preferenceBuilder.setPreference(PROFILE_NETWORK_PREFERENCE_DEFAULT);
+ }
+ List<Integer> allowedUids = Arrays.stream(
+ preferentialNetworkServiceConfig.getIncludedUids()).boxed().collect(
+ Collectors.toList());
+ List<Integer> excludedUids = Arrays.stream(
+ preferentialNetworkServiceConfig.getExcludedUids()).boxed().collect(
+ Collectors.toList());
+ preferenceBuilder.setIncludedUids(allowedUids);
+ preferenceBuilder.setExcludedUids(excludedUids);
+ preferenceBuilder.setPreferenceEnterpriseId(
+ preferentialNetworkServiceConfig.getNetworkId());
+ List<ProfileNetworkPreference> preferences = new ArrayList<>();
+ preferences.add(preferenceBuilder.build());
mInjector.binderWithCleanCallingIdentity(() ->
- mInjector.getConnectivityManager().setProfileNetworkPreference(
- UserHandle.of(userId),
- networkPreference,
+ mInjector.getConnectivityManager().setProfileNetworkPreferences(
+ UserHandle.of(userId), preferences,
null /* executor */, null /* listener */));
}
diff --git a/services/incremental/Android.bp b/services/incremental/Android.bp
index 0bd737bec460..957d7c352bdf 100644
--- a/services/incremental/Android.bp
+++ b/services/incremental/Android.bp
@@ -65,7 +65,6 @@ cc_defaults {
"libincremental_manager_aidl-cpp",
"libprotobuf-cpp-lite",
"service.incremental.proto",
- "libutils",
"libvold_binder",
"libc++fs",
"libziparchive_for_incfs",
@@ -78,6 +77,7 @@ cc_defaults {
"libincfs",
"liblog",
"libpermission",
+ "libutils",
"libz",
],
}
diff --git a/services/java/com/android/server/SystemConfigService.java b/services/java/com/android/server/SystemConfigService.java
index a2768c637d79..cb52e5f72d5f 100644
--- a/services/java/com/android/server/SystemConfigService.java
+++ b/services/java/com/android/server/SystemConfigService.java
@@ -19,8 +19,10 @@ import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toMap;
import android.Manifest;
+import android.content.ComponentName;
import android.content.Context;
import android.os.ISystemConfig;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.SparseArray;
@@ -84,6 +86,21 @@ public class SystemConfigService extends SystemService {
}
return ArrayUtils.convertToIntArray(uids);
}
+
+ @Override
+ public List<ComponentName> getEnabledComponentOverrides(String packageName) {
+ ArrayMap<String, Boolean> systemComponents = SystemConfig.getInstance()
+ .getComponentsEnabledStates(packageName);
+ List<ComponentName> enabledComponent = new ArrayList<>();
+ if (systemComponents != null) {
+ for (Map.Entry<String, Boolean> entry : systemComponents.entrySet()) {
+ if (Boolean.TRUE.equals(entry.getValue())) {
+ enabledComponent.add(new ComponentName(packageName, entry.getKey()));
+ }
+ }
+ }
+ return enabledComponent;
+ }
};
public SystemConfigService(Context context) {
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index f5f1d497878a..76c6d252492d 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -54,6 +54,7 @@ import android.hardware.display.DisplayManagerInternal;
import android.net.ConnectivityManager;
import android.net.ConnectivityModuleConnector;
import android.net.NetworkStackClient;
+import android.net.TrafficStats;
import android.os.BaseBundle;
import android.os.Binder;
import android.os.Build;
@@ -103,6 +104,7 @@ import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.widget.ILockSettings;
import com.android.server.am.ActivityManagerService;
import com.android.server.appbinding.AppBindingService;
+import com.android.server.art.ArtManagerLocal;
import com.android.server.attention.AttentionManagerService;
import com.android.server.audio.AudioService;
import com.android.server.biometrics.AuthService;
@@ -263,6 +265,8 @@ public final class SystemServer implements Dumpable {
"com.android.server.stats.StatsCompanion$Lifecycle";
private static final String STATS_PULL_ATOM_SERVICE_CLASS =
"com.android.server.stats.pull.StatsPullAtomService";
+ private static final String STATS_BOOTSTRAP_ATOM_SERVICE_LIFECYCLE_CLASS =
+ "com.android.server.stats.bootstrap.StatsBootstrapAtomService$Lifecycle";
private static final String USB_SERVICE_CLASS =
"com.android.server.usb.UsbService$Lifecycle";
private static final String MIDI_SERVICE_CLASS =
@@ -363,6 +367,8 @@ public final class SystemServer implements Dumpable {
"com.android.server.blob.BlobStoreManagerService";
private static final String APP_SEARCH_MANAGER_SERVICE_CLASS =
"com.android.server.appsearch.AppSearchManagerService";
+ private static final String ISOLATED_COMPILATION_SERVICE_CLASS =
+ "com.android.server.compos.IsolatedCompilationService";
private static final String ROLLBACK_MANAGER_SERVICE_CLASS =
"com.android.server.rollback.RollbackManagerService";
private static final String ALARM_MANAGER_SERVICE_CLASS =
@@ -1330,12 +1336,10 @@ public final class SystemServer implements Dumpable {
DynamicSystemService dynamicSystem = null;
IStorageManager storageManager = null;
NetworkManagementService networkManagement = null;
- IpSecService ipSecService = null;
VpnManagerService vpnManager = null;
VcnManagementService vcnManagement = null;
NetworkStatsService networkStats = null;
NetworkPolicyManagerService networkPolicy = null;
- NsdService serviceDiscovery = null;
WindowManagerService wm = null;
SerialService serial = null;
NetworkTimeUpdateService networkTimeUpdater = null;
@@ -1806,16 +1810,6 @@ public final class SystemServer implements Dumpable {
}
t.traceEnd();
-
- t.traceBegin("StartIpSecService");
- try {
- ipSecService = IpSecService.create(context);
- ServiceManager.addService(Context.IPSEC_SERVICE, ipSecService);
- } catch (Throwable e) {
- reportWtf("starting IpSec Service", e);
- }
- t.traceEnd();
-
t.traceBegin("StartFontManagerService");
mSystemServiceManager.startService(new FontManagerService.Lifecycle(context, safeMode));
t.traceEnd();
@@ -1837,8 +1831,9 @@ public final class SystemServer implements Dumpable {
t.traceBegin("StartNetworkStatsService");
try {
- networkStats = NetworkStatsService.create(context, networkManagement);
+ networkStats = NetworkStatsService.create(context);
ServiceManager.addService(Context.NETWORK_STATS_SERVICE, networkStats);
+ TrafficStats.init(context);
} catch (Throwable e) {
reportWtf("starting NetworkStats Service", e);
}
@@ -1941,16 +1936,6 @@ public final class SystemServer implements Dumpable {
}
t.traceEnd();
- t.traceBegin("StartNsdService");
- try {
- serviceDiscovery = NsdService.create(context);
- ServiceManager.addService(
- Context.NSD_SERVICE, serviceDiscovery);
- } catch (Throwable e) {
- reportWtf("starting Service Discovery Service", e);
- }
- t.traceEnd();
-
t.traceBegin("StartSystemUpdateManagerService");
try {
ServiceManager.addService(Context.SYSTEM_UPDATE_SERVICE,
@@ -2487,6 +2472,11 @@ public final class SystemServer implements Dumpable {
mSystemServiceManager.startService(STATS_PULL_ATOM_SERVICE_CLASS);
t.traceEnd();
+ // Log atoms to statsd from bootstrap processes.
+ t.traceBegin("StatsBootstrapAtomService");
+ mSystemServiceManager.startService(STATS_BOOTSTRAP_ATOM_SERVICE_LIFECYCLE_CLASS);
+ t.traceEnd();
+
// Incidentd and dumpstated helper
t.traceBegin("StartIncidentCompanionService");
mSystemServiceManager.startService(IncidentCompanionService.class);
@@ -2633,6 +2623,10 @@ public final class SystemServer implements Dumpable {
mSystemServiceManager.startService(GAME_MANAGER_SERVICE_CLASS);
t.traceEnd();
+ t.traceBegin("ArtManagerLocal");
+ LocalManagerRegistry.addManager(ArtManagerLocal.class, new ArtManagerLocal());
+ t.traceEnd();
+
if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_UWB)) {
t.traceBegin("UwbService");
mSystemServiceManager.startService(UWB_SERVICE_CLASS);
@@ -2647,6 +2641,12 @@ public final class SystemServer implements Dumpable {
mSystemServiceManager.startService(APP_SEARCH_MANAGER_SERVICE_CLASS);
t.traceEnd();
+ if (SystemProperties.getBoolean("ro.config.isolated_compilation_enabled", false)) {
+ t.traceBegin("IsolatedCompilationService");
+ mSystemServiceManager.startService(ISOLATED_COMPILATION_SERVICE_CLASS);
+ t.traceEnd();
+ }
+
t.traceBegin("StartMediaCommunicationService");
mSystemServiceManager.startService(MEDIA_COMMUNICATION_SERVICE_CLASS);
t.traceEnd();
@@ -2665,7 +2665,6 @@ public final class SystemServer implements Dumpable {
final TelephonyRegistry telephonyRegistryF = telephonyRegistry;
final MediaRouterService mediaRouterF = mediaRouter;
final MmsServiceBroker mmsServiceF = mmsService;
- final IpSecService ipSecServiceF = ipSecService;
final VpnManagerService vpnManagerF = vpnManager;
final VcnManagementService vcnManagementF = vcnManagement;
final WindowManagerService windowManagerF = wm;
@@ -2755,15 +2754,6 @@ public final class SystemServer implements Dumpable {
.networkScoreAndNetworkManagementServiceReady();
}
t.traceEnd();
- t.traceBegin("MakeIpSecServiceReady");
- try {
- if (ipSecServiceF != null) {
- ipSecServiceF.systemReady();
- }
- } catch (Throwable e) {
- reportWtf("making IpSec Service ready", e);
- }
- t.traceEnd();
t.traceBegin("MakeNetworkStatsServiceReady");
try {
if (networkStatsF != null) {
diff --git a/services/midi/OWNERS b/services/midi/OWNERS
new file mode 100644
index 000000000000..f4d51f91b51b
--- /dev/null
+++ b/services/midi/OWNERS
@@ -0,0 +1 @@
+philburk@google.com
diff --git a/services/net/Android.bp b/services/net/Android.bp
index 53ce6b264651..0c3f1dd3589d 100644
--- a/services/net/Android.bp
+++ b/services/net/Android.bp
@@ -23,7 +23,6 @@ java_library_static {
],
static_libs: [
"netd-client",
- "netlink-client",
"networkstack-client",
"net-utils-services-common",
],
@@ -42,6 +41,7 @@ java_library {
sdk_version: "module_current",
min_sdk_version: "30",
libs: [
+ "framework-annotations-lib",
"unsupportedappusage",
"framework-wifi-util-lib",
"framework-connectivity",
diff --git a/services/net/OWNERS b/services/net/OWNERS
index d3836d4c6c57..62c5737a2e8e 100644
--- a/services/net/OWNERS
+++ b/services/net/OWNERS
@@ -1,8 +1,2 @@
set noparent
-
-codewiz@google.com
-jchalard@google.com
-junyulai@google.com
-lorenzo@google.com
-reminv@google.com
-satk@google.com
+file:platform/packages/modules/Connectivity:master:/OWNERS_core_networking
diff --git a/services/net/java/android/net/ConnectivityModuleConnector.java b/services/net/java/android/net/ConnectivityModuleConnector.java
index 62f2c35f339b..c6b15c17bd3c 100644
--- a/services/net/java/android/net/ConnectivityModuleConnector.java
+++ b/services/net/java/android/net/ConnectivityModuleConnector.java
@@ -278,7 +278,10 @@ public class ConnectivityModuleConnector {
// This code path is only run by the system server: only the system server binds
// to the NetworkStack as a service. Other processes get the NetworkStack from
// the ServiceManager.
- maybeCrashWithTerribleFailure("Lost network stack", mPackageName);
+ maybeCrashWithTerribleFailure(
+ "Lost network stack. This is not the root cause of any issue, it is a side "
+ + "effect of a crash that happened earlier. Earlier logs should point to the "
+ + "actual issue.", mPackageName);
}
}
diff --git a/services/people/java/com/android/server/people/data/CallLogQueryHelper.java b/services/people/java/com/android/server/people/data/CallLogQueryHelper.java
index 45e0aac24ca7..ff901af3defa 100644
--- a/services/people/java/com/android/server/people/data/CallLogQueryHelper.java
+++ b/services/people/java/com/android/server/people/data/CallLogQueryHelper.java
@@ -93,6 +93,9 @@ class CallLogQueryHelper {
hasResults = true;
}
}
+ } catch (SecurityException ex) {
+ Slog.e(TAG, "Query call log failed: " + ex);
+ return false;
}
return hasResults;
}
diff --git a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
index fdf23d3836ac..c5f990d52b82 100644
--- a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
+++ b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
@@ -23,7 +23,6 @@ import android.app.job.JobService;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
-import android.content.pm.ResolveInfo;
import android.os.Handler;
import android.os.IBinder.DeathRecipient;
import android.os.Looper;
@@ -32,12 +31,11 @@ import android.os.ServiceManager;
import android.os.SystemProperties;
import android.os.UpdateEngine;
import android.os.UpdateEngineCallback;
-import android.os.UserHandle;
-import android.os.UserManager;
import android.provider.DeviceConfig;
import android.util.Log;
import com.android.internal.R;
+import com.android.internal.os.BackgroundThread;
import com.android.server.IoThread;
import com.android.server.LocalServices;
import com.android.server.SystemService;
@@ -45,9 +43,6 @@ import com.android.server.wm.ActivityMetricsLaunchObserver;
import com.android.server.wm.ActivityMetricsLaunchObserverRegistry;
import com.android.server.wm.ActivityTaskManagerInternal;
-import java.nio.file.Files;
-import java.nio.file.Paths;
-import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
@@ -65,6 +60,12 @@ public final class ProfcollectForwardingService extends SystemService {
private static ProfcollectForwardingService sSelfService;
private final Handler mHandler = new ProfcollectdHandler(IoThread.getHandler().getLooper());
+ private IProviderStatusCallback mProviderStatusCallback = new IProviderStatusCallback.Stub() {
+ public void onProviderReady() {
+ mHandler.sendEmptyMessage(ProfcollectdHandler.MESSAGE_REGISTER_SCHEDULERS);
+ }
+ };
+
public ProfcollectForwardingService(Context context) {
super(context);
@@ -96,10 +97,22 @@ public final class ProfcollectForwardingService extends SystemService {
if (mIProfcollect == null) {
return;
}
- if (serviceHasSupportedTraceProvider()) {
- registerObservers();
- }
- ProfcollectBGJobService.schedule(getContext());
+ BackgroundThread.get().getThreadHandler().post(() -> {
+ if (serviceHasSupportedTraceProvider()) {
+ registerProviderStatusCallback();
+ }
+ });
+ }
+ }
+
+ private void registerProviderStatusCallback() {
+ if (mIProfcollect == null) {
+ return;
+ }
+ try {
+ mIProfcollect.registerProviderStatusCallback(mProviderStatusCallback);
+ } catch (RemoteException e) {
+ Log.e(LOG_TAG, e.getMessage());
}
}
@@ -144,6 +157,7 @@ public final class ProfcollectForwardingService extends SystemService {
}
public static final int MESSAGE_BINDER_CONNECT = 0;
+ public static final int MESSAGE_REGISTER_SCHEDULERS = 1;
@Override
public void handleMessage(android.os.Message message) {
@@ -151,8 +165,12 @@ public final class ProfcollectForwardingService extends SystemService {
case MESSAGE_BINDER_CONNECT:
connectNativeService();
break;
+ case MESSAGE_REGISTER_SCHEDULERS:
+ registerObservers();
+ ProfcollectBGJobService.schedule(getContext());
+ break;
default:
- throw new AssertionError("Unknown message: " + message.toString());
+ throw new AssertionError("Unknown message: " + message);
}
}
}
@@ -196,11 +214,14 @@ public final class ProfcollectForwardingService extends SystemService {
Log.d(LOG_TAG, "Starting background process job");
}
- try {
- sSelfService.mIProfcollect.process(false);
- } catch (RemoteException e) {
- Log.e(LOG_TAG, e.getMessage());
- }
+ BackgroundThread.get().getThreadHandler().post(
+ () -> {
+ try {
+ sSelfService.mIProfcollect.process();
+ } catch (RemoteException e) {
+ Log.e(LOG_TAG, e.getMessage());
+ }
+ });
return true;
}
@@ -236,14 +257,16 @@ public final class ProfcollectForwardingService extends SystemService {
"applaunch_trace_freq", 2);
int randomNum = ThreadLocalRandom.current().nextInt(100);
if (randomNum < traceFrequency) {
- try {
- if (DEBUG) {
- Log.d(LOG_TAG, "Tracing on app launch event: " + packageName);
- }
- mIProfcollect.trace_once("applaunch");
- } catch (RemoteException e) {
- Log.e(LOG_TAG, e.getMessage());
+ if (DEBUG) {
+ Log.d(LOG_TAG, "Tracing on app launch event: " + packageName);
}
+ BackgroundThread.get().getThreadHandler().post(() -> {
+ try {
+ mIProfcollect.trace_once("applaunch");
+ } catch (RemoteException e) {
+ Log.e(LOG_TAG, e.getMessage());
+ }
+ });
}
}
@@ -306,79 +329,27 @@ public final class ProfcollectForwardingService extends SystemService {
return;
}
- if (!getUploaderEnabledConfig(getContext())) {
- return;
- }
-
- new Thread(() -> {
+ Context context = getContext();
+ BackgroundThread.get().getThreadHandler().post(() -> {
try {
- Context context = getContext();
- final String uploaderPkg = getUploaderPackageName(context);
- final String uploaderAction = getUploaderActionName(context);
- String reportUuid = mIProfcollect.report();
-
- final int profileId = getBBProfileId();
- String reportDir = "/data/user/" + profileId
- + "/com.google.android.apps.internal.betterbug/cache/";
- String reportPath = reportDir + reportUuid + ".zip";
-
- if (!Files.exists(Paths.get(reportDir))) {
- Log.i(LOG_TAG, "Destination directory does not exist, abort upload.");
- return;
- }
+ // Prepare profile report
+ String reportName = mIProfcollect.report() + ".zip";
- Intent uploadIntent =
- new Intent(uploaderAction)
- .setPackage(uploaderPkg)
- .putExtra("EXTRA_DESTINATION", "PROFCOLLECT")
- .putExtra("EXTRA_PACKAGE_NAME", getContext().getPackageName())
- .putExtra("EXTRA_PROFILE_PATH", reportPath)
- .addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
-
- List<ResolveInfo> receivers =
- context.getPackageManager().queryBroadcastReceivers(uploadIntent, 0);
- if (receivers == null || receivers.isEmpty()) {
- Log.i(LOG_TAG, "No one to receive upload intent, abort upload.");
+ if (!context.getResources().getBoolean(
+ R.bool.config_profcollectReportUploaderEnabled)) {
+ Log.i(LOG_TAG, "Upload is not enabled.");
return;
}
- mIProfcollect.copy_report_to_bb(profileId, reportUuid);
- context.sendBroadcast(uploadIntent);
- mIProfcollect.delete_report(reportUuid);
+
+ // Upload the report
+ Intent intent = new Intent()
+ .setPackage("com.android.shell")
+ .setAction("com.android.shell.action.PROFCOLLECT_UPLOAD")
+ .putExtra("filename", reportName);
+ context.sendBroadcast(intent);
} catch (RemoteException e) {
Log.e(LOG_TAG, e.getMessage());
}
- }).start();
- }
-
- /**
- * Get BetterBug's profile ID. It is the work profile ID, if it exists. Otherwise the system
- * user ID.
- *
- * @return BetterBug's profile ID.
- */
- private int getBBProfileId() {
- UserManager userManager = UserManager.get(getContext());
- int[] profiles = userManager.getProfileIds(UserHandle.USER_SYSTEM, false);
- for (int p : profiles) {
- if (userManager.getUserInfo(p).isManagedProfile()) {
- return p;
- }
- }
- return UserHandle.USER_SYSTEM;
- }
-
- private boolean getUploaderEnabledConfig(Context context) {
- return context.getResources().getBoolean(
- R.bool.config_profcollectReportUploaderEnabled);
- }
-
- private String getUploaderPackageName(Context context) {
- return context.getResources().getString(
- R.string.config_defaultProfcollectReportUploaderApp);
- }
-
- private String getUploaderActionName(Context context) {
- return context.getResources().getString(
- R.string.config_defaultProfcollectReportUploaderAction);
+ });
}
}
diff --git a/services/proguard.flags b/services/proguard.flags
new file mode 100644
index 000000000000..0e081f182d0d
--- /dev/null
+++ b/services/proguard.flags
@@ -0,0 +1,105 @@
+# TODO(b/210510433): Refine and optimize this configuration. Note that this
+# configuration is only used when `SOONG_CONFIG_ANDROID_SYSTEM_OPTIMIZE_JAVA=true`.
+
+# Preserve line number information for debugging stack traces.
+-keepattributes SourceFile,LineNumberTable
+
+# Allows making private and protected methods/fields public as part of
+# optimization. This enables inlining of trivial getter/setter methods.
+-allowaccessmodification
+
+# Process entrypoint
+-keep class com.android.server.SystemServer {
+ public static void main(java.lang.String[]);
+}
+
+# APIs referenced by dependent JAR files and modules
+-keep @interface android.annotation.SystemApi
+-keep @android.annotation.SystemApi class * {
+ public protected *;
+}
+-keepclasseswithmembers class * {
+ @android.annotation.SystemApi *;
+}
+
+# Derivatives of SystemService and other services created via reflection
+-keep,allowoptimization,allowaccessmodification class * extends com.android.server.SystemService {
+ public <methods>;
+}
+-keep,allowoptimization,allowaccessmodification class * extends com.android.server.devicepolicy.BaseIDevicePolicyManager {
+ public <init>(...);
+}
+-keep,allowoptimization,allowaccessmodification class com.android.server.wallpaper.WallpaperManagerService {
+ public <init>(...);
+}
+
+# Binder interfaces
+-keep,allowoptimization,allowaccessmodification class * extends android.os.IInterface
+-keep,allowoptimization,allowaccessmodification class * extends android.os.IHwInterface
+
+# Global entities normally kept through explicit Manifest entries
+# TODO(b/210510433): Revisit and consider generating from frameworks/base/core/res/AndroidManifest.xml,
+# by including that manifest with the library rule that triggers optimization.
+-keep,allowoptimization,allowaccessmodification class * extends android.app.backup.BackupAgent
+-keep,allowoptimization,allowaccessmodification class * extends android.content.BroadcastReceiver
+-keep,allowoptimization,allowaccessmodification class * extends android.content.ContentProvider
+
+# Various classes subclassed in or referenced via JNI in ethernet-service
+-keep public class android.net.** { *; }
+-keep,allowoptimization,allowaccessmodification class com.android.net.module.util.* { *; }
+-keep,allowoptimization,allowaccessmodification public class com.android.server.net.IpConfigStore { *; }
+-keep,allowoptimization,allowaccessmodification public class com.android.server.net.BaseNetworkObserver { *; }
+
+# Referenced via CarServiceHelperService in car-frameworks-service (avoid removing)
+-keep public class com.android.server.utils.Slogf { *; }
+
+# Notification extractors
+# TODO(b/210510433): Revisit and consider generating from frameworks/base/core/res/res/values/config.xml.
+-keep,allowoptimization,allowaccessmodification public class com.android.server.notification.** implements com.android.server.notification.NotificationSignalExtractor
+
+# JNI keep rules
+# TODO(b/210510433): Revisit and fix with @Keep, or consider auto-generating from
+# frameworks/base/services/core/jni/onload.cpp.
+-keep,allowoptimization,allowaccessmodification class com.android.server.broadcastradio.hal1.BroadcastRadioService { *; }
+-keep,allowoptimization,allowaccessmodification class com.android.server.broadcastradio.hal1.Convert { *; }
+-keep,allowoptimization,allowaccessmodification class com.android.server.broadcastradio.hal1.Tuner { *; }
+-keep,allowoptimization,allowaccessmodification class com.android.server.broadcastradio.hal1.TunerCallback { *; }
+-keep,allowoptimization,allowaccessmodification class com.android.server.location.gnss.GnssConfiguration$HalInterfaceVersion { *; }
+-keep,allowoptimization,allowaccessmodification class com.android.server.location.gnss.GnssPowerStats { *; }
+-keep,allowoptimization,allowaccessmodification class com.android.server.location.gnss.hal.GnssNative { *; }
+-keep,allowoptimization,allowaccessmodification class com.android.server.sensors.SensorManagerInternal$ProximityActiveListener { *; }
+-keep,allowoptimization,allowaccessmodification class com.android.server.sensors.SensorService { *; }
+-keep,allowoptimization,allowaccessmodification class com.android.server.soundtrigger_middleware.SoundTriggerMiddlewareImpl$AudioSessionProvider$AudioSession { *; }
+-keep,allowoptimization,allowaccessmodification class com.android.server.soundtrigger_middleware.ExternalCaptureStateTracker { *; }
+-keep,allowoptimization,allowaccessmodification class com.android.server.storage.AppFuseBridge { *; }
+-keep,allowoptimization,allowaccessmodification class com.android.server.tv.TvInputHal { *; }
+-keep,allowoptimization,allowaccessmodification class com.android.server.usb.UsbAlsaJackDetector { *; }
+-keep,allowoptimization,allowaccessmodification class com.android.server.usb.UsbMidiDevice { *; }
+-keep,allowoptimization,allowaccessmodification class com.android.server.vibrator.VibratorController$OnVibrationCompleteListener { *; }
+-keep,allowoptimization,allowaccessmodification class com.android.server.vibrator.VibratorManagerService$OnSyncedVibrationCompleteListener { *; }
+-keepclasseswithmembers,allowoptimization,allowaccessmodification class com.android.server.** {
+ *** *FromNative(...);
+}
+-keep,allowoptimization,allowaccessmodification class com.android.server.input.InputManagerService {
+ <methods>;
+}
+-keep,allowoptimization,allowaccessmodification class com.android.server.usb.UsbHostManager {
+ *** usbDeviceRemoved(...);
+ *** usbDeviceAdded(...);
+}
+-keep,allowoptimization,allowaccessmodification class **.*NativeWrapper* { *; }
+
+# Miscellaneous reflection keep rules
+# TODO(b/210510433): Revisit and fix with @Keep.
+-keep,allowoptimization,allowaccessmodification class com.android.server.usage.AppStandbyController {
+ public <init>(...);
+}
+-keep,allowoptimization,allowaccessmodification class android.hardware.usb.gadget.** { *; }
+
+# Needed when optimizations enabled
+# TODO(b/210510433): Revisit and fix with @Keep.
+-keep,allowoptimization,allowaccessmodification class com.android.server.SystemService { *; }
+-keep,allowoptimization,allowaccessmodification class com.android.server.SystemService$TargetUser { *; }
+-keep,allowoptimization,allowaccessmodification class com.android.server.usage.StorageStatsManagerLocal { *; }
+-keep,allowoptimization,allowaccessmodification class com.android.internal.util.** { *; }
+-keep,allowoptimization,allowaccessmodification class android.os.** { *; }
diff --git a/services/tests/PackageManagerServiceTests/OWNERS b/services/tests/PackageManagerServiceTests/OWNERS
index 182dfe8fca9e..86ae5818e91c 100644
--- a/services/tests/PackageManagerServiceTests/OWNERS
+++ b/services/tests/PackageManagerServiceTests/OWNERS
@@ -1,3 +1 @@
-chiuwinson@google.com
-patb@google.com
-toddke@google.com
+include /PACKAGE_MANAGER_OWNERS
diff --git a/services/tests/mockingservicestests/OWNERS b/services/tests/mockingservicestests/OWNERS
new file mode 100644
index 000000000000..0fb0c3021486
--- /dev/null
+++ b/services/tests/mockingservicestests/OWNERS
@@ -0,0 +1 @@
+include platform/frameworks/base:/services/core/java/com/android/server/am/OWNERS
diff --git a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
index e472b062388e..d71030802c2b 100644
--- a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
@@ -1047,7 +1047,8 @@ public class DeviceIdleControllerTest {
verifyLightStateConditions(LIGHT_STATE_IDLE);
inOrder.verify(mDeviceIdleController).scheduleLightAlarmLocked(
longThat(l -> l == mConstants.LIGHT_IDLE_TIMEOUT),
- longThat(l -> l == mConstants.LIGHT_IDLE_TIMEOUT_INITIAL_FLEX));
+ longThat(l -> l == mConstants.LIGHT_IDLE_TIMEOUT_INITIAL_FLEX),
+ eq(false));
// Should just alternate between IDLE and IDLE_MAINTENANCE now.
@@ -1055,19 +1056,22 @@ public class DeviceIdleControllerTest {
verifyLightStateConditions(LIGHT_STATE_IDLE_MAINTENANCE);
inOrder.verify(mDeviceIdleController).scheduleLightAlarmLocked(
longThat(l -> l >= mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET),
- longThat(l -> l == mConstants.FLEX_TIME_SHORT));
+ longThat(l -> l == mConstants.FLEX_TIME_SHORT),
+ eq(true));
mDeviceIdleController.stepLightIdleStateLocked("testing");
verifyLightStateConditions(LIGHT_STATE_IDLE);
inOrder.verify(mDeviceIdleController).scheduleLightAlarmLocked(
longThat(l -> l > mConstants.LIGHT_IDLE_TIMEOUT),
- longThat(l -> l > mConstants.LIGHT_IDLE_TIMEOUT_INITIAL_FLEX));
+ longThat(l -> l > mConstants.LIGHT_IDLE_TIMEOUT_INITIAL_FLEX),
+ eq(false));
mDeviceIdleController.stepLightIdleStateLocked("testing");
verifyLightStateConditions(LIGHT_STATE_IDLE_MAINTENANCE);
inOrder.verify(mDeviceIdleController).scheduleLightAlarmLocked(
longThat(l -> l >= mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET),
- longThat(l -> l == mConstants.FLEX_TIME_SHORT));
+ longThat(l -> l == mConstants.FLEX_TIME_SHORT),
+ eq(true));
// Test that motion doesn't reset the idle timeout.
mDeviceIdleController.handleMotionDetectedLocked(50, "test");
@@ -1076,7 +1080,8 @@ public class DeviceIdleControllerTest {
verifyLightStateConditions(LIGHT_STATE_IDLE);
inOrder.verify(mDeviceIdleController).scheduleLightAlarmLocked(
longThat(l -> l > mConstants.LIGHT_IDLE_TIMEOUT),
- longThat(l -> l > mConstants.LIGHT_IDLE_TIMEOUT_INITIAL_FLEX));
+ longThat(l -> l > mConstants.LIGHT_IDLE_TIMEOUT_INITIAL_FLEX),
+ eq(false));
}
///////////////// EXIT conditions ///////////////////
diff --git a/services/tests/mockingservicestests/src/com/android/server/appsearch/AppSearchConfigTest.java b/services/tests/mockingservicestests/src/com/android/server/appsearch/AppSearchConfigTest.java
deleted file mode 100644
index 9926953f110f..000000000000
--- a/services/tests/mockingservicestests/src/com/android/server/appsearch/AppSearchConfigTest.java
+++ /dev/null
@@ -1,474 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.appsearch;
-
-import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.provider.DeviceConfig;
-
-import com.android.server.testables.TestableDeviceConfig;
-
-import org.junit.Assert;
-import org.junit.Rule;
-import org.junit.Test;
-
-/**
- * Tests for {@link AppSearchConfig}.
- *
- * <p>Build/Install/Run: atest FrameworksMockingServicesTests:AppSearchConfigTest
- */
-public class AppSearchConfigTest {
- @Rule
- public final TestableDeviceConfig.TestableDeviceConfigRule
- mDeviceConfigRule = new TestableDeviceConfig.TestableDeviceConfigRule();
-
- @Test
- public void testDefaultValues_allCachedValue() {
- AppSearchConfig appSearchConfig = AppSearchConfig.create(DIRECT_EXECUTOR);
-
- assertThat(appSearchConfig.getCachedMinTimeIntervalBetweenSamplesMillis()).isEqualTo(
- AppSearchConfig.DEFAULT_MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS);
- assertThat(appSearchConfig.getCachedSamplingIntervalDefault()).isEqualTo(
- AppSearchConfig.DEFAULT_SAMPLING_INTERVAL);
- assertThat(appSearchConfig.getCachedSamplingIntervalForBatchCallStats()).isEqualTo(
- AppSearchConfig.DEFAULT_SAMPLING_INTERVAL);
- assertThat(appSearchConfig.getCachedSamplingIntervalForPutDocumentStats()).isEqualTo(
- AppSearchConfig.DEFAULT_SAMPLING_INTERVAL);
- assertThat(appSearchConfig.getCachedSamplingIntervalForInitializeStats()).isEqualTo(
- AppSearchConfig.DEFAULT_SAMPLING_INTERVAL);
- assertThat(appSearchConfig.getCachedSamplingIntervalForSearchStats()).isEqualTo(
- AppSearchConfig.DEFAULT_SAMPLING_INTERVAL);
- assertThat(appSearchConfig.getCachedSamplingIntervalForGlobalSearchStats()).isEqualTo(
- AppSearchConfig.DEFAULT_SAMPLING_INTERVAL);
- assertThat(appSearchConfig.getCachedSamplingIntervalForOptimizeStats()).isEqualTo(
- AppSearchConfig.DEFAULT_SAMPLING_INTERVAL);
- assertThat(appSearchConfig.getCachedLimitConfigMaxDocumentSizeBytes()).isEqualTo(
- AppSearchConfig.DEFAULT_LIMIT_CONFIG_MAX_DOCUMENT_SIZE_BYTES);
- assertThat(appSearchConfig.getCachedLimitConfigMaxDocumentCount()).isEqualTo(
- AppSearchConfig.DEFAULT_LIMIT_CONFIG_MAX_DOCUMENT_COUNT);
- assertThat(appSearchConfig.getCachedBytesOptimizeThreshold()).isEqualTo(
- AppSearchConfig.DEFAULT_BYTES_OPTIMIZE_THRESHOLD);
- assertThat(appSearchConfig.getCachedTimeOptimizeThresholdMs()).isEqualTo(
- AppSearchConfig.DEFAULT_TIME_OPTIMIZE_THRESHOLD_MILLIS);
- assertThat(appSearchConfig.getCachedDocCountOptimizeThreshold()).isEqualTo(
- AppSearchConfig.DEFAULT_DOC_COUNT_OPTIMIZE_THRESHOLD);
- }
-
- @Test
- public void testCustomizedValue_minTimeIntervalBetweenSamplesMillis() {
- final long minTimeIntervalBetweenSamplesMillis = -1;
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
- AppSearchConfig.KEY_MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS,
- Long.toString(minTimeIntervalBetweenSamplesMillis),
- false);
-
- AppSearchConfig appSearchConfig = AppSearchConfig.create(DIRECT_EXECUTOR);
-
- assertThat(appSearchConfig.getCachedMinTimeIntervalBetweenSamplesMillis()).isEqualTo(
- minTimeIntervalBetweenSamplesMillis);
- }
-
- @Test
- public void testCustomizedValueOverride_minTimeIntervalBetweenSamplesMillis() {
- long minTimeIntervalBetweenSamplesMillis = -1;
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
- AppSearchConfig.KEY_MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS,
- Long.toString(minTimeIntervalBetweenSamplesMillis),
- false);
- AppSearchConfig appSearchConfig = AppSearchConfig.create(DIRECT_EXECUTOR);
-
- minTimeIntervalBetweenSamplesMillis = -2;
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
- AppSearchConfig.KEY_MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS,
- Long.toString(minTimeIntervalBetweenSamplesMillis),
- false);
-
- assertThat(appSearchConfig.getCachedMinTimeIntervalBetweenSamplesMillis()).isEqualTo(
- minTimeIntervalBetweenSamplesMillis);
- }
-
- @Test
- public void testCustomizedValue_allSamplingIntervals() {
- final int samplingIntervalDefault = -1;
- final int samplingIntervalPutDocumentStats = -2;
- final int samplingIntervalBatchCallStats = -3;
- final int samplingIntervalInitializeStats = -4;
- final int samplingIntervalSearchStats = -5;
- final int samplingIntervalGlobalSearchStats = -6;
- final int samplingIntervalOptimizeStats = -7;
-
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
- AppSearchConfig.KEY_SAMPLING_INTERVAL_DEFAULT,
- Integer.toString(samplingIntervalDefault),
- false);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
- AppSearchConfig.KEY_SAMPLING_INTERVAL_FOR_PUT_DOCUMENT_STATS,
- Integer.toString(samplingIntervalPutDocumentStats),
- false);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
- AppSearchConfig.KEY_SAMPLING_INTERVAL_FOR_BATCH_CALL_STATS,
- Integer.toString(samplingIntervalBatchCallStats),
- false);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
- AppSearchConfig.KEY_SAMPLING_INTERVAL_FOR_INITIALIZE_STATS,
- Integer.toString(samplingIntervalInitializeStats),
- false);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
- AppSearchConfig.KEY_SAMPLING_INTERVAL_FOR_SEARCH_STATS,
- Integer.toString(samplingIntervalSearchStats),
- false);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
- AppSearchConfig.KEY_SAMPLING_INTERVAL_FOR_GLOBAL_SEARCH_STATS,
- Integer.toString(samplingIntervalGlobalSearchStats),
- false);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
- AppSearchConfig.KEY_SAMPLING_INTERVAL_FOR_OPTIMIZE_STATS,
- Integer.toString(samplingIntervalOptimizeStats),
- false);
-
- AppSearchConfig appSearchConfig = AppSearchConfig.create(DIRECT_EXECUTOR);
-
- assertThat(appSearchConfig.getCachedSamplingIntervalDefault()).isEqualTo(
- samplingIntervalDefault);
- assertThat(appSearchConfig.getCachedSamplingIntervalForPutDocumentStats()).isEqualTo(
- samplingIntervalPutDocumentStats);
- assertThat(appSearchConfig.getCachedSamplingIntervalForBatchCallStats()).isEqualTo(
- samplingIntervalBatchCallStats);
- assertThat(appSearchConfig.getCachedSamplingIntervalForInitializeStats()).isEqualTo(
- samplingIntervalInitializeStats);
- assertThat(appSearchConfig.getCachedSamplingIntervalForSearchStats()).isEqualTo(
- samplingIntervalSearchStats);
- assertThat(appSearchConfig.getCachedSamplingIntervalForGlobalSearchStats()).isEqualTo(
- samplingIntervalGlobalSearchStats);
- assertThat(appSearchConfig.getCachedSamplingIntervalForOptimizeStats()).isEqualTo(
- samplingIntervalOptimizeStats);
- }
-
- @Test
- public void testCustomizedValueOverride_allSamplingIntervals() {
- int samplingIntervalDefault = -1;
- int samplingIntervalPutDocumentStats = -2;
- int samplingIntervalBatchCallStats = -3;
- int samplingIntervalInitializeStats = -4;
- int samplingIntervalSearchStats = -5;
- int samplingIntervalGlobalSearchStats = -6;
- int samplingIntervalOptimizeStats = -7;
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
- AppSearchConfig.KEY_SAMPLING_INTERVAL_DEFAULT,
- Integer.toString(samplingIntervalDefault),
- false);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
- AppSearchConfig.KEY_SAMPLING_INTERVAL_FOR_PUT_DOCUMENT_STATS,
- Integer.toString(samplingIntervalPutDocumentStats),
- false);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
- AppSearchConfig.KEY_SAMPLING_INTERVAL_FOR_BATCH_CALL_STATS,
- Integer.toString(samplingIntervalBatchCallStats),
- false);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
- AppSearchConfig.KEY_SAMPLING_INTERVAL_FOR_INITIALIZE_STATS,
- Integer.toString(samplingIntervalInitializeStats),
- false);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
- AppSearchConfig.KEY_SAMPLING_INTERVAL_FOR_SEARCH_STATS,
- Integer.toString(samplingIntervalSearchStats),
- false);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
- AppSearchConfig.KEY_SAMPLING_INTERVAL_FOR_GLOBAL_SEARCH_STATS,
- Integer.toString(samplingIntervalGlobalSearchStats),
- false);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
- AppSearchConfig.KEY_SAMPLING_INTERVAL_FOR_OPTIMIZE_STATS,
- Integer.toString(samplingIntervalOptimizeStats),
- false);
- AppSearchConfig appSearchConfig = AppSearchConfig.create(DIRECT_EXECUTOR);
-
- // Overrides
- samplingIntervalDefault = -4;
- samplingIntervalPutDocumentStats = -5;
- samplingIntervalBatchCallStats = -6;
- samplingIntervalInitializeStats = -7;
- samplingIntervalSearchStats = -8;
- samplingIntervalGlobalSearchStats = -9;
- samplingIntervalOptimizeStats = -10;
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
- AppSearchConfig.KEY_SAMPLING_INTERVAL_DEFAULT,
- Integer.toString(samplingIntervalDefault),
- false);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
- AppSearchConfig.KEY_SAMPLING_INTERVAL_FOR_PUT_DOCUMENT_STATS,
- Integer.toString(samplingIntervalPutDocumentStats),
- false);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
- AppSearchConfig.KEY_SAMPLING_INTERVAL_FOR_BATCH_CALL_STATS,
- Integer.toString(samplingIntervalBatchCallStats),
- false);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
- AppSearchConfig.KEY_SAMPLING_INTERVAL_FOR_INITIALIZE_STATS,
- Integer.toString(samplingIntervalInitializeStats),
- false);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
- AppSearchConfig.KEY_SAMPLING_INTERVAL_FOR_SEARCH_STATS,
- Integer.toString(samplingIntervalSearchStats),
- false);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
- AppSearchConfig.KEY_SAMPLING_INTERVAL_FOR_GLOBAL_SEARCH_STATS,
- Integer.toString(samplingIntervalGlobalSearchStats),
- false);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
- AppSearchConfig.KEY_SAMPLING_INTERVAL_FOR_OPTIMIZE_STATS,
- Integer.toString(samplingIntervalOptimizeStats),
- false);
-
- assertThat(appSearchConfig.getCachedSamplingIntervalDefault()).isEqualTo(
- samplingIntervalDefault);
- assertThat(appSearchConfig.getCachedSamplingIntervalForPutDocumentStats()).isEqualTo(
- samplingIntervalPutDocumentStats);
- assertThat(appSearchConfig.getCachedSamplingIntervalForBatchCallStats()).isEqualTo(
- samplingIntervalBatchCallStats);
- assertThat(appSearchConfig.getCachedSamplingIntervalForInitializeStats()).isEqualTo(
- samplingIntervalInitializeStats);
- assertThat(appSearchConfig.getCachedSamplingIntervalForSearchStats()).isEqualTo(
- samplingIntervalSearchStats);
- assertThat(appSearchConfig.getCachedSamplingIntervalForGlobalSearchStats()).isEqualTo(
- samplingIntervalGlobalSearchStats);
- assertThat(appSearchConfig.getCachedSamplingIntervalForOptimizeStats()).isEqualTo(
- samplingIntervalOptimizeStats);
- }
-
- /**
- * Tests if we fall back to {@link AppSearchConfig#DEFAULT_SAMPLING_INTERVAL} if both default
- * sampling interval and custom value are not set in DeviceConfig, and there is some other
- * sampling interval set.
- */
- @Test
- public void testFallbackToDefaultSamplingValue_useHardCodedDefault() {
- final int samplingIntervalPutDocumentStats = -1;
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
- AppSearchConfig.KEY_SAMPLING_INTERVAL_FOR_PUT_DOCUMENT_STATS,
- Integer.toString(samplingIntervalPutDocumentStats),
- false);
-
- AppSearchConfig appSearchConfig = AppSearchConfig.create(DIRECT_EXECUTOR);
-
- assertThat(appSearchConfig.getCachedSamplingIntervalForPutDocumentStats()).isEqualTo(
- samplingIntervalPutDocumentStats);
- assertThat(appSearchConfig.getCachedSamplingIntervalForBatchCallStats()).isEqualTo(
- AppSearchConfig.DEFAULT_SAMPLING_INTERVAL);
- }
-
- // Tests if we fall back to configured default sampling interval if custom value is not set in
- // DeviceConfig.
- @Test
- public void testFallbackDefaultSamplingValue_useConfiguredDefault() {
- final int samplingIntervalPutDocumentStats = -1;
- final int samplingIntervalDefault = -2;
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
- AppSearchConfig.KEY_SAMPLING_INTERVAL_FOR_PUT_DOCUMENT_STATS,
- Integer.toString(samplingIntervalPutDocumentStats),
- false);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
- AppSearchConfig.KEY_SAMPLING_INTERVAL_DEFAULT,
- Integer.toString(samplingIntervalDefault),
- false);
-
- AppSearchConfig appSearchConfig = AppSearchConfig.create(DIRECT_EXECUTOR);
-
- assertThat(appSearchConfig.getCachedSamplingIntervalForPutDocumentStats()).isEqualTo(
- samplingIntervalPutDocumentStats);
- assertThat(appSearchConfig.getCachedSamplingIntervalForBatchCallStats()).isEqualTo(
- samplingIntervalDefault);
- }
-
- // Tests that cached values should reflect latest values in DeviceConfig.
- @Test
- public void testFallbackDefaultSamplingValue_defaultValueChanged() {
- int samplingIntervalPutDocumentStats = -1;
- int samplingIntervalDefault = -2;
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
- AppSearchConfig.KEY_SAMPLING_INTERVAL_FOR_PUT_DOCUMENT_STATS,
- Integer.toString(samplingIntervalPutDocumentStats),
- false);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
- AppSearchConfig.KEY_SAMPLING_INTERVAL_DEFAULT,
- Integer.toString(samplingIntervalDefault),
- false);
-
- AppSearchConfig appSearchConfig = AppSearchConfig.create(DIRECT_EXECUTOR);
-
- // Sampling values changed.
- samplingIntervalPutDocumentStats = -3;
- samplingIntervalDefault = -4;
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
- AppSearchConfig.KEY_SAMPLING_INTERVAL_FOR_PUT_DOCUMENT_STATS,
- Integer.toString(samplingIntervalPutDocumentStats),
- false);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
- AppSearchConfig.KEY_SAMPLING_INTERVAL_DEFAULT,
- Integer.toString(samplingIntervalDefault),
- false);
-
- assertThat(appSearchConfig.getCachedSamplingIntervalForPutDocumentStats()).isEqualTo(
- samplingIntervalPutDocumentStats);
- assertThat(appSearchConfig.getCachedSamplingIntervalForBatchCallStats()).isEqualTo(
- samplingIntervalDefault);
- }
-
- // Tests default sampling interval won't affect custom sampling intervals if they are set.
- @Test
- public void testShouldNotFallBack_ifValueConfigured() {
- int samplingIntervalDefault = -1;
- int samplingIntervalBatchCallStats = -2;
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
- AppSearchConfig.KEY_SAMPLING_INTERVAL_DEFAULT,
- Integer.toString(samplingIntervalDefault),
- false);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
- AppSearchConfig.KEY_SAMPLING_INTERVAL_FOR_BATCH_CALL_STATS,
- Integer.toString(samplingIntervalBatchCallStats),
- false);
-
- AppSearchConfig appSearchConfig = AppSearchConfig.create(DIRECT_EXECUTOR);
-
- // Default sampling interval changed.
- samplingIntervalDefault = -3;
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
- AppSearchConfig.KEY_SAMPLING_INTERVAL_DEFAULT,
- Integer.toString(samplingIntervalDefault),
- false);
-
- assertThat(appSearchConfig.getCachedSamplingIntervalForBatchCallStats()).isEqualTo(
- samplingIntervalBatchCallStats);
- }
-
- @Test
- public void testCustomizedValue_maxDocument() {
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
- AppSearchConfig.KEY_LIMIT_CONFIG_MAX_DOCUMENT_SIZE_BYTES,
- Integer.toString(2001),
- /*makeDefault=*/ false);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
- AppSearchConfig.KEY_LIMIT_CONFIG_MAX_DOCUMENT_COUNT,
- Integer.toString(2002),
- /*makeDefault=*/ false);
-
- AppSearchConfig appSearchConfig = AppSearchConfig.create(DIRECT_EXECUTOR);
- assertThat(appSearchConfig.getCachedLimitConfigMaxDocumentSizeBytes()).isEqualTo(2001);
- assertThat(appSearchConfig.getCachedLimitConfigMaxDocumentCount()).isEqualTo(2002);
- }
-
- @Test
- public void testCustomizedValue_optimizeThreshold() {
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
- AppSearchConfig.KEY_BYTES_OPTIMIZE_THRESHOLD,
- Integer.toString(147147),
- false);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
- AppSearchConfig.KEY_TIME_OPTIMIZE_THRESHOLD_MILLIS,
- Integer.toString(258258),
- false);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
- AppSearchConfig.KEY_DOC_COUNT_OPTIMIZE_THRESHOLD,
- Integer.toString(369369),
- false);
-
- AppSearchConfig appSearchConfig = AppSearchConfig.create(DIRECT_EXECUTOR);
-
- assertThat(appSearchConfig.getCachedBytesOptimizeThreshold()).isEqualTo(147147);
- assertThat(appSearchConfig.getCachedTimeOptimizeThresholdMs()).isEqualTo(258258);
- assertThat(appSearchConfig.getCachedDocCountOptimizeThreshold()).isEqualTo(369369);
- }
-
- @Test
- public void testCustomizedValueOverride_optimizeThreshold() {
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
- AppSearchConfig.KEY_BYTES_OPTIMIZE_THRESHOLD,
- Integer.toString(147147),
- false);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
- AppSearchConfig.KEY_TIME_OPTIMIZE_THRESHOLD_MILLIS,
- Integer.toString(258258),
- false);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
- AppSearchConfig.KEY_DOC_COUNT_OPTIMIZE_THRESHOLD,
- Integer.toString(369369),
- false);
-
- AppSearchConfig appSearchConfig = AppSearchConfig.create(DIRECT_EXECUTOR);
-
- // Override
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
- AppSearchConfig.KEY_BYTES_OPTIMIZE_THRESHOLD,
- Integer.toString(741741),
- false);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
- AppSearchConfig.KEY_TIME_OPTIMIZE_THRESHOLD_MILLIS,
- Integer.toString(852852),
- false);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
- AppSearchConfig.KEY_DOC_COUNT_OPTIMIZE_THRESHOLD,
- Integer.toString(963963),
- false);
-
- assertThat(appSearchConfig.getCachedBytesOptimizeThreshold()).isEqualTo(741741);
- assertThat(appSearchConfig.getCachedTimeOptimizeThresholdMs()).isEqualTo(852852);
- assertThat(appSearchConfig.getCachedDocCountOptimizeThreshold()).isEqualTo(963963);
- }
-
- @Test
- public void testNotUsable_afterClose() {
- AppSearchConfig appSearchConfig = AppSearchConfig.create(DIRECT_EXECUTOR);
-
- appSearchConfig.close();
-
- Assert.assertThrows("Trying to use a closed AppSearchConfig instance.",
- IllegalStateException.class,
- () -> appSearchConfig.getCachedMinTimeIntervalBetweenSamplesMillis());
- Assert.assertThrows("Trying to use a closed AppSearchConfig instance.",
- IllegalStateException.class,
- () -> appSearchConfig.getCachedSamplingIntervalDefault());
- Assert.assertThrows("Trying to use a closed AppSearchConfig instance.",
- IllegalStateException.class,
- () -> appSearchConfig.getCachedSamplingIntervalForBatchCallStats());
- Assert.assertThrows("Trying to use a closed AppSearchConfig instance.",
- IllegalStateException.class,
- () -> appSearchConfig.getCachedSamplingIntervalForPutDocumentStats());
- Assert.assertThrows("Trying to use a closed AppSearchConfig instance.",
- IllegalStateException.class,
- () -> appSearchConfig.getCachedSamplingIntervalForInitializeStats());
- Assert.assertThrows("Trying to use a closed AppSearchConfig instance.",
- IllegalStateException.class,
- () -> appSearchConfig.getCachedSamplingIntervalForSearchStats());
- Assert.assertThrows("Trying to use a closed AppSearchConfig instance.",
- IllegalStateException.class,
- () -> appSearchConfig.getCachedSamplingIntervalForGlobalSearchStats());
- Assert.assertThrows("Trying to use a closed AppSearchConfig instance.",
- IllegalStateException.class,
- () -> appSearchConfig.getCachedSamplingIntervalForOptimizeStats());
- Assert.assertThrows("Trying to use a closed AppSearchConfig instance.",
- IllegalStateException.class,
- () -> appSearchConfig.getCachedBytesOptimizeThreshold());
- Assert.assertThrows("Trying to use a closed AppSearchConfig instance.",
- IllegalStateException.class,
- () -> appSearchConfig.getCachedTimeOptimizeThresholdMs());
- Assert.assertThrows("Trying to use a closed AppSearchConfig instance.",
- IllegalStateException.class,
- () -> appSearchConfig.getCachedDocCountOptimizeThreshold());
- }
-}
diff --git a/services/tests/mockingservicestests/src/com/android/server/appsearch/FrameworkLimitConfigTest.java b/services/tests/mockingservicestests/src/com/android/server/appsearch/FrameworkLimitConfigTest.java
deleted file mode 100644
index 088ed277aa80..000000000000
--- a/services/tests/mockingservicestests/src/com/android/server/appsearch/FrameworkLimitConfigTest.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.appsearch;
-
-import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.provider.DeviceConfig;
-
-import com.android.server.testables.TestableDeviceConfig;
-
-import org.junit.Rule;
-import org.junit.Test;
-
-/**
- * Tests for {@link FrameworkLimitConfig}.
- *
- * <p>Build/Install/Run: atest FrameworksMockingServicesTests:AppSearchConfigTest
- */
-public class FrameworkLimitConfigTest {
- @Rule
- public final TestableDeviceConfig.TestableDeviceConfigRule
- mDeviceConfigRule = new TestableDeviceConfig.TestableDeviceConfigRule();
-
- @Test
- public void testDefaultValues() {
- AppSearchConfig appSearchConfig = AppSearchConfig.create(DIRECT_EXECUTOR);
- FrameworkLimitConfig config = new FrameworkLimitConfig(appSearchConfig);
- assertThat(config.getMaxDocumentSizeBytes()).isEqualTo(
- AppSearchConfig.DEFAULT_LIMIT_CONFIG_MAX_DOCUMENT_SIZE_BYTES);
- assertThat(appSearchConfig.getCachedLimitConfigMaxDocumentCount()).isEqualTo(
- AppSearchConfig.DEFAULT_LIMIT_CONFIG_MAX_DOCUMENT_COUNT);
- }
-
- @Test
- public void testCustomizedValues() {
- AppSearchConfig appSearchConfig = AppSearchConfig.create(DIRECT_EXECUTOR);
- FrameworkLimitConfig config = new FrameworkLimitConfig(appSearchConfig);
- DeviceConfig.setProperty(
- DeviceConfig.NAMESPACE_APPSEARCH,
- AppSearchConfig.KEY_LIMIT_CONFIG_MAX_DOCUMENT_SIZE_BYTES,
- "2001",
- /*makeDefault=*/ false);
- DeviceConfig.setProperty(
- DeviceConfig.NAMESPACE_APPSEARCH,
- AppSearchConfig.KEY_LIMIT_CONFIG_MAX_DOCUMENT_COUNT,
- "2002",
- /*makeDefault=*/ false);
-
- assertThat(config.getMaxDocumentSizeBytes()).isEqualTo(2001);
- assertThat(appSearchConfig.getCachedLimitConfigMaxDocumentCount()).isEqualTo(2002);
- }
-}
diff --git a/services/tests/mockingservicestests/src/com/android/server/appsearch/OWNERS b/services/tests/mockingservicestests/src/com/android/server/appsearch/OWNERS
deleted file mode 100644
index 24f6b0b6b2b6..000000000000
--- a/services/tests/mockingservicestests/src/com/android/server/appsearch/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-include /apex/appsearch/OWNERS \ No newline at end of file
diff --git a/services/tests/mockingservicestests/src/com/android/server/appsearch/stats/PlatformLoggerTest.java b/services/tests/mockingservicestests/src/com/android/server/appsearch/stats/PlatformLoggerTest.java
deleted file mode 100644
index 28fcaee79572..000000000000
--- a/services/tests/mockingservicestests/src/com/android/server/appsearch/stats/PlatformLoggerTest.java
+++ /dev/null
@@ -1,228 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.appsearch.stats;
-
-import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.os.SystemClock;
-import android.provider.DeviceConfig;
-
-import androidx.test.core.app.ApplicationProvider;
-
-import com.android.server.appsearch.AppSearchConfig;
-import com.android.server.appsearch.external.localstorage.stats.CallStats;
-import com.android.server.testables.TestableDeviceConfig;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.junit.MockitoJUnitRunner;
-
-/**
- * Tests covering the functionalities in {@link PlatformLogger} requiring overriding some flags
- * in {@link DeviceConfig}.
- *
- * <p>To add tests NOT rely on overriding the configs, please add them in
- * the tests for {@link PlatformLogger} in servicetests.
- */
-@RunWith(MockitoJUnitRunner.class)
-public class PlatformLoggerTest {
- private static final int TEST_MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS = 100;
- private static final int TEST_DEFAULT_SAMPLING_INTERVAL = 10;
- private static final String TEST_PACKAGE_NAME = "packageName";
- private AppSearchConfig mAppSearchConfig;
-
- @Rule
- public final TestableDeviceConfig.TestableDeviceConfigRule
- mDeviceConfigRule = new TestableDeviceConfig.TestableDeviceConfigRule();
-
- @Before
- public void setUp() throws Exception {
- mAppSearchConfig = AppSearchConfig.create(DIRECT_EXECUTOR);
- }
-
- @Test
- public void testCreateExtraStatsLocked_samplingIntervalNotSet_returnsDefault() {
- PlatformLogger logger = new PlatformLogger(
- ApplicationProvider.getApplicationContext(),
- mAppSearchConfig);
-
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
- AppSearchConfig.KEY_MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS,
- Long.toString(TEST_MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS),
- false);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
- AppSearchConfig.KEY_SAMPLING_INTERVAL_DEFAULT,
- Integer.toString(TEST_DEFAULT_SAMPLING_INTERVAL),
- false);
-
- // Make sure default sampling interval is used if there is no config set.
- assertThat(logger.createExtraStatsLocked(TEST_PACKAGE_NAME,
- CallStats.CALL_TYPE_UNKNOWN).mSamplingInterval).isEqualTo(
- TEST_DEFAULT_SAMPLING_INTERVAL);
- assertThat(logger.createExtraStatsLocked(TEST_PACKAGE_NAME,
- CallStats.CALL_TYPE_INITIALIZE).mSamplingInterval).isEqualTo(
- TEST_DEFAULT_SAMPLING_INTERVAL);
- assertThat(logger.createExtraStatsLocked(TEST_PACKAGE_NAME,
- CallStats.CALL_TYPE_SEARCH).mSamplingInterval).isEqualTo(
- TEST_DEFAULT_SAMPLING_INTERVAL);
- assertThat(logger.createExtraStatsLocked(TEST_PACKAGE_NAME,
- CallStats.CALL_TYPE_FLUSH).mSamplingInterval).isEqualTo(
- TEST_DEFAULT_SAMPLING_INTERVAL);
- }
-
-
- @Test
- public void testCreateExtraStatsLocked_samplingIntervalSet_returnsConfigured() {
- int putDocumentSamplingInterval = 1;
- int batchCallSamplingInterval = 2;
- PlatformLogger logger = new PlatformLogger(
- ApplicationProvider.getApplicationContext(), mAppSearchConfig);
-
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
- AppSearchConfig.KEY_MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS,
- Long.toString(TEST_MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS),
- false);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
- AppSearchConfig.KEY_SAMPLING_INTERVAL_DEFAULT,
- Integer.toString(TEST_DEFAULT_SAMPLING_INTERVAL),
- false);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
- AppSearchConfig.KEY_SAMPLING_INTERVAL_FOR_PUT_DOCUMENT_STATS,
- Integer.toString(putDocumentSamplingInterval),
- false);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
- AppSearchConfig.KEY_SAMPLING_INTERVAL_FOR_BATCH_CALL_STATS,
- Integer.toString(batchCallSamplingInterval),
- false);
-
- // The default sampling interval should be used if no sampling interval is
- // provided for certain call type.
- assertThat(logger.createExtraStatsLocked(TEST_PACKAGE_NAME,
- CallStats.CALL_TYPE_INITIALIZE).mSamplingInterval).isEqualTo(
- TEST_DEFAULT_SAMPLING_INTERVAL);
- assertThat(logger.createExtraStatsLocked(TEST_PACKAGE_NAME,
- CallStats.CALL_TYPE_FLUSH).mSamplingInterval).isEqualTo(
- TEST_DEFAULT_SAMPLING_INTERVAL);
-
- // The configured sampling interval is used if sampling interval is available
- // for certain call type.
- assertThat(logger.createExtraStatsLocked(TEST_PACKAGE_NAME,
- CallStats.CALL_TYPE_PUT_DOCUMENT).mSamplingInterval).isEqualTo(
- putDocumentSamplingInterval);
- assertThat(logger.createExtraStatsLocked(TEST_PACKAGE_NAME,
- CallStats.CALL_TYPE_PUT_DOCUMENTS).mSamplingInterval).isEqualTo(
- batchCallSamplingInterval);
- assertThat(logger.createExtraStatsLocked(TEST_PACKAGE_NAME,
- CallStats.CALL_TYPE_REMOVE_DOCUMENTS_BY_SEARCH).mSamplingInterval).isEqualTo(
- batchCallSamplingInterval);
- }
-
- @Test
- public void testShouldLogForTypeLocked_trueWhenSampleIntervalIsOne() {
- final String testPackageName = "packageName";
- PlatformLogger logger = new PlatformLogger(
- ApplicationProvider.getApplicationContext(),
- mAppSearchConfig);
-
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
- AppSearchConfig.KEY_SAMPLING_INTERVAL_DEFAULT,
- Long.toString(1),
- false);
-
- // Sample should always be logged for the first time if sampling is disabled(value is one).
- assertThat(logger.shouldLogForTypeLocked(CallStats.CALL_TYPE_PUT_DOCUMENT)).isTrue();
- assertThat(logger.createExtraStatsLocked(testPackageName,
- CallStats.CALL_TYPE_PUT_DOCUMENT).mSkippedSampleCount).isEqualTo(0);
- }
-
- @Test
- public void testShouldLogForTypeLocked_falseWhenSampleIntervalIsNegative() {
- final String testPackageName = "packageName";
- PlatformLogger logger = new PlatformLogger(
- ApplicationProvider.getApplicationContext(),
- mAppSearchConfig);
-
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
- AppSearchConfig.KEY_SAMPLING_INTERVAL_DEFAULT,
- Long.toString(-1),
- false);
-
- // Makes sure sample will be excluded due to sampling if sample interval is negative.
- assertThat(logger.shouldLogForTypeLocked(CallStats.CALL_TYPE_PUT_DOCUMENT)).isFalse();
- // Skipped count should be 0 since it doesn't pass the sampling.
- assertThat(logger.createExtraStatsLocked(testPackageName,
- CallStats.CALL_TYPE_PUT_DOCUMENT).mSkippedSampleCount).isEqualTo(0);
- }
-
- @Test
- public void testShouldLogForTypeLocked_falseWhenWithinCoolOffInterval() {
- // Next sample won't be excluded due to sampling.
- final int samplingInterval = 1;
- // Next sample would guaranteed to be too close.
- final int minTimeIntervalBetweenSamplesMillis = Integer.MAX_VALUE;
- final String testPackageName = "packageName";
- PlatformLogger logger = new PlatformLogger(
- ApplicationProvider.getApplicationContext(),
- mAppSearchConfig);
-
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
- AppSearchConfig.KEY_SAMPLING_INTERVAL_DEFAULT,
- Long.toString(samplingInterval),
- false);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
- AppSearchConfig.KEY_MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS,
- Long.toString(minTimeIntervalBetweenSamplesMillis),
- false);
- logger.setLastPushTimeMillisLocked(SystemClock.elapsedRealtime());
-
- // Makes sure sample will be excluded due to rate limiting if samples are too close.
- assertThat(logger.shouldLogForTypeLocked(CallStats.CALL_TYPE_PUT_DOCUMENT)).isFalse();
- assertThat(logger.createExtraStatsLocked(testPackageName,
- CallStats.CALL_TYPE_PUT_DOCUMENT).mSkippedSampleCount).isEqualTo(1);
- }
-
- @Test
- public void testShouldLogForTypeLocked_trueWhenOutsideOfCoolOffInterval() {
- // Next sample won't be excluded due to sampling.
- final int samplingInterval = 1;
- // Next sample would guaranteed to be included.
- final int minTimeIntervalBetweenSamplesMillis = 0;
- final String testPackageName = "packageName";
- PlatformLogger logger = new PlatformLogger(
- ApplicationProvider.getApplicationContext(),
- mAppSearchConfig);
-
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
- AppSearchConfig.KEY_SAMPLING_INTERVAL_DEFAULT,
- Long.toString(samplingInterval),
- false);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
- AppSearchConfig.KEY_MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS,
- Long.toString(minTimeIntervalBetweenSamplesMillis),
- false);
- logger.setLastPushTimeMillisLocked(SystemClock.elapsedRealtime());
-
- // Makes sure sample will be logged if it is not too close to previous sample.
- assertThat(logger.shouldLogForTypeLocked(CallStats.CALL_TYPE_PUT_DOCUMENT)).isTrue();
- assertThat(logger.createExtraStatsLocked(testPackageName,
- CallStats.CALL_TYPE_PUT_DOCUMENT).mSkippedSampleCount).isEqualTo(0);
- }
-}
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java
index 521be70df633..d7d9ed3336f7 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java
@@ -18,6 +18,7 @@ package com.android.server.pm;
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
@@ -28,17 +29,23 @@ import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import static org.testng.Assert.assertThrows;
+import android.apex.ApexInfo;
import android.apex.ApexSessionInfo;
+import android.apex.ApexSessionParams;
import android.content.Context;
import android.content.IntentSender;
+import android.content.pm.ApexStagedEvent;
+import android.content.pm.IStagedApexObserver;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageInstaller.SessionInfo;
import android.content.pm.PackageInstaller.SessionInfo.StagedSessionErrorCode;
+import android.content.pm.StagedApexInfo;
+import android.os.Message;
import android.os.SystemProperties;
import android.os.storage.IStorageManager;
import android.platform.test.annotations.Presubmit;
+import android.util.IntArray;
import android.util.SparseArray;
import com.android.dx.mockito.inline.extended.ExtendedMockito;
@@ -53,15 +60,20 @@ import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
+import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.mockito.MockitoSession;
+import org.mockito.invocation.InvocationOnMock;
import org.mockito.quality.Strictness;
+import org.mockito.stubbing.Answer;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.Map;
import java.util.function.Predicate;
@Presubmit
@@ -519,6 +531,281 @@ public class StagingManagerTest {
assertThat(mStagingManager.getSessionIdByPackageName("com.bar")).isEqualTo(-1);
}
+ @Test
+ public void getStagedApexInfos_validatePreConditions() throws Exception {
+ // Invalid session: null session
+ {
+ // Call and verify
+ IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class,
+ () -> mStagingManager.getStagedApexInfos(null));
+ assertThat(thrown).hasMessageThat().contains("Session is null");
+ }
+ // Invalid session: has parent
+ {
+ FakeStagedSession session = new FakeStagedSession(241);
+ session.setParentSessionId(239);
+ session.setSessionReady();
+ // Call and verify
+ IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class,
+ () -> mStagingManager.getStagedApexInfos(session));
+ assertThat(thrown).hasMessageThat().contains("241 session has parent");
+ }
+
+ // Invalid session: does not contain apex
+ {
+ FakeStagedSession session = new FakeStagedSession(241);
+ session.setSessionReady();
+ // Call and verify
+ IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class,
+ () -> mStagingManager.getStagedApexInfos(session));
+ assertThat(thrown).hasMessageThat().contains("241 session does not contain apex");
+ }
+ // Invalid session: not ready
+ {
+ FakeStagedSession session = new FakeStagedSession(239);
+ session.setIsApex(true);
+ // Call and verify
+ Map<String, ApexInfo> result = mStagingManager.getStagedApexInfos(session);
+ assertThat(result).isEmpty();
+ }
+ // Invalid session: destroyed
+ {
+ FakeStagedSession session = new FakeStagedSession(240);
+ session.setSessionReady();
+ session.setIsApex(true);
+ session.setDestroyed(true);
+ // Call and verify
+ Map<String, ApexInfo> result = mStagingManager.getStagedApexInfos(session);
+ assertThat(result).isEmpty();
+ }
+ }
+
+ private ApexInfo[] getFakeApexInfo(List<String> moduleNames) {
+ List<ApexInfo> result = new ArrayList<>();
+ for (String moduleName : moduleNames) {
+ ApexInfo info = new ApexInfo();
+ info.moduleName = moduleName;
+ result.add(info);
+ }
+ return result.toArray(new ApexInfo[0]);
+ }
+
+ @Test
+ public void getStagedApexInfos_nonParentSession() throws Exception {
+ FakeStagedSession validSession = new FakeStagedSession(239);
+ validSession.setIsApex(true);
+ validSession.setSessionReady();
+ ApexInfo[] fakeApexInfos = getFakeApexInfo(Arrays.asList("module1"));
+ when(mApexManager.getStagedApexInfos(any())).thenReturn(fakeApexInfos);
+
+ // Call and verify
+ Map<String, ApexInfo> result = mStagingManager.getStagedApexInfos(validSession);
+ assertThat(result).containsExactly(fakeApexInfos[0].moduleName, fakeApexInfos[0]);
+
+ ArgumentCaptor<ApexSessionParams> argumentCaptor =
+ ArgumentCaptor.forClass(ApexSessionParams.class);
+ verify(mApexManager, times(1)).getStagedApexInfos(argumentCaptor.capture());
+ ApexSessionParams params = argumentCaptor.getValue();
+ assertThat(params.sessionId).isEqualTo(239);
+ }
+
+ @Test
+ public void getStagedApexInfos_parentSession() throws Exception {
+ FakeStagedSession childSession1 = new FakeStagedSession(201);
+ childSession1.setIsApex(true);
+ FakeStagedSession childSession2 = new FakeStagedSession(202);
+ childSession2.setIsApex(true);
+ FakeStagedSession nonApexChild = new FakeStagedSession(203);
+ FakeStagedSession parentSession = new FakeStagedSession(239,
+ Arrays.asList(childSession1, childSession2, nonApexChild));
+ parentSession.setSessionReady();
+ ApexInfo[] fakeApexInfos = getFakeApexInfo(Arrays.asList("module1", "module2"));
+ when(mApexManager.getStagedApexInfos(any())).thenReturn(fakeApexInfos);
+
+ // Call and verify
+ Map<String, ApexInfo> result = mStagingManager.getStagedApexInfos(parentSession);
+ assertThat(result).containsExactly(fakeApexInfos[0].moduleName, fakeApexInfos[0],
+ fakeApexInfos[1].moduleName, fakeApexInfos[1]);
+
+ ArgumentCaptor<ApexSessionParams> argumentCaptor =
+ ArgumentCaptor.forClass(ApexSessionParams.class);
+ verify(mApexManager, times(1)).getStagedApexInfos(argumentCaptor.capture());
+ ApexSessionParams params = argumentCaptor.getValue();
+ assertThat(params.sessionId).isEqualTo(239);
+ assertThat(params.childSessionIds).asList().containsExactly(201, 202);
+ }
+
+ @Test
+ public void getStagedApexModuleNames_returnsStagedApexModules() throws Exception {
+ FakeStagedSession validSession1 = new FakeStagedSession(239);
+ validSession1.setIsApex(true);
+ validSession1.setSessionReady();
+ mStagingManager.createSession(validSession1);
+
+ FakeStagedSession childSession1 = new FakeStagedSession(123);
+ childSession1.setIsApex(true);
+ FakeStagedSession childSession2 = new FakeStagedSession(124);
+ childSession2.setIsApex(true);
+ FakeStagedSession nonApexChild = new FakeStagedSession(125);
+ FakeStagedSession parentSession = new FakeStagedSession(240,
+ Arrays.asList(childSession1, childSession2, nonApexChild));
+ parentSession.setSessionReady();
+ mStagingManager.createSession(parentSession);
+
+ mockApexManagerGetStagedApexInfoWithSessionId();
+
+ List<String> result = mStagingManager.getStagedApexModuleNames();
+ assertThat(result).containsExactly("239", "123", "124");
+ verify(mApexManager, times(2)).getStagedApexInfos(any());
+ }
+
+ // Make mApexManager return ApexInfo with same module name as the sessionId
+ // of the parameter that was passed into it
+ private void mockApexManagerGetStagedApexInfoWithSessionId() {
+ when(mApexManager.getStagedApexInfos(any())).thenAnswer(new Answer<ApexInfo[]>() {
+ @Override
+ public ApexInfo[] answer(InvocationOnMock invocation) throws Throwable {
+ Object[] args = invocation.getArguments();
+ ApexSessionParams params = (ApexSessionParams) args[0];
+ IntArray sessionsToProcess = new IntArray();
+ if (params.childSessionIds.length == 0) {
+ sessionsToProcess.add(params.sessionId);
+ } else {
+ sessionsToProcess.addAll(params.childSessionIds);
+ }
+ List<ApexInfo> result = new ArrayList<>();
+ for (int session : sessionsToProcess.toArray()) {
+ ApexInfo info = new ApexInfo();
+ info.moduleName = String.valueOf(session);
+ result.add(info);
+ }
+ return result.toArray(new ApexInfo[0]);
+ }
+ });
+ }
+
+ @Test
+ public void getStagedApexInfo() throws Exception {
+ FakeStagedSession validSession1 = new FakeStagedSession(239);
+ validSession1.setIsApex(true);
+ validSession1.setSessionReady();
+ mStagingManager.createSession(validSession1);
+ ApexInfo[] fakeApexInfos = getFakeApexInfo(Arrays.asList("module1"));
+ when(mApexManager.getStagedApexInfos(any())).thenReturn(fakeApexInfos);
+
+ // Verify null is returned if module name is not found
+ StagedApexInfo result = mStagingManager.getStagedApexInfo("not found");
+ assertThat(result).isNull();
+ verify(mApexManager, times(1)).getStagedApexInfos(any());
+ // Otherwise, the correct object is returned
+ result = mStagingManager.getStagedApexInfo("module1");
+ assertThat(result.moduleName).isEqualTo(fakeApexInfos[0].moduleName);
+ assertThat(result.diskImagePath).isEqualTo(fakeApexInfos[0].modulePath);
+ assertThat(result.versionCode).isEqualTo(fakeApexInfos[0].versionCode);
+ assertThat(result.versionName).isEqualTo(fakeApexInfos[0].versionName);
+ verify(mApexManager, times(2)).getStagedApexInfos(any());
+ }
+
+ @Test
+ public void registeredStagedApexObserverIsNotifiedOnPreRebootVerificationCompletion()
+ throws Exception {
+ // Register observer
+ IStagedApexObserver observer = Mockito.mock(IStagedApexObserver.class);
+ mStagingManager.registerStagedApexObserver(observer);
+
+ // Create one staged session and trigger end of pre-reboot verification
+ {
+ FakeStagedSession session = new FakeStagedSession(239);
+ session.setIsApex(true);
+ mStagingManager.createSession(session);
+
+ mockApexManagerGetStagedApexInfoWithSessionId();
+ triggerEndOfPreRebootVerification(session);
+
+ assertThat(session.isSessionReady()).isTrue();
+ ArgumentCaptor<ApexStagedEvent> argumentCaptor = ArgumentCaptor.forClass(
+ ApexStagedEvent.class);
+ verify(observer, times(1)).onApexStaged(argumentCaptor.capture());
+ assertThat(argumentCaptor.getValue().stagedApexModuleNames).isEqualTo(
+ new String[]{"239"});
+ }
+
+ // Create another staged session and verify observers are notified of union
+ {
+ Mockito.clearInvocations(observer);
+ FakeStagedSession session = new FakeStagedSession(240);
+ session.setIsApex(true);
+ mStagingManager.createSession(session);
+
+ triggerEndOfPreRebootVerification(session);
+
+ assertThat(session.isSessionReady()).isTrue();
+ ArgumentCaptor<ApexStagedEvent> argumentCaptor = ArgumentCaptor.forClass(
+ ApexStagedEvent.class);
+ verify(observer, times(1)).onApexStaged(argumentCaptor.capture());
+ assertThat(argumentCaptor.getValue().stagedApexModuleNames).isEqualTo(
+ new String[]{"239", "240"});
+ }
+
+ // Finally, verify that once unregistered, observer is not notified
+ mStagingManager.unregisterStagedApexObserver(observer);
+ {
+ Mockito.clearInvocations(observer);
+ FakeStagedSession session = new FakeStagedSession(241);
+ session.setIsApex(true);
+ mStagingManager.createSession(session);
+
+ triggerEndOfPreRebootVerification(session);
+
+ assertThat(session.isSessionReady()).isTrue();
+ verify(observer, never()).onApexStaged(any());
+ }
+ }
+
+ @Test
+ public void registeredStagedApexObserverIsNotifiedOnSessionAbandon() throws Exception {
+ // Register observer
+ IStagedApexObserver observer = Mockito.mock(IStagedApexObserver.class);
+ mStagingManager.registerStagedApexObserver(observer);
+
+ // Create a ready session and abandon it
+ FakeStagedSession session = new FakeStagedSession(239);
+ session.setIsApex(true);
+ session.setSessionReady();
+ session.setDestroyed(true);
+ mStagingManager.createSession(session);
+
+ mStagingManager.abortCommittedSession(session);
+
+ assertThat(session.isSessionReady()).isTrue();
+ ArgumentCaptor<ApexStagedEvent> argumentCaptor = ArgumentCaptor.forClass(
+ ApexStagedEvent.class);
+ verify(observer, times(1)).onApexStaged(argumentCaptor.capture());
+ assertThat(argumentCaptor.getValue().stagedApexModuleNames).hasLength(0);
+ }
+
+ @Test
+ public void stagedApexObserverIsOnlyCalledForApexSessions() throws Exception {
+ IStagedApexObserver observer = Mockito.mock(IStagedApexObserver.class);
+ mStagingManager.registerStagedApexObserver(observer);
+
+ // Trigger end of pre-reboot verification
+ FakeStagedSession session = new FakeStagedSession(239);
+ mStagingManager.createSession(session);
+
+ triggerEndOfPreRebootVerification(session);
+ assertThat(session.isSessionReady()).isTrue();
+ verify(observer, never()).onApexStaged(any());
+ }
+
+ private void triggerEndOfPreRebootVerification(StagingManager.StagedSession session) {
+ StagingManager.PreRebootVerificationHandler handler =
+ mStagingManager.mPreRebootVerificationHandler;
+ Message msg = handler.obtainMessage(
+ handler.MSG_PRE_REBOOT_VERIFICATION_END, session.sessionId(), -1, session);
+ handler.handleMessage(msg);
+ }
+
private StagingManager.StagedSession createSession(int sessionId, String packageName,
long committedMillis) {
PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
@@ -582,10 +869,16 @@ public class StagingManagerTest {
private String mPackageName;
private boolean mIsAbandonded = false;
private boolean mPreRebootVerificationStarted = false;
- private final List<StagingManager.StagedSession> mChildSessions = new ArrayList<>();
+ private final List<StagingManager.StagedSession> mChildSessions;
private FakeStagedSession(int sessionId) {
mSessionId = sessionId;
+ mChildSessions = new ArrayList<>();
+ }
+
+ private FakeStagedSession(int sessionId, List<StagingManager.StagedSession> childSessions) {
+ mSessionId = sessionId;
+ mChildSessions = childSessions;
}
private void setParentSessionId(int parentSessionId) {
@@ -775,9 +1068,7 @@ public class StagingManagerTest {
}
@Override
- public void notifyEndPreRebootVerification() {
- throw new UnsupportedOperationException();
- }
+ public void notifyEndPreRebootVerification() {}
@Override
public void verifySession() {
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index 7020744d661c..fb36a61bc5b1 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -25,6 +25,7 @@ android_test {
"test-apps/JobTestApp/src/**/*.java",
"test-apps/SuspendTestApp/src/**/*.java",
+ ":service-bluetooth-tests-sources", // TODO(b/214988855) : Remove once framework-bluetooth jar is ready
],
static_libs: [
"frameworks-base-testutils",
@@ -39,6 +40,7 @@ android_test {
"services.usage",
"services.uwb",
"guava",
+ "guava-android-testlib",
"androidx.test.core",
"androidx.test.ext.truth",
"androidx.test.runner",
@@ -86,7 +88,6 @@ android_test {
// These are not normally accessible from apps so they must be explicitly included.
jni_libs: [
- "libbacktrace",
"libbase",
"libbinder",
"libc++",
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index 68b84693f0ef..ccaa24495e84 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -94,6 +94,7 @@
<uses-permission android:name="android.permission.CONTROL_DEVICE_STATE"/>
<uses-permission android:name="android.permission.READ_PROJECTION_STATE"/>
<uses-permission android:name="android.permission.KILL_UID"/>
+ <uses-permission android:name="android.permission.MAINLINE_NETWORK_STACK"/>
<uses-permission
android:name="android.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD"/>
diff --git a/services/tests/servicestests/OWNERS b/services/tests/servicestests/OWNERS
new file mode 100644
index 000000000000..0fb0c3021486
--- /dev/null
+++ b/services/tests/servicestests/OWNERS
@@ -0,0 +1 @@
+include platform/frameworks/base:/services/core/java/com/android/server/am/OWNERS
diff --git a/services/tests/servicestests/src/com/android/server/BatteryServiceTest.java b/services/tests/servicestests/src/com/android/server/BatteryServiceTest.java
deleted file mode 100644
index cb12ba7d7679..000000000000
--- a/services/tests/servicestests/src/com/android/server/BatteryServiceTest.java
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * Copyright (C) 2017 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;
-
-import static junit.framework.Assert.*;
-
-import static org.mockito.Mockito.*;
-
-import android.hardware.health.V2_0.IHealth;
-import android.hidl.manager.V1_0.IServiceManager;
-import android.hidl.manager.V1_0.IServiceNotification;
-import android.test.AndroidTestCase;
-
-import androidx.test.filters.SmallTest;
-
-import org.mockito.ArgumentMatcher;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.mockito.invocation.InvocationOnMock;
-
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.NoSuchElementException;
-
-public class BatteryServiceTest extends AndroidTestCase {
-
- @Mock IServiceManager mMockedManager;
- @Mock IHealth mMockedHal;
- @Mock IHealth mMockedHal2;
-
- @Mock BatteryService.HealthServiceWrapper.Callback mCallback;
- @Mock BatteryService.HealthServiceWrapper.IServiceManagerSupplier mManagerSupplier;
- @Mock BatteryService.HealthServiceWrapper.IHealthSupplier mHealthServiceSupplier;
- BatteryService.HealthServiceWrapper mWrapper;
-
- private static final String HEALTHD = BatteryService.HealthServiceWrapper.INSTANCE_HEALTHD;
- private static final String VENDOR = BatteryService.HealthServiceWrapper.INSTANCE_VENDOR;
-
- @Override
- public void setUp() {
- MockitoAnnotations.initMocks(this);
- }
-
- @Override
- public void tearDown() {
- if (mWrapper != null)
- mWrapper.getHandlerThread().quitSafely();
- }
-
- public static <T> ArgumentMatcher<T> isOneOf(Collection<T> collection) {
- return new ArgumentMatcher<T>() {
- @Override public boolean matches(T e) {
- return collection.contains(e);
- }
- @Override public String toString() {
- return collection.toString();
- }
- };
- }
-
- private void initForInstances(String... instanceNamesArr) throws Exception {
- final Collection<String> instanceNames = Arrays.asList(instanceNamesArr);
- doAnswer((invocation) -> {
- // technically, preexisting is ignored by
- // BatteryService.HealthServiceWrapper.Notification, but still call it correctly.
- sendNotification(invocation, true);
- sendNotification(invocation, true);
- sendNotification(invocation, false);
- return null;
- }).when(mMockedManager).registerForNotifications(
- eq(IHealth.kInterfaceName),
- argThat(isOneOf(instanceNames)),
- any(IServiceNotification.class));
-
- doReturn(mMockedManager).when(mManagerSupplier).get();
- doReturn(mMockedHal) // init calls this
- .doReturn(mMockedHal) // notification 1
- .doReturn(mMockedHal) // notification 2
- .doReturn(mMockedHal2) // notification 3
- .doThrow(new RuntimeException("Should not call getService for more than 4 times"))
- .when(mHealthServiceSupplier).get(argThat(isOneOf(instanceNames)));
-
- mWrapper = new BatteryService.HealthServiceWrapper();
- }
-
- private void waitHandlerThreadFinish() throws Exception {
- for (int i = 0; i < 5; i++) {
- if (!mWrapper.getHandlerThread().getThreadHandler().hasMessagesOrCallbacks()) {
- return;
- }
- Thread.sleep(300);
- }
- assertFalse(mWrapper.getHandlerThread().getThreadHandler().hasMessagesOrCallbacks());
- }
-
- private static void sendNotification(InvocationOnMock invocation, boolean preexisting)
- throws Exception {
- ((IServiceNotification)invocation.getArguments()[2]).onRegistration(
- IHealth.kInterfaceName,
- (String)invocation.getArguments()[1],
- preexisting);
- }
-
- @SmallTest
- public void testWrapPreferVendor() throws Exception {
- initForInstances(VENDOR, HEALTHD);
- mWrapper.init(mCallback, mManagerSupplier, mHealthServiceSupplier);
- waitHandlerThreadFinish();
- verify(mCallback, times(1)).onRegistration(same(null), same(mMockedHal), eq(VENDOR));
- verify(mCallback, never()).onRegistration(same(mMockedHal), same(mMockedHal), anyString());
- verify(mCallback, times(1)).onRegistration(same(mMockedHal), same(mMockedHal2), eq(VENDOR));
- }
-
- @SmallTest
- public void testUseHealthd() throws Exception {
- initForInstances(HEALTHD);
- mWrapper.init(mCallback, mManagerSupplier, mHealthServiceSupplier);
- waitHandlerThreadFinish();
- verify(mCallback, times(1)).onRegistration(same(null), same(mMockedHal), eq(HEALTHD));
- verify(mCallback, never()).onRegistration(same(mMockedHal), same(mMockedHal), anyString());
- verify(mCallback, times(1)).onRegistration(same(mMockedHal), same(mMockedHal2), eq(HEALTHD));
- }
-
- @SmallTest
- public void testNoService() throws Exception {
- initForInstances("unrelated");
- try {
- mWrapper.init(mCallback, mManagerSupplier, mHealthServiceSupplier);
- fail("Expect NoSuchElementException");
- } catch (NoSuchElementException ex) {
- // expected
- }
- }
-}
diff --git a/services/tests/servicestests/src/com/android/server/BluetoothAirplaneModeListenerTest.java b/services/tests/servicestests/src/com/android/server/BluetoothAirplaneModeListenerTest.java
deleted file mode 100644
index 3ace3f4c79dc..000000000000
--- a/services/tests/servicestests/src/com/android/server/BluetoothAirplaneModeListenerTest.java
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * Copyright 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 com.android.server;
-
-import static org.mockito.Mockito.*;
-
-import android.bluetooth.BluetoothAdapter;
-import android.content.Context;
-import android.os.Looper;
-import android.provider.Settings;
-
-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.Mock;
-
-@MediumTest
-@RunWith(AndroidJUnit4.class)
-public class BluetoothAirplaneModeListenerTest {
- private Context mContext;
- private BluetoothAirplaneModeListener mBluetoothAirplaneModeListener;
- private BluetoothAdapter mBluetoothAdapter;
- private BluetoothModeChangeHelper mHelper;
-
- @Mock BluetoothManagerService mBluetoothManagerService;
-
- @Before
- public void setUp() throws Exception {
- mContext = InstrumentationRegistry.getTargetContext();
-
- mHelper = mock(BluetoothModeChangeHelper.class);
- when(mHelper.getSettingsInt(BluetoothAirplaneModeListener.TOAST_COUNT))
- .thenReturn(BluetoothAirplaneModeListener.MAX_TOAST_COUNT);
- doNothing().when(mHelper).setSettingsInt(anyString(), anyInt());
- doNothing().when(mHelper).showToastMessage();
- doNothing().when(mHelper).onAirplaneModeChanged(any(BluetoothManagerService.class));
-
- mBluetoothAirplaneModeListener = new BluetoothAirplaneModeListener(
- mBluetoothManagerService, Looper.getMainLooper(), mContext);
- mBluetoothAirplaneModeListener.start(mHelper);
- }
-
- @Test
- public void testIgnoreOnAirplanModeChange() {
- Assert.assertFalse(mBluetoothAirplaneModeListener.shouldSkipAirplaneModeChange());
-
- when(mHelper.isBluetoothOn()).thenReturn(true);
- Assert.assertFalse(mBluetoothAirplaneModeListener.shouldSkipAirplaneModeChange());
-
- when(mHelper.isA2dpOrHearingAidConnected()).thenReturn(true);
- Assert.assertFalse(mBluetoothAirplaneModeListener.shouldSkipAirplaneModeChange());
-
- when(mHelper.isAirplaneModeOn()).thenReturn(true);
- Assert.assertTrue(mBluetoothAirplaneModeListener.shouldSkipAirplaneModeChange());
- }
-
- @Test
- public void testHandleAirplaneModeChange_InvokeAirplaneModeChanged() {
- mBluetoothAirplaneModeListener.handleAirplaneModeChange();
- verify(mHelper).onAirplaneModeChanged(mBluetoothManagerService);
- }
-
- @Test
- public void testHandleAirplaneModeChange_NotInvokeAirplaneModeChanged_NotPopToast() {
- mBluetoothAirplaneModeListener.mToastCount = BluetoothAirplaneModeListener.MAX_TOAST_COUNT;
- when(mHelper.isBluetoothOn()).thenReturn(true);
- when(mHelper.isA2dpOrHearingAidConnected()).thenReturn(true);
- when(mHelper.isAirplaneModeOn()).thenReturn(true);
- mBluetoothAirplaneModeListener.handleAirplaneModeChange();
-
- verify(mHelper).setSettingsInt(Settings.Global.BLUETOOTH_ON,
- BluetoothManagerService.BLUETOOTH_ON_AIRPLANE);
- verify(mHelper, times(0)).showToastMessage();
- verify(mHelper, times(0)).onAirplaneModeChanged(mBluetoothManagerService);
- }
-
- @Test
- public void testHandleAirplaneModeChange_NotInvokeAirplaneModeChanged_PopToast() {
- mBluetoothAirplaneModeListener.mToastCount = 0;
- when(mHelper.isBluetoothOn()).thenReturn(true);
- when(mHelper.isA2dpOrHearingAidConnected()).thenReturn(true);
- when(mHelper.isAirplaneModeOn()).thenReturn(true);
- mBluetoothAirplaneModeListener.handleAirplaneModeChange();
-
- verify(mHelper).setSettingsInt(Settings.Global.BLUETOOTH_ON,
- BluetoothManagerService.BLUETOOTH_ON_AIRPLANE);
- verify(mHelper).showToastMessage();
- verify(mHelper, times(0)).onAirplaneModeChanged(mBluetoothManagerService);
- }
-
- @Test
- public void testIsPopToast_PopToast() {
- mBluetoothAirplaneModeListener.mToastCount = 0;
- Assert.assertTrue(mBluetoothAirplaneModeListener.shouldPopToast());
- verify(mHelper).setSettingsInt(BluetoothAirplaneModeListener.TOAST_COUNT, 1);
- }
-
- @Test
- public void testIsPopToast_NotPopToast() {
- mBluetoothAirplaneModeListener.mToastCount = BluetoothAirplaneModeListener.MAX_TOAST_COUNT;
- Assert.assertFalse(mBluetoothAirplaneModeListener.shouldPopToast());
- verify(mHelper, times(0)).setSettingsInt(anyString(), anyInt());
- }
-}
diff --git a/services/tests/servicestests/src/com/android/server/BootReceiverTest.java b/services/tests/servicestests/src/com/android/server/BootReceiverTest.java
deleted file mode 100644
index 489e2f769a3d..000000000000
--- a/services/tests/servicestests/src/com/android/server/BootReceiverTest.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server;
-
-import android.test.AndroidTestCase;
-
-/**
- * Tests for {@link com.android.server.BootReceiver}
- */
-public class BootReceiverTest extends AndroidTestCase {
- public void testLogLinePotentiallySensitive() throws Exception {
- /*
- * Strings to be dropped from the log as potentially sensitive: register dumps, process
- * names, hardware info.
- */
- final String[] becomeNull = {
- "CPU: 4 PID: 120 Comm: kunit_try_catch Tainted: G W 5.8.0-rc6+ #7",
- "Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.13.0-1 04/01/2014",
- "[ 0.083207] RSP: 0000:ffffffff8fe07ca8 EFLAGS: 00010046 ORIG_RAX: 0000000000000000",
- "[ 0.084709] RAX: 0000000000000000 RBX: ffffffffff240000 RCX: ffffffff815fcf01",
- "[ 0.086109] RDX: dffffc0000000000 RSI: 0000000000000001 RDI: ffffffffff240004",
- "[ 0.087509] RBP: ffffffff8fe07d60 R08: fffffbfff1fc0f21 R09: fffffbfff1fc0f21",
- "[ 0.088911] R10: ffffffff8fe07907 R11: fffffbfff1fc0f20 R12: ffffffff8fe07d38",
- "R13: 0000000000000001 R14: 0000000000000001 R15: ffffffff8fe07e80",
- "x29: ffff00003ce07150 x28: ffff80001aa29cc0",
- "x1 : 0000000000000000 x0 : ffff00000f628000",
- };
-
- /* Strings to be left unchanged, including non-sensitive registers and parts of reports. */
- final String[] leftAsIs = {
- "FS: 0000000000000000(0000) GS:ffffffff92409000(0000) knlGS:0000000000000000",
- "[ 69.2366] [ T6006]c7 6006 =======================================================",
- "[ 69.245688] [ T6006] BUG: KFENCE: out-of-bounds in kfence_handle_page_fault",
- "[ 69.257816] [ T6006]c7 6006 Out-of-bounds access at 0xffffffca75c45000 ",
- "[ 69.273536] [ T6006]c7 6006 __do_kernel_fault+0xa8/0x11c",
- "pc : __mutex_lock+0x428/0x99c ",
- "sp : ffff00003ce07150",
- "Call trace:",
- "",
- };
-
- final String[][] stripped = {
- { "Detected corrupted memory at 0xffffffffb6797ff9 [ 0xac . . . . . . ]:",
- "Detected corrupted memory at 0xffffffffb6797ff9" },
- };
- for (int i = 0; i < becomeNull.length; i++) {
- assertEquals(BootReceiver.stripSensitiveData(becomeNull[i]), null);
- }
-
- for (int i = 0; i < leftAsIs.length; i++) {
- assertEquals(BootReceiver.stripSensitiveData(leftAsIs[i]), leftAsIs[i]);
- }
-
- for (int i = 0; i < stripped.length; i++) {
- assertEquals(BootReceiver.stripSensitiveData(stripped[i][0]), stripped[i][1]);
- }
- }
-}
diff --git a/services/tests/servicestests/src/com/android/server/NativeDaemonConnectorTest.java b/services/tests/servicestests/src/com/android/server/NativeDaemonConnectorTest.java
deleted file mode 100644
index e2253a2151b0..000000000000
--- a/services/tests/servicestests/src/com/android/server/NativeDaemonConnectorTest.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright (C) 2011 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;
-
-import static com.android.server.NativeDaemonConnector.appendEscaped;
-import static com.android.server.NativeDaemonConnector.makeCommand;
-
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
-
-import com.android.server.NativeDaemonConnector.SensitiveArg;
-
-/**
- * Tests for {@link NativeDaemonConnector}.
- */
-@MediumTest
-public class NativeDaemonConnectorTest extends AndroidTestCase {
- private static final String TAG = "NativeDaemonConnectorTest";
-
- public void testArgumentNormal() throws Exception {
- final StringBuilder builder = new StringBuilder();
-
- builder.setLength(0);
- appendEscaped(builder, "");
- assertEquals("", builder.toString());
-
- builder.setLength(0);
- appendEscaped(builder, "foo");
- assertEquals("foo", builder.toString());
-
- builder.setLength(0);
- appendEscaped(builder, "foo\"bar");
- assertEquals("foo\\\"bar", builder.toString());
-
- builder.setLength(0);
- appendEscaped(builder, "foo\\bar\\\"baz");
- assertEquals("foo\\\\bar\\\\\\\"baz", builder.toString());
- }
-
- public void testArgumentWithSpaces() throws Exception {
- final StringBuilder builder = new StringBuilder();
-
- builder.setLength(0);
- appendEscaped(builder, "foo bar");
- assertEquals("\"foo bar\"", builder.toString());
-
- builder.setLength(0);
- appendEscaped(builder, "foo\"bar\\baz foo");
- assertEquals("\"foo\\\"bar\\\\baz foo\"", builder.toString());
- }
-
- public void testArgumentWithUtf() throws Exception {
- final StringBuilder builder = new StringBuilder();
-
- builder.setLength(0);
- appendEscaped(builder, "caf\u00E9 c\u00F6ffee");
- assertEquals("\"caf\u00E9 c\u00F6ffee\"", builder.toString());
- }
-
- public void testSensitiveArgs() throws Exception {
- final StringBuilder rawBuilder = new StringBuilder();
- final StringBuilder logBuilder = new StringBuilder();
-
- rawBuilder.setLength(0);
- logBuilder.setLength(0);
- makeCommand(rawBuilder, logBuilder, 1, "foo", "bar", "baz");
- assertEquals("1 foo bar baz\0", rawBuilder.toString());
- assertEquals("1 foo bar baz", logBuilder.toString());
-
- rawBuilder.setLength(0);
- logBuilder.setLength(0);
- makeCommand(rawBuilder, logBuilder, 1, "foo", new SensitiveArg("bar"), "baz");
- assertEquals("1 foo bar baz\0", rawBuilder.toString());
- assertEquals("1 foo [scrubbed] baz", logBuilder.toString());
-
- rawBuilder.setLength(0);
- logBuilder.setLength(0);
- makeCommand(rawBuilder, logBuilder, 1, "foo", new SensitiveArg("foo bar"), "baz baz",
- new SensitiveArg("wat"));
- assertEquals("1 foo \"foo bar\" \"baz baz\" wat\0", rawBuilder.toString());
- assertEquals("1 foo [scrubbed] \"baz baz\" [scrubbed]", logBuilder.toString());
- }
-}
diff --git a/services/tests/servicestests/src/com/android/server/OWNERS b/services/tests/servicestests/src/com/android/server/OWNERS
index f1402ead3866..68994e6b5a6f 100644
--- a/services/tests/servicestests/src/com/android/server/OWNERS
+++ b/services/tests/servicestests/src/com/android/server/OWNERS
@@ -1,6 +1,7 @@
per-file *Alarm* = file:/apex/jobscheduler/OWNERS
per-file *AppOp* = file:/core/java/android/permission/OWNERS
-per-file *Bluetooth* = file:/core/java/android/bluetooth/OWNERS
+per-file *Bluetooth* = file:platform/packages/modules/Bluetooth:master:/framework/java/android/bluetooth/OWNERS
per-file *Gnss* = file:/services/core/java/com/android/server/location/OWNERS
per-file *Network* = file:/services/core/java/com/android/server/net/OWNERS
+per-file BatteryServiceTest.java = file:platform/hardware/interfaces:/health/aidl/OWNERS
per-file GestureLauncherServiceTest.java = file:platform/packages/apps/EmergencyInfo:/OWNERS
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/AppSearchImplPlatformTest.java b/services/tests/servicestests/src/com/android/server/appsearch/AppSearchImplPlatformTest.java
deleted file mode 100644
index 91bf4d12a299..000000000000
--- a/services/tests/servicestests/src/com/android/server/appsearch/AppSearchImplPlatformTest.java
+++ /dev/null
@@ -1,463 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-// TODO(b/169883602): This is purposely a different package from the path so that it can access
-// AppSearchImpl's methods without having to make them public. This should be replaced by proper
-// global query integration tests that can test AppSearchImpl-VisibilityStore integration logic.
-package com.android.server.appsearch.external.localstorage;
-
-import static android.Manifest.permission.READ_GLOBAL_APP_SEARCH_DATA;
-import static android.content.pm.PackageManager.PERMISSION_DENIED;
-import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.when;
-
-import android.annotation.NonNull;
-import android.app.appsearch.AppSearchSchema;
-import android.app.appsearch.PackageIdentifier;
-import android.content.Context;
-import android.content.ContextWrapper;
-import android.content.pm.PackageManager;
-import android.os.UserHandle;
-import android.util.ArrayMap;
-
-import androidx.test.core.app.ApplicationProvider;
-
-import com.android.compatibility.common.util.SystemUtil;
-import com.android.server.appsearch.external.localstorage.util.PrefixUtil;
-import com.android.server.appsearch.visibilitystore.VisibilityStoreImpl;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TemporaryFolder;
-import org.mockito.Mockito;
-
-import java.util.Collections;
-import java.util.Map;
-
-/** This tests AppSearchImpl when it's running with a platform-backed VisibilityStore. */
-public class AppSearchImplPlatformTest {
- /**
- * Always trigger optimize in this class. OptimizeStrategy will be tested in its own test class.
- */
- private static final OptimizeStrategy ALWAYS_OPTIMIZE = optimizeInfo -> true;
-
- @Rule public TemporaryFolder mTemporaryFolder = new TemporaryFolder();
- private final Map<UserHandle, PackageManager> mMockPackageManagers = new ArrayMap<>();
- private Context mContext;
- private AppSearchImpl mAppSearchImpl;
- private VisibilityStoreImpl mVisibilityStore;
- private int mGlobalQuerierUid;
-
- @Before
- public void setUp() throws Exception {
- Context context = ApplicationProvider.getApplicationContext();
- mContext = new ContextWrapper(context) {
- @Override
- public Context createContextAsUser(UserHandle user, int flags) {
- return new ContextWrapper(super.createContextAsUser(user, flags)) {
- @Override
- public PackageManager getPackageManager() {
- return getMockPackageManager(user);
- }
- };
- }
-
- @Override
- public PackageManager getPackageManager() {
- return createContextAsUser(getUser(), /*flags=*/ 0).getPackageManager();
- }
- };
-
- // Give ourselves global query permissions
- mAppSearchImpl = AppSearchImpl.create(
- mTemporaryFolder.newFolder(),
- new UnlimitedLimitConfig(),
- /*initStatsBuilder=*/ null,
- ALWAYS_OPTIMIZE);
- mVisibilityStore = VisibilityStoreImpl.create(mAppSearchImpl, mContext);
- mGlobalQuerierUid =
- mContext.getPackageManager().getPackageUid(mContext.getPackageName(), /*flags=*/ 0);
- }
-
- @Test
- public void testSetSchema_existingSchemaRetainsVisibilitySetting() throws Exception {
- // Values for a "foo" client
- String packageNameFoo = "packageFoo";
- byte[] sha256CertFoo = new byte[] {10};
- int uidFoo = 1;
-
- // Make sure foo package will pass package manager checks.
- PackageManager mockPackageManager = getMockPackageManager(mContext.getUser());
- when(mockPackageManager.getPackageUid(eq(packageNameFoo), /*flags=*/ anyInt()))
- .thenReturn(uidFoo);
- when(mockPackageManager.hasSigningCertificate(
- packageNameFoo, sha256CertFoo, PackageManager.CERT_INPUT_SHA256))
- .thenReturn(true);
-
- // Make sure we have global query privileges and "foo" doesn't
- when(mockPackageManager.checkPermission(
- READ_GLOBAL_APP_SEARCH_DATA, mContext.getPackageName()))
- .thenReturn(PERMISSION_GRANTED);
- when(mockPackageManager.checkPermission(READ_GLOBAL_APP_SEARCH_DATA, packageNameFoo))
- .thenReturn(PERMISSION_DENIED);
-
- // Set schema1
- String prefix = PrefixUtil.createPrefix("package", "database");
- mAppSearchImpl.setSchema(
- "package",
- "database",
- Collections.singletonList(new AppSearchSchema.Builder("schema1").build()),
- mVisibilityStore,
- /*schemasNotDisplayedBySystem=*/ Collections.singletonList("schema1"),
- /*schemasVisibleToPackages=*/ ImmutableMap.of(
- "schema1",
- ImmutableList.of(new PackageIdentifier(packageNameFoo, sha256CertFoo))),
- /*forceOverride=*/ false,
- /*schemaVersion=*/ 0,
- /*setSchemaStatsBuilder=*/ null);
-
- // "schema1" is platform hidden now and package visible to package1
- assertThat(mVisibilityStore.isSchemaSearchableByCaller(
- "package",
- "database",
- prefix + "schema1",
- mGlobalQuerierUid,
- /*callerHasSystemAccess=*/ true))
- .isFalse();
-
- assertThat(mVisibilityStore.isSchemaSearchableByCaller(
- "package",
- "database",
- prefix + "schema1",
- uidFoo,
- /*callerHasSystemAccess=*/ false))
- .isTrue();
-
- // Add a new schema, and include the already-existing "schema1"
- mAppSearchImpl.setSchema(
- "package",
- "database",
- ImmutableList.of(
- new AppSearchSchema.Builder("schema1").build(),
- new AppSearchSchema.Builder("schema2").build()),
- mVisibilityStore,
- /*schemasNotDisplayedBySystem=*/ Collections.singletonList("schema1"),
- /*schemasVisibleToPackages=*/ ImmutableMap.of(
- "schema1",
- ImmutableList.of(new PackageIdentifier(packageNameFoo, sha256CertFoo))),
- /*forceOverride=*/ false,
- /*schemaVersion=*/ 0,
- /*setSchemaStatsBuilder=*/ null);
-
- // Check that "schema1" still has the same visibility settings
- SystemUtil.runWithShellPermissionIdentity(() -> assertThat(
- mVisibilityStore.isSchemaSearchableByCaller(
- "package",
- "database",
- prefix + "schema1",
- mGlobalQuerierUid,
- /*callerHasSystemAccess=*/ true))
- .isFalse(),
- READ_GLOBAL_APP_SEARCH_DATA);
-
- assertThat(mVisibilityStore.isSchemaSearchableByCaller(
- "package",
- "database",
- prefix + "schema1",
- uidFoo,
- /*callerHasSystemAccess=*/ false))
- .isTrue();
-
- // "schema2" has default visibility settings
- SystemUtil.runWithShellPermissionIdentity(() -> assertThat(
- mVisibilityStore.isSchemaSearchableByCaller(
- "package",
- "database",
- prefix + "schema2",
- mGlobalQuerierUid,
- /*callerHasSystemAccess=*/ true))
- .isTrue(),
- READ_GLOBAL_APP_SEARCH_DATA);
-
- assertThat(mVisibilityStore.isSchemaSearchableByCaller(
- "package",
- "database",
- prefix + "schema2",
- uidFoo,
- /*callerHasSystemAccess=*/ false))
- .isFalse();
- }
-
- @Test
- public void testRemoveSchema_removedFromVisibilityStore() throws Exception {
- // Values for a "foo" client
- String packageNameFoo = "packageFoo";
- byte[] sha256CertFoo = new byte[] {10};
- int uidFoo = 1;
-
- // Make sure foo package will pass package manager checks.
- PackageManager mockPackageManager = getMockPackageManager(mContext.getUser());
- when(mockPackageManager.getPackageUid(eq(packageNameFoo), /*flags=*/ anyInt()))
- .thenReturn(uidFoo);
- when(mockPackageManager.hasSigningCertificate(
- packageNameFoo, sha256CertFoo, PackageManager.CERT_INPUT_SHA256))
- .thenReturn(true);
-
- // Make sure we have global query privileges and "foo" doesn't
- when(mockPackageManager.checkPermission(
- READ_GLOBAL_APP_SEARCH_DATA, mContext.getPackageName()))
- .thenReturn(PERMISSION_GRANTED);
- when(mockPackageManager.checkPermission(READ_GLOBAL_APP_SEARCH_DATA, packageNameFoo))
- .thenReturn(PERMISSION_DENIED);
-
- String prefix = PrefixUtil.createPrefix("package", "database");
- mAppSearchImpl.setSchema(
- "package",
- "database",
- Collections.singletonList(new AppSearchSchema.Builder("schema1").build()),
- mVisibilityStore,
- /*schemasNotDisplayedBySystem=*/ Collections.singletonList("schema1"),
- /*schemasVisibleToPackages=*/ ImmutableMap.of(
- "schema1",
- ImmutableList.of(new PackageIdentifier(packageNameFoo, sha256CertFoo))),
- /*forceOverride=*/ false,
- /*schemaVersion=*/ 0,
- /*setSchemaStatsBuilder=*/ null);
-
- // "schema1" is platform hidden now and package accessible
- assertThat(mVisibilityStore.isSchemaSearchableByCaller(
- "package",
- "database",
- prefix + "schema1",
- mGlobalQuerierUid,
- /*callerHasSystemAccess=*/ true))
- .isFalse();
-
- assertThat(mVisibilityStore.isSchemaSearchableByCaller(
- "package",
- "database",
- prefix + "schema1",
- uidFoo,
- /*callerHasSystemAccess=*/ false))
- .isTrue();
-
- // Remove "schema1" by force overriding
- mAppSearchImpl.setSchema(
- "package",
- "database",
- /*schemas=*/ Collections.emptyList(),
- mVisibilityStore,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ true,
- /*schemaVersion=*/ 0,
- /*setSchemaStatsBuilder=*/ null);
-
- // Check that "schema1" is no longer considered platform hidden or package accessible
- assertThat(mVisibilityStore.isSchemaSearchableByCaller(
- "package",
- "database",
- prefix + "schema1",
- mGlobalQuerierUid,
- /*callerHasSystemAccess=*/ true))
- .isTrue();
-
- assertThat(mVisibilityStore.isSchemaSearchableByCaller(
- "package",
- "database",
- prefix + "schema1",
- uidFoo,
- /*callerHasSystemAccess=*/ false))
- .isFalse();
-
- // Add "schema1" back, it gets default visibility settings which means it's not platform
- // hidden and not package accessible
- mAppSearchImpl.setSchema(
- "package",
- "database",
- Collections.singletonList(new AppSearchSchema.Builder("schema1").build()),
- mVisibilityStore,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*schemaVersion=*/ 0,
- /*setSchemaStatsBuilder=*/ null);
-
- assertThat(mVisibilityStore.isSchemaSearchableByCaller(
- "package",
- "database",
- prefix + "schema1",
- mGlobalQuerierUid,
- /*callerHasSystemAccess=*/ true))
- .isTrue();
- assertThat(mVisibilityStore.isSchemaSearchableByCaller(
- "package",
- "database",
- prefix + "schema1",
- uidFoo,
- /*callerHasSystemAccess=*/ false))
- .isFalse();
- }
-
- @Test
- public void testSetSchema_defaultPlatformVisible() throws Exception {
- // Make sure we have global query privileges
- PackageManager mockPackageManager = getMockPackageManager(mContext.getUser());
- when(mockPackageManager.checkPermission(
- READ_GLOBAL_APP_SEARCH_DATA, mContext.getPackageName()))
- .thenReturn(PERMISSION_GRANTED);
-
- String prefix = PrefixUtil.createPrefix("package", "database");
- mAppSearchImpl.setSchema(
- "package",
- "database",
- Collections.singletonList(new AppSearchSchema.Builder("Schema").build()),
- mVisibilityStore,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*schemaVersion=*/ 0,
- /*setSchemaStatsBuilder=*/ null);
-
- assertThat(mVisibilityStore.isSchemaSearchableByCaller(
- "package",
- "database",
- prefix + "Schema",
- mGlobalQuerierUid,
- /*callerHasSystemAccess=*/ true))
- .isTrue();
- }
-
- @Test
- public void testSetSchema_platformHidden() throws Exception {
- // Make sure we have global query privileges
- PackageManager mockPackageManager = getMockPackageManager(mContext.getUser());
- when(mockPackageManager.checkPermission(
- READ_GLOBAL_APP_SEARCH_DATA, mContext.getPackageName()))
- .thenReturn(PERMISSION_GRANTED);
-
- String prefix = PrefixUtil.createPrefix("package", "database");
- mAppSearchImpl.setSchema(
- "package",
- "database",
- Collections.singletonList(new AppSearchSchema.Builder("Schema").build()),
- mVisibilityStore,
- /*schemasNotDisplayedBySystem=*/ Collections.singletonList("Schema"),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*schemaVersion=*/ 0,
- /*setSchemaStatsBuilder=*/ null);
-
- assertThat(mVisibilityStore.isSchemaSearchableByCaller(
- "package",
- "database",
- prefix + "Schema",
- mGlobalQuerierUid,
- /*callerHasSystemAccess=*/ true))
- .isFalse();
- }
-
- @Test
- public void testSetSchema_defaultNotVisibleToPackages() throws Exception {
- String packageName = "com.package";
-
- // Make sure package doesn't global query privileges
- PackageManager mockPackageManager = getMockPackageManager(mContext.getUser());
- when(mockPackageManager.checkPermission(
- READ_GLOBAL_APP_SEARCH_DATA, packageName)).thenReturn(PERMISSION_DENIED);
-
- String prefix = PrefixUtil.createPrefix("package", "database");
- mAppSearchImpl.setSchema(
- "package",
- "database",
- Collections.singletonList(new AppSearchSchema.Builder("Schema").build()),
- mVisibilityStore,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*schemaVersion=*/ 0,
- /*setSchemaStatsBuilder=*/ null);
- assertThat(mVisibilityStore
- .isSchemaSearchableByCaller(
- "package",
- "database",
- prefix + "Schema",
- /*callerUid=*/ 42,
- /*callerHasSystemAccess=*/ false))
- .isFalse();
- }
-
- @Test
- public void testSetSchema_visibleToPackages() throws Exception {
- // Values for a "foo" client
- String packageNameFoo = "packageFoo";
- byte[] sha256CertFoo = new byte[] {10};
- int uidFoo = 1;
-
- // Make sure foo package will pass package manager checks.
- PackageManager mockPackageManager = getMockPackageManager(mContext.getUser());
- when(mockPackageManager.getPackageUid(eq(packageNameFoo), /*flags=*/ anyInt()))
- .thenReturn(uidFoo);
- when(mockPackageManager.hasSigningCertificate(
- packageNameFoo, sha256CertFoo, PackageManager.CERT_INPUT_SHA256))
- .thenReturn(true);
-
- // Make sure foo doesn't have global query privileges
- when(mockPackageManager.checkPermission(READ_GLOBAL_APP_SEARCH_DATA, packageNameFoo))
- .thenReturn(PERMISSION_DENIED);
-
- String prefix = PrefixUtil.createPrefix("package", "database");
- mAppSearchImpl.setSchema(
- "package",
- "database",
- Collections.singletonList(new AppSearchSchema.Builder("Schema").build()),
- mVisibilityStore,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ ImmutableMap.of(
- "Schema",
- ImmutableList.of(new PackageIdentifier(packageNameFoo, sha256CertFoo))),
- /*forceOverride=*/ false,
- /*schemaVersion=*/ 0,
- /*setSchemaStatsBuilder=*/ null);
- assertThat(mVisibilityStore
- .isSchemaSearchableByCaller(
- "package",
- "database",
- prefix + "Schema",
- uidFoo,
- /*callerHasSystemAccess=*/ false))
- .isTrue();
- }
-
- @NonNull
- private PackageManager getMockPackageManager(@NonNull UserHandle user) {
- PackageManager pm = mMockPackageManagers.get(user);
- if (pm == null) {
- pm = Mockito.mock(PackageManager.class);
- mMockPackageManagers.put(user, pm);
- }
- return pm;
- }
-}
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/FrameworkOptimizeStrategyTest.java b/services/tests/servicestests/src/com/android/server/appsearch/FrameworkOptimizeStrategyTest.java
deleted file mode 100644
index 8389c85477ea..000000000000
--- a/services/tests/servicestests/src/com/android/server/appsearch/FrameworkOptimizeStrategyTest.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.server.appsearch;
-
-import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import com.android.server.appsearch.icing.proto.GetOptimizeInfoResultProto;
-import com.android.server.appsearch.icing.proto.StatusProto;
-
-import org.junit.Test;
-
-public class FrameworkOptimizeStrategyTest {
- AppSearchConfig mAppSearchConfig = AppSearchConfig.create(DIRECT_EXECUTOR);
- FrameworkOptimizeStrategy mFrameworkOptimizeStrategy =
- new FrameworkOptimizeStrategy(mAppSearchConfig);
-
- @Test
- public void testShouldOptimize_byteThreshold() {
- GetOptimizeInfoResultProto optimizeInfo =
- GetOptimizeInfoResultProto.newBuilder()
- .setTimeSinceLastOptimizeMs(0)
- .setEstimatedOptimizableBytes(
- mAppSearchConfig.getCachedBytesOptimizeThreshold())
- .setOptimizableDocs(0)
- .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.OK).build())
- .build();
- assertThat(mFrameworkOptimizeStrategy.shouldOptimize(optimizeInfo)).isTrue();
- }
-
- @Test
- public void testShouldNotOptimize_timeThreshold() {
- GetOptimizeInfoResultProto optimizeInfo =
- GetOptimizeInfoResultProto.newBuilder()
- .setTimeSinceLastOptimizeMs(
- mAppSearchConfig.getCachedTimeOptimizeThresholdMs())
- .setEstimatedOptimizableBytes(0)
- .setOptimizableDocs(0)
- .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.OK).build())
- .build();
- assertThat(mFrameworkOptimizeStrategy.shouldOptimize(optimizeInfo)).isTrue();
- }
-
- @Test
- public void testShouldOptimize_docCountThreshold() {
- GetOptimizeInfoResultProto optimizeInfo =
- GetOptimizeInfoResultProto.newBuilder()
- .setTimeSinceLastOptimizeMs(0)
- .setEstimatedOptimizableBytes(0)
- .setOptimizableDocs(
- mAppSearchConfig.getCachedDocCountOptimizeThreshold())
- .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.OK).build())
- .build();
- assertThat(mFrameworkOptimizeStrategy.shouldOptimize(optimizeInfo)).isTrue();
- }
-}
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/OWNERS b/services/tests/servicestests/src/com/android/server/appsearch/OWNERS
deleted file mode 100644
index ebe9e4ec087f..000000000000
--- a/services/tests/servicestests/src/com/android/server/appsearch/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-include /apex/appsearch/OWNERS
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java
deleted file mode 100644
index dd3b3ec08dbf..000000000000
--- a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java
+++ /dev/null
@@ -1,3346 +0,0 @@
-/*
- * 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.appsearch.external.localstorage;
-
-import static com.android.server.appsearch.external.localstorage.util.PrefixUtil.addPrefixToDocument;
-import static com.android.server.appsearch.external.localstorage.util.PrefixUtil.createPrefix;
-import static com.android.server.appsearch.external.localstorage.util.PrefixUtil.removePrefixesFromDocument;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.junit.Assert.assertThrows;
-
-import android.app.appsearch.AppSearchResult;
-import android.app.appsearch.AppSearchSchema;
-import android.app.appsearch.GenericDocument;
-import android.app.appsearch.SearchResult;
-import android.app.appsearch.SearchResultPage;
-import android.app.appsearch.SearchSpec;
-import android.app.appsearch.SetSchemaResponse;
-import android.app.appsearch.StorageInfo;
-import android.app.appsearch.exceptions.AppSearchException;
-import android.content.Context;
-import android.os.Process;
-import android.util.ArrayMap;
-import android.util.ArraySet;
-
-import androidx.test.core.app.ApplicationProvider;
-
-import com.android.server.appsearch.external.localstorage.converter.GenericDocumentToProtoConverter;
-import com.android.server.appsearch.external.localstorage.stats.InitializeStats;
-import com.android.server.appsearch.external.localstorage.stats.OptimizeStats;
-import com.android.server.appsearch.external.localstorage.util.PrefixUtil;
-import com.android.server.appsearch.icing.proto.DocumentProto;
-import com.android.server.appsearch.icing.proto.GetOptimizeInfoResultProto;
-import com.android.server.appsearch.icing.proto.PersistType;
-import com.android.server.appsearch.icing.proto.PropertyConfigProto;
-import com.android.server.appsearch.icing.proto.PropertyProto;
-import com.android.server.appsearch.icing.proto.PutResultProto;
-import com.android.server.appsearch.icing.proto.SchemaProto;
-import com.android.server.appsearch.icing.proto.SchemaTypeConfigProto;
-import com.android.server.appsearch.icing.proto.SearchResultProto;
-import com.android.server.appsearch.icing.proto.SearchSpecProto;
-import com.android.server.appsearch.icing.proto.StatusProto;
-import com.android.server.appsearch.icing.proto.StorageInfoProto;
-import com.android.server.appsearch.icing.proto.StringIndexingConfig;
-import com.android.server.appsearch.icing.proto.TermMatchType;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TemporaryFolder;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-public class AppSearchImplTest {
- /**
- * Always trigger optimize in this class. OptimizeStrategy will be tested in its own test class.
- */
- private static final OptimizeStrategy ALWAYS_OPTIMIZE = optimizeInfo -> true;
-
- @Rule public TemporaryFolder mTemporaryFolder = new TemporaryFolder();
- private AppSearchImpl mAppSearchImpl;
-
- @Before
- public void setUp() throws Exception {
- mAppSearchImpl =
- AppSearchImpl.create(
- mTemporaryFolder.newFolder(),
- new UnlimitedLimitConfig(),
- /*initStatsBuilder=*/ null,
- ALWAYS_OPTIMIZE);
- }
-
- /**
- * Ensure that we can rewrite an incoming schema type by adding the database as a prefix. While
- * also keeping any other existing schema types that may already be part of Icing's persisted
- * schema.
- */
- @Test
- public void testRewriteSchema_addType() throws Exception {
- SchemaProto.Builder existingSchemaBuilder =
- SchemaProto.newBuilder()
- .addTypes(
- SchemaTypeConfigProto.newBuilder()
- .setSchemaType("package$existingDatabase/Foo")
- .build());
-
- // Create a copy so we can modify it.
- List<SchemaTypeConfigProto> existingTypes =
- new ArrayList<>(existingSchemaBuilder.getTypesList());
- SchemaTypeConfigProto schemaTypeConfigProto1 =
- SchemaTypeConfigProto.newBuilder().setSchemaType("Foo").build();
- SchemaTypeConfigProto schemaTypeConfigProto2 =
- SchemaTypeConfigProto.newBuilder()
- .setSchemaType("TestType")
- .addProperties(
- PropertyConfigProto.newBuilder()
- .setPropertyName("subject")
- .setDataType(PropertyConfigProto.DataType.Code.STRING)
- .setCardinality(
- PropertyConfigProto.Cardinality.Code.OPTIONAL)
- .setStringIndexingConfig(
- StringIndexingConfig.newBuilder()
- .setTokenizerType(
- StringIndexingConfig.TokenizerType
- .Code.PLAIN)
- .setTermMatchType(TermMatchType.Code.PREFIX)
- .build())
- .build())
- .addProperties(
- PropertyConfigProto.newBuilder()
- .setPropertyName("link")
- .setDataType(PropertyConfigProto.DataType.Code.DOCUMENT)
- .setCardinality(
- PropertyConfigProto.Cardinality.Code.OPTIONAL)
- .setSchemaType("RefType")
- .build())
- .build();
- SchemaTypeConfigProto schemaTypeConfigProto3 =
- SchemaTypeConfigProto.newBuilder().setSchemaType("RefType").build();
- SchemaProto newSchema =
- SchemaProto.newBuilder()
- .addTypes(schemaTypeConfigProto1)
- .addTypes(schemaTypeConfigProto2)
- .addTypes(schemaTypeConfigProto3)
- .build();
-
- AppSearchImpl.RewrittenSchemaResults rewrittenSchemaResults =
- AppSearchImpl.rewriteSchema(
- createPrefix("package", "newDatabase"), existingSchemaBuilder, newSchema);
-
- // We rewrote all the new types that were added. And nothing was removed.
- assertThat(rewrittenSchemaResults.mRewrittenPrefixedTypes.keySet())
- .containsExactly(
- "package$newDatabase/Foo",
- "package$newDatabase/TestType",
- "package$newDatabase/RefType");
- assertThat(
- rewrittenSchemaResults
- .mRewrittenPrefixedTypes
- .get("package$newDatabase/Foo")
- .getSchemaType())
- .isEqualTo("package$newDatabase/Foo");
- assertThat(
- rewrittenSchemaResults
- .mRewrittenPrefixedTypes
- .get("package$newDatabase/TestType")
- .getSchemaType())
- .isEqualTo("package$newDatabase/TestType");
- assertThat(
- rewrittenSchemaResults
- .mRewrittenPrefixedTypes
- .get("package$newDatabase/RefType")
- .getSchemaType())
- .isEqualTo("package$newDatabase/RefType");
- assertThat(rewrittenSchemaResults.mDeletedPrefixedTypes).isEmpty();
-
- SchemaProto expectedSchema =
- SchemaProto.newBuilder()
- .addTypes(
- SchemaTypeConfigProto.newBuilder()
- .setSchemaType("package$newDatabase/Foo")
- .build())
- .addTypes(
- SchemaTypeConfigProto.newBuilder()
- .setSchemaType("package$newDatabase/TestType")
- .addProperties(
- PropertyConfigProto.newBuilder()
- .setPropertyName("subject")
- .setDataType(
- PropertyConfigProto.DataType.Code
- .STRING)
- .setCardinality(
- PropertyConfigProto.Cardinality.Code
- .OPTIONAL)
- .setStringIndexingConfig(
- StringIndexingConfig.newBuilder()
- .setTokenizerType(
- StringIndexingConfig
- .TokenizerType
- .Code.PLAIN)
- .setTermMatchType(
- TermMatchType.Code
- .PREFIX)
- .build())
- .build())
- .addProperties(
- PropertyConfigProto.newBuilder()
- .setPropertyName("link")
- .setDataType(
- PropertyConfigProto.DataType.Code
- .DOCUMENT)
- .setCardinality(
- PropertyConfigProto.Cardinality.Code
- .OPTIONAL)
- .setSchemaType(
- "package$newDatabase/RefType")
- .build())
- .build())
- .addTypes(
- SchemaTypeConfigProto.newBuilder()
- .setSchemaType("package$newDatabase/RefType")
- .build())
- .build();
-
- existingTypes.addAll(expectedSchema.getTypesList());
- assertThat(existingSchemaBuilder.getTypesList()).containsExactlyElementsIn(existingTypes);
- }
-
- /**
- * Ensure that we track all types that were rewritten in the input schema. Even if they were not
- * technically "added" to the existing schema.
- */
- @Test
- public void testRewriteSchema_rewriteType() throws Exception {
- SchemaProto.Builder existingSchemaBuilder =
- SchemaProto.newBuilder()
- .addTypes(
- SchemaTypeConfigProto.newBuilder()
- .setSchemaType("package$existingDatabase/Foo")
- .build());
-
- SchemaProto newSchema =
- SchemaProto.newBuilder()
- .addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType("Foo").build())
- .build();
-
- AppSearchImpl.RewrittenSchemaResults rewrittenSchemaResults =
- AppSearchImpl.rewriteSchema(
- createPrefix("package", "existingDatabase"),
- existingSchemaBuilder,
- newSchema);
-
- // Nothing was removed, but the method did rewrite the type name.
- assertThat(rewrittenSchemaResults.mRewrittenPrefixedTypes.keySet())
- .containsExactly("package$existingDatabase/Foo");
- assertThat(rewrittenSchemaResults.mDeletedPrefixedTypes).isEmpty();
-
- // Same schema since nothing was added.
- SchemaProto expectedSchema = existingSchemaBuilder.build();
- assertThat(existingSchemaBuilder.getTypesList())
- .containsExactlyElementsIn(expectedSchema.getTypesList());
- }
-
- /**
- * Ensure that we track which types from the existing schema are deleted when a new schema is
- * set.
- */
- @Test
- public void testRewriteSchema_deleteType() throws Exception {
- SchemaProto.Builder existingSchemaBuilder =
- SchemaProto.newBuilder()
- .addTypes(
- SchemaTypeConfigProto.newBuilder()
- .setSchemaType("package$existingDatabase/Foo")
- .build());
-
- SchemaProto newSchema =
- SchemaProto.newBuilder()
- .addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType("Bar").build())
- .build();
-
- AppSearchImpl.RewrittenSchemaResults rewrittenSchemaResults =
- AppSearchImpl.rewriteSchema(
- createPrefix("package", "existingDatabase"),
- existingSchemaBuilder,
- newSchema);
-
- // Bar type was rewritten, but Foo ended up being deleted since it wasn't included in the
- // new schema.
- assertThat(rewrittenSchemaResults.mRewrittenPrefixedTypes)
- .containsKey("package$existingDatabase/Bar");
- assertThat(rewrittenSchemaResults.mRewrittenPrefixedTypes.keySet().size()).isEqualTo(1);
- assertThat(rewrittenSchemaResults.mDeletedPrefixedTypes)
- .containsExactly("package$existingDatabase/Foo");
-
- // Same schema since nothing was added.
- SchemaProto expectedSchema =
- SchemaProto.newBuilder()
- .addTypes(
- SchemaTypeConfigProto.newBuilder()
- .setSchemaType("package$existingDatabase/Bar")
- .build())
- .build();
-
- assertThat(existingSchemaBuilder.getTypesList())
- .containsExactlyElementsIn(expectedSchema.getTypesList());
- }
-
- @Test
- public void testAddDocumentTypePrefix() {
- DocumentProto insideDocument =
- DocumentProto.newBuilder()
- .setUri("inside-id")
- .setSchema("type")
- .setNamespace("namespace")
- .build();
- DocumentProto documentProto =
- DocumentProto.newBuilder()
- .setUri("id")
- .setSchema("type")
- .setNamespace("namespace")
- .addProperties(PropertyProto.newBuilder().addDocumentValues(insideDocument))
- .build();
-
- DocumentProto expectedInsideDocument =
- DocumentProto.newBuilder()
- .setUri("inside-id")
- .setSchema("package$databaseName/type")
- .setNamespace("package$databaseName/namespace")
- .build();
- DocumentProto expectedDocumentProto =
- DocumentProto.newBuilder()
- .setUri("id")
- .setSchema("package$databaseName/type")
- .setNamespace("package$databaseName/namespace")
- .addProperties(
- PropertyProto.newBuilder()
- .addDocumentValues(expectedInsideDocument))
- .build();
-
- DocumentProto.Builder actualDocument = documentProto.toBuilder();
- addPrefixToDocument(actualDocument, createPrefix("package", "databaseName"));
- assertThat(actualDocument.build()).isEqualTo(expectedDocumentProto);
- }
-
- @Test
- public void testRemoveDocumentTypePrefixes() throws Exception {
- DocumentProto insideDocument =
- DocumentProto.newBuilder()
- .setUri("inside-id")
- .setSchema("package$databaseName/type")
- .setNamespace("package$databaseName/namespace")
- .build();
- DocumentProto documentProto =
- DocumentProto.newBuilder()
- .setUri("id")
- .setSchema("package$databaseName/type")
- .setNamespace("package$databaseName/namespace")
- .addProperties(PropertyProto.newBuilder().addDocumentValues(insideDocument))
- .build();
-
- DocumentProto expectedInsideDocument =
- DocumentProto.newBuilder()
- .setUri("inside-id")
- .setSchema("type")
- .setNamespace("namespace")
- .build();
-
- DocumentProto expectedDocumentProto =
- DocumentProto.newBuilder()
- .setUri("id")
- .setSchema("type")
- .setNamespace("namespace")
- .addProperties(
- PropertyProto.newBuilder()
- .addDocumentValues(expectedInsideDocument))
- .build();
-
- DocumentProto.Builder actualDocument = documentProto.toBuilder();
- assertThat(removePrefixesFromDocument(actualDocument)).isEqualTo("package$databaseName/");
- assertThat(actualDocument.build()).isEqualTo(expectedDocumentProto);
- }
-
- @Test
- public void testRemoveDatabasesFromDocumentThrowsException() {
- // Set two different database names in the document, which should never happen
- DocumentProto documentProto =
- DocumentProto.newBuilder()
- .setUri("id")
- .setSchema("prefix1/type")
- .setNamespace("prefix2/namespace")
- .build();
-
- DocumentProto.Builder actualDocument = documentProto.toBuilder();
- AppSearchException e =
- assertThrows(
- AppSearchException.class, () -> removePrefixesFromDocument(actualDocument));
- assertThat(e).hasMessageThat().contains("Found unexpected multiple prefix names");
- }
-
- @Test
- public void testNestedRemoveDatabasesFromDocumentThrowsException() {
- // Set two different database names in the outer and inner document, which should never
- // happen.
- DocumentProto insideDocument =
- DocumentProto.newBuilder()
- .setUri("inside-id")
- .setSchema("prefix1/type")
- .setNamespace("prefix1/namespace")
- .build();
- DocumentProto documentProto =
- DocumentProto.newBuilder()
- .setUri("id")
- .setSchema("prefix2/type")
- .setNamespace("prefix2/namespace")
- .addProperties(PropertyProto.newBuilder().addDocumentValues(insideDocument))
- .build();
-
- DocumentProto.Builder actualDocument = documentProto.toBuilder();
- AppSearchException e =
- assertThrows(
- AppSearchException.class, () -> removePrefixesFromDocument(actualDocument));
- assertThat(e).hasMessageThat().contains("Found unexpected multiple prefix names");
- }
-
- @Test
- public void testTriggerCheckOptimizeByMutationSize() throws Exception {
- // Insert schema
- List<AppSearchSchema> schemas =
- Collections.singletonList(new AppSearchSchema.Builder("type").build());
- mAppSearchImpl.setSchema(
- "package",
- "database",
- schemas,
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0,
- /* setSchemaStatsBuilder= */ null);
-
- // Insert a document and then remove it to generate garbage.
- GenericDocument document = new GenericDocument.Builder<>("namespace", "id", "type").build();
- mAppSearchImpl.putDocument("package", "database", document, /*logger=*/ null);
- mAppSearchImpl.remove(
- "package", "database", "namespace", "id", /*removeStatsBuilder=*/ null);
-
- // Verify there is garbage documents.
- GetOptimizeInfoResultProto optimizeInfo = mAppSearchImpl.getOptimizeInfoResultLocked();
- assertThat(optimizeInfo.getOptimizableDocs()).isEqualTo(1);
-
- // Increase mutation counter and stop before reach the threshold
- mAppSearchImpl.checkForOptimize(
- AppSearchImpl.CHECK_OPTIMIZE_INTERVAL - 1, /*builder=*/ null);
-
- // Verify the optimize() isn't triggered.
- optimizeInfo = mAppSearchImpl.getOptimizeInfoResultLocked();
- assertThat(optimizeInfo.getOptimizableDocs()).isEqualTo(1);
-
- // Increase the counter and reach the threshold, optimize() should be triggered.
- OptimizeStats.Builder builder = new OptimizeStats.Builder();
- mAppSearchImpl.checkForOptimize(/*mutateBatchSize=*/ 1, builder);
-
- // Verify optimize() is triggered.
- optimizeInfo = mAppSearchImpl.getOptimizeInfoResultLocked();
- assertThat(optimizeInfo.getOptimizableDocs()).isEqualTo(0);
- assertThat(optimizeInfo.getEstimatedOptimizableBytes()).isEqualTo(0);
-
- // Verify the stats have been set.
- OptimizeStats oStats = builder.build();
- assertThat(oStats.getOriginalDocumentCount()).isEqualTo(1);
- assertThat(oStats.getDeletedDocumentCount()).isEqualTo(1);
- }
-
- @Test
- public void testReset() throws Exception {
- // Setup the index
- Context context = ApplicationProvider.getApplicationContext();
- File appsearchDir = mTemporaryFolder.newFolder();
- AppSearchImpl appSearchImpl =
- AppSearchImpl.create(
- appsearchDir,
- new UnlimitedLimitConfig(),
- /*initStatsBuilder=*/ null,
- ALWAYS_OPTIMIZE);
-
- // Insert schema
- List<AppSearchSchema> schemas =
- ImmutableList.of(
- new AppSearchSchema.Builder("Type1").build(),
- new AppSearchSchema.Builder("Type2").build());
- appSearchImpl.setSchema(
- context.getPackageName(),
- "database1",
- schemas,
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0,
- /* setSchemaStatsBuilder= */ null);
-
- // Insert a valid doc
- GenericDocument validDoc =
- new GenericDocument.Builder<>("namespace1", "id1", "Type1").build();
- appSearchImpl.putDocument(
- context.getPackageName(), "database1", validDoc, /*logger=*/ null);
-
- // Query it via global query. We use the same code again later so this is to make sure we
- // have our global query configured right.
- SearchResultPage results =
- appSearchImpl.globalQuery(
- /*queryExpression=*/ "",
- new SearchSpec.Builder().addFilterSchemas("Type1").build(),
- context.getPackageName(),
- /*visibilityStore=*/ null,
- Process.INVALID_UID,
- /*callerHasSystemAccess=*/ false,
- /*logger=*/ null);
- assertThat(results.getResults()).hasSize(1);
- assertThat(results.getResults().get(0).getGenericDocument()).isEqualTo(validDoc);
-
- // Create a doc with a malformed namespace
- DocumentProto invalidDoc =
- DocumentProto.newBuilder()
- .setNamespace("invalidNamespace")
- .setUri("id2")
- .setSchema(context.getPackageName() + "$database1/Type1")
- .build();
- AppSearchException e =
- assertThrows(
- AppSearchException.class,
- () -> PrefixUtil.getPrefix(invalidDoc.getNamespace()));
- assertThat(e)
- .hasMessageThat()
- .isEqualTo(
- "The prefixed value \"invalidNamespace\" doesn't contain a valid database"
- + " name");
-
- // Insert the invalid doc with an invalid namespace right into icing
- PutResultProto putResultProto = appSearchImpl.mIcingSearchEngineLocked.put(invalidDoc);
- assertThat(putResultProto.getStatus().getCode()).isEqualTo(StatusProto.Code.OK);
-
- // Initialize AppSearchImpl. This should cause a reset.
- InitializeStats.Builder initStatsBuilder = new InitializeStats.Builder();
- appSearchImpl.close();
- appSearchImpl =
- AppSearchImpl.create(
- appsearchDir,
- new UnlimitedLimitConfig(),
- initStatsBuilder,
- ALWAYS_OPTIMIZE);
-
- // Check recovery state
- InitializeStats initStats = initStatsBuilder.build();
- assertThat(initStats).isNotNull();
- assertThat(initStats.getStatusCode()).isEqualTo(AppSearchResult.RESULT_INTERNAL_ERROR);
- assertThat(initStats.hasDeSync()).isFalse();
- assertThat(initStats.getDocumentStoreRecoveryCause())
- .isEqualTo(InitializeStats.RECOVERY_CAUSE_NONE);
- assertThat(initStats.getIndexRestorationCause())
- .isEqualTo(InitializeStats.RECOVERY_CAUSE_NONE);
- assertThat(initStats.getSchemaStoreRecoveryCause())
- .isEqualTo(InitializeStats.RECOVERY_CAUSE_NONE);
- assertThat(initStats.getDocumentStoreDataStatus())
- .isEqualTo(InitializeStats.DOCUMENT_STORE_DATA_STATUS_NO_DATA_LOSS);
- assertThat(initStats.hasReset()).isTrue();
- assertThat(initStats.getResetStatusCode()).isEqualTo(AppSearchResult.RESULT_OK);
-
- // Make sure all our data is gone
- assertThat(appSearchImpl.getSchema(context.getPackageName(), "database1").getSchemas())
- .isEmpty();
- results =
- appSearchImpl.globalQuery(
- /*queryExpression=*/ "",
- new SearchSpec.Builder().addFilterSchemas("Type1").build(),
- context.getPackageName(),
- /*visibilityStore=*/ null,
- Process.INVALID_UID,
- /*callerHasSystemAccess=*/ false,
- /*logger=*/ null);
- assertThat(results.getResults()).isEmpty();
-
- // Make sure the index can now be used successfully
- appSearchImpl.setSchema(
- context.getPackageName(),
- "database1",
- Collections.singletonList(new AppSearchSchema.Builder("Type1").build()),
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0,
- /* setSchemaStatsBuilder= */ null);
-
- // Insert a valid doc
- appSearchImpl.putDocument(
- context.getPackageName(), "database1", validDoc, /*logger=*/ null);
-
- // Query it via global query.
- results =
- appSearchImpl.globalQuery(
- /*queryExpression=*/ "",
- new SearchSpec.Builder().addFilterSchemas("Type1").build(),
- context.getPackageName(),
- /*visibilityStore=*/ null,
- Process.INVALID_UID,
- /*callerHasSystemAccess=*/ false,
- /*logger=*/ null);
- assertThat(results.getResults()).hasSize(1);
- assertThat(results.getResults().get(0).getGenericDocument()).isEqualTo(validDoc);
- }
-
- @Test
- public void testRewriteSearchSpec_oneInstance() throws Exception {
- SearchSpecProto.Builder searchSpecProto = SearchSpecProto.newBuilder().setQuery("");
-
- // Insert schema
- List<AppSearchSchema> schemas =
- Collections.singletonList(new AppSearchSchema.Builder("type").build());
- mAppSearchImpl.setSchema(
- "package",
- "database",
- schemas,
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0,
- /* setSchemaStatsBuilder= */ null);
-
- // Insert document
- GenericDocument document = new GenericDocument.Builder<>("namespace", "id", "type").build();
- mAppSearchImpl.putDocument("package", "database", document, /*logger=*/ null);
-
- // Rewrite SearchSpec
- mAppSearchImpl.rewriteSearchSpecForPrefixesLocked(
- searchSpecProto,
- Collections.singleton(createPrefix("package", "database")),
- ImmutableSet.of("package$database/type"));
- assertThat(searchSpecProto.getSchemaTypeFiltersList())
- .containsExactly("package$database/type");
- assertThat(searchSpecProto.getNamespaceFiltersList())
- .containsExactly("package$database/namespace");
- }
-
- @Test
- public void testRewriteSearchSpec_twoInstances() throws Exception {
- SearchSpecProto.Builder searchSpecProto = SearchSpecProto.newBuilder().setQuery("");
-
- // Insert schema
- List<AppSearchSchema> schemas =
- ImmutableList.of(
- new AppSearchSchema.Builder("typeA").build(),
- new AppSearchSchema.Builder("typeB").build());
- mAppSearchImpl.setSchema(
- "package",
- "database1",
- schemas,
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0,
- /* setSchemaStatsBuilder= */ null);
- mAppSearchImpl.setSchema(
- "package",
- "database2",
- schemas,
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0,
- /* setSchemaStatsBuilder= */ null);
-
- // Insert documents
- GenericDocument document1 =
- new GenericDocument.Builder<>("namespace", "id", "typeA").build();
- mAppSearchImpl.putDocument("package", "database1", document1, /*logger=*/ null);
-
- GenericDocument document2 =
- new GenericDocument.Builder<>("namespace", "id", "typeB").build();
- mAppSearchImpl.putDocument("package", "database2", document2, /*logger=*/ null);
-
- // Rewrite SearchSpec
- mAppSearchImpl.rewriteSearchSpecForPrefixesLocked(
- searchSpecProto,
- ImmutableSet.of(
- createPrefix("package", "database1"), createPrefix("package", "database2")),
- ImmutableSet.of(
- "package$database1/typeA", "package$database1/typeB",
- "package$database2/typeA", "package$database2/typeB"));
- assertThat(searchSpecProto.getSchemaTypeFiltersList())
- .containsExactly(
- "package$database1/typeA",
- "package$database1/typeB",
- "package$database2/typeA",
- "package$database2/typeB");
- assertThat(searchSpecProto.getNamespaceFiltersList())
- .containsExactly("package$database1/namespace", "package$database2/namespace");
- }
-
- @Test
- public void testRewriteSearchSpec_ignoresSearchSpecSchemaFilters() throws Exception {
- SearchSpecProto.Builder searchSpecProto =
- SearchSpecProto.newBuilder().setQuery("").addSchemaTypeFilters("type");
-
- // Insert schema
- List<AppSearchSchema> schemas =
- Collections.singletonList(new AppSearchSchema.Builder("type").build());
- mAppSearchImpl.setSchema(
- "package",
- "database",
- schemas,
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0,
- /* setSchemaStatsBuilder= */ null);
-
- // Insert document
- GenericDocument document = new GenericDocument.Builder<>("namespace", "id", "type").build();
- mAppSearchImpl.putDocument("package", "database", document, /*logger=*/ null);
-
- // If 'allowedPrefixedSchemas' is empty, this returns false since there's nothing to
- // search over. Despite the searchSpecProto having schema type filters.
- assertThat(
- mAppSearchImpl.rewriteSearchSpecForPrefixesLocked(
- searchSpecProto,
- Collections.singleton(createPrefix("package", "database")),
- /*allowedPrefixedSchemas=*/ Collections.emptySet()))
- .isFalse();
- }
-
- @Test
- public void testQueryEmptyDatabase() throws Exception {
- SearchSpec searchSpec =
- new SearchSpec.Builder().setTermMatch(TermMatchType.Code.PREFIX_VALUE).build();
- SearchResultPage searchResultPage =
- mAppSearchImpl.query("package", "EmptyDatabase", "", searchSpec, /*logger=*/ null);
- assertThat(searchResultPage.getResults()).isEmpty();
- }
-
- /**
- * TODO(b/169883602): This should be an integration test at the cts-level. This is a short-term
- * test until we have official support for multiple-apps indexing at once.
- */
- @Test
- public void testQueryWithMultiplePackages_noPackageFilters() throws Exception {
- // Insert package1 schema
- List<AppSearchSchema> schema1 =
- ImmutableList.of(new AppSearchSchema.Builder("schema1").build());
- mAppSearchImpl.setSchema(
- "package1",
- "database1",
- schema1,
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0,
- /* setSchemaStatsBuilder= */ null);
-
- // Insert package2 schema
- List<AppSearchSchema> schema2 =
- ImmutableList.of(new AppSearchSchema.Builder("schema2").build());
- mAppSearchImpl.setSchema(
- "package2",
- "database2",
- schema2,
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0,
- /* setSchemaStatsBuilder= */ null);
-
- // Insert package1 document
- GenericDocument document =
- new GenericDocument.Builder<>("namespace", "id", "schema1").build();
- mAppSearchImpl.putDocument("package1", "database1", document, /*logger=*/ null);
-
- // No query filters specified, package2 shouldn't be able to query for package1's documents.
- SearchSpec searchSpec =
- new SearchSpec.Builder().setTermMatch(TermMatchType.Code.PREFIX_VALUE).build();
- SearchResultPage searchResultPage =
- mAppSearchImpl.query("package2", "database2", "", searchSpec, /*logger=*/ null);
- assertThat(searchResultPage.getResults()).isEmpty();
-
- // Insert package2 document
- document = new GenericDocument.Builder<>("namespace", "id", "schema2").build();
- mAppSearchImpl.putDocument("package2", "database2", document, /*logger=*/ null);
-
- // No query filters specified. package2 should only get its own documents back.
- searchResultPage =
- mAppSearchImpl.query("package2", "database2", "", searchSpec, /*logger=
- */ null);
- assertThat(searchResultPage.getResults()).hasSize(1);
- assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document);
- }
-
- /**
- * TODO(b/169883602): This should be an integration test at the cts-level. This is a short-term
- * test until we have official support for multiple-apps indexing at once.
- */
- @Test
- public void testQueryWithMultiplePackages_withPackageFilters() throws Exception {
- // Insert package1 schema
- List<AppSearchSchema> schema1 =
- ImmutableList.of(new AppSearchSchema.Builder("schema1").build());
- mAppSearchImpl.setSchema(
- "package1",
- "database1",
- schema1,
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0,
- /* setSchemaStatsBuilder= */ null);
-
- // Insert package2 schema
- List<AppSearchSchema> schema2 =
- ImmutableList.of(new AppSearchSchema.Builder("schema2").build());
- mAppSearchImpl.setSchema(
- "package2",
- "database2",
- schema2,
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0,
- /* setSchemaStatsBuilder= */ null);
-
- // Insert package1 document
- GenericDocument document =
- new GenericDocument.Builder<>("namespace", "id", "schema1").build();
- mAppSearchImpl.putDocument("package1", "database1", document, /*logger=*/ null);
-
- // "package1" filter specified, but package2 shouldn't be able to query for package1's
- // documents.
- SearchSpec searchSpec =
- new SearchSpec.Builder()
- .setTermMatch(TermMatchType.Code.PREFIX_VALUE)
- .addFilterPackageNames("package1")
- .build();
- SearchResultPage searchResultPage =
- mAppSearchImpl.query("package2", "database2", "", searchSpec, /*logger=*/ null);
- assertThat(searchResultPage.getResults()).isEmpty();
-
- // Insert package2 document
- document = new GenericDocument.Builder<>("namespace", "id", "schema2").build();
- mAppSearchImpl.putDocument("package2", "database2", document, /*logger=*/ null);
-
- // "package2" filter specified, package2 should only get its own documents back.
- searchSpec =
- new SearchSpec.Builder()
- .setTermMatch(TermMatchType.Code.PREFIX_VALUE)
- .addFilterPackageNames("package2")
- .build();
- searchResultPage =
- mAppSearchImpl.query("package2", "database2", "", searchSpec, /*logger=
- */ null);
- assertThat(searchResultPage.getResults()).hasSize(1);
- assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document);
- }
-
- @Test
- public void testGlobalQueryEmptyDatabase() throws Exception {
- SearchSpec searchSpec =
- new SearchSpec.Builder().setTermMatch(TermMatchType.Code.PREFIX_VALUE).build();
- SearchResultPage searchResultPage =
- mAppSearchImpl.globalQuery(
- "",
- searchSpec,
- /*callerPackageName=*/ "",
- /*visibilityStore=*/ null,
- Process.INVALID_UID,
- /*callerHasSystemAccess=*/ false,
- /*logger=*/ null);
- assertThat(searchResultPage.getResults()).isEmpty();
- }
-
- @Test
- public void testGetNextPageToken_query() throws Exception {
- // Insert package1 schema
- List<AppSearchSchema> schema1 =
- ImmutableList.of(new AppSearchSchema.Builder("schema1").build());
- mAppSearchImpl.setSchema(
- "package1",
- "database1",
- schema1,
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0,
- /* setSchemaStatsBuilder= */ null);
-
- // Insert two package1 documents
- GenericDocument document1 =
- new GenericDocument.Builder<>("namespace", "id1", "schema1").build();
- GenericDocument document2 =
- new GenericDocument.Builder<>("namespace", "id2", "schema1").build();
- mAppSearchImpl.putDocument("package1", "database1", document1, /*logger=*/ null);
- mAppSearchImpl.putDocument("package1", "database1", document2, /*logger=*/ null);
-
- // Query for only 1 result per page
- SearchSpec searchSpec =
- new SearchSpec.Builder()
- .setTermMatch(TermMatchType.Code.PREFIX_VALUE)
- .setResultCountPerPage(1)
- .build();
- SearchResultPage searchResultPage =
- mAppSearchImpl.query("package1", "database1", "", searchSpec, /*logger=*/ null);
-
- // Document2 will come first because it was inserted last and default return order is
- // most recent.
- assertThat(searchResultPage.getResults()).hasSize(1);
- assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document2);
-
- long nextPageToken = searchResultPage.getNextPageToken();
- searchResultPage =
- mAppSearchImpl.getNextPage("package1", nextPageToken, /*statsBuilder=*/ null);
- assertThat(searchResultPage.getResults()).hasSize(1);
- assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document1);
- }
-
- @Test
- public void testGetNextPageWithDifferentPackage_query() throws Exception {
- // Insert package1 schema
- List<AppSearchSchema> schema1 =
- ImmutableList.of(new AppSearchSchema.Builder("schema1").build());
- mAppSearchImpl.setSchema(
- "package1",
- "database1",
- schema1,
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0,
- /* setSchemaStatsBuilder= */ null);
-
- // Insert two package1 documents
- GenericDocument document1 =
- new GenericDocument.Builder<>("namespace", "id1", "schema1").build();
- GenericDocument document2 =
- new GenericDocument.Builder<>("namespace", "id2", "schema1").build();
- mAppSearchImpl.putDocument("package1", "database1", document1, /*logger=*/ null);
- mAppSearchImpl.putDocument("package1", "database1", document2, /*logger=*/ null);
-
- // Query for only 1 result per page
- SearchSpec searchSpec =
- new SearchSpec.Builder()
- .setTermMatch(TermMatchType.Code.PREFIX_VALUE)
- .setResultCountPerPage(1)
- .build();
- SearchResultPage searchResultPage =
- mAppSearchImpl.query("package1", "database1", "", searchSpec, /*logger=*/ null);
-
- // Document2 will come first because it was inserted last and default return order is
- // most recent.
- assertThat(searchResultPage.getResults()).hasSize(1);
- assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document2);
-
- long nextPageToken = searchResultPage.getNextPageToken();
-
- // Try getting next page with the wrong package, package2
- AppSearchException e =
- assertThrows(
- AppSearchException.class,
- () ->
- mAppSearchImpl.getNextPage(
- "package2", nextPageToken, /*statsBuilder=*/ null));
- assertThat(e)
- .hasMessageThat()
- .contains("Package \"package2\" cannot use nextPageToken: " + nextPageToken);
- assertThat(e.getResultCode()).isEqualTo(AppSearchResult.RESULT_SECURITY_ERROR);
-
- // Can continue getting next page for package1
- searchResultPage =
- mAppSearchImpl.getNextPage("package1", nextPageToken, /*statsBuilder=*/ null);
- assertThat(searchResultPage.getResults()).hasSize(1);
- assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document1);
- }
-
- @Test
- public void testGetNextPageToken_globalQuery() throws Exception {
- // Insert package1 schema
- List<AppSearchSchema> schema1 =
- ImmutableList.of(new AppSearchSchema.Builder("schema1").build());
- mAppSearchImpl.setSchema(
- "package1",
- "database1",
- schema1,
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0,
- /* setSchemaStatsBuilder= */ null);
-
- // Insert two package1 documents
- GenericDocument document1 =
- new GenericDocument.Builder<>("namespace", "id1", "schema1").build();
- GenericDocument document2 =
- new GenericDocument.Builder<>("namespace", "id2", "schema1").build();
- mAppSearchImpl.putDocument("package1", "database1", document1, /*logger=*/ null);
- mAppSearchImpl.putDocument("package1", "database1", document2, /*logger=*/ null);
-
- // Query for only 1 result per page
- SearchSpec searchSpec =
- new SearchSpec.Builder()
- .setTermMatch(TermMatchType.Code.PREFIX_VALUE)
- .setResultCountPerPage(1)
- .build();
- SearchResultPage searchResultPage =
- mAppSearchImpl.globalQuery(
- /*queryExpression=*/ "",
- searchSpec,
- "package1",
- /*visibilityStore=*/ null,
- Process.myUid(),
- /*callerHasSystemAccess=*/ false,
- /*logger=*/ null);
-
- // Document2 will come first because it was inserted last and default return order is
- // most recent.
- assertThat(searchResultPage.getResults()).hasSize(1);
- assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document2);
-
- long nextPageToken = searchResultPage.getNextPageToken();
- searchResultPage =
- mAppSearchImpl.getNextPage("package1", nextPageToken, /*statsBuilder=*/ null);
- assertThat(searchResultPage.getResults()).hasSize(1);
- assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document1);
- }
-
- @Test
- public void testGetNextPageWithDifferentPackage_globalQuery() throws Exception {
- // Insert package1 schema
- List<AppSearchSchema> schema1 =
- ImmutableList.of(new AppSearchSchema.Builder("schema1").build());
- mAppSearchImpl.setSchema(
- "package1",
- "database1",
- schema1,
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0,
- /* setSchemaStatsBuilder= */ null);
-
- // Insert two package1 documents
- GenericDocument document1 =
- new GenericDocument.Builder<>("namespace", "id1", "schema1").build();
- GenericDocument document2 =
- new GenericDocument.Builder<>("namespace", "id2", "schema1").build();
- mAppSearchImpl.putDocument("package1", "database1", document1, /*logger=*/ null);
- mAppSearchImpl.putDocument("package1", "database1", document2, /*logger=*/ null);
-
- // Query for only 1 result per page
- SearchSpec searchSpec =
- new SearchSpec.Builder()
- .setTermMatch(TermMatchType.Code.PREFIX_VALUE)
- .setResultCountPerPage(1)
- .build();
- SearchResultPage searchResultPage =
- mAppSearchImpl.globalQuery(
- /*queryExpression=*/ "",
- searchSpec,
- "package1",
- /*visibilityStore=*/ null,
- Process.myUid(),
- /*callerHasSystemAccess=*/ false,
- /*logger=*/ null);
-
- // Document2 will come first because it was inserted last and default return order is
- // most recent.
- assertThat(searchResultPage.getResults()).hasSize(1);
- assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document2);
-
- long nextPageToken = searchResultPage.getNextPageToken();
-
- // Try getting next page with the wrong package, package2
- AppSearchException e =
- assertThrows(
- AppSearchException.class,
- () ->
- mAppSearchImpl.getNextPage(
- "package2", nextPageToken, /*statsBuilder=*/ null));
- assertThat(e)
- .hasMessageThat()
- .contains("Package \"package2\" cannot use nextPageToken: " + nextPageToken);
- assertThat(e.getResultCode()).isEqualTo(AppSearchResult.RESULT_SECURITY_ERROR);
-
- // Can continue getting next page for package1
- searchResultPage =
- mAppSearchImpl.getNextPage("package1", nextPageToken, /*statsBuilder=*/ null);
- assertThat(searchResultPage.getResults()).hasSize(1);
- assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document1);
- }
-
- @Test
- public void testInvalidateNextPageToken_query() throws Exception {
- // Insert package1 schema
- List<AppSearchSchema> schema1 =
- ImmutableList.of(new AppSearchSchema.Builder("schema1").build());
- mAppSearchImpl.setSchema(
- "package1",
- "database1",
- schema1,
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0,
- /* setSchemaStatsBuilder= */ null);
-
- // Insert two package1 documents
- GenericDocument document1 =
- new GenericDocument.Builder<>("namespace", "id1", "schema1").build();
- GenericDocument document2 =
- new GenericDocument.Builder<>("namespace", "id2", "schema1").build();
- mAppSearchImpl.putDocument("package1", "database1", document1, /*logger=*/ null);
- mAppSearchImpl.putDocument("package1", "database1", document2, /*logger=*/ null);
-
- // Query for only 1 result per page
- SearchSpec searchSpec =
- new SearchSpec.Builder()
- .setTermMatch(TermMatchType.Code.PREFIX_VALUE)
- .setResultCountPerPage(1)
- .build();
- SearchResultPage searchResultPage =
- mAppSearchImpl.query("package1", "database1", "", searchSpec, /*logger=*/ null);
-
- // Document2 will come first because it was inserted last and default return order is
- // most recent.
- assertThat(searchResultPage.getResults()).hasSize(1);
- assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document2);
-
- long nextPageToken = searchResultPage.getNextPageToken();
-
- // Invalidate the token
- mAppSearchImpl.invalidateNextPageToken("package1", nextPageToken);
-
- // Can't get next page because we invalidated the token.
- AppSearchException e =
- assertThrows(
- AppSearchException.class,
- () ->
- mAppSearchImpl.getNextPage(
- "package1", nextPageToken, /*statsBuilder=*/ null));
- assertThat(e)
- .hasMessageThat()
- .contains("Package \"package1\" cannot use nextPageToken: " + nextPageToken);
- assertThat(e.getResultCode()).isEqualTo(AppSearchResult.RESULT_SECURITY_ERROR);
- }
-
- @Test
- public void testInvalidateNextPageTokenWithDifferentPackage_query() throws Exception {
- // Insert package1 schema
- List<AppSearchSchema> schema1 =
- ImmutableList.of(new AppSearchSchema.Builder("schema1").build());
- mAppSearchImpl.setSchema(
- "package1",
- "database1",
- schema1,
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0,
- /* setSchemaStatsBuilder= */ null);
-
- // Insert two package1 documents
- GenericDocument document1 =
- new GenericDocument.Builder<>("namespace", "id1", "schema1").build();
- GenericDocument document2 =
- new GenericDocument.Builder<>("namespace", "id2", "schema1").build();
- mAppSearchImpl.putDocument("package1", "database1", document1, /*logger=*/ null);
- mAppSearchImpl.putDocument("package1", "database1", document2, /*logger=*/ null);
-
- // Query for only 1 result per page
- SearchSpec searchSpec =
- new SearchSpec.Builder()
- .setTermMatch(TermMatchType.Code.PREFIX_VALUE)
- .setResultCountPerPage(1)
- .build();
- SearchResultPage searchResultPage =
- mAppSearchImpl.query("package1", "database1", "", searchSpec, /*logger=*/ null);
-
- // Document2 will come first because it was inserted last and default return order is
- // most recent.
- assertThat(searchResultPage.getResults()).hasSize(1);
- assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document2);
-
- long nextPageToken = searchResultPage.getNextPageToken();
-
- // Try getting next page with the wrong package, package2
- AppSearchException e =
- assertThrows(
- AppSearchException.class,
- () -> mAppSearchImpl.invalidateNextPageToken("package2", nextPageToken));
- assertThat(e)
- .hasMessageThat()
- .contains("Package \"package2\" cannot use nextPageToken: " + nextPageToken);
- assertThat(e.getResultCode()).isEqualTo(AppSearchResult.RESULT_SECURITY_ERROR);
-
- // Can continue getting next page for package1
- searchResultPage =
- mAppSearchImpl.getNextPage("package1", nextPageToken, /*statsBuilder=*/ null);
- assertThat(searchResultPage.getResults()).hasSize(1);
- assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document1);
- }
-
- @Test
- public void testInvalidateNextPageToken_globalQuery() throws Exception {
- // Insert package1 schema
- List<AppSearchSchema> schema1 =
- ImmutableList.of(new AppSearchSchema.Builder("schema1").build());
- mAppSearchImpl.setSchema(
- "package1",
- "database1",
- schema1,
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0,
- /* setSchemaStatsBuilder= */ null);
-
- // Insert two package1 documents
- GenericDocument document1 =
- new GenericDocument.Builder<>("namespace", "id1", "schema1").build();
- GenericDocument document2 =
- new GenericDocument.Builder<>("namespace", "id2", "schema1").build();
- mAppSearchImpl.putDocument("package1", "database1", document1, /*logger=*/ null);
- mAppSearchImpl.putDocument("package1", "database1", document2, /*logger=*/ null);
-
- // Query for only 1 result per page
- SearchSpec searchSpec =
- new SearchSpec.Builder()
- .setTermMatch(TermMatchType.Code.PREFIX_VALUE)
- .setResultCountPerPage(1)
- .build();
- SearchResultPage searchResultPage =
- mAppSearchImpl.globalQuery(
- /*queryExpression=*/ "",
- searchSpec,
- "package1",
- /*visibilityStore=*/ null,
- Process.myUid(),
- /*callerHasSystemAccess=*/ false,
- /*logger=*/ null);
-
- // Document2 will come first because it was inserted last and default return order is
- // most recent.
- assertThat(searchResultPage.getResults()).hasSize(1);
- assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document2);
-
- long nextPageToken = searchResultPage.getNextPageToken();
-
- // Invalidate the token
- mAppSearchImpl.invalidateNextPageToken("package1", nextPageToken);
-
- // Can't get next page because we invalidated the token.
- AppSearchException e =
- assertThrows(
- AppSearchException.class,
- () ->
- mAppSearchImpl.getNextPage(
- "package1", nextPageToken, /*statsBuilder=*/ null));
- assertThat(e)
- .hasMessageThat()
- .contains("Package \"package1\" cannot use nextPageToken: " + nextPageToken);
- assertThat(e.getResultCode()).isEqualTo(AppSearchResult.RESULT_SECURITY_ERROR);
- }
-
- @Test
- public void testInvalidateNextPageTokenWithDifferentPackage_globalQuery() throws Exception {
- // Insert package1 schema
- List<AppSearchSchema> schema1 =
- ImmutableList.of(new AppSearchSchema.Builder("schema1").build());
- mAppSearchImpl.setSchema(
- "package1",
- "database1",
- schema1,
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0,
- /* setSchemaStatsBuilder= */ null);
-
- // Insert two package1 documents
- GenericDocument document1 =
- new GenericDocument.Builder<>("namespace", "id1", "schema1").build();
- GenericDocument document2 =
- new GenericDocument.Builder<>("namespace", "id2", "schema1").build();
- mAppSearchImpl.putDocument("package1", "database1", document1, /*logger=*/ null);
- mAppSearchImpl.putDocument("package1", "database1", document2, /*logger=*/ null);
-
- // Query for only 1 result per page
- SearchSpec searchSpec =
- new SearchSpec.Builder()
- .setTermMatch(TermMatchType.Code.PREFIX_VALUE)
- .setResultCountPerPage(1)
- .build();
- SearchResultPage searchResultPage =
- mAppSearchImpl.globalQuery(
- /*queryExpression=*/ "",
- searchSpec,
- "package1",
- /*visibilityStore=*/ null,
- Process.myUid(),
- /*callerHasSystemAccess=*/ false,
- /*logger=*/ null);
-
- // Document2 will come first because it was inserted last and default return order is
- // most recent.
- assertThat(searchResultPage.getResults()).hasSize(1);
- assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document2);
-
- long nextPageToken = searchResultPage.getNextPageToken();
-
- // Try getting next page with the wrong package, package2
- AppSearchException e =
- assertThrows(
- AppSearchException.class,
- () -> mAppSearchImpl.invalidateNextPageToken("package2", nextPageToken));
- assertThat(e)
- .hasMessageThat()
- .contains("Package \"package2\" cannot use nextPageToken: " + nextPageToken);
- assertThat(e.getResultCode()).isEqualTo(AppSearchResult.RESULT_SECURITY_ERROR);
-
- // Can continue getting next page for package1
- searchResultPage =
- mAppSearchImpl.getNextPage("package1", nextPageToken, /*statsBuilder=*/ null);
- assertThat(searchResultPage.getResults()).hasSize(1);
- assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document1);
- }
-
- @Test
- public void testRemoveEmptyDatabase_noExceptionThrown() throws Exception {
- SearchSpec searchSpec =
- new SearchSpec.Builder()
- .addFilterSchemas("FakeType")
- .setTermMatch(TermMatchType.Code.PREFIX_VALUE)
- .build();
- mAppSearchImpl.removeByQuery(
- "package", "EmptyDatabase", "", searchSpec, /*statsBuilder=*/ null);
-
- searchSpec =
- new SearchSpec.Builder()
- .addFilterNamespaces("FakeNamespace")
- .setTermMatch(TermMatchType.Code.PREFIX_VALUE)
- .build();
- mAppSearchImpl.removeByQuery(
- "package", "EmptyDatabase", "", searchSpec, /*statsBuilder=*/ null);
-
- searchSpec = new SearchSpec.Builder().setTermMatch(TermMatchType.Code.PREFIX_VALUE).build();
- mAppSearchImpl.removeByQuery(
- "package", "EmptyDatabase", "", searchSpec, /*statsBuilder=*/ null);
- }
-
- @Test
- public void testSetSchema() throws Exception {
- List<SchemaTypeConfigProto> existingSchemas =
- mAppSearchImpl.getSchemaProtoLocked().getTypesList();
-
- List<AppSearchSchema> schemas =
- Collections.singletonList(new AppSearchSchema.Builder("Email").build());
- // Set schema Email to AppSearch database1
- mAppSearchImpl.setSchema(
- "package",
- "database1",
- schemas,
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0,
- /* setSchemaStatsBuilder= */ null);
-
- // Create expected schemaType proto.
- SchemaProto expectedProto =
- SchemaProto.newBuilder()
- .addTypes(
- SchemaTypeConfigProto.newBuilder()
- .setSchemaType("package$database1/Email")
- .setVersion(0))
- .build();
-
- List<SchemaTypeConfigProto> expectedTypes = new ArrayList<>();
- expectedTypes.addAll(existingSchemas);
- expectedTypes.addAll(expectedProto.getTypesList());
- assertThat(mAppSearchImpl.getSchemaProtoLocked().getTypesList())
- .containsExactlyElementsIn(expectedTypes);
- }
-
- @Test
- public void testSetSchema_incompatible() throws Exception {
- List<AppSearchSchema> oldSchemas = new ArrayList<>();
- oldSchemas.add(
- new AppSearchSchema.Builder("Email")
- .addProperty(
- new AppSearchSchema.StringPropertyConfig.Builder("foo")
- .setCardinality(
- AppSearchSchema.PropertyConfig.CARDINALITY_REQUIRED)
- .setTokenizerType(
- AppSearchSchema.StringPropertyConfig
- .TOKENIZER_TYPE_PLAIN)
- .setIndexingType(
- AppSearchSchema.StringPropertyConfig
- .INDEXING_TYPE_PREFIXES)
- .build())
- .build());
- oldSchemas.add(new AppSearchSchema.Builder("Text").build());
- // Set schema Email to AppSearch database1
- mAppSearchImpl.setSchema(
- "package",
- "database1",
- oldSchemas,
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0,
- /* setSchemaStatsBuilder= */ null);
-
- // Create incompatible schema
- List<AppSearchSchema> newSchemas =
- Collections.singletonList(new AppSearchSchema.Builder("Email").build());
-
- // set email incompatible and delete text
- SetSchemaResponse setSchemaResponse =
- mAppSearchImpl.setSchema(
- "package",
- "database1",
- newSchemas,
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ true,
- /*version=*/ 0,
- /* setSchemaStatsBuilder= */ null);
- assertThat(setSchemaResponse.getDeletedTypes()).containsExactly("Text");
- assertThat(setSchemaResponse.getIncompatibleTypes()).containsExactly("Email");
- }
-
- @Test
- public void testRemoveSchema() throws Exception {
- List<SchemaTypeConfigProto> existingSchemas =
- mAppSearchImpl.getSchemaProtoLocked().getTypesList();
-
- List<AppSearchSchema> schemas =
- ImmutableList.of(
- new AppSearchSchema.Builder("Email").build(),
- new AppSearchSchema.Builder("Document").build());
- // Set schema Email and Document to AppSearch database1
- mAppSearchImpl.setSchema(
- "package",
- "database1",
- schemas,
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0,
- /* setSchemaStatsBuilder= */ null);
-
- // Create expected schemaType proto.
- SchemaProto expectedProto =
- SchemaProto.newBuilder()
- .addTypes(
- SchemaTypeConfigProto.newBuilder()
- .setSchemaType("package$database1/Email")
- .setVersion(0))
- .addTypes(
- SchemaTypeConfigProto.newBuilder()
- .setSchemaType("package$database1/Document")
- .setVersion(0))
- .build();
-
- // Check both schema Email and Document saved correctly.
- List<SchemaTypeConfigProto> expectedTypes = new ArrayList<>();
- expectedTypes.addAll(existingSchemas);
- expectedTypes.addAll(expectedProto.getTypesList());
- assertThat(mAppSearchImpl.getSchemaProtoLocked().getTypesList())
- .containsExactlyElementsIn(expectedTypes);
-
- final List<AppSearchSchema> finalSchemas =
- Collections.singletonList(new AppSearchSchema.Builder("Email").build());
- SetSchemaResponse setSchemaResponse =
- mAppSearchImpl.setSchema(
- "package",
- "database1",
- finalSchemas,
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0,
- /* setSchemaStatsBuilder= */ null);
- // Check the Document type has been deleted.
- assertThat(setSchemaResponse.getDeletedTypes()).containsExactly("Document");
-
- // ForceOverride to delete.
- mAppSearchImpl.setSchema(
- "package",
- "database1",
- finalSchemas,
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ true,
- /*version=*/ 0,
- /* setSchemaStatsBuilder= */ null);
-
- // Check Document schema is removed.
- expectedProto =
- SchemaProto.newBuilder()
- .addTypes(
- SchemaTypeConfigProto.newBuilder()
- .setSchemaType("package$database1/Email")
- .setVersion(0))
- .build();
-
- expectedTypes = new ArrayList<>();
- expectedTypes.addAll(existingSchemas);
- expectedTypes.addAll(expectedProto.getTypesList());
- assertThat(mAppSearchImpl.getSchemaProtoLocked().getTypesList())
- .containsExactlyElementsIn(expectedTypes);
- }
-
- @Test
- public void testRemoveSchema_differentDataBase() throws Exception {
- List<SchemaTypeConfigProto> existingSchemas =
- mAppSearchImpl.getSchemaProtoLocked().getTypesList();
-
- // Create schemas
- List<AppSearchSchema> schemas =
- ImmutableList.of(
- new AppSearchSchema.Builder("Email").build(),
- new AppSearchSchema.Builder("Document").build());
-
- // Set schema Email and Document to AppSearch database1 and 2
- mAppSearchImpl.setSchema(
- "package",
- "database1",
- schemas,
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0,
- /* setSchemaStatsBuilder= */ null);
- mAppSearchImpl.setSchema(
- "package",
- "database2",
- schemas,
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0,
- /* setSchemaStatsBuilder= */ null);
-
- // Create expected schemaType proto.
- SchemaProto expectedProto =
- SchemaProto.newBuilder()
- .addTypes(
- SchemaTypeConfigProto.newBuilder()
- .setSchemaType("package$database1/Email")
- .setVersion(0))
- .addTypes(
- SchemaTypeConfigProto.newBuilder()
- .setSchemaType("package$database1/Document")
- .setVersion(0))
- .addTypes(
- SchemaTypeConfigProto.newBuilder()
- .setSchemaType("package$database2/Email")
- .setVersion(0))
- .addTypes(
- SchemaTypeConfigProto.newBuilder()
- .setSchemaType("package$database2/Document")
- .setVersion(0))
- .build();
-
- // Check Email and Document is saved in database 1 and 2 correctly.
- List<SchemaTypeConfigProto> expectedTypes = new ArrayList<>();
- expectedTypes.addAll(existingSchemas);
- expectedTypes.addAll(expectedProto.getTypesList());
- assertThat(mAppSearchImpl.getSchemaProtoLocked().getTypesList())
- .containsExactlyElementsIn(expectedTypes);
-
- // Save only Email to database1 this time.
- schemas = Collections.singletonList(new AppSearchSchema.Builder("Email").build());
- mAppSearchImpl.setSchema(
- "package",
- "database1",
- schemas,
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ true,
- /*version=*/ 0,
- /* setSchemaStatsBuilder= */ null);
-
- // Create expected schemaType list, database 1 should only contain Email but database 2
- // remains in same.
- expectedProto =
- SchemaProto.newBuilder()
- .addTypes(
- SchemaTypeConfigProto.newBuilder()
- .setSchemaType("package$database1/Email")
- .setVersion(0))
- .addTypes(
- SchemaTypeConfigProto.newBuilder()
- .setSchemaType("package$database2/Email")
- .setVersion(0))
- .addTypes(
- SchemaTypeConfigProto.newBuilder()
- .setSchemaType("package$database2/Document")
- .setVersion(0))
- .build();
-
- // Check nothing changed in database2.
- expectedTypes = new ArrayList<>();
- expectedTypes.addAll(existingSchemas);
- expectedTypes.addAll(expectedProto.getTypesList());
- assertThat(mAppSearchImpl.getSchemaProtoLocked().getTypesList())
- .containsExactlyElementsIn(expectedTypes);
- }
-
- @Test
- public void testClearPackageData() throws AppSearchException {
- List<SchemaTypeConfigProto> existingSchemas =
- mAppSearchImpl.getSchemaProtoLocked().getTypesList();
- Map<String, Set<String>> existingDatabases = mAppSearchImpl.getPackageToDatabases();
-
- // Insert package schema
- List<AppSearchSchema> schema =
- ImmutableList.of(new AppSearchSchema.Builder("schema").build());
- mAppSearchImpl.setSchema(
- "package",
- "database",
- schema,
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0,
- /* setSchemaStatsBuilder= */ null);
-
- // Insert package document
- GenericDocument document =
- new GenericDocument.Builder<>("namespace", "id", "schema").build();
- mAppSearchImpl.putDocument("package", "database", document, /*logger=*/ null);
-
- // Verify the document is indexed.
- SearchSpec searchSpec =
- new SearchSpec.Builder().setTermMatch(TermMatchType.Code.PREFIX_VALUE).build();
- SearchResultPage searchResultPage =
- mAppSearchImpl.query(
- "package",
- "database",
- /*queryExpression=*/ "",
- searchSpec,
- /*logger=*/ null);
- assertThat(searchResultPage.getResults()).hasSize(1);
- assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document);
-
- // Remove the package
- mAppSearchImpl.clearPackageData("package");
-
- // Verify the document is cleared.
- searchResultPage =
- mAppSearchImpl.query(
- "package2",
- "database2",
- /*queryExpression=*/ "",
- searchSpec,
- /*logger=*/ null);
- assertThat(searchResultPage.getResults()).isEmpty();
-
- // Verify the schema is cleared.
- assertThat(mAppSearchImpl.getSchemaProtoLocked().getTypesList())
- .containsExactlyElementsIn(existingSchemas);
- assertThat(mAppSearchImpl.getPackageToDatabases())
- .containsExactlyEntriesIn(existingDatabases);
- }
-
- @Test
- public void testPrunePackageData() throws AppSearchException {
- List<SchemaTypeConfigProto> existingSchemas =
- mAppSearchImpl.getSchemaProtoLocked().getTypesList();
- Map<String, Set<String>> existingDatabases = mAppSearchImpl.getPackageToDatabases();
-
- Set<String> existingPackages = new ArraySet<>(existingSchemas.size());
- for (int i = 0; i < existingSchemas.size(); i++) {
- existingPackages.add(PrefixUtil.getPackageName(existingSchemas.get(i).getSchemaType()));
- }
-
- // Insert schema for package A and B.
- List<AppSearchSchema> schema =
- ImmutableList.of(new AppSearchSchema.Builder("schema").build());
- mAppSearchImpl.setSchema(
- "packageA",
- "database",
- schema,
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0,
- /* setSchemaStatsBuilder= */ null);
- mAppSearchImpl.setSchema(
- "packageB",
- "database",
- schema,
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0,
- /* setSchemaStatsBuilder= */ null);
-
- // Verify these two packages is stored in AppSearch
- SchemaProto expectedProto =
- SchemaProto.newBuilder()
- .addTypes(
- SchemaTypeConfigProto.newBuilder()
- .setSchemaType("packageA$database/schema")
- .setVersion(0))
- .addTypes(
- SchemaTypeConfigProto.newBuilder()
- .setSchemaType("packageB$database/schema")
- .setVersion(0))
- .build();
- List<SchemaTypeConfigProto> expectedTypes = new ArrayList<>();
- expectedTypes.addAll(existingSchemas);
- expectedTypes.addAll(expectedProto.getTypesList());
- assertThat(mAppSearchImpl.getSchemaProtoLocked().getTypesList())
- .containsExactlyElementsIn(expectedTypes);
-
- // Prune packages
- mAppSearchImpl.prunePackageData(existingPackages);
-
- // Verify the schema is same as beginning.
- assertThat(mAppSearchImpl.getSchemaProtoLocked().getTypesList())
- .containsExactlyElementsIn(existingSchemas);
- assertThat(mAppSearchImpl.getPackageToDatabases())
- .containsExactlyEntriesIn(existingDatabases);
- }
-
- @Test
- public void testGetPackageToDatabases() throws Exception {
- Map<String, Set<String>> existingMapping = mAppSearchImpl.getPackageToDatabases();
- Map<String, Set<String>> expectedMapping = new ArrayMap<>();
- expectedMapping.putAll(existingMapping);
-
- // Has database1
- expectedMapping.put("package1", ImmutableSet.of("database1"));
- mAppSearchImpl.setSchema(
- "package1",
- "database1",
- Collections.singletonList(new AppSearchSchema.Builder("schema").build()),
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0,
- /* setSchemaStatsBuilder= */ null);
- assertThat(mAppSearchImpl.getPackageToDatabases())
- .containsExactlyEntriesIn(expectedMapping);
-
- // Has both databases
- expectedMapping.put("package1", ImmutableSet.of("database1", "database2"));
- mAppSearchImpl.setSchema(
- "package1",
- "database2",
- Collections.singletonList(new AppSearchSchema.Builder("schema").build()),
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0,
- /* setSchemaStatsBuilder= */ null);
- assertThat(mAppSearchImpl.getPackageToDatabases())
- .containsExactlyEntriesIn(expectedMapping);
-
- // Has both packages
- expectedMapping.put("package2", ImmutableSet.of("database1"));
- mAppSearchImpl.setSchema(
- "package2",
- "database1",
- Collections.singletonList(new AppSearchSchema.Builder("schema").build()),
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0,
- /* setSchemaStatsBuilder= */ null);
- assertThat(mAppSearchImpl.getPackageToDatabases())
- .containsExactlyEntriesIn(expectedMapping);
- }
-
- @Test
- public void testRewriteSearchResultProto() throws Exception {
- final String prefix =
- "com.package.foo"
- + PrefixUtil.PACKAGE_DELIMITER
- + "databaseName"
- + PrefixUtil.DATABASE_DELIMITER;
- final String id = "id";
- final String namespace = prefix + "namespace";
- final String schemaType = prefix + "schema";
-
- // Building the SearchResult received from query.
- DocumentProto documentProto =
- DocumentProto.newBuilder()
- .setUri(id)
- .setNamespace(namespace)
- .setSchema(schemaType)
- .build();
- SearchResultProto.ResultProto resultProto =
- SearchResultProto.ResultProto.newBuilder().setDocument(documentProto).build();
- SearchResultProto searchResultProto =
- SearchResultProto.newBuilder().addResults(resultProto).build();
- SchemaTypeConfigProto schemaTypeConfigProto =
- SchemaTypeConfigProto.newBuilder().setSchemaType(schemaType).build();
- Map<String, Map<String, SchemaTypeConfigProto>> schemaMap =
- ImmutableMap.of(prefix, ImmutableMap.of(schemaType, schemaTypeConfigProto));
-
- DocumentProto.Builder strippedDocumentProto = documentProto.toBuilder();
- removePrefixesFromDocument(strippedDocumentProto);
- SearchResultPage searchResultPage =
- AppSearchImpl.rewriteSearchResultProto(searchResultProto, schemaMap);
- for (SearchResult result : searchResultPage.getResults()) {
- assertThat(result.getPackageName()).isEqualTo("com.package.foo");
- assertThat(result.getDatabaseName()).isEqualTo("databaseName");
- assertThat(result.getGenericDocument())
- .isEqualTo(
- GenericDocumentToProtoConverter.toGenericDocument(
- strippedDocumentProto.build(), prefix, schemaMap.get(prefix)));
- }
- }
-
- @Test
- public void testReportUsage() throws Exception {
- // Insert schema
- List<AppSearchSchema> schemas =
- Collections.singletonList(new AppSearchSchema.Builder("type").build());
- mAppSearchImpl.setSchema(
- "package",
- "database",
- schemas,
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0,
- /* setSchemaStatsBuilder= */ null);
-
- // Insert two docs
- GenericDocument document1 =
- new GenericDocument.Builder<>("namespace", "id1", "type").build();
- GenericDocument document2 =
- new GenericDocument.Builder<>("namespace", "id2", "type").build();
- mAppSearchImpl.putDocument("package", "database", document1, /*logger=*/ null);
- mAppSearchImpl.putDocument("package", "database", document2, /*logger=*/ null);
-
- // Report some usages. id1 has 2 app and 1 system usage, id2 has 1 app and 2 system usage.
- mAppSearchImpl.reportUsage(
- "package",
- "database",
- "namespace",
- "id1",
- /*usageTimestampMillis=*/ 10,
- /*systemUsage=*/ false);
- mAppSearchImpl.reportUsage(
- "package",
- "database",
- "namespace",
- "id1",
- /*usageTimestampMillis=*/ 20,
- /*systemUsage=*/ false);
- mAppSearchImpl.reportUsage(
- "package",
- "database",
- "namespace",
- "id1",
- /*usageTimestampMillis=*/ 1000,
- /*systemUsage=*/ true);
-
- mAppSearchImpl.reportUsage(
- "package",
- "database",
- "namespace",
- "id2",
- /*usageTimestampMillis=*/ 100,
- /*systemUsage=*/ false);
- mAppSearchImpl.reportUsage(
- "package",
- "database",
- "namespace",
- "id2",
- /*usageTimestampMillis=*/ 200,
- /*systemUsage=*/ true);
- mAppSearchImpl.reportUsage(
- "package",
- "database",
- "namespace",
- "id2",
- /*usageTimestampMillis=*/ 150,
- /*systemUsage=*/ true);
-
- // Sort by app usage count: id1 should win
- List<SearchResult> page =
- mAppSearchImpl
- .query(
- "package",
- "database",
- "",
- new SearchSpec.Builder()
- .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
- .setRankingStrategy(SearchSpec.RANKING_STRATEGY_USAGE_COUNT)
- .build(),
- /*logger=*/ null)
- .getResults();
- assertThat(page).hasSize(2);
- assertThat(page.get(0).getGenericDocument().getId()).isEqualTo("id1");
- assertThat(page.get(1).getGenericDocument().getId()).isEqualTo("id2");
-
- // Sort by app usage timestamp: id2 should win
- page =
- mAppSearchImpl
- .query(
- "package",
- "database",
- "",
- new SearchSpec.Builder()
- .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
- .setRankingStrategy(
- SearchSpec
- .RANKING_STRATEGY_USAGE_LAST_USED_TIMESTAMP)
- .build(),
- /*logger=*/ null)
- .getResults();
- assertThat(page).hasSize(2);
- assertThat(page.get(0).getGenericDocument().getId()).isEqualTo("id2");
- assertThat(page.get(1).getGenericDocument().getId()).isEqualTo("id1");
-
- // Sort by system usage count: id2 should win
- page =
- mAppSearchImpl
- .query(
- "package",
- "database",
- "",
- new SearchSpec.Builder()
- .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
- .setRankingStrategy(
- SearchSpec.RANKING_STRATEGY_SYSTEM_USAGE_COUNT)
- .build(),
- /*logger=*/ null)
- .getResults();
- assertThat(page).hasSize(2);
- assertThat(page.get(0).getGenericDocument().getId()).isEqualTo("id2");
- assertThat(page.get(1).getGenericDocument().getId()).isEqualTo("id1");
-
- // Sort by system usage timestamp: id1 should win
- page =
- mAppSearchImpl
- .query(
- "package",
- "database",
- "",
- new SearchSpec.Builder()
- .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
- .setRankingStrategy(
- SearchSpec
- .RANKING_STRATEGY_SYSTEM_USAGE_LAST_USED_TIMESTAMP)
- .build(),
- /*logger=*/ null)
- .getResults();
- assertThat(page).hasSize(2);
- assertThat(page.get(0).getGenericDocument().getId()).isEqualTo("id1");
- assertThat(page.get(1).getGenericDocument().getId()).isEqualTo("id2");
- }
-
- @Test
- public void testGetStorageInfoForPackage_nonexistentPackage() throws Exception {
- // "package2" doesn't exist yet, so it shouldn't have any storage size
- StorageInfo storageInfo = mAppSearchImpl.getStorageInfoForPackage("nonexistent.package");
- assertThat(storageInfo.getSizeBytes()).isEqualTo(0);
- assertThat(storageInfo.getAliveDocumentsCount()).isEqualTo(0);
- assertThat(storageInfo.getAliveNamespacesCount()).isEqualTo(0);
- }
-
- @Test
- public void testGetStorageInfoForPackage_withoutDocument() throws Exception {
- // Insert schema for "package1"
- List<AppSearchSchema> schemas =
- Collections.singletonList(new AppSearchSchema.Builder("type").build());
- mAppSearchImpl.setSchema(
- "package1",
- "database",
- schemas,
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0,
- /* setSchemaStatsBuilder= */ null);
-
- // Since "package1" doesn't have a document, it get any space attributed to it.
- StorageInfo storageInfo = mAppSearchImpl.getStorageInfoForPackage("package1");
- assertThat(storageInfo.getSizeBytes()).isEqualTo(0);
- assertThat(storageInfo.getAliveDocumentsCount()).isEqualTo(0);
- assertThat(storageInfo.getAliveNamespacesCount()).isEqualTo(0);
- }
-
- @Test
- public void testGetStorageInfoForPackage_proportionalToDocuments() throws Exception {
- List<AppSearchSchema> schemas =
- Collections.singletonList(new AppSearchSchema.Builder("type").build());
-
- // Insert schema for "package1"
- mAppSearchImpl.setSchema(
- "package1",
- "database",
- schemas,
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0,
- /* setSchemaStatsBuilder= */ null);
-
- // Insert document for "package1"
- GenericDocument document =
- new GenericDocument.Builder<>("namespace", "id1", "type").build();
- mAppSearchImpl.putDocument("package1", "database", document, /*logger=*/ null);
-
- // Insert schema for "package2"
- mAppSearchImpl.setSchema(
- "package2",
- "database",
- schemas,
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0,
- /* setSchemaStatsBuilder= */ null);
-
- // Insert two documents for "package2"
- document = new GenericDocument.Builder<>("namespace", "id1", "type").build();
- mAppSearchImpl.putDocument("package2", "database", document, /*logger=*/ null);
- document = new GenericDocument.Builder<>("namespace", "id2", "type").build();
- mAppSearchImpl.putDocument("package2", "database", document, /*logger=*/ null);
-
- StorageInfo storageInfo = mAppSearchImpl.getStorageInfoForPackage("package1");
- long size1 = storageInfo.getSizeBytes();
- assertThat(size1).isGreaterThan(0);
- assertThat(storageInfo.getAliveDocumentsCount()).isEqualTo(1);
- assertThat(storageInfo.getAliveNamespacesCount()).isEqualTo(1);
-
- storageInfo = mAppSearchImpl.getStorageInfoForPackage("package2");
- long size2 = storageInfo.getSizeBytes();
- assertThat(size2).isGreaterThan(0);
- assertThat(storageInfo.getAliveDocumentsCount()).isEqualTo(2);
- assertThat(storageInfo.getAliveNamespacesCount()).isEqualTo(1);
-
- // Size is proportional to number of documents. Since "package2" has twice as many
- // documents as "package1", its size is twice as much too.
- assertThat(size2).isAtLeast(2 * size1);
- }
-
- @Test
- public void testGetStorageInfoForDatabase_nonexistentPackage() throws Exception {
- // "package2" doesn't exist yet, so it shouldn't have any storage size
- StorageInfo storageInfo =
- mAppSearchImpl.getStorageInfoForDatabase(
- "nonexistent.package", "nonexistentDatabase");
- assertThat(storageInfo.getSizeBytes()).isEqualTo(0);
- assertThat(storageInfo.getAliveDocumentsCount()).isEqualTo(0);
- assertThat(storageInfo.getAliveNamespacesCount()).isEqualTo(0);
- }
-
- @Test
- public void testGetStorageInfoForDatabase_nonexistentDatabase() throws Exception {
- // Insert schema for "package1"
- List<AppSearchSchema> schemas =
- Collections.singletonList(new AppSearchSchema.Builder("type").build());
- mAppSearchImpl.setSchema(
- "package1",
- "database",
- schemas,
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0,
- /* setSchemaStatsBuilder= */ null);
-
- // "package2" doesn't exist yet, so it shouldn't have any storage size
- StorageInfo storageInfo =
- mAppSearchImpl.getStorageInfoForDatabase("package1", "nonexistentDatabase");
- assertThat(storageInfo.getSizeBytes()).isEqualTo(0);
- assertThat(storageInfo.getAliveDocumentsCount()).isEqualTo(0);
- assertThat(storageInfo.getAliveNamespacesCount()).isEqualTo(0);
- }
-
- @Test
- public void testGetStorageInfoForDatabase_withoutDocument() throws Exception {
- // Insert schema for "package1"
- List<AppSearchSchema> schemas =
- Collections.singletonList(new AppSearchSchema.Builder("type").build());
- mAppSearchImpl.setSchema(
- "package1",
- "database1",
- schemas,
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0,
- /* setSchemaStatsBuilder= */ null);
-
- // Since "package1", "database1" doesn't have a document, it get any space attributed to it.
- StorageInfo storageInfo = mAppSearchImpl.getStorageInfoForDatabase("package1", "database1");
- assertThat(storageInfo.getSizeBytes()).isEqualTo(0);
- assertThat(storageInfo.getAliveDocumentsCount()).isEqualTo(0);
- assertThat(storageInfo.getAliveNamespacesCount()).isEqualTo(0);
- }
-
- @Test
- public void testGetStorageInfoForDatabase_proportionalToDocuments() throws Exception {
- // Insert schema for "package1", "database1" and "database2"
- List<AppSearchSchema> schemas =
- Collections.singletonList(new AppSearchSchema.Builder("type").build());
- mAppSearchImpl.setSchema(
- "package1",
- "database1",
- schemas,
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0,
- /* setSchemaStatsBuilder= */ null);
- mAppSearchImpl.setSchema(
- "package1",
- "database2",
- schemas,
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0,
- /* setSchemaStatsBuilder= */ null);
-
- // Add a document for "package1", "database1"
- GenericDocument document =
- new GenericDocument.Builder<>("namespace1", "id1", "type").build();
- mAppSearchImpl.putDocument("package1", "database1", document, /*logger=*/ null);
-
- // Add two documents for "package1", "database2"
- document = new GenericDocument.Builder<>("namespace1", "id1", "type").build();
- mAppSearchImpl.putDocument("package1", "database2", document, /*logger=*/ null);
- document = new GenericDocument.Builder<>("namespace1", "id2", "type").build();
- mAppSearchImpl.putDocument("package1", "database2", document, /*logger=*/ null);
-
- StorageInfo storageInfo = mAppSearchImpl.getStorageInfoForDatabase("package1", "database1");
- long size1 = storageInfo.getSizeBytes();
- assertThat(size1).isGreaterThan(0);
- assertThat(storageInfo.getAliveDocumentsCount()).isEqualTo(1);
- assertThat(storageInfo.getAliveNamespacesCount()).isEqualTo(1);
-
- storageInfo = mAppSearchImpl.getStorageInfoForDatabase("package1", "database2");
- long size2 = storageInfo.getSizeBytes();
- assertThat(size2).isGreaterThan(0);
- assertThat(storageInfo.getAliveDocumentsCount()).isEqualTo(2);
- assertThat(storageInfo.getAliveNamespacesCount()).isEqualTo(1);
-
- // Size is proportional to number of documents. Since "database2" has twice as many
- // documents as "database1", its size is twice as much too.
- assertThat(size2).isAtLeast(2 * size1);
- }
-
- @Test
- public void testThrowsExceptionIfClosed() throws Exception {
- AppSearchImpl appSearchImpl =
- AppSearchImpl.create(
- mTemporaryFolder.newFolder(),
- new UnlimitedLimitConfig(),
- /*initStatsBuilder=*/ null,
- ALWAYS_OPTIMIZE);
-
- // Initial check that we could do something at first.
- List<AppSearchSchema> schemas =
- Collections.singletonList(new AppSearchSchema.Builder("type").build());
- appSearchImpl.setSchema(
- "package",
- "database",
- schemas,
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0,
- /* setSchemaStatsBuilder= */ null);
-
- appSearchImpl.close();
-
- // Check all our public APIs
- assertThrows(
- IllegalStateException.class,
- () ->
- appSearchImpl.setSchema(
- "package",
- "database",
- schemas,
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0,
- /* setSchemaStatsBuilder= */ null));
-
- assertThrows(
- IllegalStateException.class, () -> appSearchImpl.getSchema("package", "database"));
-
- assertThrows(
- IllegalStateException.class,
- () ->
- appSearchImpl.putDocument(
- "package",
- "database",
- new GenericDocument.Builder<>("namespace", "id", "type").build(),
- /*logger=*/ null));
-
- assertThrows(
- IllegalStateException.class,
- () ->
- appSearchImpl.getDocument(
- "package", "database", "namespace", "id", Collections.emptyMap()));
-
- assertThrows(
- IllegalStateException.class,
- () ->
- appSearchImpl.query(
- "package",
- "database",
- "query",
- new SearchSpec.Builder().build(),
- /*logger=*/ null));
-
- assertThrows(
- IllegalStateException.class,
- () ->
- appSearchImpl.globalQuery(
- "query",
- new SearchSpec.Builder().build(),
- "package",
- /*visibilityStore=*/ null,
- Process.INVALID_UID,
- /*callerHasSystemAccess=*/ false,
- /*logger=*/ null));
-
- assertThrows(
- IllegalStateException.class,
- () ->
- appSearchImpl.getNextPage(
- "package", /*nextPageToken=*/ 1L, /*statsBuilder=*/ null));
-
- assertThrows(
- IllegalStateException.class,
- () -> appSearchImpl.invalidateNextPageToken("package", /*nextPageToken=*/ 1L));
-
- assertThrows(
- IllegalStateException.class,
- () ->
- appSearchImpl.reportUsage(
- "package",
- "database",
- "namespace",
- "id",
- /*usageTimestampMillis=*/ 1000L,
- /*systemUsage=*/ false));
-
- assertThrows(
- IllegalStateException.class,
- () ->
- appSearchImpl.remove(
- "package",
- "database",
- "namespace",
- "id",
- /*removeStatsBuilder=*/ null));
-
- assertThrows(
- IllegalStateException.class,
- () ->
- appSearchImpl.removeByQuery(
- "package",
- "database",
- "query",
- new SearchSpec.Builder().build(),
- /*removeStatsBuilder=*/ null));
-
- assertThrows(
- IllegalStateException.class,
- () -> appSearchImpl.getStorageInfoForPackage("package"));
-
- assertThrows(
- IllegalStateException.class,
- () -> appSearchImpl.getStorageInfoForDatabase("package", "database"));
-
- assertThrows(
- IllegalStateException.class,
- () -> appSearchImpl.persistToDisk(PersistType.Code.FULL));
- }
-
- @Test
- public void testPutPersistsWithLiteFlush() throws Exception {
- // Setup the index
- File appsearchDir = mTemporaryFolder.newFolder();
- AppSearchImpl appSearchImpl =
- AppSearchImpl.create(
- appsearchDir,
- new UnlimitedLimitConfig(),
- /*initStatsBuilder=*/ null,
- ALWAYS_OPTIMIZE);
-
- List<AppSearchSchema> schemas =
- Collections.singletonList(new AppSearchSchema.Builder("type").build());
- appSearchImpl.setSchema(
- "package",
- "database",
- schemas,
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0,
- /* setSchemaStatsBuilder= */ null);
-
- // Add a document and persist it.
- GenericDocument document =
- new GenericDocument.Builder<>("namespace1", "id1", "type").build();
- appSearchImpl.putDocument("package", "database", document, /*logger=*/ null);
- appSearchImpl.persistToDisk(PersistType.Code.LITE);
-
- GenericDocument getResult =
- appSearchImpl.getDocument(
- "package", "database", "namespace1", "id1", Collections.emptyMap());
- assertThat(getResult).isEqualTo(document);
-
- // That document should be visible even from another instance.
- AppSearchImpl appSearchImpl2 =
- AppSearchImpl.create(
- appsearchDir,
- new UnlimitedLimitConfig(),
- /*initStatsBuilder=*/ null,
- ALWAYS_OPTIMIZE);
- getResult =
- appSearchImpl2.getDocument(
- "package", "database", "namespace1", "id1", Collections.emptyMap());
- assertThat(getResult).isEqualTo(document);
- }
-
- @Test
- public void testDeletePersistsWithLiteFlush() throws Exception {
- // Setup the index
- File appsearchDir = mTemporaryFolder.newFolder();
- AppSearchImpl appSearchImpl =
- AppSearchImpl.create(
- appsearchDir,
- new UnlimitedLimitConfig(),
- /*initStatsBuilder=*/ null,
- ALWAYS_OPTIMIZE);
-
- List<AppSearchSchema> schemas =
- Collections.singletonList(new AppSearchSchema.Builder("type").build());
- appSearchImpl.setSchema(
- "package",
- "database",
- schemas,
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0,
- /* setSchemaStatsBuilder= */ null);
-
- // Add two documents and persist them.
- GenericDocument document1 =
- new GenericDocument.Builder<>("namespace1", "id1", "type").build();
- appSearchImpl.putDocument("package", "database", document1, /*logger=*/ null);
- GenericDocument document2 =
- new GenericDocument.Builder<>("namespace1", "id2", "type").build();
- appSearchImpl.putDocument("package", "database", document2, /*logger=*/ null);
- appSearchImpl.persistToDisk(PersistType.Code.LITE);
-
- GenericDocument getResult =
- appSearchImpl.getDocument(
- "package", "database", "namespace1", "id1", Collections.emptyMap());
- assertThat(getResult).isEqualTo(document1);
- getResult =
- appSearchImpl.getDocument(
- "package", "database", "namespace1", "id2", Collections.emptyMap());
- assertThat(getResult).isEqualTo(document2);
-
- // Delete the first document
- appSearchImpl.remove("package", "database", "namespace1", "id1", /*statsBuilder=*/ null);
- appSearchImpl.persistToDisk(PersistType.Code.LITE);
- assertThrows(
- AppSearchException.class,
- () ->
- appSearchImpl.getDocument(
- "package",
- "database",
- "namespace1",
- "id1",
- Collections.emptyMap()));
- getResult =
- appSearchImpl.getDocument(
- "package", "database", "namespace1", "id2", Collections.emptyMap());
- assertThat(getResult).isEqualTo(document2);
-
- // Only the second document should be retrievable from another instance.
- AppSearchImpl appSearchImpl2 =
- AppSearchImpl.create(
- appsearchDir,
- new UnlimitedLimitConfig(),
- /*initStatsBuilder=*/ null,
- ALWAYS_OPTIMIZE);
- assertThrows(
- AppSearchException.class,
- () ->
- appSearchImpl2.getDocument(
- "package",
- "database",
- "namespace1",
- "id1",
- Collections.emptyMap()));
- getResult =
- appSearchImpl2.getDocument(
- "package", "database", "namespace1", "id2", Collections.emptyMap());
- assertThat(getResult).isEqualTo(document2);
- }
-
- @Test
- public void testDeleteByQueryPersistsWithLiteFlush() throws Exception {
- // Setup the index
- File appsearchDir = mTemporaryFolder.newFolder();
- AppSearchImpl appSearchImpl =
- AppSearchImpl.create(
- appsearchDir,
- new UnlimitedLimitConfig(),
- /*initStatsBuilder=*/ null,
- ALWAYS_OPTIMIZE);
-
- List<AppSearchSchema> schemas =
- Collections.singletonList(new AppSearchSchema.Builder("type").build());
- appSearchImpl.setSchema(
- "package",
- "database",
- schemas,
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0,
- /* setSchemaStatsBuilder= */ null);
-
- // Add two documents and persist them.
- GenericDocument document1 =
- new GenericDocument.Builder<>("namespace1", "id1", "type").build();
- appSearchImpl.putDocument("package", "database", document1, /*logger=*/ null);
- GenericDocument document2 =
- new GenericDocument.Builder<>("namespace2", "id2", "type").build();
- appSearchImpl.putDocument("package", "database", document2, /*logger=*/ null);
- appSearchImpl.persistToDisk(PersistType.Code.LITE);
-
- GenericDocument getResult =
- appSearchImpl.getDocument(
- "package", "database", "namespace1", "id1", Collections.emptyMap());
- assertThat(getResult).isEqualTo(document1);
- getResult =
- appSearchImpl.getDocument(
- "package", "database", "namespace2", "id2", Collections.emptyMap());
- assertThat(getResult).isEqualTo(document2);
-
- // Delete the first document
- appSearchImpl.removeByQuery(
- "package",
- "database",
- "",
- new SearchSpec.Builder()
- .addFilterNamespaces("namespace1")
- .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
- .build(),
- /*statsBuilder=*/ null);
- appSearchImpl.persistToDisk(PersistType.Code.LITE);
- assertThrows(
- AppSearchException.class,
- () ->
- appSearchImpl.getDocument(
- "package",
- "database",
- "namespace1",
- "id1",
- Collections.emptyMap()));
- getResult =
- appSearchImpl.getDocument(
- "package", "database", "namespace2", "id2", Collections.emptyMap());
- assertThat(getResult).isEqualTo(document2);
-
- // Only the second document should be retrievable from another instance.
- AppSearchImpl appSearchImpl2 =
- AppSearchImpl.create(
- appsearchDir,
- new UnlimitedLimitConfig(),
- /*initStatsBuilder=*/ null,
- ALWAYS_OPTIMIZE);
- assertThrows(
- AppSearchException.class,
- () ->
- appSearchImpl2.getDocument(
- "package",
- "database",
- "namespace1",
- "id1",
- Collections.emptyMap()));
- getResult =
- appSearchImpl2.getDocument(
- "package", "database", "namespace2", "id2", Collections.emptyMap());
- assertThat(getResult).isEqualTo(document2);
- }
-
- @Test
- public void testGetIcingSearchEngineStorageInfo() throws Exception {
- // Setup the index
- File appsearchDir = mTemporaryFolder.newFolder();
- AppSearchImpl appSearchImpl =
- AppSearchImpl.create(
- appsearchDir,
- new UnlimitedLimitConfig(),
- /*initStatsBuilder=*/ null,
- ALWAYS_OPTIMIZE);
-
- List<AppSearchSchema> schemas =
- Collections.singletonList(new AppSearchSchema.Builder("type").build());
- appSearchImpl.setSchema(
- "package",
- "database",
- schemas,
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0,
- /* setSchemaStatsBuilder= */ null);
-
- // Add two documents
- GenericDocument document1 =
- new GenericDocument.Builder<>("namespace1", "id1", "type").build();
- appSearchImpl.putDocument("package", "database", document1, /*logger=*/ null);
- GenericDocument document2 =
- new GenericDocument.Builder<>("namespace1", "id2", "type").build();
- appSearchImpl.putDocument("package", "database", document2, /*logger=*/ null);
-
- StorageInfoProto storageInfo = appSearchImpl.getRawStorageInfoProto();
-
- // Simple checks to verify if we can get correct StorageInfoProto from IcingSearchEngine
- // No need to cover all the fields
- assertThat(storageInfo.getTotalStorageSize()).isGreaterThan(0);
- assertThat(storageInfo.getDocumentStorageInfo().getNumAliveDocuments()).isEqualTo(2);
- assertThat(storageInfo.getSchemaStoreStorageInfo().getNumSchemaTypes()).isEqualTo(1);
- }
-
- @Test
- public void testLimitConfig_DocumentSize() throws Exception {
- // Create a new mAppSearchImpl with a lower limit
- mAppSearchImpl.close();
- mAppSearchImpl =
- AppSearchImpl.create(
- mTemporaryFolder.newFolder(),
- new LimitConfig() {
- @Override
- public int getMaxDocumentSizeBytes() {
- return 80;
- }
-
- @Override
- public int getMaxDocumentCount() {
- return 1;
- }
- },
- /*initStatsBuilder=*/ null,
- ALWAYS_OPTIMIZE);
-
- // Insert schema
- List<AppSearchSchema> schemas =
- Collections.singletonList(new AppSearchSchema.Builder("type").build());
- mAppSearchImpl.setSchema(
- "package",
- "database",
- schemas,
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0,
- /* setSchemaStatsBuilder= */ null);
-
- // Insert a document which is too large
- GenericDocument document =
- new GenericDocument.Builder<>(
- "this_namespace_is_long_to_make_the_doc_big", "id", "type")
- .build();
- AppSearchException e =
- assertThrows(
- AppSearchException.class,
- () ->
- mAppSearchImpl.putDocument(
- "package", "database", document, /*logger=*/ null));
- assertThat(e.getResultCode()).isEqualTo(AppSearchResult.RESULT_OUT_OF_SPACE);
- assertThat(e)
- .hasMessageThat()
- .contains(
- "Document \"id\" for package \"package\" serialized to 99 bytes, which"
- + " exceeds limit of 80 bytes");
-
- // Make sure this failure didn't increase our document count. We should still be able to
- // index 1 document.
- GenericDocument document2 =
- new GenericDocument.Builder<>("namespace", "id2", "type").build();
- mAppSearchImpl.putDocument("package", "database", document2, /*logger=*/ null);
-
- // Now we should get a failure
- GenericDocument document3 =
- new GenericDocument.Builder<>("namespace", "id3", "type").build();
- e =
- assertThrows(
- AppSearchException.class,
- () ->
- mAppSearchImpl.putDocument(
- "package", "database", document3, /*logger=*/ null));
- assertThat(e.getResultCode()).isEqualTo(AppSearchResult.RESULT_OUT_OF_SPACE);
- assertThat(e)
- .hasMessageThat()
- .contains("Package \"package\" exceeded limit of 1 documents");
- }
-
- @Test
- public void testLimitConfig_Init() throws Exception {
- // Create a new mAppSearchImpl with a lower limit
- mAppSearchImpl.close();
- File tempFolder = mTemporaryFolder.newFolder();
- mAppSearchImpl =
- AppSearchImpl.create(
- tempFolder,
- new LimitConfig() {
- @Override
- public int getMaxDocumentSizeBytes() {
- return 80;
- }
-
- @Override
- public int getMaxDocumentCount() {
- return 1;
- }
- },
- /*initStatsBuilder=*/ null,
- ALWAYS_OPTIMIZE);
-
- // Insert schema
- List<AppSearchSchema> schemas =
- Collections.singletonList(new AppSearchSchema.Builder("type").build());
- mAppSearchImpl.setSchema(
- "package",
- "database",
- schemas,
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0,
- /* setSchemaStatsBuilder= */ null);
-
- // Index a document
- mAppSearchImpl.putDocument(
- "package",
- "database",
- new GenericDocument.Builder<>("namespace", "id1", "type").build(),
- /*logger=*/ null);
-
- // Now we should get a failure
- GenericDocument document2 =
- new GenericDocument.Builder<>("namespace", "id2", "type").build();
- AppSearchException e =
- assertThrows(
- AppSearchException.class,
- () ->
- mAppSearchImpl.putDocument(
- "package", "database", document2, /*logger=*/ null));
- assertThat(e.getResultCode()).isEqualTo(AppSearchResult.RESULT_OUT_OF_SPACE);
- assertThat(e)
- .hasMessageThat()
- .contains("Package \"package\" exceeded limit of 1 documents");
-
- // Close and reinitialize AppSearchImpl
- mAppSearchImpl.close();
- mAppSearchImpl =
- AppSearchImpl.create(
- tempFolder,
- new LimitConfig() {
- @Override
- public int getMaxDocumentSizeBytes() {
- return 80;
- }
-
- @Override
- public int getMaxDocumentCount() {
- return 1;
- }
- },
- /*initStatsBuilder=*/ null,
- ALWAYS_OPTIMIZE);
-
- // Make sure the limit is maintained
- e =
- assertThrows(
- AppSearchException.class,
- () ->
- mAppSearchImpl.putDocument(
- "package", "database", document2, /*logger=*/ null));
- assertThat(e.getResultCode()).isEqualTo(AppSearchResult.RESULT_OUT_OF_SPACE);
- assertThat(e)
- .hasMessageThat()
- .contains("Package \"package\" exceeded limit of 1 documents");
- }
-
- @Test
- public void testLimitConfig_Remove() throws Exception {
- // Create a new mAppSearchImpl with a lower limit
- mAppSearchImpl.close();
- mAppSearchImpl =
- AppSearchImpl.create(
- mTemporaryFolder.newFolder(),
- new LimitConfig() {
- @Override
- public int getMaxDocumentSizeBytes() {
- return Integer.MAX_VALUE;
- }
-
- @Override
- public int getMaxDocumentCount() {
- return 3;
- }
- },
- /*initStatsBuilder=*/ null,
- ALWAYS_OPTIMIZE);
-
- // Insert schema
- List<AppSearchSchema> schemas =
- Collections.singletonList(new AppSearchSchema.Builder("type").build());
- mAppSearchImpl.setSchema(
- "package",
- "database",
- schemas,
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0,
- /* setSchemaStatsBuilder= */ null);
-
- // Index 3 documents
- mAppSearchImpl.putDocument(
- "package",
- "database",
- new GenericDocument.Builder<>("namespace", "id1", "type").build(),
- /*logger=*/ null);
- mAppSearchImpl.putDocument(
- "package",
- "database",
- new GenericDocument.Builder<>("namespace", "id2", "type").build(),
- /*logger=*/ null);
- mAppSearchImpl.putDocument(
- "package",
- "database",
- new GenericDocument.Builder<>("namespace", "id3", "type").build(),
- /*logger=*/ null);
-
- // Now we should get a failure
- GenericDocument document4 =
- new GenericDocument.Builder<>("namespace", "id4", "type").build();
- AppSearchException e =
- assertThrows(
- AppSearchException.class,
- () ->
- mAppSearchImpl.putDocument(
- "package", "database", document4, /*logger=*/ null));
- assertThat(e.getResultCode()).isEqualTo(AppSearchResult.RESULT_OUT_OF_SPACE);
- assertThat(e)
- .hasMessageThat()
- .contains("Package \"package\" exceeded limit of 3 documents");
-
- // Remove a document that doesn't exist
- assertThrows(
- AppSearchException.class,
- () ->
- mAppSearchImpl.remove(
- "package",
- "database",
- "namespace",
- "id4",
- /*removeStatsBuilder=*/ null));
-
- // Should still fail
- e =
- assertThrows(
- AppSearchException.class,
- () ->
- mAppSearchImpl.putDocument(
- "package", "database", document4, /*logger=*/ null));
- assertThat(e.getResultCode()).isEqualTo(AppSearchResult.RESULT_OUT_OF_SPACE);
- assertThat(e)
- .hasMessageThat()
- .contains("Package \"package\" exceeded limit of 3 documents");
-
- // Remove a document that does exist
- mAppSearchImpl.remove(
- "package", "database", "namespace", "id2", /*removeStatsBuilder=*/ null);
-
- // Now doc4 should work
- mAppSearchImpl.putDocument("package", "database", document4, /*logger=*/ null);
-
- // The next one should fail again
- e =
- assertThrows(
- AppSearchException.class,
- () ->
- mAppSearchImpl.putDocument(
- "package",
- "database",
- new GenericDocument.Builder<>("namespace", "id5", "type")
- .build(),
- /*logger=*/ null));
- assertThat(e.getResultCode()).isEqualTo(AppSearchResult.RESULT_OUT_OF_SPACE);
- assertThat(e)
- .hasMessageThat()
- .contains("Package \"package\" exceeded limit of 3 documents");
- }
-
- @Test
- public void testLimitConfig_DifferentPackages() throws Exception {
- // Create a new mAppSearchImpl with a lower limit
- mAppSearchImpl.close();
- File tempFolder = mTemporaryFolder.newFolder();
- mAppSearchImpl =
- AppSearchImpl.create(
- tempFolder,
- new LimitConfig() {
- @Override
- public int getMaxDocumentSizeBytes() {
- return Integer.MAX_VALUE;
- }
-
- @Override
- public int getMaxDocumentCount() {
- return 2;
- }
- },
- /*initStatsBuilder=*/ null,
- ALWAYS_OPTIMIZE);
-
- // Insert schema
- List<AppSearchSchema> schemas =
- Collections.singletonList(new AppSearchSchema.Builder("type").build());
- mAppSearchImpl.setSchema(
- "package1",
- "database1",
- schemas,
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0,
- /* setSchemaStatsBuilder= */ null);
- mAppSearchImpl.setSchema(
- "package1",
- "database2",
- schemas,
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0,
- /* setSchemaStatsBuilder= */ null);
- mAppSearchImpl.setSchema(
- "package2",
- "database1",
- schemas,
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0,
- /* setSchemaStatsBuilder= */ null);
- mAppSearchImpl.setSchema(
- "package2",
- "database2",
- schemas,
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0,
- /* setSchemaStatsBuilder= */ null);
-
- // Index documents in package1/database1
- mAppSearchImpl.putDocument(
- "package1",
- "database1",
- new GenericDocument.Builder<>("namespace", "id1", "type").build(),
- /*logger=*/ null);
- mAppSearchImpl.putDocument(
- "package1",
- "database2",
- new GenericDocument.Builder<>("namespace", "id2", "type").build(),
- /*logger=*/ null);
-
- // Indexing a third doc into package1 should fail (here we use database3)
- AppSearchException e =
- assertThrows(
- AppSearchException.class,
- () ->
- mAppSearchImpl.putDocument(
- "package1",
- "database3",
- new GenericDocument.Builder<>("namespace", "id3", "type")
- .build(),
- /*logger=*/ null));
- assertThat(e.getResultCode()).isEqualTo(AppSearchResult.RESULT_OUT_OF_SPACE);
- assertThat(e)
- .hasMessageThat()
- .contains("Package \"package1\" exceeded limit of 2 documents");
-
- // Indexing a doc into package2 should succeed
- mAppSearchImpl.putDocument(
- "package2",
- "database1",
- new GenericDocument.Builder<>("namespace", "id1", "type").build(),
- /*logger=*/ null);
-
- // Reinitialize to make sure packages are parsed correctly on init
- mAppSearchImpl.close();
- mAppSearchImpl =
- AppSearchImpl.create(
- tempFolder,
- new LimitConfig() {
- @Override
- public int getMaxDocumentSizeBytes() {
- return Integer.MAX_VALUE;
- }
-
- @Override
- public int getMaxDocumentCount() {
- return 2;
- }
- },
- /*initStatsBuilder=*/ null,
- ALWAYS_OPTIMIZE);
-
- // package1 should still be out of space
- e =
- assertThrows(
- AppSearchException.class,
- () ->
- mAppSearchImpl.putDocument(
- "package1",
- "database4",
- new GenericDocument.Builder<>("namespace", "id4", "type")
- .build(),
- /*logger=*/ null));
- assertThat(e.getResultCode()).isEqualTo(AppSearchResult.RESULT_OUT_OF_SPACE);
- assertThat(e)
- .hasMessageThat()
- .contains("Package \"package1\" exceeded limit of 2 documents");
-
- // package2 has room for one more
- mAppSearchImpl.putDocument(
- "package2",
- "database2",
- new GenericDocument.Builder<>("namespace", "id2", "type").build(),
- /*logger=*/ null);
-
- // now package2 really is out of space
- e =
- assertThrows(
- AppSearchException.class,
- () ->
- mAppSearchImpl.putDocument(
- "package2",
- "database3",
- new GenericDocument.Builder<>("namespace", "id3", "type")
- .build(),
- /*logger=*/ null));
- assertThat(e.getResultCode()).isEqualTo(AppSearchResult.RESULT_OUT_OF_SPACE);
- assertThat(e)
- .hasMessageThat()
- .contains("Package \"package2\" exceeded limit of 2 documents");
- }
-
- @Test
- public void testLimitConfig_RemoveByQyery() throws Exception {
- // Create a new mAppSearchImpl with a lower limit
- mAppSearchImpl.close();
- mAppSearchImpl =
- AppSearchImpl.create(
- mTemporaryFolder.newFolder(),
- new LimitConfig() {
- @Override
- public int getMaxDocumentSizeBytes() {
- return Integer.MAX_VALUE;
- }
-
- @Override
- public int getMaxDocumentCount() {
- return 3;
- }
- },
- /*initStatsBuilder=*/ null,
- ALWAYS_OPTIMIZE);
-
- // Insert schema
- List<AppSearchSchema> schemas =
- Collections.singletonList(
- new AppSearchSchema.Builder("type")
- .addProperty(
- new AppSearchSchema.StringPropertyConfig.Builder("body")
- .setIndexingType(
- AppSearchSchema.StringPropertyConfig
- .INDEXING_TYPE_PREFIXES)
- .setTokenizerType(
- AppSearchSchema.StringPropertyConfig
- .TOKENIZER_TYPE_PLAIN)
- .build())
- .build());
- mAppSearchImpl.setSchema(
- "package",
- "database",
- schemas,
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0,
- /* setSchemaStatsBuilder= */ null);
-
- // Index 3 documents
- mAppSearchImpl.putDocument(
- "package",
- "database",
- new GenericDocument.Builder<>("namespace", "id1", "type")
- .setPropertyString("body", "tablet")
- .build(),
- /*logger=*/ null);
- mAppSearchImpl.putDocument(
- "package",
- "database",
- new GenericDocument.Builder<>("namespace", "id2", "type")
- .setPropertyString("body", "tabby")
- .build(),
- /*logger=*/ null);
- mAppSearchImpl.putDocument(
- "package",
- "database",
- new GenericDocument.Builder<>("namespace", "id3", "type")
- .setPropertyString("body", "grabby")
- .build(),
- /*logger=*/ null);
-
- // Now we should get a failure
- GenericDocument document4 =
- new GenericDocument.Builder<>("namespace", "id4", "type").build();
- AppSearchException e =
- assertThrows(
- AppSearchException.class,
- () ->
- mAppSearchImpl.putDocument(
- "package", "database", document4, /*logger=*/ null));
- assertThat(e.getResultCode()).isEqualTo(AppSearchResult.RESULT_OUT_OF_SPACE);
- assertThat(e)
- .hasMessageThat()
- .contains("Package \"package\" exceeded limit of 3 documents");
-
- // Run removebyquery, deleting nothing
- mAppSearchImpl.removeByQuery(
- "package",
- "database",
- "nothing",
- new SearchSpec.Builder().build(),
- /*removeStatsBuilder=*/ null);
-
- // Should still fail
- e =
- assertThrows(
- AppSearchException.class,
- () ->
- mAppSearchImpl.putDocument(
- "package", "database", document4, /*logger=*/ null));
- assertThat(e.getResultCode()).isEqualTo(AppSearchResult.RESULT_OUT_OF_SPACE);
- assertThat(e)
- .hasMessageThat()
- .contains("Package \"package\" exceeded limit of 3 documents");
-
- // Remove "tab*"
- mAppSearchImpl.removeByQuery(
- "package",
- "database",
- "tab",
- new SearchSpec.Builder().build(),
- /*removeStatsBuilder=*/ null);
-
- // Now doc4 and doc5 should work
- mAppSearchImpl.putDocument("package", "database", document4, /*logger=*/ null);
- mAppSearchImpl.putDocument(
- "package",
- "database",
- new GenericDocument.Builder<>("namespace", "id5", "type").build(),
- /*logger=*/ null);
-
- // We only deleted 2 docs so the next one should fail again
- e =
- assertThrows(
- AppSearchException.class,
- () ->
- mAppSearchImpl.putDocument(
- "package",
- "database",
- new GenericDocument.Builder<>("namespace", "id6", "type")
- .build(),
- /*logger=*/ null));
- assertThat(e.getResultCode()).isEqualTo(AppSearchResult.RESULT_OUT_OF_SPACE);
- assertThat(e)
- .hasMessageThat()
- .contains("Package \"package\" exceeded limit of 3 documents");
- }
-
- @Test
- public void testLimitConfig_Replace() throws Exception {
- // Create a new mAppSearchImpl with a lower limit
- mAppSearchImpl.close();
- mAppSearchImpl =
- AppSearchImpl.create(
- mTemporaryFolder.newFolder(),
- new LimitConfig() {
- @Override
- public int getMaxDocumentSizeBytes() {
- return Integer.MAX_VALUE;
- }
-
- @Override
- public int getMaxDocumentCount() {
- return 2;
- }
- },
- /*initStatsBuilder=*/ null,
- ALWAYS_OPTIMIZE);
-
- // Insert schema
- List<AppSearchSchema> schemas =
- Collections.singletonList(
- new AppSearchSchema.Builder("type")
- .addProperty(
- new AppSearchSchema.StringPropertyConfig.Builder("body")
- .build())
- .build());
- mAppSearchImpl.setSchema(
- "package",
- "database",
- schemas,
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0,
- /* setSchemaStatsBuilder= */ null);
-
- // Index a document
- mAppSearchImpl.putDocument(
- "package",
- "database",
- new GenericDocument.Builder<>("namespace", "id1", "type")
- .setPropertyString("body", "id1.orig")
- .build(),
- /*logger=*/ null);
- // Replace it with another doc
- mAppSearchImpl.putDocument(
- "package",
- "database",
- new GenericDocument.Builder<>("namespace", "id1", "type")
- .setPropertyString("body", "id1.new")
- .build(),
- /*logger=*/ null);
-
- // Index id2. This should pass but only because we check for replacements.
- mAppSearchImpl.putDocument(
- "package",
- "database",
- new GenericDocument.Builder<>("namespace", "id2", "type").build(),
- /*logger=*/ null);
-
- // Now we should get a failure on id3
- GenericDocument document3 =
- new GenericDocument.Builder<>("namespace", "id3", "type").build();
- AppSearchException e =
- assertThrows(
- AppSearchException.class,
- () ->
- mAppSearchImpl.putDocument(
- "package", "database", document3, /*logger=*/ null));
- assertThat(e.getResultCode()).isEqualTo(AppSearchResult.RESULT_OUT_OF_SPACE);
- assertThat(e)
- .hasMessageThat()
- .contains("Package \"package\" exceeded limit of 2 documents");
- }
-
- @Test
- public void testLimitConfig_ReplaceReinit() throws Exception {
- // Create a new mAppSearchImpl with a lower limit
- mAppSearchImpl.close();
- File tempFolder = mTemporaryFolder.newFolder();
- mAppSearchImpl =
- AppSearchImpl.create(
- tempFolder,
- new LimitConfig() {
- @Override
- public int getMaxDocumentSizeBytes() {
- return Integer.MAX_VALUE;
- }
-
- @Override
- public int getMaxDocumentCount() {
- return 2;
- }
- },
- /*initStatsBuilder=*/ null,
- ALWAYS_OPTIMIZE);
-
- // Insert schema
- List<AppSearchSchema> schemas =
- Collections.singletonList(
- new AppSearchSchema.Builder("type")
- .addProperty(
- new AppSearchSchema.StringPropertyConfig.Builder("body")
- .build())
- .build());
- mAppSearchImpl.setSchema(
- "package",
- "database",
- schemas,
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0,
- /* setSchemaStatsBuilder= */ null);
-
- // Index a document
- mAppSearchImpl.putDocument(
- "package",
- "database",
- new GenericDocument.Builder<>("namespace", "id1", "type")
- .setPropertyString("body", "id1.orig")
- .build(),
- /*logger=*/ null);
- // Replace it with another doc
- mAppSearchImpl.putDocument(
- "package",
- "database",
- new GenericDocument.Builder<>("namespace", "id1", "type")
- .setPropertyString("body", "id1.new")
- .build(),
- /*logger=*/ null);
-
- // Reinitialize to make sure replacements are correctly accounted for by init
- mAppSearchImpl.close();
- mAppSearchImpl =
- AppSearchImpl.create(
- tempFolder,
- new LimitConfig() {
- @Override
- public int getMaxDocumentSizeBytes() {
- return Integer.MAX_VALUE;
- }
-
- @Override
- public int getMaxDocumentCount() {
- return 2;
- }
- },
- /*initStatsBuilder=*/ null,
- ALWAYS_OPTIMIZE);
-
- // Index id2. This should pass but only because we check for replacements.
- mAppSearchImpl.putDocument(
- "package",
- "database",
- new GenericDocument.Builder<>("namespace", "id2", "type").build(),
- /*logger=*/ null);
-
- // Now we should get a failure on id3
- GenericDocument document3 =
- new GenericDocument.Builder<>("namespace", "id3", "type").build();
- AppSearchException e =
- assertThrows(
- AppSearchException.class,
- () ->
- mAppSearchImpl.putDocument(
- "package", "database", document3, /*logger=*/ null));
- assertThat(e.getResultCode()).isEqualTo(AppSearchResult.RESULT_OUT_OF_SPACE);
- assertThat(e)
- .hasMessageThat()
- .contains("Package \"package\" exceeded limit of 2 documents");
- }
-}
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchLoggerTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchLoggerTest.java
deleted file mode 100644
index 2ab5fd554675..000000000000
--- a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchLoggerTest.java
+++ /dev/null
@@ -1,906 +0,0 @@
-/*
- * Copyright 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.appsearch.external.localstorage;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.appsearch.AppSearchResult;
-import android.app.appsearch.AppSearchSchema;
-import android.app.appsearch.GenericDocument;
-import android.app.appsearch.SearchResultPage;
-import android.app.appsearch.SearchSpec;
-import android.app.appsearch.exceptions.AppSearchException;
-
-import com.android.server.appsearch.external.localstorage.stats.CallStats;
-import com.android.server.appsearch.external.localstorage.stats.InitializeStats;
-import com.android.server.appsearch.external.localstorage.stats.OptimizeStats;
-import com.android.server.appsearch.external.localstorage.stats.PutDocumentStats;
-import com.android.server.appsearch.external.localstorage.stats.RemoveStats;
-import com.android.server.appsearch.external.localstorage.stats.SearchStats;
-import com.android.server.appsearch.external.localstorage.stats.SetSchemaStats;
-import com.android.server.appsearch.icing.proto.DeleteStatsProto;
-import com.android.server.appsearch.icing.proto.DocumentProto;
-import com.android.server.appsearch.icing.proto.InitializeStatsProto;
-import com.android.server.appsearch.icing.proto.OptimizeStatsProto;
-import com.android.server.appsearch.icing.proto.PutDocumentStatsProto;
-import com.android.server.appsearch.icing.proto.PutResultProto;
-import com.android.server.appsearch.icing.proto.QueryStatsProto;
-import com.android.server.appsearch.icing.proto.ScoringSpecProto;
-import com.android.server.appsearch.icing.proto.SetSchemaResultProto;
-import com.android.server.appsearch.icing.proto.StatusProto;
-import com.android.server.appsearch.icing.proto.TermMatchType;
-
-import com.google.common.collect.ImmutableList;
-
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TemporaryFolder;
-
-import java.io.File;
-import java.util.Collections;
-import java.util.List;
-
-public class AppSearchLoggerTest {
- private static final String PACKAGE_NAME = "packageName";
- private static final String DATABASE = "database";
- /**
- * Always trigger optimize in this class. OptimizeStrategy will be tested in its own test class.
- */
- private static final OptimizeStrategy ALWAYS_OPTIMIZE = optimizeInfo -> true;
-
- @Rule public TemporaryFolder mTemporaryFolder = new TemporaryFolder();
- private AppSearchImpl mAppSearchImpl;
- private TestLogger mLogger;
-
- @Before
- public void setUp() throws Exception {
- mAppSearchImpl =
- AppSearchImpl.create(
- mTemporaryFolder.newFolder(),
- new UnlimitedLimitConfig(),
- /*initStatsBuilder=*/ null,
- ALWAYS_OPTIMIZE);
- mLogger = new TestLogger();
- }
-
- // Test only not thread safe.
- public static class TestLogger implements AppSearchLogger {
- @Nullable CallStats mCallStats;
- @Nullable PutDocumentStats mPutDocumentStats;
- @Nullable InitializeStats mInitializeStats;
- @Nullable SearchStats mSearchStats;
- @Nullable RemoveStats mRemoveStats;
- @Nullable OptimizeStats mOptimizeStats;
- @Nullable SetSchemaStats mSetSchemaStats;
-
- @Override
- public void logStats(@NonNull CallStats stats) {
- mCallStats = stats;
- }
-
- @Override
- public void logStats(@NonNull PutDocumentStats stats) {
- mPutDocumentStats = stats;
- }
-
- @Override
- public void logStats(@NonNull InitializeStats stats) {
- mInitializeStats = stats;
- }
-
- @Override
- public void logStats(@NonNull SearchStats stats) {
- mSearchStats = stats;
- }
-
- @Override
- public void logStats(@NonNull RemoveStats stats) {
- mRemoveStats = stats;
- }
-
- @Override
- public void logStats(@NonNull OptimizeStats stats) {
- mOptimizeStats = stats;
- }
-
- @Override
- public void logStats(@NonNull SetSchemaStats stats) {
- mSetSchemaStats = stats;
- }
- }
-
- @Test
- public void testAppSearchLoggerHelper_testCopyNativeStats_initialize() {
- int nativeLatencyMillis = 3;
- int nativeDocumentStoreRecoveryCause = InitializeStatsProto.RecoveryCause.DATA_LOSS_VALUE;
- int nativeIndexRestorationCause =
- InitializeStatsProto.RecoveryCause.INCONSISTENT_WITH_GROUND_TRUTH_VALUE;
- int nativeSchemaStoreRecoveryCause =
- InitializeStatsProto.RecoveryCause.SCHEMA_CHANGES_OUT_OF_SYNC_VALUE;
- int nativeDocumentStoreRecoveryLatencyMillis = 7;
- int nativeIndexRestorationLatencyMillis = 8;
- int nativeSchemaStoreRecoveryLatencyMillis = 9;
- int nativeDocumentStoreDataStatus =
- InitializeStatsProto.DocumentStoreDataStatus.NO_DATA_LOSS_VALUE;
- int nativeNumDocuments = 11;
- int nativeNumSchemaTypes = 12;
- InitializeStatsProto.Builder nativeInitBuilder =
- InitializeStatsProto.newBuilder()
- .setLatencyMs(nativeLatencyMillis)
- .setDocumentStoreRecoveryCause(
- InitializeStatsProto.RecoveryCause.forNumber(
- nativeDocumentStoreRecoveryCause))
- .setIndexRestorationCause(
- InitializeStatsProto.RecoveryCause.forNumber(
- nativeIndexRestorationCause))
- .setSchemaStoreRecoveryCause(
- InitializeStatsProto.RecoveryCause.forNumber(
- nativeSchemaStoreRecoveryCause))
- .setDocumentStoreRecoveryLatencyMs(nativeDocumentStoreRecoveryLatencyMillis)
- .setIndexRestorationLatencyMs(nativeIndexRestorationLatencyMillis)
- .setSchemaStoreRecoveryLatencyMs(nativeSchemaStoreRecoveryLatencyMillis)
- .setDocumentStoreDataStatus(
- InitializeStatsProto.DocumentStoreDataStatus.forNumber(
- nativeDocumentStoreDataStatus))
- .setNumDocuments(nativeNumDocuments)
- .setNumSchemaTypes(nativeNumSchemaTypes);
- InitializeStats.Builder initBuilder = new InitializeStats.Builder();
-
- AppSearchLoggerHelper.copyNativeStats(nativeInitBuilder.build(), initBuilder);
-
- InitializeStats iStats = initBuilder.build();
- assertThat(iStats.getNativeLatencyMillis()).isEqualTo(nativeLatencyMillis);
- assertThat(iStats.getDocumentStoreRecoveryCause())
- .isEqualTo(nativeDocumentStoreRecoveryCause);
- assertThat(iStats.getIndexRestorationCause()).isEqualTo(nativeIndexRestorationCause);
- assertThat(iStats.getSchemaStoreRecoveryCause()).isEqualTo(nativeSchemaStoreRecoveryCause);
- assertThat(iStats.getDocumentStoreRecoveryLatencyMillis())
- .isEqualTo(nativeDocumentStoreRecoveryLatencyMillis);
- assertThat(iStats.getIndexRestorationLatencyMillis())
- .isEqualTo(nativeIndexRestorationLatencyMillis);
- assertThat(iStats.getSchemaStoreRecoveryLatencyMillis())
- .isEqualTo(nativeSchemaStoreRecoveryLatencyMillis);
- assertThat(iStats.getDocumentStoreDataStatus()).isEqualTo(nativeDocumentStoreDataStatus);
- assertThat(iStats.getDocumentCount()).isEqualTo(nativeNumDocuments);
- assertThat(iStats.getSchemaTypeCount()).isEqualTo(nativeNumSchemaTypes);
- }
-
- @Test
- public void testAppSearchLoggerHelper_testCopyNativeStats_putDocument() {
- final int nativeLatencyMillis = 3;
- final int nativeDocumentStoreLatencyMillis = 4;
- final int nativeIndexLatencyMillis = 5;
- final int nativeIndexMergeLatencyMillis = 6;
- final int nativeDocumentSize = 7;
- final int nativeNumTokensIndexed = 8;
- final boolean nativeExceededMaxNumTokens = true;
- PutDocumentStatsProto nativePutDocumentStats =
- PutDocumentStatsProto.newBuilder()
- .setLatencyMs(nativeLatencyMillis)
- .setDocumentStoreLatencyMs(nativeDocumentStoreLatencyMillis)
- .setIndexLatencyMs(nativeIndexLatencyMillis)
- .setIndexMergeLatencyMs(nativeIndexMergeLatencyMillis)
- .setDocumentSize(nativeDocumentSize)
- .setTokenizationStats(
- PutDocumentStatsProto.TokenizationStats.newBuilder()
- .setNumTokensIndexed(nativeNumTokensIndexed)
- .setExceededMaxTokenNum(nativeExceededMaxNumTokens)
- .build())
- .build();
- PutDocumentStats.Builder pBuilder = new PutDocumentStats.Builder(PACKAGE_NAME, DATABASE);
-
- AppSearchLoggerHelper.copyNativeStats(nativePutDocumentStats, pBuilder);
-
- PutDocumentStats pStats = pBuilder.build();
- assertThat(pStats.getNativeLatencyMillis()).isEqualTo(nativeLatencyMillis);
- assertThat(pStats.getNativeDocumentStoreLatencyMillis())
- .isEqualTo(nativeDocumentStoreLatencyMillis);
- assertThat(pStats.getNativeIndexLatencyMillis()).isEqualTo(nativeIndexLatencyMillis);
- assertThat(pStats.getNativeIndexMergeLatencyMillis())
- .isEqualTo(nativeIndexMergeLatencyMillis);
- assertThat(pStats.getNativeDocumentSizeBytes()).isEqualTo(nativeDocumentSize);
- assertThat(pStats.getNativeNumTokensIndexed()).isEqualTo(nativeNumTokensIndexed);
- assertThat(pStats.getNativeExceededMaxNumTokens()).isEqualTo(nativeExceededMaxNumTokens);
- }
-
- @Test
- public void testAppSearchLoggerHelper_testCopyNativeStats_search() {
- int nativeLatencyMillis = 4;
- int nativeNumTerms = 5;
- int nativeQueryLength = 6;
- int nativeNumNamespacesFiltered = 7;
- int nativeNumSchemaTypesFiltered = 8;
- int nativeRequestedPageSize = 9;
- int nativeNumResultsReturnedCurrentPage = 10;
- boolean nativeIsFirstPage = true;
- int nativeParseQueryLatencyMillis = 11;
- int nativeRankingStrategy = ScoringSpecProto.RankingStrategy.Code.CREATION_TIMESTAMP_VALUE;
- int nativeNumDocumentsScored = 13;
- int nativeScoringLatencyMillis = 14;
- int nativeRankingLatencyMillis = 15;
- int nativeNumResultsWithSnippets = 16;
- int nativeDocumentRetrievingLatencyMillis = 17;
- QueryStatsProto nativeQueryStats =
- QueryStatsProto.newBuilder()
- .setLatencyMs(nativeLatencyMillis)
- .setNumTerms(nativeNumTerms)
- .setQueryLength(nativeQueryLength)
- .setNumNamespacesFiltered(nativeNumNamespacesFiltered)
- .setNumSchemaTypesFiltered(nativeNumSchemaTypesFiltered)
- .setRequestedPageSize(nativeRequestedPageSize)
- .setNumResultsReturnedCurrentPage(nativeNumResultsReturnedCurrentPage)
- .setIsFirstPage(nativeIsFirstPage)
- .setParseQueryLatencyMs(nativeParseQueryLatencyMillis)
- .setRankingStrategy(
- ScoringSpecProto.RankingStrategy.Code.forNumber(
- nativeRankingStrategy))
- .setNumDocumentsScored(nativeNumDocumentsScored)
- .setScoringLatencyMs(nativeScoringLatencyMillis)
- .setRankingLatencyMs(nativeRankingLatencyMillis)
- .setNumResultsWithSnippets(nativeNumResultsWithSnippets)
- .setDocumentRetrievalLatencyMs(nativeDocumentRetrievingLatencyMillis)
- .build();
- SearchStats.Builder qBuilder =
- new SearchStats.Builder(SearchStats.VISIBILITY_SCOPE_LOCAL, PACKAGE_NAME)
- .setDatabase(DATABASE);
-
- AppSearchLoggerHelper.copyNativeStats(nativeQueryStats, qBuilder);
-
- SearchStats sStats = qBuilder.build();
- assertThat(sStats.getNativeLatencyMillis()).isEqualTo(nativeLatencyMillis);
- assertThat(sStats.getTermCount()).isEqualTo(nativeNumTerms);
- assertThat(sStats.getQueryLength()).isEqualTo(nativeQueryLength);
- assertThat(sStats.getFilteredNamespaceCount()).isEqualTo(nativeNumNamespacesFiltered);
- assertThat(sStats.getFilteredSchemaTypeCount()).isEqualTo(nativeNumSchemaTypesFiltered);
- assertThat(sStats.getRequestedPageSize()).isEqualTo(nativeRequestedPageSize);
- assertThat(sStats.getCurrentPageReturnedResultCount())
- .isEqualTo(nativeNumResultsReturnedCurrentPage);
- assertThat(sStats.isFirstPage()).isTrue();
- assertThat(sStats.getParseQueryLatencyMillis()).isEqualTo(nativeParseQueryLatencyMillis);
- assertThat(sStats.getRankingStrategy()).isEqualTo(nativeRankingStrategy);
- assertThat(sStats.getScoredDocumentCount()).isEqualTo(nativeNumDocumentsScored);
- assertThat(sStats.getScoringLatencyMillis()).isEqualTo(nativeScoringLatencyMillis);
- assertThat(sStats.getRankingLatencyMillis()).isEqualTo(nativeRankingLatencyMillis);
- assertThat(sStats.getResultWithSnippetsCount()).isEqualTo(nativeNumResultsWithSnippets);
- assertThat(sStats.getDocumentRetrievingLatencyMillis())
- .isEqualTo(nativeDocumentRetrievingLatencyMillis);
- }
-
- @Test
- public void testAppSearchLoggerHelper_testCopyNativeStats_remove() {
- final int nativeLatencyMillis = 1;
- final int nativeDeleteType = 2;
- final int nativeNumDocumentDeleted = 3;
- DeleteStatsProto nativeDeleteStatsProto =
- DeleteStatsProto.newBuilder()
- .setLatencyMs(nativeLatencyMillis)
- .setDeleteType(DeleteStatsProto.DeleteType.Code.forNumber(nativeDeleteType))
- .setNumDocumentsDeleted(nativeNumDocumentDeleted)
- .build();
- RemoveStats.Builder rBuilder = new RemoveStats.Builder("packageName", "database");
-
- AppSearchLoggerHelper.copyNativeStats(nativeDeleteStatsProto, rBuilder);
-
- RemoveStats rStats = rBuilder.build();
- assertThat(rStats.getNativeLatencyMillis()).isEqualTo(nativeLatencyMillis);
- assertThat(rStats.getDeleteType()).isEqualTo(nativeDeleteType);
- assertThat(rStats.getDeletedDocumentCount()).isEqualTo(nativeNumDocumentDeleted);
- }
-
- @Test
- public void testAppSearchLoggerHelper_testCopyNativeStats_optimize() {
- int nativeLatencyMillis = 1;
- int nativeDocumentStoreOptimizeLatencyMillis = 2;
- int nativeIndexRestorationLatencyMillis = 3;
- int nativeNumOriginalDocuments = 4;
- int nativeNumDeletedDocuments = 5;
- int nativeNumExpiredDocuments = 6;
- long nativeStorageSizeBeforeBytes = Integer.MAX_VALUE + 1;
- long nativeStorageSizeAfterBytes = Integer.MAX_VALUE + 2;
- long nativeTimeSinceLastOptimizeMillis = Integer.MAX_VALUE + 3;
- OptimizeStatsProto optimizeStatsProto =
- OptimizeStatsProto.newBuilder()
- .setLatencyMs(nativeLatencyMillis)
- .setDocumentStoreOptimizeLatencyMs(nativeDocumentStoreOptimizeLatencyMillis)
- .setIndexRestorationLatencyMs(nativeIndexRestorationLatencyMillis)
- .setNumOriginalDocuments(nativeNumOriginalDocuments)
- .setNumDeletedDocuments(nativeNumDeletedDocuments)
- .setNumExpiredDocuments(nativeNumExpiredDocuments)
- .setStorageSizeBefore(nativeStorageSizeBeforeBytes)
- .setStorageSizeAfter(nativeStorageSizeAfterBytes)
- .setTimeSinceLastOptimizeMs(nativeTimeSinceLastOptimizeMillis)
- .build();
- OptimizeStats.Builder oBuilder = new OptimizeStats.Builder();
-
- AppSearchLoggerHelper.copyNativeStats(optimizeStatsProto, oBuilder);
-
- OptimizeStats oStats = oBuilder.build();
- assertThat(oStats.getNativeLatencyMillis()).isEqualTo(nativeLatencyMillis);
- assertThat(oStats.getDocumentStoreOptimizeLatencyMillis())
- .isEqualTo(nativeDocumentStoreOptimizeLatencyMillis);
- assertThat(oStats.getIndexRestorationLatencyMillis())
- .isEqualTo(nativeIndexRestorationLatencyMillis);
- assertThat(oStats.getOriginalDocumentCount()).isEqualTo(nativeNumOriginalDocuments);
- assertThat(oStats.getDeletedDocumentCount()).isEqualTo(nativeNumDeletedDocuments);
- assertThat(oStats.getExpiredDocumentCount()).isEqualTo(nativeNumExpiredDocuments);
- assertThat(oStats.getStorageSizeBeforeBytes()).isEqualTo(nativeStorageSizeBeforeBytes);
- assertThat(oStats.getStorageSizeAfterBytes()).isEqualTo(nativeStorageSizeAfterBytes);
- assertThat(oStats.getTimeSinceLastOptimizeMillis())
- .isEqualTo(nativeTimeSinceLastOptimizeMillis);
- }
-
- @Test
- public void testAppSearchLoggerHelper_testCopyNativeStats_setSchema() {
- ImmutableList<String> newSchemaTypeChangeList = ImmutableList.of("new1");
- ImmutableList<String> deletedSchemaTypesList = ImmutableList.of("deleted1", "deleted2");
- ImmutableList<String> compatibleTypesList = ImmutableList.of("compatible1", "compatible2");
- ImmutableList<String> indexIncompatibleTypeChangeList = ImmutableList.of("index1");
- ImmutableList<String> backwardsIncompatibleTypeChangeList = ImmutableList.of("backwards1");
- SetSchemaResultProto setSchemaResultProto =
- SetSchemaResultProto.newBuilder()
- .addAllNewSchemaTypes(newSchemaTypeChangeList)
- .addAllDeletedSchemaTypes(deletedSchemaTypesList)
- .addAllFullyCompatibleChangedSchemaTypes(compatibleTypesList)
- .addAllIndexIncompatibleChangedSchemaTypes(indexIncompatibleTypeChangeList)
- .addAllIncompatibleSchemaTypes(backwardsIncompatibleTypeChangeList)
- .build();
- SetSchemaStats.Builder sBuilder = new SetSchemaStats.Builder(PACKAGE_NAME, DATABASE);
-
- AppSearchLoggerHelper.copyNativeStats(setSchemaResultProto, sBuilder);
-
- SetSchemaStats sStats = sBuilder.build();
- assertThat(sStats.getNewTypeCount()).isEqualTo(newSchemaTypeChangeList.size());
- assertThat(sStats.getDeletedTypeCount()).isEqualTo(deletedSchemaTypesList.size());
- assertThat(sStats.getCompatibleTypeChangeCount()).isEqualTo(compatibleTypesList.size());
- assertThat(sStats.getIndexIncompatibleTypeChangeCount())
- .isEqualTo(indexIncompatibleTypeChangeList.size());
- assertThat(sStats.getBackwardsIncompatibleTypeChangeCount())
- .isEqualTo(backwardsIncompatibleTypeChangeList.size());
- }
-
- //
- // Testing actual logging
- //
- @Test
- public void testLoggingStats_initializeWithoutDocuments_success() throws Exception {
- // Create an unused AppSearchImpl to generated an InitializeStats.
- InitializeStats.Builder initStatsBuilder = new InitializeStats.Builder();
- AppSearchImpl.create(
- mTemporaryFolder.newFolder(),
- new UnlimitedLimitConfig(),
- initStatsBuilder,
- ALWAYS_OPTIMIZE);
- InitializeStats iStats = initStatsBuilder.build();
-
- assertThat(iStats).isNotNull();
- assertThat(iStats.getStatusCode()).isEqualTo(AppSearchResult.RESULT_OK);
- // Total latency captured in LocalStorage
- assertThat(iStats.getTotalLatencyMillis()).isEqualTo(0);
- assertThat(iStats.hasDeSync()).isFalse();
- assertThat(iStats.getNativeLatencyMillis()).isGreaterThan(0);
- assertThat(iStats.getDocumentStoreDataStatus())
- .isEqualTo(InitializeStatsProto.DocumentStoreDataStatus.NO_DATA_LOSS_VALUE);
- assertThat(iStats.getDocumentCount()).isEqualTo(0);
- assertThat(iStats.getSchemaTypeCount()).isEqualTo(0);
- assertThat(iStats.hasReset()).isEqualTo(false);
- assertThat(iStats.getResetStatusCode()).isEqualTo(AppSearchResult.RESULT_OK);
- }
-
- @Test
- public void testLoggingStats_initializeWithDocuments_success() throws Exception {
- final String testPackageName = "testPackage";
- final String testDatabase = "testDatabase";
- final File folder = mTemporaryFolder.newFolder();
-
- AppSearchImpl appSearchImpl =
- AppSearchImpl.create(
- folder,
- new UnlimitedLimitConfig(),
- /*initStatsBuilder=*/ null,
- ALWAYS_OPTIMIZE);
- List<AppSearchSchema> schemas =
- ImmutableList.of(
- new AppSearchSchema.Builder("Type1").build(),
- new AppSearchSchema.Builder("Type2").build());
- appSearchImpl.setSchema(
- testPackageName,
- testDatabase,
- schemas,
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0,
- /* setSchemaStatsBuilder= */ null);
- GenericDocument doc1 = new GenericDocument.Builder<>("namespace", "id1", "Type1").build();
- GenericDocument doc2 = new GenericDocument.Builder<>("namespace", "id2", "Type1").build();
- appSearchImpl.putDocument(testPackageName, testDatabase, doc1, mLogger);
- appSearchImpl.putDocument(testPackageName, testDatabase, doc2, mLogger);
- appSearchImpl.close();
-
- // Create another appsearchImpl on the same folder
- InitializeStats.Builder initStatsBuilder = new InitializeStats.Builder();
- AppSearchImpl.create(folder, new UnlimitedLimitConfig(), initStatsBuilder, ALWAYS_OPTIMIZE);
- InitializeStats iStats = initStatsBuilder.build();
-
- assertThat(iStats).isNotNull();
- assertThat(iStats.getStatusCode()).isEqualTo(AppSearchResult.RESULT_OK);
- // Total latency captured in LocalStorage
- assertThat(iStats.getTotalLatencyMillis()).isEqualTo(0);
- assertThat(iStats.hasDeSync()).isFalse();
- assertThat(iStats.getNativeLatencyMillis()).isGreaterThan(0);
- assertThat(iStats.getDocumentStoreDataStatus())
- .isEqualTo(InitializeStatsProto.DocumentStoreDataStatus.NO_DATA_LOSS_VALUE);
- assertThat(iStats.getDocumentCount()).isEqualTo(2);
- assertThat(iStats.getSchemaTypeCount()).isEqualTo(2);
- assertThat(iStats.hasReset()).isEqualTo(false);
- assertThat(iStats.getResetStatusCode()).isEqualTo(AppSearchResult.RESULT_OK);
- }
-
- @Test
- public void testLoggingStats_initialize_failure() throws Exception {
- final String testPackageName = "testPackage";
- final String testDatabase = "testDatabase";
- final File folder = mTemporaryFolder.newFolder();
-
- AppSearchImpl appSearchImpl =
- AppSearchImpl.create(
- folder,
- new UnlimitedLimitConfig(),
- /*initStatsBuilder=*/ null,
- ALWAYS_OPTIMIZE);
-
- List<AppSearchSchema> schemas =
- ImmutableList.of(
- new AppSearchSchema.Builder("Type1").build(),
- new AppSearchSchema.Builder("Type2").build());
- appSearchImpl.setSchema(
- testPackageName,
- testDatabase,
- schemas,
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0,
- /* setSchemaStatsBuilder= */ null);
-
- // Insert a valid doc
- GenericDocument doc1 = new GenericDocument.Builder<>("namespace", "id1", "Type1").build();
- appSearchImpl.putDocument(testPackageName, testDatabase, doc1, mLogger);
-
- // Insert the invalid doc with an invalid namespace right into icing
- DocumentProto invalidDoc =
- DocumentProto.newBuilder()
- .setNamespace("invalidNamespace")
- .setUri("id2")
- .setSchema(String.format("%s$%s/Type1", testPackageName, testDatabase))
- .build();
- PutResultProto putResultProto = appSearchImpl.mIcingSearchEngineLocked.put(invalidDoc);
- assertThat(putResultProto.getStatus().getCode()).isEqualTo(StatusProto.Code.OK);
- appSearchImpl.close();
-
- // Create another appsearchImpl on the same folder
- InitializeStats.Builder initStatsBuilder = new InitializeStats.Builder();
- AppSearchImpl.create(folder, new UnlimitedLimitConfig(), initStatsBuilder, ALWAYS_OPTIMIZE);
- InitializeStats iStats = initStatsBuilder.build();
-
- // Some of other fields are already covered by AppSearchImplTest#testReset()
- assertThat(iStats).isNotNull();
- assertThat(iStats.getStatusCode()).isEqualTo(AppSearchResult.RESULT_INTERNAL_ERROR);
- assertThat(iStats.hasReset()).isTrue();
- }
-
- @Test
- public void testLoggingStats_putDocument_success() throws Exception {
- // Insert schema
- final String testPackageName = "testPackage";
- final String testDatabase = "testDatabase";
- AppSearchSchema testSchema =
- new AppSearchSchema.Builder("type")
- .addProperty(
- new AppSearchSchema.StringPropertyConfig.Builder("subject")
- .setCardinality(
- AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
- .setIndexingType(
- AppSearchSchema.StringPropertyConfig
- .INDEXING_TYPE_PREFIXES)
- .setTokenizerType(
- AppSearchSchema.StringPropertyConfig
- .TOKENIZER_TYPE_PLAIN)
- .build())
- .build();
- List<AppSearchSchema> schemas = Collections.singletonList(testSchema);
- mAppSearchImpl.setSchema(
- testPackageName,
- testDatabase,
- schemas,
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0,
- /* setSchemaStatsBuilder= */ null);
-
- GenericDocument document =
- new GenericDocument.Builder<>("namespace", "id", "type")
- .setPropertyString("subject", "testPut example1")
- .build();
-
- mAppSearchImpl.putDocument(testPackageName, testDatabase, document, mLogger);
-
- PutDocumentStats pStats = mLogger.mPutDocumentStats;
- assertThat(pStats).isNotNull();
- assertThat(pStats.getPackageName()).isEqualTo(testPackageName);
- assertThat(pStats.getDatabase()).isEqualTo(testDatabase);
- assertThat(pStats.getStatusCode()).isEqualTo(AppSearchResult.RESULT_OK);
- // The latency related native stats have been tested in testCopyNativeStats
- assertThat(pStats.getNativeDocumentSizeBytes()).isGreaterThan(0);
- assertThat(pStats.getNativeNumTokensIndexed()).isGreaterThan(0);
- }
-
- @Test
- public void testLoggingStats_putDocument_failure() throws Exception {
- // Insert schema
- final String testPackageName = "testPackage";
- final String testDatabase = "testDatabase";
- AppSearchSchema testSchema =
- new AppSearchSchema.Builder("type")
- .addProperty(
- new AppSearchSchema.StringPropertyConfig.Builder("subject")
- .setCardinality(
- AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
- .setIndexingType(
- AppSearchSchema.StringPropertyConfig
- .INDEXING_TYPE_PREFIXES)
- .setTokenizerType(
- AppSearchSchema.StringPropertyConfig
- .TOKENIZER_TYPE_PLAIN)
- .build())
- .build();
- List<AppSearchSchema> schemas = Collections.singletonList(testSchema);
- mAppSearchImpl.setSchema(
- testPackageName,
- testDatabase,
- schemas,
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0,
- /* setSchemaStatsBuilder= */ null);
-
- GenericDocument document =
- new GenericDocument.Builder<>("namespace", "id", "type")
- .setPropertyString("nonExist", "testPut example1")
- .build();
-
- AppSearchException exception =
- Assert.assertThrows(
- AppSearchException.class,
- () ->
- mAppSearchImpl.putDocument(
- testPackageName, testDatabase, document, mLogger));
- assertThat(exception.getResultCode()).isEqualTo(AppSearchResult.RESULT_NOT_FOUND);
-
- PutDocumentStats pStats = mLogger.mPutDocumentStats;
- assertThat(pStats).isNotNull();
- assertThat(pStats.getPackageName()).isEqualTo(testPackageName);
- assertThat(pStats.getDatabase()).isEqualTo(testDatabase);
- assertThat(pStats.getStatusCode()).isEqualTo(AppSearchResult.RESULT_NOT_FOUND);
- }
-
- @Test
- public void testLoggingStats_search_success() throws Exception {
- // Insert schema
- final String testPackageName = "testPackage";
- final String testDatabase = "testDatabase";
- AppSearchSchema testSchema =
- new AppSearchSchema.Builder("type")
- .addProperty(
- new AppSearchSchema.StringPropertyConfig.Builder("subject")
- .setCardinality(
- AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
- .setIndexingType(
- AppSearchSchema.StringPropertyConfig
- .INDEXING_TYPE_PREFIXES)
- .setTokenizerType(
- AppSearchSchema.StringPropertyConfig
- .TOKENIZER_TYPE_PLAIN)
- .build())
- .build();
- List<AppSearchSchema> schemas = Collections.singletonList(testSchema);
- mAppSearchImpl.setSchema(
- testPackageName,
- testDatabase,
- schemas,
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0,
- /* setSchemaStatsBuilder= */ null);
- GenericDocument document1 =
- new GenericDocument.Builder<>("namespace", "id1", "type")
- .setPropertyString("subject", "testPut example1")
- .build();
- GenericDocument document2 =
- new GenericDocument.Builder<>("namespace", "id2", "type")
- .setPropertyString("subject", "testPut example2")
- .build();
- GenericDocument document3 =
- new GenericDocument.Builder<>("namespace", "id3", "type")
- .setPropertyString("subject", "testPut 3")
- .build();
- mAppSearchImpl.putDocument(testPackageName, testDatabase, document1, mLogger);
- mAppSearchImpl.putDocument(testPackageName, testDatabase, document2, mLogger);
- mAppSearchImpl.putDocument(testPackageName, testDatabase, document3, mLogger);
-
- // No query filters specified. package2 should only get its own documents back.
- SearchSpec searchSpec =
- new SearchSpec.Builder()
- .setTermMatch(TermMatchType.Code.PREFIX_VALUE)
- .setRankingStrategy(SearchSpec.RANKING_STRATEGY_CREATION_TIMESTAMP)
- .build();
- String queryStr = "testPut e";
- SearchResultPage searchResultPage =
- mAppSearchImpl.query(
- testPackageName, testDatabase, queryStr, searchSpec, /*logger=*/ mLogger);
-
- assertThat(searchResultPage.getResults()).hasSize(2);
- // The ranking strategy is LIFO
- assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document2);
- assertThat(searchResultPage.getResults().get(1).getGenericDocument()).isEqualTo(document1);
-
- SearchStats sStats = mLogger.mSearchStats;
-
- assertThat(sStats).isNotNull();
- assertThat(sStats.getPackageName()).isEqualTo(testPackageName);
- assertThat(sStats.getDatabase()).isEqualTo(testDatabase);
- assertThat(sStats.getStatusCode()).isEqualTo(AppSearchResult.RESULT_OK);
- assertThat(sStats.getTotalLatencyMillis()).isGreaterThan(0);
- assertThat(sStats.getVisibilityScope()).isEqualTo(SearchStats.VISIBILITY_SCOPE_LOCAL);
- assertThat(sStats.getTermCount()).isEqualTo(2);
- assertThat(sStats.getQueryLength()).isEqualTo(queryStr.length());
- assertThat(sStats.getFilteredNamespaceCount()).isEqualTo(1);
- assertThat(sStats.getFilteredSchemaTypeCount()).isEqualTo(1);
- assertThat(sStats.getCurrentPageReturnedResultCount()).isEqualTo(2);
- assertThat(sStats.isFirstPage()).isTrue();
- assertThat(sStats.getRankingStrategy())
- .isEqualTo(SearchSpec.RANKING_STRATEGY_CREATION_TIMESTAMP);
- assertThat(sStats.getScoredDocumentCount()).isEqualTo(2);
- assertThat(sStats.getResultWithSnippetsCount()).isEqualTo(0);
- }
-
- @Test
- public void testLoggingStats_search_failure() throws Exception {
- final String testPackageName = "testPackage";
- final String testDatabase = "testDatabase";
- List<AppSearchSchema> schemas =
- ImmutableList.of(
- new AppSearchSchema.Builder("Type1").build(),
- new AppSearchSchema.Builder("Type2").build());
- mAppSearchImpl.setSchema(
- testPackageName,
- testDatabase,
- schemas,
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0,
- /* setSchemaStatsBuilder= */ null);
-
- SearchSpec searchSpec =
- new SearchSpec.Builder()
- .setTermMatch(TermMatchType.Code.PREFIX_VALUE)
- .setRankingStrategy(SearchSpec.RANKING_STRATEGY_CREATION_TIMESTAMP)
- .addFilterPackageNames("anotherPackage")
- .build();
-
- mAppSearchImpl.query(
- testPackageName,
- testPackageName,
- /* queryExpression= */ "",
- searchSpec,
- /*logger=*/ mLogger);
-
- SearchStats sStats = mLogger.mSearchStats;
- assertThat(sStats).isNotNull();
- assertThat(sStats.getPackageName()).isEqualTo(testPackageName);
- assertThat(sStats.getDatabase()).isEqualTo(testPackageName);
- assertThat(sStats.getStatusCode()).isEqualTo(AppSearchResult.RESULT_SECURITY_ERROR);
- }
-
- @Test
- public void testLoggingStats_remove_success() throws Exception {
- // Insert schema
- final String testPackageName = "testPackage";
- final String testDatabase = "testDatabase";
- final String testNamespace = "testNameSpace";
- final String testId = "id";
- List<AppSearchSchema> schemas =
- Collections.singletonList(new AppSearchSchema.Builder("type").build());
- mAppSearchImpl.setSchema(
- testPackageName,
- testDatabase,
- schemas,
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0,
- /* setSchemaStatsBuilder= */ null);
- GenericDocument document =
- new GenericDocument.Builder<>(testNamespace, testId, "type").build();
- mAppSearchImpl.putDocument(testPackageName, testDatabase, document, /*logger=*/ null);
-
- RemoveStats.Builder rStatsBuilder = new RemoveStats.Builder(testPackageName, testDatabase);
- mAppSearchImpl.remove(testPackageName, testDatabase, testNamespace, testId, rStatsBuilder);
- RemoveStats rStats = rStatsBuilder.build();
-
- assertThat(rStats.getPackageName()).isEqualTo(testPackageName);
- assertThat(rStats.getDatabase()).isEqualTo(testDatabase);
- // delete by namespace + id
- assertThat(rStats.getStatusCode()).isEqualTo(AppSearchResult.RESULT_OK);
- assertThat(rStats.getDeleteType()).isEqualTo(DeleteStatsProto.DeleteType.Code.SINGLE_VALUE);
- assertThat(rStats.getDeletedDocumentCount()).isEqualTo(1);
- }
-
- @Test
- public void testLoggingStats_remove_failure() throws Exception {
- // Insert schema
- final String testPackageName = "testPackage";
- final String testDatabase = "testDatabase";
- final String testNamespace = "testNameSpace";
- final String testId = "id";
- List<AppSearchSchema> schemas =
- Collections.singletonList(new AppSearchSchema.Builder("type").build());
- mAppSearchImpl.setSchema(
- testPackageName,
- testDatabase,
- schemas,
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0,
- /* setSchemaStatsBuilder= */ null);
-
- GenericDocument document =
- new GenericDocument.Builder<>(testNamespace, testId, "type").build();
- mAppSearchImpl.putDocument(testPackageName, testDatabase, document, /*logger=*/ null);
-
- RemoveStats.Builder rStatsBuilder = new RemoveStats.Builder(testPackageName, testDatabase);
-
- AppSearchException exception =
- Assert.assertThrows(
- AppSearchException.class,
- () ->
- mAppSearchImpl.remove(
- testPackageName,
- testDatabase,
- testNamespace,
- "invalidId",
- rStatsBuilder));
- assertThat(exception.getResultCode()).isEqualTo(AppSearchResult.RESULT_NOT_FOUND);
-
- RemoveStats rStats = rStatsBuilder.build();
- assertThat(rStats.getPackageName()).isEqualTo(testPackageName);
- assertThat(rStats.getDatabase()).isEqualTo(testDatabase);
- assertThat(rStats.getStatusCode()).isEqualTo(AppSearchResult.RESULT_NOT_FOUND);
- // delete by namespace + id
- assertThat(rStats.getDeleteType()).isEqualTo(DeleteStatsProto.DeleteType.Code.SINGLE_VALUE);
- assertThat(rStats.getDeletedDocumentCount()).isEqualTo(0);
- }
-
- @Test
- public void testLoggingStats_removeByQuery_success() throws Exception {
- // Insert schema
- final String testPackageName = "testPackage";
- final String testDatabase = "testDatabase";
- final String testNamespace = "testNameSpace";
- List<AppSearchSchema> schemas =
- Collections.singletonList(new AppSearchSchema.Builder("type").build());
- mAppSearchImpl.setSchema(
- testPackageName,
- testDatabase,
- schemas,
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0,
- /* setSchemaStatsBuilder= */ null);
- GenericDocument document1 =
- new GenericDocument.Builder<>(testNamespace, "id1", "type").build();
- GenericDocument document2 =
- new GenericDocument.Builder<>(testNamespace, "id2", "type").build();
- mAppSearchImpl.putDocument(testPackageName, testDatabase, document1, mLogger);
- mAppSearchImpl.putDocument(testPackageName, testDatabase, document2, mLogger);
- // No query filters specified. package2 should only get its own documents back.
- SearchSpec searchSpec =
- new SearchSpec.Builder().setTermMatch(TermMatchType.Code.PREFIX_VALUE).build();
-
- RemoveStats.Builder rStatsBuilder = new RemoveStats.Builder(testPackageName, testDatabase);
- mAppSearchImpl.removeByQuery(
- testPackageName, testDatabase, /*queryExpression=*/ "", searchSpec, rStatsBuilder);
- RemoveStats rStats = rStatsBuilder.build();
-
- assertThat(rStats.getPackageName()).isEqualTo(testPackageName);
- assertThat(rStats.getDatabase()).isEqualTo(testDatabase);
- assertThat(rStats.getStatusCode()).isEqualTo(AppSearchResult.RESULT_OK);
- // delete by query
- assertThat(rStats.getDeleteType())
- .isEqualTo(DeleteStatsProto.DeleteType.Code.DEPRECATED_QUERY_VALUE);
- assertThat(rStats.getDeletedDocumentCount()).isEqualTo(2);
- }
-
- @Test
- public void testLoggingStats_setSchema() throws Exception {
- AppSearchSchema schema1 =
- new AppSearchSchema.Builder("testSchema")
- .addProperty(
- new AppSearchSchema.StringPropertyConfig.Builder("subject")
- .setCardinality(
- AppSearchSchema.PropertyConfig.CARDINALITY_REQUIRED)
- .setIndexingType(
- AppSearchSchema.StringPropertyConfig
- .INDEXING_TYPE_PREFIXES)
- .setTokenizerType(
- AppSearchSchema.StringPropertyConfig
- .TOKENIZER_TYPE_PLAIN)
- .build())
- .build();
- mAppSearchImpl.setSchema(
- PACKAGE_NAME,
- DATABASE,
- Collections.singletonList(schema1),
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0,
- /* setSchemaStatsBuilder= */ null);
-
- // create a backwards incompatible schema
- SetSchemaStats.Builder sStatsBuilder = new SetSchemaStats.Builder(PACKAGE_NAME, DATABASE);
- AppSearchSchema schema2 = new AppSearchSchema.Builder("testSchema").build();
- mAppSearchImpl.setSchema(
- PACKAGE_NAME,
- DATABASE,
- Collections.singletonList(schema2),
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0,
- /* setSchemaStatsBuilder= */ sStatsBuilder);
-
- SetSchemaStats sStats = sStatsBuilder.build();
- assertThat(sStats.getPackageName()).isEqualTo(PACKAGE_NAME);
- assertThat(sStats.getDatabase()).isEqualTo(DATABASE);
- assertThat(sStats.getNewTypeCount()).isEqualTo(0);
- assertThat(sStats.getCompatibleTypeChangeCount()).isEqualTo(0);
- assertThat(sStats.getIndexIncompatibleTypeChangeCount()).isEqualTo(1);
- assertThat(sStats.getBackwardsIncompatibleTypeChangeCount()).isEqualTo(1);
- }
-}
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverterTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverterTest.java
deleted file mode 100644
index 204fc54fab5b..000000000000
--- a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverterTest.java
+++ /dev/null
@@ -1,256 +0,0 @@
-/*
- * 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.appsearch.external.localstorage.converter;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.app.appsearch.GenericDocument;
-
-import com.android.server.appsearch.icing.proto.DocumentProto;
-import com.android.server.appsearch.icing.proto.PropertyConfigProto;
-import com.android.server.appsearch.icing.proto.PropertyProto;
-import com.android.server.appsearch.icing.proto.SchemaTypeConfigProto;
-import com.android.server.appsearch.protobuf.ByteString;
-
-import com.google.common.collect.ImmutableMap;
-
-import org.junit.Test;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-public class GenericDocumentToProtoConverterTest {
- private static final byte[] BYTE_ARRAY_1 = new byte[] {(byte) 1, (byte) 2, (byte) 3};
- private static final byte[] BYTE_ARRAY_2 = new byte[] {(byte) 4, (byte) 5, (byte) 6, (byte) 7};
- private static final String SCHEMA_TYPE_1 = "sDocumentPropertiesSchemaType1";
- private static final String SCHEMA_TYPE_2 = "sDocumentPropertiesSchemaType2";
- private static final GenericDocument DOCUMENT_PROPERTIES_1 =
- new GenericDocument.Builder<GenericDocument.Builder<?>>(
- "namespace", "sDocumentProperties1", SCHEMA_TYPE_1)
- .setCreationTimestampMillis(12345L)
- .build();
- private static final GenericDocument DOCUMENT_PROPERTIES_2 =
- new GenericDocument.Builder<GenericDocument.Builder<?>>(
- "namespace", "sDocumentProperties2", SCHEMA_TYPE_2)
- .setCreationTimestampMillis(6789L)
- .build();
- private static final SchemaTypeConfigProto SCHEMA_PROTO_1 =
- SchemaTypeConfigProto.newBuilder().setSchemaType(SCHEMA_TYPE_1).build();
- private static final SchemaTypeConfigProto SCHEMA_PROTO_2 =
- SchemaTypeConfigProto.newBuilder().setSchemaType(SCHEMA_TYPE_2).build();
- private static final String PREFIX = "package$databaseName/";
- private static final Map<String, SchemaTypeConfigProto> SCHEMA_MAP =
- ImmutableMap.of(
- PREFIX + SCHEMA_TYPE_1, SCHEMA_PROTO_1, PREFIX + SCHEMA_TYPE_2, SCHEMA_PROTO_2);
-
- @Test
- public void testDocumentProtoConvert() {
- GenericDocument document =
- new GenericDocument.Builder<GenericDocument.Builder<?>>(
- "namespace", "id1", SCHEMA_TYPE_1)
- .setCreationTimestampMillis(5L)
- .setScore(1)
- .setTtlMillis(1L)
- .setPropertyLong("longKey1", 1L)
- .setPropertyDouble("doubleKey1", 1.0)
- .setPropertyBoolean("booleanKey1", true)
- .setPropertyString("stringKey1", "test-value1")
- .setPropertyBytes("byteKey1", BYTE_ARRAY_1, BYTE_ARRAY_2)
- .setPropertyDocument("documentKey1", DOCUMENT_PROPERTIES_1)
- .setPropertyDocument("documentKey2", DOCUMENT_PROPERTIES_2)
- .build();
-
- // Create the Document proto. Need to sort the property order by key.
- DocumentProto.Builder documentProtoBuilder =
- DocumentProto.newBuilder()
- .setUri("id1")
- .setSchema(SCHEMA_TYPE_1)
- .setCreationTimestampMs(5L)
- .setScore(1)
- .setTtlMs(1L)
- .setNamespace("namespace");
- HashMap<String, PropertyProto.Builder> propertyProtoMap = new HashMap<>();
- propertyProtoMap.put(
- "longKey1", PropertyProto.newBuilder().setName("longKey1").addInt64Values(1L));
- propertyProtoMap.put(
- "doubleKey1",
- PropertyProto.newBuilder().setName("doubleKey1").addDoubleValues(1.0));
- propertyProtoMap.put(
- "booleanKey1",
- PropertyProto.newBuilder().setName("booleanKey1").addBooleanValues(true));
- propertyProtoMap.put(
- "stringKey1",
- PropertyProto.newBuilder().setName("stringKey1").addStringValues("test-value1"));
- propertyProtoMap.put(
- "byteKey1",
- PropertyProto.newBuilder()
- .setName("byteKey1")
- .addBytesValues(ByteString.copyFrom(BYTE_ARRAY_1))
- .addBytesValues(ByteString.copyFrom(BYTE_ARRAY_2)));
- propertyProtoMap.put(
- "documentKey1",
- PropertyProto.newBuilder()
- .setName("documentKey1")
- .addDocumentValues(
- GenericDocumentToProtoConverter.toDocumentProto(
- DOCUMENT_PROPERTIES_1)));
- propertyProtoMap.put(
- "documentKey2",
- PropertyProto.newBuilder()
- .setName("documentKey2")
- .addDocumentValues(
- GenericDocumentToProtoConverter.toDocumentProto(
- DOCUMENT_PROPERTIES_2)));
- List<String> sortedKey = new ArrayList<>(propertyProtoMap.keySet());
- Collections.sort(sortedKey);
- for (String key : sortedKey) {
- documentProtoBuilder.addProperties(propertyProtoMap.get(key));
- }
- DocumentProto documentProto = documentProtoBuilder.build();
-
- GenericDocument convertedGenericDocument =
- GenericDocumentToProtoConverter.toGenericDocument(
- documentProto, PREFIX, SCHEMA_MAP);
- DocumentProto convertedDocumentProto =
- GenericDocumentToProtoConverter.toDocumentProto(document);
-
- assertThat(convertedDocumentProto).isEqualTo(documentProto);
- assertThat(convertedGenericDocument).isEqualTo(document);
- }
-
- @Test
- public void testConvertDocument_whenPropertyHasEmptyList() {
- String emptyStringPropertyName = "emptyStringProperty";
- DocumentProto documentProto =
- DocumentProto.newBuilder()
- .setUri("id1")
- .setSchema(SCHEMA_TYPE_1)
- .setCreationTimestampMs(5L)
- .setNamespace("namespace")
- .addProperties(
- PropertyProto.newBuilder().setName(emptyStringPropertyName).build())
- .build();
-
- PropertyConfigProto emptyStringListProperty =
- PropertyConfigProto.newBuilder()
- .setCardinality(PropertyConfigProto.Cardinality.Code.REPEATED)
- .setDataType(PropertyConfigProto.DataType.Code.STRING)
- .setPropertyName(emptyStringPropertyName)
- .build();
- SchemaTypeConfigProto schemaTypeConfigProto =
- SchemaTypeConfigProto.newBuilder()
- .addProperties(emptyStringListProperty)
- .setSchemaType(SCHEMA_TYPE_1)
- .build();
- Map<String, SchemaTypeConfigProto> schemaMap =
- ImmutableMap.of(PREFIX + SCHEMA_TYPE_1, schemaTypeConfigProto);
-
- GenericDocument convertedDocument =
- GenericDocumentToProtoConverter.toGenericDocument(documentProto, PREFIX, schemaMap);
-
- GenericDocument expectedDocument =
- new GenericDocument.Builder<GenericDocument.Builder<?>>(
- "namespace", "id1", SCHEMA_TYPE_1)
- .setCreationTimestampMillis(5L)
- .setPropertyString(emptyStringPropertyName)
- .build();
- assertThat(convertedDocument).isEqualTo(expectedDocument);
- assertThat(expectedDocument.getPropertyStringArray(emptyStringPropertyName)).isEmpty();
- }
-
- @Test
- public void testConvertDocument_whenNestedDocumentPropertyHasEmptyList() {
- String emptyStringPropertyName = "emptyStringProperty";
- String documentPropertyName = "documentProperty";
- DocumentProto nestedDocumentProto =
- DocumentProto.newBuilder()
- .setUri("id2")
- .setSchema(SCHEMA_TYPE_2)
- .setCreationTimestampMs(5L)
- .setNamespace("namespace")
- .addProperties(
- PropertyProto.newBuilder().setName(emptyStringPropertyName).build())
- .build();
- DocumentProto documentProto =
- DocumentProto.newBuilder()
- .setUri("id1")
- .setSchema(SCHEMA_TYPE_1)
- .setCreationTimestampMs(5L)
- .setNamespace("namespace")
- .addProperties(
- PropertyProto.newBuilder()
- .addDocumentValues(nestedDocumentProto)
- .setName(documentPropertyName)
- .build())
- .build();
-
- PropertyConfigProto documentProperty =
- PropertyConfigProto.newBuilder()
- .setCardinality(PropertyConfigProto.Cardinality.Code.REPEATED)
- .setDataType(PropertyConfigProto.DataType.Code.DOCUMENT)
- .setPropertyName(documentPropertyName)
- .setSchemaType(SCHEMA_TYPE_2)
- .build();
- SchemaTypeConfigProto schemaTypeConfigProto =
- SchemaTypeConfigProto.newBuilder()
- .addProperties(documentProperty)
- .setSchemaType(SCHEMA_TYPE_1)
- .build();
- PropertyConfigProto emptyStringListProperty =
- PropertyConfigProto.newBuilder()
- .setCardinality(PropertyConfigProto.Cardinality.Code.REPEATED)
- .setDataType(PropertyConfigProto.DataType.Code.STRING)
- .setPropertyName(emptyStringPropertyName)
- .build();
- SchemaTypeConfigProto nestedSchemaTypeConfigProto =
- SchemaTypeConfigProto.newBuilder()
- .addProperties(emptyStringListProperty)
- .setSchemaType(SCHEMA_TYPE_2)
- .build();
- Map<String, SchemaTypeConfigProto> schemaMap =
- ImmutableMap.of(
- PREFIX + SCHEMA_TYPE_1,
- schemaTypeConfigProto,
- PREFIX + SCHEMA_TYPE_2,
- nestedSchemaTypeConfigProto);
-
- GenericDocument convertedDocument =
- GenericDocumentToProtoConverter.toGenericDocument(documentProto, PREFIX, schemaMap);
-
- GenericDocument expectedDocument =
- new GenericDocument.Builder<GenericDocument.Builder<?>>(
- "namespace", "id1", SCHEMA_TYPE_1)
- .setCreationTimestampMillis(5L)
- .setPropertyDocument(
- documentPropertyName,
- new GenericDocument.Builder<GenericDocument.Builder<?>>(
- "namespace", "id2", SCHEMA_TYPE_2)
- .setCreationTimestampMillis(5L)
- .setPropertyString(emptyStringPropertyName)
- .build())
- .build();
- assertThat(convertedDocument).isEqualTo(expectedDocument);
- assertThat(
- expectedDocument
- .getPropertyDocument(documentPropertyName)
- .getPropertyStringArray(emptyStringPropertyName))
- .isEmpty();
- }
-}
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SchemaToProtoConverterTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SchemaToProtoConverterTest.java
deleted file mode 100644
index ebceba4ffa71..000000000000
--- a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SchemaToProtoConverterTest.java
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * 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.appsearch.external.localstorage.converter;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.app.appsearch.AppSearchSchema;
-
-import com.android.server.appsearch.icing.proto.PropertyConfigProto;
-import com.android.server.appsearch.icing.proto.SchemaTypeConfigProto;
-import com.android.server.appsearch.icing.proto.StringIndexingConfig;
-import com.android.server.appsearch.icing.proto.TermMatchType;
-
-import org.junit.Test;
-
-public class SchemaToProtoConverterTest {
- @Test
- public void testGetProto_Email() {
- AppSearchSchema emailSchema =
- new AppSearchSchema.Builder("Email")
- .addProperty(
- new AppSearchSchema.StringPropertyConfig.Builder("subject")
- .setCardinality(
- AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
- .setIndexingType(
- AppSearchSchema.StringPropertyConfig
- .INDEXING_TYPE_PREFIXES)
- .setTokenizerType(
- AppSearchSchema.StringPropertyConfig
- .TOKENIZER_TYPE_PLAIN)
- .build())
- .addProperty(
- new AppSearchSchema.StringPropertyConfig.Builder("body")
- .setCardinality(
- AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
- .setIndexingType(
- AppSearchSchema.StringPropertyConfig
- .INDEXING_TYPE_PREFIXES)
- .setTokenizerType(
- AppSearchSchema.StringPropertyConfig
- .TOKENIZER_TYPE_PLAIN)
- .build())
- .build();
-
- SchemaTypeConfigProto expectedEmailProto =
- SchemaTypeConfigProto.newBuilder()
- .setSchemaType("Email")
- .setVersion(12345)
- .addProperties(
- PropertyConfigProto.newBuilder()
- .setPropertyName("subject")
- .setDataType(PropertyConfigProto.DataType.Code.STRING)
- .setCardinality(
- PropertyConfigProto.Cardinality.Code.OPTIONAL)
- .setStringIndexingConfig(
- StringIndexingConfig.newBuilder()
- .setTokenizerType(
- StringIndexingConfig.TokenizerType
- .Code.PLAIN)
- .setTermMatchType(
- TermMatchType.Code.PREFIX)))
- .addProperties(
- PropertyConfigProto.newBuilder()
- .setPropertyName("body")
- .setDataType(PropertyConfigProto.DataType.Code.STRING)
- .setCardinality(
- PropertyConfigProto.Cardinality.Code.OPTIONAL)
- .setStringIndexingConfig(
- StringIndexingConfig.newBuilder()
- .setTokenizerType(
- StringIndexingConfig.TokenizerType
- .Code.PLAIN)
- .setTermMatchType(
- TermMatchType.Code.PREFIX)))
- .build();
-
- assertThat(SchemaToProtoConverter.toSchemaTypeConfigProto(emailSchema, /*version=*/ 12345))
- .isEqualTo(expectedEmailProto);
- assertThat(SchemaToProtoConverter.toAppSearchSchema(expectedEmailProto))
- .isEqualTo(emailSchema);
- }
-
- @Test
- public void testGetProto_MusicRecording() {
- AppSearchSchema musicRecordingSchema =
- new AppSearchSchema.Builder("MusicRecording")
- .addProperty(
- new AppSearchSchema.StringPropertyConfig.Builder("artist")
- .setCardinality(
- AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
- .setIndexingType(
- AppSearchSchema.StringPropertyConfig
- .INDEXING_TYPE_PREFIXES)
- .setTokenizerType(
- AppSearchSchema.StringPropertyConfig
- .TOKENIZER_TYPE_PLAIN)
- .build())
- .addProperty(
- new AppSearchSchema.LongPropertyConfig.Builder("pubDate")
- .setCardinality(
- AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
- .build())
- .build();
-
- SchemaTypeConfigProto expectedMusicRecordingProto =
- SchemaTypeConfigProto.newBuilder()
- .setSchemaType("MusicRecording")
- .setVersion(0)
- .addProperties(
- PropertyConfigProto.newBuilder()
- .setPropertyName("artist")
- .setDataType(PropertyConfigProto.DataType.Code.STRING)
- .setCardinality(
- PropertyConfigProto.Cardinality.Code.REPEATED)
- .setStringIndexingConfig(
- StringIndexingConfig.newBuilder()
- .setTokenizerType(
- StringIndexingConfig.TokenizerType
- .Code.PLAIN)
- .setTermMatchType(
- TermMatchType.Code.PREFIX)))
- .addProperties(
- PropertyConfigProto.newBuilder()
- .setPropertyName("pubDate")
- .setDataType(PropertyConfigProto.DataType.Code.INT64)
- .setCardinality(
- PropertyConfigProto.Cardinality.Code.OPTIONAL))
- .build();
-
- assertThat(
- SchemaToProtoConverter.toSchemaTypeConfigProto(
- musicRecordingSchema, /*version=*/ 0))
- .isEqualTo(expectedMusicRecordingProto);
- assertThat(SchemaToProtoConverter.toAppSearchSchema(expectedMusicRecordingProto))
- .isEqualTo(musicRecordingSchema);
- }
-}
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SnippetTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SnippetTest.java
deleted file mode 100644
index 992961c0c23a..000000000000
--- a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SnippetTest.java
+++ /dev/null
@@ -1,322 +0,0 @@
-/*
- * 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.appsearch.external.localstorage.converter;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.app.appsearch.SearchResult;
-import android.app.appsearch.SearchResultPage;
-
-import com.android.server.appsearch.external.localstorage.util.PrefixUtil;
-import com.android.server.appsearch.icing.proto.DocumentProto;
-import com.android.server.appsearch.icing.proto.PropertyProto;
-import com.android.server.appsearch.icing.proto.SchemaTypeConfigProto;
-import com.android.server.appsearch.icing.proto.SearchResultProto;
-import com.android.server.appsearch.icing.proto.SnippetMatchProto;
-import com.android.server.appsearch.icing.proto.SnippetProto;
-
-import org.junit.Test;
-
-import java.util.Collections;
-import java.util.Map;
-
-public class SnippetTest {
- private static final String SCHEMA_TYPE = "schema1";
- private static final String PACKAGE_NAME = "packageName";
- private static final String DATABASE_NAME = "databaseName";
- private static final String PREFIX = PrefixUtil.createPrefix(PACKAGE_NAME, DATABASE_NAME);
- private static final SchemaTypeConfigProto SCHEMA_TYPE_CONFIG_PROTO =
- SchemaTypeConfigProto.newBuilder().setSchemaType(PREFIX + SCHEMA_TYPE).build();
- private static final Map<String, Map<String, SchemaTypeConfigProto>> SCHEMA_MAP =
- Collections.singletonMap(
- PREFIX,
- Collections.singletonMap(PREFIX + SCHEMA_TYPE, SCHEMA_TYPE_CONFIG_PROTO));
-
- @Test
- public void testSingleStringSnippet() {
- final String propertyKeyString = "content";
- final String propertyValueString =
- "A commonly used fake word is foo.\n"
- + " Another nonsense word that’s used a lot\n"
- + " is bar.\n";
- final String id = "id1";
- final String exactMatch = "foo";
- final String window = "is foo";
-
- // Building the SearchResult received from query.
- DocumentProto documentProto =
- DocumentProto.newBuilder()
- .setUri(id)
- .setSchema(SCHEMA_TYPE)
- .addProperties(
- PropertyProto.newBuilder()
- .setName(propertyKeyString)
- .addStringValues(propertyValueString))
- .build();
- SnippetProto snippetProto =
- SnippetProto.newBuilder()
- .addEntries(
- SnippetProto.EntryProto.newBuilder()
- .setPropertyName(propertyKeyString)
- .addSnippetMatches(
- SnippetMatchProto.newBuilder()
- .setExactMatchBytePosition(29)
- .setExactMatchByteLength(3)
- .setExactMatchUtf16Position(29)
- .setExactMatchUtf16Length(3)
- .setWindowBytePosition(26)
- .setWindowByteLength(6)
- .setWindowUtf16Position(26)
- .setWindowUtf16Length(6)))
- .build();
- SearchResultProto searchResultProto =
- SearchResultProto.newBuilder()
- .addResults(
- SearchResultProto.ResultProto.newBuilder()
- .setDocument(documentProto)
- .setSnippet(snippetProto))
- .build();
-
- // Making ResultReader and getting Snippet values.
- SearchResultPage searchResultPage =
- SearchResultToProtoConverter.toSearchResultPage(
- searchResultProto,
- Collections.singletonList(PACKAGE_NAME),
- Collections.singletonList(DATABASE_NAME),
- SCHEMA_MAP);
- assertThat(searchResultPage.getResults()).hasSize(1);
- SearchResult.MatchInfo match = searchResultPage.getResults().get(0).getMatchInfos().get(0);
- assertThat(match.getPropertyPath()).isEqualTo(propertyKeyString);
- assertThat(match.getFullText()).isEqualTo(propertyValueString);
- assertThat(match.getExactMatch()).isEqualTo(exactMatch);
- assertThat(match.getExactMatchRange())
- .isEqualTo(new SearchResult.MatchRange(/*lower=*/ 29, /*upper=*/ 32));
- assertThat(match.getFullText()).isEqualTo(propertyValueString);
- assertThat(match.getSnippetRange())
- .isEqualTo(new SearchResult.MatchRange(/*lower=*/ 26, /*upper=*/ 32));
- assertThat(match.getSnippet()).isEqualTo(window);
- }
-
- @Test
- public void testNoSnippets() {
- final String propertyKeyString = "content";
- final String propertyValueString =
- "A commonly used fake word is foo.\n"
- + " Another nonsense word that’s used a lot\n"
- + " is bar.\n";
- final String id = "id1";
-
- // Building the SearchResult received from query.
- DocumentProto documentProto =
- DocumentProto.newBuilder()
- .setUri(id)
- .setSchema(SCHEMA_TYPE)
- .addProperties(
- PropertyProto.newBuilder()
- .setName(propertyKeyString)
- .addStringValues(propertyValueString))
- .build();
- SearchResultProto searchResultProto =
- SearchResultProto.newBuilder()
- .addResults(
- SearchResultProto.ResultProto.newBuilder()
- .setDocument(documentProto))
- .build();
-
- SearchResultPage searchResultPage =
- SearchResultToProtoConverter.toSearchResultPage(
- searchResultProto,
- Collections.singletonList(PACKAGE_NAME),
- Collections.singletonList(DATABASE_NAME),
- SCHEMA_MAP);
- assertThat(searchResultPage.getResults()).hasSize(1);
- assertThat(searchResultPage.getResults().get(0).getMatchInfos()).isEmpty();
- }
-
- @Test
- public void testMultipleStringSnippet() {
- // Building the SearchResult received from query.
- DocumentProto documentProto =
- DocumentProto.newBuilder()
- .setUri("uri1")
- .setSchema(SCHEMA_TYPE)
- .addProperties(
- PropertyProto.newBuilder()
- .setName("senderName")
- .addStringValues("Test Name Jr."))
- .addProperties(
- PropertyProto.newBuilder()
- .setName("senderEmail")
- .addStringValues("TestNameJr@gmail.com"))
- .build();
- SnippetProto snippetProto =
- SnippetProto.newBuilder()
- .addEntries(
- SnippetProto.EntryProto.newBuilder()
- .setPropertyName("senderName")
- .addSnippetMatches(
- SnippetMatchProto.newBuilder()
- .setExactMatchBytePosition(0)
- .setExactMatchByteLength(4)
- .setExactMatchUtf16Position(0)
- .setExactMatchUtf16Length(4)
- .setWindowBytePosition(0)
- .setWindowByteLength(9)
- .setWindowUtf16Position(0)
- .setWindowUtf16Length(9)))
- .addEntries(
- SnippetProto.EntryProto.newBuilder()
- .setPropertyName("senderEmail")
- .addSnippetMatches(
- SnippetMatchProto.newBuilder()
- .setExactMatchBytePosition(0)
- .setExactMatchByteLength(20)
- .setExactMatchUtf16Position(0)
- .setExactMatchUtf16Length(20)
- .setWindowBytePosition(0)
- .setWindowByteLength(20)
- .setWindowUtf16Position(0)
- .setWindowUtf16Length(20)))
- .build();
- SearchResultProto searchResultProto =
- SearchResultProto.newBuilder()
- .addResults(
- SearchResultProto.ResultProto.newBuilder()
- .setDocument(documentProto)
- .setSnippet(snippetProto))
- .build();
-
- // Making ResultReader and getting Snippet values.
- SearchResultPage searchResultPage =
- SearchResultToProtoConverter.toSearchResultPage(
- searchResultProto,
- Collections.singletonList(PACKAGE_NAME),
- Collections.singletonList(DATABASE_NAME),
- SCHEMA_MAP);
- assertThat(searchResultPage.getResults()).hasSize(1);
- SearchResult.MatchInfo match1 = searchResultPage.getResults().get(0).getMatchInfos().get(0);
- assertThat(match1.getPropertyPath()).isEqualTo("senderName");
- assertThat(match1.getFullText()).isEqualTo("Test Name Jr.");
- assertThat(match1.getExactMatchRange())
- .isEqualTo(new SearchResult.MatchRange(/*lower=*/ 0, /*upper=*/ 4));
- assertThat(match1.getExactMatch()).isEqualTo("Test");
- assertThat(match1.getSnippetRange())
- .isEqualTo(new SearchResult.MatchRange(/*lower=*/ 0, /*upper=*/ 9));
- assertThat(match1.getSnippet()).isEqualTo("Test Name");
-
- SearchResult.MatchInfo match2 = searchResultPage.getResults().get(0).getMatchInfos().get(1);
- assertThat(match2.getPropertyPath()).isEqualTo("senderEmail");
- assertThat(match2.getFullText()).isEqualTo("TestNameJr@gmail.com");
- assertThat(match2.getExactMatchRange())
- .isEqualTo(new SearchResult.MatchRange(/*lower=*/ 0, /*upper=*/ 20));
- assertThat(match2.getExactMatch()).isEqualTo("TestNameJr@gmail.com");
- assertThat(match2.getSnippetRange())
- .isEqualTo(new SearchResult.MatchRange(/*lower=*/ 0, /*upper=*/ 20));
- assertThat(match2.getSnippet()).isEqualTo("TestNameJr@gmail.com");
- }
-
- @Test
- public void testNestedDocumentSnippet() {
- // Building the SearchResult received from query.
- DocumentProto documentProto =
- DocumentProto.newBuilder()
- .setUri("id1")
- .setSchema(SCHEMA_TYPE)
- .addProperties(
- PropertyProto.newBuilder()
- .setName("sender")
- .addDocumentValues(
- DocumentProto.newBuilder()
- .addProperties(
- PropertyProto.newBuilder()
- .setName("name")
- .addStringValues(
- "Test Name Jr."))
- .addProperties(
- PropertyProto.newBuilder()
- .setName("email")
- .addStringValues(
- "TestNameJr@gmail.com")
- .addStringValues(
- "TestNameJr2@gmail.com"))))
- .build();
- SnippetProto snippetProto =
- SnippetProto.newBuilder()
- .addEntries(
- SnippetProto.EntryProto.newBuilder()
- .setPropertyName("sender.name")
- .addSnippetMatches(
- SnippetMatchProto.newBuilder()
- .setExactMatchBytePosition(0)
- .setExactMatchByteLength(4)
- .setExactMatchUtf16Position(0)
- .setExactMatchUtf16Length(4)
- .setWindowBytePosition(0)
- .setWindowByteLength(9)
- .setWindowUtf16Position(0)
- .setWindowUtf16Length(9)))
- .addEntries(
- SnippetProto.EntryProto.newBuilder()
- .setPropertyName("sender.email[1]")
- .addSnippetMatches(
- SnippetMatchProto.newBuilder()
- .setExactMatchBytePosition(0)
- .setExactMatchByteLength(21)
- .setExactMatchUtf16Position(0)
- .setExactMatchUtf16Length(21)
- .setWindowBytePosition(0)
- .setWindowByteLength(21)
- .setWindowUtf16Position(0)
- .setWindowUtf16Length(21)))
- .build();
- SearchResultProto searchResultProto =
- SearchResultProto.newBuilder()
- .addResults(
- SearchResultProto.ResultProto.newBuilder()
- .setDocument(documentProto)
- .setSnippet(snippetProto))
- .build();
-
- // Making ResultReader and getting Snippet values.
- SearchResultPage searchResultPage =
- SearchResultToProtoConverter.toSearchResultPage(
- searchResultProto,
- Collections.singletonList(PACKAGE_NAME),
- Collections.singletonList(DATABASE_NAME),
- SCHEMA_MAP);
- assertThat(searchResultPage.getResults()).hasSize(1);
- SearchResult.MatchInfo match1 = searchResultPage.getResults().get(0).getMatchInfos().get(0);
- assertThat(match1.getPropertyPath()).isEqualTo("sender.name");
- assertThat(match1.getFullText()).isEqualTo("Test Name Jr.");
- assertThat(match1.getExactMatchRange())
- .isEqualTo(new SearchResult.MatchRange(/*lower=*/ 0, /*upper=*/ 4));
- assertThat(match1.getExactMatch()).isEqualTo("Test");
- assertThat(match1.getSnippetRange())
- .isEqualTo(new SearchResult.MatchRange(/*lower=*/ 0, /*upper=*/ 9));
- assertThat(match1.getSnippet()).isEqualTo("Test Name");
-
- SearchResult.MatchInfo match2 = searchResultPage.getResults().get(0).getMatchInfos().get(1);
- assertThat(match2.getPropertyPath()).isEqualTo("sender.email[1]");
- assertThat(match2.getFullText()).isEqualTo("TestNameJr2@gmail.com");
- assertThat(match2.getExactMatchRange())
- .isEqualTo(new SearchResult.MatchRange(/*lower=*/ 0, /*upper=*/ 21));
- assertThat(match2.getExactMatch()).isEqualTo("TestNameJr2@gmail.com");
- assertThat(match2.getSnippetRange())
- .isEqualTo(new SearchResult.MatchRange(/*lower=*/ 0, /*upper=*/ 21));
- assertThat(match2.getSnippet()).isEqualTo("TestNameJr2@gmail.com");
- }
-}
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/stats/AppSearchStatsTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/stats/AppSearchStatsTest.java
deleted file mode 100644
index 81aab416a9f9..000000000000
--- a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/stats/AppSearchStatsTest.java
+++ /dev/null
@@ -1,393 +0,0 @@
-/*
- * Copyright 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.appsearch.external.localstorage.stats;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.app.appsearch.AppSearchResult;
-
-import org.junit.Test;
-
-public class AppSearchStatsTest {
- static final String TEST_PACKAGE_NAME = "com.google.test";
- static final String TEST_DATA_BASE = "testDataBase";
- static final int TEST_STATUS_CODE = AppSearchResult.RESULT_INTERNAL_ERROR;
- static final int TEST_TOTAL_LATENCY_MILLIS = 20;
-
- @Test
- public void testAppSearchStats_CallStats() {
- final int estimatedBinderLatencyMillis = 1;
- final int numOperationsSucceeded = 2;
- final int numOperationsFailed = 3;
- final @CallStats.CallType int callType = CallStats.CALL_TYPE_PUT_DOCUMENTS;
-
- final CallStats cStats =
- new CallStats.Builder()
- .setPackageName(TEST_PACKAGE_NAME)
- .setDatabase(TEST_DATA_BASE)
- .setStatusCode(TEST_STATUS_CODE)
- .setTotalLatencyMillis(TEST_TOTAL_LATENCY_MILLIS)
- .setCallType(callType)
- .setEstimatedBinderLatencyMillis(estimatedBinderLatencyMillis)
- .setNumOperationsSucceeded(numOperationsSucceeded)
- .setNumOperationsFailed(numOperationsFailed)
- .build();
-
- assertThat(cStats.getPackageName()).isEqualTo(TEST_PACKAGE_NAME);
- assertThat(cStats.getDatabase()).isEqualTo(TEST_DATA_BASE);
- assertThat(cStats.getStatusCode()).isEqualTo(TEST_STATUS_CODE);
- assertThat(cStats.getTotalLatencyMillis()).isEqualTo(TEST_TOTAL_LATENCY_MILLIS);
- assertThat(cStats.getEstimatedBinderLatencyMillis())
- .isEqualTo(estimatedBinderLatencyMillis);
- assertThat(cStats.getCallType()).isEqualTo(callType);
- assertThat(cStats.getNumOperationsSucceeded()).isEqualTo(numOperationsSucceeded);
- assertThat(cStats.getNumOperationsFailed()).isEqualTo(numOperationsFailed);
- }
-
- @Test
- public void testAppSearchCallStats_nullValues() {
- final @CallStats.CallType int callType = CallStats.CALL_TYPE_PUT_DOCUMENTS;
-
- final CallStats.Builder cStatsBuilder = new CallStats.Builder().setCallType(callType);
-
- final CallStats cStats = cStatsBuilder.build();
-
- assertThat(cStats.getPackageName()).isNull();
- assertThat(cStats.getDatabase()).isNull();
- assertThat(cStats.getCallType()).isEqualTo(callType);
- }
-
- @Test
- public void testAppSearchStats_PutDocumentStats() {
- final int generateDocumentProtoLatencyMillis = 1;
- final int rewriteDocumentTypesLatencyMillis = 2;
- final int nativeLatencyMillis = 3;
- final int nativeDocumentStoreLatencyMillis = 4;
- final int nativeIndexLatencyMillis = 5;
- final int nativeIndexMergeLatencyMillis = 6;
- final int nativeDocumentSize = 7;
- final int nativeNumTokensIndexed = 8;
- final boolean nativeExceededMaxNumTokens = true;
- final PutDocumentStats.Builder pStatsBuilder =
- new PutDocumentStats.Builder(TEST_PACKAGE_NAME, TEST_DATA_BASE)
- .setStatusCode(TEST_STATUS_CODE)
- .setTotalLatencyMillis(TEST_TOTAL_LATENCY_MILLIS)
- .setGenerateDocumentProtoLatencyMillis(generateDocumentProtoLatencyMillis)
- .setRewriteDocumentTypesLatencyMillis(rewriteDocumentTypesLatencyMillis)
- .setNativeLatencyMillis(nativeLatencyMillis)
- .setNativeDocumentStoreLatencyMillis(nativeDocumentStoreLatencyMillis)
- .setNativeIndexLatencyMillis(nativeIndexLatencyMillis)
- .setNativeIndexMergeLatencyMillis(nativeIndexMergeLatencyMillis)
- .setNativeDocumentSizeBytes(nativeDocumentSize)
- .setNativeNumTokensIndexed(nativeNumTokensIndexed)
- .setNativeExceededMaxNumTokens(nativeExceededMaxNumTokens);
-
- final PutDocumentStats pStats = pStatsBuilder.build();
-
- assertThat(pStats.getPackageName()).isEqualTo(TEST_PACKAGE_NAME);
- assertThat(pStats.getDatabase()).isEqualTo(TEST_DATA_BASE);
- assertThat(pStats.getStatusCode()).isEqualTo(TEST_STATUS_CODE);
- assertThat(pStats.getTotalLatencyMillis()).isEqualTo(TEST_TOTAL_LATENCY_MILLIS);
- assertThat(pStats.getGenerateDocumentProtoLatencyMillis())
- .isEqualTo(generateDocumentProtoLatencyMillis);
- assertThat(pStats.getRewriteDocumentTypesLatencyMillis())
- .isEqualTo(rewriteDocumentTypesLatencyMillis);
- assertThat(pStats.getNativeLatencyMillis()).isEqualTo(nativeLatencyMillis);
- assertThat(pStats.getNativeDocumentStoreLatencyMillis())
- .isEqualTo(nativeDocumentStoreLatencyMillis);
- assertThat(pStats.getNativeIndexLatencyMillis()).isEqualTo(nativeIndexLatencyMillis);
- assertThat(pStats.getNativeIndexMergeLatencyMillis())
- .isEqualTo(nativeIndexMergeLatencyMillis);
- assertThat(pStats.getNativeDocumentSizeBytes()).isEqualTo(nativeDocumentSize);
- assertThat(pStats.getNativeNumTokensIndexed()).isEqualTo(nativeNumTokensIndexed);
- assertThat(pStats.getNativeExceededMaxNumTokens()).isEqualTo(nativeExceededMaxNumTokens);
- }
-
- @Test
- public void testAppSearchStats_InitializeStats() {
- int prepareSchemaAndNamespacesLatencyMillis = 1;
- int prepareVisibilityFileLatencyMillis = 2;
- int nativeLatencyMillis = 3;
- int nativeDocumentStoreRecoveryCause = 4;
- int nativeIndexRestorationCause = 5;
- int nativeSchemaStoreRecoveryCause = 6;
- int nativeDocumentStoreRecoveryLatencyMillis = 7;
- int nativeIndexRestorationLatencyMillis = 8;
- int nativeSchemaStoreRecoveryLatencyMillis = 9;
- int nativeDocumentStoreDataStatus = 10;
- int nativeNumDocuments = 11;
- int nativeNumSchemaTypes = 12;
-
- final InitializeStats.Builder iStatsBuilder =
- new InitializeStats.Builder()
- .setStatusCode(TEST_STATUS_CODE)
- .setTotalLatencyMillis(TEST_TOTAL_LATENCY_MILLIS)
- .setHasDeSync(/* hasDeSyncs= */ true)
- .setPrepareSchemaAndNamespacesLatencyMillis(
- prepareSchemaAndNamespacesLatencyMillis)
- .setPrepareVisibilityStoreLatencyMillis(prepareVisibilityFileLatencyMillis)
- .setNativeLatencyMillis(nativeLatencyMillis)
- .setDocumentStoreRecoveryCause(nativeDocumentStoreRecoveryCause)
- .setIndexRestorationCause(nativeIndexRestorationCause)
- .setSchemaStoreRecoveryCause(nativeSchemaStoreRecoveryCause)
- .setDocumentStoreRecoveryLatencyMillis(
- nativeDocumentStoreRecoveryLatencyMillis)
- .setIndexRestorationLatencyMillis(nativeIndexRestorationLatencyMillis)
- .setSchemaStoreRecoveryLatencyMillis(nativeSchemaStoreRecoveryLatencyMillis)
- .setDocumentStoreDataStatus(nativeDocumentStoreDataStatus)
- .setDocumentCount(nativeNumDocuments)
- .setSchemaTypeCount(nativeNumSchemaTypes)
- .setHasReset(true)
- .setResetStatusCode(AppSearchResult.RESULT_INVALID_SCHEMA);
- final InitializeStats iStats = iStatsBuilder.build();
-
- assertThat(iStats.getStatusCode()).isEqualTo(TEST_STATUS_CODE);
- assertThat(iStats.getTotalLatencyMillis()).isEqualTo(TEST_TOTAL_LATENCY_MILLIS);
- assertThat(iStats.hasDeSync()).isTrue();
- assertThat(iStats.getPrepareSchemaAndNamespacesLatencyMillis())
- .isEqualTo(prepareSchemaAndNamespacesLatencyMillis);
- assertThat(iStats.getPrepareVisibilityStoreLatencyMillis())
- .isEqualTo(prepareVisibilityFileLatencyMillis);
- assertThat(iStats.getNativeLatencyMillis()).isEqualTo(nativeLatencyMillis);
- assertThat(iStats.getDocumentStoreRecoveryCause())
- .isEqualTo(nativeDocumentStoreRecoveryCause);
- assertThat(iStats.getIndexRestorationCause()).isEqualTo(nativeIndexRestorationCause);
- assertThat(iStats.getSchemaStoreRecoveryCause()).isEqualTo(nativeSchemaStoreRecoveryCause);
- assertThat(iStats.getDocumentStoreRecoveryLatencyMillis())
- .isEqualTo(nativeDocumentStoreRecoveryLatencyMillis);
- assertThat(iStats.getIndexRestorationLatencyMillis())
- .isEqualTo(nativeIndexRestorationLatencyMillis);
- assertThat(iStats.getSchemaStoreRecoveryLatencyMillis())
- .isEqualTo(nativeSchemaStoreRecoveryLatencyMillis);
- assertThat(iStats.getDocumentStoreDataStatus()).isEqualTo(nativeDocumentStoreDataStatus);
- assertThat(iStats.getDocumentCount()).isEqualTo(nativeNumDocuments);
- assertThat(iStats.getSchemaTypeCount()).isEqualTo(nativeNumSchemaTypes);
- assertThat(iStats.hasReset()).isTrue();
- assertThat(iStats.getResetStatusCode()).isEqualTo(AppSearchResult.RESULT_INVALID_SCHEMA);
- }
-
- @Test
- public void testAppSearchStats_SearchStats() {
- int rewriteSearchSpecLatencyMillis = 1;
- int rewriteSearchResultLatencyMillis = 2;
- int visibilityScope = SearchStats.VISIBILITY_SCOPE_LOCAL;
- int nativeLatencyMillis = 4;
- int nativeNumTerms = 5;
- int nativeQueryLength = 6;
- int nativeNumNamespacesFiltered = 7;
- int nativeNumSchemaTypesFiltered = 8;
- int nativeRequestedPageSize = 9;
- int nativeNumResultsReturnedCurrentPage = 10;
- boolean nativeIsFirstPage = true;
- int nativeParseQueryLatencyMillis = 11;
- int nativeRankingStrategy = 12;
- int nativeNumDocumentsScored = 13;
- int nativeScoringLatencyMillis = 14;
- int nativeRankingLatencyMillis = 15;
- int nativeNumResultsSnippeted = 16;
- int nativeDocumentRetrievingLatencyMillis = 17;
- final SearchStats.Builder sStatsBuilder =
- new SearchStats.Builder(visibilityScope, TEST_PACKAGE_NAME)
- .setDatabase(TEST_DATA_BASE)
- .setStatusCode(TEST_STATUS_CODE)
- .setTotalLatencyMillis(TEST_TOTAL_LATENCY_MILLIS)
- .setRewriteSearchSpecLatencyMillis(rewriteSearchSpecLatencyMillis)
- .setRewriteSearchResultLatencyMillis(rewriteSearchResultLatencyMillis)
- .setNativeLatencyMillis(nativeLatencyMillis)
- .setTermCount(nativeNumTerms)
- .setQueryLength(nativeQueryLength)
- .setFilteredNamespaceCount(nativeNumNamespacesFiltered)
- .setFilteredSchemaTypeCount(nativeNumSchemaTypesFiltered)
- .setRequestedPageSize(nativeRequestedPageSize)
- .setCurrentPageReturnedResultCount(nativeNumResultsReturnedCurrentPage)
- .setIsFirstPage(nativeIsFirstPage)
- .setParseQueryLatencyMillis(nativeParseQueryLatencyMillis)
- .setRankingStrategy(nativeRankingStrategy)
- .setScoredDocumentCount(nativeNumDocumentsScored)
- .setScoringLatencyMillis(nativeScoringLatencyMillis)
- .setRankingLatencyMillis(nativeRankingLatencyMillis)
- .setResultWithSnippetsCount(nativeNumResultsSnippeted)
- .setDocumentRetrievingLatencyMillis(nativeDocumentRetrievingLatencyMillis);
- final SearchStats sStats = sStatsBuilder.build();
-
- assertThat(sStats.getPackageName()).isEqualTo(TEST_PACKAGE_NAME);
- assertThat(sStats.getDatabase()).isEqualTo(TEST_DATA_BASE);
- assertThat(sStats.getStatusCode()).isEqualTo(TEST_STATUS_CODE);
- assertThat(sStats.getTotalLatencyMillis()).isEqualTo(TEST_TOTAL_LATENCY_MILLIS);
- assertThat(sStats.getRewriteSearchSpecLatencyMillis())
- .isEqualTo(rewriteSearchSpecLatencyMillis);
- assertThat(sStats.getRewriteSearchResultLatencyMillis())
- .isEqualTo(rewriteSearchResultLatencyMillis);
- assertThat(sStats.getVisibilityScope()).isEqualTo(visibilityScope);
- assertThat(sStats.getNativeLatencyMillis()).isEqualTo(nativeLatencyMillis);
- assertThat(sStats.getTermCount()).isEqualTo(nativeNumTerms);
- assertThat(sStats.getQueryLength()).isEqualTo(nativeQueryLength);
- assertThat(sStats.getFilteredNamespaceCount()).isEqualTo(nativeNumNamespacesFiltered);
- assertThat(sStats.getFilteredSchemaTypeCount()).isEqualTo(nativeNumSchemaTypesFiltered);
- assertThat(sStats.getRequestedPageSize()).isEqualTo(nativeRequestedPageSize);
- assertThat(sStats.getCurrentPageReturnedResultCount())
- .isEqualTo(nativeNumResultsReturnedCurrentPage);
- assertThat(sStats.isFirstPage()).isTrue();
- assertThat(sStats.getParseQueryLatencyMillis()).isEqualTo(nativeParseQueryLatencyMillis);
- assertThat(sStats.getRankingStrategy()).isEqualTo(nativeRankingStrategy);
- assertThat(sStats.getScoredDocumentCount()).isEqualTo(nativeNumDocumentsScored);
- assertThat(sStats.getScoringLatencyMillis()).isEqualTo(nativeScoringLatencyMillis);
- assertThat(sStats.getRankingLatencyMillis()).isEqualTo(nativeRankingLatencyMillis);
- assertThat(sStats.getResultWithSnippetsCount()).isEqualTo(nativeNumResultsSnippeted);
- assertThat(sStats.getDocumentRetrievingLatencyMillis())
- .isEqualTo(nativeDocumentRetrievingLatencyMillis);
- }
-
- @Test
- public void testAppSearchStats_SetSchemaStats() {
- SchemaMigrationStats schemaMigrationStats =
- new SchemaMigrationStats.Builder()
- .setGetSchemaLatencyMillis(1)
- .setQueryAndTransformLatencyMillis(2)
- .setFirstSetSchemaLatencyMillis(3)
- .setSecondSetSchemaLatencyMillis(4)
- .setSaveDocumentLatencyMillis(5)
- .setMigratedDocumentCount(6)
- .setSavedDocumentCount(7)
- .build();
- int newTypeCount = 1;
- int compatibleTypeChangeCount = 2;
- int indexIncompatibleTypeChangeCount = 3;
- int backwardsIncompatibleTypeChangeCount = 4;
- SetSchemaStats sStats =
- new SetSchemaStats.Builder(TEST_PACKAGE_NAME, TEST_DATA_BASE)
- .setStatusCode(TEST_STATUS_CODE)
- .setSchemaMigrationStats(schemaMigrationStats)
- .setTotalLatencyMillis(TEST_TOTAL_LATENCY_MILLIS)
- .setNewTypeCount(newTypeCount)
- .setCompatibleTypeChangeCount(compatibleTypeChangeCount)
- .setIndexIncompatibleTypeChangeCount(indexIncompatibleTypeChangeCount)
- .setBackwardsIncompatibleTypeChangeCount(
- backwardsIncompatibleTypeChangeCount)
- .build();
-
- assertThat(sStats.getPackageName()).isEqualTo(TEST_PACKAGE_NAME);
- assertThat(sStats.getDatabase()).isEqualTo(TEST_DATA_BASE);
- assertThat(sStats.getStatusCode()).isEqualTo(TEST_STATUS_CODE);
- assertThat(sStats.getSchemaMigrationStats()).isEqualTo(schemaMigrationStats);
- assertThat(sStats.getTotalLatencyMillis()).isEqualTo(TEST_TOTAL_LATENCY_MILLIS);
- assertThat(sStats.getNewTypeCount()).isEqualTo(newTypeCount);
- assertThat(sStats.getCompatibleTypeChangeCount()).isEqualTo(compatibleTypeChangeCount);
- assertThat(sStats.getIndexIncompatibleTypeChangeCount())
- .isEqualTo(indexIncompatibleTypeChangeCount);
- assertThat(sStats.getBackwardsIncompatibleTypeChangeCount())
- .isEqualTo(backwardsIncompatibleTypeChangeCount);
- }
-
- @Test
- public void testAppSearchStats_SchemaMigrationStats() {
- int getSchemaLatency = 1;
- int queryAndTransformLatency = 2;
- int firstSetSchemaLatency = 3;
- int secondSetSchemaLatency = 4;
- int saveDocumentLatency = 5;
- int migratedDocumentCount = 6;
- int savedDocumentCount = 7;
- SchemaMigrationStats sStats =
- new SchemaMigrationStats.Builder()
- .setGetSchemaLatencyMillis(getSchemaLatency)
- .setQueryAndTransformLatencyMillis(queryAndTransformLatency)
- .setFirstSetSchemaLatencyMillis(firstSetSchemaLatency)
- .setSecondSetSchemaLatencyMillis(secondSetSchemaLatency)
- .setSaveDocumentLatencyMillis(saveDocumentLatency)
- .setMigratedDocumentCount(migratedDocumentCount)
- .setSavedDocumentCount(savedDocumentCount)
- .build();
-
- assertThat(sStats.getGetSchemaLatencyMillis()).isEqualTo(getSchemaLatency);
- assertThat(sStats.getQueryAndTransformLatencyMillis()).isEqualTo(queryAndTransformLatency);
- assertThat(sStats.getFirstSetSchemaLatencyMillis()).isEqualTo(firstSetSchemaLatency);
- assertThat(sStats.getSecondSetSchemaLatencyMillis()).isEqualTo(secondSetSchemaLatency);
- assertThat(sStats.getSaveDocumentLatencyMillis()).isEqualTo(saveDocumentLatency);
- assertThat(sStats.getMigratedDocumentCount()).isEqualTo(migratedDocumentCount);
- assertThat(sStats.getSavedDocumentCount()).isEqualTo(savedDocumentCount);
- }
-
- @Test
- public void testAppSearchStats_RemoveStats() {
- int nativeLatencyMillis = 1;
- @RemoveStats.DeleteType int deleteType = 2;
- int documentDeletedCount = 3;
-
- final RemoveStats rStats =
- new RemoveStats.Builder(TEST_PACKAGE_NAME, TEST_DATA_BASE)
- .setStatusCode(TEST_STATUS_CODE)
- .setTotalLatencyMillis(TEST_TOTAL_LATENCY_MILLIS)
- .setNativeLatencyMillis(nativeLatencyMillis)
- .setDeleteType(deleteType)
- .setDeletedDocumentCount(documentDeletedCount)
- .build();
-
- assertThat(rStats.getPackageName()).isEqualTo(TEST_PACKAGE_NAME);
- assertThat(rStats.getDatabase()).isEqualTo(TEST_DATA_BASE);
- assertThat(rStats.getStatusCode()).isEqualTo(TEST_STATUS_CODE);
- assertThat(rStats.getTotalLatencyMillis()).isEqualTo(TEST_TOTAL_LATENCY_MILLIS);
- assertThat(rStats.getNativeLatencyMillis()).isEqualTo(nativeLatencyMillis);
- assertThat(rStats.getDeleteType()).isEqualTo(deleteType);
- assertThat(rStats.getDeletedDocumentCount()).isEqualTo(documentDeletedCount);
- }
-
- @Test
- public void testAppSearchStats_OptimizeStats() {
- int nativeLatencyMillis = 1;
- int nativeDocumentStoreOptimizeLatencyMillis = 2;
- int nativeIndexRestorationLatencyMillis = 3;
- int nativeNumOriginalDocuments = 4;
- int nativeNumDeletedDocuments = 5;
- int nativeNumExpiredDocuments = 6;
- long nativeStorageSizeBeforeBytes = Integer.MAX_VALUE + 1;
- long nativeStorageSizeAfterBytes = Integer.MAX_VALUE + 2;
- long nativeTimeSinceLastOptimizeMillis = Integer.MAX_VALUE + 3;
-
- final OptimizeStats oStats =
- new OptimizeStats.Builder()
- .setStatusCode(TEST_STATUS_CODE)
- .setTotalLatencyMillis(TEST_TOTAL_LATENCY_MILLIS)
- .setNativeLatencyMillis(nativeLatencyMillis)
- .setDocumentStoreOptimizeLatencyMillis(
- nativeDocumentStoreOptimizeLatencyMillis)
- .setIndexRestorationLatencyMillis(nativeIndexRestorationLatencyMillis)
- .setOriginalDocumentCount(nativeNumOriginalDocuments)
- .setDeletedDocumentCount(nativeNumDeletedDocuments)
- .setExpiredDocumentCount(nativeNumExpiredDocuments)
- .setStorageSizeBeforeBytes(nativeStorageSizeBeforeBytes)
- .setStorageSizeAfterBytes(nativeStorageSizeAfterBytes)
- .setTimeSinceLastOptimizeMillis(nativeTimeSinceLastOptimizeMillis)
- .build();
-
- assertThat(oStats.getStatusCode()).isEqualTo(TEST_STATUS_CODE);
- assertThat(oStats.getTotalLatencyMillis()).isEqualTo(TEST_TOTAL_LATENCY_MILLIS);
- assertThat(oStats.getNativeLatencyMillis()).isEqualTo(nativeLatencyMillis);
- assertThat(oStats.getNativeLatencyMillis()).isEqualTo(nativeLatencyMillis);
- assertThat(oStats.getDocumentStoreOptimizeLatencyMillis())
- .isEqualTo(nativeDocumentStoreOptimizeLatencyMillis);
- assertThat(oStats.getIndexRestorationLatencyMillis())
- .isEqualTo(nativeIndexRestorationLatencyMillis);
- assertThat(oStats.getOriginalDocumentCount()).isEqualTo(nativeNumOriginalDocuments);
- assertThat(oStats.getDeletedDocumentCount()).isEqualTo(nativeNumDeletedDocuments);
- assertThat(oStats.getExpiredDocumentCount()).isEqualTo(nativeNumExpiredDocuments);
- assertThat(oStats.getStorageSizeBeforeBytes()).isEqualTo(nativeStorageSizeBeforeBytes);
- assertThat(oStats.getStorageSizeAfterBytes()).isEqualTo(nativeStorageSizeAfterBytes);
- assertThat(oStats.getTimeSinceLastOptimizeMillis())
- .isEqualTo(nativeTimeSinceLastOptimizeMillis);
- }
-}
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/stats/PlatformLoggerTest.java b/services/tests/servicestests/src/com/android/server/appsearch/stats/PlatformLoggerTest.java
deleted file mode 100644
index ec96d6a71be4..000000000000
--- a/services/tests/servicestests/src/com/android/server/appsearch/stats/PlatformLoggerTest.java
+++ /dev/null
@@ -1,199 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.appsearch.stats;
-
-import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.anyInt;
-import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.annotation.NonNull;
-import android.content.Context;
-import android.content.ContextWrapper;
-import android.content.pm.PackageManager;
-import android.os.UserHandle;
-import android.util.ArrayMap;
-
-import androidx.test.core.app.ApplicationProvider;
-
-import com.android.server.appsearch.AppSearchConfig;
-import com.android.server.appsearch.external.localstorage.stats.CallStats;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.Mockito;
-
-import java.io.UnsupportedEncodingException;
-import java.math.BigInteger;
-import java.nio.charset.StandardCharsets;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.util.Map;
-
-/**
- * Tests covering the functionalities in {@link PlatformLogger} NOT requiring overriding any flags
- * in {@link android.provider.DeviceConfig}.
- *
- * <p>To add tests rely on overriding the flags, please add them in the
- * tests for {@link PlatformLogger} in mockingservicestests.
- */
-public class PlatformLoggerTest {
- private final Map<UserHandle, PackageManager> mMockPackageManagers = new ArrayMap<>();
- private Context mContext;
-
- @Before
- public void setUp() throws Exception {
- Context context = ApplicationProvider.getApplicationContext();
- mContext = new ContextWrapper(context) {
- @Override
- public PackageManager getPackageManager() {
- return getMockPackageManager(mContext.getUser());
- }
- };
- }
-
- @Test
- public void testCalculateHashCode_MD5_int32_shortString()
- throws NoSuchAlgorithmException, UnsupportedEncodingException {
- final String str1 = "d1";
- final String str2 = "d2";
-
- int hashCodeForStr1 = PlatformLogger.calculateHashCodeMd5(str1);
-
- // hashing should be stable
- assertThat(hashCodeForStr1).isEqualTo(
- PlatformLogger.calculateHashCodeMd5(str1));
- assertThat(hashCodeForStr1).isNotEqualTo(
- PlatformLogger.calculateHashCodeMd5(str2));
- }
-
- @Test
- public void testGetCalculateCode_MD5_int32_mediumString()
- throws NoSuchAlgorithmException, UnsupportedEncodingException {
- final String str1 = "Siblings";
- final String str2 = "Teheran";
-
- int hashCodeForStr1 = PlatformLogger.calculateHashCodeMd5(str1);
-
- // hashing should be stable
- assertThat(hashCodeForStr1).isEqualTo(
- PlatformLogger.calculateHashCodeMd5(str1));
- assertThat(hashCodeForStr1).isNotEqualTo(
- PlatformLogger.calculateHashCodeMd5(str2));
- }
-
- @Test
- public void testCalculateHashCode_MD5_int32_longString() throws NoSuchAlgorithmException,
- UnsupportedEncodingException {
- final String str1 = "abcdefghijkl-mnopqrstuvwxyz";
- final String str2 = "abcdefghijkl-mnopqrstuvwxy123";
-
- int hashCodeForStr1 = PlatformLogger.calculateHashCodeMd5(str1);
-
- // hashing should be stable
- assertThat(hashCodeForStr1).isEqualTo(
- PlatformLogger.calculateHashCodeMd5(str1));
- assertThat(hashCodeForStr1).isNotEqualTo(
- PlatformLogger.calculateHashCodeMd5(str2));
- }
-
- @Test
- public void testCalculateHashCode_MD5_int32_sameAsBigInteger_intValue() throws
- NoSuchAlgorithmException, UnsupportedEncodingException {
- final String emptyStr = "";
- final String shortStr = "a";
- final String mediumStr = "Teheran";
- final String longStr = "abcd-efgh-ijkl-mnop-qrst-uvwx-yz";
-
- int emptyHashCode = PlatformLogger.calculateHashCodeMd5(emptyStr);
- int shortHashCode = PlatformLogger.calculateHashCodeMd5(shortStr);
- int mediumHashCode = PlatformLogger.calculateHashCodeMd5(mediumStr);
- int longHashCode = PlatformLogger.calculateHashCodeMd5(longStr);
-
- assertThat(emptyHashCode).isEqualTo(calculateHashCodeMd5withBigInteger(emptyStr));
- assertThat(shortHashCode).isEqualTo(calculateHashCodeMd5withBigInteger(shortStr));
- assertThat(mediumHashCode).isEqualTo(calculateHashCodeMd5withBigInteger(mediumStr));
- assertThat(longHashCode).isEqualTo(calculateHashCodeMd5withBigInteger(longStr));
- }
-
- @Test
- public void testCalculateHashCode_MD5_strIsNull() throws
- NoSuchAlgorithmException, UnsupportedEncodingException {
- assertThat(PlatformLogger.calculateHashCodeMd5(/*str=*/ null)).isEqualTo(-1);
- }
-
- /** Makes sure the caching works while getting the UID for calling package. */
- @Test
- public void testGetPackageUidAsUser() throws Exception {
- final String testPackageName = "packageName";
- final int testUid = 1234;
- PlatformLogger logger = new PlatformLogger(
- mContext,
- AppSearchConfig.create(DIRECT_EXECUTOR));
- PackageManager mockPackageManager = getMockPackageManager(mContext.getUser());
- when(mockPackageManager.getPackageUid(testPackageName, /*flags=*/0)).thenReturn(testUid);
-
- // First time, no cache
- PlatformLogger.ExtraStats extraStats = logger.createExtraStatsLocked(testPackageName,
- CallStats.CALL_TYPE_PUT_DOCUMENT);
- verify(mockPackageManager, times(1))
- .getPackageUid(eq(testPackageName), /*flags=*/ anyInt());
- assertThat(extraStats.mPackageUid).isEqualTo(testUid);
-
- // Second time, we have cache
- extraStats = logger.createExtraStatsLocked(testPackageName,
- CallStats.CALL_TYPE_PUT_DOCUMENT);
-
- // Count is still one since we will use the cache
- verify(mockPackageManager, times(1))
- .getPackageUid(eq(testPackageName), /*flags=*/ anyInt());
- assertThat(extraStats.mPackageUid).isEqualTo(testUid);
-
- // Remove the cache and try again
- assertThat(logger.removeCachedUidForPackage(testPackageName)).isEqualTo(testUid);
- extraStats = logger.createExtraStatsLocked(testPackageName,
- CallStats.CALL_TYPE_PUT_DOCUMENT);
-
- // count increased by 1 since cache is cleared
- verify(mockPackageManager, times(2))
- .getPackageUid(eq(testPackageName), /*flags=*/ anyInt());
- assertThat(extraStats.mPackageUid).isEqualTo(testUid);
- }
-
- private static int calculateHashCodeMd5withBigInteger(@NonNull String str)
- throws NoSuchAlgorithmException {
- MessageDigest md = MessageDigest.getInstance("MD5");
- md.update(str.getBytes(StandardCharsets.UTF_8));
- byte[] digest = md.digest();
- return new BigInteger(digest).intValue();
- }
-
- @NonNull
- private PackageManager getMockPackageManager(@NonNull UserHandle user) {
- PackageManager pm = mMockPackageManagers.get(user);
- if (pm == null) {
- pm = Mockito.mock(PackageManager.class);
- mMockPackageManagers.put(user, pm);
- }
- return pm;
- }
-}
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/visibilitystore/VisibilityStoreImplTest.java b/services/tests/servicestests/src/com/android/server/appsearch/visibilitystore/VisibilityStoreImplTest.java
deleted file mode 100644
index 374642b676d2..000000000000
--- a/services/tests/servicestests/src/com/android/server/appsearch/visibilitystore/VisibilityStoreImplTest.java
+++ /dev/null
@@ -1,469 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.appsearch.visibilitystore;
-
-import static android.Manifest.permission.READ_GLOBAL_APP_SEARCH_DATA;
-import static android.content.pm.PackageManager.PERMISSION_DENIED;
-import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.when;
-
-import android.annotation.NonNull;
-import android.app.appsearch.PackageIdentifier;
-import android.content.Context;
-import android.content.ContextWrapper;
-import android.content.pm.PackageManager;
-import android.os.UserHandle;
-import android.util.ArrayMap;
-
-import androidx.test.core.app.ApplicationProvider;
-
-import com.android.server.appsearch.external.localstorage.AppSearchImpl;
-import com.android.server.appsearch.external.localstorage.OptimizeStrategy;
-import com.android.server.appsearch.external.localstorage.UnlimitedLimitConfig;
-import com.android.server.appsearch.external.localstorage.util.PrefixUtil;
-import com.android.server.appsearch.external.localstorage.visibilitystore.VisibilityStore;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TemporaryFolder;
-import org.mockito.Mockito;
-
-import java.util.Collections;
-import java.util.Map;
-
-public class VisibilityStoreImplTest {
- /**
- * Always trigger optimize in this class. OptimizeStrategy will be tested in its own test class.
- */
- private static final OptimizeStrategy ALWAYS_OPTIMIZE = optimizeInfo -> true;
-
- @Rule public TemporaryFolder mTemporaryFolder = new TemporaryFolder();
- private final Map<UserHandle, PackageManager> mMockPackageManagers = new ArrayMap<>();
- private Context mContext;
- private VisibilityStoreImpl mVisibilityStore;
- private int mUid;
-
- @Before
- public void setUp() throws Exception {
- Context context = ApplicationProvider.getApplicationContext();
- mContext = new ContextWrapper(context) {
- @Override
- public Context createContextAsUser(UserHandle user, int flags) {
- return new ContextWrapper(super.createContextAsUser(user, flags)) {
- @Override
- public PackageManager getPackageManager() {
- return getMockPackageManager(user);
- }
- };
- }
-
- @Override
- public PackageManager getPackageManager() {
- return createContextAsUser(getUser(), /*flags=*/ 0).getPackageManager();
- }
- };
-
- // Give ourselves global query permissions
- AppSearchImpl appSearchImpl = AppSearchImpl.create(
- mTemporaryFolder.newFolder(),
- new UnlimitedLimitConfig(),
- /*initStatsBuilder=*/ null,
- ALWAYS_OPTIMIZE);
- mVisibilityStore = VisibilityStoreImpl.create(appSearchImpl, mContext);
- mUid = mContext.getPackageManager().getPackageUid(mContext.getPackageName(), /*flags=*/ 0);
- }
-
- /**
- * Make sure that we don't conflict with any special characters that AppSearchImpl has reserved.
- */
- @Test
- public void testValidPackageName() {
- assertThat(VisibilityStore.PACKAGE_NAME)
- .doesNotContain(String.valueOf(PrefixUtil.PACKAGE_DELIMITER));
- assertThat(VisibilityStore.PACKAGE_NAME)
- .doesNotContain(String.valueOf(PrefixUtil.DATABASE_DELIMITER));
- }
-
- /**
- * Make sure that we don't conflict with any special characters that AppSearchImpl has reserved.
- */
- @Test
- public void testValidDatabaseName() {
- assertThat(VisibilityStore.DATABASE_NAME)
- .doesNotContain(String.valueOf(PrefixUtil.PACKAGE_DELIMITER));
- assertThat(VisibilityStore.DATABASE_NAME)
- .doesNotContain(String.valueOf(PrefixUtil.DATABASE_DELIMITER));
- }
-
- @Test
- public void testDoesCallerHaveSystemAccess() {
- PackageManager mockPackageManager = getMockPackageManager(mContext.getUser());
- when(mockPackageManager
- .checkPermission(READ_GLOBAL_APP_SEARCH_DATA, mContext.getPackageName()))
- .thenReturn(PERMISSION_GRANTED);
- assertThat(mVisibilityStore.doesCallerHaveSystemAccess(mContext.getPackageName())).isTrue();
-
- when(mockPackageManager
- .checkPermission(READ_GLOBAL_APP_SEARCH_DATA, mContext.getPackageName()))
- .thenReturn(PERMISSION_DENIED);
- assertThat(mVisibilityStore.doesCallerHaveSystemAccess(mContext.getPackageName()))
- .isFalse();
- }
-
- @Test
- public void testSetVisibility_displayedBySystem() throws Exception {
- // Make sure we have global query privileges
- PackageManager mockPackageManager = getMockPackageManager(mContext.getUser());
- when(mockPackageManager
- .checkPermission(READ_GLOBAL_APP_SEARCH_DATA, mContext.getPackageName()))
- .thenReturn(PERMISSION_GRANTED);
- assertThat(mVisibilityStore.doesCallerHaveSystemAccess(mContext.getPackageName())).isTrue();
-
- mVisibilityStore.setVisibility(
- "package",
- "database",
- /*schemasNotDisplayedBySystem=*/ ImmutableSet.of(
- "prefix/schema1", "prefix/schema2"),
- /*schemasVisibleToPackages=*/ Collections.emptyMap());
- assertThat(
- mVisibilityStore.isSchemaSearchableByCaller(
- "package",
- "database",
- "prefix/schema1",
- mUid,
- /*callerHasSystemAccess=*/ true))
- .isFalse();
- assertThat(
- mVisibilityStore.isSchemaSearchableByCaller(
- "package",
- "database",
- "prefix/schema2",
- mUid,
- /*callerHasSystemAccess=*/ true))
- .isFalse();
-
- // New .setVisibility() call completely overrides previous visibility settings.
- // So "schema2" isn't preserved.
- mVisibilityStore.setVisibility(
- "package",
- "database",
- /*schemasNotDisplayedBySystem=*/ ImmutableSet.of(
- "prefix/schema1", "prefix/schema3"),
- /*schemasVisibleToPackages=*/ Collections.emptyMap());
- assertThat(
- mVisibilityStore.isSchemaSearchableByCaller(
- "package",
- "database",
- "prefix/schema1",
- mUid,
- /*callerHasSystemAccess=*/ true))
- .isFalse();
- assertThat(
- mVisibilityStore.isSchemaSearchableByCaller(
- "package",
- "database",
- "prefix/schema2",
- mUid,
- /*callerHasSystemAccess=*/ true))
- .isTrue();
- assertThat(
- mVisibilityStore.isSchemaSearchableByCaller(
- "package",
- "database",
- "prefix/schema3",
- mUid,
- /*callerHasSystemAccess=*/ true))
- .isFalse();
-
- // Everything defaults to visible again.
- mVisibilityStore.setVisibility(
- "package",
- "database",
- /*schemasNotDisplayedBySystem=*/ Collections.emptySet(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap());
- assertThat(
- mVisibilityStore.isSchemaSearchableByCaller(
- "package",
- "database",
- "prefix/schema1",
- mUid,
- /*callerHasSystemAccess=*/ true))
- .isTrue();
- assertThat(
- mVisibilityStore.isSchemaSearchableByCaller(
- "package",
- "database",
- "prefix/schema2",
- mUid,
- /*callerHasSystemAccess=*/ true))
- .isTrue();
- assertThat(
- mVisibilityStore.isSchemaSearchableByCaller(
- "package",
- "database",
- "prefix/schema3",
- mUid,
- /*callerHasSystemAccess=*/ true))
- .isTrue();
- }
-
- @Test
- public void testSetVisibility_visibleToPackages() throws Exception {
- // Values for a "foo" client
- String packageNameFoo = "packageFoo";
- byte[] sha256CertFoo = new byte[] {10};
- int uidFoo = 1;
-
- // Values for a "bar" client
- String packageNameBar = "packageBar";
- byte[] sha256CertBar = new byte[] {100};
- int uidBar = 2;
-
- // Can't be the same value as uidFoo nor uidBar
- int uidNotFooOrBar = 3;
-
- // Make sure none of them have global query privileges
- PackageManager mockPackageManager = getMockPackageManager(mContext.getUser());
- when(mockPackageManager
- .checkPermission(READ_GLOBAL_APP_SEARCH_DATA, packageNameFoo))
- .thenReturn(PERMISSION_DENIED);
- when(mockPackageManager
- .checkPermission(READ_GLOBAL_APP_SEARCH_DATA, packageNameBar))
- .thenReturn(PERMISSION_DENIED);
-
- // By default, a schema isn't package accessible.
- assertThat(mVisibilityStore.isSchemaSearchableByCaller(
- "package",
- "database",
- "prefix/schemaFoo",
- uidFoo,
- /*callerHasSystemAccess=*/ false))
- .isFalse();
- assertThat(mVisibilityStore.isSchemaSearchableByCaller(
- "package",
- "database",
- "prefix/schemaBar",
- uidBar,
- /*callerHasSystemAccess=*/ false))
- .isFalse();
-
- // Grant package access
- mVisibilityStore.setVisibility(
- "package",
- "database",
- /*schemasNotDisplayedBySystem=*/ Collections.emptySet(),
- /*schemasVisibleToPackages=*/ ImmutableMap.of(
- "prefix/schemaFoo",
- ImmutableList.of(new PackageIdentifier(packageNameFoo, sha256CertFoo)),
- "prefix/schemaBar",
- ImmutableList.of(new PackageIdentifier(packageNameBar, sha256CertBar))));
-
- // Should fail if PackageManager doesn't see that it has the proper certificate
- when(mockPackageManager.getPackageUid(eq(packageNameFoo), /*flags=*/ anyInt()))
- .thenReturn(uidFoo);
- when(mockPackageManager.hasSigningCertificate(
- packageNameFoo, sha256CertFoo, PackageManager.CERT_INPUT_SHA256))
- .thenReturn(false);
- assertThat(mVisibilityStore.isSchemaSearchableByCaller(
- "package",
- "database",
- "prefix/schemaFoo",
- uidFoo,
- /*callerHasSystemAccess=*/ false))
- .isFalse();
-
- // Should fail if PackageManager doesn't think the package belongs to the uid
- when(mockPackageManager.getPackageUid(eq(packageNameFoo), /*flags=*/ anyInt()))
- .thenReturn(uidNotFooOrBar);
- when(mockPackageManager.hasSigningCertificate(
- packageNameFoo, sha256CertFoo, PackageManager.CERT_INPUT_SHA256))
- .thenReturn(true);
- assertThat(mVisibilityStore.isSchemaSearchableByCaller(
- "package",
- "database",
- "prefix/schemaFoo",
- uidFoo,
- /*callerHasSystemAccess=*/ false))
- .isFalse();
-
- // But if uid and certificate match, then we should have access
- when(mockPackageManager.getPackageUid(eq(packageNameFoo), /*flags=*/ anyInt()))
- .thenReturn(uidFoo);
- when(mockPackageManager.hasSigningCertificate(
- packageNameFoo, sha256CertFoo, PackageManager.CERT_INPUT_SHA256))
- .thenReturn(true);
- assertThat(mVisibilityStore.isSchemaSearchableByCaller(
- "package",
- "database",
- "prefix/schemaFoo",
- uidFoo,
- /*callerHasSystemAccess=*/ false))
- .isTrue();
-
- when(mockPackageManager.getPackageUid(eq(packageNameBar), /*flags=*/ anyInt()))
- .thenReturn(uidBar);
- when(mockPackageManager.hasSigningCertificate(
- packageNameBar, sha256CertBar, PackageManager.CERT_INPUT_SHA256))
- .thenReturn(true);
- assertThat(mVisibilityStore.isSchemaSearchableByCaller(
- "package",
- "database",
- "prefix/schemaBar",
- uidBar,
- /*callerHasSystemAccess=*/ false))
- .isTrue();
-
- // New .setVisibility() call completely overrides previous visibility settings. So
- // "schemaBar" settings aren't preserved.
- mVisibilityStore.setVisibility(
- "package",
- "database",
- /*schemasNotDisplayedBySystem=*/ Collections.emptySet(),
- /*schemasVisibleToPackages=*/ ImmutableMap.of(
- "prefix/schemaFoo",
- ImmutableList.of(new PackageIdentifier(packageNameFoo, sha256CertFoo))));
-
- when(mockPackageManager.getPackageUid(eq(packageNameFoo), /*flags=*/ anyInt()))
- .thenReturn(uidFoo);
- when(mockPackageManager.hasSigningCertificate(
- packageNameFoo, sha256CertFoo, PackageManager.CERT_INPUT_SHA256))
- .thenReturn(true);
- assertThat(mVisibilityStore.isSchemaSearchableByCaller(
- "package",
- "database",
- "prefix/schemaFoo",
- uidFoo,
- /*callerHasSystemAccess=*/ false))
- .isTrue();
-
- when(mockPackageManager.getPackageUid(eq(packageNameBar), /*flags=*/ anyInt()))
- .thenReturn(uidBar);
- when(mockPackageManager.hasSigningCertificate(
- packageNameBar, sha256CertBar, PackageManager.CERT_INPUT_SHA256))
- .thenReturn(true);
- assertThat(mVisibilityStore.isSchemaSearchableByCaller(
- "package",
- "database",
- "prefix/schemaBar",
- uidBar,
- /*callerHasSystemAccess=*/ false))
- .isFalse();
- }
-
- @Test
- public void testIsSchemaSearchableByCaller_packageAccessibilityHandlesNameNotFoundException()
- throws Exception {
- // Values for a "foo" client
- String packageNameFoo = "packageFoo";
- byte[] sha256CertFoo = new byte[] {10};
- int uidFoo = 1;
-
- // Pretend we can't find the Foo package.
- PackageManager mockPackageManager = getMockPackageManager(mContext.getUser());
- when(mockPackageManager.getPackageUid(eq(packageNameFoo), /*flags=*/ anyInt()))
- .thenThrow(new PackageManager.NameNotFoundException());
-
- // Make sure "foo" doesn't have global query privileges
- when(mockPackageManager.checkPermission(READ_GLOBAL_APP_SEARCH_DATA, packageNameFoo))
- .thenReturn(PERMISSION_DENIED);
-
- // Grant package access
- mVisibilityStore.setVisibility(
- "package",
- "database",
- /*schemasNotDisplayedBySystem=*/ Collections.emptySet(),
- /*schemasVisibleToPackages=*/ ImmutableMap.of(
- "prefix/schemaFoo",
- ImmutableList.of(new PackageIdentifier(packageNameFoo, sha256CertFoo))));
-
- // If we can't verify the Foo package that has access, assume it doesn't have access.
- assertThat(mVisibilityStore.isSchemaSearchableByCaller(
- "package",
- "database",
- "prefix/schemaFoo",
- uidFoo,
- /*callerHasSystemAccess=*/ false))
- .isFalse();
- }
-
- @Test
- public void testEmptyPrefix() throws Exception {
- // Values for a "foo" client
- String packageNameFoo = "packageFoo";
- byte[] sha256CertFoo = new byte[] {10};
- int uidFoo = 1;
-
- // Set it up such that the test package has global query privileges, but "foo" doesn't.
- PackageManager mockPackageManager = getMockPackageManager(mContext.getUser());
- when(mockPackageManager.checkPermission(
- READ_GLOBAL_APP_SEARCH_DATA, mContext.getPackageName()))
- .thenReturn(PERMISSION_GRANTED);
- when(mockPackageManager.checkPermission(READ_GLOBAL_APP_SEARCH_DATA, packageNameFoo))
- .thenReturn(PERMISSION_DENIED);
-
- mVisibilityStore.setVisibility(
- /*packageName=*/ "",
- /*databaseName=*/ "",
- /*schemasNotDisplayedBySystem=*/ Collections.emptySet(),
- /*schemasVisibleToPackages=*/ ImmutableMap.of(
- "schema",
- ImmutableList.of(new PackageIdentifier(packageNameFoo, sha256CertFoo))));
-
- assertThat(
- mVisibilityStore.isSchemaSearchableByCaller(
- /*packageName=*/ "",
- /*databaseName=*/ "",
- "schema",
- mUid,
- /*callerHasSystemAccess=*/ true))
- .isTrue();
-
- when(mockPackageManager.getPackageUid(eq(packageNameFoo), /*flags=*/ anyInt()))
- .thenReturn(uidFoo);
- when(mockPackageManager.hasSigningCertificate(
- packageNameFoo, sha256CertFoo, PackageManager.CERT_INPUT_SHA256))
- .thenReturn(true);
- assertThat(
- mVisibilityStore.isSchemaSearchableByCaller(
- /*packageName=*/ "",
- /*databaseName=*/ "",
- "schema",
- uidFoo,
- /*callerHasSystemAccess=*/ false))
- .isTrue();
- }
-
- @NonNull
- private PackageManager getMockPackageManager(@NonNull UserHandle user) {
- PackageManager pm = mMockPackageManagers.get(user);
- if (pm == null) {
- pm = Mockito.mock(PackageManager.class);
- mMockPackageManagers.put(user, pm);
- }
- return pm;
- }
-}
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 5c53d43fa1df..bdea679a3311 100644
--- a/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java
+++ b/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java
@@ -26,11 +26,11 @@ import static org.mockito.Mockito.when;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
-import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.content.Intent;
import android.media.AudioManager;
import android.media.AudioSystem;
+import android.media.BluetoothProfileConnectionInfo;
import android.util.Log;
import androidx.test.InstrumentationRegistry;
@@ -98,16 +98,12 @@ public class AudioDeviceBrokerTest {
Log.i(TAG, "starting testPostA2dpDeviceConnectionChange");
Assert.assertNotNull("invalid null BT device", mFakeBtDevice);
- mAudioDeviceBroker.queueBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
- new AudioDeviceBroker.BtDeviceConnectionInfo(mFakeBtDevice,
- BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP, true, 1));
+ mAudioDeviceBroker.queueOnBluetoothActiveDeviceChanged(
+ new AudioDeviceBroker.BtDeviceChangedData(mFakeBtDevice, null,
+ BluetoothProfileConnectionInfo.createA2dpInfo(true, 1), "testSource"));
Thread.sleep(2 * MAX_MESSAGE_HANDLING_DELAY_MS);
- verify(mSpyDevInventory, times(1)).setBluetoothA2dpDeviceConnectionState(
- any(BluetoothDevice.class),
- ArgumentMatchers.eq(BluetoothProfile.STATE_CONNECTED) /*state*/,
- ArgumentMatchers.eq(BluetoothProfile.A2DP) /*profile*/,
- ArgumentMatchers.eq(true) /*suppressNoisyIntent*/, anyInt() /*musicDevice*/,
- ArgumentMatchers.eq(1) /*a2dpVolume*/
+ verify(mSpyDevInventory, times(1)).setBluetoothActiveDevice(
+ any(AudioDeviceBroker.BtDeviceInfo.class)
);
// verify the connection was reported to AudioSystem
@@ -210,30 +206,29 @@ public class AudioDeviceBrokerTest {
((NoOpAudioSystemAdapter) mSpyAudioSystem).configureIsStreamActive(mockMediaPlayback);
// first connection: ensure the device is connected as a starting condition for the test
- mAudioDeviceBroker.queueBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
- new AudioDeviceBroker.BtDeviceConnectionInfo(mFakeBtDevice,
- BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP, true, 1));
+ mAudioDeviceBroker.queueOnBluetoothActiveDeviceChanged(
+ new AudioDeviceBroker.BtDeviceChangedData(mFakeBtDevice, null,
+ BluetoothProfileConnectionInfo.createA2dpInfo(true, 1), "testSource"));
Thread.sleep(MAX_MESSAGE_HANDLING_DELAY_MS);
// disconnection
- mAudioDeviceBroker.queueBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
- new AudioDeviceBroker.BtDeviceConnectionInfo(mFakeBtDevice,
- BluetoothProfile.STATE_DISCONNECTED, BluetoothProfile.A2DP, false, -1));
+ mAudioDeviceBroker.queueOnBluetoothActiveDeviceChanged(
+ new AudioDeviceBroker.BtDeviceChangedData(null, mFakeBtDevice,
+ BluetoothProfileConnectionInfo.createA2dpInfo(false, -1), "testSource"));
if (delayAfterDisconnection > 0) {
Thread.sleep(delayAfterDisconnection);
}
// reconnection
- mAudioDeviceBroker.queueBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
- new AudioDeviceBroker.BtDeviceConnectionInfo(mFakeBtDevice,
- BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP, true, 2));
+ mAudioDeviceBroker.queueOnBluetoothActiveDeviceChanged(
+ new AudioDeviceBroker.BtDeviceChangedData(mFakeBtDevice, null,
+ BluetoothProfileConnectionInfo.createA2dpInfo(true, 2), "testSource"));
Thread.sleep(AudioService.BECOMING_NOISY_DELAY_MS + MAX_MESSAGE_HANDLING_DELAY_MS);
// Verify disconnection has been cancelled and we're seeing two connections attempts,
// with the device connected at the end of the test
- verify(mSpyDevInventory, times(2)).onSetA2dpSinkConnectionState(
- any(BtHelper.BluetoothA2dpDeviceInfo.class),
- ArgumentMatchers.eq(BluetoothProfile.STATE_CONNECTED));
+ verify(mSpyDevInventory, times(2)).onSetBtActiveDevice(
+ any(AudioDeviceBroker.BtDeviceInfo.class), anyInt());
Assert.assertTrue("Mock device not connected",
mSpyDevInventory.isA2dpDeviceConnected(mFakeBtDevice));
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/OWNERS b/services/tests/servicestests/src/com/android/server/biometrics/OWNERS
index 8765c9a64b77..6a2192a2c7fb 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/OWNERS
+++ b/services/tests/servicestests/src/com/android/server/biometrics/OWNERS
@@ -1,7 +1 @@
-set noparent
-
-kchyn@google.com
-jaggies@google.com
-curtislb@google.com
-ilyamaty@google.com
-joshmccloskey@google.com
+include /services/core/java/com/android/server/biometrics/OWNERS
diff --git a/services/tests/servicestests/src/com/android/server/criticalevents/OWNERS b/services/tests/servicestests/src/com/android/server/criticalevents/OWNERS
new file mode 100644
index 000000000000..07c0e70574f0
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/criticalevents/OWNERS
@@ -0,0 +1 @@
+include /services/core/java/com/android/server/criticalevents/OWNERS
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 97fb399c63ef..4ae855ff8b80 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -37,7 +37,11 @@ import static android.app.admin.DevicePolicyManager.PRIVATE_DNS_SET_NO_ERROR;
import static android.app.admin.DevicePolicyManager.WIPE_EUICC;
import static android.app.admin.PasswordMetrics.computeForPasswordOrPin;
import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_DIRECT_BOOT_AWARE;
+import static android.net.ConnectivityManager.PROFILE_NETWORK_PREFERENCE_DEFAULT;
+import static android.net.ConnectivityManager.PROFILE_NETWORK_PREFERENCE_ENTERPRISE;
+import static android.net.ConnectivityManager.PROFILE_NETWORK_PREFERENCE_ENTERPRISE_NO_FALLBACK;
import static android.net.InetAddresses.parseNumericAddress;
+import static android.net.NetworkCapabilities.NET_ENTERPRISE_ID_1;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE;
import static com.android.internal.widget.LockPatternUtils.EscrowTokenStateChangeCallback;
@@ -88,6 +92,7 @@ import android.app.admin.DevicePolicyManagerInternal;
import android.app.admin.DevicePolicyManagerLiteInternal;
import android.app.admin.FactoryResetProtectionPolicy;
import android.app.admin.PasswordMetrics;
+import android.app.admin.PreferentialNetworkServiceConfig;
import android.app.admin.SystemUpdatePolicy;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -100,7 +105,7 @@ import android.content.pm.StringParceledListSlice;
import android.content.pm.UserInfo;
import android.graphics.Color;
import android.hardware.usb.UsbManager;
-import android.net.ConnectivityManager;
+import android.net.ProfileNetworkPreference;
import android.net.Uri;
import android.os.Build;
import android.os.Build.VERSION_CODES;
@@ -4057,12 +4062,15 @@ public class DevicePolicyManagerTest extends DpmTestBase {
mServiceContext.permissions.add(permission.INTERACT_ACROSS_USERS_FULL);
dpms.handleStartUser(managedProfileUserId);
- verify(getServices().connectivityManager, times(1)).setProfileNetworkPreference(
- eq(UserHandle.of(managedProfileUserId)),
- anyInt(),
- any(),
- any()
- );
+ ProfileNetworkPreference preferenceDetails =
+ new ProfileNetworkPreference.Builder()
+ .setPreference(PROFILE_NETWORK_PREFERENCE_DEFAULT)
+ .build();
+ List<ProfileNetworkPreference> preferences = new ArrayList<>();
+ preferences.add(preferenceDetails);
+ verify(getServices().connectivityManager, times(1))
+ .setProfileNetworkPreferences(UserHandle.of(managedProfileUserId), preferences,
+ null, null);
}
@Test
@@ -4074,12 +4082,15 @@ public class DevicePolicyManagerTest extends DpmTestBase {
mServiceContext.permissions.add(permission.INTERACT_ACROSS_USERS_FULL);
dpms.handleStopUser(managedProfileUserId);
- verify(getServices().connectivityManager, times(1)).setProfileNetworkPreference(
- eq(UserHandle.of(managedProfileUserId)),
- eq(ConnectivityManager.PROFILE_NETWORK_PREFERENCE_DEFAULT),
- any(),
- any()
- );
+ ProfileNetworkPreference preferenceDetails =
+ new ProfileNetworkPreference.Builder()
+ .setPreference(PROFILE_NETWORK_PREFERENCE_DEFAULT)
+ .build();
+ List<ProfileNetworkPreference> preferences = new ArrayList<>();
+ preferences.add(preferenceDetails);
+ verify(getServices().connectivityManager, times(1))
+ .setProfileNetworkPreferences(UserHandle.of(managedProfileUserId), preferences,
+ null, null);
}
@Test
@@ -4097,21 +4108,188 @@ public class DevicePolicyManagerTest extends DpmTestBase {
dpm.setPreferentialNetworkServiceEnabled(false);
assertThat(dpm.isPreferentialNetworkServiceEnabled()).isFalse();
- verify(getServices().connectivityManager, times(1)).setProfileNetworkPreference(
- eq(UserHandle.of(managedProfileUserId)),
- eq(ConnectivityManager.PROFILE_NETWORK_PREFERENCE_DEFAULT),
- any(),
- any()
- );
+
+ ProfileNetworkPreference preferenceDetails =
+ new ProfileNetworkPreference.Builder()
+ .setPreference(PROFILE_NETWORK_PREFERENCE_DEFAULT)
+ .build();
+ List<ProfileNetworkPreference> preferences = new ArrayList<>();
+ preferences.add(preferenceDetails);
+ verify(getServices().connectivityManager, times(1))
+ .setProfileNetworkPreferences(UserHandle.of(managedProfileUserId), preferences,
+ null, null);
dpm.setPreferentialNetworkServiceEnabled(true);
assertThat(dpm.isPreferentialNetworkServiceEnabled()).isTrue();
- verify(getServices().connectivityManager, times(1)).setProfileNetworkPreference(
- eq(UserHandle.of(managedProfileUserId)),
- eq(ConnectivityManager.PROFILE_NETWORK_PREFERENCE_ENTERPRISE),
- any(),
- any()
- );
+
+ ProfileNetworkPreference preferenceDetails2 =
+ new ProfileNetworkPreference.Builder()
+ .setPreference(PROFILE_NETWORK_PREFERENCE_ENTERPRISE)
+ .setPreferenceEnterpriseId(NET_ENTERPRISE_ID_1)
+ .build();
+ List<ProfileNetworkPreference> preferences2 = new ArrayList<>();
+ preferences2.add(preferenceDetails);
+ verify(getServices().connectivityManager, times(1))
+ .setProfileNetworkPreferences(UserHandle.of(managedProfileUserId), preferences2,
+ null, null);
+ }
+
+ @Test
+ public void testSetPreferentialNetworkServiceConfig_noProfileOwner() throws Exception {
+ assertExpectException(SecurityException.class, null,
+ () -> dpm.setPreferentialNetworkServiceConfig(
+ PreferentialNetworkServiceConfig.DEFAULT));
+ }
+
+ @Test
+ public void testIsPreferentialNetworkServiceEnabled_noProfileOwner() throws Exception {
+ assertExpectException(SecurityException.class, null,
+ () -> dpm.isPreferentialNetworkServiceEnabled());
+ }
+
+ @Test
+ public void testSetPreferentialNetworkServiceConfig_invalidConfig() throws Exception {
+ final int managedProfileUserId = 15;
+ final int managedProfileAdminUid = UserHandle.getUid(managedProfileUserId, 19436);
+ addManagedProfile(admin1, managedProfileAdminUid, admin1);
+ mContext.binder.callingUid = managedProfileAdminUid;
+
+ PreferentialNetworkServiceConfig.Builder preferentialNetworkServiceConfigBuilder =
+ new PreferentialNetworkServiceConfig.Builder();
+ assertExpectException(NullPointerException.class, null,
+ () -> preferentialNetworkServiceConfigBuilder.setIncludedUids(null));
+ assertExpectException(NullPointerException.class, null,
+ () -> preferentialNetworkServiceConfigBuilder.setExcludedUids(null));
+ assertExpectException(IllegalArgumentException.class, null,
+ () -> preferentialNetworkServiceConfigBuilder.setNetworkId(6));
+ int[] includedUids = new int[]{1, 2};
+ int[] excludedUids = new int[]{3, 4};
+ preferentialNetworkServiceConfigBuilder.setIncludedUids(includedUids);
+ preferentialNetworkServiceConfigBuilder.setExcludedUids(excludedUids);
+
+ assertExpectException(IllegalStateException.class, null,
+ () -> preferentialNetworkServiceConfigBuilder.build());
+ }
+
+ @Test
+ public void testSetPreferentialNetworkServiceConfig_defaultPreference() throws Exception {
+ final int managedProfileUserId = 15;
+ final int managedProfileAdminUid = UserHandle.getUid(managedProfileUserId, 19436);
+ addManagedProfile(admin1, managedProfileAdminUid, admin1);
+ mContext.binder.callingUid = managedProfileAdminUid;
+
+ dpm.setPreferentialNetworkServiceConfig(PreferentialNetworkServiceConfig.DEFAULT);
+ assertThat(dpm.isPreferentialNetworkServiceEnabled()).isFalse();
+ assertThat(dpm.getPreferentialNetworkServiceConfig()
+ .isEnabled()).isFalse();
+
+ ProfileNetworkPreference preferenceDetails =
+ new ProfileNetworkPreference.Builder()
+ .setPreference(PROFILE_NETWORK_PREFERENCE_DEFAULT)
+ .build();
+ List<ProfileNetworkPreference> preferences = new ArrayList<>();
+ preferences.add(preferenceDetails);
+ verify(getServices().connectivityManager, times(1))
+ .setProfileNetworkPreferences(UserHandle.of(managedProfileUserId), preferences,
+ null, null);
+ }
+
+ @Test
+ public void testSetPreferentialNetworkServiceConfig_enterprisePreference() throws Exception {
+ PreferentialNetworkServiceConfig preferentialNetworkServiceConfigEnabled =
+ (new PreferentialNetworkServiceConfig.Builder())
+ .setEnabled(true)
+ .setNetworkId(NET_ENTERPRISE_ID_1)
+ .build();
+
+ final int managedProfileUserId = 15;
+ final int managedProfileAdminUid = UserHandle.getUid(managedProfileUserId, 19436);
+ addManagedProfile(admin1, managedProfileAdminUid, admin1);
+ mContext.binder.callingUid = managedProfileAdminUid;
+
+ dpm.setPreferentialNetworkServiceConfig(preferentialNetworkServiceConfigEnabled);
+ assertThat(dpm.getPreferentialNetworkServiceConfig()
+ .isEnabled()).isTrue();
+ ProfileNetworkPreference preferenceDetails =
+ new ProfileNetworkPreference.Builder()
+ .setPreference(PROFILE_NETWORK_PREFERENCE_ENTERPRISE)
+ .setPreferenceEnterpriseId(NET_ENTERPRISE_ID_1)
+ .build();
+ List<ProfileNetworkPreference> preferences = new ArrayList<>();
+ preferences.add(preferenceDetails);
+ verify(getServices().connectivityManager, times(1))
+ .setProfileNetworkPreferences(UserHandle.of(managedProfileUserId), preferences,
+ null, null);
+ }
+
+ @Test
+ public void testSetPreferentialNetworkServiceConfig_enterprisePreferenceIncludedUids()
+ throws Exception {
+ final int managedProfileUserId = 15;
+ final int managedProfileAdminUid = UserHandle.getUid(managedProfileUserId, 19436);
+ addManagedProfile(admin1, managedProfileAdminUid, admin1);
+ mContext.binder.callingUid = managedProfileAdminUid;
+
+ PreferentialNetworkServiceConfig preferentialNetworkServiceConfigEnabled =
+ (new PreferentialNetworkServiceConfig.Builder())
+ .setEnabled(true)
+ .setNetworkId(NET_ENTERPRISE_ID_1)
+ .setFallbackToDefaultConnectionAllowed(false)
+ .setIncludedUids(new int[]{1, 2})
+ .build();
+ dpm.setPreferentialNetworkServiceConfig(preferentialNetworkServiceConfigEnabled);
+ assertThat(dpm.getPreferentialNetworkServiceConfig()
+ .isEnabled()).isTrue();
+ List<Integer> includedList = new ArrayList<>();
+ includedList.add(1);
+ includedList.add(2);
+ ProfileNetworkPreference preferenceDetails =
+ new ProfileNetworkPreference.Builder()
+ .setPreference(PROFILE_NETWORK_PREFERENCE_ENTERPRISE_NO_FALLBACK)
+ .setPreferenceEnterpriseId(NET_ENTERPRISE_ID_1)
+ .setIncludedUids(includedList)
+ .build();
+ List<ProfileNetworkPreference> preferences = new ArrayList<>();
+ preferences.add(preferenceDetails);
+ verify(getServices().connectivityManager, times(1))
+ .setProfileNetworkPreferences(UserHandle.of(managedProfileUserId), preferences,
+ null, null);
+ }
+
+ @Test
+ public void testSetPreferentialNetworkServiceConfig_enterprisePreferenceExcludedUids()
+ throws Exception {
+ final int managedProfileUserId = 15;
+ final int managedProfileAdminUid = UserHandle.getUid(managedProfileUserId, 19436);
+ addManagedProfile(admin1, managedProfileAdminUid, admin1);
+ mContext.binder.callingUid = managedProfileAdminUid;
+
+ PreferentialNetworkServiceConfig preferentialNetworkServiceConfigEnabled =
+ (new PreferentialNetworkServiceConfig.Builder())
+ .setEnabled(true)
+ .setNetworkId(NET_ENTERPRISE_ID_1)
+ .setFallbackToDefaultConnectionAllowed(false)
+ .setExcludedUids(new int[]{1, 2})
+ .build();
+
+ dpm.setPreferentialNetworkServiceConfig(preferentialNetworkServiceConfigEnabled);
+ assertThat(dpm.getPreferentialNetworkServiceConfig()
+ .isEnabled()).isTrue();
+ List<Integer> excludedUids = new ArrayList<>();
+ excludedUids.add(1);
+ excludedUids.add(2);
+ ProfileNetworkPreference preferenceDetails =
+ new ProfileNetworkPreference.Builder()
+ .setPreference(PROFILE_NETWORK_PREFERENCE_ENTERPRISE_NO_FALLBACK)
+ .setPreferenceEnterpriseId(NET_ENTERPRISE_ID_1)
+ .setExcludedUids(excludedUids)
+ .build();
+ List<ProfileNetworkPreference> preferences = new ArrayList<>();
+ preferences.clear();
+ preferences.add(preferenceDetails);
+ verify(getServices().connectivityManager, times(1))
+ .setProfileNetworkPreferences(UserHandle.of(managedProfileUserId), preferences,
+ null, null);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java
index 6502e4813171..4426f00fba20 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java
@@ -47,6 +47,8 @@ import android.platform.test.annotations.Presubmit;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
+import com.google.common.testing.EqualsTester;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -189,7 +191,7 @@ public class HdmiCecLocalDeviceTest {
mMessageValidator =
new HdmiCecMessageValidator(mHdmiControlService) {
@Override
- int isValid(HdmiCecMessage message) {
+ int isValid(HdmiCecMessage message, boolean isMessageReceived) {
return HdmiCecMessageValidator.OK;
}
};
@@ -208,6 +210,24 @@ public class HdmiCecLocalDeviceTest {
}
@Test
+ public void testEqualsActiveSource() {
+ int logicalAddress = 0;
+ int physicalAddress = 0x0000;
+ new EqualsTester()
+ .addEqualityGroup(
+ new HdmiCecLocalDevice.ActiveSource(logicalAddress, physicalAddress),
+ new HdmiCecLocalDevice.ActiveSource(logicalAddress, physicalAddress))
+ .addEqualityGroup(
+ new HdmiCecLocalDevice.ActiveSource(logicalAddress, physicalAddress + 1))
+ .addEqualityGroup(
+ new HdmiCecLocalDevice.ActiveSource(logicalAddress + 1, physicalAddress))
+ .addEqualityGroup(
+ new HdmiCecLocalDevice.ActiveSource(
+ logicalAddress + 1, physicalAddress + 1))
+ .testEquals();
+ }
+
+ @Test
public void dispatchMessage_logicalAddressDoesNotMatch() {
HdmiCecMessage msg =
new HdmiCecMessage(
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageTest.java
new file mode 100755
index 000000000000..036d08412e7c
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageTest.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2018 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.hdmi;
+
+import androidx.test.filters.SmallTest;
+
+import com.google.common.testing.EqualsTester;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Tests for {@link HdmiCecMessage} class. */
+@SmallTest
+@RunWith(JUnit4.class)
+public class HdmiCecMessageTest {
+
+ @Test
+ public void testEqualsHdmiCecMessage() {
+ int source = 0;
+ int destination = 1;
+ int opcode = 0x7f;
+ byte[] params1 = {0x00, 0x1a, 0x2b, 0x3c};
+ byte[] params2 = {0x00, 0x1a, 0x2b, 0x3c, 0x4d};
+
+ new EqualsTester()
+ .addEqualityGroup(
+ new HdmiCecMessage(source, destination, opcode, params1),
+ new HdmiCecMessage(source, destination, opcode, params1))
+ .addEqualityGroup(new HdmiCecMessage(source, destination, opcode, params2))
+ .addEqualityGroup(new HdmiCecMessage(source + 1, destination, opcode, params1))
+ .addEqualityGroup(new HdmiCecMessage(source, destination + 1, opcode, params1))
+ .addEqualityGroup(new HdmiCecMessage(source, destination, opcode + 1, params1))
+ .testEquals();
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java
index 2307a85fe9cd..3de7969eef50 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java
@@ -647,6 +647,6 @@ public class HdmiCecMessageValidatorTest {
}
private IntegerSubject assertMessageValidity(String message) {
- return assertThat(mHdmiCecMessageValidator.isValid(HdmiUtils.buildMessage(message)));
+ return assertThat(mHdmiCecMessageValidator.isValid(HdmiUtils.buildMessage(message), false));
}
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
index f53ae525eaba..77e49addf79f 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
@@ -33,6 +33,8 @@ import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
@@ -41,6 +43,7 @@ import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiPortInfo;
import android.hardware.hdmi.IHdmiCecVolumeControlFeatureListener;
import android.hardware.hdmi.IHdmiControlStatusChangeListener;
+import android.hardware.hdmi.IHdmiVendorCommandListener;
import android.os.Binder;
import android.os.IPowerManager;
import android.os.IThermalService;
@@ -59,6 +62,7 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import org.mockito.Mock;
+import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
@@ -321,6 +325,18 @@ public class HdmiControlServiceTest {
}
@Test
+ public void normalBoot_queuedActionsStartedAfterBoot() {
+ Mockito.clearInvocations(mAudioSystemDeviceSpy);
+ Mockito.clearInvocations(mPlaybackDeviceSpy);
+
+ mHdmiControlServiceSpy.onBootPhase(PHASE_BOOT_COMPLETED);
+ mTestLooper.dispatchAll();
+
+ verify(mAudioSystemDeviceSpy, times(1)).startQueuedActions();
+ verify(mPlaybackDeviceSpy, times(1)).startQueuedActions();
+ }
+
+ @Test
public void initialPowerStatus_normalBoot_goToStandby_broadcastsPowerStatus_2_0() {
mHdmiControlServiceSpy.getHdmiCecConfig().setIntValue(
HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION,
@@ -793,7 +809,7 @@ public class HdmiControlServiceTest {
@Test
public void handleCecCommand_errorParameter_returnsAbortInvalidOperand() {
// Validity ERROR_PARAMETER. Taken from HdmiCecMessageValidatorTest#isValid_menuStatus
- HdmiCecMessage message = HdmiUtils.buildMessage("40:8D:03");
+ HdmiCecMessage message = HdmiUtils.buildMessage("80:8D:03");
assertThat(mHdmiControlServiceSpy.handleCecCommand(message))
.isEqualTo(Constants.ABORT_INVALID_OPERAND);
@@ -871,6 +887,117 @@ public class HdmiControlServiceTest {
}
@Test
+ public void addVendorCommandListener_receiveCallback_VendorCmdNoIdTest() {
+ int destAddress = mHdmiControlServiceSpy.playback().getDeviceInfo().getLogicalAddress();
+ int sourceAddress = Constants.ADDR_TV;
+ byte[] params = {0x00, 0x01, 0x02, 0x03};
+ int vendorId = 0x123456;
+ mHdmiControlServiceSpy.setPowerStatus(HdmiControlManager.POWER_STATUS_ON);
+
+ VendorCommandListener vendorCmdListener =
+ new VendorCommandListener(sourceAddress, destAddress, params, vendorId);
+ mHdmiControlServiceSpy.addVendorCommandListener(vendorCmdListener, vendorId);
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage vendorCommandNoId =
+ HdmiCecMessageBuilder.buildVendorCommand(sourceAddress, destAddress, params);
+ mNativeWrapper.onCecMessage(vendorCommandNoId);
+ mTestLooper.dispatchAll();
+ assertThat(vendorCmdListener.mVendorCommandCallbackReceived).isTrue();
+ assertThat(vendorCmdListener.mParamsCorrect).isTrue();
+ assertThat(vendorCmdListener.mHasVendorId).isFalse();
+ }
+
+ @Test
+ public void addVendorCommandListener_receiveCallback_VendorCmdWithIdTest() {
+ int destAddress = mHdmiControlServiceSpy.playback().getDeviceInfo().getLogicalAddress();
+ int sourceAddress = Constants.ADDR_TV;
+ byte[] params = {0x00, 0x01, 0x02, 0x03};
+ int vendorId = 0x123456;
+ mHdmiControlServiceSpy.setPowerStatus(HdmiControlManager.POWER_STATUS_ON);
+
+ VendorCommandListener vendorCmdListener =
+ new VendorCommandListener(sourceAddress, destAddress, params, vendorId);
+ mHdmiControlServiceSpy.addVendorCommandListener(vendorCmdListener, vendorId);
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage vendorCommandWithId =
+ HdmiCecMessageBuilder.buildVendorCommandWithId(
+ sourceAddress, destAddress, vendorId, params);
+ mNativeWrapper.onCecMessage(vendorCommandWithId);
+ mTestLooper.dispatchAll();
+ assertThat(vendorCmdListener.mVendorCommandCallbackReceived).isTrue();
+ assertThat(vendorCmdListener.mParamsCorrect).isTrue();
+ assertThat(vendorCmdListener.mHasVendorId).isTrue();
+ }
+
+ @Test
+ public void addVendorCommandListener_noCallback_VendorCmdDiffIdTest() {
+ int destAddress = mHdmiControlServiceSpy.playback().getDeviceInfo().getLogicalAddress();
+ int sourceAddress = Constants.ADDR_TV;
+ byte[] params = {0x00, 0x01, 0x02, 0x03};
+ int vendorId = 0x123456;
+ int diffVendorId = 0x345678;
+ mHdmiControlServiceSpy.setPowerStatus(HdmiControlManager.POWER_STATUS_ON);
+
+ VendorCommandListener vendorCmdListener =
+ new VendorCommandListener(sourceAddress, destAddress, params, vendorId);
+ mHdmiControlServiceSpy.addVendorCommandListener(vendorCmdListener, vendorId);
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage vendorCommandWithDiffId =
+ HdmiCecMessageBuilder.buildVendorCommandWithId(
+ sourceAddress, destAddress, diffVendorId, params);
+ mNativeWrapper.onCecMessage(vendorCommandWithDiffId);
+ mTestLooper.dispatchAll();
+ assertThat(vendorCmdListener.mVendorCommandCallbackReceived).isFalse();
+ }
+
+ private static class VendorCommandListener extends IHdmiVendorCommandListener.Stub {
+ boolean mVendorCommandCallbackReceived = false;
+ boolean mParamsCorrect = false;
+ boolean mHasVendorId = false;
+
+ int mSourceAddress;
+ int mDestAddress;
+ byte[] mParams;
+ int mVendorId;
+
+ VendorCommandListener(int sourceAddress, int destAddress, byte[] params, int vendorId) {
+ this.mSourceAddress = sourceAddress;
+ this.mDestAddress = destAddress;
+ this.mParams = params.clone();
+ this.mVendorId = vendorId;
+ }
+
+ @Override
+ public void onReceived(
+ int sourceAddress, int destAddress, byte[] params, boolean hasVendorId) {
+ mVendorCommandCallbackReceived = true;
+ if (mSourceAddress == sourceAddress && mDestAddress == destAddress) {
+ byte[] expectedParams;
+ if (hasVendorId) {
+ // If the command has vendor ID, we have to add it to mParams.
+ expectedParams = new byte[params.length];
+ expectedParams[0] = (byte) ((mVendorId >> 16) & 0xFF);
+ expectedParams[1] = (byte) ((mVendorId >> 8) & 0xFF);
+ expectedParams[2] = (byte) (mVendorId & 0xFF);
+ System.arraycopy(mParams, 0, expectedParams, 3, mParams.length);
+ } else {
+ expectedParams = params.clone();
+ }
+ if (Arrays.equals(expectedParams, params)) {
+ mParamsCorrect = true;
+ }
+ }
+ mHasVendorId = hasVendorId;
+ }
+
+ @Override
+ public void onControlStateChanged(boolean enabled, int reason) {}
+ }
+
+ @Test
public void dispatchMessageToLocalDevice_broadcastMessage_returnsHandled() {
HdmiCecMessage message = HdmiCecMessageBuilder.buildStandby(
Constants.ADDR_TV,
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiUtilsTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiUtilsTest.java
index 9f0cdd56ed7f..c89c32a03553 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiUtilsTest.java
@@ -31,6 +31,8 @@ import androidx.test.filters.SmallTest;
import com.android.server.hdmi.HdmiUtils.CodecSad;
import com.android.server.hdmi.HdmiUtils.DeviceConfig;
+import com.google.common.testing.EqualsTester;
+
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@@ -116,6 +118,51 @@ public class HdmiUtilsTest {
}
@Test
+ public void testEqualsCodecSad() {
+ byte[] sad = {0x0a, 0x1b, 0x2c};
+ String sadString = "0a1b2c";
+ new EqualsTester()
+ .addEqualityGroup(
+ new HdmiUtils.CodecSad(Constants.AUDIO_CODEC_LPCM, sad),
+ new HdmiUtils.CodecSad(Constants.AUDIO_CODEC_LPCM, sadString))
+ .addEqualityGroup(
+ new HdmiUtils.CodecSad(Constants.AUDIO_CODEC_LPCM, sadString + "01"))
+ .addEqualityGroup(new HdmiUtils.CodecSad(Constants.AUDIO_CODEC_DD, sadString))
+ .addEqualityGroup(
+ new HdmiUtils.CodecSad(Constants.AUDIO_CODEC_DD, sadString + "01"))
+ .testEquals();
+ }
+
+ @Test
+ public void testEqualsDeviceConfig() {
+ String name = "Name";
+
+ CodecSad expectedCodec1 = new CodecSad(Constants.AUDIO_CODEC_LPCM, "011a03");
+ CodecSad expectedCodec2 = new CodecSad(Constants.AUDIO_CODEC_DD, "0d0506");
+ CodecSad expectedCodec3 = new CodecSad(Constants.AUDIO_CODEC_LPCM, "010203");
+ CodecSad expectedCodec4 = new CodecSad(Constants.AUDIO_CODEC_DD, "040506");
+
+ List<CodecSad> list1 = new ArrayList();
+ list1.add(expectedCodec1);
+ list1.add(expectedCodec2);
+ list1.add(expectedCodec3);
+
+ List<CodecSad> list1Duplicate = new ArrayList(list1);
+
+ List<CodecSad> list2 = new ArrayList(list1);
+ list2.add(expectedCodec4);
+
+ new EqualsTester()
+ .addEqualityGroup(
+ new HdmiUtils.DeviceConfig(name, list1),
+ new HdmiUtils.DeviceConfig(name, list1Duplicate))
+ .addEqualityGroup(new HdmiUtils.DeviceConfig(name, list2))
+ .addEqualityGroup(new HdmiUtils.DeviceConfig("my" + name, list1))
+ .addEqualityGroup(new HdmiUtils.DeviceConfig("my" + name, list2))
+ .testEquals();
+ }
+
+ @Test
public void parseSampleXML() {
List<DeviceConfig> config = new ArrayList<>();
try {
diff --git a/services/tests/servicestests/src/com/android/server/health/HealthServiceWrapperTest.java b/services/tests/servicestests/src/com/android/server/health/HealthServiceWrapperTest.java
new file mode 100644
index 000000000000..16d97a454050
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/health/HealthServiceWrapperTest.java
@@ -0,0 +1,242 @@
+/*
+ * Copyright (C) 2017 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.health;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.fail;
+
+import static org.mockito.AdditionalMatchers.not;
+import static org.mockito.Mockito.*;
+
+import android.hidl.manager.V1_0.IServiceManager;
+import android.hidl.manager.V1_0.IServiceNotification;
+import android.os.IServiceCallback;
+import android.os.RemoteException;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentMatcher;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.invocation.InvocationOnMock;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.NoSuchElementException;
+
+@RunWith(AndroidJUnit4.class)
+public class HealthServiceWrapperTest {
+ @Mock IServiceManager mMockedManager;
+ @Mock android.hardware.health.V2_0.IHealth mMockedHal;
+ @Mock android.hardware.health.V2_0.IHealth mMockedHal2;
+
+ @Mock HealthServiceWrapperHidl.Callback mCallback;
+ @Mock HealthServiceWrapperHidl.IServiceManagerSupplier mManagerSupplier;
+ @Mock HealthServiceWrapperHidl.IHealthSupplier mHealthServiceSupplier;
+
+ @Mock android.hardware.health.IHealth.Stub mMockedAidlHal;
+ @Mock android.hardware.health.IHealth.Stub mMockedAidlHal2;
+ @Mock HealthServiceWrapperAidl.ServiceManagerStub mMockedAidlManager;
+ @Mock HealthRegCallbackAidl mRegCallbackAidl;
+
+ HealthServiceWrapper mWrapper;
+
+ private static final String VENDOR = HealthServiceWrapperHidl.INSTANCE_VENDOR;
+ private static final String AIDL_SERVICE_NAME = HealthServiceWrapperAidl.SERVICE_NAME;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ // Mocks the conversion between IHealth and IBinder.
+ when(mMockedAidlHal.asBinder()).thenCallRealMethod(); // returns mMockedAidlHal
+ when(mMockedAidlHal2.asBinder()).thenCallRealMethod(); // returns mMockedAidlHal2
+ when(mMockedAidlHal.queryLocalInterface(android.hardware.health.IHealth.DESCRIPTOR))
+ .thenReturn(mMockedAidlHal);
+ when(mMockedAidlHal2.queryLocalInterface(android.hardware.health.IHealth.DESCRIPTOR))
+ .thenReturn(mMockedAidlHal2);
+ }
+
+ @After
+ public void tearDown() {
+ validateMockitoUsage();
+ if (mWrapper != null) mWrapper.getHandlerThread().quitSafely();
+ }
+
+ public static <T> ArgumentMatcher<T> isOneOf(T[] collection) {
+ return isOneOf(Arrays.asList(collection));
+ }
+
+ public static <T> ArgumentMatcher<T> isOneOf(Collection<T> collection) {
+ return new ArgumentMatcher<T>() {
+ @Override
+ public boolean matches(T e) {
+ return collection.contains(e);
+ }
+
+ @Override
+ public String toString() {
+ return "is one of " + collection.toString();
+ }
+ };
+ }
+
+ /**
+ * Set up mock objects to pretend that the given AIDL and HIDL instances exists.
+ *
+ * <p>Also, when registering service notifications, the mocked service managers immediately
+ * sends 3 registration notifications, including 2 referring to the original HAL and 1 referring
+ * to the new HAL.
+ *
+ * @param aidlInstances e.g. {"android.hardware.health.IHealth/default"}
+ * @param hidlInstances e.g. {"default", "backup"}
+ * @throws Exception
+ */
+ private void initForInstances(String[] aidlInstances, String[] hidlInstances) throws Exception {
+ doAnswer(
+ (invocation) -> {
+ sendAidlRegCallback(invocation, mMockedAidlHal);
+ sendAidlRegCallback(invocation, mMockedAidlHal);
+ sendAidlRegCallback(invocation, mMockedAidlHal2);
+ return null;
+ })
+ .when(mMockedAidlManager)
+ .registerForNotifications(
+ argThat(isOneOf(aidlInstances)), any(IServiceCallback.class));
+ when(mMockedAidlManager.waitForDeclaredService(argThat(isOneOf(aidlInstances))))
+ .thenReturn(mMockedAidlHal)
+ .thenThrow(new RuntimeException("waitForDeclaredService called more than once"));
+ when(mMockedAidlManager.waitForDeclaredService(not(argThat(isOneOf(aidlInstances)))))
+ .thenReturn(null);
+
+ doAnswer(
+ (invocation) -> {
+ // technically, preexisting is ignored by
+ // HealthServiceWrapperHidl.Notification, but still call it correctly.
+ sendNotification(invocation, true);
+ sendNotification(invocation, true);
+ sendNotification(invocation, false);
+ return null;
+ })
+ .when(mMockedManager)
+ .registerForNotifications(
+ eq(android.hardware.health.V2_0.IHealth.kInterfaceName),
+ argThat(isOneOf(hidlInstances)),
+ any(IServiceNotification.class));
+
+ doReturn(mMockedManager).when(mManagerSupplier).get();
+ doReturn(mMockedHal) // init calls this
+ .doReturn(mMockedHal) // notification 1
+ .doReturn(mMockedHal) // notification 2
+ .doReturn(mMockedHal2) // notification 3
+ .doThrow(new RuntimeException("Should not call getService for more than 4 times"))
+ .when(mHealthServiceSupplier)
+ .get(argThat(isOneOf(hidlInstances)));
+ }
+
+ private void waitHandlerThreadFinish() throws Exception {
+ for (int i = 0; i < 5; i++) {
+ if (!mWrapper.getHandlerThread().getThreadHandler().hasMessagesOrCallbacks()) {
+ return;
+ }
+ Thread.sleep(300);
+ }
+ assertFalse(mWrapper.getHandlerThread().getThreadHandler().hasMessagesOrCallbacks());
+ }
+
+ private static void sendNotification(InvocationOnMock invocation, boolean preexisting)
+ throws Exception {
+ ((IServiceNotification) invocation.getArguments()[2])
+ .onRegistration(
+ android.hardware.health.V2_0.IHealth.kInterfaceName,
+ (String) invocation.getArguments()[1],
+ preexisting);
+ }
+
+ private static void sendAidlRegCallback(
+ InvocationOnMock invocation, android.hardware.health.IHealth service) throws Exception {
+ ((IServiceCallback) invocation.getArguments()[1])
+ .onRegistration((String) invocation.getArguments()[0], service.asBinder());
+ }
+
+ private void createWrapper() throws RemoteException {
+ mWrapper =
+ HealthServiceWrapper.create(
+ mRegCallbackAidl,
+ mMockedAidlManager,
+ mCallback,
+ mManagerSupplier,
+ mHealthServiceSupplier);
+ }
+
+ @SmallTest
+ @Test
+ public void testWrapAidlOnly() throws Exception {
+ initForInstances(new String[] {AIDL_SERVICE_NAME}, new String[0]);
+ createWrapper();
+ waitHandlerThreadFinish();
+ verify(mRegCallbackAidl, times(1)).onRegistration(same(null), same(mMockedAidlHal));
+ verify(mRegCallbackAidl, never())
+ .onRegistration(same(mMockedAidlHal), same(mMockedAidlHal));
+ verify(mRegCallbackAidl, times(1))
+ .onRegistration(same(mMockedAidlHal), same(mMockedAidlHal2));
+ verify(mCallback, never()).onRegistration(any(), any(), anyString());
+ }
+
+ @SmallTest
+ @Test
+ public void testWrapPreferAidl() throws Exception {
+ initForInstances(new String[] {AIDL_SERVICE_NAME}, new String[] {VENDOR});
+ createWrapper();
+ waitHandlerThreadFinish();
+ verify(mRegCallbackAidl, times(1)).onRegistration(same(null), same(mMockedAidlHal));
+ verify(mRegCallbackAidl, never())
+ .onRegistration(same(mMockedAidlHal), same(mMockedAidlHal));
+ verify(mRegCallbackAidl, times(1))
+ .onRegistration(same(mMockedAidlHal), same(mMockedAidlHal2));
+ verify(mCallback, never()).onRegistration(any(), any(), anyString());
+ }
+
+ @SmallTest
+ @Test
+ public void testWrapFallbackHidl() throws Exception {
+ initForInstances(new String[0], new String[] {VENDOR});
+ createWrapper();
+ waitHandlerThreadFinish();
+ verify(mRegCallbackAidl, never()).onRegistration(any(), any());
+ verify(mCallback, times(1)).onRegistration(same(null), same(mMockedHal), eq(VENDOR));
+ verify(mCallback, never()).onRegistration(same(mMockedHal), same(mMockedHal), anyString());
+ verify(mCallback, times(1)).onRegistration(same(mMockedHal), same(mMockedHal2), eq(VENDOR));
+ }
+
+ @SmallTest
+ @Test
+ public void testNoService() throws Exception {
+ initForInstances(new String[0], new String[] {"unrelated"});
+ try {
+ createWrapper();
+ fail("Expect NoSuchElementException");
+ } catch (NoSuchElementException ex) {
+ // expected
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/health/OWNERS b/services/tests/servicestests/src/com/android/server/health/OWNERS
new file mode 100644
index 000000000000..81522fcaa09f
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/health/OWNERS
@@ -0,0 +1 @@
+file:platform/hardware/interfaces:/health/aidl/OWNERS
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
index 70641c2938a7..94cf20f9c15b 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
@@ -49,15 +49,11 @@ import static android.net.NetworkPolicyManager.blockedReasonsToString;
import static android.net.NetworkPolicyManager.uidPoliciesToString;
import static android.net.NetworkPolicyManager.uidRulesToString;
import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK;
-import static android.net.NetworkStats.IFACE_ALL;
import static android.net.NetworkStats.METERED_NO;
import static android.net.NetworkStats.METERED_YES;
-import static android.net.NetworkStats.SET_ALL;
-import static android.net.NetworkStats.TAG_ALL;
-import static android.net.NetworkStats.TAG_NONE;
-import static android.net.NetworkTemplate.buildTemplateCarrierMetered;
-import static android.net.NetworkTemplate.buildTemplateWifi;
-import static android.net.TrafficStats.MB_IN_BYTES;
+import static android.net.NetworkTemplate.MATCH_CARRIER;
+import static android.net.NetworkTemplate.MATCH_MOBILE;
+import static android.net.NetworkTemplate.MATCH_WIFI;
import static android.telephony.CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED;
import static android.telephony.CarrierConfigManager.DATA_CYCLE_THRESHOLD_DISABLED;
import static android.telephony.CarrierConfigManager.DATA_CYCLE_USE_PLATFORM_DEFAULT;
@@ -98,6 +94,7 @@ import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -108,6 +105,8 @@ import android.app.IActivityManager;
import android.app.IUidObserver;
import android.app.Notification;
import android.app.NotificationManager;
+import android.app.usage.NetworkStats;
+import android.app.usage.NetworkStatsManager;
import android.app.usage.UsageStatsManagerInternal;
import android.content.Context;
import android.content.Intent;
@@ -125,10 +124,9 @@ import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkPolicy;
import android.net.NetworkStateSnapshot;
-import android.net.NetworkStats;
-import android.net.NetworkStatsHistory;
import android.net.NetworkTemplate;
import android.net.TelephonyNetworkSpecifier;
+import android.net.wifi.WifiInfo;
import android.os.Binder;
import android.os.Handler;
import android.os.INetworkManagementService;
@@ -137,7 +135,6 @@ import android.os.PowerManagerInternal;
import android.os.PowerSaveState;
import android.os.RemoteException;
import android.os.SimpleClock;
-import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
import android.platform.test.annotations.Presubmit;
@@ -207,6 +204,7 @@ import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.TimeZone;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
@@ -226,15 +224,17 @@ public class NetworkPolicyManagerServiceTest {
private static final long TEST_START = 1194220800000L;
private static final String TEST_IFACE = "test0";
- private static final String TEST_SSID = "AndroidAP";
+ private static final String TEST_WIFI_NETWORK_KEY = "TestWifiNetworkKey";
private static final String TEST_IMSI = "310210";
private static final int TEST_SUB_ID = 42;
private static final Network TEST_NETWORK = mock(Network.class, CALLS_REAL_METHODS);
-
- private static NetworkTemplate sTemplateWifi = buildTemplateWifi(TEST_SSID);
+ private static NetworkTemplate sTemplateWifi = new NetworkTemplate.Builder(MATCH_WIFI)
+ .setWifiNetworkKeys(Set.of(TEST_WIFI_NETWORK_KEY)).build();
private static NetworkTemplate sTemplateCarrierMetered =
- buildTemplateCarrierMetered(TEST_IMSI);
+ new NetworkTemplate.Builder(MATCH_CARRIER)
+ .setSubscriberIds(Set.of(TEST_IMSI))
+ .setMeteredness(METERED_YES).build();
/**
* Path on assets where files used by {@link NetPolicyXml} are located.
@@ -262,12 +262,13 @@ public class NetworkPolicyManagerServiceTest {
private @Mock CarrierConfigManager mCarrierConfigManager;
private @Mock TelephonyManager mTelephonyManager;
private @Mock UserManager mUserManager;
+ private @Mock NetworkStatsManager mStatsManager;
+ private TestDependencies mDeps;
private ArgumentCaptor<ConnectivityManager.NetworkCallback> mNetworkCallbackCaptor =
ArgumentCaptor.forClass(ConnectivityManager.NetworkCallback.class);
private ActivityManagerInternal mActivityManagerInternal;
- private NetworkStatsManagerInternal mStatsService;
private IUidObserver mUidObserver;
private INetworkManagementEventObserver mNetworkObserver;
@@ -334,8 +335,47 @@ public class NetworkPolicyManagerServiceTest {
.setBatterySaverEnabled(false).build();
final PowerManagerInternal pmInternal = addLocalServiceMock(PowerManagerInternal.class);
when(pmInternal.getLowPowerState(anyInt())).thenReturn(state);
+ }
+
+ private class TestDependencies extends NetworkPolicyManagerService.Dependencies {
+ private final SparseArray<NetworkStats.Bucket> mMockedStats = new SparseArray<>();
+
+ TestDependencies(Context context) {
+ super(context);
+ }
+
+ @Override
+ long getNetworkTotalBytes(NetworkTemplate template, long start, long end) {
+ int total = 0;
+ for (int i = 0; i < mMockedStats.size(); i++) {
+ NetworkStats.Bucket bucket = mMockedStats.valueAt(i);
+ total += bucket.getRxBytes() + bucket.getTxBytes();
+ }
+ return total;
+ }
- mStatsService = addLocalServiceMock(NetworkStatsManagerInternal.class);
+ @Override
+ List<NetworkStats.Bucket> getNetworkUidBytes(NetworkTemplate template, long start,
+ long end) {
+ final List<NetworkStats.Bucket> ret = new ArrayList<>();
+ for (int i = 0; i < mMockedStats.size(); i++) {
+ ret.add(mMockedStats.valueAt(i));
+ }
+ return ret;
+ }
+
+ private void setMockedTotalBytes(int uid, long rxBytes, long txBytes) {
+ final NetworkStats.Bucket bucket = mock(NetworkStats.Bucket.class);
+ when(bucket.getUid()).thenReturn(uid);
+ when(bucket.getRxBytes()).thenReturn(rxBytes);
+ when(bucket.getTxBytes()).thenReturn(txBytes);
+ mMockedStats.set(uid, bucket);
+ }
+
+ private void increaseMockedTotalBytes(int uid, long rxBytes, long txBytes) {
+ final NetworkStats.Bucket bucket = mMockedStats.get(uid);
+ setMockedTotalBytes(uid, bucket.getRxBytes() + rxBytes, bucket.getTxBytes() + txBytes);
+ }
}
@Before
@@ -375,6 +415,8 @@ public class NetworkPolicyManagerServiceTest {
return mConnManager;
case Context.USER_SERVICE:
return mUserManager;
+ case Context.NETWORK_STATS_SERVICE:
+ return mStatsManager;
default:
return super.getSystemService(name);
}
@@ -399,8 +441,9 @@ public class NetworkPolicyManagerServiceTest {
}).when(mActivityManager).registerUidObserver(any(), anyInt(), anyInt(), any(String.class));
mFutureIntent = newRestrictBackgroundChangedFuture();
+ mDeps = new TestDependencies(mServiceContext);
mService = new NetworkPolicyManagerService(mServiceContext, mActivityManager,
- mNetworkManager, mIpm, mClock, mPolicyDir, true);
+ mNetworkManager, mIpm, mClock, mPolicyDir, true, mDeps);
mService.bindConnectivityManager();
mPolicyListener = new NetworkPolicyListenerAnswer(mService);
@@ -455,6 +498,15 @@ public class NetworkPolicyManagerServiceTest {
verify(mNetworkManager).registerObserver(networkObserver.capture());
mNetworkObserver = networkObserver.getValue();
+ // Catch UsageCallback during systemReady(). Simulate NetworkStatsService triggered
+ // stats updated callback to signal its readiness.
+ final ArgumentCaptor<NetworkStatsManager.UsageCallback> usageObserver =
+ ArgumentCaptor.forClass(NetworkStatsManager.UsageCallback.class);
+ verify(mStatsManager, times(2))
+ .registerUsageCallback(any(), anyLong(), any(), usageObserver.capture());
+ usageObserver.getValue().onThresholdReached(
+ new NetworkTemplate.Builder(MATCH_MOBILE).build());
+
NetworkPolicy defaultPolicy = mService.buildDefaultCarrierPolicy(0, "");
mDefaultWarningBytes = defaultPolicy.warningBytes;
mDefaultLimitBytes = defaultPolicy.limitBytes;
@@ -478,7 +530,6 @@ public class NetworkPolicyManagerServiceTest {
LocalServices.removeServiceForTest(DeviceIdleInternal.class);
LocalServices.removeServiceForTest(AppStandbyInternal.class);
LocalServices.removeServiceForTest(UsageStatsManagerInternal.class);
- LocalServices.removeServiceForTest(NetworkStatsManagerInternal.class);
}
@After
@@ -1107,42 +1158,20 @@ public class NetworkPolicyManagerServiceTest {
when(mConnManager.getAllNetworkStateSnapshots()).thenReturn(snapshots);
// pretend that 512 bytes total have happened
- stats = new NetworkStats(getElapsedRealtime(), 1)
- .insertEntry(TEST_IFACE, 256L, 2L, 256L, 2L);
- when(mStatsService.getNetworkTotalBytes(sTemplateWifi, CYCLE_START, CYCLE_END))
- .thenReturn(stats.getTotalBytes());
+ mDeps.setMockedTotalBytes(UID_A, 256L, 256L);
mPolicyListener.expect().onMeteredIfacesChanged(any());
setNetworkPolicies(new NetworkPolicy(
- sTemplateWifi, CYCLE_DAY, TIMEZONE_UTC, 1 * MB_IN_BYTES, 2 * MB_IN_BYTES, false));
+ sTemplateWifi, CYCLE_DAY, TIMEZONE_UTC, DataUnit.MEBIBYTES.toBytes(1),
+ DataUnit.MEBIBYTES.toBytes(2), false));
mPolicyListener.waitAndVerify().onMeteredIfacesChanged(eq(new String[]{TEST_IFACE}));
verify(mNetworkManager, atLeastOnce()).setInterfaceQuota(TEST_IFACE,
- (2 * MB_IN_BYTES) - 512);
+ DataUnit.MEBIBYTES.toBytes(2) - 512);
}
@Test
public void testNotificationWarningLimitSnooze() throws Exception {
- // Create a place to store fake usage
- final NetworkStatsHistory history = new NetworkStatsHistory(TimeUnit.HOURS.toMillis(1));
- final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 0);
- when(mStatsService.getNetworkTotalBytes(any(), anyLong(), anyLong()))
- .thenAnswer(new Answer<Long>() {
- @Override
- public Long answer(InvocationOnMock invocation) throws Throwable {
- final NetworkStatsHistory.Entry entry = history.getValues(
- invocation.getArgument(1), invocation.getArgument(2), null);
- return entry.rxBytes + entry.txBytes;
- }
- });
- when(mStatsService.getNetworkUidBytes(any(), anyLong(), anyLong()))
- .thenAnswer(new Answer<NetworkStats>() {
- @Override
- public NetworkStats answer(InvocationOnMock invocation) throws Throwable {
- return stats;
- }
- });
-
// Get active mobile network in place
expectMobileDefaults();
mService.updateNetworks();
@@ -1160,9 +1189,7 @@ public class NetworkPolicyManagerServiceTest {
// Normal usage means no notification
{
- history.clear();
- history.recordData(start, end,
- new NetworkStats.Entry(DataUnit.MEGABYTES.toBytes(360), 0L, 0L, 0L, 0));
+ mDeps.setMockedTotalBytes(UID_A, DataUnit.MEGABYTES.toBytes(360), 0);
reset(mTelephonyManager, mNetworkManager, mNotifManager);
TelephonyManager tmSub = expectMobileDefaults();
@@ -1177,9 +1204,7 @@ public class NetworkPolicyManagerServiceTest {
// Push over warning
{
- history.clear();
- history.recordData(start, end,
- new NetworkStats.Entry(DataUnit.MEGABYTES.toBytes(1799), 0L, 0L, 0L, 0));
+ mDeps.setMockedTotalBytes(UID_A, DataUnit.MEGABYTES.toBytes(1799), 0);
reset(mTelephonyManager, mNetworkManager, mNotifManager);
TelephonyManager tmSub = expectMobileDefaults();
@@ -1195,9 +1220,7 @@ public class NetworkPolicyManagerServiceTest {
// Push over warning, but with a config that isn't from an identified carrier
{
- history.clear();
- history.recordData(start, end,
- new NetworkStats.Entry(DataUnit.MEGABYTES.toBytes(1799), 0L, 0L, 0L, 0));
+ mDeps.setMockedTotalBytes(UID_A, DataUnit.MEGABYTES.toBytes(1799), 0);
reset(mTelephonyManager, mNetworkManager, mNotifManager);
TelephonyManager tmSub = expectMobileDefaults();
@@ -1214,9 +1237,7 @@ public class NetworkPolicyManagerServiceTest {
// Push over limit
{
- history.clear();
- history.recordData(start, end,
- new NetworkStats.Entry(DataUnit.MEGABYTES.toBytes(1810), 0L, 0L, 0L, 0));
+ mDeps.setMockedTotalBytes(UID_A, DataUnit.MEGABYTES.toBytes(1810), 0);
reset(mTelephonyManager, mNetworkManager, mNotifManager);
TelephonyManager tmSub = expectMobileDefaults();
@@ -1234,7 +1255,7 @@ public class NetworkPolicyManagerServiceTest {
reset(mTelephonyManager, mNetworkManager, mNotifManager);
TelephonyManager tmSub = expectMobileDefaults();
- mService.snoozeLimit(NetworkTemplate.buildTemplateCarrierMetered(TEST_IMSI));
+ mService.snoozeLimit(sTemplateCarrierMetered);
mService.updateNetworks();
verify(tmSub, atLeastOnce()).setPolicyDataEnabled(true);
@@ -1247,26 +1268,6 @@ public class NetworkPolicyManagerServiceTest {
@Test
public void testNotificationRapid() throws Exception {
- // Create a place to store fake usage
- final NetworkStatsHistory history = new NetworkStatsHistory(TimeUnit.HOURS.toMillis(1));
- final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 0);
- when(mStatsService.getNetworkTotalBytes(any(), anyLong(), anyLong()))
- .thenAnswer(new Answer<Long>() {
- @Override
- public Long answer(InvocationOnMock invocation) throws Throwable {
- final NetworkStatsHistory.Entry entry = history.getValues(
- invocation.getArgument(1), invocation.getArgument(2), null);
- return entry.rxBytes + entry.txBytes;
- }
- });
- when(mStatsService.getNetworkUidBytes(any(), anyLong(), anyLong()))
- .thenAnswer(new Answer<NetworkStats>() {
- @Override
- public NetworkStats answer(InvocationOnMock invocation) throws Throwable {
- return stats;
- }
- });
-
// Get active mobile network in place
expectMobileDefaults();
mService.updateNetworks();
@@ -1284,9 +1285,7 @@ public class NetworkPolicyManagerServiceTest {
// Using 20% data in 20% time is normal
{
- history.clear();
- history.recordData(start, end,
- new NetworkStats.Entry(DataUnit.MEGABYTES.toBytes(360), 0L, 0L, 0L, 0));
+ mDeps.setMockedTotalBytes(UID_A, DataUnit.MEGABYTES.toBytes(360), 0);
reset(mNotifManager);
mService.updateNetworks();
@@ -1296,16 +1295,9 @@ public class NetworkPolicyManagerServiceTest {
// Using 80% data in 20% time is alarming; but spread equally among
// three UIDs means we get generic alert
{
- history.clear();
- history.recordData(start, end,
- new NetworkStats.Entry(DataUnit.MEGABYTES.toBytes(1440), 0L, 0L, 0L, 0));
- stats.clear();
- stats.insertEntry(IFACE_ALL, UID_A, SET_ALL, TAG_ALL,
- DataUnit.MEGABYTES.toBytes(480), 0, 0, 0, 0);
- stats.insertEntry(IFACE_ALL, UID_B, SET_ALL, TAG_ALL,
- DataUnit.MEGABYTES.toBytes(480), 0, 0, 0, 0);
- stats.insertEntry(IFACE_ALL, UID_C, SET_ALL, TAG_ALL,
- DataUnit.MEGABYTES.toBytes(480), 0, 0, 0, 0);
+ mDeps.setMockedTotalBytes(UID_A, DataUnit.MEGABYTES.toBytes(480), 0);
+ mDeps.setMockedTotalBytes(UID_B, DataUnit.MEGABYTES.toBytes(480), 0);
+ mDeps.setMockedTotalBytes(UID_C, DataUnit.MEGABYTES.toBytes(480), 0);
reset(mNotifManager);
mService.updateNetworks();
@@ -1324,14 +1316,9 @@ public class NetworkPolicyManagerServiceTest {
// Using 80% data in 20% time is alarming; but mostly done by one UID
// means we get specific alert
{
- history.clear();
- history.recordData(start, end,
- new NetworkStats.Entry(DataUnit.MEGABYTES.toBytes(1440), 0L, 0L, 0L, 0));
- stats.clear();
- stats.insertEntry(IFACE_ALL, UID_A, SET_ALL, TAG_ALL,
- DataUnit.MEGABYTES.toBytes(960), 0, 0, 0, 0);
- stats.insertEntry(IFACE_ALL, UID_B, SET_ALL, TAG_ALL,
- DataUnit.MEGABYTES.toBytes(480), 0, 0, 0, 0);
+ mDeps.setMockedTotalBytes(UID_A, DataUnit.MEGABYTES.toBytes(960), 0);
+ mDeps.setMockedTotalBytes(UID_B, DataUnit.MEGABYTES.toBytes(480), 0);
+ mDeps.setMockedTotalBytes(UID_C, 0, 0);
reset(mNotifManager);
mService.updateNetworks();
@@ -1361,13 +1348,10 @@ public class NetworkPolicyManagerServiceTest {
// bring up wifi network with metered policy
snapshots = List.of(buildWifi());
- stats = new NetworkStats(getElapsedRealtime(), 1)
- .insertEntry(TEST_IFACE, 0L, 0L, 0L, 0L);
+ mDeps.setMockedTotalBytes(UID_A, 0L, 0L);
{
when(mConnManager.getAllNetworkStateSnapshots()).thenReturn(snapshots);
- when(mStatsService.getNetworkTotalBytes(sTemplateWifi, TIME_FEB_15,
- currentTimeMillis())).thenReturn(stats.getTotalBytes());
mPolicyListener.expect().onMeteredIfacesChanged(any());
setNetworkPolicies(new NetworkPolicy(
@@ -1646,18 +1630,6 @@ public class NetworkPolicyManagerServiceTest {
final NetworkPolicyManagerInternal internal = LocalServices
.getService(NetworkPolicyManagerInternal.class);
- // Create a place to store fake usage
- final NetworkStatsHistory history = new NetworkStatsHistory(TimeUnit.HOURS.toMillis(1));
- final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 0);
- when(mStatsService.getNetworkTotalBytes(any(), anyLong(), anyLong()))
- .thenAnswer(invocation -> {
- final NetworkStatsHistory.Entry entry = history.getValues(
- invocation.getArgument(1), invocation.getArgument(2), null);
- return entry.rxBytes + entry.txBytes;
- });
- when(mStatsService.getNetworkUidBytes(any(), anyLong(), anyLong()))
- .thenReturn(stats);
-
// Get active mobile network in place
expectMobileDefaults();
mService.updateNetworks();
@@ -1668,9 +1640,7 @@ public class NetworkPolicyManagerServiceTest {
setCurrentTimeMillis(end);
// Get some data usage in place
- history.clear();
- history.recordData(start, end,
- new NetworkStats.Entry(DataUnit.MEGABYTES.toBytes(360), 0L, 0L, 0L, 0));
+ mDeps.setMockedTotalBytes(UID_A, DataUnit.MEGABYTES.toBytes(360), 0);
// No data plan
{
@@ -1785,22 +1755,11 @@ public class NetworkPolicyManagerServiceTest {
true);
}
- private void increaseMockedTotalBytes(NetworkStats stats, long rxBytes, long txBytes) {
- stats.insertEntry(TEST_IFACE, UID_A, SET_ALL, TAG_NONE,
- rxBytes, 1, txBytes, 1, 0);
- when(mStatsService.getNetworkTotalBytes(any(), anyLong(), anyLong()))
- .thenReturn(stats.getTotalBytes());
- when(mStatsService.getNetworkUidBytes(any(), anyLong(), anyLong()))
- .thenReturn(stats);
- }
-
private void triggerOnStatsProviderWarningOrLimitReached() throws InterruptedException {
- final NetworkPolicyManagerInternal npmi = LocalServices
- .getService(NetworkPolicyManagerInternal.class);
- npmi.onStatsProviderWarningOrLimitReached("TEST");
+ mService.notifyStatsProviderWarningOrLimitReached();
// Wait for processing of MSG_STATS_PROVIDER_WARNING_OR_LIMIT_REACHED.
postMsgAndWaitForCompletion();
- verify(mStatsService).forceUpdate();
+ verify(mStatsManager).forceUpdate();
// Wait for processing of MSG_*_INTERFACE_QUOTAS.
postMsgAndWaitForCompletion();
}
@@ -1813,13 +1772,12 @@ public class NetworkPolicyManagerServiceTest {
public void testStatsProviderWarningAndLimitReached() throws Exception {
final int CYCLE_DAY = 15;
- final NetworkStats stats = new NetworkStats(0L, 1);
- increaseMockedTotalBytes(stats, 2999, 2000);
+ mDeps.setMockedTotalBytes(UID_A, 2999, 2000);
// Get active mobile network in place
expectMobileDefaults();
mService.updateNetworks();
- verify(mStatsService).setStatsProviderWarningAndLimitAsync(TEST_IFACE, Long.MAX_VALUE,
+ verify(mStatsManager).setStatsProviderWarningAndLimitAsync(TEST_IFACE, Long.MAX_VALUE,
Long.MAX_VALUE);
// Set warning to 7KB and limit to 10KB.
@@ -1829,32 +1787,32 @@ public class NetworkPolicyManagerServiceTest {
postMsgAndWaitForCompletion();
// Verifies that remaining quotas are set to providers.
- verify(mStatsService).setStatsProviderWarningAndLimitAsync(TEST_IFACE, 2001L, 5001L);
- reset(mStatsService);
+ verify(mStatsManager).setStatsProviderWarningAndLimitAsync(TEST_IFACE, 2001L, 5001L);
+ reset(mStatsManager);
// Increase the usage and simulates that limit reached fires earlier by provider,
// but actually the quota is not yet reached. Verifies that the limit reached leads to
// a force update and new quotas should be set.
- increaseMockedTotalBytes(stats, 1000, 999);
+ mDeps.increaseMockedTotalBytes(UID_A, 1000, 999);
triggerOnStatsProviderWarningOrLimitReached();
- verify(mStatsService).setStatsProviderWarningAndLimitAsync(TEST_IFACE, 2L, 3002L);
- reset(mStatsService);
+ verify(mStatsManager).setStatsProviderWarningAndLimitAsync(TEST_IFACE, 2L, 3002L);
+ reset(mStatsManager);
// Increase the usage and simulate warning reached, the new warning should be unlimited
// since service will disable warning quota to stop lower layer from keep triggering
// warning reached event.
- increaseMockedTotalBytes(stats, 1000L, 1000);
+ mDeps.increaseMockedTotalBytes(UID_A, 1000L, 1000);
triggerOnStatsProviderWarningOrLimitReached();
- verify(mStatsService).setStatsProviderWarningAndLimitAsync(
+ verify(mStatsManager).setStatsProviderWarningAndLimitAsync(
TEST_IFACE, Long.MAX_VALUE, 1002L);
- reset(mStatsService);
+ reset(mStatsManager);
// Increase the usage that over the warning and limit, the new limit should set to 1 to
// block the network traffic.
- increaseMockedTotalBytes(stats, 1000L, 1000);
+ mDeps.increaseMockedTotalBytes(UID_A, 1000L, 1000);
triggerOnStatsProviderWarningOrLimitReached();
- verify(mStatsService).setStatsProviderWarningAndLimitAsync(TEST_IFACE, Long.MAX_VALUE, 1L);
- reset(mStatsService);
+ verify(mStatsManager).setStatsProviderWarningAndLimitAsync(TEST_IFACE, Long.MAX_VALUE, 1L);
+ reset(mStatsManager);
}
private void enableRestrictedMode(boolean enable) throws Exception {
@@ -1986,9 +1944,8 @@ public class NetworkPolicyManagerServiceTest {
assertEquals("Unexpected template match rule in network policies",
NetworkTemplate.MATCH_CARRIER,
actualPolicy.template.getMatchRule());
- assertEquals("Unexpected subscriberId match rule in network policies",
- NetworkTemplate.SUBSCRIBER_ID_MATCH_RULE_EXACT,
- actualPolicy.template.getSubscriberIdMatchRule());
+ assertTrue("Unexpected subscriberIds size in network policies",
+ actualPolicy.template.getSubscriberIds().size() > 0);
assertEquals("Unexpected template meteredness in network policies",
METERED_YES, actualPolicy.template.getMeteredness());
}
@@ -2001,11 +1958,10 @@ public class NetworkPolicyManagerServiceTest {
assertEquals("Unexpected number of network policies", 1, policies.length);
NetworkPolicy actualPolicy = policies[0];
assertEquals("Unexpected template match rule in network policies",
- NetworkTemplate.MATCH_WIFI,
+ MATCH_WIFI,
actualPolicy.template.getMatchRule());
- assertEquals("Unexpected subscriberId match rule in network policies",
- NetworkTemplate.SUBSCRIBER_ID_MATCH_RULE_ALL,
- actualPolicy.template.getSubscriberIdMatchRule());
+ assertEquals("Unexpected subscriberIds size in network policies",
+ actualPolicy.template.getSubscriberIds().size(), 0);
assertEquals("Unexpected template meteredness in network policies",
METERED_NO, actualPolicy.template.getMeteredness());
}
@@ -2073,7 +2029,10 @@ public class NetworkPolicyManagerServiceTest {
private static NetworkPolicy buildFakeCarrierPolicy(int cycleDay, long warningBytes,
long limitBytes, boolean inferred) {
- final NetworkTemplate template = buildTemplateCarrierMetered(FAKE_SUBSCRIBER_ID);
+ // TODO: Refactor this to use sTemplateCarrierMetered.
+ final NetworkTemplate template = new NetworkTemplate.Builder(MATCH_CARRIER)
+ .setSubscriberIds(Set.of(FAKE_SUBSCRIBER_ID))
+ .setMeteredness(METERED_YES).build();
return new NetworkPolicy(template, cycleDay, TimeZone.getDefault().getID(), warningBytes,
limitBytes, SNOOZE_NEVER, SNOOZE_NEVER, true, inferred);
}
@@ -2098,10 +2057,13 @@ public class NetworkPolicyManagerServiceTest {
}
private static NetworkStateSnapshot buildWifi() {
+ WifiInfo mockWifiInfo = mock(WifiInfo.class);
+ when(mockWifiInfo.makeCopy(anyLong())).thenReturn(mockWifiInfo);
+ when(mockWifiInfo.getNetworkKey()).thenReturn(TEST_WIFI_NETWORK_KEY);
final LinkProperties prop = new LinkProperties();
prop.setInterfaceName(TEST_IFACE);
final NetworkCapabilities networkCapabilities = new NetworkCapabilities.Builder()
- .addTransportType(TRANSPORT_WIFI).setSsid(TEST_SSID).build();
+ .addTransportType(TRANSPORT_WIFI).setTransportInfo(mockWifiInfo).build();
return new NetworkStateSnapshot(TEST_NETWORK, networkCapabilities, prop,
null /*subscriberId*/, TYPE_WIFI);
}
@@ -2143,7 +2105,7 @@ public class NetworkPolicyManagerServiceTest {
}
private void verifyAdvisePersistThreshold() throws Exception {
- verify(mStatsService).advisePersistThreshold(anyLong());
+ verify(mStatsManager).setDefaultGlobalAlert(anyLong());
}
private static class TestAbstractFuture<T> extends AbstractFuture<T> {
diff --git a/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java
index 8b8a7e631caf..2bda120afb9d 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java
@@ -40,6 +40,7 @@ import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.os.RemoteException;
+import android.os.ServiceSpecificException;
import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
@@ -232,6 +233,21 @@ public class ApexManagerTest {
}
@Test
+ public void testGetStagedApexInfos_throwRunTimeException() throws RemoteException {
+ doThrow(RemoteException.class).when(mApexService).getStagedApexInfos(any());
+
+ assertThrows(RuntimeException.class,
+ () -> mApexManager.getStagedApexInfos(testParamsWithChildren()));
+ }
+
+ @Test
+ public void testGetStagedApexInfos_returnsEmptyArrayOnError() throws RemoteException {
+ doThrow(ServiceSpecificException.class).when(mApexService).getStagedApexInfos(any());
+
+ assertThat(mApexManager.getStagedApexInfos(testParamsWithChildren())).hasLength(0);
+ }
+
+ @Test
public void testMarkStagedSessionReady_throwPackageManagerException() throws RemoteException {
doAnswer(invocation -> {
throw new Exception();
diff --git a/services/tests/servicestests/src/com/android/server/pm/OWNERS b/services/tests/servicestests/src/com/android/server/pm/OWNERS
index e15b5f57069c..2f51994a2b1a 100644
--- a/services/tests/servicestests/src/com/android/server/pm/OWNERS
+++ b/services/tests/servicestests/src/com/android/server/pm/OWNERS
@@ -1,3 +1,7 @@
include /services/core/java/com/android/server/pm/OWNERS
per-file *Shortcut* = file:/core/java/android/content/pm/SHORTCUT_OWNERS
+
+# apex support
+per-file ApexManagerTest.java = dariofreni@google.com, ioffe@google.com, olilan@google.com
+
diff --git a/services/tests/servicestests/src/com/android/server/pm/SELinuxMMACTest.java b/services/tests/servicestests/src/com/android/server/pm/SELinuxMMACTest.java
index f1930d7268d7..901b200417c9 100644
--- a/services/tests/servicestests/src/com/android/server/pm/SELinuxMMACTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/SELinuxMMACTest.java
@@ -44,7 +44,7 @@ import org.mockito.junit.MockitoJUnitRunner;
public class SELinuxMMACTest {
private static final String PACKAGE_NAME = "my.package";
- private static final int LATEST_OPT_IN_VERSION = Build.VERSION_CODES.S;
+ private static final int LATEST_OPT_IN_VERSION = Build.VERSION_CODES.CUR_DEVELOPMENT;
private static final int R_OPT_IN_VERSION = Build.VERSION_CODES.R;
@Mock
@@ -91,6 +91,16 @@ public class SELinuxMMACTest {
}
@Test
+ public void getSeInfoTargetingCurDevelopment() {
+ AndroidPackage pkg = makePackage(Build.VERSION_CODES.CUR_DEVELOPMENT);
+ when(mMockCompatibility.isChangeEnabledInternal(eq(SELinuxMMAC.SELINUX_LATEST_CHANGES),
+ argThat(argument -> argument.packageName.equals(pkg.getPackageName()))))
+ .thenReturn(true);
+ assertThat(SELinuxMMAC.getSeInfo(pkg, null, mMockCompatibility),
+ is("default:targetSdkVersion=" + Build.VERSION_CODES.CUR_DEVELOPMENT));
+ }
+
+ @Test
public void getSeInfoNoOptInButAlreadyR() {
AndroidPackage pkg = makePackage(R_OPT_IN_VERSION);
when(mMockCompatibility.isChangeEnabledInternal(eq(SELinuxMMAC.SELINUX_R_CHANGES),
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/OWNERS b/services/tests/servicestests/src/com/android/server/timedetector/OWNERS
index 8f8089717e3b..a0f46e172da6 100644
--- a/services/tests/servicestests/src/com/android/server/timedetector/OWNERS
+++ b/services/tests/servicestests/src/com/android/server/timedetector/OWNERS
@@ -1,3 +1,2 @@
# Bug component: 847766
-mingaleev@google.com
-include /core/java/android/app/timedetector/OWNERS
+include /services/core/java/com/android/server/timedetector/OWNERS
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/ReferenceWithHistoryTest.java b/services/tests/servicestests/src/com/android/server/timedetector/ReferenceWithHistoryTest.java
index ce72499007ba..d5d2cbd0749e 100644
--- a/services/tests/servicestests/src/com/android/server/timedetector/ReferenceWithHistoryTest.java
+++ b/services/tests/servicestests/src/com/android/server/timedetector/ReferenceWithHistoryTest.java
@@ -17,17 +17,19 @@
package com.android.server.timedetector;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import android.util.IndentingPrintWriter;
import androidx.test.runner.AndroidJUnit4;
-import com.android.internal.util.IndentingPrintWriter;
import com.android.server.timezonedetector.ReferenceWithHistory;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.io.StringWriter;
+import java.util.Arrays;
@RunWith(AndroidJUnit4.class)
public class ReferenceWithHistoryTest {
@@ -41,31 +43,34 @@ public class ReferenceWithHistoryTest {
// Check unset behavior.
compareGet(referenceWithHistory, reference, null);
- assertNotNull(dumpReferenceWithHistory(referenceWithHistory));
+ assertDumpContent(referenceWithHistory);
compareToString(referenceWithHistory, reference, "null");
// Try setting null.
setAndCompareReturnValue(referenceWithHistory, reference, null);
compareGet(referenceWithHistory, reference, null);
- assertNotNull(dumpReferenceWithHistory(referenceWithHistory));
+ assertDumpContent(referenceWithHistory, new DumpLine(0, "null"));
compareToString(referenceWithHistory, reference, "null");
// Try setting a non-null value.
setAndCompareReturnValue(referenceWithHistory, reference, "Foo");
compareGet(referenceWithHistory, reference, "Foo");
- assertNotNull(dumpReferenceWithHistory(referenceWithHistory));
+ assertDumpContent(referenceWithHistory,
+ new DumpLine(0, "null"), new DumpLine(1, "Foo"));
compareToString(referenceWithHistory, reference, "Foo");
// Try setting null again.
- setAndCompareReturnValue(referenceWithHistory, reference, "Foo");
- compareGet(referenceWithHistory, reference, "Foo");
- assertNotNull(dumpReferenceWithHistory(referenceWithHistory));
- compareToString(referenceWithHistory, reference, "Foo");
+ setAndCompareReturnValue(referenceWithHistory, reference, null);
+ compareGet(referenceWithHistory, reference, null);
+ assertDumpContent(referenceWithHistory,
+ new DumpLine(1, "Foo"), new DumpLine(2, "null"));
+ compareToString(referenceWithHistory, reference, "null");
// Try a non-null value again.
setAndCompareReturnValue(referenceWithHistory, reference, "Bar");
compareGet(referenceWithHistory, reference, "Bar");
- assertNotNull(dumpReferenceWithHistory(referenceWithHistory));
+ assertDumpContent(referenceWithHistory,
+ new DumpLine(2, "null"), new DumpLine(3, "Bar"));
compareToString(referenceWithHistory, reference, "Bar");
}
@@ -132,11 +137,54 @@ public class ReferenceWithHistoryTest {
assertEquals(expected, referenceWithHistory.toString());
}
- private static String dumpReferenceWithHistory(ReferenceWithHistory<?> referenceWithHistory) {
+ private static void assertDumpContent(
+ ReferenceWithHistory<?> referenceWithHistory, DumpLine... expectedLines) {
+ String[] actualLines = dumpReferenceWithHistory(referenceWithHistory);
+
+ if (expectedLines.length == 0) {
+ String expectedEmptyOutput = "{Empty}";
+ assertEquals(expectedEmptyOutput, 1, actualLines.length);
+ assertEquals(expectedEmptyOutput, actualLines[0]);
+ } else {
+ assertEquals("Expected=" + Arrays.toString(expectedLines)
+ + ", actual=" + Arrays.toString(actualLines),
+ expectedLines.length, actualLines.length);
+ for (int i = 0; i < expectedLines.length; i++) {
+ DumpLine expectedLine = expectedLines[i];
+ String actualLine = actualLines[i];
+ assertTrue("i=" + i + ", expected=" + expectedLine + ", actual=" + actualLine,
+ actualLine.startsWith(Integer.toString(expectedLine.mIndex)));
+ assertTrue("i=" + i + ", expected=" + expectedLine + ", actual=" + actualLine,
+ actualLine.endsWith(expectedLine.mLine));
+ }
+ }
+ }
+
+ private static String[] dumpReferenceWithHistory(ReferenceWithHistory<?> referenceWithHistory) {
StringWriter stringWriter = new StringWriter();
try (IndentingPrintWriter ipw = new IndentingPrintWriter(stringWriter, " ")) {
referenceWithHistory.dump(ipw);
- return stringWriter.toString();
+ return stringWriter.toString().split("\n");
+ }
+ }
+
+ /** An expected line of {@link ReferenceWithHistory#dump} output. */
+ private static class DumpLine {
+
+ final int mIndex;
+ final String mLine;
+
+ DumpLine(int index, String line) {
+ mIndex = index;
+ mLine = line;
+ }
+
+ @Override
+ public String toString() {
+ return "DumpLine{"
+ + "mIndex=" + mIndex
+ + ", mLine='" + mLine + '\''
+ + '}';
}
}
}
diff --git a/services/tests/servicestests/src/com/android/server/timezone/OWNERS b/services/tests/servicestests/src/com/android/server/timezone/OWNERS
index 8f8089717e3b..61652604ee9f 100644
--- a/services/tests/servicestests/src/com/android/server/timezone/OWNERS
+++ b/services/tests/servicestests/src/com/android/server/timezone/OWNERS
@@ -1,3 +1,2 @@
-# Bug component: 847766
-mingaleev@google.com
-include /core/java/android/app/timedetector/OWNERS
+# Bug component: 24949
+include /services/core/java/com/android/server/timezone/OWNERS
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/OWNERS b/services/tests/servicestests/src/com/android/server/timezonedetector/OWNERS
index 8f8089717e3b..a6ff1ba8a8cb 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/OWNERS
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/OWNERS
@@ -1,3 +1,2 @@
# Bug component: 847766
-mingaleev@google.com
-include /core/java/android/app/timedetector/OWNERS
+include /services/core/java/com/android/server/timezonedetector/OWNERS
diff --git a/services/tests/servicestests/src/com/android/server/wm/OWNERS b/services/tests/servicestests/src/com/android/server/wm/OWNERS
new file mode 100644
index 000000000000..361760d7f945
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/wm/OWNERS
@@ -0,0 +1 @@
+include platform/frameworks/base:/services/core/java/com/android/server/wm/OWNERS
diff --git a/services/tests/uiservicestests/Android.bp b/services/tests/uiservicestests/Android.bp
index 1ba414e33d1b..2128a08d53d2 100644
--- a/services/tests/uiservicestests/Android.bp
+++ b/services/tests/uiservicestests/Android.bp
@@ -58,7 +58,6 @@ android_test {
jni_libs: [
"libdexmakerjvmtiagent",
"libmultiplejvmtiagentsinterferenceagent",
- "libbacktrace",
"libbase",
"libbinder",
"libc++",
diff --git a/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java
index c4cccf022322..19247604ad10 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java
@@ -104,7 +104,8 @@ public class RefreshRatePolicyTest extends WindowTestsBase {
mPolicy.addNonHighRefreshRatePackage("com.android.test");
assertEquals(0, mPolicy.getPreferredModeId(cameraUsingWindow));
assertEquals(0, mPolicy.getPreferredRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE);
- assertEquals(0, mPolicy.getPreferredMinRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE);
+ assertEquals(LOW_REFRESH_RATE,
+ mPolicy.getPreferredMinRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE);
assertEquals(LOW_REFRESH_RATE,
mPolicy.getPreferredMaxRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE);
mPolicy.removeNonHighRefreshRatePackage("com.android.test");
@@ -165,7 +166,8 @@ public class RefreshRatePolicyTest extends WindowTestsBase {
assertEquals(HI_MODE_ID, mPolicy.getPreferredModeId(overrideWindow));
assertEquals(HI_REFRESH_RATE,
mPolicy.getPreferredRefreshRate(overrideWindow), FLOAT_TOLERANCE);
- assertEquals(0, mPolicy.getPreferredMinRefreshRate(overrideWindow), FLOAT_TOLERANCE);
+ assertEquals(LOW_REFRESH_RATE,
+ mPolicy.getPreferredMinRefreshRate(overrideWindow), FLOAT_TOLERANCE);
assertEquals(LOW_REFRESH_RATE,
mPolicy.getPreferredMaxRefreshRate(overrideWindow), FLOAT_TOLERANCE);
}
@@ -180,7 +182,8 @@ public class RefreshRatePolicyTest extends WindowTestsBase {
assertEquals(0, mPolicy.getPreferredModeId(overrideWindow));
assertEquals(HI_REFRESH_RATE,
mPolicy.getPreferredRefreshRate(overrideWindow), FLOAT_TOLERANCE);
- assertEquals(0, mPolicy.getPreferredMinRefreshRate(overrideWindow), FLOAT_TOLERANCE);
+ assertEquals(LOW_REFRESH_RATE,
+ mPolicy.getPreferredMinRefreshRate(overrideWindow), FLOAT_TOLERANCE);
assertEquals(LOW_REFRESH_RATE,
mPolicy.getPreferredMaxRefreshRate(overrideWindow), FLOAT_TOLERANCE);
}
@@ -257,7 +260,8 @@ public class RefreshRatePolicyTest extends WindowTestsBase {
mPolicy.addNonHighRefreshRatePackage("com.android.test");
assertEquals(0, mPolicy.getPreferredModeId(cameraUsingWindow));
assertEquals(0, mPolicy.getPreferredRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE);
- assertEquals(0, mPolicy.getPreferredMinRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE);
+ assertEquals(LOW_REFRESH_RATE,
+ mPolicy.getPreferredMinRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE);
assertEquals(LOW_REFRESH_RATE,
mPolicy.getPreferredMaxRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE);
diff --git a/services/translation/OWNERS b/services/translation/OWNERS
index a1e663aa8ff7..440f9a840057 100644
--- a/services/translation/OWNERS
+++ b/services/translation/OWNERS
@@ -1,8 +1,3 @@
# Bug component: 994311
-adamhe@google.com
-augale@google.com
-joannechung@google.com
-lpeter@google.com
-svetoslavganov@google.com
-tymtsai@google.com
+include /core/java/android/view/translation/OWNERS
diff --git a/services/usage/OWNERS b/services/usage/OWNERS
index 9daa0930e73a..6c20e746f35e 100644
--- a/services/usage/OWNERS
+++ b/services/usage/OWNERS
@@ -2,3 +2,5 @@ mwachens@google.com
varunshah@google.com
huiyu@google.com
yamasani@google.com
+
+per-file *StorageStats* = file:/core/java/android/os/storage/OWNERS \ No newline at end of file
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index 661dcbb7f489..5ada2a43ea2f 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -489,6 +489,7 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser
// current USB state
private boolean mHostConnected;
+ private boolean mUsbAccessoryConnected;
private boolean mSourcePower;
private boolean mSinkPower;
private boolean mConfigured;
@@ -762,6 +763,8 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser
// send a sticky broadcast containing USB accessory handshake information
Intent intent = new Intent(UsbManager.ACTION_USB_ACCESSORY_HANDSHAKE)
+ .addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND
+ | Intent.FLAG_RECEIVER_FOREGROUND)
.putExtra(UsbManager.EXTRA_ACCESSORY_UEVENT_TIME,
mAccessoryConnectionStartTime)
.putExtra(UsbManager.EXTRA_ACCESSORY_STRING_COUNT,
@@ -956,10 +959,10 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser
break;
case MSG_UPDATE_HOST_STATE:
Iterator devices = (Iterator) msg.obj;
- boolean connected = (msg.arg1 == 1);
+ mUsbAccessoryConnected = (msg.arg1 == 1);
if (DEBUG) {
- Slog.i(TAG, "HOST_STATE connected:" + connected);
+ Slog.i(TAG, "HOST_STATE connected:" + mUsbAccessoryConnected);
}
mHideUsbNotification = false;
@@ -1213,7 +1216,7 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser
} else if (mSourcePower) {
titleRes = com.android.internal.R.string.usb_supplying_notification_title;
id = SystemMessage.NOTE_USB_SUPPLYING;
- } else if (mHostConnected && mSinkPower && mUsbCharging) {
+ } else if (mHostConnected && mSinkPower && (mUsbCharging || mUsbAccessoryConnected)) {
titleRes = com.android.internal.R.string.usb_charging_notification_title;
id = SystemMessage.NOTE_USB_CHARGING;
}
diff --git a/services/wallpapereffectsgeneration/OWNERS b/services/wallpapereffectsgeneration/OWNERS
new file mode 100644
index 000000000000..d2d3e2c0a7b6
--- /dev/null
+++ b/services/wallpapereffectsgeneration/OWNERS
@@ -0,0 +1,4 @@
+susharon@google.com
+shanh@google.com
+huiwu@google.com
+srazdan@google.com