Merge "Fix backward compatibility when there's no secureElementName"
diff --git a/Android.bp b/Android.bp
index 9cc17ea..1d5e858 100644
--- a/Android.bp
+++ b/Android.bp
@@ -25,652 +25,224 @@
//
// READ ME: ########################################################
-java_defaults {
- name: "framework-defaults",
- installable: true,
-
+filegroup {
+ name: "framework-core-sources",
srcs: [
- // From build/make/core/pathmap.mk FRAMEWORK_BASE_SUBDIRS
"core/java/**/*.java",
- "graphics/java/**/*.java",
- "location/java/**/*.java",
- "lowpan/java/**/*.java",
- "media/java/**/*.java",
- "media/mca/effect/java/**/*.java",
- "media/mca/filterfw/java/**/*.java",
- "media/mca/filterpacks/java/**/*.java",
- "drm/java/**/*.java",
- "opengl/java/**/*.java",
- "sax/java/**/*.java",
- "telecomm/java/**/*.java",
- "telephony/java/**/*.java",
- "wifi/java/**/*.java",
- "keystore/java/**/*.java",
- "rs/java/**/*.java",
-
- ":framework-javastream-protos",
-
- "core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl",
- "core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl",
- "core/java/android/accounts/IAccountManager.aidl",
- "core/java/android/accounts/IAccountManagerResponse.aidl",
- "core/java/android/accounts/IAccountAuthenticator.aidl",
- "core/java/android/accounts/IAccountAuthenticatorResponse.aidl",
- "core/java/android/app/IActivityController.aidl",
- "core/java/android/app/IActivityManager.aidl",
- "core/java/android/app/IActivityPendingResult.aidl",
- "core/java/android/app/IAlarmCompleteListener.aidl",
- "core/java/android/app/IAlarmListener.aidl",
- "core/java/android/app/IAlarmManager.aidl",
- "core/java/android/app/IAppTask.aidl",
- "core/java/android/app/IApplicationThread.aidl",
- "core/java/android/app/IAssistDataReceiver.aidl",
- "core/java/android/app/ITaskStackListener.aidl",
- "core/java/android/app/IBackupAgent.aidl",
- "core/java/android/app/IEphemeralResolver.aidl",
- "core/java/android/app/IInputForwarder.aidl",
- "core/java/android/app/IInstantAppResolver.aidl",
- "core/java/android/app/IInstrumentationWatcher.aidl",
- "core/java/android/app/INotificationManager.aidl",
- "core/java/android/app/IProcessObserver.aidl",
- "core/java/android/app/ISearchManager.aidl",
- "core/java/android/app/ISearchManagerCallback.aidl",
- "core/java/android/app/IServiceConnection.aidl",
- "core/java/android/app/IStopUserCallback.aidl",
- "core/java/android/app/job/IJobCallback.aidl",
- "core/java/android/app/job/IJobScheduler.aidl",
- "core/java/android/app/job/IJobService.aidl",
- "core/java/android/app/ITransientNotification.aidl",
- "core/java/android/app/IUidObserver.aidl",
- "core/java/android/app/IUiAutomationConnection.aidl",
- "core/java/android/app/IUiModeManager.aidl",
- "core/java/android/app/IUserSwitchObserver.aidl",
- "core/java/android/app/IWallpaperManager.aidl",
- "core/java/android/app/IWallpaperManagerCallback.aidl",
- "core/java/android/app/admin/IDeviceAdminService.aidl",
- "core/java/android/app/admin/IDevicePolicyManager.aidl",
- "core/java/android/app/trust/IStrongAuthTracker.aidl",
- "core/java/android/app/trust/ITrustManager.aidl",
- "core/java/android/app/trust/ITrustListener.aidl",
- "core/java/android/app/backup/IBackupManager.aidl",
- "core/java/android/app/backup/IBackupObserver.aidl",
- "core/java/android/app/backup/IBackupManagerMonitor.aidl",
- "core/java/android/app/backup/IFullBackupRestoreObserver.aidl",
- "core/java/android/app/backup/IRestoreObserver.aidl",
- "core/java/android/app/backup/IRestoreSession.aidl",
- "core/java/android/app/backup/ISelectBackupTransportCallback.aidl",
- "core/java/android/app/slice/ISliceManager.aidl",
- "core/java/android/app/slice/ISliceListener.aidl",
- "core/java/android/app/timedetector/ITimeDetectorService.aidl",
- "core/java/android/app/timezone/ICallback.aidl",
- "core/java/android/app/timezone/IRulesManager.aidl",
- "core/java/android/app/usage/ICacheQuotaService.aidl",
- "core/java/android/app/usage/IStorageStatsManager.aidl",
- "core/java/android/app/usage/IUsageStatsManager.aidl",
- ":libbluetooth-binder-aidl",
- "core/java/android/content/IClipboard.aidl",
- "core/java/android/content/IContentService.aidl",
- "core/java/android/content/IIntentReceiver.aidl",
- "core/java/android/content/IIntentSender.aidl",
- "core/java/android/content/IOnPrimaryClipChangedListener.aidl",
- "core/java/android/content/IRestrictionsManager.aidl",
- "core/java/android/content/ISyncAdapter.aidl",
- "core/java/android/content/ISyncAdapterUnsyncableAccountCallback.aidl",
- "core/java/android/content/ISyncContext.aidl",
- "core/java/android/content/ISyncServiceAdapter.aidl",
- "core/java/android/content/ISyncStatusObserver.aidl",
- "core/java/android/content/om/IOverlayManager.aidl",
- "core/java/android/content/pm/ICrossProfileApps.aidl",
- "core/java/android/content/pm/IDexModuleRegisterCallback.aidl",
- "core/java/android/content/pm/ILauncherApps.aidl",
- "core/java/android/content/pm/IOnAppsChangedListener.aidl",
- "core/java/android/content/pm/IOnPermissionsChangeListener.aidl",
- "core/java/android/content/pm/IOtaDexopt.aidl",
- "core/java/android/content/pm/IPackageDataObserver.aidl",
- "core/java/android/content/pm/IPackageDeleteObserver.aidl",
- "core/java/android/content/pm/IPackageDeleteObserver2.aidl",
- "core/java/android/content/pm/IPackageInstallObserver2.aidl",
- "core/java/android/content/pm/IPackageInstaller.aidl",
- "core/java/android/content/pm/IPackageInstallerCallback.aidl",
- "core/java/android/content/pm/IPackageInstallerSession.aidl",
- "core/java/android/content/pm/IPackageManager.aidl",
- ":libbinder_aidl",
- "core/java/android/content/pm/IPackageMoveObserver.aidl",
- "core/java/android/content/pm/IPackageStatsObserver.aidl",
- "core/java/android/content/pm/IPinItemRequest.aidl",
- "core/java/android/content/pm/IShortcutService.aidl",
- "core/java/android/content/pm/dex/IArtManager.aidl",
- "core/java/android/content/pm/dex/ISnapshotRuntimeProfileCallback.aidl",
- "core/java/android/content/pm/permission/IRuntimePermissionPresenter.aidl",
- "core/java/android/database/IContentObserver.aidl",
- ":libcamera_client_aidl",
- ":libcamera_client_framework_aidl",
- "core/java/android/hardware/IConsumerIrService.aidl",
- "core/java/android/hardware/ISerialManager.aidl",
- "core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl",
- "core/java/android/hardware/display/IDisplayManager.aidl",
- "core/java/android/hardware/display/IDisplayManagerCallback.aidl",
- "core/java/android/hardware/display/IVirtualDisplayCallback.aidl",
- "core/java/android/hardware/fingerprint/IFingerprintClientActiveCallback.aidl",
- "core/java/android/hardware/fingerprint/IFingerprintService.aidl",
- "core/java/android/hardware/fingerprint/IFingerprintServiceLockoutResetCallback.aidl",
- "core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl",
- "core/java/android/hardware/hdmi/IHdmiControlCallback.aidl",
- "core/java/android/hardware/hdmi/IHdmiControlService.aidl",
- "core/java/android/hardware/hdmi/IHdmiDeviceEventListener.aidl",
- "core/java/android/hardware/hdmi/IHdmiHotplugEventListener.aidl",
- "core/java/android/hardware/hdmi/IHdmiInputChangeListener.aidl",
- "core/java/android/hardware/hdmi/IHdmiMhlVendorCommandListener.aidl",
- "core/java/android/hardware/hdmi/IHdmiRecordListener.aidl",
- "core/java/android/hardware/hdmi/IHdmiSystemAudioModeChangeListener.aidl",
- "core/java/android/hardware/hdmi/IHdmiVendorCommandListener.aidl",
- "core/java/android/hardware/input/IInputManager.aidl",
- "core/java/android/hardware/input/IInputDevicesChangedListener.aidl",
- "core/java/android/hardware/input/ITabletModeChangedListener.aidl",
- "core/java/android/hardware/location/IActivityRecognitionHardware.aidl",
- "core/java/android/hardware/location/IActivityRecognitionHardwareClient.aidl",
- "core/java/android/hardware/location/IActivityRecognitionHardwareSink.aidl",
- "core/java/android/hardware/location/IActivityRecognitionHardwareWatcher.aidl",
- "core/java/android/hardware/location/IGeofenceHardware.aidl",
- "core/java/android/hardware/location/IGeofenceHardwareCallback.aidl",
- "core/java/android/hardware/location/IGeofenceHardwareMonitorCallback.aidl",
- "core/java/android/hardware/location/IContextHubCallback.aidl",
- "core/java/android/hardware/location/IContextHubClient.aidl",
- "core/java/android/hardware/location/IContextHubClientCallback.aidl",
- "core/java/android/hardware/location/IContextHubService.aidl",
- "core/java/android/hardware/location/IContextHubTransactionCallback.aidl",
- "core/java/android/hardware/radio/IAnnouncementListener.aidl",
- "core/java/android/hardware/radio/ICloseHandle.aidl",
- "core/java/android/hardware/radio/IRadioService.aidl",
- "core/java/android/hardware/radio/ITuner.aidl",
- "core/java/android/hardware/radio/ITunerCallback.aidl",
- "core/java/android/hardware/soundtrigger/IRecognitionStatusCallback.aidl",
- "core/java/android/hardware/usb/IUsbManager.aidl",
- "core/java/android/net/ICaptivePortal.aidl",
- "core/java/android/net/IConnectivityManager.aidl",
- "core/java/android/net/IIpConnectivityMetrics.aidl",
- "core/java/android/net/IEthernetManager.aidl",
- "core/java/android/net/IEthernetServiceListener.aidl",
- "core/java/android/net/INetdEventCallback.aidl",
- "core/java/android/net/IIpSecService.aidl",
- "core/java/android/net/INetworkManagementEventObserver.aidl",
- "core/java/android/net/INetworkPolicyListener.aidl",
- "core/java/android/net/INetworkPolicyManager.aidl",
- "core/java/android/net/INetworkRecommendationProvider.aidl",
- "core/java/android/net/INetworkScoreCache.aidl",
- "core/java/android/net/INetworkScoreService.aidl",
- "core/java/android/net/INetworkStatsService.aidl",
- "core/java/android/net/INetworkStatsSession.aidl",
- "core/java/android/net/ISocketKeepaliveCallback.aidl",
- "core/java/android/net/ITestNetworkManager.aidl",
- "core/java/android/net/ITetheringEventCallback.aidl",
- "core/java/android/net/ITetheringStatsProvider.aidl",
- "core/java/android/net/nsd/INsdManager.aidl",
- "core/java/android/nfc/IAppCallback.aidl",
- "core/java/android/nfc/INfcAdapter.aidl",
- "core/java/android/nfc/INfcAdapterExtras.aidl",
- "core/java/android/nfc/INfcTag.aidl",
- "core/java/android/nfc/INfcCardEmulation.aidl",
- "core/java/android/nfc/INfcFCardEmulation.aidl",
- "core/java/android/nfc/INfcUnlockHandler.aidl",
- "core/java/android/nfc/INfcDta.aidl",
- "core/java/android/nfc/ITagRemovedCallback.aidl",
- "core/java/android/se/omapi/ISecureElementService.aidl",
- "core/java/android/se/omapi/ISecureElementListener.aidl",
- "core/java/android/se/omapi/ISecureElementChannel.aidl",
- "core/java/android/se/omapi/ISecureElementReader.aidl",
- "core/java/android/se/omapi/ISecureElementSession.aidl",
- "core/java/android/os/IBatteryPropertiesRegistrar.aidl",
- "core/java/android/os/ICancellationSignal.aidl",
- "core/java/android/os/IDeviceIdentifiersPolicyService.aidl",
- "core/java/android/os/IDeviceIdleController.aidl",
- "core/java/android/os/IDynamicAndroidService.aidl",
- "core/java/android/os/IHardwarePropertiesManager.aidl",
- ":libincident_aidl",
- "core/java/android/os/IMaintenanceActivityListener.aidl",
- "core/java/android/os/IMessenger.aidl",
- "core/java/android/os/INetworkActivityListener.aidl",
- "core/java/android/os/INetworkManagementService.aidl",
- "core/java/android/os/IPermissionController.aidl",
- "core/java/android/os/IProcessInfoService.aidl",
- "core/java/android/os/IProgressListener.aidl",
- "core/java/android/os/IPowerManager.aidl",
- "core/java/android/os/IRecoverySystem.aidl",
- "core/java/android/os/IRecoverySystemProgressListener.aidl",
- "core/java/android/os/IRemoteCallback.aidl",
- "core/java/android/os/ISchedulingPolicyService.aidl",
- ":statsd_aidl",
- "core/java/android/os/ISystemUpdateManager.aidl",
- "core/java/android/os/IThermalEventListener.aidl",
- "core/java/android/os/IThermalService.aidl",
- "core/java/android/os/IUpdateLock.aidl",
- "core/java/android/os/IUserManager.aidl",
- "core/java/android/os/IVibratorService.aidl",
- "core/java/android/os/storage/IStorageManager.aidl",
- "core/java/android/os/storage/IStorageEventListener.aidl",
- "core/java/android/os/storage/IStorageShutdownObserver.aidl",
- "core/java/android/os/storage/IObbActionListener.aidl",
- ":keystore_aidl",
- "core/java/android/security/keymaster/IKeyAttestationApplicationIdProvider.aidl",
- "core/java/android/service/autofill/IAutoFillService.aidl",
- "core/java/android/service/autofill/IAutofillFieldClassificationService.aidl",
- "core/java/android/service/autofill/IFillCallback.aidl",
- "core/java/android/service/autofill/ISaveCallback.aidl",
- "core/java/android/service/carrier/ICarrierService.aidl",
- "core/java/android/service/carrier/ICarrierMessagingCallback.aidl",
- "core/java/android/service/carrier/ICarrierMessagingService.aidl",
- "core/java/android/service/euicc/IDeleteSubscriptionCallback.aidl",
- "core/java/android/service/euicc/IDownloadSubscriptionCallback.aidl",
- "core/java/android/service/euicc/IEraseSubscriptionsCallback.aidl",
- "core/java/android/service/euicc/IEuiccService.aidl",
- "core/java/android/service/euicc/IGetDefaultDownloadableSubscriptionListCallback.aidl",
- "core/java/android/service/euicc/IGetDownloadableSubscriptionMetadataCallback.aidl",
- "core/java/android/service/euicc/IGetEidCallback.aidl",
- "core/java/android/service/euicc/IGetEuiccInfoCallback.aidl",
- "core/java/android/service/euicc/IGetEuiccProfileInfoListCallback.aidl",
- "core/java/android/service/euicc/IGetOtaStatusCallback.aidl",
- "core/java/android/service/euicc/IOtaStatusChangedCallback.aidl",
- "core/java/android/service/euicc/IRetainSubscriptionsForFactoryResetCallback.aidl",
- "core/java/android/service/euicc/ISwitchToSubscriptionCallback.aidl",
- "core/java/android/service/euicc/IUpdateSubscriptionNicknameCallback.aidl",
- ":gatekeeper_aidl",
- "core/java/android/service/notification/INotificationListener.aidl",
- "core/java/android/service/notification/IStatusBarNotificationHolder.aidl",
- "core/java/android/service/notification/IConditionListener.aidl",
- "core/java/android/service/notification/IConditionProvider.aidl",
- "core/java/android/service/settings/suggestions/ISuggestionService.aidl",
- "core/java/android/service/vr/IPersistentVrStateCallbacks.aidl",
- "core/java/android/service/vr/IVrListener.aidl",
- "core/java/android/service/vr/IVrManager.aidl",
- "core/java/android/service/vr/IVrStateCallbacks.aidl",
- "core/java/android/print/ILayoutResultCallback.aidl",
- "core/java/android/print/IPrinterDiscoveryObserver.aidl",
- "core/java/android/print/IPrintDocumentAdapter.aidl",
- "core/java/android/print/IPrintDocumentAdapterObserver.aidl",
- "core/java/android/print/IPrintJobStateChangeListener.aidl",
- "core/java/android/print/IPrintServicesChangeListener.aidl",
- "core/java/android/printservice/recommendation/IRecommendationsChangeListener.aidl",
- "core/java/android/print/IPrintManager.aidl",
- "core/java/android/print/IPrintSpooler.aidl",
- "core/java/android/print/IPrintSpoolerCallbacks.aidl",
- "core/java/android/print/IPrintSpoolerClient.aidl",
- "core/java/android/printservice/recommendation/IRecommendationServiceCallbacks.aidl",
- "core/java/android/printservice/recommendation/IRecommendationService.aidl",
- "core/java/android/print/IWriteResultCallback.aidl",
- "core/java/android/printservice/IPrintService.aidl",
- "core/java/android/printservice/IPrintServiceClient.aidl",
- "core/java/android/companion/ICompanionDeviceManager.aidl",
- "core/java/android/companion/ICompanionDeviceDiscoveryService.aidl",
- "core/java/android/companion/ICompanionDeviceDiscoveryServiceCallback.aidl",
- "core/java/android/companion/IFindDeviceCallback.aidl",
- "core/java/android/service/dreams/IDreamManager.aidl",
- "core/java/android/service/dreams/IDreamService.aidl",
- "core/java/android/service/oemlock/IOemLockService.aidl",
- "core/java/android/service/persistentdata/IPersistentDataBlockService.aidl",
- "core/java/android/service/trust/ITrustAgentService.aidl",
- "core/java/android/service/trust/ITrustAgentServiceCallback.aidl",
- "core/java/android/service/voice/IVoiceInteractionService.aidl",
- "core/java/android/service/voice/IVoiceInteractionSession.aidl",
- "core/java/android/service/voice/IVoiceInteractionSessionService.aidl",
- "core/java/android/service/wallpaper/IWallpaperConnection.aidl",
- "core/java/android/service/wallpaper/IWallpaperEngine.aidl",
- "core/java/android/service/wallpaper/IWallpaperService.aidl",
- "core/java/android/service/chooser/IChooserTargetService.aidl",
- "core/java/android/service/chooser/IChooserTargetResult.aidl",
- "core/java/android/service/resolver/IResolverRankerService.aidl",
- "core/java/android/service/resolver/IResolverRankerResult.aidl",
- "core/java/android/service/textclassifier/ITextClassificationCallback.aidl",
- "core/java/android/service/textclassifier/ITextClassifierService.aidl",
- "core/java/android/service/textclassifier/ITextLinksCallback.aidl",
- "core/java/android/service/textclassifier/ITextSelectionCallback.aidl",
- "core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl",
- "core/java/android/view/accessibility/IAccessibilityInteractionConnectionCallback.aidl",
- "core/java/android/view/accessibility/IAccessibilityManager.aidl",
- "core/java/android/view/accessibility/IAccessibilityManagerClient.aidl",
- "core/java/android/view/autofill/IAutoFillManager.aidl",
- "core/java/android/view/autofill/IAutoFillManagerClient.aidl",
- "core/java/android/view/autofill/IAutofillWindowPresenter.aidl",
- "core/java/android/view/IApplicationToken.aidl",
- "core/java/android/view/IAppTransitionAnimationSpecsFuture.aidl",
- "core/java/android/view/IDockedStackListener.aidl",
- "core/java/android/view/IGraphicsStats.aidl",
- "core/java/android/view/IGraphicsStatsCallback.aidl",
- "core/java/android/view/IInputFilter.aidl",
- "core/java/android/view/IInputFilterHost.aidl",
- "core/java/android/view/IOnKeyguardExitResult.aidl",
- "core/java/android/view/IPinnedStackController.aidl",
- "core/java/android/view/IPinnedStackListener.aidl",
- "core/java/android/view/IRemoteAnimationRunner.aidl",
- "core/java/android/view/IRecentsAnimationController.aidl",
- "core/java/android/view/IRecentsAnimationRunner.aidl",
- "core/java/android/view/IRemoteAnimationFinishedCallback.aidl",
- "core/java/android/view/IRotationWatcher.aidl",
- "core/java/android/view/IWallpaperVisibilityListener.aidl",
- "core/java/android/view/IWindow.aidl",
- "core/java/android/view/IWindowFocusObserver.aidl",
- "core/java/android/view/IWindowId.aidl",
- "core/java/android/view/IWindowManager.aidl",
- "core/java/android/view/IWindowSession.aidl",
- "core/java/android/view/IWindowSessionCallback.aidl",
- "core/java/android/webkit/IWebViewUpdateService.aidl",
- "core/java/android/speech/IRecognitionListener.aidl",
- "core/java/android/speech/IRecognitionService.aidl",
- "core/java/android/speech/tts/ITextToSpeechCallback.aidl",
- "core/java/android/speech/tts/ITextToSpeechService.aidl",
- "core/java/com/android/internal/app/IAppOpsActiveCallback.aidl",
- "core/java/com/android/internal/app/IAppOpsCallback.aidl",
- "core/java/com/android/internal/app/IAppOpsService.aidl",
- "core/java/com/android/internal/app/IBatteryStats.aidl",
- "core/java/com/android/internal/app/ISoundTriggerService.aidl",
- "core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl",
- "core/java/com/android/internal/app/IVoiceInteractionSessionListener.aidl",
- "core/java/com/android/internal/app/IVoiceInteractionSessionShowCallback.aidl",
- "core/java/com/android/internal/app/IVoiceInteractor.aidl",
- "core/java/com/android/internal/app/IVoiceInteractorCallback.aidl",
- "core/java/com/android/internal/app/IVoiceInteractorRequest.aidl",
- "core/java/com/android/internal/app/IMediaContainerService.aidl",
- "core/java/com/android/internal/app/procstats/IProcessStats.aidl",
- "core/java/com/android/internal/appwidget/IAppWidgetService.aidl",
- "core/java/com/android/internal/appwidget/IAppWidgetHost.aidl",
- "core/java/com/android/internal/backup/IBackupTransport.aidl",
- "core/java/com/android/internal/backup/IObbBackupService.aidl",
- "core/java/com/android/internal/inputmethod/IInputContentUriToken.aidl",
- "core/java/com/android/internal/net/INetworkWatchlistManager.aidl",
- "core/java/com/android/internal/policy/IKeyguardDrawnCallback.aidl",
- "core/java/com/android/internal/policy/IKeyguardDismissCallback.aidl",
- "core/java/com/android/internal/policy/IKeyguardExitCallback.aidl",
- "core/java/com/android/internal/policy/IKeyguardService.aidl",
- "core/java/com/android/internal/policy/IKeyguardStateCallback.aidl",
- "core/java/com/android/internal/policy/IShortcutService.aidl",
- "core/java/com/android/internal/os/IDropBoxManagerService.aidl",
- "core/java/com/android/internal/os/IParcelFileDescriptorFactory.aidl",
- "core/java/com/android/internal/os/IResultReceiver.aidl",
- "core/java/com/android/internal/os/IShellCallback.aidl",
- "core/java/com/android/internal/statusbar/IStatusBar.aidl",
- "core/java/com/android/internal/statusbar/IStatusBarService.aidl",
- "core/java/com/android/internal/textservice/ISpellCheckerService.aidl",
- "core/java/com/android/internal/textservice/ISpellCheckerServiceCallback.aidl",
- "core/java/com/android/internal/textservice/ISpellCheckerSession.aidl",
- "core/java/com/android/internal/textservice/ISpellCheckerSessionListener.aidl",
- "core/java/com/android/internal/textservice/ITextServicesManager.aidl",
- "core/java/com/android/internal/textservice/ITextServicesSessionListener.aidl",
- "core/java/com/android/internal/view/IDragAndDropPermissions.aidl",
- "core/java/com/android/internal/view/IInputContext.aidl",
- "core/java/com/android/internal/view/IInputContextCallback.aidl",
- "core/java/com/android/internal/view/IInputMethod.aidl",
- "core/java/com/android/internal/view/IInputMethodClient.aidl",
- "core/java/com/android/internal/view/IInputMethodManager.aidl",
- "core/java/com/android/internal/view/IInputMethodSession.aidl",
- "core/java/com/android/internal/view/IInputSessionCallback.aidl",
- "core/java/com/android/internal/widget/ICheckCredentialProgressCallback.aidl",
- "core/java/com/android/internal/widget/ILockSettings.aidl",
- "core/java/com/android/internal/widget/IRemoteViewsFactory.aidl",
- "keystore/java/android/security/IKeyChainAliasCallback.aidl",
- "keystore/java/android/security/IKeyChainService.aidl",
- "location/java/android/location/IBatchedLocationCallback.aidl",
- "location/java/android/location/ICountryDetector.aidl",
- "location/java/android/location/ICountryListener.aidl",
- "location/java/android/location/IGeocodeProvider.aidl",
- "location/java/android/location/IGeofenceProvider.aidl",
- "location/java/android/location/IGnssStatusListener.aidl",
- "location/java/android/location/IGnssStatusProvider.aidl",
- "location/java/android/location/IGnssMeasurementsListener.aidl",
- "location/java/android/location/IGnssNavigationMessageListener.aidl",
- "location/java/android/location/ILocationListener.aidl",
- "location/java/android/location/ILocationManager.aidl",
- "location/java/android/location/IFusedGeofenceHardware.aidl",
- "location/java/android/location/IGpsGeofenceHardware.aidl",
- "location/java/android/location/INetInitiatedListener.aidl",
- "location/java/com/android/internal/location/ILocationProvider.aidl",
- "media/java/android/media/IAudioFocusDispatcher.aidl",
- "media/java/android/media/IAudioRoutesObserver.aidl",
- "media/java/android/media/IAudioService.aidl",
- "media/java/android/media/IAudioServerStateDispatcher.aidl",
- "media/java/android/media/IMediaHTTPConnection.aidl",
- "media/java/android/media/IMediaHTTPService.aidl",
- "media/java/android/media/IMediaResourceMonitor.aidl",
- "media/java/android/media/IMediaRouterClient.aidl",
- "media/java/android/media/IMediaRouterService.aidl",
- "media/java/android/media/IMediaScannerListener.aidl",
- "media/java/android/media/IMediaScannerService.aidl",
- "media/java/android/media/IPlaybackConfigDispatcher.aidl",
- "media/java/android/media/ISessionTokensListener.aidl",
- ":libaudioclient_aidl",
- "media/java/android/media/IRecordingConfigDispatcher.aidl",
- "media/java/android/media/IRemoteDisplayCallback.aidl",
- "media/java/android/media/IRemoteDisplayProvider.aidl",
- "media/java/android/media/IRemoteVolumeController.aidl",
- "media/java/android/media/IRemoteVolumeObserver.aidl",
- "media/java/android/media/IRingtonePlayer.aidl",
- "media/java/android/media/IVolumeController.aidl",
- "media/java/android/media/audiopolicy/IAudioPolicyCallback.aidl",
- "media/java/android/media/midi/IBluetoothMidiService.aidl",
- "media/java/android/media/midi/IMidiDeviceListener.aidl",
- "media/java/android/media/midi/IMidiDeviceOpenCallback.aidl",
- "media/java/android/media/midi/IMidiDeviceServer.aidl",
- "media/java/android/media/midi/IMidiManager.aidl",
- "media/java/android/media/projection/IMediaProjection.aidl",
- "media/java/android/media/projection/IMediaProjectionCallback.aidl",
- "media/java/android/media/projection/IMediaProjectionManager.aidl",
- "media/java/android/media/projection/IMediaProjectionWatcherCallback.aidl",
- "media/java/android/media/session/IActiveSessionsListener.aidl",
- "media/java/android/media/session/ICallback.aidl",
- "media/java/android/media/session/IOnMediaKeyListener.aidl",
- "media/java/android/media/session/IOnVolumeKeyLongPressListener.aidl",
- "media/java/android/media/session/ISession.aidl",
- "media/java/android/media/session/ISessionCallback.aidl",
- "media/java/android/media/session/ISessionController.aidl",
- "media/java/android/media/session/ISessionControllerCallback.aidl",
- "media/java/android/media/session/ISessionManager.aidl",
- "media/java/android/media/soundtrigger/ISoundTriggerDetectionService.aidl",
- "media/java/android/media/soundtrigger/ISoundTriggerDetectionServiceClient.aidl",
- "media/java/android/media/tv/ITvInputClient.aidl",
- "media/java/android/media/tv/ITvInputHardware.aidl",
- "media/java/android/media/tv/ITvInputHardwareCallback.aidl",
- "media/java/android/media/tv/ITvInputManager.aidl",
- "media/java/android/media/tv/ITvInputManagerCallback.aidl",
- "media/java/android/media/tv/ITvInputService.aidl",
- "media/java/android/media/tv/ITvInputServiceCallback.aidl",
- "media/java/android/media/tv/ITvInputSession.aidl",
- "media/java/android/media/tv/ITvInputSessionCallback.aidl",
- "media/java/android/media/tv/ITvRemoteProvider.aidl",
- "media/java/android/media/tv/ITvRemoteServiceInput.aidl",
- "media/java/android/service/media/IMediaBrowserService.aidl",
- "media/java/android/service/media/IMediaBrowserServiceCallbacks.aidl",
- "telecomm/java/com/android/internal/telecom/ICallRedirectionAdapter.aidl",
- "telecomm/java/com/android/internal/telecom/ICallRedirectionService.aidl",
- "telecomm/java/com/android/internal/telecom/ICallScreeningAdapter.aidl",
- "telecomm/java/com/android/internal/telecom/ICallScreeningService.aidl",
- "telecomm/java/com/android/internal/telecom/IVideoCallback.aidl",
- "telecomm/java/com/android/internal/telecom/IVideoProvider.aidl",
- "telecomm/java/com/android/internal/telecom/IConnectionService.aidl",
- "telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl",
- "telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl",
- "telecomm/java/com/android/internal/telecom/IInCallService.aidl",
- "telecomm/java/com/android/internal/telecom/IPhoneAccountSuggestionCallback.aidl",
- "telecomm/java/com/android/internal/telecom/IPhoneAccountSuggestionService.aidl",
- "telecomm/java/com/android/internal/telecom/ITelecomService.aidl",
- "telecomm/java/com/android/internal/telecom/RemoteServiceCallback.aidl",
- "telephony/java/android/telephony/data/IDataService.aidl",
- "telephony/java/android/telephony/data/IDataServiceCallback.aidl",
- "telephony/java/android/telephony/data/IQualifiedNetworksService.aidl",
- "telephony/java/android/telephony/data/IQualifiedNetworksServiceCallback.aidl",
- "telephony/java/android/telephony/ims/aidl/IImsCallSessionListener.aidl",
- "telephony/java/android/telephony/ims/aidl/IImsCapabilityCallback.aidl",
- "telephony/java/android/telephony/ims/aidl/IImsConfig.aidl",
- "telephony/java/android/telephony/ims/aidl/IImsConfigCallback.aidl",
- "telephony/java/android/telephony/ims/aidl/IImsMmTelFeature.aidl",
- "telephony/java/android/telephony/ims/aidl/IImsMmTelListener.aidl",
- "telephony/java/android/telephony/ims/aidl/IImsRegistration.aidl",
- "telephony/java/android/telephony/ims/aidl/IImsRegistrationCallback.aidl",
- "telephony/java/android/telephony/ims/aidl/IImsRcsFeature.aidl",
- "telephony/java/android/telephony/ims/aidl/IImsServiceController.aidl",
- "telephony/java/android/telephony/ims/aidl/IImsServiceControllerListener.aidl",
- "telephony/java/android/telephony/ims/aidl/IImsSmsListener.aidl",
- "telephony/java/android/telephony/ims/aidl/IRcsMessage.aidl",
- "telephony/java/android/telephony/mbms/IMbmsDownloadSessionCallback.aidl",
- "telephony/java/android/telephony/mbms/IMbmsStreamingSessionCallback.aidl",
- "telephony/java/android/telephony/mbms/IMbmsGroupCallSessionCallback.aidl",
- "telephony/java/android/telephony/mbms/IDownloadStatusListener.aidl",
- "telephony/java/android/telephony/mbms/IDownloadProgressListener.aidl",
- "telephony/java/android/telephony/mbms/IStreamingServiceCallback.aidl",
- "telephony/java/android/telephony/mbms/IGroupCallCallback.aidl",
- "telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl",
- "telephony/java/android/telephony/mbms/vendor/IMbmsStreamingService.aidl",
- "telephony/java/android/telephony/mbms/vendor/IMbmsGroupCallService.aidl",
- "telephony/java/android/telephony/ICellInfoCallback.aidl",
- "telephony/java/android/telephony/INetworkService.aidl",
- "telephony/java/android/telephony/INetworkServiceCallback.aidl",
- "telephony/java/com/android/ims/internal/IImsCallSession.aidl",
- "telephony/java/com/android/ims/internal/IImsCallSessionListener.aidl",
- "telephony/java/com/android/ims/internal/IImsConfig.aidl",
- "telephony/java/com/android/ims/internal/IImsRegistrationListener.aidl",
- "telephony/java/com/android/ims/internal/IImsEcbm.aidl",
- "telephony/java/com/android/ims/internal/IImsEcbmListener.aidl",
- "telephony/java/com/android/ims/internal/IImsExternalCallStateListener.aidl",
- "telephony/java/com/android/ims/internal/IImsFeatureStatusCallback.aidl",
- "telephony/java/com/android/ims/internal/IImsMMTelFeature.aidl",
- "telephony/java/com/android/ims/internal/IImsMultiEndpoint.aidl",
- "telephony/java/com/android/ims/internal/IImsRcsFeature.aidl",
- "telephony/java/com/android/ims/internal/IImsService.aidl",
- "telephony/java/com/android/ims/internal/IImsServiceController.aidl",
- "telephony/java/com/android/ims/internal/IImsServiceFeatureCallback.aidl",
- "telephony/java/com/android/ims/internal/IImsStreamMediaSession.aidl",
- "telephony/java/com/android/ims/internal/IImsUt.aidl",
- "telephony/java/com/android/ims/internal/IImsUtListener.aidl",
- "telephony/java/com/android/ims/internal/IImsVideoCallCallback.aidl",
- "telephony/java/com/android/ims/internal/IImsVideoCallProvider.aidl",
- "telephony/java/com/android/ims/internal/uce/uceservice/IUceService.aidl",
- "telephony/java/com/android/ims/internal/uce/uceservice/IUceListener.aidl",
- "telephony/java/com/android/ims/internal/uce/options/IOptionsService.aidl",
- "telephony/java/com/android/ims/internal/uce/options/IOptionsListener.aidl",
- "telephony/java/com/android/ims/internal/uce/presence/IPresenceService.aidl",
- "telephony/java/com/android/ims/internal/uce/presence/IPresenceListener.aidl",
- "telephony/java/com/android/ims/ImsConfigListener.aidl",
- "telephony/java/com/android/internal/telephony/IApnSourceService.aidl",
- "telephony/java/com/android/internal/telephony/ICarrierConfigLoader.aidl",
- "telephony/java/com/android/internal/telephony/IIntegerConsumer.aidl",
- "telephony/java/com/android/internal/telephony/IMms.aidl",
- "telephony/java/com/android/internal/telephony/INumberVerificationCallback.aidl",
- "telephony/java/com/android/internal/telephony/IOnSubscriptionsChangedListener.aidl",
- "telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl",
- "telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl",
- "telephony/java/com/android/internal/telephony/ISetOpportunisticDataCallback.aidl",
- "telephony/java/com/android/internal/telephony/ISms.aidl",
- "telephony/java/com/android/internal/telephony/ISub.aidl",
- "telephony/java/com/android/internal/telephony/IOns.aidl",
- "telephony/java/com/android/internal/telephony/ITelephony.aidl",
- "telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl",
- "telephony/java/com/android/internal/telephony/IUpdateAvailableNetworksCallback.aidl",
- "telephony/java/com/android/internal/telephony/IWapPushManager.aidl",
- "telephony/java/com/android/internal/telephony/euicc/IAuthenticateServerCallback.aidl",
- "telephony/java/com/android/internal/telephony/euicc/ICancelSessionCallback.aidl",
- "telephony/java/com/android/internal/telephony/euicc/IDeleteProfileCallback.aidl",
- "telephony/java/com/android/internal/telephony/euicc/IDisableProfileCallback.aidl",
- "telephony/java/com/android/internal/telephony/euicc/IEuiccCardController.aidl",
- "telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl",
- "telephony/java/com/android/internal/telephony/euicc/IGetAllProfilesCallback.aidl",
- "telephony/java/com/android/internal/telephony/euicc/IGetDefaultSmdpAddressCallback.aidl",
- "telephony/java/com/android/internal/telephony/euicc/IGetEuiccChallengeCallback.aidl",
- "telephony/java/com/android/internal/telephony/euicc/IGetEuiccInfo1Callback.aidl",
- "telephony/java/com/android/internal/telephony/euicc/IGetEuiccInfo2Callback.aidl",
- "telephony/java/com/android/internal/telephony/euicc/IGetProfileCallback.aidl",
- "telephony/java/com/android/internal/telephony/euicc/IGetRulesAuthTableCallback.aidl",
- "telephony/java/com/android/internal/telephony/euicc/IGetSmdsAddressCallback.aidl",
- "telephony/java/com/android/internal/telephony/euicc/IListNotificationsCallback.aidl",
- "telephony/java/com/android/internal/telephony/euicc/ILoadBoundProfilePackageCallback.aidl",
- "telephony/java/com/android/internal/telephony/euicc/IPrepareDownloadCallback.aidl",
- "telephony/java/com/android/internal/telephony/euicc/IRemoveNotificationFromListCallback.aidl",
- "telephony/java/com/android/internal/telephony/euicc/IResetMemoryCallback.aidl",
- "telephony/java/com/android/internal/telephony/euicc/IRetrieveNotificationCallback.aidl",
- "telephony/java/com/android/internal/telephony/euicc/IRetrieveNotificationListCallback.aidl",
- "telephony/java/com/android/internal/telephony/euicc/ISetDefaultSmdpAddressCallback.aidl",
- "telephony/java/com/android/internal/telephony/euicc/ISetNicknameCallback.aidl",
- "telephony/java/com/android/internal/telephony/euicc/ISwitchToProfileCallback.aidl",
- "wifi/java/android/net/wifi/ISoftApCallback.aidl",
- "wifi/java/android/net/wifi/IWifiManager.aidl",
- "wifi/java/android/net/wifi/aware/IWifiAwareDiscoverySessionCallback.aidl",
- "wifi/java/android/net/wifi/aware/IWifiAwareEventCallback.aidl",
- "wifi/java/android/net/wifi/aware/IWifiAwareMacAddressProvider.aidl",
- "wifi/java/android/net/wifi/aware/IWifiAwareManager.aidl",
- "wifi/java/android/net/wifi/p2p/IWifiP2pManager.aidl",
- "wifi/java/android/net/wifi/rtt/IRttCallback.aidl",
- "wifi/java/android/net/wifi/rtt/IWifiRttManager.aidl",
- "wifi/java/android/net/wifi/hotspot2/IProvisioningCallback.aidl",
- "wifi/java/android/net/wifi/IWifiScanner.aidl",
- "packages/services/PacProcessor/com/android/net/IProxyService.aidl",
- "packages/services/Proxy/com/android/net/IProxyCallback.aidl",
- "packages/services/Proxy/com/android/net/IProxyPortListener.aidl",
- "core/java/android/service/quicksettings/IQSService.aidl",
- "core/java/android/service/quicksettings/IQSTileService.aidl",
-
- ":libupdate_engine_aidl",
-
- ":storaged_aidl",
- ":vold_aidl",
- ":gsiservice_aidl",
- ":installd_aidl",
- ":dumpstate_aidl",
-
- "lowpan/java/android/net/lowpan/ILowpanEnergyScanCallback.aidl",
- "lowpan/java/android/net/lowpan/ILowpanNetScanCallback.aidl",
- "lowpan/java/android/net/lowpan/ILowpanInterfaceListener.aidl",
- "lowpan/java/android/net/lowpan/ILowpanInterface.aidl",
- "lowpan/java/android/net/lowpan/ILowpanManagerListener.aidl",
- "lowpan/java/android/net/lowpan/ILowpanManager.aidl",
-
- "core/java/android/app/admin/SecurityLogTags.logtags",
- "core/java/android/content/EventLogTags.logtags",
- "core/java/android/speech/tts/EventLogTags.logtags",
- "core/java/android/net/EventLogTags.logtags",
- "core/java/android/os/EventLogTags.logtags",
- "core/java/android/webkit/EventLogTags.logtags",
- "core/java/com/android/internal/app/EventLogTags.logtags",
- "core/java/com/android/internal/logging/EventLogTags.logtags",
- "core/java/com/android/server/DropboxLogTags.logtags",
- "core/java/org/chromium/arc/EventLogTags.logtags",
-
- ":platform-properties",
-
- ":framework-statslog-gen",
+ "core/java/**/*.aidl",
],
+ path: "core/java",
+}
+filegroup {
+ name: "framework-drm-sources",
+ srcs: [
+ "drm/java/**/*.java",
+ ],
+ path: "drm/java",
+}
+
+filegroup {
+ name: "framework-graphics-sources",
+ srcs: [
+ "graphics/java/**/*.java",
+ "graphics/java/**/*.aidl",
+ ],
+ path: "graphics/java",
+}
+
+filegroup {
+ name: "framework-keystore-sources",
+ srcs: [
+ "keystore/java/**/*.java",
+ "keystore/java/**/*.aidl",
+ ],
+ path: "keystore/java",
+}
+
+filegroup {
+ name: "framework-location-sources",
+ srcs: [
+ "location/java/**/*.java",
+ "location/java/**/*.aidl",
+ ],
+ path: "location/java",
+}
+
+filegroup {
+ name: "framework-lowpan-sources",
+ srcs: [
+ "lowpan/java/**/*.java",
+ "lowpan/java/**/*.aidl",
+ ],
+ path: "lowpan/java",
+}
+
+filegroup {
+ name: "framework-media-sources",
+ srcs: [
+ "media/java/**/*.java",
+ "media/java/**/*.aidl",
+ ],
+ path: "media/java",
+}
+
+filegroup {
+ name: "framework-mca-effect-sources",
+ srcs: [
+ "media/mca/effect/java/**/*.java",
+ ],
+ path: "media/mca/effect/java",
+}
+
+filegroup {
+ name: "framework-mca-filterfw-sources",
+ srcs: [
+ "media/mca/filterfw/java/**/*.java",
+ ],
+ path: "media/mca/filterfw/java",
+}
+
+filegroup {
+ name: "framework-mca-filterpacks-sources",
+ srcs: [
+ "media/mca/filterpacks/java/**/*.java",
+ ],
+ path: "media/mca/filterpacks/java",
+}
+
+filegroup {
+ name: "framework-opengl-sources",
+ srcs: [
+ "opengl/java/**/*.java",
+ ],
+ path: "opengl/java",
+}
+
+filegroup {
+ name: "framework-rs-sources",
+ srcs: [
+ "rs/java/**/*.java",
+ ],
+ path: "rs/java",
+}
+
+filegroup {
+ name: "framework-sax-sources",
+ srcs: [
+ "sax/java/**/*.java",
+ ],
+ path: "sax/java",
+}
+
+filegroup {
+ name: "framework-telecomm-sources",
+ srcs: [
+ "telecomm/java/**/*.java",
+ "telecomm/java/**/*.aidl",
+ ],
+ path: "telecomm/java",
+}
+
+filegroup {
+ name: "framework-telephony-sources",
+ srcs: [
+ "telephony/java/**/*.java",
+ "telephony/java/**/*.aidl",
+ ],
+ path: "telephony/java",
+}
+
+filegroup {
+ name: "framework-wifi-sources",
+ srcs: [
+ "wifi/java/**/*.java",
+ "wifi/java/**/*.aidl",
+ ],
+ path: "wifi/java",
+}
+
+framework_srcs = [
+ // Java/AIDL sources under frameworks/base
+ ":framework-core-sources",
+ ":framework-drm-sources",
+ ":framework-graphics-sources",
+ ":framework-keystore-sources",
+ ":framework-location-sources",
+ ":framework-lowpan-sources",
+ ":framework-media-sources",
+ ":framework-mca-effect-sources",
+ ":framework-mca-filterfw-sources",
+ ":framework-mca-filterpacks-sources",
+ ":framework-opengl-sources",
+ ":framework-rs-sources",
+ ":framework-sax-sources",
+ ":framework-telecomm-sources",
+ ":framework-telephony-sources",
+ ":framework-wifi-sources",
+ ":PacProcessor-aidl-sources",
+ ":ProxyHandler-aidl-sources",
+
+ // AIDL sources from external directories
+ ":dumpstate_aidl",
+ ":gatekeeper_aidl",
+ ":gsiservice_aidl",
+ ":incidentcompanion_aidl",
+ ":installd_aidl",
+ ":keystore_aidl",
+ ":libaudioclient_aidl",
+ ":libbinder_aidl",
+ ":libbluetooth-binder-aidl",
+ ":libcamera_client_aidl",
+ ":libcamera_client_framework_aidl",
+ ":libupdate_engine_aidl",
+ ":storaged_aidl",
+ ":vold_aidl",
+
+ // etc.
+ "core/java/**/*.logtags",
+ ":framework-javastream-protos",
+ ":framework-statslog-gen",
+]
+
+java_defaults {
+ name: "framework-aidl-export-defaults",
aidl: {
export_include_dirs: [
- // From build/make/core/pathmap.mk FRAMEWORK_BASE_SUBDIRS
"core/java",
+ "drm/java",
"graphics/java",
+ "keystore/java",
"location/java",
"lowpan/java",
"media/java",
"media/mca/effect/java",
"media/mca/filterfw/java",
"media/mca/filterpacks/java",
- "drm/java",
"opengl/java",
+ "rs/java",
"sax/java",
"telecomm/java",
"telephony/java",
"wifi/java",
- "keystore/java",
- "rs/java",
],
+ },
+}
+java_defaults {
+ name: "framework-defaults",
+ defaults: ["framework-aidl-export-defaults"],
+ installable: true,
+
+ srcs: framework_srcs,
+
+ aidl: {
+ // TODO(b/70046217) remove this by moving the AIDL files into frameworks/base
+ // so that they are referenced via framework-core-sources
include_dirs: [
- "system/update_engine/binder_bindings",
"frameworks/native/aidl/binder",
- "frameworks/native/cmds/dumpstate/binder",
- "frameworks/av/camera/aidl",
- "frameworks/av/media/libaudioclient/aidl",
"frameworks/native/aidl/gui",
- "system/core/gatekeeperd/binder",
- "system/core/storaged/binder",
- "system/vold/binder",
- "system/gsid/aidl",
- "system/bt/binder",
- "system/security/keystore/binder",
],
-
generate_get_transaction_name: true,
},
@@ -687,6 +259,7 @@
jarjar_rules: ":framework-jarjar-rules",
static_libs: [
+ "mimemap",
"apex_aidl_interface-java",
"suspend_control_aidl_interface-java",
"framework-protos",
@@ -709,6 +282,8 @@
"android.hardware.vibrator-V1.1-java",
"android.hardware.vibrator-V1.2-java",
"android.hardware.wifi-V1.0-java-constants",
+
+ "PlatformProperties",
],
required: [
@@ -752,14 +327,27 @@
name: "framework",
defaults: ["framework-defaults"],
javac_shard_size: 150,
+ required: [
+ "framework-platform-compat-config",
+ "libcore-platform-compat-config",
+ "services-platform-compat-config",
+ ],
}
java_library {
name: "framework-annotation-proc",
defaults: ["framework-defaults"],
installable: false,
- // Use UsedByApps annotation processor
- plugins: ["unsupportedappusage-annotation-processor"],
+ plugins: [
+ "unsupportedappusage-annotation-processor",
+ "compat-changeid-annotation-processor",
+ ],
+}
+
+platform_compat_config {
+ name: "framework-platform-compat-config",
+ prefix: "framework",
+ src: ":framework-annotation-proc",
}
// A library including just UnsupportedAppUsage.java classes.
@@ -1214,7 +802,6 @@
"test-runner/src/**/*.java",
],
srcs_lib: "framework",
- srcs_lib_whitelist_dirs: frameworks_base_subdirs,
srcs_lib_whitelist_pkgs: packages_to_document,
libs: framework_docs_only_libs,
local_sourcepaths: frameworks_base_subdirs,
@@ -1270,13 +857,7 @@
":core_public_api_files",
],
srcs_lib: "framework",
- srcs_lib_whitelist_dirs: frameworks_base_subdirs,
srcs_lib_whitelist_pkgs: packages_to_document,
- libs: [
- "ext",
- "framework",
- "voip-common",
- ],
local_sourcepaths: frameworks_base_subdirs,
installable: false,
annotations_enabled: true,
@@ -1597,10 +1178,9 @@
name: "hiddenapi-mappings",
defaults: ["metalava-api-stubs-default"],
srcs: [
- ":openjdk_java_files",
":non_openjdk_java_files",
+ ":openjdk_java_files",
":opt-telephony-common-srcs",
- "core/java/**/*.java",
],
arg_files: [
"core/res/AndroidManifest.xml",
@@ -1655,6 +1235,7 @@
last_released: {
api_file: ":last-released-public-api",
removed_api_file: "api/removed.txt",
+ baseline_file: ":public-api-incompatibilities-with-last-released",
},
},
jdiff_enabled: true,
@@ -1680,6 +1261,7 @@
last_released: {
api_file: ":last-released-system-api",
removed_api_file: "api/system-removed.txt",
+ baseline_file: ":system-api-incompatibilities-with-last-released"
},
},
jdiff_enabled: true,
@@ -1714,7 +1296,7 @@
// annotations to private apis
aidl_mapping {
name: "framework-aidl-mappings",
- srcs: [":framework-defaults"],
+ srcs: framework_srcs,
output: "framework-aidl-mappings.txt",
}
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 543f0ed..e731138 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -9,6 +9,8 @@
hidden_api_txt_exclude_hook = ${REPO_ROOT}/frameworks/base/tools/hiddenapi/exclude.sh ${PREUPLOAD_COMMIT} ${REPO_ROOT}
+ktlint_hook = ${REPO_ROOT}/prebuilts/ktlint/ktlint.py -f ${PREUPLOAD_FILES}
+
owners_hook = ${REPO_ROOT}/frameworks/base/tools/aosp/aosp_sha.sh ${PREUPLOAD_COMMIT} "OWNERS$"
shell_hook = ${REPO_ROOT}/frameworks/base/tools/aosp/aosp_sha.sh ${PREUPLOAD_COMMIT} "^packages/Shell/"
diff --git a/api/current.txt b/api/current.txt
index 015874d..0e45300 100755
--- a/api/current.txt
+++ b/api/current.txt
@@ -11361,6 +11361,9 @@
field public static final String FEATURE_SENSOR_RELATIVE_HUMIDITY = "android.hardware.sensor.relative_humidity";
field public static final String FEATURE_SENSOR_STEP_COUNTER = "android.hardware.sensor.stepcounter";
field public static final String FEATURE_SENSOR_STEP_DETECTOR = "android.hardware.sensor.stepdetector";
+ field public static final String FEATURE_SE_OMAPI_ESE = "android.hardware.se.omapi.ese";
+ field public static final String FEATURE_SE_OMAPI_SD = "android.hardware.se.omapi.sd";
+ field public static final String FEATURE_SE_OMAPI_UICC = "android.hardware.se.omapi.uicc";
field public static final String FEATURE_SIP = "android.software.sip";
field public static final String FEATURE_SIP_VOIP = "android.software.sip.voip";
field public static final String FEATURE_STRONGBOX_KEYSTORE = "android.hardware.strongbox_keystore";
@@ -17489,8 +17492,12 @@
field public static final int EARLY_DYNASTIC_CUNEIFORM_ID = 257; // 0x101
field public static final android.icu.lang.UCharacter.UnicodeBlock EGYPTIAN_HIEROGLYPHS;
field public static final int EGYPTIAN_HIEROGLYPHS_ID = 194; // 0xc2
+ field public static final android.icu.lang.UCharacter.UnicodeBlock EGYPTIAN_HIEROGLYPH_FORMAT_CONTROLS;
+ field public static final int EGYPTIAN_HIEROGLYPH_FORMAT_CONTROLS_ID = 292; // 0x124
field public static final android.icu.lang.UCharacter.UnicodeBlock ELBASAN;
field public static final int ELBASAN_ID = 226; // 0xe2
+ field public static final android.icu.lang.UCharacter.UnicodeBlock ELYMAIC;
+ field public static final int ELYMAIC_ID = 293; // 0x125
field public static final android.icu.lang.UCharacter.UnicodeBlock EMOTICONS;
field public static final int EMOTICONS_ID = 206; // 0xce
field public static final android.icu.lang.UCharacter.UnicodeBlock ENCLOSED_ALPHANUMERICS;
@@ -17717,6 +17724,8 @@
field public static final int MYANMAR_ID = 28; // 0x1c
field public static final android.icu.lang.UCharacter.UnicodeBlock NABATAEAN;
field public static final int NABATAEAN_ID = 239; // 0xef
+ field public static final android.icu.lang.UCharacter.UnicodeBlock NANDINAGARI;
+ field public static final int NANDINAGARI_ID = 294; // 0x126
field public static final android.icu.lang.UCharacter.UnicodeBlock NEWA;
field public static final int NEWA_ID = 270; // 0x10e
field public static final android.icu.lang.UCharacter.UnicodeBlock NEW_TAI_LUE;
@@ -17728,6 +17737,8 @@
field public static final int NUMBER_FORMS_ID = 45; // 0x2d
field public static final android.icu.lang.UCharacter.UnicodeBlock NUSHU;
field public static final int NUSHU_ID = 277; // 0x115
+ field public static final android.icu.lang.UCharacter.UnicodeBlock NYIAKENG_PUACHUE_HMONG;
+ field public static final int NYIAKENG_PUACHUE_HMONG_ID = 295; // 0x127
field public static final android.icu.lang.UCharacter.UnicodeBlock OGHAM;
field public static final int OGHAM_ID = 34; // 0x22
field public static final android.icu.lang.UCharacter.UnicodeBlock OLD_HUNGARIAN;
@@ -17758,6 +17769,8 @@
field public static final int OSAGE_ID = 271; // 0x10f
field public static final android.icu.lang.UCharacter.UnicodeBlock OSMANYA;
field public static final int OSMANYA_ID = 122; // 0x7a
+ field public static final android.icu.lang.UCharacter.UnicodeBlock OTTOMAN_SIYAQ_NUMBERS;
+ field public static final int OTTOMAN_SIYAQ_NUMBERS_ID = 296; // 0x128
field public static final android.icu.lang.UCharacter.UnicodeBlock PAHAWH_HMONG;
field public static final int PAHAWH_HMONG_ID = 243; // 0xf3
field public static final android.icu.lang.UCharacter.UnicodeBlock PALMYRENE;
@@ -17806,6 +17819,8 @@
field public static final int SINHALA_ID = 24; // 0x18
field public static final android.icu.lang.UCharacter.UnicodeBlock SMALL_FORM_VARIANTS;
field public static final int SMALL_FORM_VARIANTS_ID = 84; // 0x54
+ field public static final android.icu.lang.UCharacter.UnicodeBlock SMALL_KANA_EXTENSION;
+ field public static final int SMALL_KANA_EXTENSION_ID = 297; // 0x129
field public static final android.icu.lang.UCharacter.UnicodeBlock SOGDIAN;
field public static final int SOGDIAN_ID = 291; // 0x123
field public static final android.icu.lang.UCharacter.UnicodeBlock SORA_SOMPENG;
@@ -17842,6 +17857,8 @@
field public static final int SUTTON_SIGNWRITING_ID = 262; // 0x106
field public static final android.icu.lang.UCharacter.UnicodeBlock SYLOTI_NAGRI;
field public static final int SYLOTI_NAGRI_ID = 143; // 0x8f
+ field public static final android.icu.lang.UCharacter.UnicodeBlock SYMBOLS_AND_PICTOGRAPHS_EXTENDED_A;
+ field public static final int SYMBOLS_AND_PICTOGRAPHS_EXTENDED_A_ID = 298; // 0x12a
field public static final android.icu.lang.UCharacter.UnicodeBlock SYRIAC;
field public static final int SYRIAC_ID = 13; // 0xd
field public static final android.icu.lang.UCharacter.UnicodeBlock SYRIAC_SUPPLEMENT;
@@ -17864,6 +17881,8 @@
field public static final int TAKRI_ID = 220; // 0xdc
field public static final android.icu.lang.UCharacter.UnicodeBlock TAMIL;
field public static final int TAMIL_ID = 20; // 0x14
+ field public static final android.icu.lang.UCharacter.UnicodeBlock TAMIL_SUPPLEMENT;
+ field public static final int TAMIL_SUPPLEMENT_ID = 299; // 0x12b
field public static final android.icu.lang.UCharacter.UnicodeBlock TANGUT;
field public static final android.icu.lang.UCharacter.UnicodeBlock TANGUT_COMPONENTS;
field public static final int TANGUT_COMPONENTS_ID = 273; // 0x111
@@ -17898,6 +17917,8 @@
field public static final int VEDIC_EXTENSIONS_ID = 175; // 0xaf
field public static final android.icu.lang.UCharacter.UnicodeBlock VERTICAL_FORMS;
field public static final int VERTICAL_FORMS_ID = 145; // 0x91
+ field public static final android.icu.lang.UCharacter.UnicodeBlock WANCHO;
+ field public static final int WANCHO_ID = 300; // 0x12c
field public static final android.icu.lang.UCharacter.UnicodeBlock WARANG_CITI;
field public static final int WARANG_CITI_ID = 252; // 0xfc
field public static final android.icu.lang.UCharacter.UnicodeBlock YIJING_HEXAGRAM_SYMBOLS;
@@ -18210,6 +18231,7 @@
field public static final int EASTERN_SYRIAC = 97; // 0x61
field public static final int EGYPTIAN_HIEROGLYPHS = 71; // 0x47
field public static final int ELBASAN = 136; // 0x88
+ field public static final int ELYMAIC = 185; // 0xb9
field public static final int ESTRANGELO_SYRIAC = 95; // 0x5f
field public static final int ETHIOPIC = 11; // 0xb
field public static final int GEORGIAN = 12; // 0xc
@@ -18289,10 +18311,12 @@
field public static final int MYANMAR = 28; // 0x1c
field public static final int NABATAEAN = 143; // 0x8f
field public static final int NAKHI_GEBA = 132; // 0x84
+ field public static final int NANDINAGARI = 187; // 0xbb
field public static final int NEWA = 170; // 0xaa
field public static final int NEW_TAI_LUE = 59; // 0x3b
field public static final int NKO = 87; // 0x57
field public static final int NUSHU = 150; // 0x96
+ field public static final int NYIAKENG_PUACHUE_HMONG = 186; // 0xba
field public static final int OGHAM = 29; // 0x1d
field public static final int OLD_CHURCH_SLAVONIC_CYRILLIC = 68; // 0x44
field public static final int OLD_HUNGARIAN = 76; // 0x4c
@@ -18356,6 +18380,7 @@
field public static final int UNWRITTEN_LANGUAGES = 102; // 0x66
field public static final int VAI = 99; // 0x63
field public static final int VISIBLE_SPEECH = 100; // 0x64
+ field public static final int WANCHO = 188; // 0xbc
field public static final int WARANG_CITI = 146; // 0x92
field public static final int WESTERN_SYRIAC = 96; // 0x60
field public static final int WOLEAI = 155; // 0x9b
@@ -19144,6 +19169,7 @@
method public String getDateTimeFormat();
method public String getDecimal();
method public static android.icu.text.DateTimePatternGenerator getEmptyInstance();
+ method public String getFieldDisplayName(int, android.icu.text.DateTimePatternGenerator.DisplayWidth);
method public static android.icu.text.DateTimePatternGenerator getInstance();
method public static android.icu.text.DateTimePatternGenerator getInstance(android.icu.util.ULocale);
method public static android.icu.text.DateTimePatternGenerator getInstance(java.util.Locale);
@@ -19177,6 +19203,12 @@
field public static final int ZONE = 15; // 0xf
}
+ public enum DateTimePatternGenerator.DisplayWidth {
+ enum_constant public static final android.icu.text.DateTimePatternGenerator.DisplayWidth ABBREVIATED;
+ enum_constant public static final android.icu.text.DateTimePatternGenerator.DisplayWidth NARROW;
+ enum_constant public static final android.icu.text.DateTimePatternGenerator.DisplayWidth WIDE;
+ }
+
public static final class DateTimePatternGenerator.PatternInfo {
ctor public DateTimePatternGenerator.PatternInfo();
field public static final int BASE_CONFLICT = 1; // 0x1
@@ -20731,6 +20763,7 @@
method public static boolean isAvailable(String, java.util.Date, java.util.Date);
method public java.util.Currency toJavaCurrency();
field public static final int LONG_NAME = 1; // 0x1
+ field public static final int NARROW_SYMBOL_NAME = 3; // 0x3
field public static final int PLURAL_LONG_NAME = 2; // 0x2
field public static final int SYMBOL_NAME = 0; // 0x0
}
@@ -20933,6 +20966,7 @@
ctor public JapaneseCalendar(int, int, int, int, int, int);
field public static final int HEISEI;
field public static final int MEIJI;
+ field public static final int REIWA;
field public static final int SHOWA;
field public static final int TAISHO;
}
@@ -21408,6 +21442,8 @@
field public static final android.icu.util.VersionInfo UCOL_RUNTIME_VERSION;
field public static final android.icu.util.VersionInfo UNICODE_10_0;
field public static final android.icu.util.VersionInfo UNICODE_11_0;
+ field public static final android.icu.util.VersionInfo UNICODE_12_0;
+ field public static final android.icu.util.VersionInfo UNICODE_12_1;
field public static final android.icu.util.VersionInfo UNICODE_1_0;
field public static final android.icu.util.VersionInfo UNICODE_1_0_1;
field public static final android.icu.util.VersionInfo UNICODE_1_1_0;
@@ -37379,6 +37415,7 @@
}
public static final class Telephony.Mms.Addr implements android.provider.BaseColumns {
+ method @NonNull public static android.net.Uri getAddrUriForMessage(@NonNull String);
field public static final String ADDRESS = "address";
field public static final String CHARSET = "charset";
field public static final String CONTACT_ID = "contact_id";
@@ -37407,12 +37444,13 @@
}
public static final class Telephony.Mms.Part implements android.provider.BaseColumns {
+ method @NonNull public static android.net.Uri getPartUriForMessage(@NonNull String);
field public static final String CHARSET = "chset";
field public static final String CONTENT_DISPOSITION = "cd";
field public static final String CONTENT_ID = "cid";
field public static final String CONTENT_LOCATION = "cl";
field public static final String CONTENT_TYPE = "ct";
- field public static final android.net.Uri CONTENT_URI;
+ field @NonNull public static final android.net.Uri CONTENT_URI;
field public static final String CT_START = "ctt_s";
field public static final String CT_TYPE = "ctt_t";
field public static final String FILENAME = "fn";
@@ -42104,6 +42142,7 @@
public class CarrierConfigManager {
method @Nullable public android.os.PersistableBundle getConfig();
+ method @Nullable public android.os.PersistableBundle getConfigByComponentForSubId(String, int);
method @Nullable public android.os.PersistableBundle getConfigForSubId(int);
method public static boolean isConfigForIdentifiedCarrier(android.os.PersistableBundle);
method public void notifyConfigChangedForSubId(int);
@@ -42281,6 +42320,10 @@
field public static final String KEY_WORLD_PHONE_BOOL = "world_phone_bool";
}
+ public static final class CarrierConfigManager.Ims {
+ field public static final String KEY_PREFIX = "ims.";
+ }
+
public abstract class CellIdentity implements android.os.Parcelable {
method public int describeContents();
method @Nullable public CharSequence getOperatorAlphaLong();
@@ -42463,6 +42506,7 @@
method public int getBitErrorRate();
method public int getDbm();
method @IntRange(from=android.telephony.CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN, to=android.telephony.CellSignalStrength.SIGNAL_STRENGTH_GREAT) public int getLevel();
+ method public int getRssi();
method public int getTimingAdvance();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.telephony.CellSignalStrengthGsm> CREATOR;
@@ -70466,7 +70510,7 @@
method public void logp(@NonNull java.util.logging.Level, @Nullable String, @Nullable String, @Nullable String);
method public void logp(@NonNull java.util.logging.Level, @Nullable String, @Nullable String, @NonNull java.util.function.Supplier<java.lang.String>);
method public void logp(@NonNull java.util.logging.Level, @Nullable String, @Nullable String, @Nullable String, @Nullable Object);
- method public void logp(@NonNull java.util.logging.Level, @Nullable String, @Nullable String, @Nullable String, Object[]);
+ method public void logp(@NonNull java.util.logging.Level, @Nullable String, @Nullable String, @Nullable String, @Nullable Object[]);
method public void logp(@NonNull java.util.logging.Level, @Nullable String, @Nullable String, @Nullable String, @Nullable Throwable);
method public void logp(@NonNull java.util.logging.Level, @Nullable String, @Nullable String, @Nullable Throwable, @NonNull java.util.function.Supplier<java.lang.String>);
method @Deprecated public void logrb(@NonNull java.util.logging.Level, @Nullable String, @Nullable String, @Nullable String, @Nullable String);
diff --git a/api/removed.txt b/api/removed.txt
index f40b146..0795438 100644
--- a/api/removed.txt
+++ b/api/removed.txt
@@ -278,7 +278,7 @@
package android.icu.util {
public class JapaneseCalendar extends android.icu.util.GregorianCalendar {
- field public static final int CURRENT_ERA;
+ field @Deprecated public static final int CURRENT_ERA;
}
}
diff --git a/api/system-current.txt b/api/system-current.txt
index 593b45d..9200ce5 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -7424,6 +7424,7 @@
public abstract class ImsFeature {
ctor public ImsFeature();
method public abstract void changeEnabledCapabilities(android.telephony.ims.feature.CapabilityChangeRequest, android.telephony.ims.feature.ImsFeature.CapabilityCallbackProxy);
+ method public final int getSlotIndex();
method public abstract void onFeatureReady();
method public abstract void onFeatureRemoved();
method public final void setFeatureState(int);
@@ -7437,7 +7438,7 @@
field public static final int STATE_UNAVAILABLE = 0; // 0x0
}
- @Deprecated public static class ImsFeature.Capabilities {
+ public static class ImsFeature.Capabilities {
field @Deprecated protected int mCapabilities;
}
diff --git a/api/test-current.txt b/api/test-current.txt
index 144e5f5..7ea9a26 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -1521,6 +1521,11 @@
method @NonNull public android.telephony.NetworkRegistrationInfo.Builder setTransportType(int);
}
+ public class PhoneNumberUtils {
+ method public static int getMinMatchForTest();
+ method public static void setMinMatchForTest(int);
+ }
+
public class ServiceState implements android.os.Parcelable {
method public void addNetworkRegistrationInfo(android.telephony.NetworkRegistrationInfo);
method public void setCdmaSystemAndNetworkId(int, int);
diff --git a/cmds/bootanimation/Android.bp b/cmds/bootanimation/Android.bp
index e17f735..3e5877b 100644
--- a/cmds/bootanimation/Android.bp
+++ b/cmds/bootanimation/Android.bp
@@ -40,24 +40,6 @@
"audioplay.cpp",
],
- product_variables: {
- product_is_iot: {
- shared_libs: [
- "libandroidthings",
- "libchrome",
- ],
- srcs: [
- "iot/iotbootanimation_main.cpp",
- "iot/BootAction.cpp",
- "iot/BootParameters.cpp",
- ],
- exclude_srcs: [
- "bootanimation_main.cpp",
- "audioplay.cpp",
- ],
- },
- },
-
init_rc: ["bootanim.rc"],
}
@@ -77,10 +59,4 @@
"libGLESv1_CM",
"libgui",
],
-
- product_variables: {
- product_is_iot: {
- init_rc: ["iot/bootanim_iot.rc"],
- },
- },
}
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index ed6c25d..95bdc4a 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -63,6 +63,10 @@
#include "BootAnimation.h"
+#define ANIM_PATH_MAX 255
+#define STR(x) #x
+#define STRTO(x) STR(x)
+
namespace android {
static const char OEM_BOOTANIMATION_FILE[] = "/oem/media/bootanimation.zip";
@@ -94,7 +98,7 @@
static const int TEXT_CENTER_VALUE = INT_MAX;
static const int TEXT_MISSING_VALUE = INT_MIN;
static const char EXIT_PROP_NAME[] = "service.bootanim.exit";
-static const int ANIM_ENTRY_NAME_MAX = 256;
+static const int ANIM_ENTRY_NAME_MAX = ANIM_PATH_MAX + 1;
static constexpr size_t TEXT_POS_LEN_MAX = 16;
// ---------------------------------------------------------------------------
@@ -658,7 +662,7 @@
animation.width = width;
animation.height = height;
animation.fps = fps;
- } else if (sscanf(l, " %c %d %d %s #%6s %16s %16s",
+ } else if (sscanf(l, " %c %d %d %" STRTO(ANIM_PATH_MAX) "s #%6s %16s %16s",
&pathType, &count, &pause, path, color, clockPos1, clockPos2) >= 4) {
//ALOGD("> type=%c, count=%d, pause=%d, path=%s, color=%s, clockPos1=%s, clockPos2=%s",
// pathType, count, pause, path, color, clockPos1, clockPos2);
diff --git a/cmds/bootanimation/iot/BootAction.cpp b/cmds/bootanimation/iot/BootAction.cpp
deleted file mode 100644
index fa79744..0000000
--- a/cmds/bootanimation/iot/BootAction.cpp
+++ /dev/null
@@ -1,109 +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.
- */
-
-#include "BootAction.h"
-
-#define LOG_TAG "BootAction"
-
-#include <dlfcn.h>
-
-#include <pio/peripheral_manager_client.h>
-#include <utils/Log.h>
-
-namespace android {
-
-BootAction::~BootAction() {
- if (mLibHandle != nullptr) {
- dlclose(mLibHandle);
- }
-}
-
-bool BootAction::init(const std::string& libraryPath,
- const std::vector<ABootActionParameter>& parameters) {
- APeripheralManagerClient* client = nullptr;
- ALOGD("Connecting to peripheralmanager");
- // Wait for peripheral manager to come up.
- while (client == nullptr) {
- client = APeripheralManagerClient_new();
- if (client == nullptr) {
- ALOGV("peripheralmanager is not up, sleeping before we check again.");
- usleep(250000);
- }
- }
- ALOGD("Peripheralmanager is up.");
- APeripheralManagerClient_delete(client);
-
-
- ALOGI("Loading boot action %s", libraryPath.c_str());
- mLibHandle = dlopen(libraryPath.c_str(), RTLD_NOW);
- if (mLibHandle == nullptr) {
- ALOGE("Unable to load library at %s :: %s",
- libraryPath.c_str(), dlerror());
- return false;
- }
-
- void* loaded = nullptr;
- if (!loadSymbol("boot_action_init", &loaded) || loaded == nullptr) {
- return false;
- }
- mLibInit = reinterpret_cast<libInit>(loaded);
-
- loaded = nullptr;
- if (!loadSymbol("boot_action_shutdown", &loaded) || loaded == nullptr) {
- return false;
- }
- mLibShutdown = reinterpret_cast<libShutdown>(loaded);
-
- // StartPart is considered optional, if it isn't exported by the library
- // we will still call init and shutdown.
- loaded = nullptr;
- if (!loadSymbol("boot_action_start_part", &loaded) || loaded == nullptr) {
- ALOGI("No boot_action_start_part found, action will not be told when "
- "Animation parts change.");
- } else {
- mLibStartPart = reinterpret_cast<libStartPart>(loaded);
- }
-
- ALOGD("Entering boot_action_init");
- bool result = mLibInit(parameters.data(), parameters.size());
- ALOGD("Returned from boot_action_init");
- return result;
-}
-
-void BootAction::startPart(int partNumber, int playNumber) {
- if (mLibStartPart == nullptr) return;
-
- ALOGD("Entering boot_action_start_part");
- mLibStartPart(partNumber, playNumber);
- ALOGD("Returned from boot_action_start_part");
-}
-
-void BootAction::shutdown() {
- ALOGD("Entering boot_action_shutdown");
- mLibShutdown();
- ALOGD("Returned from boot_action_shutdown");
-}
-
-bool BootAction::loadSymbol(const char* symbol, void** loaded) {
- *loaded = dlsym(mLibHandle, symbol);
- if (loaded == nullptr) {
- ALOGE("Unable to load symbol : %s :: %s", symbol, dlerror());
- return false;
- }
- return true;
-}
-
-} // namespace android
diff --git a/cmds/bootanimation/iot/BootAction.h b/cmds/bootanimation/iot/BootAction.h
deleted file mode 100644
index 5e2495f..0000000
--- a/cmds/bootanimation/iot/BootAction.h
+++ /dev/null
@@ -1,63 +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.
- */
-
-#ifndef _BOOTANIMATION_BOOTACTION_H
-#define _BOOTANIMATION_BOOTACTION_H
-
-#include <string>
-#include <vector>
-
-#include <boot_action/boot_action.h> // libandroidthings native API.
-#include <utils/RefBase.h>
-
-namespace android {
-
-class BootAction : public RefBase {
-public:
- ~BootAction();
-
- // libraryPath is a fully qualified path to the target .so library.
- bool init(const std::string& libraryPath,
- const std::vector<ABootActionParameter>& parameters);
-
- // The animation is going to start playing partNumber for the playCount'th
- // time, update the action as needed.
- // This is run in the same thread as the boot animation,
- // you must not block here.
- void startPart(int partNumber, int playCount);
-
- // Shutdown the boot action, this will be called shortly before the
- // process is shut down to allow time for cleanup.
- void shutdown();
-
-private:
- typedef bool (*libInit)(const ABootActionParameter* parameters,
- size_t num_parameters);
- typedef void (*libStartPart)(int partNumber, int playNumber);
- typedef void (*libShutdown)();
-
- bool loadSymbol(const char* symbol, void** loaded);
-
- void* mLibHandle = nullptr;
- libInit mLibInit = nullptr;
- libStartPart mLibStartPart = nullptr;
- libShutdown mLibShutdown = nullptr;
-};
-
-} // namespace android
-
-
-#endif // _BOOTANIMATION_BOOTACTION_H
diff --git a/cmds/bootanimation/iot/BootParameters.cpp b/cmds/bootanimation/iot/BootParameters.cpp
deleted file mode 100644
index da6ad0d..0000000
--- a/cmds/bootanimation/iot/BootParameters.cpp
+++ /dev/null
@@ -1,124 +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.
- */
-
-#include "BootParameters.h"
-
-#define LOG_TAG "BootParameters"
-
-#include <fcntl.h>
-
-#include <string>
-
-#include <android-base/file.h>
-#include <base/json/json_parser.h>
-#include <base/json/json_reader.h>
-#include <base/json/json_value_converter.h>
-#include <utils/Log.h>
-
-using android::base::RemoveFileIfExists;
-using android::base::ReadFileToString;
-using base::JSONReader;
-using base::JSONValueConverter;
-using base::Value;
-
-namespace android {
-
-namespace {
-
-// Brightness and volume are stored as integer strings in next_boot.json.
-// They are divided by this constant to produce the actual float values in
-// range [0.0, 1.0]. This constant must match its counterpart in
-// DeviceManager.
-constexpr const float kFloatScaleFactor = 1000.0f;
-
-constexpr const char* kNextBootFile = "/data/misc/bootanimation/next_boot.json";
-constexpr const char* kLastBootFile = "/data/misc/bootanimation/last_boot.json";
-
-void swapBootConfigs() {
- // rename() will fail if next_boot.json doesn't exist, so delete
- // last_boot.json manually first.
- std::string err;
- if (!RemoveFileIfExists(kLastBootFile, &err))
- ALOGE("Unable to delete last boot file: %s", err.c_str());
-
- if (rename(kNextBootFile, kLastBootFile) && errno != ENOENT)
- ALOGE("Unable to swap boot files: %s", strerror(errno));
-
- int fd = open(kNextBootFile, O_CREAT, DEFFILEMODE);
- if (fd == -1) {
- ALOGE("Unable to create next boot file: %s", strerror(errno));
- } else {
- // Make next_boot.json writable to everyone so DeviceManagementService
- // can save saved_parameters there.
- if (fchmod(fd, DEFFILEMODE))
- ALOGE("Unable to set next boot file permissions: %s", strerror(errno));
- close(fd);
- }
-}
-
-} // namespace
-
-BootParameters::SavedBootParameters::SavedBootParameters()
- : brightness(-kFloatScaleFactor), volume(-kFloatScaleFactor) {}
-
-void BootParameters::SavedBootParameters::RegisterJSONConverter(
- JSONValueConverter<SavedBootParameters>* converter) {
- converter->RegisterIntField("brightness", &SavedBootParameters::brightness);
- converter->RegisterIntField("volume", &SavedBootParameters::volume);
- converter->RegisterRepeatedString("param_names",
- &SavedBootParameters::param_names);
- converter->RegisterRepeatedString("param_values",
- &SavedBootParameters::param_values);
-}
-
-BootParameters::BootParameters() {
- swapBootConfigs();
- loadParameters();
-}
-
-void BootParameters::loadParameters() {
- std::string contents;
- if (!ReadFileToString(kLastBootFile, &contents)) {
- if (errno != ENOENT)
- ALOGE("Unable to read from %s: %s", kLastBootFile, strerror(errno));
-
- return;
- }
-
- std::unique_ptr<Value> json = JSONReader::Read(contents);
- if (json.get() == nullptr) {
- return;
- }
-
- JSONValueConverter<SavedBootParameters> converter;
- if (converter.Convert(*(json.get()), &mRawParameters)) {
- mBrightness = mRawParameters.brightness / kFloatScaleFactor;
- mVolume = mRawParameters.volume / kFloatScaleFactor;
-
- if (mRawParameters.param_names.size() == mRawParameters.param_values.size()) {
- for (size_t i = 0; i < mRawParameters.param_names.size(); i++) {
- mParameters.push_back({
- .key = mRawParameters.param_names[i]->c_str(),
- .value = mRawParameters.param_values[i]->c_str()
- });
- }
- } else {
- ALOGW("Parameter names and values size mismatch");
- }
- }
-}
-
-} // namespace android
diff --git a/cmds/bootanimation/iot/BootParameters.h b/cmds/bootanimation/iot/BootParameters.h
deleted file mode 100644
index c10bd44..0000000
--- a/cmds/bootanimation/iot/BootParameters.h
+++ /dev/null
@@ -1,73 +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.
- */
-
-#ifndef _BOOTANIMATION_BOOT_PARAMETERS_H_
-#define _BOOTANIMATION_BOOT_PARAMETERS_H_
-
-#include <list>
-#include <vector>
-
-#include <base/json/json_value_converter.h>
-#include <boot_action/boot_action.h> // libandroidthings native API.
-
-namespace android {
-
-// Provides access to the parameters set by DeviceManager.reboot().
-class BootParameters {
-public:
- // Constructor loads the parameters for this boot and swaps the param files
- // to clear the parameters for next boot.
- BootParameters();
-
- // Returns true if volume/brightness were explicitly set on reboot.
- bool hasVolume() const { return mVolume >= 0; }
- bool hasBrightness() const { return mBrightness >= 0; }
-
- // Returns volume/brightness in [0,1], or -1 if unset.
- float getVolume() const { return mVolume; }
- float getBrightness() const { return mBrightness; }
-
- // Returns the additional boot parameters that were set on reboot.
- const std::vector<ABootActionParameter>& getParameters() const { return mParameters; }
-
-private:
- // Raw boot saved_parameters loaded from .json.
- struct SavedBootParameters {
- int brightness;
- int volume;
- std::vector<std::unique_ptr<std::string>> param_names;
- std::vector<std::unique_ptr<std::string>> param_values;
-
- SavedBootParameters();
- static void RegisterJSONConverter(
- ::base::JSONValueConverter<SavedBootParameters>* converter);
- };
-
- void loadParameters();
-
- float mVolume = -1.f;
- float mBrightness = -1.f;
- std::vector<ABootActionParameter> mParameters;
-
- // ABootActionParameter is just a raw pointer so we need to keep the
- // original strings around to avoid losing them.
- SavedBootParameters mRawParameters;
-};
-
-} // namespace android
-
-
-#endif // _BOOTANIMATION_BOOT_PARAMETERS_H_
diff --git a/cmds/bootanimation/iot/bootanim_iot.rc b/cmds/bootanimation/iot/bootanim_iot.rc
deleted file mode 100644
index 2fc1336..0000000
--- a/cmds/bootanimation/iot/bootanim_iot.rc
+++ /dev/null
@@ -1,2 +0,0 @@
-on post-fs-data
- mkdir /data/misc/bootanimation 0777 root root
diff --git a/cmds/bootanimation/iot/iotbootanimation_main.cpp b/cmds/bootanimation/iot/iotbootanimation_main.cpp
deleted file mode 100644
index 00cef43..0000000
--- a/cmds/bootanimation/iot/iotbootanimation_main.cpp
+++ /dev/null
@@ -1,124 +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.
- */
-
-#define LOG_TAG "IotBootAnimation"
-
-#include <base/files/file_util.h>
-#include <binder/IPCThreadState.h>
-#include <binder/IServiceManager.h>
-#include <binder/ProcessState.h>
-#include <cutils/properties.h>
-#include <sys/resource.h>
-#include <utils/Log.h>
-#include <utils/threads.h>
-#include <BootAnimation.h>
-
-#include "BootAction.h"
-#include "BootAnimationUtil.h"
-#include "BootParameters.h"
-
-using namespace android;
-
-// Create a typedef for readability.
-typedef android::BootAnimation::Animation Animation;
-
-namespace {
-
-constexpr const char* kDefaultLibName = "libbootaction.so";
-
-class BootActionAnimationCallbacks : public android::BootAnimation::Callbacks {
-public:
- BootActionAnimationCallbacks(std::unique_ptr<BootParameters> bootParameters)
- : mBootParameters(std::move(bootParameters)) {}
-
- void init(const Vector<Animation::Part>&) override {
- std::string library_path("/oem/lib/");
-
- // This value is optionally provided by the user and will be written to
- // /oem/oem.prop.
- char property[PROP_VALUE_MAX] = {0};
- property_get("ro.oem.bootactions.lib", property, kDefaultLibName);
- library_path += property;
-
- if (!::base::PathExists(::base::FilePath(library_path))) {
- ALOGI("Skipping boot actions: %s does not exist", library_path.c_str());
- return;
- }
-
- mBootAction = new BootAction();
- if (!mBootAction->init(library_path, mBootParameters->getParameters())) {
- mBootAction = NULL;
- }
- };
-
- void playPart(int partNumber, const Animation::Part&, int playNumber) override {
- if (mBootAction != nullptr) {
- mBootAction->startPart(partNumber, playNumber);
- }
- };
-
- void shutdown() override {
- if (mBootAction != nullptr) {
- // If we have a bootaction we want to wait until we are actually
- // told to shut down. If the animation exits early keep the action
- // running.
- char value[PROPERTY_VALUE_MAX] = {0};
- for (int exitRequested = 0; exitRequested == 0; ) {
- property_get("service.bootanim.exit", value, "0");
- exitRequested = atoi(value);
-
- // Poll value at 10hz.
- if (exitRequested == 0) {
- usleep(100000);
- }
- }
-
- mBootAction->shutdown();
- // Give it two seconds to shut down.
- sleep(2);
- mBootAction = nullptr;
- }
- };
-
-private:
- std::unique_ptr<BootParameters> mBootParameters;
- sp<BootAction> mBootAction = nullptr;
-};
-
-} // namespace
-
-int main() {
- setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_DISPLAY);
-
- // Clear our params for next boot no matter what.
- std::unique_ptr<BootParameters> bootParameters(new BootParameters());
-
- if (bootAnimationDisabled()) {
- ALOGI("boot animation disabled");
- return 0;
- }
-
- waitForSurfaceFlinger();
-
- sp<ProcessState> proc(ProcessState::self());
- ProcessState::self()->startThreadPool();
-
- sp<BootAnimation> boot = new BootAnimation(
- new BootActionAnimationCallbacks(std::move(bootParameters)));
-
- IPCThreadState::self()->joinThreadPool();
- return 0;
-}
diff --git a/cmds/idmap/scan.cpp b/cmds/idmap/scan.cpp
index d69dd79..847dda3 100644
--- a/cmds/idmap/scan.cpp
+++ b/cmds/idmap/scan.cpp
@@ -9,7 +9,6 @@
#include <androidfw/ResourceTypes.h>
#include <androidfw/StreamingZipInflater.h>
#include <androidfw/ZipFileRO.h>
-#include <cutils/jstring.h>
#include <cutils/properties.h>
#include <private/android_filesystem_config.h> // for AID_SYSTEM
#include <utils/SortedVector.h>
@@ -84,15 +83,9 @@
}
bool check_property(String16 property, String16 value) {
- const char *prop;
- const char *val;
-
- prop = strndup16to8(property.string(), property.size());
char propBuf[PROPERTY_VALUE_MAX];
- property_get(prop, propBuf, NULL);
- val = strndup16to8(value.string(), value.size());
-
- return (strcmp(propBuf, val) == 0);
+ property_get(String8(property).c_str(), propBuf, NULL);
+ return String8(value) == propBuf;
}
int parse_overlay_tag(const ResXMLTree& parser, const char *target_package_name,
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 1254f1a..f9b6219 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -2871,6 +2871,9 @@
// Additional pass-through fields opaque to statsd.
// The DNS resolver Mainline module can add new fields here without requiring an OS update.
optional android.stats.dnsresolver.DnsQueryEvents dns_query_events = 8 [(log_mode) = MODE_BYTES];
+
+ // The sample rate of DNS stats (to statsd) is 1/sampling_rate_denom.
+ optional int32 sampling_rate_denom = 9;
}
/**
diff --git a/cmds/statsd/src/external/StatsPuller.cpp b/cmds/statsd/src/external/StatsPuller.cpp
index b29e979..8233eee 100644
--- a/cmds/statsd/src/external/StatsPuller.cpp
+++ b/cmds/statsd/src/external/StatsPuller.cpp
@@ -35,8 +35,14 @@
// ValueMetric has a minimum bucket size of 10min so that we don't pull too frequently
StatsPuller::StatsPuller(const int tagId)
: mTagId(tagId) {
- mCoolDownNs = StatsPullerManagerImpl::kAllPullAtomInfo.find(tagId)->second.coolDownNs;
- VLOG("Puller for tag %d created. Cooldown set to %lld", mTagId, (long long)mCoolDownNs);
+ auto pullAtomInfo = StatsPullerManagerImpl::kAllPullAtomInfo.find(tagId);
+ if (pullAtomInfo != StatsPullerManagerImpl::kAllPullAtomInfo.end()) {
+ mCoolDownNs = pullAtomInfo->second.coolDownNs;
+ VLOG("Puller for tag %d created. Cooldown set to %lld", mTagId, (long long)mCoolDownNs);
+ } else {
+ mCoolDownNs = 0;
+ VLOG("Creating puller for a non-recognised tag %d.", mTagId);
+ }
}
bool StatsPuller::Pull(const int64_t elapsedTimeNs, std::vector<std::shared_ptr<LogEvent>>* data) {
diff --git a/config/boot-profile.txt b/config/boot-profile.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/config/boot-profile.txt
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index d9c82ea..8299492 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -168,6 +168,8 @@
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.InetAddress;
+import java.nio.file.Files;
+import java.nio.file.Path;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Arrays;
@@ -703,6 +705,8 @@
boolean autofillCompatibilityEnabled;
+ long[] disabledCompatChanges;
+
public String toString() {
return "AppBindData{appInfo=" + appInfo + "}";
}
@@ -920,7 +924,8 @@
boolean enableBinderTracking, boolean trackAllocation,
boolean isRestrictedBackupMode, boolean persistent, Configuration config,
CompatibilityInfo compatInfo, Map services, Bundle coreSettings,
- String buildSerial, boolean autofillCompatibilityEnabled) {
+ String buildSerial, boolean autofillCompatibilityEnabled,
+ long[] disabledCompatChanges) {
if (services != null) {
if (false) {
@@ -968,6 +973,7 @@
data.initProfilerInfo = profilerInfo;
data.buildSerial = buildSerial;
data.autofillCompatibilityEnabled = autofillCompatibilityEnabled;
+ data.disabledCompatChanges = disabledCompatChanges;
sendMessage(H.BIND_APPLICATION, data);
}
@@ -5670,6 +5676,7 @@
// Note when this process has started.
Process.setStartTimes(SystemClock.elapsedRealtime(), SystemClock.uptimeMillis());
+ AppCompatCallbacks.install(data.disabledCompatChanges);
mBoundApplication = data;
mConfiguration = new Configuration(data.config);
mCompatConfiguration = new Configuration(data.config);
@@ -5898,6 +5905,26 @@
NetworkSecurityConfigProvider.install(appContext);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+
+ if (isAppDebuggable) {
+ try {
+ // Load all the agents in the code_cache/startup_agents directory.
+ // We pass the absolute path to the data_dir as an argument.
+ Path startup_path = appContext.getCodeCacheDir().toPath().resolve("startup_agents");
+ if (Files.exists(startup_path)) {
+ for (Path p : Files.newDirectoryStream(startup_path)) {
+ handleAttachAgent(
+ p.toAbsolutePath().toString()
+ + "="
+ + appContext.getDataDir().toPath().toAbsolutePath().toString(),
+ data.info);
+ }
+ }
+ } catch (Exception e) {
+ // Ignored.
+ }
+ }
+
// Continue loading instrumentation.
if (ii != null) {
ApplicationInfo instrApp;
diff --git a/core/java/android/app/AppCompatCallbacks.java b/core/java/android/app/AppCompatCallbacks.java
new file mode 100644
index 0000000..17697db
--- /dev/null
+++ b/core/java/android/app/AppCompatCallbacks.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+import android.compat.Compatibility;
+import android.os.Process;
+import android.util.Log;
+
+import java.util.Arrays;
+
+/**
+ * App process implementation of the {@link Compatibility} API.
+ *
+ * @hide
+ */
+public final class AppCompatCallbacks extends Compatibility.Callbacks {
+
+ private static final String TAG = "Compatibility";
+
+ private final long[] mDisabledChanges;
+
+ /**
+ * Install this class into the current process.
+ *
+ * @param disabledChanges Set of compatibility changes that are disabled for this process.
+ */
+ public static void install(long[] disabledChanges) {
+ Compatibility.setCallbacks(new AppCompatCallbacks(disabledChanges));
+ }
+
+ private AppCompatCallbacks(long[] disabledChanges) {
+ mDisabledChanges = Arrays.copyOf(disabledChanges, disabledChanges.length);
+ Arrays.sort(mDisabledChanges);
+ }
+
+ protected void reportChange(long changeId) {
+ Log.d(TAG, "Compat change reported: " + changeId + "; UID " + Process.myUid());
+ // TODO log via StatsLog
+ }
+
+ protected boolean isChangeEnabled(long changeId) {
+ if (Arrays.binarySearch(mDisabledChanges, changeId) < 0) {
+ // Not present in the disabled array
+ reportChange(changeId);
+ return true;
+ }
+ return false;
+ }
+
+}
diff --git a/core/java/android/app/IApplicationThread.aidl b/core/java/android/app/IApplicationThread.aidl
index d478cd6..1f45fc5 100644
--- a/core/java/android/app/IApplicationThread.aidl
+++ b/core/java/android/app/IApplicationThread.aidl
@@ -67,7 +67,8 @@
int debugMode, boolean enableBinderTracking, boolean trackAllocation,
boolean restrictedBackupMode, boolean persistent, in Configuration config,
in CompatibilityInfo compatInfo, in Map services,
- in Bundle coreSettings, in String buildSerial, boolean isAutofillCompatEnabled);
+ in Bundle coreSettings, in String buildSerial, boolean isAutofillCompatEnabled,
+ in long[] disabledCompatChanges);
void runIsolatedEntryPoint(in String entryPoint, in String[] entryPointArgs);
void scheduleExit();
void scheduleServiceArgs(IBinder token, in ParceledListSlice args);
diff --git a/core/java/android/app/job/JobInfo.java b/core/java/android/app/job/JobInfo.java
index 556ffa24..1c1fad3 100644
--- a/core/java/android/app/job/JobInfo.java
+++ b/core/java/android/app/job/JobInfo.java
@@ -1592,5 +1592,15 @@
}
return new JobInfo(this);
}
+
+ /**
+ * @hide
+ */
+ public String summarize() {
+ final String service = (mJobService != null)
+ ? mJobService.flattenToShortString()
+ : "null";
+ return "JobInfo.Builder{job:" + mJobId + "/" + service + "}";
+ }
}
}
diff --git a/core/java/android/bluetooth/BluetoothCodecConfig.java b/core/java/android/bluetooth/BluetoothCodecConfig.java
index 79c0a3a..c79df17 100644
--- a/core/java/android/bluetooth/BluetoothCodecConfig.java
+++ b/core/java/android/bluetooth/BluetoothCodecConfig.java
@@ -428,6 +428,43 @@
}
/**
+ * Checks whether a value set presented by a bitmask has zero or single bit
+ *
+ * @param valueSet the value set presented by a bitmask
+ * @return true if the valueSet contains zero or single bit, otherwise false.
+ */
+ private static boolean hasSingleBit(int valueSet) {
+ return (valueSet == 0 || (valueSet & (valueSet - 1)) == 0);
+ }
+
+ /**
+ * Checks whether the object contains none or single sample rate.
+ *
+ * @return true if the object contains none or single sample rate, otherwise false.
+ */
+ public boolean hasSingleSampleRate() {
+ return hasSingleBit(mSampleRate);
+ }
+
+ /**
+ * Checks whether the object contains none or single bits per sample.
+ *
+ * @return true if the object contains none or single bits per sample, otherwise false.
+ */
+ public boolean hasSingleBitsPerSample() {
+ return hasSingleBit(mBitsPerSample);
+ }
+
+ /**
+ * Checks whether the object contains none or single channel mode.
+ *
+ * @return true if the object contains none or single channel mode, otherwise false.
+ */
+ public boolean hasSingleChannelMode() {
+ return hasSingleBit(mChannelMode);
+ }
+
+ /**
* Checks whether the audio feeding parameters are same.
*
* @param other the codec config to compare against
@@ -438,4 +475,58 @@
&& other.mBitsPerSample == mBitsPerSample
&& other.mChannelMode == mChannelMode);
}
+
+ /**
+ * Checks whether another codec config has the similar feeding parameters.
+ * Any parameters with NONE value will be considered to be a wildcard matching.
+ *
+ * @param other the codec config to compare against
+ * @return true if the audio feeding parameters are similar, otherwise false.
+ */
+ public boolean similarCodecFeedingParameters(BluetoothCodecConfig other) {
+ if (other == null || mCodecType != other.mCodecType) {
+ return false;
+ }
+ int sampleRate = other.mSampleRate;
+ if (mSampleRate == BluetoothCodecConfig.SAMPLE_RATE_NONE
+ || sampleRate == BluetoothCodecConfig.SAMPLE_RATE_NONE) {
+ sampleRate = mSampleRate;
+ }
+ int bitsPerSample = other.mBitsPerSample;
+ if (mBitsPerSample == BluetoothCodecConfig.BITS_PER_SAMPLE_NONE
+ || bitsPerSample == BluetoothCodecConfig.BITS_PER_SAMPLE_NONE) {
+ bitsPerSample = mBitsPerSample;
+ }
+ int channelMode = other.mChannelMode;
+ if (mChannelMode == BluetoothCodecConfig.CHANNEL_MODE_NONE
+ || channelMode == BluetoothCodecConfig.CHANNEL_MODE_NONE) {
+ channelMode = mChannelMode;
+ }
+ return sameAudioFeedingParameters(new BluetoothCodecConfig(
+ mCodecType, /* priority */ 0, sampleRate, bitsPerSample, channelMode,
+ /* specific1 */ 0, /* specific2 */ 0, /* specific3 */ 0,
+ /* specific4 */ 0));
+ }
+
+ /**
+ * Checks whether the codec specific parameters are the same.
+ *
+ * @param other the codec config to compare against
+ * @return true if the codec specific parameters are the same, otherwise false.
+ */
+ public boolean sameCodecSpecificParameters(BluetoothCodecConfig other) {
+ if (other == null && mCodecType != other.mCodecType) {
+ return false;
+ }
+ // Currently we only care about the LDAC Playback Quality at CodecSpecific1
+ switch (mCodecType) {
+ case SOURCE_CODEC_TYPE_LDAC:
+ if (mCodecSpecific1 != other.mCodecSpecific1) {
+ return false;
+ }
+ // fall through
+ default:
+ return true;
+ }
+ }
}
diff --git a/core/java/android/bluetooth/BluetoothCodecStatus.java b/core/java/android/bluetooth/BluetoothCodecStatus.java
index 32bb681..8237d6a 100644
--- a/core/java/android/bluetooth/BluetoothCodecStatus.java
+++ b/core/java/android/bluetooth/BluetoothCodecStatus.java
@@ -88,6 +88,43 @@
return Arrays.asList(c1).containsAll(Arrays.asList(c2));
}
+ /**
+ * Checks whether the codec config matches the selectable capabilities.
+ * Any parameters of the codec config with NONE value will be considered a wildcard matching.
+ *
+ * @param codecConfig the codec config to compare against
+ * @return true if the codec config matches, otherwise false
+ */
+ public boolean isCodecConfigSelectable(BluetoothCodecConfig codecConfig) {
+ if (codecConfig == null || !codecConfig.hasSingleSampleRate()
+ || !codecConfig.hasSingleBitsPerSample() || !codecConfig.hasSingleChannelMode()) {
+ return false;
+ }
+ for (BluetoothCodecConfig selectableConfig : mCodecsSelectableCapabilities) {
+ if (codecConfig.getCodecType() != selectableConfig.getCodecType()) {
+ continue;
+ }
+ int sampleRate = codecConfig.getSampleRate();
+ if ((sampleRate & selectableConfig.getSampleRate()) == 0
+ && sampleRate != BluetoothCodecConfig.SAMPLE_RATE_NONE) {
+ continue;
+ }
+ int bitsPerSample = codecConfig.getBitsPerSample();
+ if ((bitsPerSample & selectableConfig.getBitsPerSample()) == 0
+ && bitsPerSample != BluetoothCodecConfig.BITS_PER_SAMPLE_NONE) {
+ continue;
+ }
+ int channelMode = codecConfig.getChannelMode();
+ if ((channelMode & selectableConfig.getChannelMode()) == 0
+ && channelMode != BluetoothCodecConfig.CHANNEL_MODE_NONE) {
+ continue;
+ }
+ return true;
+ }
+ return false;
+ }
+
+
@Override
public int hashCode() {
return Objects.hash(mCodecConfig, mCodecsLocalCapabilities,
diff --git a/core/java/android/bluetooth/BluetoothHeadsetClient.java b/core/java/android/bluetooth/BluetoothHeadsetClient.java
index 05833b5..5d00f09 100644
--- a/core/java/android/bluetooth/BluetoothHeadsetClient.java
+++ b/core/java/android/bluetooth/BluetoothHeadsetClient.java
@@ -126,6 +126,17 @@
"android.bluetooth.headsetclient.profile.action.RESULT";
/**
+ * Intent that notifies about vendor specific event arrival. Events not defined in
+ * HFP spec will be matched with supported vendor event list and this intent will
+ * be broadcasted upon a match. Supported vendor events are of format of
+ * of "+eventCode" or "+eventCode=xxxx" or "+eventCode:=xxxx".
+ * Vendor event can be a response to an vendor specific command or unsolicited.
+ *
+ */
+ public static final String ACTION_VENDOR_SPECIFIC_HEADSETCLIENT_EVENT =
+ "android.bluetooth.headsetclient.profile.action.VENDOR_SPECIFIC_EVENT";
+
+ /**
* Intent that notifies about the number attached to the last voice tag
* recorded on AG.
*
@@ -243,6 +254,28 @@
public static final String EXTRA_CME_CODE =
"android.bluetooth.headsetclient.extra.CME_CODE";
+ /**
+ * Extra for VENDOR_SPECIFIC_HEADSETCLIENT_EVENT intent that
+ * indicates vendor ID.
+ */
+ public static final String EXTRA_VENDOR_ID =
+ "android.bluetooth.headsetclient.extra.VENDOR_ID";
+
+ /**
+ * Extra for VENDOR_SPECIFIC_HEADSETCLIENT_EVENT intent that
+ * indicates vendor event code.
+ */
+ public static final String EXTRA_VENDOR_EVENT_CODE =
+ "android.bluetooth.headsetclient.extra.VENDOR_EVENT_CODE";
+
+ /**
+ * Extra for VENDOR_SPECIFIC_HEADSETCLIENT_EVENT intent that
+ * contains full vendor event including event code and full arguments.
+ */
+ public static final String EXTRA_VENDOR_EVENT_FULL_ARGS =
+ "android.bluetooth.headsetclient.extra.VENDOR_EVENT_FULL_ARGS";
+
+
/* Extras for AG_FEATURES, extras type is boolean */
// TODO verify if all of those are actually useful
/**
@@ -588,6 +621,31 @@
}
/**
+ * Send vendor specific AT command.
+ *
+ * @param device remote device
+ * @param vendorId vendor number by Bluetooth SIG
+ * @param atCommand command to be sent. It start with + prefix and only one command at one time.
+ * @return <code>true</code> if command has been issued successfully; <code>false</code>
+ * otherwise.
+ */
+ public boolean sendVendorAtCommand(BluetoothDevice device, int vendorId,
+ String atCommand) {
+ if (DBG) log("sendVendorSpecificCommand()");
+ final IBluetoothHeadsetClient service =
+ getService();
+ if (service != null && isEnabled() && isValidDevice(device)) {
+ try {
+ return service.sendVendorAtCommand(device, vendorId, atCommand);
+ } catch (RemoteException e) {
+ Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ }
+ }
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
+ return false;
+ }
+
+ /**
* Stops voice recognition.
*
* @param device remote device
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 99ff53d..7baca9b 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -71,6 +71,8 @@
import android.view.autofill.AutofillManager.AutofillClient;
import android.view.textclassifier.TextClassificationManager;
+import com.android.internal.compat.IPlatformCompat;
+
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
@@ -3037,6 +3039,7 @@
TELEPHONY_SERVICE,
TELEPHONY_SUBSCRIPTION_SERVICE,
CARRIER_CONFIG_SERVICE,
+ EUICC_SERVICE,
TELECOM_SERVICE,
CLIPBOARD_SERVICE,
INPUT_METHOD_SERVICE,
@@ -3063,6 +3066,7 @@
RESTRICTIONS_SERVICE,
APP_OPS_SERVICE,
CAMERA_SERVICE,
+ //@hide: PLATFORM_COMPAT_SERVICE,
PRINT_SERVICE,
CONSUMER_IR_SERVICE,
//@hide: TRUST_SERVICE,
@@ -3216,6 +3220,8 @@
* @see android.telephony.SubscriptionManager
* @see #CARRIER_CONFIG_SERVICE
* @see android.telephony.CarrierConfigManager
+ * @see #EUICC_SERVICE
+ * @see android.telephony.euicc.EuiccManager
* @see #INPUT_METHOD_SERVICE
* @see android.view.inputmethod.InputMethodManager
* @see #UI_MODE_SERVICE
@@ -4223,6 +4229,13 @@
public static final String STATS_MANAGER = "stats";
/**
+ * Use with {@link android.os.ServiceManager.getService()} to retrieve a
+ * {@link IPlatformCompat} IBinder for communicating with the platform compat service.
+ * @hide
+ */
+ public static final String PLATFORM_COMPAT_SERVICE = "platform_compat";
+
+ /**
* Service to capture a bugreport.
* @see #getSystemService(String)
* @see android.os.BugreportManager
diff --git a/core/java/android/content/pm/PackageList.java b/core/java/android/content/pm/PackageList.java
index f781758..e3eb2c5 100644
--- a/core/java/android/content/pm/PackageList.java
+++ b/core/java/android/content/pm/PackageList.java
@@ -52,6 +52,13 @@
}
@Override
+ public void onPackageChanged(String packageName, int uid) {
+ if (mWrappedObserver != null) {
+ mWrappedObserver.onPackageChanged(packageName, uid);
+ }
+ }
+
+ @Override
public void onPackageRemoved(String packageName, int uid) {
if (mWrappedObserver != null) {
mWrappedObserver.onPackageRemoved(packageName, uid);
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 4f7f07b..f7c9635 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -1949,6 +1949,30 @@
/**
* Feature for {@link #getSystemAvailableFeatures} and
+ * {@link #hasSystemFeature}: The device supports Open Mobile API capable UICC-based secure
+ * elements.
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_SE_OMAPI_UICC = "android.hardware.se.omapi.uicc";
+
+ /**
+ * Feature for {@link #getSystemAvailableFeatures} and
+ * {@link #hasSystemFeature}: The device supports Open Mobile API capable eSE-based secure
+ * elements.
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_SE_OMAPI_ESE = "android.hardware.se.omapi.ese";
+
+ /**
+ * Feature for {@link #getSystemAvailableFeatures} and
+ * {@link #hasSystemFeature}: The device supports Open Mobile API capable SD-based secure
+ * elements.
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_SE_OMAPI_SD = "android.hardware.se.omapi.sd";
+
+ /**
+ * Feature for {@link #getSystemAvailableFeatures} and
* {@link #hasSystemFeature}: The device supports the OpenGL ES
* <a href="http://www.khronos.org/registry/gles/extensions/ANDROID/ANDROID_extension_pack_es31a.txt">
* Android Extension Pack</a>.
diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java
index c299369..0694c5f 100644
--- a/core/java/android/content/pm/PackageManagerInternal.java
+++ b/core/java/android/content/pm/PackageManagerInternal.java
@@ -63,6 +63,8 @@
public interface PackageListObserver {
/** A package was added to the system. */
void onPackageAdded(@NonNull String packageName, int uid);
+ /** A package was changed - either installed for a specific user or updated. */
+ default void onPackageChanged(@NonNull String packageName, int uid) {}
/** A package was removed from the system. */
void onPackageRemoved(@NonNull String packageName, int uid);
}
diff --git a/core/java/android/content/res/CompatibilityInfo.java b/core/java/android/content/res/CompatibilityInfo.java
index 7d101b8..888380b 100644
--- a/core/java/android/content/res/CompatibilityInfo.java
+++ b/core/java/android/content/res/CompatibilityInfo.java
@@ -32,8 +32,8 @@
import android.view.WindowManager.LayoutParams;
/**
- * CompatibilityInfo class keeps the information about compatibility mode that the application is
- * running under.
+ * CompatibilityInfo class keeps the information about the screen compatibility mode that the
+ * application is running under.
*
* {@hide}
*/
diff --git a/core/java/android/net/LinkQualityInfo.java b/core/java/android/net/LinkQualityInfo.java
index b6f8825..78d7787 100644
--- a/core/java/android/net/LinkQualityInfo.java
+++ b/core/java/android/net/LinkQualityInfo.java
@@ -24,8 +24,8 @@
* Class that represents useful attributes of generic network links
* such as the upload/download throughput or packet error rate.
* Generally speaking, you should be dealing with instances of
- * LinkQualityInfo subclasses, such as {@link android.net.#WifiLinkQualityInfo}
- * or {@link android.net.#MobileLinkQualityInfo} which provide additional
+ * LinkQualityInfo subclasses, such as {@link android.net.WifiLinkQualityInfo}
+ * or {@link android.net.MobileLinkQualityInfo} which provide additional
* information.
* @hide
*/
diff --git a/core/java/android/net/MacAddress.java b/core/java/android/net/MacAddress.java
index c2b7d2c..52d485d 100644
--- a/core/java/android/net/MacAddress.java
+++ b/core/java/android/net/MacAddress.java
@@ -20,6 +20,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UnsupportedAppUsage;
+import android.net.wifi.WifiInfo;
import android.os.Parcel;
import android.os.Parcelable;
@@ -364,7 +365,12 @@
long addr = r.nextLong() & VALID_LONG_MASK;
addr |= LOCALLY_ASSIGNED_MASK;
addr &= ~MULTICAST_MASK;
- return new MacAddress(addr);
+ MacAddress mac = new MacAddress(addr);
+ // WifiInfo.DEFAULT_MAC_ADDRESS is being used for another purpose, so do not use it here.
+ if (mac.toString().equals(WifiInfo.DEFAULT_MAC_ADDRESS)) {
+ return createRandomUnicastAddress();
+ }
+ return mac;
}
/**
@@ -383,7 +389,12 @@
long addr = (base.mAddr & OUI_MASK) | (NIC_MASK & r.nextLong());
addr |= LOCALLY_ASSIGNED_MASK;
addr &= ~MULTICAST_MASK;
- return new MacAddress(addr);
+ MacAddress mac = new MacAddress(addr);
+ // WifiInfo.DEFAULT_MAC_ADDRESS is being used for another purpose, so do not use it here.
+ if (mac.toString().equals(WifiInfo.DEFAULT_MAC_ADDRESS)) {
+ return createRandomUnicastAddress(base, r);
+ }
+ return mac;
}
// Convenience function for working around the lack of byte literals.
diff --git a/core/java/android/net/util/MultinetworkPolicyTracker.java b/core/java/android/net/util/MultinetworkPolicyTracker.java
index 30c5cd9..f7e494d 100644
--- a/core/java/android/net/util/MultinetworkPolicyTracker.java
+++ b/core/java/android/net/util/MultinetworkPolicyTracker.java
@@ -16,29 +16,32 @@
package android.net.util;
+import static android.provider.Settings.Global.NETWORK_AVOID_BAD_WIFI;
+import static android.provider.Settings.Global.NETWORK_METERED_MULTIPATH_PREFERENCE;
+
+import android.annotation.NonNull;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.res.Resources;
import android.database.ContentObserver;
-import android.net.ConnectivityManager;
import android.net.Uri;
import android.os.Handler;
-import android.os.Message;
import android.os.UserHandle;
import android.provider.Settings;
+import android.telephony.PhoneStateListener;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
import android.util.Slog;
+import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
+
import java.util.Arrays;
import java.util.List;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.R;
-
-import static android.provider.Settings.Global.NETWORK_AVOID_BAD_WIFI;
-import static android.provider.Settings.Global.NETWORK_METERED_MULTIPATH_PREFERENCE;
-
/**
* A class to encapsulate management of the "Smart Networking" capability of
* avoiding bad Wi-Fi when, for example upstream connectivity is lost or
@@ -69,6 +72,7 @@
private volatile boolean mAvoidBadWifi = true;
private volatile int mMeteredMultipathPreference;
+ private int mActiveSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
public MultinetworkPolicyTracker(Context ctx, Handler handler) {
this(ctx, handler, null);
@@ -95,6 +99,14 @@
}
};
+ TelephonyManager.from(ctx).listen(new PhoneStateListener() {
+ @Override
+ public void onActiveDataSubscriptionIdChanged(int subId) {
+ mActiveSubId = subId;
+ reevaluate();
+ }
+ }, PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE);
+
updateAvoidBadWifi();
updateMeteredMultipathPreference();
}
@@ -131,7 +143,12 @@
* Whether the device or carrier configuration disables avoiding bad wifi by default.
*/
public boolean configRestrictsAvoidBadWifi() {
- return (mContext.getResources().getInteger(R.integer.config_networkAvoidBadWifi) == 0);
+ return (getResourcesForActiveSubId().getInteger(R.integer.config_networkAvoidBadWifi) == 0);
+ }
+
+ @NonNull
+ private Resources getResourcesForActiveSubId() {
+ return SubscriptionManager.getResourcesForSubId(mContext, mActiveSubId);
}
/**
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index 3de1183..bf17671 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -752,6 +752,8 @@
Log.w(TAG, "Caught a RuntimeException from the binder stub implementation.", e);
}
} else {
+ // Clear the parcel before writing the exception
+ reply.setDataSize(0);
reply.setDataPosition(0);
reply.writeException(e);
}
diff --git a/core/java/android/os/BugreportManager.java b/core/java/android/os/BugreportManager.java
index 32735aa..4583483 100644
--- a/core/java/android/os/BugreportManager.java
+++ b/core/java/android/os/BugreportManager.java
@@ -233,22 +233,5 @@
Binder.restoreCallingIdentity(identity);
}
}
-
- // Old methods; should go away
- @Override
- public void onProgressUpdated(int progress) throws RemoteException {
- // TODO(b/111441001): remove from interface
- }
-
- @Override
- public void onMaxProgressUpdated(int maxProgress) throws RemoteException {
- // TODO(b/111441001): remove from interface
- }
-
- @Override
- public void onSectionComplete(String title, int status, int size, int durationMs)
- throws RemoteException {
- // TODO(b/111441001): remove from interface
- }
}
}
diff --git a/core/java/android/os/RemoteCallbackList.java b/core/java/android/os/RemoteCallbackList.java
index b13e68d..0c3f291 100644
--- a/core/java/android/os/RemoteCallbackList.java
+++ b/core/java/android/os/RemoteCallbackList.java
@@ -123,6 +123,7 @@
IBinder binder = callback.asBinder();
try {
Callback cb = new Callback(callback, cookie);
+ unregister(callback);
binder.linkToDeath(cb, 0);
mCallbacks.put(binder, cb);
return true;
diff --git a/core/java/android/os/SharedMemory.java b/core/java/android/os/SharedMemory.java
index 6025c34..2ba3982 100644
--- a/core/java/android/os/SharedMemory.java
+++ b/core/java/android/os/SharedMemory.java
@@ -25,6 +25,8 @@
import dalvik.system.VMRuntime;
+import libcore.io.IoUtils;
+
import java.io.Closeable;
import java.io.FileDescriptor;
import java.nio.ByteBuffer;
@@ -62,7 +64,7 @@
mMemoryRegistration = new MemoryRegistration(mSize);
mCleaner = Cleaner.create(mFileDescriptor,
- new Closer(mFileDescriptor, mMemoryRegistration));
+ new Closer(mFileDescriptor.getInt$(), mMemoryRegistration));
}
/**
@@ -259,6 +261,9 @@
mCleaner.clean();
mCleaner = null;
}
+
+ // Cleaner.clean doesn't clear the value of the file descriptor.
+ mFileDescriptor.setInt$(-1);
}
@Override
@@ -290,19 +295,24 @@
* Cleaner that closes the FD
*/
private static final class Closer implements Runnable {
+ // This is a copy of the FileDescriptor we're attached to, in order to avoid a reference
+ // cycle.
private FileDescriptor mFd;
private MemoryRegistration mMemoryReference;
- private Closer(FileDescriptor fd, MemoryRegistration memoryReference) {
- mFd = fd;
+ private Closer(int fd, MemoryRegistration memoryReference) {
+ mFd = new FileDescriptor();
+ mFd.setInt$(fd);
+ IoUtils.setFdOwner(mFd, this);
+
mMemoryReference = memoryReference;
}
@Override
public void run() {
- try {
- Os.close(mFd);
- } catch (ErrnoException e) { /* swallow error */ }
+ IoUtils.closeQuietly(mFd);
+ mFd = null;
+
mMemoryReference.release();
mMemoryReference = null;
}
diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java
index e8f4641..d438103 100644
--- a/core/java/android/os/ZygoteProcess.java
+++ b/core/java/android/os/ZygoteProcess.java
@@ -648,6 +648,31 @@
}
/**
+ * Notify the Zygote processes that boot completed.
+ */
+ public void bootCompleted() {
+ // Notify both the 32-bit and 64-bit zygote.
+ if (Build.SUPPORTED_32_BIT_ABIS.length > 0) {
+ bootCompleted(Build.SUPPORTED_32_BIT_ABIS[0]);
+ }
+ if (Build.SUPPORTED_64_BIT_ABIS.length > 0) {
+ bootCompleted(Build.SUPPORTED_64_BIT_ABIS[0]);
+ }
+ }
+
+ private void bootCompleted(String abi) {
+ try {
+ synchronized (mLock) {
+ ZygoteState state = openZygoteSocketIfNeeded(abi);
+ state.mZygoteOutputWriter.write("1\n--boot-completed\n");
+ state.mZygoteOutputWriter.flush();
+ }
+ } catch (Exception ex) {
+ throw new RuntimeException("Failed to inform zygote of boot_completed", ex);
+ }
+ }
+
+ /**
* Push hidden API blacklisting exemptions into the zygote process(es).
*
* <p>The list of exemptions will take affect for all new processes forked from the zygote after
diff --git a/core/java/android/util/Half.java b/core/java/android/util/Half.java
index 70d049a..fe536a6 100644
--- a/core/java/android/util/Half.java
+++ b/core/java/android/util/Half.java
@@ -20,6 +20,8 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import libcore.util.FP16;
+
/**
* <p>The {@code Half} class is a wrapper and a utility class to manipulate half-precision 16-bit
* <a href="https://en.wikipedia.org/wiki/Half-precision_floating-point_format">IEEE 754</a>
@@ -148,25 +150,6 @@
*/
public static final @HalfFloat short POSITIVE_ZERO = (short) 0x0000;
- private static final int FP16_SIGN_SHIFT = 15;
- private static final int FP16_SIGN_MASK = 0x8000;
- private static final int FP16_EXPONENT_SHIFT = 10;
- private static final int FP16_EXPONENT_MASK = 0x1f;
- private static final int FP16_SIGNIFICAND_MASK = 0x3ff;
- private static final int FP16_EXPONENT_BIAS = 15;
- private static final int FP16_COMBINED = 0x7fff;
- private static final int FP16_EXPONENT_MAX = 0x7c00;
-
- private static final int FP32_SIGN_SHIFT = 31;
- private static final int FP32_EXPONENT_SHIFT = 23;
- private static final int FP32_EXPONENT_MASK = 0xff;
- private static final int FP32_SIGNIFICAND_MASK = 0x7fffff;
- private static final int FP32_EXPONENT_BIAS = 127;
- private static final int FP32_QNAN_MASK = 0x400000;
-
- private static final int FP32_DENORMAL_MAGIC = 126 << 23;
- private static final float FP32_DENORMAL_FLOAT = Float.intBitsToFloat(FP32_DENORMAL_MAGIC);
-
private final @HalfFloat short mValue;
/**
@@ -414,16 +397,7 @@
* than {@code y}
*/
public static int compare(@HalfFloat short x, @HalfFloat short y) {
- if (less(x, y)) return -1;
- if (greater(x, y)) return 1;
-
- // Collapse NaNs, akin to halfToIntBits(), but we want to keep
- // (signed) short value types to preserve the ordering of -0.0
- // and +0.0
- short xBits = (x & FP16_COMBINED) > FP16_EXPONENT_MAX ? NaN : x;
- short yBits = (y & FP16_COMBINED) > FP16_EXPONENT_MAX ? NaN : y;
-
- return (xBits == yBits ? 0 : (xBits < yBits ? -1 : 1));
+ return FP16.compare(x, y);
}
/**
@@ -440,7 +414,7 @@
* @see #halfToIntBits(short)
*/
public static @HalfFloat short halfToShortBits(@HalfFloat short h) {
- return (h & FP16_COMBINED) > FP16_EXPONENT_MAX ? NaN : h;
+ return (h & FP16.EXPONENT_SIGNIFICAND_MASK) > FP16.POSITIVE_INFINITY ? NaN : h;
}
/**
@@ -459,7 +433,7 @@
* @see #intBitsToHalf(int)
*/
public static int halfToIntBits(@HalfFloat short h) {
- return (h & FP16_COMBINED) > FP16_EXPONENT_MAX ? NaN : h & 0xffff;
+ return (h & FP16.EXPONENT_SIGNIFICAND_MASK) > FP16.POSITIVE_INFINITY ? NaN : h & 0xffff;
}
/**
@@ -505,7 +479,7 @@
* of the second parameter
*/
public static @HalfFloat short copySign(@HalfFloat short magnitude, @HalfFloat short sign) {
- return (short) ((sign & FP16_SIGN_MASK) | (magnitude & FP16_COMBINED));
+ return (short) ((sign & FP16.SIGN_MASK) | (magnitude & FP16.EXPONENT_SIGNIFICAND_MASK));
}
/**
@@ -523,7 +497,7 @@
* @return The absolute value of the specified half-precision float
*/
public static @HalfFloat short abs(@HalfFloat short h) {
- return (short) (h & FP16_COMBINED);
+ return (short) (h & FP16.EXPONENT_SIGNIFICAND_MASK);
}
/**
@@ -538,26 +512,18 @@
* the result is zero (with the same sign)</li>
* </ul>
*
+ * <p class=note>
+ * <strong>Note:</strong> Unlike the identically named
+ * <code class=prettyprint>int java.lang.Math.round(float)</code> method,
+ * this returns a Half value stored in a short, <strong>not</strong> an
+ * actual short integer result.
+ *
* @param h A half-precision float value
* @return The value of the specified half-precision float rounded to the nearest
* half-precision float value
*/
public static @HalfFloat short round(@HalfFloat short h) {
- int bits = h & 0xffff;
- int e = bits & 0x7fff;
- int result = bits;
-
- if (e < 0x3c00) {
- result &= FP16_SIGN_MASK;
- result |= (0x3c00 & (e >= 0x3800 ? 0xffff : 0x0));
- } else if (e < 0x6400) {
- e = 25 - (e >> 10);
- int mask = (1 << e) - 1;
- result += (1 << (e - 1));
- result &= ~mask;
- }
-
- return (short) result;
+ return FP16.rint(h);
}
/**
@@ -577,21 +543,7 @@
* greater than or equal to the specified half-precision float value
*/
public static @HalfFloat short ceil(@HalfFloat short h) {
- int bits = h & 0xffff;
- int e = bits & 0x7fff;
- int result = bits;
-
- if (e < 0x3c00) {
- result &= FP16_SIGN_MASK;
- result |= 0x3c00 & -(~(bits >> 15) & (e != 0 ? 1 : 0));
- } else if (e < 0x6400) {
- e = 25 - (e >> 10);
- int mask = (1 << e) - 1;
- result += mask & ((bits >> 15) - 1);
- result &= ~mask;
- }
-
- return (short) result;
+ return FP16.ceil(h);
}
/**
@@ -611,21 +563,7 @@
* less than or equal to the specified half-precision float value
*/
public static @HalfFloat short floor(@HalfFloat short h) {
- int bits = h & 0xffff;
- int e = bits & 0x7fff;
- int result = bits;
-
- if (e < 0x3c00) {
- result &= FP16_SIGN_MASK;
- result |= 0x3c00 & (bits > 0x8000 ? 0xffff : 0x0);
- } else if (e < 0x6400) {
- e = 25 - (e >> 10);
- int mask = (1 << e) - 1;
- result += mask & -(bits >> 15);
- result &= ~mask;
- }
-
- return (short) result;
+ return FP16.floor(h);
}
/**
@@ -644,19 +582,7 @@
* half-precision float value
*/
public static @HalfFloat short trunc(@HalfFloat short h) {
- int bits = h & 0xffff;
- int e = bits & 0x7fff;
- int result = bits;
-
- if (e < 0x3c00) {
- result &= FP16_SIGN_MASK;
- } else if (e < 0x6400) {
- e = 25 - (e >> 10);
- int mask = (1 << e) - 1;
- result &= ~mask;
- }
-
- return (short) result;
+ return FP16.trunc(h);
}
/**
@@ -672,15 +598,7 @@
* @return The smaller of the two specified half-precision values
*/
public static @HalfFloat short min(@HalfFloat short x, @HalfFloat short y) {
- if ((x & FP16_COMBINED) > FP16_EXPONENT_MAX) return NaN;
- if ((y & FP16_COMBINED) > FP16_EXPONENT_MAX) return NaN;
-
- if ((x & FP16_COMBINED) == 0 && (y & FP16_COMBINED) == 0) {
- return (x & FP16_SIGN_MASK) != 0 ? x : y;
- }
-
- return ((x & FP16_SIGN_MASK) != 0 ? 0x8000 - (x & 0xffff) : x & 0xffff) <
- ((y & FP16_SIGN_MASK) != 0 ? 0x8000 - (y & 0xffff) : y & 0xffff) ? x : y;
+ return FP16.min(x, y);
}
/**
@@ -697,15 +615,7 @@
* @return The larger of the two specified half-precision values
*/
public static @HalfFloat short max(@HalfFloat short x, @HalfFloat short y) {
- if ((x & FP16_COMBINED) > FP16_EXPONENT_MAX) return NaN;
- if ((y & FP16_COMBINED) > FP16_EXPONENT_MAX) return NaN;
-
- if ((x & FP16_COMBINED) == 0 && (y & FP16_COMBINED) == 0) {
- return (x & FP16_SIGN_MASK) != 0 ? y : x;
- }
-
- return ((x & FP16_SIGN_MASK) != 0 ? 0x8000 - (x & 0xffff) : x & 0xffff) >
- ((y & FP16_SIGN_MASK) != 0 ? 0x8000 - (y & 0xffff) : y & 0xffff) ? x : y;
+ return FP16.max(x, y);
}
/**
@@ -719,11 +629,7 @@
* @return True if x is less than y, false otherwise
*/
public static boolean less(@HalfFloat short x, @HalfFloat short y) {
- if ((x & FP16_COMBINED) > FP16_EXPONENT_MAX) return false;
- if ((y & FP16_COMBINED) > FP16_EXPONENT_MAX) return false;
-
- return ((x & FP16_SIGN_MASK) != 0 ? 0x8000 - (x & 0xffff) : x & 0xffff) <
- ((y & FP16_SIGN_MASK) != 0 ? 0x8000 - (y & 0xffff) : y & 0xffff);
+ return FP16.less(x, y);
}
/**
@@ -737,11 +643,7 @@
* @return True if x is less than or equal to y, false otherwise
*/
public static boolean lessEquals(@HalfFloat short x, @HalfFloat short y) {
- if ((x & FP16_COMBINED) > FP16_EXPONENT_MAX) return false;
- if ((y & FP16_COMBINED) > FP16_EXPONENT_MAX) return false;
-
- return ((x & FP16_SIGN_MASK) != 0 ? 0x8000 - (x & 0xffff) : x & 0xffff) <=
- ((y & FP16_SIGN_MASK) != 0 ? 0x8000 - (y & 0xffff) : y & 0xffff);
+ return FP16.lessEquals(x, y);
}
/**
@@ -755,11 +657,7 @@
* @return True if x is greater than y, false otherwise
*/
public static boolean greater(@HalfFloat short x, @HalfFloat short y) {
- if ((x & FP16_COMBINED) > FP16_EXPONENT_MAX) return false;
- if ((y & FP16_COMBINED) > FP16_EXPONENT_MAX) return false;
-
- return ((x & FP16_SIGN_MASK) != 0 ? 0x8000 - (x & 0xffff) : x & 0xffff) >
- ((y & FP16_SIGN_MASK) != 0 ? 0x8000 - (y & 0xffff) : y & 0xffff);
+ return FP16.greater(x, y);
}
/**
@@ -773,11 +671,7 @@
* @return True if x is greater than y, false otherwise
*/
public static boolean greaterEquals(@HalfFloat short x, @HalfFloat short y) {
- if ((x & FP16_COMBINED) > FP16_EXPONENT_MAX) return false;
- if ((y & FP16_COMBINED) > FP16_EXPONENT_MAX) return false;
-
- return ((x & FP16_SIGN_MASK) != 0 ? 0x8000 - (x & 0xffff) : x & 0xffff) >=
- ((y & FP16_SIGN_MASK) != 0 ? 0x8000 - (y & 0xffff) : y & 0xffff);
+ return FP16.greaterEquals(x, y);
}
/**
@@ -791,10 +685,7 @@
* @return True if x is equal to y, false otherwise
*/
public static boolean equals(@HalfFloat short x, @HalfFloat short y) {
- if ((x & FP16_COMBINED) > FP16_EXPONENT_MAX) return false;
- if ((y & FP16_COMBINED) > FP16_EXPONENT_MAX) return false;
-
- return x == y || ((x | y) & FP16_COMBINED) == 0;
+ return FP16.equals(x, y);
}
/**
@@ -804,7 +695,7 @@
* @return 1 if the value is positive, -1 if the value is negative
*/
public static int getSign(@HalfFloat short h) {
- return (h & FP16_SIGN_MASK) == 0 ? 1 : -1;
+ return (h & FP16.SIGN_MASK) == 0 ? 1 : -1;
}
/**
@@ -818,7 +709,7 @@
* @return The unbiased exponent of the specified value
*/
public static int getExponent(@HalfFloat short h) {
- return ((h >>> FP16_EXPONENT_SHIFT) & FP16_EXPONENT_MASK) - FP16_EXPONENT_BIAS;
+ return ((h >>> FP16.EXPONENT_SHIFT) & FP16.SHIFTED_EXPONENT_MASK) - FP16.EXPONENT_BIAS;
}
/**
@@ -829,7 +720,7 @@
* @return The significand, or significand, of the specified vlaue
*/
public static int getSignificand(@HalfFloat short h) {
- return h & FP16_SIGNIFICAND_MASK;
+ return h & FP16.SIGNIFICAND_MASK;
}
/**
@@ -841,7 +732,7 @@
* false otherwise
*/
public static boolean isInfinite(@HalfFloat short h) {
- return (h & FP16_COMBINED) == FP16_EXPONENT_MAX;
+ return FP16.isInfinite(h);
}
/**
@@ -852,7 +743,7 @@
* @return True if the value is a NaN, false otherwise
*/
public static boolean isNaN(@HalfFloat short h) {
- return (h & FP16_COMBINED) > FP16_EXPONENT_MAX;
+ return FP16.isNaN(h);
}
/**
@@ -866,7 +757,7 @@
* @return True if the value is normalized, false otherwise
*/
public static boolean isNormalized(@HalfFloat short h) {
- return (h & FP16_EXPONENT_MAX) != 0 && (h & FP16_EXPONENT_MAX) != FP16_EXPONENT_MAX;
+ return FP16.isNormalized(h);
}
/**
@@ -885,35 +776,7 @@
* @return A normalized single-precision float value
*/
public static float toFloat(@HalfFloat short h) {
- int bits = h & 0xffff;
- int s = bits & FP16_SIGN_MASK;
- int e = (bits >>> FP16_EXPONENT_SHIFT) & FP16_EXPONENT_MASK;
- int m = (bits ) & FP16_SIGNIFICAND_MASK;
-
- int outE = 0;
- int outM = 0;
-
- if (e == 0) { // Denormal or 0
- if (m != 0) {
- // Convert denorm fp16 into normalized fp32
- float o = Float.intBitsToFloat(FP32_DENORMAL_MAGIC + m);
- o -= FP32_DENORMAL_FLOAT;
- return s == 0 ? o : -o;
- }
- } else {
- outM = m << 13;
- if (e == 0x1f) { // Infinite or NaN
- outE = 0xff;
- if (outM != 0) { // SNaNs are quieted
- outM |= FP32_QNAN_MASK;
- }
- } else {
- outE = e - FP16_EXPONENT_BIAS + FP32_EXPONENT_BIAS;
- }
- }
-
- int out = (s << 16) | (outE << FP32_EXPONENT_SHIFT) | outM;
- return Float.intBitsToFloat(out);
+ return FP16.toFloat(h);
}
/**
@@ -940,44 +803,7 @@
*/
@SuppressWarnings("StatementWithEmptyBody")
public static @HalfFloat short toHalf(float f) {
- int bits = Float.floatToRawIntBits(f);
- int s = (bits >>> FP32_SIGN_SHIFT );
- int e = (bits >>> FP32_EXPONENT_SHIFT) & FP32_EXPONENT_MASK;
- int m = (bits ) & FP32_SIGNIFICAND_MASK;
-
- int outE = 0;
- int outM = 0;
-
- if (e == 0xff) { // Infinite or NaN
- outE = 0x1f;
- outM = m != 0 ? 0x200 : 0;
- } else {
- e = e - FP32_EXPONENT_BIAS + FP16_EXPONENT_BIAS;
- if (e >= 0x1f) { // Overflow
- outE = 0x31;
- } else if (e <= 0) { // Underflow
- if (e < -10) {
- // The absolute fp32 value is less than MIN_VALUE, flush to +/-0
- } else {
- // The fp32 value is a normalized float less than MIN_NORMAL,
- // we convert to a denorm fp16
- m = (m | 0x800000) >> (1 - e);
- if ((m & 0x1000) != 0) m += 0x2000;
- outM = m >> 13;
- }
- } else {
- outE = e;
- outM = m >> 13;
- if ((m & 0x1000) != 0) {
- // Round to nearest "0.5" up
- int out = (outE << FP16_EXPONENT_SHIFT) | outM;
- out++;
- return (short) (out | (s << FP16_SIGN_SHIFT));
- }
- }
- }
-
- return (short) ((s << FP16_SIGN_SHIFT) | (outE << FP16_EXPONENT_SHIFT) | outM);
+ return FP16.toHalf(f);
}
/**
@@ -1073,40 +899,6 @@
*/
@NonNull
public static String toHexString(@HalfFloat short h) {
- StringBuilder o = new StringBuilder();
-
- int bits = h & 0xffff;
- int s = (bits >>> FP16_SIGN_SHIFT );
- int e = (bits >>> FP16_EXPONENT_SHIFT) & FP16_EXPONENT_MASK;
- int m = (bits ) & FP16_SIGNIFICAND_MASK;
-
- if (e == 0x1f) { // Infinite or NaN
- if (m == 0) {
- if (s != 0) o.append('-');
- o.append("Infinity");
- } else {
- o.append("NaN");
- }
- } else {
- if (s == 1) o.append('-');
- if (e == 0) {
- if (m == 0) {
- o.append("0x0.0p0");
- } else {
- o.append("0x0.");
- String significand = Integer.toHexString(m);
- o.append(significand.replaceFirst("0{2,}$", ""));
- o.append("p-14");
- }
- } else {
- o.append("0x1.");
- String significand = Integer.toHexString(m);
- o.append(significand.replaceFirst("0{2,}$", ""));
- o.append('p');
- o.append(Integer.toString(e - FP16_EXPONENT_BIAS));
- }
- }
-
- return o.toString();
+ return FP16.toHexString(h);
}
}
diff --git a/core/java/android/widget/NumberPicker.java b/core/java/android/widget/NumberPicker.java
index b6ed22c..6f7d456 100644
--- a/core/java/android/widget/NumberPicker.java
+++ b/core/java/android/widget/NumberPicker.java
@@ -905,10 +905,12 @@
if (!mFlingScroller.isFinished()) {
mFlingScroller.forceFinished(true);
mAdjustScroller.forceFinished(true);
+ onScrollerFinished(mFlingScroller);
onScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);
} else if (!mAdjustScroller.isFinished()) {
mFlingScroller.forceFinished(true);
mAdjustScroller.forceFinished(true);
+ onScrollerFinished(mAdjustScroller);
} else if (mLastDownEventY < mTopSelectionDividerTop) {
postChangeCurrentByOneFromLongPress(
false, ViewConfiguration.getLongPressTimeout());
diff --git a/core/java/com/android/internal/app/AssistUtils.java b/core/java/com/android/internal/app/AssistUtils.java
index fde01dd..750d698 100644
--- a/core/java/com/android/internal/app/AssistUtils.java
+++ b/core/java/com/android/internal/app/AssistUtils.java
@@ -195,7 +195,7 @@
return applicationInfo.isSystemApp() || applicationInfo.isUpdatedSystemApp();
}
- private static boolean isDisclosureEnabled(Context context) {
+ public static boolean isDisclosureEnabled(Context context) {
return Settings.Secure.getInt(context.getContentResolver(),
Settings.Secure.ASSIST_DISCLOSURE_ENABLED, 0) != 0;
}
diff --git a/core/java/com/android/internal/compat/IPlatformCompat.aidl b/core/java/com/android/internal/compat/IPlatformCompat.aidl
new file mode 100644
index 0000000..9049c3a
--- /dev/null
+++ b/core/java/com/android/internal/compat/IPlatformCompat.aidl
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.compat;
+
+import android.content.pm.ApplicationInfo;
+
+/**
+ * Platform private API for talking with the PlatformCompat service.
+ *
+ * <p> Should be used for gating and logging from non-app processes.
+ * For app processes please use android.compat.Compatibility API.
+ *
+ * {@hide}
+ */
+interface IPlatformCompat
+{
+
+ /**
+ * Reports that a compatibility change is affecting an app process now.
+ *
+ * <p>Note: for changes that are gated using {@link #isChangeEnabled(long, ApplicationInfo)},
+ * you do not need to call this API directly. The change will be reported for you in the case
+ * that {@link #isChangeEnabled(long, ApplicationInfo)} returns {@code true}.
+ *
+ * @param changeId The ID of the compatibility change taking effect.
+ * @param appInfo Representing the affected app.
+ */
+ void reportChange(long changeId, in ApplicationInfo appInfo);
+
+ /**
+ * Query if a given compatibility change is enabled for an app process. This method should
+ * be called when implementing functionality on behalf of the affected app.
+ *
+ * <p>If this method returns {@code true}, the calling code should implement the compatibility
+ * change, resulting in differing behaviour compared to earlier releases. If this method returns
+ * {@code false}, the calling code should behave as it did in earlier releases.
+ *
+ * <p>When this method returns {@code true}, it will also report the change as
+ * {@link #reportChange(long, ApplicationInfo)} would, so there is no need to call that method
+ * directly.
+ *
+ * @param changeId The ID of the compatibility change in question.
+ * @param appInfo Representing the app in question.
+ * @return {@code true} if the change is enabled for the current app.
+ */
+ boolean isChangeEnabled(long changeId, in ApplicationInfo appInfo);
+}
\ No newline at end of file
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 274c444..a47beb5 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -4938,11 +4938,11 @@
final long uptime = mClocks.uptimeMillis();
boolean updateHistory = false;
- if (isScreenDoze(state)) {
+ if (isScreenDoze(state) && !isScreenDoze(oldState)) {
mHistoryCur.states |= HistoryItem.STATE_SCREEN_DOZE_FLAG;
mScreenDozeTimer.startRunningLocked(elapsedRealtime);
updateHistory = true;
- } else if (isScreenDoze(oldState)) {
+ } else if (isScreenDoze(oldState) && !isScreenDoze(state)) {
mHistoryCur.states &= ~HistoryItem.STATE_SCREEN_DOZE_FLAG;
mScreenDozeTimer.stopRunningLocked(elapsedRealtime);
updateHistory = true;
diff --git a/core/java/com/android/internal/os/KernelWakelockReader.java b/core/java/com/android/internal/os/KernelWakelockReader.java
index df8c6d0..b3b0ba1 100644
--- a/core/java/com/android/internal/os/KernelWakelockReader.java
+++ b/core/java/com/android/internal/os/KernelWakelockReader.java
@@ -27,6 +27,7 @@
import com.android.internal.annotations.VisibleForTesting;
+import java.io.File;
import java.io.FileInputStream;
import java.util.Iterator;
@@ -38,6 +39,7 @@
private static int sKernelWakelockUpdateVersion = 0;
private static final String sWakelockFile = "/proc/wakelocks";
private static final String sWakeupSourceFile = "/d/wakeup_sources";
+ private static final String sSysClassWakeupDir = "/sys/class/wakeup";
private static final int[] PROC_WAKELOCKS_FORMAT = new int[] {
Process.PROC_TAB_TERM|Process.PROC_OUT_STRING| // 0: name
@@ -71,99 +73,125 @@
* @return the updated data.
*/
public final KernelWakelockStats readKernelWakelockStats(KernelWakelockStats staleStats) {
- byte[] buffer = new byte[32*1024];
- int len = 0;
- boolean wakeup_sources;
- final long startTime = SystemClock.uptimeMillis();
+ boolean useSystemSuspend = (new File(sSysClassWakeupDir)).exists();
- final int oldMask = StrictMode.allowThreadDiskReadsMask();
- try {
- FileInputStream is;
+ if (useSystemSuspend) {
+ // Get both kernel and native wakelock stats from SystemSuspend
+ updateVersion(staleStats);
+ if (getWakelockStatsFromSystemSuspend(staleStats) == null) {
+ Slog.w(TAG, "Failed to get wakelock stats from SystemSuspend");
+ return null;
+ }
+ return removeOldStats(staleStats);
+ } else {
+ byte[] buffer = new byte[32*1024];
+ int len = 0;
+ boolean wakeup_sources;
+ final long startTime = SystemClock.uptimeMillis();
+
+ final int oldMask = StrictMode.allowThreadDiskReadsMask();
try {
- is = new FileInputStream(sWakelockFile);
- wakeup_sources = false;
- } catch (java.io.FileNotFoundException e) {
+ FileInputStream is;
try {
- is = new FileInputStream(sWakeupSourceFile);
- wakeup_sources = true;
- } catch (java.io.FileNotFoundException e2) {
- Slog.wtf(TAG, "neither " + sWakelockFile + " nor " +
- sWakeupSourceFile + " exists");
- return null;
+ is = new FileInputStream(sWakelockFile);
+ wakeup_sources = false;
+ } catch (java.io.FileNotFoundException e) {
+ try {
+ is = new FileInputStream(sWakeupSourceFile);
+ wakeup_sources = true;
+ } catch (java.io.FileNotFoundException e2) {
+ Slog.wtf(TAG, "neither " + sWakelockFile + " nor " +
+ sWakeupSourceFile + " exists");
+ return null;
+ }
+ }
+
+ int cnt;
+ while ((cnt = is.read(buffer, len, buffer.length - len)) > 0) {
+ len += cnt;
+ }
+
+ is.close();
+ } catch (java.io.IOException e) {
+ Slog.wtf(TAG, "failed to read kernel wakelocks", e);
+ return null;
+ } finally {
+ StrictMode.setThreadPolicyMask(oldMask);
+ }
+
+ final long readTime = SystemClock.uptimeMillis() - startTime;
+ if (readTime > 100) {
+ Slog.w(TAG, "Reading wakelock stats took " + readTime + "ms");
+ }
+
+ if (len > 0) {
+ if (len >= buffer.length) {
+ Slog.wtf(TAG, "Kernel wake locks exceeded buffer size " + buffer.length);
+ }
+ int i;
+ for (i=0; i<len; i++) {
+ if (buffer[i] == '\0') {
+ len = i;
+ break;
+ }
}
}
- int cnt;
- while ((cnt = is.read(buffer, len, buffer.length - len)) > 0) {
- len += cnt;
+ updateVersion(staleStats);
+ // Get native wakelock stats from SystemSuspend
+ if (getWakelockStatsFromSystemSuspend(staleStats) == null) {
+ Slog.w(TAG, "Failed to get Native wakelock stats from SystemSuspend");
}
-
- is.close();
- } catch (java.io.IOException e) {
- Slog.wtf(TAG, "failed to read kernel wakelocks", e);
- return null;
- } finally {
- StrictMode.setThreadPolicyMask(oldMask);
+ // Get kernel wakelock stats
+ parseProcWakelocks(buffer, len, wakeup_sources, staleStats);
+ return removeOldStats(staleStats);
}
+ }
- final long readTime = SystemClock.uptimeMillis() - startTime;
- if (readTime > 100) {
- Slog.w(TAG, "Reading wakelock stats took " + readTime + "ms");
- }
-
- if (len > 0) {
- if (len >= buffer.length) {
- Slog.wtf(TAG, "Kernel wake locks exceeded buffer size " + buffer.length);
- }
- int i;
- for (i=0; i<len; i++) {
- if (buffer[i] == '\0') {
- len = i;
- break;
- }
- }
- }
-
- updateVersion(staleStats);
-
- parseProcWakelocks(buffer, len, wakeup_sources, staleStats);
-
+ /**
+ * On success, returns the updated stats from SystemSupend, else returns null.
+ */
+ private KernelWakelockStats getWakelockStatsFromSystemSuspend(
+ final KernelWakelockStats staleStats) {
+ WakeLockInfo[] wlStats = null;
if (mSuspendControlService == null) {
try {
mSuspendControlService = ISuspendControlService.Stub.asInterface(
ServiceManager.getServiceOrThrow("suspend_control"));
} catch (ServiceNotFoundException e) {
Slog.wtf(TAG, "Required service suspend_control not available", e);
+ return null;
}
}
try {
- WakeLockInfo[] wlStats = mSuspendControlService.getWakeLockStats();
- getNativeWakelockStats(wlStats, staleStats);
+ wlStats = mSuspendControlService.getWakeLockStats();
+ updateWakelockStats(wlStats, staleStats);
} catch (RemoteException e) {
Slog.wtf(TAG, "Failed to obtain wakelock stats from ISuspendControlService", e);
+ return null;
}
- return removeOldStats(staleStats);
+ return staleStats;
}
/**
- * Reads native wakelock stats from SystemSuspend and updates staleStats with the new
- * information.
+ * Updates statleStats with stats from SystemSuspend.
* @param staleStats Existing object to update.
* @return the updated stats.
*/
@VisibleForTesting
- public KernelWakelockStats getNativeWakelockStats(WakeLockInfo[] wlStats,
+ public KernelWakelockStats updateWakelockStats(WakeLockInfo[] wlStats,
final KernelWakelockStats staleStats) {
for (WakeLockInfo info : wlStats) {
if (!staleStats.containsKey(info.name)) {
staleStats.put(info.name, new KernelWakelockStats.Entry((int) info.activeCount,
- info.totalTime, sKernelWakelockUpdateVersion));
+ info.totalTime * 1000 /* ms to us */, sKernelWakelockUpdateVersion));
} else {
KernelWakelockStats.Entry kwlStats = staleStats.get(info.name);
kwlStats.mCount = (int) info.activeCount;
- kwlStats.mTotalTime = info.totalTime;
+ // Convert milliseconds to microseconds
+ kwlStats.mTotalTime = info.totalTime * 1000;
kwlStats.mVersion = sKernelWakelockUpdateVersion;
}
}
diff --git a/core/java/com/android/internal/os/RuntimeInit.java b/core/java/com/android/internal/os/RuntimeInit.java
index 1de2e72..5b129f4 100644
--- a/core/java/com/android/internal/os/RuntimeInit.java
+++ b/core/java/com/android/internal/os/RuntimeInit.java
@@ -20,6 +20,7 @@
import android.app.ActivityManager;
import android.app.ActivityThread;
import android.app.ApplicationErrorReport;
+import android.content.type.MimeMapImpl;
import android.os.Build;
import android.os.DeadObjectException;
import android.os.Debug;
@@ -33,6 +34,9 @@
import com.android.server.NetworkManagementSocketTagger;
import dalvik.system.RuntimeHooks;
import dalvik.system.VMRuntime;
+
+import libcore.net.MimeMap;
+
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
@@ -210,6 +214,14 @@
RuntimeHooks.setTimeZoneIdSupplier(() -> SystemProperties.get("persist.sys.timezone"));
/*
+ * Replace libcore's minimal default mapping between MIME types and file
+ * extensions with a mapping that's suitable for Android. Android's mapping
+ * contains many more entries that are derived from IANA registrations but
+ * with several customizations (extensions, overrides).
+ */
+ MimeMap.setDefault(MimeMapImpl.createDefaultInstance());
+
+ /*
* Sets handler for java.util.logging to use Android log facilities.
* The odd "new instance-and-then-throw-away" is a mirror of how
* the "java.util.logging.config.class" system property works. We
diff --git a/core/java/com/android/internal/os/ZygoteArguments.java b/core/java/com/android/internal/os/ZygoteArguments.java
index af90b15..c64103f 100644
--- a/core/java/com/android/internal/os/ZygoteArguments.java
+++ b/core/java/com/android/internal/os/ZygoteArguments.java
@@ -169,6 +169,11 @@
boolean mPidQuery;
/**
+ * Whether the current arguments constitute a notification that boot completed.
+ */
+ boolean mBootCompleted;
+
+ /**
* Exemptions from API blacklisting. These are sent to the pre-forked zygote at boot time, or
* when they change, via --set-api-blacklist-exemptions.
*/
@@ -330,6 +335,8 @@
mAbiListQuery = true;
} else if (arg.equals("--get-pid")) {
mPidQuery = true;
+ } else if (arg.equals("--boot-completed")) {
+ mBootCompleted = true;
} else if (arg.startsWith("--instruction-set=")) {
mInstructionSet = arg.substring(arg.indexOf('=') + 1);
} else if (arg.startsWith("--app-data-dir=")) {
@@ -364,7 +371,11 @@
}
}
- if (mAbiListQuery || mPidQuery) {
+ if (mBootCompleted) {
+ if (args.length - curArg > 0) {
+ throw new IllegalArgumentException("Unexpected arguments after --boot-completed");
+ }
+ } else if (mAbiListQuery || mPidQuery) {
if (args.length - curArg > 0) {
throw new IllegalArgumentException("Unexpected arguments after --query-abi-list.");
}
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index ad9f64c..fc25a47 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -149,6 +149,11 @@
parsedArgs = new ZygoteArguments(args);
+ if (parsedArgs.mBootCompleted) {
+ handleBootCompleted();
+ return null;
+ }
+
if (parsedArgs.mAbiListQuery) {
handleAbiListQuery();
return null;
@@ -291,6 +296,10 @@
}
}
+ private void handleBootCompleted() {
+ VMRuntime.bootCompleted();
+ }
+
/**
* Preloads resources if the zygote is in lazily preload mode. Writes the result of the
* preload operation; {@code 0} when a preload was initiated due to this request and {@code 1}
diff --git a/core/java/com/android/internal/util/TrafficStatsConstants.java b/core/java/com/android/internal/util/TrafficStatsConstants.java
index 2806ae2..413be48 100644
--- a/core/java/com/android/internal/util/TrafficStatsConstants.java
+++ b/core/java/com/android/internal/util/TrafficStatsConstants.java
@@ -40,4 +40,5 @@
// {@link android.net.TrafficStats#TAG_NETWORK_STACK_IMPERSONATION_RANGE_START} and
// {@link android.net.TrafficStats#TAG_NETWORK_STACK_IMPERSONATION_RANGE_END}.
public static final int TAG_SYSTEM_PROBE = 0xFFFFFF81;
+ public static final int TAG_SYSTEM_DNS = 0xFFFFFF82;
}
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 0da314b..6c351fe 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -232,6 +232,9 @@
// Copying (CC) garbage collector.
static const char* kNoGenerationalCCRuntimeOption = "-Xgc:nogenerational_cc";
+// Phenotype property name for enabling profiling the boot class path.
+static const char* PROFILE_BOOT_CLASS_PATH = "profilebootclasspath";
+
// Feature flag name for running the JIT in Zygote experiment, b/119800099.
static const char* ENABLE_APEX_IMAGE = "enable_apex_image";
// Flag to pass to the runtime when using the apex image.
@@ -677,6 +680,24 @@
char jdwpProviderBuf[sizeof("-XjdwpProvider:") - 1 + PROPERTY_VALUE_MAX];
char bootImageBuf[sizeof("-Ximage:") - 1 + PROPERTY_VALUE_MAX];
+ // Read if we are using the profile configuration, do this at the start since the last ART args
+ // take precedence.
+ property_get("dalvik.vm.profilebootclasspath", propBuf, "");
+ std::string profile_boot_class_path = propBuf;
+ // Empty means the property is unset and we should default to the phenotype property.
+ // The possible values are {"true", "false", ""}
+ if (profile_boot_class_path.empty()) {
+ profile_boot_class_path = server_configurable_flags::GetServerConfigurableFlag(
+ RUNTIME_NATIVE_BOOT_NAMESPACE,
+ PROFILE_BOOT_CLASS_PATH,
+ /*default_value=*/ "");
+ }
+ if (profile_boot_class_path == "true") {
+ addOption("-Xps-profile-boot-class-path");
+ addOption("-Xps-profile-aot-code");
+ addOption("-Xjitsaveprofilinginfo");
+ }
+
std::string use_apex_image =
server_configurable_flags::GetServerConfigurableFlag(RUNTIME_NATIVE_BOOT_NAMESPACE,
ENABLE_APEX_IMAGE,
@@ -794,13 +815,6 @@
parseRuntimeOption("dalvik.vm.jittransitionweight",
jittransitionweightOptBuf,
"-Xjittransitionweight:");
-
- property_get("dalvik.vm.profilebootimage", propBuf, "");
- if (strcmp(propBuf, "true") == 0) {
- addOption("-Xps-profile-boot-class-path");
- addOption("-Xps-profile-aot-code");
- }
-
/*
* Madvise related options.
*/
diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp
index 62c4d76..bbd8ffe 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -306,6 +306,8 @@
whichHeap = HEAP_NATIVE;
} else if (strncmp(name, "[stack", 6) == 0) {
whichHeap = HEAP_STACK;
+ } else if (strncmp(name, "[anon:stack_and_tls:", 20) == 0) {
+ whichHeap = HEAP_STACK;
} else if (nameLen > 3 && strcmp(name+nameLen-3, ".so") == 0) {
whichHeap = HEAP_SO;
is_swappable = true;
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 614a8ff..04b0609 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -308,7 +308,8 @@
buffer->getHeight(),
buffer->getPixelFormat(),
(jint)buffer->getUsage(),
- (jlong)buffer.get());
+ (jlong)buffer.get(),
+ false /* capturedSecureLayers */);
}
static void nativeApplyTransaction(JNIEnv* env, jclass clazz, jlong transactionObj, jboolean sync) {
diff --git a/core/proto/OWNERS b/core/proto/OWNERS
index 480b1ea..d31df38 100644
--- a/core/proto/OWNERS
+++ b/core/proto/OWNERS
@@ -12,7 +12,7 @@
yro@google.com
# Settings UI
-per-file settings_enums.proto=zhfan@google.com
+per-file settings_enums.proto=tmfang@google.com
# Frameworks
ogunwale@google.com
@@ -20,3 +20,6 @@
# Launcher
hyunyoungs@google.com
+
+# Graphics stats
+jreck@google.com
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 1fce5be..111f937 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1172,6 +1172,9 @@
<bool name="config_use_strict_phone_number_comparation">false</bool>
+ <!-- The character count of the minimum match for comparison phone numbers -->
+ <integer name="config_phonenumber_compare_min_match">7</integer>
+
<!-- Display low battery warning when battery level dips to this value.
Also, the battery stats are flushed to disk when we hit this level. -->
<integer name="config_criticalBatteryWarningLevel">5</integer>
@@ -3347,8 +3350,10 @@
<!-- Flag indicates that whether non-system apps can be installed on internal storage. -->
<bool name="config_allow3rdPartyAppOnInternal">true</bool>
- <!-- Package name of the default cell broadcast receiver -->
- <string name="config_defaultCellBroadcastReceiverPkg" translatable="false">com.android.cellbroadcastreceiver</string>
+ <!-- Package names of the default cell broadcast receivers -->
+ <string-array name="config_defaultCellBroadcastReceiverPkgs" translatable="false">
+ <item>com.android.cellbroadcastreceiver</item>
+ </string-array>
<!-- Specifies the path that is used by AdaptiveIconDrawable class to crop launcher icons. -->
<string name="config_icon_mask" translatable="false">"M50,0L92,0C96.42,0 100,4.58 100 8L100,92C100, 96.42 96.42 100 92 100L8 100C4.58, 100 0 96.42 0 92L0 8 C 0 4.42 4.42 0 8 0L50 0Z"</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index e8b2b1f..00808df 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -290,6 +290,7 @@
<java-symbol type="bool" name="config_ui_enableFadingMarquee" />
<java-symbol type="bool" name="config_enableHapticTextHandle" />
<java-symbol type="bool" name="config_use_strict_phone_number_comparation" />
+ <java-symbol type="integer" name="config_phonenumber_compare_min_match" />
<java-symbol type="bool" name="config_single_volume" />
<java-symbol type="bool" name="config_voice_capable" />
<java-symbol type="bool" name="config_requireCallCapableAccountForHandle" />
@@ -3035,7 +3036,7 @@
<java-symbol type="drawable" name="lockscreen_selected" />
<java-symbol type="string" name="notification_header_divider_symbol_with_spaces" />
- <java-symbol type="string" name="config_defaultCellBroadcastReceiverPkg" />
+ <java-symbol type="array" name="config_defaultCellBroadcastReceiverPkgs" />
<java-symbol type="color" name="notification_primary_text_color_light" />
<java-symbol type="color" name="notification_primary_text_color_dark" />
diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp
index a909487..2fa4487 100644
--- a/core/tests/coretests/Android.bp
+++ b/core/tests/coretests/Android.bp
@@ -55,6 +55,8 @@
resource_dirs: ["res"],
resource_zips: [":FrameworksCoreTests_apks_as_resources"],
+
+ data: [":BstatsTestApp"],
}
// Rules to copy all the test apks to the intermediate raw resource directory
diff --git a/core/tests/coretests/BstatsTestApp/Android.bp b/core/tests/coretests/BstatsTestApp/Android.bp
index 424c71a..a89d728 100644
--- a/core/tests/coretests/BstatsTestApp/Android.bp
+++ b/core/tests/coretests/BstatsTestApp/Android.bp
@@ -15,10 +15,6 @@
android_test_helper_app {
name: "BstatsTestApp",
- test_suites: [
- "device-tests",
- ],
-
static_libs: ["coretests-aidl"],
srcs: ["**/*.java"],
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
index d922c16..b9f5ef9 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
@@ -412,7 +412,8 @@
IUiAutomationConnection iUiAutomationConnection, int i, boolean b, boolean b1,
boolean b2, boolean b3, Configuration configuration,
CompatibilityInfo compatibilityInfo, Map map, Bundle bundle1, String s1,
- boolean autofillCompatEnabled) throws RemoteException {
+ boolean autofillCompatEnabled, long[] disableCompatChanges)
+ throws RemoteException {
}
@Override
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelWakelockReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelWakelockReaderTest.java
index 008085e..dc9208d 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelWakelockReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelWakelockReaderTest.java
@@ -68,12 +68,7 @@
private WakeLockInfo createWakeLockInfo(String name, int activeCount, long totalTime) {
WakeLockInfo info = new WakeLockInfo();
info.name = name;
- info.pid = 1;
info.activeCount = activeCount;
- info.isActive = true;
- info.activeSince = 0;
- info.lastChange = 0;
- info.maxTime = 0;
info.totalTime = totalTime;
return info;
}
@@ -89,7 +84,7 @@
byte[] buffer, WakeLockInfo[] wlStats) {
mReader.updateVersion(staleStats);
mReader.parseProcWakelocks(buffer, buffer.length, true, staleStats);
- mReader.getNativeWakelockStats(wlStats, staleStats);
+ mReader.updateWakelockStats(wlStats, staleStats);
return mReader.removeOldStats(staleStats);
}
@@ -101,7 +96,7 @@
mReader = new KernelWakelockReader();
}
-// ------------------------- Kernel Wakelock Stats Test ------------------------
+// ------------------------- Legacy Wakelock Stats Test ------------------------
@SmallTest
public void testParseEmptyFile() throws Exception {
KernelWakelockStats staleStats = mReader.parseProcWakelocks(new byte[0], 0, true,
@@ -196,10 +191,10 @@
assertFalse(staleStats.containsKey("Fakelock"));
}
-// -------------------- Native (SystemSuspend) Wakelock Stats Test -------------------
+// -------------------- SystemSuspend Wakelock Stats Test -------------------
@SmallTest
public void testEmptyWakeLockInfoList() {
- KernelWakelockStats staleStats = mReader.getNativeWakelockStats(new WakeLockInfo[0],
+ KernelWakelockStats staleStats = mReader.updateWakelockStats(new WakeLockInfo[0],
new KernelWakelockStats());
assertTrue(staleStats.isEmpty());
@@ -208,9 +203,9 @@
@SmallTest
public void testOneWakeLockInfo() {
WakeLockInfo[] wlStats = new WakeLockInfo[1];
- wlStats[0] = createWakeLockInfo("WakeLock", 20, 10000);
+ wlStats[0] = createWakeLockInfo("WakeLock", 20, 1000); // Milliseconds
- KernelWakelockStats staleStats = mReader.getNativeWakelockStats(wlStats,
+ KernelWakelockStats staleStats = mReader.updateWakelockStats(wlStats,
new KernelWakelockStats());
assertEquals(1, staleStats.size());
@@ -219,16 +214,16 @@
KernelWakelockStats.Entry entry = staleStats.get("WakeLock");
assertEquals(20, entry.mCount);
- assertEquals(10000, entry.mTotalTime);
+ assertEquals(1000 * 1000, entry.mTotalTime); // Microseconds
}
@SmallTest
public void testTwoWakeLockInfos() {
WakeLockInfo[] wlStats = new WakeLockInfo[2];
- wlStats[0] = createWakeLockInfo("WakeLock1", 10, 1000);
- wlStats[1] = createWakeLockInfo("WakeLock2", 20, 2000);
+ wlStats[0] = createWakeLockInfo("WakeLock1", 10, 1000); // Milliseconds
+ wlStats[1] = createWakeLockInfo("WakeLock2", 20, 2000); // Milliseconds
- KernelWakelockStats staleStats = mReader.getNativeWakelockStats(wlStats,
+ KernelWakelockStats staleStats = mReader.updateWakelockStats(wlStats,
new KernelWakelockStats());
assertEquals(2, staleStats.size());
@@ -238,17 +233,17 @@
KernelWakelockStats.Entry entry1 = staleStats.get("WakeLock1");
assertEquals(10, entry1.mCount);
- assertEquals(1000, entry1.mTotalTime);
+ assertEquals(1000 * 1000, entry1.mTotalTime); // Microseconds
KernelWakelockStats.Entry entry2 = staleStats.get("WakeLock2");
assertEquals(20, entry2.mCount);
- assertEquals(2000, entry2.mTotalTime);
+ assertEquals(2000 * 1000, entry2.mTotalTime); // Microseconds
}
@SmallTest
public void testWakeLockInfosBecomeStale() {
WakeLockInfo[] wlStats = new WakeLockInfo[1];
- wlStats[0] = createWakeLockInfo("WakeLock1", 10, 1000);
+ wlStats[0] = createWakeLockInfo("WakeLock1", 10, 1000); // Milliseconds
KernelWakelockStats staleStats = new KernelWakelockStats();
@@ -259,9 +254,9 @@
assertTrue(staleStats.containsKey("WakeLock1"));
KernelWakelockStats.Entry entry = staleStats.get("WakeLock1");
assertEquals(10, entry.mCount);
- assertEquals(1000, entry.mTotalTime);
+ assertEquals(1000 * 1000, entry.mTotalTime); // Microseconds
- wlStats[0] = createWakeLockInfo("WakeLock2", 20, 2000);
+ wlStats[0] = createWakeLockInfo("WakeLock2", 20, 2000); // Milliseconds
readKernelWakelockStats(staleStats, new byte[0], wlStats);
@@ -271,7 +266,7 @@
assertTrue(staleStats.containsKey("WakeLock2"));
entry = staleStats.get("WakeLock2");
assertEquals(20, entry.mCount);
- assertEquals(2000, entry.mTotalTime);
+ assertEquals(2000 * 1000, entry.mTotalTime); // Micro seconds
}
// -------------------- Aggregate Wakelock Stats Tests --------------------
@@ -313,7 +308,7 @@
byte[] buffer = new byte[0];
WakeLockInfo[] wlStats = new WakeLockInfo[1];
- wlStats[0] = createWakeLockInfo("WakeLock", 10, 1000);
+ wlStats[0] = createWakeLockInfo("WakeLock", 10, 1000); // Milliseconds
readKernelWakelockStats(staleStats, buffer, wlStats);
@@ -323,7 +318,7 @@
KernelWakelockStats.Entry entry = staleStats.get("WakeLock");
assertEquals(10, entry.mCount);
- assertEquals(1000, entry.mTotalTime);
+ assertEquals(1000 * 1000, entry.mTotalTime); // Microseconds
}
@SmallTest
@@ -334,7 +329,7 @@
.addLine("WakeLock1", 34, 123) // Milliseconds
.getBytes();
WakeLockInfo[] wlStats = new WakeLockInfo[1];
- wlStats[0] = createWakeLockInfo("WakeLock2", 10, 1000);
+ wlStats[0] = createWakeLockInfo("WakeLock2", 10, 1000); // Milliseconds
readKernelWakelockStats(staleStats, buffer, wlStats);
@@ -348,7 +343,7 @@
assertTrue(staleStats.containsKey("WakeLock2"));
KernelWakelockStats.Entry entry2 = staleStats.get("WakeLock2");
assertEquals(10, entry2.mCount);
- assertEquals(1000, entry2.mTotalTime);
+ assertEquals(1000 * 1000, entry2.mTotalTime); // Microseconds
}
@SmallTest
@@ -360,8 +355,8 @@
.addLine("WakeLock2", 46, 345) // Milliseconds
.getBytes();
WakeLockInfo[] wlStats = new WakeLockInfo[2];
- wlStats[0] = createWakeLockInfo("WakeLock3", 10, 1000);
- wlStats[1] = createWakeLockInfo("WakeLock4", 20, 2000);
+ wlStats[0] = createWakeLockInfo("WakeLock3", 10, 1000); // Milliseconds
+ wlStats[1] = createWakeLockInfo("WakeLock4", 20, 2000); // Milliseconds
readKernelWakelockStats(staleStats, buffer, wlStats);
@@ -382,18 +377,18 @@
KernelWakelockStats.Entry entry3 = staleStats.get("WakeLock3");
assertEquals(10, entry3.mCount);
- assertEquals(1000, entry3.mTotalTime);
+ assertEquals(1000 * 1000, entry3.mTotalTime); // Microseconds
KernelWakelockStats.Entry entry4 = staleStats.get("WakeLock4");
assertEquals(20, entry4.mCount);
- assertEquals(2000, entry4.mTotalTime);
+ assertEquals(2000 * 1000, entry4.mTotalTime); // Microseconds
buffer = new ProcFileBuilder()
.addLine("WakeLock1", 45, 789) // Milliseconds
.addLine("WakeLock1", 56, 123) // Milliseconds
.getBytes();
wlStats = new WakeLockInfo[1];
- wlStats[0] = createWakeLockInfo("WakeLock4", 40, 4000);
+ wlStats[0] = createWakeLockInfo("WakeLock4", 40, 4000); // Milliseconds
readKernelWakelockStats(staleStats, buffer, wlStats);
@@ -411,6 +406,6 @@
entry2 = staleStats.get("WakeLock4");
assertEquals(40, entry2.mCount);
- assertEquals(4000, entry4.mTotalTime);
+ assertEquals(4000 * 1000, entry4.mTotalTime); // Microseconds
}
}
diff --git a/data/keyboards/Vendor_045e_Product_02d1.kl b/data/keyboards/Vendor_045e_Product_02d1.kl
index 5d1637b..0867670 100644
--- a/data/keyboards/Vendor_045e_Product_02d1.kl
+++ b/data/keyboards/Vendor_045e_Product_02d1.kl
@@ -13,20 +13,22 @@
# limitations under the License.
#
-# XBox One USB Controller
+# XBox One Controller - Model 1537 - USB
#
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
key 304 BUTTON_A
key 305 BUTTON_B
key 307 BUTTON_X
key 308 BUTTON_Y
+
key 310 BUTTON_L1
key 311 BUTTON_R1
-key 314 BACK
-key 315 BUTTON_START
-key 316 HOME
-key 317 BUTTON_THUMBL
-key 318 BUTTON_THUMBR
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
# Left and right stick.
# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
@@ -37,10 +39,19 @@
axis 0x03 Z flat 4096
axis 0x04 RZ flat 4096
-# Triggers.
-axis 0x02 LTRIGGER
-axis 0x05 RTRIGGER
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
# Hat.
axis 0x10 HAT_X
axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_045e_Product_02e3.kl b/data/keyboards/Vendor_045e_Product_02e3.kl
new file mode 100644
index 0000000..0a6e7d7
--- /dev/null
+++ b/data/keyboards/Vendor_045e_Product_02e3.kl
@@ -0,0 +1,56 @@
+# Copyright (C) 2019 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Microsoft X-Box One Elite Pad - Model 1698 - USB
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 0x130 BUTTON_A
+key 0x131 BUTTON_B
+key 0x133 BUTTON_X
+key 0x134 BUTTON_Y
+
+key 0x136 BUTTON_L1
+key 0x137 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left stick
+axis 0x00 X
+axis 0x01 Y
+# Right stick
+axis 0x03 Z
+axis 0x04 RZ
+
+key 0x13d BUTTON_THUMBL
+key 0x13e BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+
+# Two overlapping rectangles
+key 0x13a BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 0x13b BUTTON_START
+
+# Xbox key
+key 0x13c BUTTON_MODE
diff --git a/data/keyboards/Vendor_045e_Product_02ea.kl b/data/keyboards/Vendor_045e_Product_02ea.kl
new file mode 100644
index 0000000..3b46db2
--- /dev/null
+++ b/data/keyboards/Vendor_045e_Product_02ea.kl
@@ -0,0 +1,57 @@
+# Copyright (C) 2019 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# XBox One Controller - Model 1708 - USB
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_045e_Product_02fd.kl b/data/keyboards/Vendor_045e_Product_02fd.kl
new file mode 100644
index 0000000..1b03497
--- /dev/null
+++ b/data/keyboards/Vendor_045e_Product_02fd.kl
@@ -0,0 +1,62 @@
+# Copyright (C) 2019 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# XBox One Controller - Model 1708 - Bluetooth
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x0a LTRIGGER
+axis 0x09 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x02 Z flat 4096
+axis 0x05 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 158 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# There are at least two versions of firmware out for this controller.
+# They send different linux keys for the "Xbox" button.
+# Xbox key (original firmware)
+key 172 BUTTON_MODE
+
+# Xbox key (newer firmware)
+key 316 BUTTON_MODE
diff --git a/location/lib/Android.bp b/location/lib/Android.bp
index 35f2877..1bf5221 100644
--- a/location/lib/Android.bp
+++ b/location/lib/Android.bp
@@ -19,6 +19,7 @@
srcs: ["java/**/*.java"],
api_packages: ["com.android.location.provider"],
srcs_lib: "framework",
- srcs_lib_whitelist_dirs: ["location/java"],
- srcs_lib_whitelist_pkgs: ["com.android.internal.location"],
+ // TODO(b/70046217): remove core/java and android below. It was added to provide definitions for
+ // types like android.os.Bundle
+ srcs_lib_whitelist_pkgs: ["android", "com.android.internal.location"],
}
diff --git a/media/OWNERS b/media/OWNERS
index 72c8952..0a12adc 100644
--- a/media/OWNERS
+++ b/media/OWNERS
@@ -9,6 +9,7 @@
jaewan@google.com
jmtrivi@google.com
jsharkey@android.com
+klhyun@google.com
lajos@google.com
marcone@google.com
sungsoo@google.com
diff --git a/media/java/android/media/AudioPortConfig.java b/media/java/android/media/AudioPortConfig.java
index 45e49a7..ac19bb1 100644
--- a/media/java/android/media/AudioPortConfig.java
+++ b/media/java/android/media/AudioPortConfig.java
@@ -95,7 +95,6 @@
/**
* The gain configuration if this port supports gain control, null otherwise
- * @see AudioGainConfig.
*/
public AudioGainConfig gain() {
return mGain;
diff --git a/media/java/android/media/AudioPortEventHandler.java b/media/java/android/media/AudioPortEventHandler.java
index f9a4b1e..6d9d626 100644
--- a/media/java/android/media/AudioPortEventHandler.java
+++ b/media/java/android/media/AudioPortEventHandler.java
@@ -19,10 +19,12 @@
import android.annotation.UnsupportedAppUsage;
import android.os.Handler;
import android.os.HandlerThread;
-import android.os.Looper;
import android.os.Message;
-import java.util.ArrayList;
+
+import com.android.internal.annotations.GuardedBy;
+
import java.lang.ref.WeakReference;
+import java.util.ArrayList;
/**
* The AudioPortEventHandler handles AudioManager.OnAudioPortUpdateListener callbacks
@@ -33,6 +35,9 @@
class AudioPortEventHandler {
private Handler mHandler;
private HandlerThread mHandlerThread;
+ private final Object mLock = new Object();
+
+ @GuardedBy("mLock")
private final ArrayList<AudioManager.OnAudioPortUpdateListener> mListeners =
new ArrayList<AudioManager.OnAudioPortUpdateListener>();
@@ -53,7 +58,7 @@
private long mJniCallback;
void init() {
- synchronized (this) {
+ synchronized (mLock) {
if (mHandler != null) {
return;
}
@@ -66,7 +71,7 @@
@Override
public void handleMessage(Message msg) {
ArrayList<AudioManager.OnAudioPortUpdateListener> listeners;
- synchronized (this) {
+ synchronized (mLock) {
if (msg.what == AUDIOPORT_EVENT_NEW_LISTENER) {
listeners = new ArrayList<AudioManager.OnAudioPortUpdateListener>();
if (mListeners.contains(msg.obj)) {
@@ -152,7 +157,7 @@
private native void native_finalize();
void registerListener(AudioManager.OnAudioPortUpdateListener l) {
- synchronized (this) {
+ synchronized (mLock) {
mListeners.add(l);
}
if (mHandler != null) {
@@ -162,7 +167,7 @@
}
void unregisterListener(AudioManager.OnAudioPortUpdateListener l) {
- synchronized (this) {
+ synchronized (mLock) {
mListeners.remove(l);
}
}
diff --git a/media/lib/signer/Android.bp b/media/lib/signer/Android.bp
index 44f8725..f04b2fc 100644
--- a/media/lib/signer/Android.bp
+++ b/media/lib/signer/Android.bp
@@ -19,6 +19,5 @@
srcs: ["java/**/*.java"],
api_packages: ["com.android.mediadrm.signer"],
srcs_lib: "framework",
- srcs_lib_whitelist_dirs: ["media/java"],
srcs_lib_whitelist_pkgs: ["android.media"],
}
diff --git a/mime/Android.bp b/mime/Android.bp
new file mode 100644
index 0000000..9303755
--- /dev/null
+++ b/mime/Android.bp
@@ -0,0 +1,43 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+java_library {
+ name: "mimemap",
+ visibility: [
+ "//cts/tests/tests/mimemap:__subpackages__",
+ "//frameworks/base:__subpackages__",
+ ],
+
+ srcs: [
+ "java/android/content/type/MimeMapImpl.java",
+ ],
+
+ java_resources: [
+ ":debian.mime.types",
+ ":android.mime.types",
+ ],
+
+ sdk_version: "core_platform",
+}
+
+filegroup {
+ name: "android.mime.types",
+ visibility: [
+ "//visibility:private",
+ ],
+ path: "java-res/",
+ srcs: [
+ "java-res/android.mime.types",
+ ],
+}
diff --git a/mime/java-res/android.mime.types b/mime/java-res/android.mime.types
new file mode 100644
index 0000000..1ca912e
--- /dev/null
+++ b/mime/java-res/android.mime.types
@@ -0,0 +1,146 @@
+
+###############################################################################
+#
+# Android-specific MIME type <-> extension mappings
+#
+# Each line below defines an mapping from one MIME type to the first of the
+# listed extensions, and from listed extension back to the MIME type.
+# A mapping overrides any previous mapping _from_ that same MIME type or
+# extension (put() semantics), unless that MIME type / extension is prefixed with '?'
+# (putIfAbsent() semantics).
+#
+#
+###############################################################################
+#
+# EXAMPLES
+#
+# A line of the form:
+#
+# ?mime ext1 ?ext2 ext3
+#
+# affects the current mappings along the lines of the following pseudo code:
+#
+# mimeToExt.putIfAbsent("mime", "ext1");
+# extToMime.put("ext1", "mime");
+# extToMime.putIfAbsent("ext2", "mime");
+# extToMime.put("ext3", "mime");
+#
+# The line:
+#
+# ?text/plain txt
+#
+# leaves any earlier mapping for "text/plain" untouched, or maps that MIME type
+# to the file extension ".txt" if there is no earlier mapping. The line also
+# sets the mapping from file extension ".txt" to be the MIME type "text/plain",
+# regardless of whether a previous mapping existed.
+#
+###############################################################################
+
+
+# File extensions that Android wants to override to point to the given MIME type.
+#
+# After processing a line of the form:
+# ?<mimeType> <extension1> <extension2>
+# If <mimeType> was not already mapped to an extension then it will be
+# mapped to <extension1>.
+# <extension1> and <extension2> are mapped (or remapped) to <mimeType>.
+
+?application/epub+zip epub
+?application/pkix-cert cer
+?application/rss+xml rss
+?application/vnd.android.ota ota
+?application/vnd.apple.mpegurl m3u8
+?application/vnd.ms-pki.stl stl
+?application/vnd.ms-powerpoint pot
+?application/vnd.ms-wpl wpl
+?application/vnd.stardivision.impress sdp
+?application/vnd.stardivision.writer vor
+?application/vnd.youtube.yt yt
+?application/x-android-drm-fl fl
+?application/x-flac flac
+?application/x-font pcf
+?application/x-mpegurl m3u m3u8
+?application/x-pem-file pem
+?application/x-pkcs12 p12 pfx
+?application/x-webarchive webarchive
+?application/x-webarchive-xml webarchivexml
+?application/x-x509-server-cert crt
+?application/x-x509-user-cert crt
+
+?audio/3gpp 3gpp
+?audio/aac-adts aac
+?audio/imelody imy
+?audio/midi rtttl xmf
+?audio/mobile-xmf mxmf
+?audio/mp4 m4a
+?audio/mpegurl m3u
+?audio/sp-midi smf
+?audio/x-matroska mka
+?audio/x-pn-realaudio ra
+
+?image/bmp bmp
+?image/heic heic
+?image/heic-sequence heics
+?image/heif heif hif
+?image/heif-sequence heifs
+?image/ico cur
+?image/webp webp
+?image/x-adobe-dng dng
+?image/x-fuji-raf raf
+?image/x-icon ico
+?image/x-nikon-nrw nrw
+?image/x-panasonic-rw2 rw2
+?image/x-pentax-pef pef
+?image/x-samsung-srw srw
+?image/x-sony-arw arw
+
+?text/comma-separated-values csv
+?text/plain diff po
+?text/rtf rtf
+?text/text phps
+?text/xml xml
+?text/x-vcard vcf
+
+?video/3gpp2 3gpp2 3g2
+?video/3gpp 3gpp
+?video/avi avi
+?video/m4v m4v
+?video/mp2p mpeg
+?video/mp2t m2ts mts
+?video/mp2ts ts
+?video/vnd.youtube.yt yt
+?video/x-webex wrf
+
+# Optional additions that should not override any previous mapping.
+
+?application/x-wifi-config ?xml
+
+# Special cases where Android has a strong opinion about mappings, so we
+# define them very last and make them override in both directions (no "?").
+#
+# Lines here are of the form:
+# <mimeType> <extension1> <extension2> ...
+#
+# After processing each line,
+# <mimeType> is mapped to <extension1>
+# <extension1>, <extension2>, ... are all mapped to <mimeType>
+# This overrides any mappings for this <mimeType> / for these extensions
+# that may have been defined earlier.
+
+application/pgp-signature pgp
+application/x-x509-ca-cert crt
+audio/aac aac
+audio/basic snd
+audio/flac flac
+audio/midi rtx
+audio/mpeg mp3 m4a m4r
+audio/x-mpegurl m3u m3u8
+image/jpeg jpg
+image/x-ms-bmp bmp
+text/plain txt
+text/x-c++hdr hpp
+text/x-c++src cpp
+video/3gpp 3gpp
+video/mpeg mpeg
+video/quicktime mov
+video/x-matroska mkv
diff --git a/mime/java/android/content/type/MimeMapImpl.java b/mime/java/android/content/type/MimeMapImpl.java
new file mode 100644
index 0000000..c904ea3
--- /dev/null
+++ b/mime/java/android/content/type/MimeMapImpl.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.type;
+
+import libcore.net.MimeMap;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Pattern;
+
+/**
+ * Default implementation of {@link MimeMap}, a bidirectional mapping between
+ * MIME types and file extensions.
+ *
+ * This default mapping is loaded from data files that start with some mappings
+ * recognized by IANA plus some custom extensions and overrides.
+ *
+ * @hide
+ */
+public class MimeMapImpl extends MimeMap {
+
+ /**
+ * Creates and returns a new {@link MimeMapImpl} instance that implements.
+ * Android's default mapping between MIME types and extensions.
+ */
+ public static MimeMapImpl createDefaultInstance() {
+ return parseFromResources("/mime.types", "/android.mime.types");
+ }
+
+ private static final Pattern SPLIT_PATTERN = Pattern.compile("\\s+");
+
+ /**
+ * Note: These maps only contain lowercase keys/values, regarded as the
+ * {@link #toLowerCase(String) canonical form}.
+ *
+ * <p>This is the case for both extensions and MIME types. The mime.types
+ * data file contains examples of mixed-case MIME types, but some applications
+ * use the lowercase version of these same types. RFC 2045 section 2 states
+ * that MIME types are case insensitive.
+ */
+ private final Map<String, String> mMimeTypeToExtension;
+ private final Map<String, String> mExtensionToMimeType;
+
+ public MimeMapImpl(Map<String, String> mimeTypeToExtension,
+ Map<String, String> extensionToMimeType) {
+ this.mMimeTypeToExtension = new HashMap<>(mimeTypeToExtension);
+ for (Map.Entry<String, String> entry : mimeTypeToExtension.entrySet()) {
+ checkValidMimeType(entry.getKey());
+ checkValidExtension(entry.getValue());
+ }
+ this.mExtensionToMimeType = new HashMap<>(extensionToMimeType);
+ for (Map.Entry<String, String> entry : extensionToMimeType.entrySet()) {
+ checkValidExtension(entry.getKey());
+ checkValidMimeType(entry.getValue());
+ }
+ }
+
+ private static void checkValidMimeType(String s) {
+ if (MimeMap.isNullOrEmpty(s) || !s.equals(MimeMap.toLowerCase(s))) {
+ throw new IllegalArgumentException("Invalid MIME type: " + s);
+ }
+ }
+
+ private static void checkValidExtension(String s) {
+ if (MimeMap.isNullOrEmpty(s) || !s.equals(MimeMap.toLowerCase(s))) {
+ throw new IllegalArgumentException("Invalid extension: " + s);
+ }
+ }
+
+ static MimeMapImpl parseFromResources(String... resourceNames) {
+ Map<String, String> mimeTypeToExtension = new HashMap<>();
+ Map<String, String> extensionToMimeType = new HashMap<>();
+ for (String resourceName : resourceNames) {
+ parseTypes(mimeTypeToExtension, extensionToMimeType, resourceName);
+ }
+ return new MimeMapImpl(mimeTypeToExtension, extensionToMimeType);
+ }
+
+ /**
+ * An element of a *mime.types file: A MIME type or an extension, with an optional
+ * prefix of "?" (if not overriding an earlier value).
+ */
+ private static class Element {
+ public final boolean keepExisting;
+ public final String s;
+
+ Element(boolean keepExisting, String value) {
+ this.keepExisting = keepExisting;
+ this.s = toLowerCase(value);
+ if (value.isEmpty()) {
+ throw new IllegalArgumentException();
+ }
+ }
+
+ public String toString() {
+ return keepExisting ? ("?" + s) : s;
+ }
+ }
+
+ private static String maybePut(Map<String, String> map, Element keyElement, String value) {
+ if (keyElement.keepExisting) {
+ return map.putIfAbsent(keyElement.s, value);
+ } else {
+ return map.put(keyElement.s, value);
+ }
+ }
+
+ private static void parseTypes(Map<String, String> mimeTypeToExtension,
+ Map<String, String> extensionToMimeType, String resource) {
+ try (BufferedReader r = new BufferedReader(
+ new InputStreamReader(MimeMapImpl.class.getResourceAsStream(resource)))) {
+ String line;
+ while ((line = r.readLine()) != null) {
+ int commentPos = line.indexOf('#');
+ if (commentPos >= 0) {
+ line = line.substring(0, commentPos);
+ }
+ line = line.trim();
+ // The first time a MIME type is encountered it is mapped to the first extension
+ // listed in its line. The first time an extension is encountered it is mapped
+ // to the MIME type.
+ //
+ // When encountering a previously seen MIME type or extension, then by default
+ // the later ones override earlier mappings (put() semantics); however if a MIME
+ // type or extension is prefixed with '?' then any earlier mapping _from_ that
+ // MIME type / extension is kept (putIfAbsent() semantics).
+ final String[] split = SPLIT_PATTERN.split(line);
+ if (split.length <= 1) {
+ // Need mimeType + at least one extension to make a mapping.
+ // "mime.types" files may also contain lines with just a mimeType without
+ // an extension but we skip them as they provide no mapping info.
+ continue;
+ }
+ List<Element> lineElements = new ArrayList<>(split.length);
+ for (String s : split) {
+ boolean keepExisting = s.startsWith("?");
+ if (keepExisting) {
+ s = s.substring(1);
+ }
+ if (s.isEmpty()) {
+ throw new IllegalArgumentException("Invalid entry in '" + line + "'");
+ }
+ lineElements.add(new Element(keepExisting, s));
+ }
+
+ // MIME type -> first extension (one mapping)
+ // This will override any earlier mapping from this MIME type to another
+ // extension, unless this MIME type was prefixed with '?'.
+ Element mimeElement = lineElements.get(0);
+ List<Element> extensionElements = lineElements.subList(1, lineElements.size());
+ String firstExtension = extensionElements.get(0).s;
+ maybePut(mimeTypeToExtension, mimeElement, firstExtension);
+
+ // extension -> MIME type (one or more mappings).
+ // This will override any earlier mapping from this extension to another
+ // MIME type, unless this extension was prefixed with '?'.
+ for (Element extensionElement : extensionElements) {
+ maybePut(extensionToMimeType, extensionElement, mimeElement.s);
+ }
+ }
+ } catch (IOException | RuntimeException e) {
+ throw new RuntimeException("Failed to parse " + resource, e);
+ }
+ }
+
+ @Override
+ protected String guessExtensionFromLowerCaseMimeType(String mimeType) {
+ return mMimeTypeToExtension.get(mimeType);
+ }
+
+ @Override
+ protected String guessMimeTypeFromLowerCaseExtension(String extension) {
+ return mExtensionToMimeType.get(extension);
+ }
+}
diff --git a/packages/EasterEgg/src/com/android/egg/paint/BrushPropertyDrawable.kt b/packages/EasterEgg/src/com/android/egg/paint/BrushPropertyDrawable.kt
index d06e5ec..f10a3ac 100644
--- a/packages/EasterEgg/src/com/android/egg/paint/BrushPropertyDrawable.kt
+++ b/packages/EasterEgg/src/com/android/egg/paint/BrushPropertyDrawable.kt
@@ -63,7 +63,7 @@
}
override fun draw(c: Canvas) {
- c?.let {
+ c.let {
val w = bounds.width().toFloat()
val h = bounds.height().toFloat()
val inset = _size / 12 // 2dp in a 24x24 icon
diff --git a/packages/InputDevices/Android.bp b/packages/InputDevices/Android.bp
new file mode 100644
index 0000000..7532aea
--- /dev/null
+++ b/packages/InputDevices/Android.bp
@@ -0,0 +1,42 @@
+// 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.
+
+android_app {
+ name: "InputDevices",
+
+ srcs: [
+ "**/*.java",
+ ":validate_input_devices_keymaps",
+ ],
+
+ resource_dirs: ["res"],
+
+ sdk_version: "current",
+ certificate: "platform",
+ privileged: true,
+}
+
+// Validate all key maps.
+// Produces an empty srcjar that is used as an input to InputDevices to make sure
+// the check runs for platform builds.
+genrule {
+ name: "validate_input_devices_keymaps",
+ tools: [
+ "validatekeymaps",
+ "soong_zip",
+ ],
+ srcs: ["res/raw/*.kcm"],
+ out: ["validate_input_devices_keymaps.srcjar"],
+ cmd: "$(location validatekeymaps) -q $(in) && $(location soong_zip) -o $(out)",
+}
diff --git a/packages/InputDevices/Android.mk b/packages/InputDevices/Android.mk
deleted file mode 100644
index 6de1f1d..0000000
--- a/packages/InputDevices/Android.mk
+++ /dev/null
@@ -1,50 +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.
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_JAVA_LIBRARIES :=
-
-LOCAL_PACKAGE_NAME := InputDevices
-LOCAL_SDK_VERSION := current
-LOCAL_CERTIFICATE := platform
-LOCAL_PRIVILEGED_MODULE := true
-
-include $(BUILD_PACKAGE)
-
-# Validate all key maps.
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := validate_input_devices_keymaps
-intermediates := $(call intermediates-dir-for,ETC,$(LOCAL_MODULE),,COMMON)
-LOCAL_BUILT_MODULE := $(intermediates)/stamp
-
-validatekeymaps := $(HOST_OUT_EXECUTABLES)/validatekeymaps$(HOST_EXECUTABLE_SUFFIX)
-input_devices_keymaps := $(wildcard $(LOCAL_PATH)/res/raw/*.kcm)
-$(LOCAL_BUILT_MODULE): PRIVATE_VALIDATEKEYMAPS := $(validatekeymaps)
-$(LOCAL_BUILT_MODULE) : $(input_devices_keymaps) | $(validatekeymaps)
- $(hide) $(PRIVATE_VALIDATEKEYMAPS) $^
- $(hide) mkdir -p $(dir $@) && touch $@
-
-# Run validatekeymaps unconditionally for platform build.
-droidcore : $(LOCAL_BUILT_MODULE)
-
-# Reset temp vars.
-validatekeymaps :=
-input_devices_keymaps :=
diff --git a/packages/MtpDocumentsProvider/Android.bp b/packages/MtpDocumentsProvider/Android.bp
new file mode 100644
index 0000000..3dafa26
--- /dev/null
+++ b/packages/MtpDocumentsProvider/Android.bp
@@ -0,0 +1,11 @@
+android_app {
+ name: "MtpDocumentsProvider",
+
+ srcs: ["src/**/*.java"],
+ platform_apis: true,
+ certificate: "media",
+ privileged: true,
+ optimize: {
+ proguard_flags_files: ["proguard.flags"],
+ },
+}
diff --git a/packages/MtpDocumentsProvider/Android.mk b/packages/MtpDocumentsProvider/Android.mk
deleted file mode 100644
index 2d62a07..0000000
--- a/packages/MtpDocumentsProvider/Android.mk
+++ /dev/null
@@ -1,18 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := optional
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_PACKAGE_NAME := MtpDocumentsProvider
-LOCAL_PRIVATE_PLATFORM_APIS := true
-LOCAL_CERTIFICATE := media
-LOCAL_PRIVILEGED_MODULE := true
-LOCAL_PROGUARD_FLAG_FILES := proguard.flags
-
-# Only enable asserts on userdebug/eng builds
-ifneq (,$(filter userdebug eng, $(TARGET_BUILD_VARIANT)))
-LOCAL_JACK_FLAGS += -D jack.assert.policy=always
-endif
-
-include $(BUILD_PACKAGE)
-include $(call all-makefiles-under, $(LOCAL_PATH))
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 8428797..53ccd1b 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -112,7 +112,6 @@
<uses-permission android:name="android.permission.BIND_APPWIDGET" />
<uses-permission android:name="android.permission.UPDATE_APP_OPS_STATS" />
<uses-permission android:name="android.permission.MODIFY_APPWIDGET_BIND_PERMISSIONS"/>
- <uses-permission android:name="android.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS" />
<uses-permission android:name="android.permission.CHANGE_APP_IDLE_STATE" />
<uses-permission android:name="android.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST" />
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index 1060c7b..3750a8a 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -175,9 +175,9 @@
// Passed to Message.obtain() when msg.arg2 is not used.
private static final int UNUSED_ARG2 = -2;
- // Maximum progress displayed (like 99.00%).
- private static final int CAPPED_PROGRESS = 9900;
- private static final int CAPPED_MAX = 10000;
+ // Maximum progress displayed in %.
+ private static final int CAPPED_PROGRESS = 99;
+ private static final int CAPPED_MAX = 100;
/** Show the progress log every this percent. */
private static final int LOG_PROGRESS_STEP = 10;
@@ -1954,7 +1954,10 @@
@Override
public void onProgress(int progress) throws RemoteException {
- updateProgressInfo(progress, 100 /* progress is already a percentage; so max = 100 */);
+ if (progress > CAPPED_PROGRESS) {
+ progress = CAPPED_PROGRESS;
+ }
+ updateProgressInfo(progress, CAPPED_MAX);
}
@Override
@@ -1967,46 +1970,6 @@
// TODO(b/111441001): implement
}
- @Override
- public void onProgressUpdated(int progress) throws RemoteException {
- /*
- * Checks whether the progress changed in a way that should be displayed to the user:
- * - info.progress / info.max represents the displayed progress
- * - info.realProgress / info.realMax represents the real progress
- * - since the real progress can decrease, the displayed progress is only updated if it
- * increases
- * - the displayed progress is capped at a maximum (like 99%)
- */
- info.realProgress = progress;
- final int oldPercentage = (CAPPED_MAX * info.progress) / info.max;
- int newPercentage = (CAPPED_MAX * info.realProgress) / info.realMax;
- int max = info.realMax;
-
- if (newPercentage > CAPPED_PROGRESS) {
- progress = newPercentage = CAPPED_PROGRESS;
- max = CAPPED_MAX;
- }
-
- if (newPercentage > oldPercentage) {
- updateProgressInfo(progress, max);
- }
- }
-
- @Override
- public void onMaxProgressUpdated(int maxProgress) throws RemoteException {
- Log.d(TAG, "onMaxProgressUpdated: " + maxProgress);
- info.realMax = maxProgress;
- }
-
- @Override
- public void onSectionComplete(String title, int status, int size, int durationMs)
- throws RemoteException {
- if (DEBUG) {
- Log.v(TAG, "Title: " + title + " Status: " + status + " Size: " + size
- + " Duration: " + durationMs + "ms");
- }
- }
-
public void dump(String prefix, PrintWriter pw) {
pw.print(prefix); pw.print("token: "); pw.println(token);
}
diff --git a/packages/SimAppDialog/Android.bp b/packages/SimAppDialog/Android.bp
new file mode 100644
index 0000000..d91a116
--- /dev/null
+++ b/packages/SimAppDialog/Android.bp
@@ -0,0 +1,15 @@
+android_app {
+ name: "SimAppDialog",
+
+ srcs: ["src/**/*.java"],
+
+ platform_apis: true,
+ certificate: "platform",
+
+ static_libs: [
+ "android-support-v4",
+ "setup-wizard-lib",
+ ],
+
+ resource_dirs: ["res"],
+}
diff --git a/packages/SimAppDialog/Android.mk b/packages/SimAppDialog/Android.mk
deleted file mode 100644
index 6a4099b..0000000
--- a/packages/SimAppDialog/Android.mk
+++ /dev/null
@@ -1,19 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_PACKAGE_NAME := SimAppDialog
-LOCAL_PRIVATE_PLATFORM_APIS := true
-LOCAL_CERTIFICATE := platform
-
-
-LOCAL_STATIC_ANDROID_LIBRARIES := \
- android-support-v4
-
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-include frameworks/opt/setupwizard/library/common-platform-deprecated.mk
-
-include $(BUILD_PACKAGE)
diff --git a/packages/SystemUI/plugin/ExamplePlugin/Android.bp b/packages/SystemUI/plugin/ExamplePlugin/Android.bp
index a0eaf14..c6c80f3 100644
--- a/packages/SystemUI/plugin/ExamplePlugin/Android.bp
+++ b/packages/SystemUI/plugin/ExamplePlugin/Android.bp
@@ -11,4 +11,5 @@
srcs: ["src/**/*.java"],
+ platform_apis: true,
}
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
index 38a90cf..c906240 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
@@ -219,7 +219,7 @@
intent.setComponent(assistComponent);
intent.putExtras(args);
- if (structureEnabled) {
+ if (structureEnabled && AssistUtils.isDisclosureEnabled(mContext)) {
showDisclosure();
}
diff --git a/packages/VpnDialogs/AndroidManifest.xml b/packages/VpnDialogs/AndroidManifest.xml
index 1d0b9b6..1f49aa1 100644
--- a/packages/VpnDialogs/AndroidManifest.xml
+++ b/packages/VpnDialogs/AndroidManifest.xml
@@ -22,6 +22,7 @@
<uses-permission android:name="android.permission.CONTROL_VPN" />
<uses-permission android:name="android.permission.CONTROL_ALWAYS_ON_VPN" />
<uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" />
+ <uses-permission android:name="android.permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS"/>
<application android:label="VpnDialogs"
android:allowBackup="false">
diff --git a/packages/VpnDialogs/src/com/android/vpndialogs/AlwaysOnDisconnectedDialog.java b/packages/VpnDialogs/src/com/android/vpndialogs/AlwaysOnDisconnectedDialog.java
index 846fcf8..ba4baf3 100644
--- a/packages/VpnDialogs/src/com/android/vpndialogs/AlwaysOnDisconnectedDialog.java
+++ b/packages/VpnDialogs/src/com/android/vpndialogs/AlwaysOnDisconnectedDialog.java
@@ -16,6 +16,10 @@
package com.android.vpndialogs;
+import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
+import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
+
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
@@ -31,7 +35,6 @@
import android.text.style.ClickableSpan;
import android.util.Log;
import android.view.View;
-import android.view.WindowManager;
import android.widget.TextView;
import com.android.internal.app.AlertActivity;
@@ -74,8 +77,9 @@
setupAlert();
getWindow().setCloseOnTouchOutside(false);
- getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
- getWindow().addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
+ getWindow().setType(TYPE_SYSTEM_ALERT);
+ getWindow().addFlags(FLAG_ALT_FOCUSABLE_IM);
+ getWindow().addPrivateFlags(PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
}
@Override
diff --git a/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java b/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java
index 72ce9c4..0933974 100644
--- a/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java
+++ b/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java
@@ -16,6 +16,8 @@
package com.android.vpndialogs;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
+
import android.content.Context;
import android.content.DialogInterface;
import android.content.pm.PackageManager;
@@ -78,6 +80,7 @@
setupAlert();
getWindow().setCloseOnTouchOutside(false);
+ getWindow().addPrivateFlags(PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
Button button = mAlert.getButton(DialogInterface.BUTTON_POSITIVE);
button.setFilterTouchesWhenObscured(true);
}
diff --git a/packages/services/PacProcessor/Android.bp b/packages/services/PacProcessor/Android.bp
index 93b2d95..494a818 100644
--- a/packages/services/PacProcessor/Android.bp
+++ b/packages/services/PacProcessor/Android.bp
@@ -21,3 +21,9 @@
certificate: "platform",
jni_libs: ["libjni_pacprocessor"],
}
+
+filegroup {
+ name: "PacProcessor-aidl-sources",
+ srcs: ["src/**/*.aidl"],
+ path: "src",
+}
diff --git a/packages/services/PacProcessor/com/android/net/IProxyService.aidl b/packages/services/PacProcessor/src/com/android/net/IProxyService.aidl
similarity index 100%
rename from packages/services/PacProcessor/com/android/net/IProxyService.aidl
rename to packages/services/PacProcessor/src/com/android/net/IProxyService.aidl
diff --git a/packages/services/Proxy/Android.bp b/packages/services/Proxy/Android.bp
index 87aa763..d93c9f8 100644
--- a/packages/services/Proxy/Android.bp
+++ b/packages/services/Proxy/Android.bp
@@ -5,3 +5,9 @@
certificate: "platform",
privileged: true,
}
+
+filegroup {
+ name: "ProxyHandler-aidl-sources",
+ srcs: ["src/**/*.aidl"],
+ path: "src",
+}
diff --git a/packages/services/Proxy/com/android/net/IProxyCallback.aidl b/packages/services/Proxy/src/com/android/net/IProxyCallback.aidl
similarity index 100%
rename from packages/services/Proxy/com/android/net/IProxyCallback.aidl
rename to packages/services/Proxy/src/com/android/net/IProxyCallback.aidl
diff --git a/packages/services/Proxy/com/android/net/IProxyPortListener.aidl b/packages/services/Proxy/src/com/android/net/IProxyPortListener.aidl
similarity index 100%
rename from packages/services/Proxy/com/android/net/IProxyPortListener.aidl
rename to packages/services/Proxy/src/com/android/net/IProxyPortListener.aidl
diff --git a/services/Android.bp b/services/Android.bp
index bea51be..75fd012 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -37,6 +37,10 @@
"android.hidl.manager-V1.0-java",
],
+ plugins: [
+ "compat-changeid-annotation-processor",
+ ],
+
// Uncomment to enable output of certain warnings (deprecated, unchecked)
//javacflags: ["-Xlint"],
@@ -50,3 +54,9 @@
defaults: ["libservices.core-libs"],
whole_static_libs: ["libservices.core"],
}
+
+platform_compat_config {
+ name: "services-platform-compat-config",
+ prefix: "services",
+ src: ":services",
+}
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 5ddb687..4d8d992 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -18,6 +18,7 @@
":vold_aidl",
":gsiservice_aidl",
":mediaupdateservice_aidl",
+ ":platform-compat-config",
"java/com/android/server/EventLogTags.logtags",
"java/com/android/server/am/EventLogTags.logtags",
"java/com/android/server/policy/EventLogTags.logtags",
@@ -77,4 +78,4 @@
prebuilt_etc {
name: "gps_debug.conf",
src: "java/com/android/server/location/gps_debug.conf",
-}
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java
index 3b78fda..9465617 100644
--- a/services/core/java/com/android/server/BatteryService.java
+++ b/services/core/java/com/android/server/BatteryService.java
@@ -1227,14 +1227,21 @@
}
@Override
public void scheduleUpdate() throws RemoteException {
- traceBegin("HealthScheduleUpdate");
- try {
- IHealth service = mHealthServiceWrapper.getLastService();
- if (service == null) throw new RemoteException("no health service");
- service.update();
- } finally {
- traceEnd();
- }
+ 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();
+ }
+ });
}
}
@@ -1311,7 +1318,7 @@
Arrays.asList(INSTANCE_VENDOR, INSTANCE_HEALTHD);
private final IServiceNotification mNotification = new Notification();
- private final HandlerThread mHandlerThread = new HandlerThread("HealthServiceRefresh");
+ private final HandlerThread mHandlerThread = new HandlerThread("HealthServiceHwbinder");
// These variables are fixed after init.
private Callback mCallback;
private IHealthSupplier mHealthSupplier;
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index 223eb55..89b59cf 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -1142,7 +1142,8 @@
if (isBluetoothDisallowed) {
return;
}
- if (mEnableExternal && isBluetoothPersistedStateOnBluetooth()) {
+ final boolean isSafeMode = mContext.getPackageManager().isSafeMode();
+ if (mEnableExternal && isBluetoothPersistedStateOnBluetooth() && !isSafeMode) {
if (DBG) {
Slog.d(TAG, "Auto-enabling Bluetooth.");
}
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 231c015..667445b 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -149,7 +149,6 @@
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
-import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
import android.util.Xml;
@@ -167,7 +166,6 @@
import com.android.internal.util.DumpUtils;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.MessageUtils;
-import com.android.internal.util.WakeupMessage;
import com.android.internal.util.XmlUtils;
import com.android.server.am.BatteryStatsService;
import com.android.server.connectivity.AutodestructReference;
@@ -304,7 +302,8 @@
/** Flag indicating if background data is restricted. */
private boolean mRestrictBackground;
- final private Context mContext;
+ private final Context mContext;
+ private final Dependencies mDeps;
// 0 is full bad, 100 is full good
private int mDefaultInetConditionPublished = 0;
@@ -585,11 +584,6 @@
private NetworkNotificationManager mNotifier;
private LingerMonitor mLingerMonitor;
- // sequence number for Networks; keep in sync with system/netd/NetworkController.cpp
- private static final int MIN_NET_ID = 100; // some reserved marks
- private static final int MAX_NET_ID = 65535 - 0x0400; // Top 1024 bits reserved by IpSecService
- private int mNextNetId = MIN_NET_ID;
-
// sequence number of NetworkRequests
private int mNextNetworkRequestId = 1;
@@ -833,19 +827,113 @@
}
};
+ /**
+ * Dependencies of ConnectivityService, for injection in tests.
+ */
+ @VisibleForTesting
+ public static class Dependencies {
+ /**
+ * Get system properties to use in ConnectivityService.
+ */
+ public MockableSystemProperties getSystemProperties() {
+ return new MockableSystemProperties();
+ }
+
+ /**
+ * Create a HandlerThread to use in ConnectivityService.
+ */
+ public HandlerThread makeHandlerThread() {
+ return new HandlerThread("ConnectivityServiceThread");
+ }
+
+ /**
+ * Get a reference to the NetworkStackClient.
+ */
+ public NetworkStackClient getNetworkStack() {
+ return NetworkStackClient.getInstance();
+ }
+
+ /**
+ * @see Tethering
+ */
+ public Tethering makeTethering(@NonNull Context context,
+ @NonNull INetworkManagementService nms,
+ @NonNull INetworkStatsService statsService,
+ @NonNull INetworkPolicyManager policyManager,
+ @NonNull TetheringDependencies tetheringDeps) {
+ return new Tethering(context, nms, statsService, policyManager,
+ IoThread.get().getLooper(), getSystemProperties(), tetheringDeps);
+ }
+
+ /**
+ * @see ProxyTracker
+ */
+ public ProxyTracker makeProxyTracker(@NonNull Context context,
+ @NonNull Handler connServiceHandler) {
+ return new ProxyTracker(context, connServiceHandler, EVENT_PROXY_HAS_CHANGED);
+ }
+
+ /**
+ * @see NetIdManager
+ */
+ public NetIdManager makeNetIdManager() {
+ return new NetIdManager();
+ }
+
+ /**
+ * @see NetworkUtils#queryUserAccess(int, int)
+ */
+ public boolean queryUserAccess(int uid, int netId) {
+ return NetworkUtils.queryUserAccess(uid, netId);
+ }
+
+ /**
+ * @see MultinetworkPolicyTracker
+ */
+ public MultinetworkPolicyTracker makeMultinetworkPolicyTracker(
+ @NonNull Context c, @NonNull Handler h, @NonNull Runnable r) {
+ return new MultinetworkPolicyTracker(c, h, r);
+ }
+
+ /**
+ * @see ServiceManager#checkService(String)
+ */
+ public boolean hasService(@NonNull String name) {
+ return ServiceManager.checkService(name) != null;
+ }
+
+ /**
+ * @see IpConnectivityMetrics.Logger
+ */
+ public IpConnectivityMetrics.Logger getMetricsLogger() {
+ return checkNotNull(LocalServices.getService(IpConnectivityMetrics.Logger.class),
+ "no IpConnectivityMetrics service");
+ }
+
+ /**
+ * @see IpConnectivityMetrics
+ */
+ public IIpConnectivityMetrics getIpConnectivityMetrics() {
+ return IIpConnectivityMetrics.Stub.asInterface(
+ ServiceManager.getService(IpConnectivityLog.SERVICE_NAME));
+ }
+ }
+
public ConnectivityService(Context context, INetworkManagementService netManager,
INetworkStatsService statsService, INetworkPolicyManager policyManager) {
- this(context, netManager, statsService, policyManager,
- getDnsResolver(), new IpConnectivityLog(), NetdService.getInstance());
+ this(context, netManager, statsService, policyManager, getDnsResolver(),
+ new IpConnectivityLog(), NetdService.getInstance(), new Dependencies());
}
@VisibleForTesting
protected ConnectivityService(Context context, INetworkManagementService netManager,
INetworkStatsService statsService, INetworkPolicyManager policyManager,
- IDnsResolver dnsresolver, IpConnectivityLog logger, INetd netd) {
+ IDnsResolver dnsresolver, IpConnectivityLog logger, INetd netd, Dependencies deps) {
if (DBG) log("ConnectivityService starting up");
- mSystemProperties = getSystemProperties();
+ mDeps = checkNotNull(deps, "missing Dependencies");
+ mSystemProperties = mDeps.getSystemProperties();
+ mNetIdManager = mDeps.makeNetIdManager();
mMetricsLog = logger;
mDefaultRequest = createDefaultInternetRequestForTransport(-1, NetworkRequest.Type.REQUEST);
@@ -862,7 +950,7 @@
mDefaultWifiRequest = createDefaultInternetRequestForTransport(
NetworkCapabilities.TRANSPORT_WIFI, NetworkRequest.Type.BACKGROUND_REQUEST);
- mHandlerThread = new HandlerThread("ConnectivityServiceThread");
+ mHandlerThread = mDeps.makeHandlerThread();
mHandlerThread.start();
mHandler = new InternalHandler(mHandlerThread.getLooper());
mTrackerHandler = new NetworkStateTrackerHandler(mHandlerThread.getLooper());
@@ -880,7 +968,7 @@
LocalServices.getService(NetworkPolicyManagerInternal.class),
"missing NetworkPolicyManagerInternal");
mDnsResolver = checkNotNull(dnsresolver, "missing IDnsResolver");
- mProxyTracker = makeProxyTracker();
+ mProxyTracker = mDeps.makeProxyTracker(mContext, mHandler);
mNetd = netd;
mKeyStore = KeyStore.getInstance();
@@ -948,7 +1036,7 @@
// Do the same for Ethernet, since it's often not specified in the configs, although many
// devices can use it via USB host adapters.
- if (mNetConfigs[TYPE_ETHERNET] == null && hasService(Context.ETHERNET_SERVICE)) {
+ if (mNetConfigs[TYPE_ETHERNET] == null && mDeps.hasService(Context.ETHERNET_SERVICE)) {
mLegacyTypeTracker.addSupportedType(TYPE_ETHERNET);
mNetworksDefined++;
}
@@ -968,7 +1056,8 @@
mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
- mTethering = makeTethering();
+ mTethering = deps.makeTethering(mContext, mNMS, mStatsService, mPolicyManager,
+ makeTetheringDependencies());
mPermissionMonitor = new PermissionMonitor(mContext, mNetd);
@@ -1027,7 +1116,7 @@
LingerMonitor.DEFAULT_NOTIFICATION_RATE_LIMIT_MILLIS);
mLingerMonitor = new LingerMonitor(mContext, mNotifier, dailyLimit, rateLimit);
- mMultinetworkPolicyTracker = createMultinetworkPolicyTracker(
+ mMultinetworkPolicyTracker = mDeps.makeMultinetworkPolicyTracker(
mContext, mHandler, () -> rematchForAvoidBadWifiUpdate());
mMultinetworkPolicyTracker.start();
@@ -1037,10 +1126,8 @@
registerPrivateDnsSettingsCallbacks();
}
- @VisibleForTesting
- protected Tethering makeTethering() {
- // TODO: Move other elements into @Overridden getters.
- final TetheringDependencies deps = new TetheringDependencies() {
+ private TetheringDependencies makeTetheringDependencies() {
+ return new TetheringDependencies() {
@Override
public boolean isTetheringSupported() {
return ConnectivityService.this.isTetheringSupported();
@@ -1050,14 +1137,6 @@
return mDefaultRequest;
}
};
- return new Tethering(mContext, mNMS, mStatsService, mPolicyManager,
- IoThread.get().getLooper(), new MockableSystemProperties(),
- deps);
- }
-
- @VisibleForTesting
- protected ProxyTracker makeProxyTracker() {
- return new ProxyTracker(mContext, mHandler, EVENT_PROXY_HAS_CHANGED);
}
private static NetworkCapabilities createDefaultNetworkCapabilitiesForUid(int uid) {
@@ -1149,22 +1228,6 @@
return mNextNetworkRequestId++;
}
- @VisibleForTesting
- protected int reserveNetId() {
- synchronized (mNetworkForNetId) {
- for (int i = MIN_NET_ID; i <= MAX_NET_ID; i++) {
- int netId = mNextNetId;
- if (++mNextNetId > MAX_NET_ID) mNextNetId = MIN_NET_ID;
- // Make sure NetID unused. http://b/16815182
- if (!mNetIdInUse.get(netId)) {
- mNetIdInUse.put(netId, true);
- return netId;
- }
- }
- }
- throw new IllegalStateException("No free netIds");
- }
-
private NetworkState getFilteredNetworkState(int networkType, int uid) {
if (mLegacyTypeTracker.isTypeSupported(networkType)) {
final NetworkAgentInfo nai = mLegacyTypeTracker.getNetworkForType(networkType);
@@ -1796,11 +1859,8 @@
}
};
- @VisibleForTesting
- protected void registerNetdEventCallback() {
- final IIpConnectivityMetrics ipConnectivityMetrics =
- IIpConnectivityMetrics.Stub.asInterface(
- ServiceManager.getService(IpConnectivityLog.SERVICE_NAME));
+ private void registerNetdEventCallback() {
+ final IIpConnectivityMetrics ipConnectivityMetrics = mDeps.getIpConnectivityMetrics();
if (ipConnectivityMetrics == null) {
Slog.wtf(TAG, "Missing IIpConnectivityMetrics");
return;
@@ -2236,12 +2296,6 @@
protected static final String DEFAULT_TCP_BUFFER_SIZES = "4096,87380,110208,4096,16384,110208";
private static final String DEFAULT_TCP_RWND_KEY = "net.tcp.default_init_rwnd";
- // Overridden for testing purposes to avoid writing to SystemProperties.
- @VisibleForTesting
- protected MockableSystemProperties getSystemProperties() {
- return new MockableSystemProperties();
- }
-
private void updateTcpBufferSizes(String tcpBufferSizes) {
String[] values = null;
if (tcpBufferSizes != null) {
@@ -2631,8 +2685,9 @@
}
if (valid != nai.lastValidated) {
if (wasDefault) {
- metricsLogger().defaultNetworkMetrics().logDefaultNetworkValidity(
- SystemClock.elapsedRealtime(), valid);
+ mDeps.getMetricsLogger()
+ .defaultNetworkMetrics().logDefaultNetworkValidity(
+ SystemClock.elapsedRealtime(), valid);
}
final int oldScore = nai.getCurrentScore();
nai.lastValidated = valid;
@@ -2967,8 +3022,8 @@
final boolean wasDefault = isDefaultNetwork(nai);
synchronized (mNetworkForNetId) {
mNetworkForNetId.remove(nai.network.netId);
- mNetIdInUse.delete(nai.network.netId);
}
+ mNetIdManager.releaseNetId(nai.network.netId);
// Just in case.
mLegacyTypeTracker.remove(nai, wasDefault);
}
@@ -3015,7 +3070,7 @@
// if there is a fallback. Taken together, the two form a X -> 0, 0 -> Y sequence
// whose timestamps tell how long it takes to recover a default network.
long now = SystemClock.elapsedRealtime();
- metricsLogger().defaultNetworkMetrics().logDefaultNetworkEvent(now, null, nai);
+ mDeps.getMetricsLogger().defaultNetworkMetrics().logDefaultNetworkEvent(now, null, nai);
}
notifyIfacesChangedForNetworkStats();
// TODO - we shouldn't send CALLBACK_LOST to requests that can be satisfied
@@ -3069,9 +3124,7 @@
destroyNativeNetwork(nai);
mDnsManager.removeNetwork(nai.network);
}
- synchronized (mNetworkForNetId) {
- mNetIdInUse.delete(nai.network.netId);
- }
+ mNetIdManager.releaseNetId(nai.network.netId);
}
private boolean createNativeNetwork(@NonNull NetworkAgentInfo networkAgent) {
@@ -4155,7 +4208,7 @@
return null;
}
return getLinkPropertiesProxyInfo(activeNetwork);
- } else if (queryUserAccess(Binder.getCallingUid(), network.netId)) {
+ } else if (mDeps.queryUserAccess(Binder.getCallingUid(), network.netId)) {
// Don't call getLinkProperties() as it requires ACCESS_NETWORK_STATE permission, which
// caller may not have.
return getLinkPropertiesProxyInfo(network);
@@ -4164,10 +4217,6 @@
return null;
}
- @VisibleForTesting
- protected boolean queryUserAccess(int uid, int netId) {
- return NetworkUtils.queryUserAccess(uid, netId);
- }
private ProxyInfo getLinkPropertiesProxyInfo(Network network) {
final NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
@@ -4760,7 +4809,7 @@
final long ident = Binder.clearCallingIdentity();
try {
// Concatenate the range of types onto the range of NetIDs.
- int id = MAX_NET_ID + 1 + (networkType - ConnectivityManager.TYPE_NONE);
+ int id = NetIdManager.MAX_NET_ID + 1 + (networkType - ConnectivityManager.TYPE_NONE);
mNotifier.setProvNotificationVisible(visible, id, action);
} finally {
Binder.restoreCallingIdentity(ident);
@@ -5371,10 +5420,9 @@
@GuardedBy("mNetworkForNetId")
private final SparseArray<NetworkAgentInfo> mNetworkForNetId = new SparseArray<>();
// NOTE: Accessed on multiple threads, synchronized with mNetworkForNetId.
- // An entry is first added to mNetIdInUse, prior to mNetworkForNetId, so
+ // An entry is first reserved with NetIdManager, prior to being added to mNetworkForNetId, so
// there may not be a strict 1:1 correlation between the two.
- @GuardedBy("mNetworkForNetId")
- private final SparseBooleanArray mNetIdInUse = new SparseBooleanArray();
+ private final NetIdManager mNetIdManager;
// NetworkAgentInfo keyed off its connecting messenger
// TODO - eval if we can reduce the number of lists/hashmaps/sparsearrays
@@ -5476,9 +5524,9 @@
// satisfies mDefaultRequest.
final NetworkCapabilities nc = new NetworkCapabilities(networkCapabilities);
final NetworkAgentInfo nai = new NetworkAgentInfo(messenger, new AsyncChannel(),
- new Network(reserveNetId()), new NetworkInfo(networkInfo), lp, nc, currentScore,
- mContext, mTrackerHandler, new NetworkMisc(networkMisc), this, mNetd, mDnsResolver,
- mNMS, factorySerialNumber);
+ new Network(mNetIdManager.reserveNetId()), new NetworkInfo(networkInfo), lp, nc,
+ currentScore, mContext, mTrackerHandler, new NetworkMisc(networkMisc), this, mNetd,
+ mDnsResolver, mNMS, factorySerialNumber);
// Make sure the network capabilities reflect what the agent info says.
nai.setNetworkCapabilities(mixInCapabilities(nai, nc));
final String extraInfo = networkInfo.getExtraInfo();
@@ -5487,7 +5535,7 @@
if (DBG) log("registerNetworkAgent " + nai);
final long token = Binder.clearCallingIdentity();
try {
- getNetworkStack().makeNetworkMonitor(
+ mDeps.getNetworkStack().makeNetworkMonitor(
nai.network, name, new NetworkMonitorCallbacks(nai));
} finally {
Binder.restoreCallingIdentity(token);
@@ -5499,11 +5547,6 @@
return nai.network.netId;
}
- @VisibleForTesting
- protected NetworkStackClient getNetworkStack() {
- return NetworkStackClient.getInstance();
- }
-
private void handleRegisterNetworkAgent(NetworkAgentInfo nai, INetworkMonitor networkMonitor) {
nai.onNetworkMonitorCreated(networkMonitor);
if (VDBG) log("Got NetworkAgent Messenger");
@@ -5519,7 +5562,6 @@
}
nai.asyncChannel.connect(mContext, mTrackerHandler, nai.messenger);
NetworkInfo networkInfo = nai.networkInfo;
- nai.networkInfo = null;
updateNetworkInfo(nai, networkInfo);
updateUids(nai, null, nai.networkCapabilities);
}
@@ -6313,7 +6355,7 @@
// Notify system services that this network is up.
makeDefault(newNetwork);
// Log 0 -> X and Y -> X default network transitions, where X is the new default.
- metricsLogger().defaultNetworkMetrics().logDefaultNetworkEvent(
+ mDeps.getMetricsLogger().defaultNetworkMetrics().logDefaultNetworkEvent(
now, newNetwork, oldDefaultNetwork);
// Have a new default network, release the transition wakelock in
scheduleReleaseNetworkTransitionWakelock();
@@ -6518,8 +6560,7 @@
if (DBG) {
log(networkAgent.name() + " EVENT_NETWORK_INFO_CHANGED, going from " +
- (oldInfo == null ? "null" : oldInfo.getState()) +
- " to " + state);
+ oldInfo.getState() + " to " + state);
}
if (!networkAgent.created
@@ -6592,8 +6633,8 @@
// TODO(b/122649188): send the broadcast only to VPN users.
mProxyTracker.sendProxyBroadcast();
}
- } else if ((oldInfo != null && oldInfo.getState() == NetworkInfo.State.SUSPENDED) ||
- state == NetworkInfo.State.SUSPENDED) {
+ } else if (networkAgent.created && (oldInfo.getState() == NetworkInfo.State.SUSPENDED ||
+ state == NetworkInfo.State.SUSPENDED)) {
// going into or coming out of SUSPEND: re-score and notify
if (networkAgent.getCurrentScore() != oldScore) {
rematchAllNetworksAndRequests(networkAgent, oldScore);
@@ -6985,27 +7026,6 @@
return nwm.getWatchlistConfigHash();
}
- @VisibleForTesting
- MultinetworkPolicyTracker createMultinetworkPolicyTracker(Context c, Handler h, Runnable r) {
- return new MultinetworkPolicyTracker(c, h, r);
- }
-
- @VisibleForTesting
- public WakeupMessage makeWakeupMessage(Context c, Handler h, String s, int cmd, Object obj) {
- return new WakeupMessage(c, h, s, cmd, 0, 0, obj);
- }
-
- @VisibleForTesting
- public boolean hasService(String name) {
- return ServiceManager.checkService(name) != null;
- }
-
- @VisibleForTesting
- protected IpConnectivityMetrics.Logger metricsLogger() {
- return checkNotNull(LocalServices.getService(IpConnectivityMetrics.Logger.class),
- "no IpConnectivityMetrics service");
- }
-
private void logNetworkEvent(NetworkAgentInfo nai, int evtype) {
int[] transports = nai.networkCapabilities.getTransportTypes();
mMetricsLog.log(nai.network.netId, transports, new NetworkEvent(evtype));
diff --git a/services/core/java/com/android/server/GraphicsStatsService.java b/services/core/java/com/android/server/GraphicsStatsService.java
index 4639d75..70569db 100644
--- a/services/core/java/com/android/server/GraphicsStatsService.java
+++ b/services/core/java/com/android/server/GraphicsStatsService.java
@@ -191,7 +191,7 @@
if (!file.getFileDescriptor().valid()) {
throw new IllegalStateException("Invalid file descriptor");
}
- return new ParcelFileDescriptor(file.getFileDescriptor());
+ return ParcelFileDescriptor.dup(file.getFileDescriptor());
} catch (IOException ex) {
throw new IllegalStateException("Failed to get PFD from memory file", ex);
}
diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java
index b4e1c32..a0946a0 100644
--- a/services/core/java/com/android/server/IpSecService.java
+++ b/services/core/java/com/android/server/IpSecService.java
@@ -750,10 +750,10 @@
}
}
- // These values have been reserved in ConnectivityService
+ // These values have been reserved in NetIdManager
@VisibleForTesting static final int TUN_INTF_NETID_START = 0xFC00;
- @VisibleForTesting static final int TUN_INTF_NETID_RANGE = 0x0400;
+ public static final int TUN_INTF_NETID_RANGE = 0x0400;
private final SparseBooleanArray mTunnelNetIds = new SparseBooleanArray();
private int mNextTunnelNetIdIndex = 0;
diff --git a/services/core/java/com/android/server/NetIdManager.java b/services/core/java/com/android/server/NetIdManager.java
new file mode 100644
index 0000000..11533be
--- /dev/null
+++ b/services/core/java/com/android/server/NetIdManager.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import android.annotation.NonNull;
+import android.util.SparseBooleanArray;
+
+import com.android.internal.annotations.GuardedBy;
+
+/**
+ * Class used to reserve and release net IDs.
+ *
+ * <p>Instances of this class are thread-safe.
+ */
+public class NetIdManager {
+ // Sequence number for Networks; keep in sync with system/netd/NetworkController.cpp
+ public static final int MIN_NET_ID = 100; // some reserved marks
+ // Top IDs reserved by IpSecService
+ public static final int MAX_NET_ID = 65535 - IpSecService.TUN_INTF_NETID_RANGE;
+
+ @GuardedBy("mNetIdInUse")
+ private final SparseBooleanArray mNetIdInUse = new SparseBooleanArray();
+
+ @GuardedBy("mNetIdInUse")
+ private int mLastNetId = MIN_NET_ID - 1;
+
+ /**
+ * Get the first netId that follows the provided lastId and is available.
+ */
+ private static int getNextAvailableNetIdLocked(
+ int lastId, @NonNull SparseBooleanArray netIdInUse) {
+ int netId = lastId;
+ for (int i = MIN_NET_ID; i <= MAX_NET_ID; i++) {
+ netId = netId < MAX_NET_ID ? netId + 1 : MIN_NET_ID;
+ if (!netIdInUse.get(netId)) {
+ return netId;
+ }
+ }
+ throw new IllegalStateException("No free netIds");
+ }
+
+ /**
+ * Reserve a new ID for a network.
+ */
+ public int reserveNetId() {
+ synchronized (mNetIdInUse) {
+ mLastNetId = getNextAvailableNetIdLocked(mLastNetId, mNetIdInUse);
+ // Make sure NetID unused. http://b/16815182
+ mNetIdInUse.put(mLastNetId, true);
+ return mLastNetId;
+ }
+ }
+
+ /**
+ * Clear a previously reserved ID for a network.
+ */
+ public void releaseNetId(int id) {
+ synchronized (mNetIdInUse) {
+ mNetIdInUse.delete(id);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 371e517..d46758c 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -1452,10 +1452,11 @@
}
private void start() {
- connect();
+ connectStoraged();
+ connectVold();
}
- private void connect() {
+ private void connectStoraged() {
IBinder binder = ServiceManager.getService("storaged");
if (binder != null) {
try {
@@ -1464,7 +1465,7 @@
public void binderDied() {
Slog.w(TAG, "storaged died; reconnecting");
mStoraged = null;
- connect();
+ connectStoraged();
}
}, 0);
} catch (RemoteException e) {
@@ -1478,7 +1479,17 @@
Slog.w(TAG, "storaged not found; trying again");
}
- binder = ServiceManager.getService("vold");
+ if (mStoraged == null) {
+ BackgroundThread.getHandler().postDelayed(() -> {
+ connectStoraged();
+ }, DateUtils.SECOND_IN_MILLIS);
+ } else {
+ onDaemonConnected();
+ }
+ }
+
+ private void connectVold() {
+ IBinder binder = ServiceManager.getService("vold");
if (binder != null) {
try {
binder.linkToDeath(new DeathRecipient() {
@@ -1486,7 +1497,7 @@
public void binderDied() {
Slog.w(TAG, "vold died; reconnecting");
mVold = null;
- connect();
+ connectVold();
}
}, 0);
} catch (RemoteException e) {
@@ -1506,9 +1517,9 @@
Slog.w(TAG, "vold not found; trying again");
}
- if (mStoraged == null || mVold == null) {
+ if (mVold == null) {
BackgroundThread.getHandler().postDelayed(() -> {
- connect();
+ connectVold();
}, DateUtils.SECOND_IN_MILLIS);
} else {
onDaemonConnected();
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 534d76c..52ec0ad 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -1027,7 +1027,12 @@
log(str);
}
mLocalLog.log(str);
- if (validatePhoneId(phoneId)) {
+ // for service state updates, don't notify clients when subId is invalid. This prevents
+ // us from sending incorrect notifications like b/133140128
+ // In the future, we can remove this logic for every notification here and add a
+ // callback so listeners know when their PhoneStateListener's subId becomes invalid, but
+ // for now we use the simplest fix.
+ if (validatePhoneId(phoneId) && SubscriptionManager.isValidSubscriptionId(subId)) {
mServiceState[phoneId] = state;
for (Record r : mRecords) {
@@ -1059,7 +1064,8 @@
}
}
} else {
- log("notifyServiceStateForSubscriber: INVALID phoneId=" + phoneId);
+ log("notifyServiceStateForSubscriber: INVALID phoneId=" + phoneId
+ + " or subId=" + subId);
}
handleRemoveListLocked();
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 4eafe7c..e3cb5ad 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -452,20 +452,10 @@
import com.android.server.SystemServiceManager;
import com.android.server.ThreadPriorityBooster;
import com.android.server.Watchdog;
-import com.android.server.am.ActivityManagerServiceDumpActivitiesProto;
-import com.android.server.am.ActivityManagerServiceDumpBroadcastsProto;
-import com.android.server.am.ActivityManagerServiceDumpProcessesProto;
import com.android.server.am.ActivityManagerServiceDumpProcessesProto.UidObserverRegistrationProto;
-import com.android.server.am.ActivityManagerServiceDumpServicesProto;
import com.android.server.am.ActivityStack.ActivityState;
-import com.android.server.am.GrantUriProto;
-import com.android.server.am.ImportanceTokenProto;
-import com.android.server.am.MemInfoDumpProto;
import com.android.server.am.MemoryStatUtil.MemoryStat;
-import com.android.server.am.NeededUriGrantsProto;
-import com.android.server.am.ProcessOomProto;
-import com.android.server.am.ProcessToGcProto;
-import com.android.server.am.StickyBroadcastProto;
+import com.android.server.compat.CompatConfig;
import com.android.server.firewall.IntentFirewall;
import com.android.server.job.JobSchedulerInternal;
import com.android.server.pm.Installer;
@@ -478,12 +468,12 @@
import dalvik.system.VMRuntime;
-import com.google.android.collect.Lists;
-import com.google.android.collect.Maps;
-
import libcore.io.IoUtils;
import libcore.util.EmptyArray;
+import com.google.android.collect.Lists;
+import com.google.android.collect.Maps;
+
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
@@ -4212,6 +4202,7 @@
}
checkTime(startTime, "startProcess: done removing from pids map");
app.setPid(0);
+ app.startSeq = 0;
}
if (DEBUG_PROCESSES && mProcessesOnHold.contains(app)) Slog.v(TAG_PROCESSES,
@@ -4403,6 +4394,14 @@
app.killedByAm = false;
app.removed = false;
app.killed = false;
+ if (app.startSeq != 0) {
+ Slog.wtf(TAG, "startProcessLocked processName:" + app.processName
+ + " with non-zero startSeq:" + app.startSeq);
+ }
+ if (app.pid != 0) {
+ Slog.wtf(TAG, "startProcessLocked processName:" + app.processName
+ + " with non-zero pid:" + app.pid);
+ }
final long startSeq = app.startSeq = ++mProcStartSeqCounter;
app.setStartParams(uid, hostingType, hostingNameStr, seInfo, startTime);
if (mConstants.FLAG_PROCESS_START_ASYNC) {
@@ -4588,8 +4587,11 @@
// If there is already an app occupying that pid that hasn't been cleaned up
if (oldApp != null && !app.isolated) {
// Clean up anything relating to this pid first
- Slog.w(TAG, "Reusing pid " + pid
- + " while app is still mapped to it");
+ Slog.wtf(TAG, "handleProcessStartedLocked process:" + app.processName
+ + " startSeq:" + app.startSeq
+ + " pid:" + pid
+ + " belongs to another existing app:" + oldApp.processName
+ + " startSeq:" + oldApp.startSeq);
cleanUpApplicationRecordLocked(oldApp, false, false, -1,
true /*replacingPid*/);
}
@@ -7445,6 +7447,26 @@
synchronized (mPidsSelfLocked) {
app = mPidsSelfLocked.get(pid);
}
+ if (app != null && (app.startUid != callingUid || app.startSeq != startSeq)) {
+ String processName = null;
+ final ProcessRecord pending = mPendingStarts.get(startSeq);
+ if (pending != null) {
+ processName = pending.processName;
+ }
+ final String msg = "attachApplicationLocked process:" + processName
+ + " startSeq:" + startSeq
+ + " pid:" + pid
+ + " belongs to another existing app:" + app.processName
+ + " startSeq:" + app.startSeq;
+ Slog.wtf(TAG, msg);
+ // SafetyNet logging for b/131105245.
+ EventLog.writeEvent(0x534e4554, "131105245", app.startUid, msg);
+ // If there is already an app occupying that pid that hasn't been cleaned up
+ cleanUpApplicationRecordLocked(app, false, false, -1,
+ true /*replacingPid*/);
+ mPidsSelfLocked.remove(pid);
+ app = null;
+ }
} else {
app = null;
}
@@ -7453,7 +7475,7 @@
// update the internal state.
if (app == null && startSeq > 0) {
final ProcessRecord pending = mPendingStarts.get(startSeq);
- if (pending != null && pending.startUid == callingUid
+ if (pending != null && pending.startUid == callingUid && pending.startSeq == startSeq
&& handleProcessStartedLocked(pending, pid, pending.usingWrapper,
startSeq, true)) {
app = pending;
@@ -7673,6 +7695,7 @@
checkTime(startTime, "attachApplicationLocked: immediately before bindApplication");
bindApplicationTimeMillis = SystemClock.elapsedRealtime();
mStackSupervisor.getActivityMetricsLogger().notifyBindApplication(app);
+ long[] disabledCompatChanges = CompatConfig.get().getDisabledChanges(app.info);
if (app.isolatedEntryPoint != null) {
// This is an isolated process which should just call an entry point instead of
// being bound to an application.
@@ -7688,7 +7711,7 @@
new Configuration(getGlobalConfiguration()), app.compat,
getCommonServicesLocked(app.isolated),
mCoreSettingsObserver.getCoreSettingsLocked(),
- buildSerial, isAutofillCompatEnabled);
+ buildSerial, isAutofillCompatEnabled, disabledCompatChanges);
} else {
thread.bindApplication(processName, appInfo, providers, null, profilerInfo,
null, null, null, testMode,
@@ -7697,7 +7720,7 @@
new Configuration(getGlobalConfiguration()), app.compat,
getCommonServicesLocked(app.isolated),
mCoreSettingsObserver.getCoreSettingsLocked(),
- buildSerial, isAutofillCompatEnabled);
+ buildSerial, isAutofillCompatEnabled, disabledCompatChanges);
}
if (profilerInfo != null) {
profilerInfo.closeFd();
@@ -7907,6 +7930,10 @@
}
}
+ // Let the ART runtime in zygote and system_server know that the boot completed.
+ ZYGOTE_PROCESS.bootCompleted();
+ VMRuntime.bootCompleted();
+
IntentFilter pkgFilter = new IntentFilter();
pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
pkgFilter.addDataScheme("package");
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 3399a76..6596cff 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -16,6 +16,15 @@
package com.android.server.am;
+import static android.app.ActivityManager.RESIZE_MODE_SYSTEM;
+import static android.app.ActivityManager.RESIZE_MODE_USER;
+import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.view.Display.INVALID_DISPLAY;
+
+import static com.android.server.am.TaskRecord.INVALID_TASK_ID;
+
import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.app.AppGlobals;
@@ -74,6 +83,7 @@
import com.android.internal.util.HexDump;
import com.android.internal.util.MemInfoReader;
import com.android.internal.util.Preconditions;
+import com.android.server.compat.CompatConfig;
import java.io.BufferedReader;
import java.io.File;
@@ -96,15 +106,6 @@
import javax.microedition.khronos.egl.EGLDisplay;
import javax.microedition.khronos.egl.EGLSurface;
-import static android.app.ActivityManager.RESIZE_MODE_SYSTEM;
-import static android.app.ActivityManager.RESIZE_MODE_USER;
-import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
-import static android.view.Display.INVALID_DISPLAY;
-
-import static com.android.server.am.TaskRecord.INVALID_TASK_ID;
-
final class ActivityManagerShellCommand extends ShellCommand {
public static final String NO_CLASS_ERROR_CODE = "Error type 3";
private static final String SHELL_PACKAGE_NAME = "com.android.shell";
@@ -277,6 +278,8 @@
return runNoHomeScreen(pw);
case "wait-for-broadcast-idle":
return runWaitForBroadcastIdle(pw);
+ case "compat":
+ return runCompat(pw);
default:
return handleDefaultCommands(cmd);
}
@@ -2794,6 +2797,50 @@
return 0;
}
+ private int runCompat(PrintWriter pw) {
+ final CompatConfig config = CompatConfig.get();
+ String toggleValue = getNextArgRequired();
+ long changeId;
+ String changeIdString = getNextArgRequired();
+ try {
+ changeId = Long.parseLong(changeIdString);
+ } catch (NumberFormatException e) {
+ changeId = config.lookupChangeId(changeIdString);
+ }
+ if (changeId == -1) {
+ pw.println("Unknown or invalid change: '" + changeIdString + "'.");
+ }
+ String packageName = getNextArgRequired();
+ switch(toggleValue) {
+ case "enable":
+ if (!config.addOverride(changeId, packageName, true)) {
+ pw.println("Warning! Change " + changeId + " is not known yet. Enabling it"
+ + " could have no effect.");
+ }
+ pw.println("Enabled change " + changeId + " for " + packageName + ".");
+ return 0;
+ case "disable":
+ if (!config.addOverride(changeId, packageName, false)) {
+ pw.println("Warning! Change " + changeId + " is not known yet. Disabling it"
+ + " could have no effect.");
+ }
+ pw.println("Disabled change " + changeId + " for " + packageName + ".");
+ return 0;
+ case "reset":
+ if (config.removeOverride(changeId, packageName)) {
+ pw.println("Reset change " + changeId + " for " + packageName
+ + " to default value.");
+ } else {
+ pw.println("No override exists for changeId " + changeId + ".");
+ }
+ return 0;
+ default:
+ pw.println("Invalid toggle value: '" + toggleValue + "'.");
+ }
+ return -1;
+ }
+
+
private Resources getResources(PrintWriter pw) throws RemoteException {
// system resources does not contain all the device configuration, construct it manually.
Configuration config = mInterface.getConfiguration();
@@ -3090,6 +3137,8 @@
pw.println(" without restarting any processes.");
pw.println(" write");
pw.println(" Write all pending state to storage.");
+ pw.println(" compat enable|disable|reset <CHANGE_ID|CHANGE_NAME> <PACKAGE_NAME>");
+ pw.println(" Toggles a change either by id or by name for <PACKAGE_NAME>.");
pw.println();
Intent.printIntentArgsHelp(pw, "");
}
diff --git a/services/core/java/com/android/server/am/LockTaskController.java b/services/core/java/com/android/server/am/LockTaskController.java
index 151ef49..bd0506b 100644
--- a/services/core/java/com/android/server/am/LockTaskController.java
+++ b/services/core/java/com/android/server/am/LockTaskController.java
@@ -777,18 +777,24 @@
* leaves the pinned mode.
*/
private void lockKeyguardIfNeeded() {
+ if (shouldLockKeyguard()) {
+ mWindowManager.lockNow(null);
+ mWindowManager.dismissKeyguard(null /* callback */, null /* message */);
+ getLockPatternUtils().requireCredentialEntry(USER_ALL);
+ }
+ }
+
+ private boolean shouldLockKeyguard() {
+ // This functionality should be kept consistent with
+ // com.android.settings.security.ScreenPinningSettings (see b/127605586)
try {
- boolean shouldLockKeyguard = Settings.Secure.getIntForUser(
+ return Settings.Secure.getIntForUser(
mContext.getContentResolver(),
- Settings.Secure.LOCK_TO_APP_EXIT_LOCKED,
- USER_CURRENT) != 0;
- if (shouldLockKeyguard) {
- mWindowManager.lockNow(null);
- mWindowManager.dismissKeyguard(null /* callback */, null /* message */);
- getLockPatternUtils().requireCredentialEntry(USER_ALL);
- }
+ Settings.Secure.LOCK_TO_APP_EXIT_LOCKED, USER_CURRENT) != 0;
} catch (Settings.SettingNotFoundException e) {
- // No setting, don't lock.
+ // Log to SafetyNet for b/127605586
+ android.util.EventLog.writeEvent(0x534e4554, "127605586", -1, "");
+ return getLockPatternUtils().isSecure(USER_CURRENT);
}
}
diff --git a/services/core/java/com/android/server/compat/CompatChange.java b/services/core/java/com/android/server/compat/CompatChange.java
index bb3b9be..bc5973d 100644
--- a/services/core/java/com/android/server/compat/CompatChange.java
+++ b/services/core/java/com/android/server/compat/CompatChange.java
@@ -20,6 +20,8 @@
import android.compat.annotation.EnabledAfter;
import android.content.pm.ApplicationInfo;
+import com.android.server.compat.config.Change;
+
import java.util.HashMap;
import java.util.Map;
@@ -60,6 +62,16 @@
mDisabled = disabled;
}
+ /**
+ * @param change an object generated by services/core/xsd/platform-compat-config.xsd
+ */
+ public CompatChange(Change change) {
+ mChangeId = change.getId();
+ mName = change.getName();
+ mEnableAfterTargetSdk = change.getEnableAfterTargetSdk();
+ mDisabled = change.getDisabled();
+ }
+
long getId() {
return mChangeId;
}
diff --git a/services/core/java/com/android/server/compat/CompatConfig.java b/services/core/java/com/android/server/compat/CompatConfig.java
index fea5d83..044e417 100644
--- a/services/core/java/com/android/server/compat/CompatConfig.java
+++ b/services/core/java/com/android/server/compat/CompatConfig.java
@@ -17,13 +17,27 @@
package com.android.server.compat;
import android.content.pm.ApplicationInfo;
+import android.os.Environment;
import android.text.TextUtils;
import android.util.LongArray;
import android.util.LongSparseArray;
+import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.compat.config.Change;
+import com.android.server.compat.config.XmlParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintWriter;
+
+import javax.xml.datatype.DatatypeConfigurationException;
/**
* This class maintains state relating to platform compatibility changes.
*
@@ -32,7 +46,12 @@
*/
public final class CompatConfig {
- private static final CompatConfig sInstance = new CompatConfig();
+ private static final String TAG = "CompatConfig";
+ private static final String CONFIG_FILE_SUFFIX = "platform_compat_config.xml";
+
+ private static final CompatConfig sInstance = new CompatConfig().initConfigFromLib(
+ Environment.buildPath(
+ Environment.getRootDirectory(), "etc", "sysconfig"));
@GuardedBy("mChanges")
private final LongSparseArray<CompatChange> mChanges = new LongSparseArray<>();
@@ -128,20 +147,24 @@
* <p>Note, package overrides are not persistent and will be lost on system or runtime restart.
*
* @param changeId The ID of the change to be overridden. Note, this call will succeed even if
- * this change is not known; it will only have any affect if any code in the
+ * this change is not known; it will only have any effect if any code in the
* platform is gated on the ID given.
* @param packageName The app package name to override the change for.
* @param enabled If the change should be enabled or disabled.
+ * @return {@code true} if the change existed before adding the override.
*/
- public void addOverride(long changeId, String packageName, boolean enabled) {
+ public boolean addOverride(long changeId, String packageName, boolean enabled) {
+ boolean alreadyKnown = true;
synchronized (mChanges) {
CompatChange c = mChanges.get(changeId);
if (c == null) {
+ alreadyKnown = false;
c = new CompatChange(changeId);
addChange(c);
}
c.addPackageOverride(packageName, enabled);
}
+ return alreadyKnown;
}
/**
@@ -151,14 +174,61 @@
*
* @param changeId The ID of the change that was overridden.
* @param packageName The app package name that was overridden.
+ * @return {@code true} if an override existed;
*/
- public void removeOverride(long changeId, String packageName) {
+ public boolean removeOverride(long changeId, String packageName) {
+ boolean overrideExists = false;
synchronized (mChanges) {
CompatChange c = mChanges.get(changeId);
if (c != null) {
+ overrideExists = true;
c.removePackageOverride(packageName);
}
}
+ return overrideExists;
+ }
+
+ /**
+ * Dumps the current list of compatibility config information.
+ *
+ * @param pw The {@link PrintWriter} instance to which the information will be dumped.
+ */
+ public void dumpConfig(PrintWriter pw) {
+ synchronized (mChanges) {
+ if (mChanges.size() == 0) {
+ pw.println("No compat overrides.");
+ return;
+ }
+ for (int i = 0; i < mChanges.size(); ++i) {
+ CompatChange c = mChanges.valueAt(i);
+ pw.println(c.toString());
+ }
+ }
+ }
+
+ CompatConfig initConfigFromLib(File libraryDir) {
+ if (!libraryDir.exists() || !libraryDir.isDirectory()) {
+ Slog.e(TAG, "No directory " + libraryDir + ", skipping");
+ return this;
+ }
+ for (File f : libraryDir.listFiles()) {
+ //TODO(b/138222363): Handle duplicate ids across config files.
+ if (f.getPath().endsWith(CONFIG_FILE_SUFFIX)) {
+ readConfig(f);
+ }
+ }
+ return this;
+ }
+
+ private void readConfig(File configFile) {
+ try (InputStream in = new BufferedInputStream(new FileInputStream(configFile))) {
+ for (Change change : XmlParser.read(in).getCompatChange()) {
+ Slog.w(TAG, "Adding: " + change.toString());
+ addChange(new CompatChange(change));
+ }
+ } catch (IOException | DatatypeConfigurationException | XmlPullParserException e) {
+ Slog.e(TAG, "Encountered an error while reading/parsing compat config file", e);
+ }
}
}
diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java
index 456d15e..fc38735 100644
--- a/services/core/java/com/android/server/compat/PlatformCompat.java
+++ b/services/core/java/com/android/server/compat/PlatformCompat.java
@@ -16,52 +16,47 @@
package com.android.server.compat;
+import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.util.Slog;
+import com.android.internal.compat.IPlatformCompat;
+import com.android.internal.util.DumpUtils;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
/**
* System server internal API for gating and reporting compatibility changes.
*/
-public class PlatformCompat {
+public class PlatformCompat extends IPlatformCompat.Stub {
private static final String TAG = "Compatibility";
- /**
- * Reports that a compatibility change is affecting an app process now.
- *
- * <p>Note: for changes that are gated using {@link #isChangeEnabled(long, ApplicationInfo)},
- * you do not need to call this API directly. The change will be reported for you in the case
- * that {@link #isChangeEnabled(long, ApplicationInfo)} returns {@code true}.
- *
- * @param changeId The ID of the compatibility change taking effect.
- * @param appInfo Representing the affected app.
- */
- public static void reportChange(long changeId, ApplicationInfo appInfo) {
+ private final Context mContext;
+
+ public PlatformCompat(Context context) {
+ mContext = context;
+ }
+
+ @Override
+ public void reportChange(long changeId, ApplicationInfo appInfo) {
Slog.d(TAG, "Compat change reported: " + changeId + "; UID " + appInfo.uid);
// TODO log via StatsLog
}
- /**
- * Query if a given compatibility change is enabled for an app process. This method should
- * be called when implementing functionality on behalf of the affected app.
- *
- * <p>If this method returns {@code true}, the calling code should implement the compatibility
- * change, resulting in differing behaviour compared to earlier releases. If this method returns
- * {@code false}, the calling code should behave as it did in earlier releases.
- *
- * <p>When this method returns {@code true}, it will also report the change as
- * {@link #reportChange(long, ApplicationInfo)} would, so there is no need to call that method
- * directly.
- *
- * @param changeId The ID of the compatibility change in question.
- * @param appInfo Representing the app in question.
- * @return {@code true} if the change is enabled for the current app.
- */
- public static boolean isChangeEnabled(long changeId, ApplicationInfo appInfo) {
+ @Override
+ public boolean isChangeEnabled(long changeId, ApplicationInfo appInfo) {
if (CompatConfig.get().isChangeEnabled(changeId, appInfo)) {
reportChange(changeId, appInfo);
return true;
}
return false;
}
+
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, "platform_compat", pw)) return;
+ CompatConfig.get().dumpConfig(pw);
+ }
}
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index 5b04379..96b7cb3 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -16,6 +16,7 @@
package com.android.server.connectivity;
+import android.annotation.NonNull;
import android.content.Context;
import android.net.IDnsResolver;
import android.net.INetd;
@@ -116,7 +117,7 @@
// not, ConnectivityService disconnects the NetworkAgent's AsyncChannel.
public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
- public NetworkInfo networkInfo;
+ @NonNull public NetworkInfo networkInfo;
// This Network object should always be used if possible, so as to encourage reuse of the
// enclosed socket factory and connection pool. Avoid creating other Network objects.
// This Network object is always valid.
@@ -579,10 +580,12 @@
}
if (newExpiry > 0) {
- mLingerMessage = mConnService.makeWakeupMessage(
+ mLingerMessage = new WakeupMessage(
mContext, mHandler,
- "NETWORK_LINGER_COMPLETE." + network.netId,
- EVENT_NETWORK_LINGER_COMPLETE, this);
+ "NETWORK_LINGER_COMPLETE." + network.netId /* cmdName */,
+ EVENT_NETWORK_LINGER_COMPLETE /* cmd */,
+ 0 /* arg1 (unused) */, 0 /* arg2 (unused) */,
+ this /* obj (NetworkAgentInfo) */);
mLingerMessage.schedule(newExpiry);
}
diff --git a/services/core/java/com/android/server/connectivity/PermissionMonitor.java b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
index fbe2589..29c4bad 100644
--- a/services/core/java/com/android/server/connectivity/PermissionMonitor.java
+++ b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
@@ -130,6 +130,11 @@
}
@Override
+ public void onPackageChanged(@NonNull String packageName, int uid) {
+ sendPackagePermissionsForUid(uid, getPermissionForUid(uid));
+ }
+
+ @Override
public void onPackageRemoved(String packageName, int uid) {
sendPackagePermissionsForUid(uid, getPermissionForUid(uid));
}
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index 4957eed..86d1212 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -772,7 +772,6 @@
case WifiManager.WIFI_AP_STATE_FAILED:
default:
disableWifiIpServingLocked(ifname, curState);
- mEntitlementMgr.stopProvisioningIfNeeded(TETHERING_WIFI);
break;
}
}
@@ -1315,11 +1314,15 @@
mOffload.excludeDownstreamInterface(who.interfaceName());
mForwardedDownstreams.remove(who);
- // If this is a Wi-Fi interface, tell WifiManager of any errors.
+ // If this is a Wi-Fi interface, tell WifiManager of any errors
+ // or the inactive serving state.
if (who.interfaceType() == TETHERING_WIFI) {
if (who.lastError() != TETHER_ERROR_NO_ERROR) {
getWifiManager().updateInterfaceIpState(
who.interfaceName(), IFACE_IP_MODE_CONFIGURATION_ERROR);
+ } else {
+ getWifiManager().updateInterfaceIpState(
+ who.interfaceName(), IFACE_IP_MODE_UNSPECIFIED);
}
}
}
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index e7a8b13..1bd29e5 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -58,7 +58,6 @@
import android.net.NetworkInfo;
import android.net.NetworkInfo.DetailedState;
import android.net.NetworkMisc;
-import android.net.NetworkUtils;
import android.net.RouteInfo;
import android.net.UidRange;
import android.net.VpnService;
@@ -114,7 +113,6 @@
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
-import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.Set;
@@ -902,38 +900,6 @@
}
/**
- * Analyzes the passed LinkedProperties to figure out whether it routes to most of the IP space.
- *
- * This returns true if the passed LinkedProperties contains routes to either most of the IPv4
- * space or to most of the IPv6 address space, where "most" is defined by the value of the
- * MOST_IPV{4,6}_ADDRESSES_COUNT constants : if more than this number of addresses are matched
- * by any of the routes, then it's decided that most of the space is routed.
- * @hide
- */
- @VisibleForTesting
- static boolean providesRoutesToMostDestinations(LinkProperties lp) {
- final List<RouteInfo> routes = lp.getAllRoutes();
- if (routes.size() > MAX_ROUTES_TO_EVALUATE) return true;
- final Comparator<IpPrefix> prefixLengthComparator = IpPrefix.lengthComparator();
- TreeSet<IpPrefix> ipv4Prefixes = new TreeSet<>(prefixLengthComparator);
- TreeSet<IpPrefix> ipv6Prefixes = new TreeSet<>(prefixLengthComparator);
- for (final RouteInfo route : routes) {
- if (route.getType() == RouteInfo.RTN_UNREACHABLE) continue;
- IpPrefix destination = route.getDestination();
- if (destination.isIPv4()) {
- ipv4Prefixes.add(destination);
- } else {
- ipv6Prefixes.add(destination);
- }
- }
- if (NetworkUtils.routedIPv4AddressCount(ipv4Prefixes) > MOST_IPV4_ADDRESSES_COUNT) {
- return true;
- }
- return NetworkUtils.routedIPv6AddressCount(ipv6Prefixes)
- .compareTo(MOST_IPV6_ADDRESSES_COUNT) >= 0;
- }
-
- /**
* Attempt to perform a seamless handover of VPNs by only updating LinkProperties without
* registering a new NetworkAgent. This is not always possible if the new VPN configuration
* has certain changes, in which case this method would just return {@code false}.
@@ -1079,7 +1045,8 @@
// TEMP use the old jni calls until there is support for netd address setting
StringBuilder builder = new StringBuilder();
for (LinkAddress address : config.addresses) {
- builder.append(" " + address);
+ builder.append(" ");
+ builder.append(address);
}
if (jniSetAddresses(interfaze, builder.toString()) < 1) {
throw new IllegalArgumentException("At least one address must be specified");
@@ -1163,7 +1130,7 @@
// Note: Return type guarantees results are deduped and sorted, which callers require.
private SortedSet<Integer> getAppsUids(List<String> packageNames, int userHandle) {
- SortedSet<Integer> uids = new TreeSet<Integer>();
+ SortedSet<Integer> uids = new TreeSet<>();
for (String app : packageNames) {
int uid = getAppUid(app, userHandle);
if (uid != -1) uids.add(uid);
@@ -1266,7 +1233,7 @@
// UidRange#createForUser returns the entire range of UIDs available to a macro-user.
// This is something like 0-99999 ; {@see UserHandle#PER_USER_RANGE}
final UidRange userRange = UidRange.createForUser(userHandle);
- final List<UidRange> ranges = new ArrayList<UidRange>();
+ final List<UidRange> ranges = new ArrayList<>();
for (UidRange range : existingRanges) {
if (userRange.containsRange(range)) {
ranges.add(range);
@@ -1765,7 +1732,7 @@
byte[] value = keyStore.get(Credentials.USER_CERTIFICATE + profile.ipsecServerCert);
serverCert = (value == null) ? null : new String(value, StandardCharsets.UTF_8);
}
- if (privateKey == null || userCert == null || caCert == null || serverCert == null) {
+ if (userCert == null || caCert == null || serverCert == null) {
throw new IllegalStateException("Cannot load credentials");
}
@@ -1884,7 +1851,7 @@
* Return the information of the current ongoing legacy VPN.
* Callers are responsible for checking permissions if needed.
*/
- public synchronized LegacyVpnInfo getLegacyVpnInfoPrivileged() {
+ private synchronized LegacyVpnInfo getLegacyVpnInfoPrivileged() {
if (mLegacyVpnRunner == null) return null;
final LegacyVpnInfo info = new LegacyVpnInfo();
@@ -2038,7 +2005,6 @@
private void bringup() {
// Catch all exceptions so we can clean up a few things.
- boolean initFinished = false;
try {
// Initialize the timer.
mBringupStartTime = SystemClock.elapsedRealtime();
@@ -2057,7 +2023,6 @@
throw new IllegalStateException("Cannot delete the state");
}
new File("/data/misc/vpn/abort").delete();
- initFinished = true;
// Check if we need to restart any of the daemons.
boolean restart = false;
diff --git a/services/core/java/com/android/server/connectivity/tethering/EntitlementManager.java b/services/core/java/com/android/server/connectivity/tethering/EntitlementManager.java
index 836f1e6..f952bce 100644
--- a/services/core/java/com/android/server/connectivity/tethering/EntitlementManager.java
+++ b/services/core/java/com/android/server/connectivity/tethering/EntitlementManager.java
@@ -161,6 +161,12 @@
* Check if cellular upstream is permitted.
*/
public boolean isCellularUpstreamPermitted() {
+ // If provisioning is required and EntitlementManager don't know any downstream,
+ // cellular upstream should not be allowed.
+ final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration();
+ if (mCurrentTethers.size() == 0 && isTetherProvisioningRequired(config)) {
+ return false;
+ }
return mCellularUpstreamPermitted;
}
diff --git a/services/core/java/com/android/server/display/OWNERS b/services/core/java/com/android/server/display/OWNERS
index 98e3299..e2f06bf 100644
--- a/services/core/java/com/android/server/display/OWNERS
+++ b/services/core/java/com/android/server/display/OWNERS
@@ -1,5 +1,6 @@
michaelwr@google.com
hackbod@google.com
ogunwale@google.com
+santoscordon@google.com
per-file ColorDisplayService.java=christyfranks@google.com
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecKeycode.java b/services/core/java/com/android/server/hdmi/HdmiCecKeycode.java
index adc1cd7..8148216 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecKeycode.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecKeycode.java
@@ -290,8 +290,7 @@
new KeycodeEntry(KeyEvent.KEYCODE_CHANNEL_UP, CEC_KEYCODE_CHANNEL_UP),
new KeycodeEntry(KeyEvent.KEYCODE_CHANNEL_DOWN, CEC_KEYCODE_CHANNEL_DOWN),
new KeycodeEntry(KeyEvent.KEYCODE_LAST_CHANNEL, CEC_KEYCODE_PREVIOUS_CHANNEL),
- // No Android keycode defined for CEC_KEYCODE_SOUND_SELECT
- new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_SOUND_SELECT),
+ new KeycodeEntry(KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK, CEC_KEYCODE_SOUND_SELECT),
new KeycodeEntry(KeyEvent.KEYCODE_TV_INPUT, CEC_KEYCODE_INPUT_SELECT),
new KeycodeEntry(KeyEvent.KEYCODE_INFO, CEC_KEYCODE_DISPLAY_INFORMATION),
// No Android keycode defined for CEC_KEYCODE_HELP
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 25ca278..3a6987f 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -850,7 +850,11 @@
mSystemAudioActivated = on;
mService.announceSystemAudioModeChange(on);
}
- startArcAction(on);
+ if (on && !mArcEstablished) {
+ startArcAction(true);
+ } else if (!on) {
+ startArcAction(false);
+ }
}
}
diff --git a/services/core/java/com/android/server/job/JobStore.java b/services/core/java/com/android/server/job/JobStore.java
index 4f8b1dc..dad435b 100644
--- a/services/core/java/com/android/server/job/JobStore.java
+++ b/services/core/java/com/android/server/job/JobStore.java
@@ -492,6 +492,9 @@
if (jobStatus.hasBatteryNotLowConstraint()) {
out.attribute(null, "battery-not-low", Boolean.toString(true));
}
+ if (jobStatus.hasStorageNotLowConstraint()) {
+ out.attribute(null, "storage-not-low", Boolean.toString(true));
+ }
out.endTag(null, XML_TAG_PARAMS_CONSTRAINTS);
}
@@ -852,6 +855,15 @@
jobBuilder.setExtras(extras);
parser.nextTag(); // Consume </extras>
+ final JobInfo builtJob;
+ try {
+ builtJob = jobBuilder.build();
+ } catch (Exception e) {
+ Slog.w(TAG, "Unable to build job from XML, ignoring: "
+ + jobBuilder.summarize());
+ return null;
+ }
+
// Migrate sync jobs forward from earlier, incomplete representation
if ("android".equals(sourcePackageName)
&& extras != null
@@ -935,6 +947,14 @@
if (val != null) {
jobBuilder.setRequiresCharging(true);
}
+ val = parser.getAttributeValue(null, "battery-not-low");
+ if (val != null) {
+ jobBuilder.setRequiresBatteryNotLow(true);
+ }
+ val = parser.getAttributeValue(null, "storage-not-low");
+ if (val != null) {
+ jobBuilder.setRequiresStorageNotLow(true);
+ }
}
/**
diff --git a/services/core/java/com/android/server/media/OWNERS b/services/core/java/com/android/server/media/OWNERS
index 4bc9373..b460cb5 100644
--- a/services/core/java/com/android/server/media/OWNERS
+++ b/services/core/java/com/android/server/media/OWNERS
@@ -2,5 +2,6 @@
hdmoon@google.com
insun@google.com
jaewan@google.com
+klhyun@google.com
lajos@google.com
sungsoo@google.com
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 0188f7c..b241a67 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -795,8 +795,22 @@
@Override
public void onNotificationError(int callingUid, int callingPid, String pkg, String tag, int id,
int uid, int initialPid, String message, int userId) {
- cancelNotification(callingUid, callingPid, pkg, tag, id, 0, 0, false, userId,
- REASON_ERROR, null);
+ final boolean fgService;
+ synchronized (mNotificationLock) {
+ NotificationRecord r = findNotificationLocked(pkg, tag, id, userId);
+ fgService = r != null && (r.getNotification().flags & FLAG_FOREGROUND_SERVICE) != 0;
+ }
+ cancelNotification(callingUid, callingPid, pkg, tag, id, 0, 0,
+ false, userId, REASON_ERROR, null);
+ if (fgService) {
+ // Still crash for foreground services, preventing the not-crash behaviour abused
+ // by apps to give us a garbage notification and silently start a fg service.
+ Binder.withCleanCallingIdentity(
+ () -> mAm.crashApplication(uid, initialPid, pkg, -1,
+ "Bad notification(tag=" + tag + ", id=" + id + ") posted from package "
+ + pkg + ", crashing app(uid=" + uid + ", pid=" + initialPid + "): "
+ + message));
+ }
}
@Override
diff --git a/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java b/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java
index c98a79a..714bbb9 100644
--- a/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java
+++ b/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java
@@ -297,19 +297,5 @@
}
mDs.asBinder().unlinkToDeath(this, 0);
}
-
- // Old methods; unused in the API flow.
- @Override
- public void onProgressUpdated(int progress) throws RemoteException {
- }
-
- @Override
- public void onMaxProgressUpdated(int maxProgress) throws RemoteException {
- }
-
- @Override
- public void onSectionComplete(String title, int status, int size, int durationMs)
- throws RemoteException {
- }
}
}
diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
index 2b33ace..c712431 100644
--- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java
+++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
@@ -281,20 +281,7 @@
mAbortIdleOptimization.set(false);
long lowStorageThreshold = getLowStorageThreshold(context);
- // Optimize primary apks.
- int result = optimizePackages(pm, pkgs, lowStorageThreshold,
- /*isForPrimaryDex=*/ true);
- if (result == OPTIMIZE_ABORT_BY_JOB_SCHEDULER) {
- return result;
- }
- if (supportSecondaryDex()) {
- result = reconcileSecondaryDexFiles(pm.getDexManager());
- if (result == OPTIMIZE_ABORT_BY_JOB_SCHEDULER) {
- return result;
- }
- result = optimizePackages(pm, pkgs, lowStorageThreshold,
- /*isForPrimaryDex=*/ false);
- }
+ int result = idleOptimizePackages(pm, pkgs, lowStorageThreshold);
return result;
}
@@ -342,46 +329,88 @@
return 0;
}
- private int optimizePackages(PackageManagerService pm, ArraySet<String> pkgs,
- long lowStorageThreshold, boolean isForPrimaryDex) {
+ private int idleOptimizePackages(PackageManagerService pm, ArraySet<String> pkgs,
+ long lowStorageThreshold) {
ArraySet<String> updatedPackages = new ArraySet<>();
- Set<String> unusedPackages = pm.getUnusedPackages(mDowngradeUnusedAppsThresholdInMillis);
- boolean hadSomeLowSpaceFailure = false;
- Log.d(TAG, "Unsused Packages " + String.join(",", unusedPackages));
- // Only downgrade apps when space is low on device.
- // Threshold is selected above the lowStorageThreshold so that we can pro-actively clean
- // up disk before user hits the actual lowStorageThreshold.
- final long lowStorageThresholdForDowngrade = LOW_THRESHOLD_MULTIPLIER_FOR_DOWNGRADE *
- lowStorageThreshold;
- boolean shouldDowngrade = shouldDowngrade(lowStorageThresholdForDowngrade);
- Log.d(TAG, "Should Downgrade " + shouldDowngrade);
- boolean dex_opt_performed = false;
- for (String pkg : pkgs) {
- int abort_code = abortIdleOptimizations(lowStorageThreshold);
- if (abort_code == OPTIMIZE_ABORT_BY_JOB_SCHEDULER) {
- return abort_code;
- }
- // Downgrade unused packages.
- if (unusedPackages.contains(pkg) && shouldDowngrade) {
- dex_opt_performed = downgradePackage(pm, pkg, isForPrimaryDex);
- } else {
- if (abort_code == OPTIMIZE_ABORT_NO_SPACE_LEFT) {
- // can't dexopt because of low space.
- hadSomeLowSpaceFailure = true;
- continue;
+
+ try {
+ final boolean supportSecondaryDex = supportSecondaryDex();
+
+ if (supportSecondaryDex) {
+ int result = reconcileSecondaryDexFiles(pm.getDexManager());
+ if (result == OPTIMIZE_ABORT_BY_JOB_SCHEDULER) {
+ return result;
}
- dex_opt_performed = optimizePackage(pm, pkg, isForPrimaryDex);
}
- if (dex_opt_performed) {
+
+ // Only downgrade apps when space is low on device.
+ // Threshold is selected above the lowStorageThreshold so that we can pro-actively clean
+ // up disk before user hits the actual lowStorageThreshold.
+ final long lowStorageThresholdForDowngrade = LOW_THRESHOLD_MULTIPLIER_FOR_DOWNGRADE
+ * lowStorageThreshold;
+ boolean shouldDowngrade = shouldDowngrade(lowStorageThresholdForDowngrade);
+ Log.d(TAG, "Should Downgrade " + shouldDowngrade);
+ if (shouldDowngrade) {
+ Set<String> unusedPackages =
+ pm.getUnusedPackages(mDowngradeUnusedAppsThresholdInMillis);
+ Log.d(TAG, "Unsused Packages " + String.join(",", unusedPackages));
+
+ if (!unusedPackages.isEmpty()) {
+ for (String pkg : unusedPackages) {
+ int abortCode = abortIdleOptimizations(/*lowStorageThreshold*/ -1);
+ if (abortCode != OPTIMIZE_CONTINUE) {
+ // Should be aborted by the scheduler.
+ return abortCode;
+ }
+ if (downgradePackage(pm, pkg, /*isForPrimaryDex*/ true)) {
+ updatedPackages.add(pkg);
+ }
+ if (supportSecondaryDex) {
+ downgradePackage(pm, pkg, /*isForPrimaryDex*/ false);
+ }
+ }
+
+ pkgs = new ArraySet<>(pkgs);
+ pkgs.removeAll(unusedPackages);
+ }
+ }
+
+ int primaryResult = optimizePackages(pm, pkgs, lowStorageThreshold,
+ /*isForPrimaryDex*/ true, updatedPackages);
+ if (primaryResult != OPTIMIZE_PROCESSED) {
+ return primaryResult;
+ }
+
+ if (!supportSecondaryDex) {
+ return OPTIMIZE_PROCESSED;
+ }
+
+ int secondaryResult = optimizePackages(pm, pkgs, lowStorageThreshold,
+ /*isForPrimaryDex*/ false, updatedPackages);
+ return secondaryResult;
+ } finally {
+ // Always let the pinner service know about changes.
+ notifyPinService(updatedPackages);
+ }
+ }
+
+ private int optimizePackages(PackageManagerService pm, ArraySet<String> pkgs,
+ long lowStorageThreshold, boolean isForPrimaryDex, ArraySet<String> updatedPackages) {
+ for (String pkg : pkgs) {
+ int abortCode = abortIdleOptimizations(lowStorageThreshold);
+ if (abortCode != OPTIMIZE_CONTINUE) {
+ // Either aborted by the scheduler or no space left.
+ return abortCode;
+ }
+
+ boolean dexOptPerformed = optimizePackage(pm, pkg, isForPrimaryDex);
+ if (dexOptPerformed) {
updatedPackages.add(pkg);
}
}
-
- notifyPinService(updatedPackages);
- return hadSomeLowSpaceFailure ? OPTIMIZE_ABORT_NO_SPACE_LEFT : OPTIMIZE_PROCESSED;
+ return OPTIMIZE_PROCESSED;
}
-
/**
* Try to downgrade the package to a smaller compilation filter.
* eg. if the package is in speed-profile the package will be downgraded to verify.
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index aac8dad..5b83f44 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -2155,6 +2155,8 @@
if (allNewUsers && !update) {
notifyPackageAdded(packageName, res.uid);
+ } else {
+ notifyPackageChanged(packageName, res.uid);
}
// Log current value of "unknown sources" setting
@@ -8900,10 +8902,10 @@
+ " better than this " + pkg.getLongVersionCode());
}
- // Verify certificates against what was last scanned. If it is an updated priv app, we will
- // force re-collecting certificate.
- final boolean forceCollect = PackageManagerServiceUtils.isApkVerificationForced(
- disabledPkgSetting);
+ // Verify certificates against what was last scanned. If there was an upgrade or this is an
+ // updated priv app, we will force re-collecting certificate.
+ final boolean forceCollect = mIsUpgrade ||
+ PackageManagerServiceUtils.isApkVerificationForced(disabledPkgSetting);
// Full APK verification can be skipped during certificate collection, only if the file is
// in verified partition, or can be verified on access (when apk verity is enabled). In both
// cases, only data in Signing Block is verified instead of the whole file.
@@ -13762,6 +13764,22 @@
}
@Override
+ public void notifyPackageChanged(String packageName, int uid) {
+ final PackageListObserver[] observers;
+ synchronized (mPackages) {
+ if (mPackageListObservers.size() == 0) {
+ return;
+ }
+ final PackageListObserver[] observerArray =
+ new PackageListObserver[mPackageListObservers.size()];
+ observers = mPackageListObservers.toArray(observerArray);
+ }
+ for (int i = observers.length - 1; i >= 0; --i) {
+ observers[i].onPackageChanged(packageName, uid);
+ }
+ }
+
+ @Override
public void notifyPackageRemoved(String packageName, int uid) {
final PackageListObserver[] observers;
synchronized (mPackages) {
@@ -24987,5 +25005,6 @@
void sendPackageAddedForNewUsers(String packageName, boolean sendBootCompleted,
boolean includeStopped, int appId, int[] userIds, int[] instantUserIds);
void notifyPackageAdded(String packageName, int uid);
+ void notifyPackageChanged(String packageName, int uid);
void notifyPackageRemoved(String packageName, int uid);
}
diff --git a/services/core/java/com/android/server/tv/TvInputHardwareManager.java b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
index f08e585..6ea274d 100755
--- a/services/core/java/com/android/server/tv/TvInputHardwareManager.java
+++ b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
@@ -403,6 +403,10 @@
|| checkUidChangedLocked(connection, callingUid, resolvedUserId)) {
return;
}
+ ITvInputHardwareCallback callback = connection.getCallbackLocked();
+ if (callback != null) {
+ callback.asBinder().unlinkToDeath(connection, 0);
+ }
connection.resetLocked(null, null, null, null, null);
}
}
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index b2677cb..ef57ca1 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -840,6 +840,10 @@
private void setStateLocked(String inputId, int state, int userId) {
UserState userState = getOrCreateUserStateLocked(userId);
TvInputState inputState = userState.inputMap.get(inputId);
+ if (inputState == null) {
+ Slog.e(TAG, "failed to setStateLocked - unknown input id " + inputId);
+ return;
+ }
ServiceState serviceState = userState.serviceStateMap.get(inputState.info.getComponent());
int oldState = inputState.state;
inputState.state = state;
diff --git a/services/core/xsd/Android.bp b/services/core/xsd/Android.bp
index 98e4343..8b2cbbd 100644
--- a/services/core/xsd/Android.bp
+++ b/services/core/xsd/Android.bp
@@ -4,3 +4,11 @@
api_dir: "schema",
package_name: "com.android.server.pm.permission.configfile",
}
+
+
+xsd_config {
+ name: "platform-compat-config",
+ srcs: ["platform-compat-config.xsd"],
+ api_dir: "platform-compat-schema",
+ package_name: "com.android.server.compat.config",
+}
diff --git a/services/core/xsd/platform-compat-config.xsd b/services/core/xsd/platform-compat-config.xsd
new file mode 100644
index 0000000..ee39e50
--- /dev/null
+++ b/services/core/xsd/platform-compat-config.xsd
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<!-- This defines the format of the XML file generated by
+ ~ com.android.compat.annotation.ChangeIdProcessor annotation processor (from
+ ~ tools/platform-compat), and is parsed in com/android/server/compat/CompatConfig.java.
+-->
+<xs:schema version="2.0" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
+
+ <xs:complexType name="change">
+ <xs:simpleContent>
+ <xs:extension base="xs:string">
+ <xs:attribute type="xs:long" name="id" use="required"/>
+ <xs:attribute type="xs:string" name="name" use="required"/>
+ <xs:attribute type="xs:boolean" name="disabled"/>
+ <xs:attribute type="xs:int" name="enableAfterTargetSdk"/>
+ </xs:extension>
+ </xs:simpleContent>
+ </xs:complexType>
+
+ <xs:element name="config">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="compat-change" type="change" maxOccurs="unbounded"
+ minOccurs="0"/>
+ </xs:sequence>
+ </xs:complexType>
+ <xs:unique name="UniqueId">
+ <xs:selector xpath="compat-change" />
+ <xs:field xpath="@id" />
+ </xs:unique>
+ </xs:element>
+</xs:schema>
+
+
+
+
diff --git a/services/core/xsd/platform-compat-schema/current.txt b/services/core/xsd/platform-compat-schema/current.txt
new file mode 100644
index 0000000..8456785
--- /dev/null
+++ b/services/core/xsd/platform-compat-schema/current.txt
@@ -0,0 +1,31 @@
+// Signature format: 2.0
+package com.android.server.compat.config {
+
+ public class Change {
+ ctor public Change();
+ method public boolean getDisabled();
+ method public int getEnableAfterTargetSdk();
+ method public long getId();
+ method public String getName();
+ method public String getValue();
+ method public void setDisabled(boolean);
+ method public void setEnableAfterTargetSdk(int);
+ method public void setId(long);
+ method public void setName(String);
+ method public void setValue(String);
+ }
+
+ public class Config {
+ ctor public Config();
+ method public java.util.List<com.android.server.compat.config.Change> getCompatChange();
+ }
+
+ public class XmlParser {
+ ctor public XmlParser();
+ method public static com.android.server.compat.config.Config read(java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+ method public static String readText(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+ method public static void skip(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+ }
+
+}
+
diff --git a/services/core/xsd/platform-compat-schema/last_current.txt b/services/core/xsd/platform-compat-schema/last_current.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/services/core/xsd/platform-compat-schema/last_current.txt
diff --git a/services/core/xsd/platform-compat-schema/last_removed.txt b/services/core/xsd/platform-compat-schema/last_removed.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/services/core/xsd/platform-compat-schema/last_removed.txt
diff --git a/services/core/xsd/platform-compat-schema/removed.txt b/services/core/xsd/platform-compat-schema/removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/services/core/xsd/platform-compat-schema/removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index dab1603..1ce72c5 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -37,6 +37,7 @@
import android.database.sqlite.SQLiteCompatibilityWalFlags;
import android.database.sqlite.SQLiteGlobal;
import android.hardware.display.DisplayManagerInternal;
+import android.net.ConnectivityModuleConnector;
import android.net.NetworkStackClient;
import android.os.BaseBundle;
import android.os.Binder;
@@ -79,6 +80,7 @@
import com.android.server.broadcastradio.BroadcastRadioService;
import com.android.server.camera.CameraServiceProxy;
import com.android.server.clipboard.ClipboardService;
+import com.android.server.compat.PlatformCompat;
import com.android.server.connectivity.IpConnectivityMetrics;
import com.android.server.coverage.CoverageService;
import com.android.server.devicepolicy.DevicePolicyManagerService;
@@ -561,6 +563,13 @@
SystemServerInitThreadPool.get().submit(SystemConfig::getInstance, TAG_SYSTEM_CONFIG);
traceEnd();
+ // Platform compat service is used by ActivityManagerService, PackageManagerService, and
+ // possibly others in the future. b/135010838.
+ traceBeginAndSlog("PlatformCompat");
+ ServiceManager.addService(Context.PLATFORM_COMPAT_SERVICE,
+ new PlatformCompat(mSystemContext));
+ traceEnd();
+
// Wait for installd to finish starting up so that it has a chance to
// create critical directories such as /data/user with the appropriate
// permissions. We need this to complete before we initialize other services.
@@ -975,6 +984,7 @@
traceBeginAndSlog("PinnerService");
mSystemServiceManager.startService(PinnerService.class);
traceEnd();
+
} catch (RuntimeException e) {
Slog.e("System", "******************************************");
Slog.e("System", "************ Failure starting core service", e);
@@ -1123,6 +1133,14 @@
mSystemServiceManager.startService(ClipboardService.class);
traceEnd();
+ traceBeginAndSlog("InitConnectivityModuleConnector");
+ try {
+ ConnectivityModuleConnector.getInstance().init(context);
+ } catch (Throwable e) {
+ reportWtf("initializing ConnectivityModuleConnector", e);
+ }
+ traceEnd();
+
traceBeginAndSlog("InitNetworkStackClient");
try {
NetworkStackClient.getInstance().init();
@@ -1931,7 +1949,7 @@
// ActivityManagerService.mSystemReady and ActivityManagerService.mProcessesReady
// are set to true. Be careful if moving this to a different place in the
// startup sequence.
- NetworkStackClient.getInstance().start(context);
+ NetworkStackClient.getInstance().start();
} catch (Throwable e) {
reportWtf("starting Network Stack", e);
}
diff --git a/services/net/java/android/net/ConnectivityModuleConnector.java b/services/net/java/android/net/ConnectivityModuleConnector.java
new file mode 100644
index 0000000..e84dac2
--- /dev/null
+++ b/services/net/java/android/net/ConnectivityModuleConnector.java
@@ -0,0 +1,423 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.net;
+
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
+import android.net.util.SharedLog;
+import android.os.Build;
+import android.os.Environment;
+import android.os.IBinder;
+import android.os.Process;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.text.format.DateUtils;
+import android.util.ArraySet;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.io.File;
+import java.io.PrintWriter;
+
+/**
+ * Class used to communicate to the various networking mainline modules running in the network stack
+ * process from {@link com.android.server.SystemServer}.
+ * @hide
+ */
+public class ConnectivityModuleConnector {
+ private static final String TAG = ConnectivityModuleConnector.class.getSimpleName();
+ private static final String IN_PROCESS_SUFFIX = ".InProcess";
+
+ private static final String PREFS_FILE = "ConnectivityModuleConnector.xml";
+ private static final String PREF_KEY_LAST_CRASH_TIME = "lastcrash_time";
+ private static final String CONFIG_MIN_CRASH_INTERVAL_MS = "min_crash_interval";
+ private static final String CONFIG_MIN_UPTIME_BEFORE_CRASH_MS = "min_uptime_before_crash";
+ private static final String CONFIG_ALWAYS_RATELIMIT_NETWORKSTACK_CRASH =
+ "always_ratelimit_networkstack_crash";
+
+ // Even if the network stack is lost, do not crash the system more often than this.
+ // Connectivity would be broken, but if the user needs the device for something urgent
+ // (like calling emergency services) we should not bootloop the device.
+ // This is the default value: the actual value can be adjusted via device config.
+ private static final long DEFAULT_MIN_CRASH_INTERVAL_MS = 6 * DateUtils.HOUR_IN_MILLIS;
+
+ // Even if the network stack is lost, do not crash the system server if it was less than
+ // this much after boot. This avoids bootlooping the device, and crashes should address very
+ // infrequent failures, not failures on boot.
+ private static final long DEFAULT_MIN_UPTIME_BEFORE_CRASH_MS = 30 * DateUtils.MINUTE_IN_MILLIS;
+
+ private static ConnectivityModuleConnector sInstance;
+
+ private Context mContext;
+ @GuardedBy("mLog")
+ private final SharedLog mLog = new SharedLog(TAG);
+ @GuardedBy("mHealthListeners")
+ private final ArraySet<ConnectivityModuleHealthListener> mHealthListeners = new ArraySet<>();
+
+ private ConnectivityModuleConnector() { }
+
+ /**
+ * Get the {@link ConnectivityModuleConnector} singleton instance.
+ */
+ public static synchronized ConnectivityModuleConnector getInstance() {
+ if (sInstance == null) {
+ sInstance = new ConnectivityModuleConnector();
+ }
+ return sInstance;
+ }
+
+ /**
+ * Initialize the network stack connector. Should be called only once on device startup, before
+ * any client attempts to use the network stack.
+ */
+ public void init(Context context) {
+ log("Network stack init");
+ mContext = context;
+ }
+
+ /**
+ * Callback interface for severe failures of the NetworkStack.
+ *
+ * <p>Useful for health monitors such as PackageWatchdog.
+ */
+ public interface ConnectivityModuleHealthListener {
+ /**
+ * Called when there is a severe failure of the network stack.
+ * @param packageName Package name of the network stack.
+ */
+ void onNetworkStackFailure(@NonNull String packageName);
+ }
+
+ /**
+ * Callback invoked by the connector once the connection to the corresponding module is
+ * established.
+ */
+ public interface ModuleServiceCallback {
+ /**
+ * Invoked when the corresponding service has connected.
+ *
+ * @param iBinder Binder object for the service.
+ */
+ void onModuleServiceConnected(@NonNull IBinder iBinder);
+ }
+
+
+ /**
+ * Add a {@link ConnectivityModuleHealthListener} to listen to network stack health events.
+ */
+ public void registerHealthListener(@NonNull ConnectivityModuleHealthListener listener) {
+ synchronized (mHealthListeners) {
+ mHealthListeners.add(listener);
+ }
+ }
+
+ /**
+ * Start a module running in the network stack or system_server process. Should be called only
+ * once for each module per device startup.
+ *
+ * <p>This method will start a networking module either in the network stack
+ * process, or inside the system server on devices that do not support the corresponding
+ * mainline network . The corresponding networking module service's binder
+ * object will then be delivered asynchronously via the provided {@link ModuleServiceCallback}.
+ *
+ * @param serviceIntentBaseAction Base action to use for constructing the intent needed to
+ * bind to the corresponding module.
+ * @param servicePermissionName Permission to be held by the corresponding module.
+ */
+ public void startModuleService(
+ @NonNull String serviceIntentBaseAction,
+ @NonNull String servicePermissionName,
+ @NonNull ModuleServiceCallback callback) {
+ log("Starting networking module " + serviceIntentBaseAction);
+
+ final PackageManager pm = mContext.getPackageManager();
+
+ // Try to bind in-process if the device was shipped with an in-process version
+ Intent intent = getModuleServiceIntent(pm, serviceIntentBaseAction, servicePermissionName,
+ true /* inSystemProcess */);
+
+ // Otherwise use the updatable module version
+ if (intent == null) {
+ intent = getModuleServiceIntent(pm, serviceIntentBaseAction, servicePermissionName,
+ false /* inSystemProcess */);
+ log("Starting networking module in network_stack process");
+ } else {
+ log("Starting networking module in system_server process");
+ }
+
+ if (intent == null) {
+ maybeCrashWithTerribleFailure("Could not resolve the networking module", null);
+ return;
+ }
+
+ final String packageName = intent.getComponent().getPackageName();
+
+ // Start the network stack. The service will be added to the service manager by the
+ // corresponding client in ModuleServiceCallback.onModuleServiceConnected().
+ if (!mContext.bindServiceAsUser(
+ intent, new ModuleServiceConnection(packageName, callback),
+ Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT, UserHandle.SYSTEM)) {
+ maybeCrashWithTerribleFailure(
+ "Could not bind to networking module in-process, or in app with "
+ + intent, packageName);
+ return;
+ }
+
+ log("Networking module service start requested");
+ }
+
+ private class ModuleServiceConnection implements ServiceConnection {
+ @NonNull
+ private final String mPackageName;
+ @NonNull
+ private final ModuleServiceCallback mModuleServiceCallback;
+
+ private ModuleServiceConnection(
+ @NonNull String packageName,
+ @NonNull ModuleServiceCallback moduleCallback) {
+ mPackageName = packageName;
+ mModuleServiceCallback = moduleCallback;
+ }
+
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ logi("Networking module service connected");
+ mModuleServiceCallback.onModuleServiceConnected(service);
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ // onServiceDisconnected is not being called on device shutdown, so this method being
+ // called always indicates a bad state for the system server.
+ // 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);
+ }
+ }
+
+ @Nullable
+ private Intent getModuleServiceIntent(
+ @NonNull PackageManager pm, @NonNull String serviceIntentBaseAction,
+ @NonNull String servicePermissionName, boolean inSystemProcess) {
+ final Intent intent =
+ new Intent(inSystemProcess
+ ? serviceIntentBaseAction + IN_PROCESS_SUFFIX
+ : serviceIntentBaseAction);
+ final ComponentName comp = intent.resolveSystemService(pm, 0);
+ if (comp == null) {
+ return null;
+ }
+ intent.setComponent(comp);
+
+ int uid = -1;
+ try {
+ uid = pm.getPackageUidAsUser(comp.getPackageName(), UserHandle.USER_SYSTEM);
+ } catch (PackageManager.NameNotFoundException e) {
+ logWtf("Networking module package not found", e);
+ // Fall through
+ }
+
+ final int expectedUid = inSystemProcess ? Process.SYSTEM_UID : Process.NETWORK_STACK_UID;
+ if (uid != expectedUid) {
+ throw new SecurityException("Invalid network stack UID: " + uid);
+ }
+
+ if (!inSystemProcess) {
+ checkModuleServicePermission(pm, comp, servicePermissionName);
+ }
+
+ return intent;
+ }
+
+ private void checkModuleServicePermission(
+ @NonNull PackageManager pm, @NonNull ComponentName comp,
+ @NonNull String servicePermissionName) {
+ final int hasPermission =
+ pm.checkPermission(servicePermissionName, comp.getPackageName());
+ if (hasPermission != PERMISSION_GRANTED) {
+ throw new SecurityException(
+ "Networking module does not have permission " + servicePermissionName);
+ }
+ }
+
+ private synchronized void maybeCrashWithTerribleFailure(@NonNull String message,
+ @Nullable String packageName) {
+ logWtf(message, null);
+ // Called DeviceConfig to minimize merge conflicts
+ final DeviceConfigStub DeviceConfig = new DeviceConfigStub(mContext);
+ // uptime is monotonic even after a framework restart
+ final long uptime = SystemClock.elapsedRealtime();
+ final long now = System.currentTimeMillis();
+ final long minCrashIntervalMs = DeviceConfig.getLong(DeviceConfig.NAMESPACE_CONNECTIVITY,
+ CONFIG_MIN_CRASH_INTERVAL_MS, DEFAULT_MIN_CRASH_INTERVAL_MS);
+ final long minUptimeBeforeCrash = DeviceConfig.getLong(DeviceConfig.NAMESPACE_CONNECTIVITY,
+ CONFIG_MIN_UPTIME_BEFORE_CRASH_MS, DEFAULT_MIN_UPTIME_BEFORE_CRASH_MS);
+ final boolean alwaysRatelimit = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_CONNECTIVITY,
+ CONFIG_ALWAYS_RATELIMIT_NETWORKSTACK_CRASH, false);
+
+ final SharedPreferences prefs = getSharedPreferences();
+ final long lastCrashTime = tryGetLastCrashTime(prefs);
+
+ // Only crash if there was enough time since boot, and (if known) enough time passed since
+ // the last crash.
+ // time and lastCrashTime may be unreliable if devices have incorrect clock time, but they
+ // are only used to limit the number of crashes compared to only using the time since boot,
+ // which would also be OK behavior by itself.
+ // - If lastCrashTime is incorrectly more than the current time, only look at uptime
+ // - If it is much less than current time, only look at uptime
+ // - If current time is during the next few hours after last crash time, don't crash.
+ // Considering that this only matters if last boot was some time ago, it's likely that
+ // time will be set correctly. Otherwise, not crashing is not a big problem anyway. Being
+ // in this last state would also not last for long since the window is only a few hours.
+ final boolean alwaysCrash = Build.IS_DEBUGGABLE && !alwaysRatelimit;
+ final boolean justBooted = uptime < minUptimeBeforeCrash;
+ final boolean haveLastCrashTime = (lastCrashTime != 0) && (lastCrashTime < now);
+ final boolean haveKnownRecentCrash =
+ haveLastCrashTime && (now < lastCrashTime + minCrashIntervalMs);
+ if (alwaysCrash || (!justBooted && !haveKnownRecentCrash)) {
+ // The system is not bound to its network stack (for example due to a crash in the
+ // network stack process): better crash rather than stay in a bad state where all
+ // networking is broken.
+ // Using device-encrypted SharedPreferences as DeviceConfig does not have a synchronous
+ // API to persist settings before a crash.
+ tryWriteLastCrashTime(prefs, now);
+ throw new IllegalStateException(message);
+ }
+
+ // Here the system crashed recently already. Inform listeners that something is
+ // definitely wrong.
+ if (packageName != null) {
+ final ArraySet<ConnectivityModuleHealthListener> listeners;
+ synchronized (mHealthListeners) {
+ listeners = new ArraySet<>(mHealthListeners);
+ }
+ for (ConnectivityModuleHealthListener listener : listeners) {
+ listener.onNetworkStackFailure(packageName);
+ }
+ }
+ }
+
+ @Nullable
+ private SharedPreferences getSharedPreferences() {
+ try {
+ final File prefsFile = new File(
+ Environment.getDataSystemDeDirectory(UserHandle.USER_SYSTEM), PREFS_FILE);
+ return mContext.createDeviceProtectedStorageContext()
+ .getSharedPreferences(prefsFile, Context.MODE_PRIVATE);
+ } catch (Throwable e) {
+ logWtf("Error loading shared preferences", e);
+ return null;
+ }
+ }
+
+ private long tryGetLastCrashTime(@Nullable SharedPreferences prefs) {
+ if (prefs == null) return 0L;
+ try {
+ return prefs.getLong(PREF_KEY_LAST_CRASH_TIME, 0L);
+ } catch (Throwable e) {
+ logWtf("Error getting last crash time", e);
+ return 0L;
+ }
+ }
+
+ private void tryWriteLastCrashTime(@Nullable SharedPreferences prefs, long value) {
+ if (prefs == null) return;
+ try {
+ prefs.edit().putLong(PREF_KEY_LAST_CRASH_TIME, value).commit();
+ } catch (Throwable e) {
+ logWtf("Error writing last crash time", e);
+ }
+ }
+
+ private void log(@NonNull String message) {
+ Slog.d(TAG, message);
+ synchronized (mLog) {
+ mLog.log(message);
+ }
+ }
+
+ private void logWtf(@NonNull String message, @Nullable Throwable e) {
+ Slog.wtf(TAG, message, e);
+ synchronized (mLog) {
+ mLog.e(message);
+ }
+ }
+
+ private void loge(@NonNull String message, @Nullable Throwable e) {
+ Slog.e(TAG, message, e);
+ synchronized (mLog) {
+ mLog.e(message);
+ }
+ }
+
+ private void logi(@NonNull String message) {
+ Slog.i(TAG, message);
+ synchronized (mLog) {
+ mLog.i(message);
+ }
+ }
+
+ /**
+ * Dump ConnectivityModuleConnector logs to the specified {@link PrintWriter}.
+ */
+ public void dump(PrintWriter pw) {
+ // dump is thread-safe on SharedLog
+ mLog.dump(null, pw, null);
+ }
+
+ /**
+ * Stub class to replicate DeviceConfig behavior with minimal merge conflicts.
+ */
+ private class DeviceConfigStub {
+ private final Context mContext;
+
+ // Namespace is actually unused, but is here to replicate the final API.
+ private static final String NAMESPACE_CONNECTIVITY = "connectivity";
+
+ private DeviceConfigStub(Context context) {
+ mContext = context;
+ }
+
+ private long getLong(
+ @NonNull String namespace, @NonNull String key, long defaultVal) {
+ // Temporary solution until DeviceConfig is available
+ try {
+ return Settings.Global.getLong(
+ mContext.getContentResolver(), TAG + "_" + key, defaultVal);
+ } catch (Throwable e) {
+ logWtf("Could not obtain setting " + key, e);
+ return defaultVal;
+ }
+ }
+
+ private boolean getBoolean(
+ @NonNull String namespace, @NonNull String key, boolean defaultVal) {
+ // Temporary solution until DeviceConfig is available
+ return getLong(namespace, key, defaultVal ? 1 : 0) != 0;
+ }
+ }
+}
diff --git a/services/net/java/android/net/NetworkStackClient.java b/services/net/java/android/net/NetworkStackClient.java
index 787fda3..5a8e1e6 100644
--- a/services/net/java/android/net/NetworkStackClient.java
+++ b/services/net/java/android/net/NetworkStackClient.java
@@ -15,40 +15,27 @@
*/
package android.net;
-import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK;
import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_HIGH;
import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.content.ComponentName;
import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.content.SharedPreferences;
-import android.content.pm.PackageManager;
import android.net.dhcp.DhcpServingParamsParcel;
import android.net.dhcp.IDhcpServerCallbacks;
import android.net.ip.IIpClientCallbacks;
import android.net.util.SharedLog;
import android.os.Binder;
-import android.os.Build;
-import android.os.Environment;
import android.os.IBinder;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
-import android.os.SystemClock;
import android.os.UserHandle;
-import android.provider.Settings;
-import android.text.format.DateUtils;
-import android.util.ArraySet;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
-import java.io.File;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -60,24 +47,6 @@
private static final String TAG = NetworkStackClient.class.getSimpleName();
private static final int NETWORKSTACK_TIMEOUT_MS = 10_000;
- private static final String IN_PROCESS_SUFFIX = ".InProcess";
- private static final String PREFS_FILE = "NetworkStackClientPrefs.xml";
- private static final String PREF_KEY_LAST_CRASH_TIME = "lastcrash_time";
- private static final String CONFIG_MIN_CRASH_INTERVAL_MS = "min_crash_interval";
- private static final String CONFIG_MIN_UPTIME_BEFORE_CRASH_MS = "min_uptime_before_crash";
- private static final String CONFIG_ALWAYS_RATELIMIT_NETWORKSTACK_CRASH =
- "always_ratelimit_networkstack_crash";
-
- // Even if the network stack is lost, do not crash the system more often than this.
- // Connectivity would be broken, but if the user needs the device for something urgent
- // (like calling emergency services) we should not bootloop the device.
- // This is the default value: the actual value can be adjusted via device config.
- private static final long DEFAULT_MIN_CRASH_INTERVAL_MS = 6 * DateUtils.HOUR_IN_MILLIS;
-
- // Even if the network stack is lost, do not crash the system server if it was less than
- // this much after boot. This avoids bootlooping the device, and crashes should address very
- // infrequent failures, not failures on boot.
- private static final long DEFAULT_MIN_UPTIME_BEFORE_CRASH_MS = 30 * DateUtils.MINUTE_IN_MILLIS;
private static NetworkStackClient sInstance;
@@ -93,26 +62,10 @@
private volatile boolean mWasSystemServerInitialized = false;
- @GuardedBy("mHealthListeners")
- private final ArraySet<NetworkStackHealthListener> mHealthListeners = new ArraySet<>();
-
private interface NetworkStackCallback {
void onNetworkStackConnected(INetworkStackConnector connector);
}
- /**
- * Callback interface for severe failures of the NetworkStack.
- *
- * <p>Useful for health monitors such as PackageWatchdog.
- */
- public interface NetworkStackHealthListener {
- /**
- * Called when there is a severe failure of the network stack.
- * @param packageName Package name of the network stack.
- */
- void onNetworkStackFailure(@NonNull String packageName);
- }
-
private NetworkStackClient() { }
/**
@@ -126,15 +79,6 @@
}
/**
- * Add a {@link NetworkStackHealthListener} to listen to network stack health events.
- */
- public void registerHealthListener(@NonNull NetworkStackHealthListener listener) {
- synchronized (mHealthListeners) {
- mHealthListeners.add(listener);
- }
- }
-
- /**
* Create a DHCP server according to the specified parameters.
*
* <p>The server will be returned asynchronously through the provided callbacks.
@@ -195,32 +139,13 @@
});
}
- private class NetworkStackConnection implements ServiceConnection {
- @NonNull
- private final Context mContext;
- @NonNull
- private final String mPackageName;
-
- private NetworkStackConnection(@NonNull Context context, @NonNull String packageName) {
- mContext = context;
- mPackageName = packageName;
- }
-
+ private class NetworkStackConnection implements
+ ConnectivityModuleConnector.ModuleServiceCallback {
@Override
- public void onServiceConnected(ComponentName name, IBinder service) {
+ public void onModuleServiceConnected(IBinder service) {
logi("Network stack service connected");
registerNetworkStackService(service);
}
-
- @Override
- public void onServiceDisconnected(ComponentName name) {
- // onServiceDisconnected is not being called on device shutdown, so this method being
- // called always indicates a bad state for the system server.
- // 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", mContext, mPackageName);
- }
}
private void registerNetworkStackService(@NonNull IBinder service) {
@@ -259,174 +184,14 @@
* connector will then be delivered asynchronously to clients that requested it before it was
* started.
*/
- public void start(Context context) {
- log("Starting network stack");
-
- final PackageManager pm = context.getPackageManager();
-
- // Try to bind in-process if the device was shipped with an in-process version
- Intent intent = getNetworkStackIntent(pm, true /* inSystemProcess */);
-
- // Otherwise use the updatable module version
- if (intent == null) {
- intent = getNetworkStackIntent(pm, false /* inSystemProcess */);
- log("Starting network stack process");
- } else {
- log("Starting network stack in-process");
- }
-
- if (intent == null) {
- maybeCrashWithTerribleFailure("Could not resolve the network stack", context, null);
- return;
- }
-
- final String packageName = intent.getComponent().getPackageName();
-
- // Start the network stack. The service will be added to the service manager in
- // NetworkStackConnection.onServiceConnected().
- if (!context.bindServiceAsUser(intent, new NetworkStackConnection(context, packageName),
- Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT, UserHandle.SYSTEM)) {
- maybeCrashWithTerribleFailure(
- "Could not bind to network stack in-process, or in app with " + intent,
- context, packageName);
- return;
- }
-
+ public void start() {
+ ConnectivityModuleConnector.getInstance().startModuleService(
+ INetworkStackConnector.class.getName(), PERMISSION_MAINLINE_NETWORK_STACK,
+ new NetworkStackConnection());
log("Network stack service start requested");
}
- @Nullable
- private Intent getNetworkStackIntent(@NonNull PackageManager pm, boolean inSystemProcess) {
- final String baseAction = INetworkStackConnector.class.getName();
- final Intent intent =
- new Intent(inSystemProcess ? baseAction + IN_PROCESS_SUFFIX : baseAction);
- final ComponentName comp = intent.resolveSystemService(pm, 0);
-
- if (comp == null) {
- return null;
- }
- intent.setComponent(comp);
-
- int uid = -1;
- try {
- uid = pm.getPackageUidAsUser(comp.getPackageName(), UserHandle.USER_SYSTEM);
- } catch (PackageManager.NameNotFoundException e) {
- logWtf("Network stack package not found", e);
- // Fall through
- }
-
- final int expectedUid = inSystemProcess ? Process.SYSTEM_UID : Process.NETWORK_STACK_UID;
- if (uid != expectedUid) {
- throw new SecurityException("Invalid network stack UID: " + uid);
- }
-
- if (!inSystemProcess) {
- checkNetworkStackPermission(pm, comp);
- }
-
- return intent;
- }
-
- private void checkNetworkStackPermission(
- @NonNull PackageManager pm, @NonNull ComponentName comp) {
- final int hasPermission =
- pm.checkPermission(PERMISSION_MAINLINE_NETWORK_STACK, comp.getPackageName());
- if (hasPermission != PERMISSION_GRANTED) {
- throw new SecurityException(
- "Network stack does not have permission " + PERMISSION_MAINLINE_NETWORK_STACK);
- }
- }
-
- private void maybeCrashWithTerribleFailure(@NonNull String message,
- @NonNull Context context, @Nullable String packageName) {
- logWtf(message, null);
- // Called DeviceConfig to minimize merge conflicts
- final DeviceConfigStub DeviceConfig = new DeviceConfigStub(context);
- // uptime is monotonic even after a framework restart
- final long uptime = SystemClock.elapsedRealtime();
- final long now = System.currentTimeMillis();
- final long minCrashIntervalMs = DeviceConfig.getLong(DeviceConfig.NAMESPACE_CONNECTIVITY,
- CONFIG_MIN_CRASH_INTERVAL_MS, DEFAULT_MIN_CRASH_INTERVAL_MS);
- final long minUptimeBeforeCrash = DeviceConfig.getLong(DeviceConfig.NAMESPACE_CONNECTIVITY,
- CONFIG_MIN_UPTIME_BEFORE_CRASH_MS, DEFAULT_MIN_UPTIME_BEFORE_CRASH_MS);
- final boolean alwaysRatelimit = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_CONNECTIVITY,
- CONFIG_ALWAYS_RATELIMIT_NETWORKSTACK_CRASH, false);
-
- final SharedPreferences prefs = getSharedPreferences(context);
- final long lastCrashTime = tryGetLastCrashTime(prefs);
-
- // Only crash if there was enough time since boot, and (if known) enough time passed since
- // the last crash.
- // time and lastCrashTime may be unreliable if devices have incorrect clock time, but they
- // are only used to limit the number of crashes compared to only using the time since boot,
- // which would also be OK behavior by itself.
- // - If lastCrashTime is incorrectly more than the current time, only look at uptime
- // - If it is much less than current time, only look at uptime
- // - If current time is during the next few hours after last crash time, don't crash.
- // Considering that this only matters if last boot was some time ago, it's likely that
- // time will be set correctly. Otherwise, not crashing is not a big problem anyway. Being
- // in this last state would also not last for long since the window is only a few hours.
- final boolean alwaysCrash = Build.IS_DEBUGGABLE && !alwaysRatelimit;
- final boolean justBooted = uptime < minUptimeBeforeCrash;
- final boolean haveLastCrashTime = (lastCrashTime != 0) && (lastCrashTime < now);
- final boolean haveKnownRecentCrash =
- haveLastCrashTime && (now < lastCrashTime + minCrashIntervalMs);
- if (alwaysCrash || (!justBooted && !haveKnownRecentCrash)) {
- // The system is not bound to its network stack (for example due to a crash in the
- // network stack process): better crash rather than stay in a bad state where all
- // networking is broken.
- // Using device-encrypted SharedPreferences as DeviceConfig does not have a synchronous
- // API to persist settings before a crash.
- tryWriteLastCrashTime(prefs, now);
- throw new IllegalStateException(message);
- }
-
- // Here the system crashed recently already. Inform listeners that something is
- // definitely wrong.
- if (packageName != null) {
- final ArraySet<NetworkStackHealthListener> listeners;
- synchronized (mHealthListeners) {
- listeners = new ArraySet<>(mHealthListeners);
- }
- for (NetworkStackHealthListener listener : listeners) {
- listener.onNetworkStackFailure(packageName);
- }
- }
- }
-
- @Nullable
- private SharedPreferences getSharedPreferences(@NonNull Context context) {
- try {
- final File prefsFile = new File(
- Environment.getDataSystemDeDirectory(UserHandle.USER_SYSTEM), PREFS_FILE);
- return context.createDeviceProtectedStorageContext()
- .getSharedPreferences(prefsFile, Context.MODE_PRIVATE);
- } catch (Throwable e) {
- logWtf("Error loading shared preferences", e);
- return null;
- }
- }
-
- private long tryGetLastCrashTime(@Nullable SharedPreferences prefs) {
- if (prefs == null) return 0L;
- try {
- return prefs.getLong(PREF_KEY_LAST_CRASH_TIME, 0L);
- } catch (Throwable e) {
- logWtf("Error getting last crash time", e);
- return 0L;
- }
- }
-
- private void tryWriteLastCrashTime(@Nullable SharedPreferences prefs, long value) {
- if (prefs == null) return;
- try {
- prefs.edit().putLong(PREF_KEY_LAST_CRASH_TIME, value).commit();
- } catch (Throwable e) {
- logWtf("Error writing last crash time", e);
- }
- }
-
- /**
+ /**
* Log a message in the local log.
*/
private void log(@NonNull String message) {
@@ -526,6 +291,8 @@
public void dump(PrintWriter pw) {
// dump is thread-safe on SharedLog
mLog.dump(null, pw, null);
+ // dump connectivity module connector logs.
+ ConnectivityModuleConnector.getInstance().dump(pw);
final int requestsQueueLength;
synchronized (mPendingNetStackRequests) {
@@ -536,35 +303,4 @@
pw.println("pendingNetStackRequests length: " + requestsQueueLength);
}
- /**
- * Stub class to replicate DeviceConfig behavior with minimal merge conflicts.
- */
- private class DeviceConfigStub {
- private final Context mContext;
-
- // Namespace is actually unused, but is here to replicate the final API.
- private static final String NAMESPACE_CONNECTIVITY = "connectivity";
-
- private DeviceConfigStub(Context context) {
- mContext = context;
- }
-
- private long getLong(
- @NonNull String namespace, @NonNull String key, long defaultVal) {
- // Temporary solution until DeviceConfig is available
- try {
- return Settings.Global.getLong(
- mContext.getContentResolver(), TAG + "_" + key, defaultVal);
- } catch (Throwable e) {
- logWtf("Could not obtain setting " + key, e);
- return defaultVal;
- }
- }
-
- private boolean getBoolean(
- @NonNull String namespace, @NonNull String key, boolean defaultVal) {
- // Temporary solution until DeviceConfig is available
- return getLong(namespace, key, defaultVal ? 1 : 0) != 0;
- }
- }
}
diff --git a/services/net/java/android/net/dhcp/DhcpServerCallbacks.java b/services/net/java/android/net/dhcp/DhcpServerCallbacks.java
index bb56876..7c413779 100644
--- a/services/net/java/android/net/dhcp/DhcpServerCallbacks.java
+++ b/services/net/java/android/net/dhcp/DhcpServerCallbacks.java
@@ -21,13 +21,11 @@
* @hide
*/
public abstract class DhcpServerCallbacks extends IDhcpServerCallbacks.Stub {
- // TODO: add @Override here once the API is versioned
-
/**
* Get the version of the aidl interface implemented by the callbacks.
*/
+ @Override
public int getInterfaceVersion() {
- // TODO: return IDhcpServerCallbacks.VERSION;
- return 0;
+ return IDhcpServerCallbacks.VERSION;
}
}
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index 153820d..41f46f5 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -38,6 +38,7 @@
"ub-uiautomator",
"platformprotosnano",
"servicestests-utils",
+ "xml-writer-device-lib",
],
aidl: {
@@ -78,6 +79,8 @@
optimize: {
enabled: false,
},
+
+ data: [":JobTestApp"],
}
java_library {
diff --git a/services/tests/servicestests/src/com/android/server/am/LockTaskControllerTest.java b/services/tests/servicestests/src/com/android/server/am/LockTaskControllerTest.java
index 863a0d8..5fbf241 100644
--- a/services/tests/servicestests/src/com/android/server/am/LockTaskControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/LockTaskControllerTest.java
@@ -390,7 +390,7 @@
mLockTaskController.startLockTaskMode(tr1, false, TEST_UID);
mLockTaskController.startLockTaskMode(tr2, false, TEST_UID);
- // WHEN calling stopLockTaskMode on the root task
+ // WHEN calling clearLockedTasks on the root task
mLockTaskController.clearLockedTasks("testClearLockedTasks");
// THEN the lock task mode should be inactive
@@ -404,7 +404,81 @@
}
@Test
- public void testUpdateLockTaskPackages() throws Exception {
+ public void testClearLockedTasks_noLockSetting_noPassword_deviceIsUnlocked() throws Exception {
+ // GIVEN There is no setting set for LOCK_TO_APP_EXIT_LOCKED
+ Settings.Secure.clearProviderForTest();
+
+ // AND no password is set
+ when(mLockPatternUtils.getKeyguardStoredPasswordQuality(anyInt()))
+ .thenReturn(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED);
+
+ // AND there is a task record
+ TaskRecord tr1 = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_WHITELISTED);
+ mLockTaskController.startLockTaskMode(tr1, true, TEST_UID);
+
+ // WHEN calling clearLockedTasks on the root task
+ mLockTaskController.clearLockedTasks("testClearLockedTasks");
+
+ // THEN the device should not be locked
+ verify(mWindowManager, never()).lockNow(any());
+ }
+
+ @Test
+ public void testClearLockedTasks_noLockSetting_password_deviceIsLocked() throws Exception {
+ // GIVEN There is no setting set for LOCK_TO_APP_EXIT_LOCKED
+ Settings.Secure.clearProviderForTest();
+
+ // AND a password is set
+ when(mLockPatternUtils.isSecure(anyInt()))
+ .thenReturn(true);
+
+ // AND there is a task record
+ TaskRecord tr1 = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_WHITELISTED);
+ mLockTaskController.startLockTaskMode(tr1, true, TEST_UID);
+
+ // WHEN calling clearLockedTasks on the root task
+ mLockTaskController.clearLockedTasks("testClearLockedTasks");
+
+ // THEN the device should be locked
+ verify(mWindowManager, times(1)).lockNow(any());
+ }
+
+ @Test
+ public void testClearLockedTasks_lockSettingTrue_deviceIsLocked() throws Exception {
+ // GIVEN LOCK_TO_APP_EXIT_LOCKED is set to 1
+ Settings.Secure.putIntForUser(mContext.getContentResolver(),
+ Settings.Secure.LOCK_TO_APP_EXIT_LOCKED, 1, mContext.getUserId());
+
+ // AND there is a task record
+ TaskRecord tr1 = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_WHITELISTED);
+ mLockTaskController.startLockTaskMode(tr1, true, TEST_UID);
+
+ // WHEN calling clearLockedTasks on the root task
+ mLockTaskController.clearLockedTasks("testClearLockedTasks");
+
+ // THEN the device should be locked
+ verify(mWindowManager, times(1)).lockNow(any());
+ }
+
+ @Test
+ public void testClearLockedTasks_lockSettingFalse_doesNotRequirePassword() throws Exception {
+ // GIVEN LOCK_TO_APP_EXIT_LOCKED is set to 1
+ Settings.Secure.putIntForUser(mContext.getContentResolver(),
+ Settings.Secure.LOCK_TO_APP_EXIT_LOCKED, 0, mContext.getUserId());
+
+ // AND there is a task record
+ TaskRecord tr1 = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_WHITELISTED);
+ mLockTaskController.startLockTaskMode(tr1, true, TEST_UID);
+
+ // WHEN calling clearLockedTasks on the root task
+ mLockTaskController.clearLockedTasks("testClearLockedTasks");
+
+ // THEN the device should be unlocked
+ verify(mWindowManager, never()).lockNow(any());
+ }
+
+ @Test
+ public void testUpdateLockTaskPackages() {
String[] whitelist1 = {TEST_PACKAGE_NAME, TEST_PACKAGE_NAME_2};
String[] whitelist2 = {TEST_PACKAGE_NAME};
diff --git a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
index e6c484a..f8c87fc 100644
--- a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
@@ -22,9 +22,17 @@
import androidx.test.runner.AndroidJUnit4;
+import com.android.compat.annotation.Change;
+import com.android.compat.annotation.XmlWriter;
+
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.UUID;
+
@RunWith(AndroidJUnit4.class)
public class CompatConfigTest {
@@ -35,6 +43,27 @@
return ai;
}
+ private File createTempDir() {
+ String base = System.getProperty("java.io.tmpdir");
+ File dir = new File(base, UUID.randomUUID().toString());
+ assertThat(dir.mkdirs()).isTrue();
+ return dir;
+ }
+
+ private void writeChangesToFile(Change[] changes, File f) {
+ XmlWriter writer = new XmlWriter();
+ for (Change change: changes) {
+ writer.addChange(change);
+ }
+ try {
+ f.createNewFile();
+ writer.write(new FileOutputStream(f));
+ } catch (IOException e) {
+ throw new RuntimeException(
+ "Encountered an error while writing compat config file", e);
+ }
+ }
+
@Test
public void testUnknownChangeEnabled() {
CompatConfig pc = new CompatConfig();
@@ -142,4 +171,45 @@
CompatConfig pc = new CompatConfig();
assertThat(pc.lookupChangeId("MY_CHANGE")).isEqualTo(-1L);
}
+
+ @Test
+ public void testReadConfig() {
+ Change[] changes = {new Change(1234L, "MY_CHANGE1", false, 2), new Change(1235L,
+ "MY_CHANGE2", true, null), new Change(1236L, "MY_CHANGE3", false, null)};
+
+ File dir = createTempDir();
+ writeChangesToFile(changes, new File(dir.getPath() + "/platform_compat_config.xml"));
+
+ CompatConfig pc = new CompatConfig();
+ pc.initConfigFromLib(dir);
+
+ assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 1))).isFalse();
+ assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 3))).isTrue();
+ assertThat(pc.isChangeEnabled(1235L, makeAppInfo("com.some.package", 5))).isFalse();
+ assertThat(pc.isChangeEnabled(1236L, makeAppInfo("com.some.package", 1))).isTrue();
+ }
+
+ @Test
+ public void testReadConfigMultipleFiles() {
+ Change[] changes1 = {new Change(1234L, "MY_CHANGE1", false, 2)};
+ Change[] changes2 = {new Change(1235L, "MY_CHANGE2", true, null), new Change(1236L,
+ "MY_CHANGE3", false, null)};
+
+ File dir = createTempDir();
+ writeChangesToFile(changes1,
+ new File(dir.getPath() + "/libcore_platform_compat_config.xml"));
+ writeChangesToFile(changes2,
+ new File(dir.getPath() + "/frameworks_platform_compat_config.xml"));
+
+
+ CompatConfig pc = new CompatConfig();
+ pc.initConfigFromLib(dir);
+
+ assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 1))).isFalse();
+ assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 3))).isTrue();
+ assertThat(pc.isChangeEnabled(1235L, makeAppInfo("com.some.package", 5))).isFalse();
+ assertThat(pc.isChangeEnabled(1236L, makeAppInfo("com.some.package", 1))).isTrue();
+ }
}
+
+
diff --git a/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java b/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java
index 543f51cba..6fa5cd29 100644
--- a/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java
+++ b/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java
@@ -382,6 +382,82 @@
.build());
}
+ @Test
+ public void testPersistedIdleConstraint() throws Exception {
+ JobInfo.Builder b = new Builder(8, mComponent)
+ .setRequiresDeviceIdle(true)
+ .setPersisted(true);
+ JobStatus taskStatus = JobStatus.createFromJobInfo(b.build(), SOME_UID, null, -1, null);
+
+ mTaskStoreUnderTest.add(taskStatus);
+ waitForPendingIo();
+
+ final JobSet jobStatusSet = new JobSet();
+ mTaskStoreUnderTest.readJobMapFromDisk(jobStatusSet, true);
+ assertEquals("Incorrect # of persisted tasks.", 1, jobStatusSet.size());
+ JobStatus loaded = jobStatusSet.getAllJobs().iterator().next();
+ assertEquals("Idle constraint not persisted correctly.",
+ loaded.getJob().isRequireDeviceIdle(),
+ taskStatus.getJob().isRequireDeviceIdle());
+ }
+
+ @Test
+ public void testPersistedChargingConstraint() throws Exception {
+ JobInfo.Builder b = new Builder(8, mComponent)
+ .setRequiresCharging(true)
+ .setPersisted(true);
+ JobStatus taskStatus = JobStatus.createFromJobInfo(b.build(), SOME_UID, null, -1, null);
+
+ mTaskStoreUnderTest.add(taskStatus);
+ waitForPendingIo();
+
+ final JobSet jobStatusSet = new JobSet();
+ mTaskStoreUnderTest.readJobMapFromDisk(jobStatusSet, true);
+ assertEquals("Incorrect # of persisted tasks.", 1, jobStatusSet.size());
+ JobStatus loaded = jobStatusSet.getAllJobs().iterator().next();
+ assertEquals("Charging constraint not persisted correctly.",
+ loaded.getJob().isRequireCharging(),
+ taskStatus.getJob().isRequireCharging());
+ }
+
+ @Test
+ public void testPersistedStorageNotLowConstraint() throws Exception {
+ JobInfo.Builder b = new Builder(8, mComponent)
+ .setRequiresStorageNotLow(true)
+ .setPersisted(true);
+ JobStatus taskStatus = JobStatus.createFromJobInfo(b.build(), SOME_UID, null, -1, null);
+
+ mTaskStoreUnderTest.add(taskStatus);
+ waitForPendingIo();
+
+ final JobSet jobStatusSet = new JobSet();
+ mTaskStoreUnderTest.readJobMapFromDisk(jobStatusSet, true);
+ assertEquals("Incorrect # of persisted tasks.", 1, jobStatusSet.size());
+ JobStatus loaded = jobStatusSet.getAllJobs().iterator().next();
+ assertEquals("Storage-not-low constraint not persisted correctly.",
+ loaded.getJob().isRequireStorageNotLow(),
+ taskStatus.getJob().isRequireStorageNotLow());
+ }
+
+ @Test
+ public void testPersistedBatteryNotLowConstraint() throws Exception {
+ JobInfo.Builder b = new Builder(8, mComponent)
+ .setRequiresBatteryNotLow(true)
+ .setPersisted(true);
+ JobStatus taskStatus = JobStatus.createFromJobInfo(b.build(), SOME_UID, null, -1, null);
+
+ mTaskStoreUnderTest.add(taskStatus);
+ waitForPendingIo();
+
+ final JobSet jobStatusSet = new JobSet();
+ mTaskStoreUnderTest.readJobMapFromDisk(jobStatusSet, true);
+ assertEquals("Incorrect # of persisted tasks.", 1, jobStatusSet.size());
+ JobStatus loaded = jobStatusSet.getAllJobs().iterator().next();
+ assertEquals("Battery-not-low constraint not persisted correctly.",
+ loaded.getJob().isRequireBatteryNotLow(),
+ taskStatus.getJob().isRequireBatteryNotLow());
+ }
+
/**
* Helper function to kick a {@link JobInfo} through a persistence cycle and
* assert that it's unchanged.
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java
index 68728af..6a03aed 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java
@@ -60,6 +60,11 @@
}
@Override
+ public void notifyPackageChanged(String packageName, int uid) {
+
+ }
+
+ @Override
public void notifyPackageRemoved(String packageName, int uid) {
}
}
diff --git a/services/tests/servicestests/test-apps/JobTestApp/Android.bp b/services/tests/servicestests/test-apps/JobTestApp/Android.bp
index ae1eca7..b29e187 100644
--- a/services/tests/servicestests/test-apps/JobTestApp/Android.bp
+++ b/services/tests/servicestests/test-apps/JobTestApp/Android.bp
@@ -17,8 +17,6 @@
sdk_version: "current",
- test_suites: ["device-tests"],
-
srcs: ["**/*.java"],
dex_preopt: {
diff --git a/startop/apps/test/Android.bp b/startop/apps/test/Android.bp
index 7a1678a..a4906d7 100644
--- a/startop/apps/test/Android.bp
+++ b/startop/apps/test/Android.bp
@@ -23,4 +23,5 @@
"src/FrameLayoutInflationActivity.java",
"src/TextViewInflationActivity.java",
],
+ platform_apis: true,
}
diff --git a/startop/view_compiler/Android.bp b/startop/view_compiler/Android.bp
index 92ea872..4f6524e 100644
--- a/startop/view_compiler/Android.bp
+++ b/startop/view_compiler/Android.bp
@@ -77,7 +77,6 @@
name: "view-compiler-tests",
defaults: ["viewcompiler_defaults"],
srcs: [
- "dex_builder_test.cc",
"layout_validation_test.cc",
"util_test.cc",
],
diff --git a/startop/view_compiler/dex_builder.cc b/startop/view_compiler/dex_builder.cc
index 6047e8c..499c42e 100644
--- a/startop/view_compiler/dex_builder.cc
+++ b/startop/view_compiler/dex_builder.cc
@@ -102,6 +102,18 @@
case Instruction::Op::kCheckCast:
out << "kCheckCast";
return out;
+ case Instruction::Op::kGetStaticField:
+ out << "kGetStaticField";
+ return out;
+ case Instruction::Op::kSetStaticField:
+ out << "kSetStaticField";
+ return out;
+ case Instruction::Op::kGetInstanceField:
+ out << "kGetInstanceField";
+ return out;
+ case Instruction::Op::kSetInstanceField:
+ out << "kSetInstanceField";
+ return out;
}
}
@@ -229,6 +241,23 @@
return type;
}
+ir::FieldDecl* DexBuilder::GetOrAddField(TypeDescriptor parent, const std::string& name,
+ TypeDescriptor type) {
+ const auto key = std::make_tuple(parent, name);
+ if (field_decls_by_key_.find(key) != field_decls_by_key_.end()) {
+ return field_decls_by_key_[key];
+ }
+
+ ir::FieldDecl* field = Alloc<ir::FieldDecl>();
+ field->parent = GetOrAddType(parent);
+ field->name = GetOrAddString(name);
+ field->type = GetOrAddType(type);
+ field->orig_index = dex_file_->fields_indexes.AllocateIndex();
+ dex_file_->fields_map[field->orig_index] = field;
+ field_decls_by_key_[key] = field;
+ return field;
+}
+
ir::Proto* Prototype::Encode(DexBuilder* dex) const {
auto* proto = dex->Alloc<ir::Proto>();
proto->shorty = dex->GetOrAddString(Shorty());
@@ -360,6 +389,11 @@
return EncodeNew(instruction);
case Instruction::Op::kCheckCast:
return EncodeCast(instruction);
+ case Instruction::Op::kGetStaticField:
+ case Instruction::Op::kSetStaticField:
+ case Instruction::Op::kGetInstanceField:
+ case Instruction::Op::kSetInstanceField:
+ return EncodeFieldOp(instruction);
}
}
@@ -428,7 +462,7 @@
// first move all the arguments into contiguous temporary registers.
std::array<Value, kMaxArgs> scratch = GetScratchRegisters<kMaxArgs>();
- const auto& prototype = dex_->GetPrototypeByMethodId(instruction.method_id());
+ const auto& prototype = dex_->GetPrototypeByMethodId(instruction.index_argument());
CHECK(prototype.has_value());
for (size_t i = 0; i < instruction.args().size(); ++i) {
@@ -452,12 +486,12 @@
Encode3rc(InvokeToInvokeRange(opcode),
instruction.args().size(),
- instruction.method_id(),
+ instruction.index_argument(),
RegisterValue(scratch[0]));
} else {
Encode35c(opcode,
instruction.args().size(),
- instruction.method_id(),
+ instruction.index_argument(),
arguments[0],
arguments[1],
arguments[2],
@@ -514,6 +548,54 @@
Encode21c(::art::Instruction::CHECK_CAST, RegisterValue(*instruction.dest()), type.value());
}
+void MethodBuilder::EncodeFieldOp(const Instruction& instruction) {
+ const auto& args = instruction.args();
+ switch (instruction.opcode()) {
+ case Instruction::Op::kGetStaticField: {
+ CHECK(instruction.dest().has_value());
+ CHECK(instruction.dest()->is_variable());
+ CHECK_EQ(0, instruction.args().size());
+
+ Encode21c(::art::Instruction::SGET,
+ RegisterValue(*instruction.dest()),
+ instruction.index_argument());
+ break;
+ }
+ case Instruction::Op::kSetStaticField: {
+ CHECK(!instruction.dest().has_value());
+ CHECK_EQ(1, args.size());
+ CHECK(args[0].is_variable());
+
+ Encode21c(::art::Instruction::SPUT, RegisterValue(args[0]), instruction.index_argument());
+ break;
+ }
+ case Instruction::Op::kGetInstanceField: {
+ CHECK(instruction.dest().has_value());
+ CHECK(instruction.dest()->is_variable());
+ CHECK_EQ(1, instruction.args().size());
+
+ Encode22c(::art::Instruction::IGET,
+ RegisterValue(*instruction.dest()),
+ RegisterValue(args[0]),
+ instruction.index_argument());
+ break;
+ }
+ case Instruction::Op::kSetInstanceField: {
+ CHECK(!instruction.dest().has_value());
+ CHECK_EQ(2, args.size());
+ CHECK(args[0].is_variable());
+ CHECK(args[1].is_variable());
+
+ Encode22c(::art::Instruction::IPUT,
+ RegisterValue(args[1]),
+ RegisterValue(args[0]),
+ instruction.index_argument());
+ break;
+ }
+ default: { LOG(FATAL) << "Unsupported field operation"; }
+ }
+}
+
size_t MethodBuilder::RegisterValue(const Value& value) const {
if (value.is_register()) {
return value.value();
diff --git a/startop/view_compiler/dex_builder.h b/startop/view_compiler/dex_builder.h
index 541d800..292d659 100644
--- a/startop/view_compiler/dex_builder.h
+++ b/startop/view_compiler/dex_builder.h
@@ -153,6 +153,8 @@
kBranchEqz,
kBranchNEqz,
kCheckCast,
+ kGetInstanceField,
+ kGetStaticField,
kInvokeDirect,
kInvokeInterface,
kInvokeStatic,
@@ -162,6 +164,8 @@
kNew,
kReturn,
kReturnObject,
+ kSetInstanceField,
+ kSetStaticField
};
////////////////////////
@@ -170,12 +174,12 @@
// For instructions with no return value and no arguments.
static inline Instruction OpNoArgs(Op opcode) {
- return Instruction{opcode, /*method_id*/ 0, /*dest*/ {}};
+ return Instruction{opcode, /*index_argument*/ 0, /*dest*/ {}};
}
// For most instructions, which take some number of arguments and have an optional return value.
template <typename... T>
static inline Instruction OpWithArgs(Op opcode, std::optional<const Value> dest, T... args) {
- return Instruction{opcode, /*method_id=*/0, /*result_is_object=*/false, dest, args...};
+ return Instruction{opcode, /*index_argument=*/0, /*result_is_object=*/false, dest, args...};
}
// A cast instruction. Basically, `(type)val`
@@ -186,49 +190,71 @@
// For method calls.
template <typename... T>
- static inline Instruction InvokeVirtual(size_t method_id, std::optional<const Value> dest,
+ static inline Instruction InvokeVirtual(size_t index_argument, std::optional<const Value> dest,
Value this_arg, T... args) {
return Instruction{
- Op::kInvokeVirtual, method_id, /*result_is_object=*/false, dest, this_arg, args...};
+ Op::kInvokeVirtual, index_argument, /*result_is_object=*/false, dest, this_arg, args...};
}
// Returns an object
template <typename... T>
- static inline Instruction InvokeVirtualObject(size_t method_id, std::optional<const Value> dest,
- Value this_arg, T... args) {
+ static inline Instruction InvokeVirtualObject(size_t index_argument,
+ std::optional<const Value> dest, Value this_arg,
+ T... args) {
return Instruction{
- Op::kInvokeVirtual, method_id, /*result_is_object=*/true, dest, this_arg, args...};
+ Op::kInvokeVirtual, index_argument, /*result_is_object=*/true, dest, this_arg, args...};
}
// For direct calls (basically, constructors).
template <typename... T>
- static inline Instruction InvokeDirect(size_t method_id, std::optional<const Value> dest,
+ static inline Instruction InvokeDirect(size_t index_argument, std::optional<const Value> dest,
Value this_arg, T... args) {
return Instruction{
- Op::kInvokeDirect, method_id, /*result_is_object=*/false, dest, this_arg, args...};
+ Op::kInvokeDirect, index_argument, /*result_is_object=*/false, dest, this_arg, args...};
}
// Returns an object
template <typename... T>
- static inline Instruction InvokeDirectObject(size_t method_id, std::optional<const Value> dest,
- Value this_arg, T... args) {
- return Instruction{
- Op::kInvokeDirect, method_id, /*result_is_object=*/true, dest, this_arg, args...};
- }
- // For static calls.
- template <typename... T>
- static inline Instruction InvokeStatic(size_t method_id, std::optional<const Value> dest,
- T... args) {
- return Instruction{Op::kInvokeStatic, method_id, /*result_is_object=*/false, dest, args...};
- }
- // Returns an object
- template <typename... T>
- static inline Instruction InvokeStaticObject(size_t method_id, std::optional<const Value> dest,
+ static inline Instruction InvokeDirectObject(size_t index_argument,
+ std::optional<const Value> dest, Value this_arg,
T... args) {
- return Instruction{Op::kInvokeStatic, method_id, /*result_is_object=*/true, dest, args...};
+ return Instruction{
+ Op::kInvokeDirect, index_argument, /*result_is_object=*/true, dest, this_arg, args...};
}
// For static calls.
template <typename... T>
- static inline Instruction InvokeInterface(size_t method_id, std::optional<const Value> dest,
+ static inline Instruction InvokeStatic(size_t index_argument, std::optional<const Value> dest,
+ T... args) {
+ return Instruction{
+ Op::kInvokeStatic, index_argument, /*result_is_object=*/false, dest, args...};
+ }
+ // Returns an object
+ template <typename... T>
+ static inline Instruction InvokeStaticObject(size_t index_argument,
+ std::optional<const Value> dest, T... args) {
+ return Instruction{Op::kInvokeStatic, index_argument, /*result_is_object=*/true, dest, args...};
+ }
+ // For static calls.
+ template <typename... T>
+ static inline Instruction InvokeInterface(size_t index_argument, std::optional<const Value> dest,
T... args) {
- return Instruction{Op::kInvokeInterface, method_id, /*result_is_object=*/false, dest, args...};
+ return Instruction{
+ Op::kInvokeInterface, index_argument, /*result_is_object=*/false, dest, args...};
+ }
+
+ static inline Instruction GetStaticField(size_t field_id, Value dest) {
+ return Instruction{Op::kGetStaticField, field_id, dest};
+ }
+
+ static inline Instruction SetStaticField(size_t field_id, Value value) {
+ return Instruction{
+ Op::kSetStaticField, field_id, /*result_is_object=*/false, /*dest=*/{}, value};
+ }
+
+ static inline Instruction GetField(size_t field_id, Value dest, Value object) {
+ return Instruction{Op::kGetInstanceField, field_id, /*result_is_object=*/false, dest, object};
+ }
+
+ static inline Instruction SetField(size_t field_id, Value object, Value value) {
+ return Instruction{
+ Op::kSetInstanceField, field_id, /*result_is_object=*/false, /*dest=*/{}, object, value};
}
///////////////
@@ -236,27 +262,31 @@
///////////////
Op opcode() const { return opcode_; }
- size_t method_id() const { return method_id_; }
+ size_t index_argument() const { return index_argument_; }
bool result_is_object() const { return result_is_object_; }
const std::optional<const Value>& dest() const { return dest_; }
const std::vector<const Value>& args() const { return args_; }
private:
- inline Instruction(Op opcode, size_t method_id, std::optional<const Value> dest)
- : opcode_{opcode}, method_id_{method_id}, result_is_object_{false}, dest_{dest}, args_{} {}
+ inline Instruction(Op opcode, size_t index_argument, std::optional<const Value> dest)
+ : opcode_{opcode},
+ index_argument_{index_argument},
+ result_is_object_{false},
+ dest_{dest},
+ args_{} {}
template <typename... T>
- inline constexpr Instruction(Op opcode, size_t method_id, bool result_is_object,
+ inline Instruction(Op opcode, size_t index_argument, bool result_is_object,
std::optional<const Value> dest, T... args)
: opcode_{opcode},
- method_id_{method_id},
+ index_argument_{index_argument},
result_is_object_{result_is_object},
dest_{dest},
args_{args...} {}
const Op opcode_;
// The index of the method to invoke, for kInvokeVirtual and similar opcodes.
- const size_t method_id_{0};
+ const size_t index_argument_{0};
const bool result_is_object_;
const std::optional<const Value> dest_;
const std::vector<const Value> args_;
@@ -319,6 +349,7 @@
void EncodeBranch(art::Instruction::Code op, const Instruction& instruction);
void EncodeNew(const Instruction& instruction);
void EncodeCast(const Instruction& instruction);
+ void EncodeFieldOp(const Instruction& instruction);
// Low-level instruction format encoding. See
// https://source.android.com/devices/tech/dalvik/instruction-formats for documentation of
@@ -351,6 +382,14 @@
buffer_.push_back(b);
}
+ inline void Encode22c(art::Instruction::Code opcode, uint8_t a, uint8_t b, uint16_t c) {
+ // b|a|op|bbbb
+ CHECK(IsShortRegister(a));
+ CHECK(IsShortRegister(b));
+ buffer_.push_back((b << 12) | (a << 8) | opcode);
+ buffer_.push_back(c);
+ }
+
inline void Encode32x(art::Instruction::Code opcode, uint16_t a, uint16_t b) {
buffer_.push_back(opcode);
buffer_.push_back(a);
@@ -481,6 +520,11 @@
// See the TypeDescriptor class for help generating these. GetOrAddType can be used to declare
// imported classes.
ir::Type* GetOrAddType(const std::string& descriptor);
+ inline ir::Type* GetOrAddType(TypeDescriptor descriptor) {
+ return GetOrAddType(descriptor.descriptor());
+ }
+
+ ir::FieldDecl* GetOrAddField(TypeDescriptor parent, const std::string& name, TypeDescriptor type);
// Returns the method id for the method, creating it if it has not been created yet.
const MethodDeclData& GetOrDeclareMethod(TypeDescriptor type, const std::string& name,
@@ -526,6 +570,9 @@
// Keep track of already-encoded protos.
std::map<Prototype, ir::Proto*> proto_map_;
+
+ // Keep track of fields that have been declared
+ std::map<std::tuple<TypeDescriptor, std::string>, ir::FieldDecl*> field_decls_by_key_;
};
template <typename... T>
diff --git a/startop/view_compiler/dex_builder_test.cc b/startop/view_compiler/dex_builder_test.cc
deleted file mode 100644
index 90c256f..0000000
--- a/startop/view_compiler/dex_builder_test.cc
+++ /dev/null
@@ -1,180 +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.
- */
-
-#include "dex_builder.h"
-
-#include "dex/art_dex_file_loader.h"
-#include "dex/dex_file.h"
-#include "gtest/gtest.h"
-
-using namespace startop::dex;
-
-// Takes a DexBuilder, encodes it into an in-memory DEX file, verifies the resulting DEX file and
-// returns whether the verification was successful.
-bool EncodeAndVerify(DexBuilder* dex_file) {
- slicer::MemView image{dex_file->CreateImage()};
-
- art::ArtDexFileLoader loader;
- std::string error_msg;
- std::unique_ptr<const art::DexFile> loaded_dex_file{loader.Open(image.ptr<const uint8_t>(),
- image.size(),
- /*location=*/"",
- /*location_checksum=*/0,
- /*oat_dex_file=*/nullptr,
- /*verify=*/true,
- /*verify_checksum=*/false,
- &error_msg)};
- return loaded_dex_file != nullptr;
-}
-
-// Write out and verify a DEX file that corresponds to:
-//
-// package dextest;
-// public class DexTest {
-// public static void foo() {}
-// }
-TEST(DexBuilderTest, VerifyDexWithClassMethod) {
- DexBuilder dex_file;
-
- auto cbuilder{dex_file.MakeClass("dextest.DexTest")};
-
- auto method{cbuilder.CreateMethod("foo", Prototype{TypeDescriptor::Void()})};
- method.BuildReturn();
- method.Encode();
-
- EXPECT_TRUE(EncodeAndVerify(&dex_file));
-}
-
-// Makes sure a bad DEX class fails to verify.
-TEST(DexBuilderTest, VerifyBadDexWithClassMethod) {
- DexBuilder dex_file;
-
- auto cbuilder{dex_file.MakeClass("dextest.DexTest")};
-
- // This method has the error, because methods cannot take Void() as a parameter.
- auto method{
- cbuilder.CreateMethod("foo", Prototype{TypeDescriptor::Void(), TypeDescriptor::Void()})};
- method.BuildReturn();
- method.Encode();
-
- EXPECT_FALSE(EncodeAndVerify(&dex_file));
-}
-
-// Write out and verify a DEX file that corresponds to:
-//
-// package dextest;
-// public class DexTest {
-// public static int foo() { return 5; }
-// }
-TEST(DexBuilderTest, VerifyDexReturn5) {
- DexBuilder dex_file;
-
- auto cbuilder{dex_file.MakeClass("dextest.DexTest")};
-
- auto method{cbuilder.CreateMethod("foo", Prototype{TypeDescriptor::Int()})};
- auto r = method.MakeRegister();
- method.BuildConst4(r, 5);
- method.BuildReturn(r);
- method.Encode();
-
- EXPECT_TRUE(EncodeAndVerify(&dex_file));
-}
-
-// Write out and verify a DEX file that corresponds to:
-//
-// package dextest;
-// public class DexTest {
-// public static int foo(int x) { return x; }
-// }
-TEST(DexBuilderTest, VerifyDexReturnIntParam) {
- DexBuilder dex_file;
-
- auto cbuilder{dex_file.MakeClass("dextest.DexTest")};
-
- auto method{
- cbuilder.CreateMethod("foo", Prototype{TypeDescriptor::Int(), TypeDescriptor::Int()})};
- method.BuildReturn(Value::Parameter(0));
- method.Encode();
-
- EXPECT_TRUE(EncodeAndVerify(&dex_file));
-}
-
-// Write out and verify a DEX file that corresponds to:
-//
-// package dextest;
-// public class DexTest {
-// public static int foo(String s) { return s.length(); }
-// }
-TEST(DexBuilderTest, VerifyDexCallStringLength) {
- DexBuilder dex_file;
-
- auto cbuilder{dex_file.MakeClass("dextest.DexTest")};
-
- MethodBuilder method{cbuilder.CreateMethod(
- "foo", Prototype{TypeDescriptor::Int(), TypeDescriptor::FromClassname("java.lang.String")})};
-
- Value result = method.MakeRegister();
-
- MethodDeclData string_length =
- dex_file.GetOrDeclareMethod(TypeDescriptor::FromClassname("java.lang.String"),
- "length",
- Prototype{TypeDescriptor::Int()});
-
- method.AddInstruction(Instruction::InvokeVirtual(string_length.id, result, Value::Parameter(0)));
- method.BuildReturn(result);
-
- method.Encode();
-
- EXPECT_TRUE(EncodeAndVerify(&dex_file));
-}
-
-// Write out and verify a DEX file that corresponds to:
-//
-// package dextest;
-// public class DexTest {
-// public static int foo(String s) { return s.length(); }
-// }
-TEST(DexBuilderTest, VerifyDexCallManyRegisters) {
- DexBuilder dex_file;
-
- auto cbuilder{dex_file.MakeClass("dextest.DexTest")};
-
- MethodBuilder method{cbuilder.CreateMethod(
- "foo", Prototype{TypeDescriptor::Int()})};
-
- Value result = method.MakeRegister();
-
- // Make a bunch of registers
- for (size_t i = 0; i < 25; ++i) {
- method.MakeRegister();
- }
-
- // Now load a string literal into a register
- Value string_val = method.MakeRegister();
- method.BuildConstString(string_val, "foo");
-
- MethodDeclData string_length =
- dex_file.GetOrDeclareMethod(TypeDescriptor::FromClassname("java.lang.String"),
- "length",
- Prototype{TypeDescriptor::Int()});
-
- method.AddInstruction(Instruction::InvokeVirtual(string_length.id, result, string_val));
- method.BuildReturn(result);
-
- method.Encode();
-
- EXPECT_TRUE(EncodeAndVerify(&dex_file));
-}
diff --git a/startop/view_compiler/dex_builder_test/Android.bp b/startop/view_compiler/dex_builder_test/Android.bp
index ac60e96..9ad1ca1 100644
--- a/startop/view_compiler/dex_builder_test/Android.bp
+++ b/startop/view_compiler/dex_builder_test/Android.bp
@@ -39,6 +39,7 @@
srcs: [
"src/android/startop/test/DexBuilderTest.java",
"src/android/startop/test/LayoutCompilerTest.java",
+ "src/android/startop/test/TestClass.java",
],
sdk_version: "current",
data: [":generate_dex_testcases", ":generate_compiled_layout1", ":generate_compiled_layout2"],
diff --git a/startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java b/startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java
index 42d4161..93496d0 100644
--- a/startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java
+++ b/startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java
@@ -28,10 +28,10 @@
// Adding tests here requires changes in several other places. See README.md in
// the view_compiler directory for more information.
-public class DexBuilderTest {
+public final class DexBuilderTest {
static ClassLoader loadDexFile(String filename) throws Exception {
return new PathClassLoader("/data/local/tmp/dex-builder-test/" + filename,
- ClassLoader.getSystemClassLoader());
+ DexBuilderTest.class.getClassLoader());
}
public void hello() {}
@@ -171,4 +171,44 @@
}
Assert.assertTrue(castFailed);
}
+
+ @Test
+ public void readStaticField() throws Exception {
+ ClassLoader loader = loadDexFile("simple.dex");
+ Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests");
+ Method method = clazz.getMethod("readStaticField");
+ TestClass.staticInteger = 5;
+ Assert.assertEquals(5, method.invoke(null));
+ }
+
+ @Test
+ public void setStaticField() throws Exception {
+ ClassLoader loader = loadDexFile("simple.dex");
+ Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests");
+ Method method = clazz.getMethod("setStaticField");
+ TestClass.staticInteger = 5;
+ method.invoke(null);
+ Assert.assertEquals(7, TestClass.staticInteger);
+ }
+
+ @Test
+ public void readInstanceField() throws Exception {
+ ClassLoader loader = loadDexFile("simple.dex");
+ Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests");
+ Method method = clazz.getMethod("readInstanceField", TestClass.class);
+ TestClass obj = new TestClass();
+ obj.instanceField = 5;
+ Assert.assertEquals(5, method.invoke(null, obj));
+ }
+
+ @Test
+ public void setInstanceField() throws Exception {
+ ClassLoader loader = loadDexFile("simple.dex");
+ Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests");
+ Method method = clazz.getMethod("setInstanceField", TestClass.class);
+ TestClass obj = new TestClass();
+ obj.instanceField = 5;
+ method.invoke(null, obj);
+ Assert.assertEquals(7, obj.instanceField);
+ }
}
diff --git a/startop/view_compiler/dex_builder_test/src/android/startop/test/TestClass.java b/startop/view_compiler/dex_builder_test/src/android/startop/test/TestClass.java
new file mode 100644
index 0000000..dd77923
--- /dev/null
+++ b/startop/view_compiler/dex_builder_test/src/android/startop/test/TestClass.java
@@ -0,0 +1,23 @@
+/*
+ * 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 android.startop.test;
+
+ /**
+ * A simple class to help test DexBuilder.
+ */
+public final class TestClass {
+ public static int staticInteger;
+
+ public int instanceField;
+}
diff --git a/startop/view_compiler/dex_layout_compiler.cc b/startop/view_compiler/dex_layout_compiler.cc
index c68793d..8febfb7 100644
--- a/startop/view_compiler/dex_layout_compiler.cc
+++ b/startop/view_compiler/dex_layout_compiler.cc
@@ -23,25 +23,6 @@
using android::base::StringPrintf;
-void LayoutValidationVisitor::VisitStartTag(const std::u16string& name) {
- if (0 == name.compare(u"merge")) {
- message_ = "Merge tags are not supported";
- can_compile_ = false;
- }
- if (0 == name.compare(u"include")) {
- message_ = "Include tags are not supported";
- can_compile_ = false;
- }
- if (0 == name.compare(u"view")) {
- message_ = "View tags are not supported";
- can_compile_ = false;
- }
- if (0 == name.compare(u"fragment")) {
- message_ = "Fragment tags are not supported";
- can_compile_ = false;
- }
-}
-
DexViewBuilder::DexViewBuilder(dex::MethodBuilder* method)
: method_{method},
context_{dex::Value::Parameter(0)},
diff --git a/startop/view_compiler/dex_testcase_generator.cc b/startop/view_compiler/dex_testcase_generator.cc
index f62ec5dd..6dedf24 100644
--- a/startop/view_compiler/dex_testcase_generator.cc
+++ b/startop/view_compiler/dex_testcase_generator.cc
@@ -282,6 +282,62 @@
method.Encode();
}(castObjectToString);
+ TypeDescriptor test_class = TypeDescriptor::FromClassname("android.startop.test.TestClass");
+
+ // Read a static field
+ // int readStaticField() { return TestClass.staticInteger; }
+ MethodBuilder readStaticField{
+ cbuilder.CreateMethod("readStaticField", Prototype{TypeDescriptor::Int()})};
+ [&](MethodBuilder& method) {
+ const ir::FieldDecl* field =
+ dex_file.GetOrAddField(test_class, "staticInteger", TypeDescriptor::Int());
+ Value result{method.MakeRegister()};
+ method.AddInstruction(Instruction::GetStaticField(field->orig_index, result));
+ method.BuildReturn(result, /*is_object=*/false);
+ method.Encode();
+ }(readStaticField);
+
+ // Set a static field
+ // void setStaticField() { TestClass.staticInteger = 7; }
+ MethodBuilder setStaticField{
+ cbuilder.CreateMethod("setStaticField", Prototype{TypeDescriptor::Void()})};
+ [&](MethodBuilder& method) {
+ const ir::FieldDecl* field =
+ dex_file.GetOrAddField(test_class, "staticInteger", TypeDescriptor::Int());
+ Value number{method.MakeRegister()};
+ method.BuildConst4(number, 7);
+ method.AddInstruction(Instruction::SetStaticField(field->orig_index, number));
+ method.BuildReturn();
+ method.Encode();
+ }(setStaticField);
+
+ // Read an instance field
+ // int readInstanceField(TestClass obj) { return obj.instanceField; }
+ MethodBuilder readInstanceField{
+ cbuilder.CreateMethod("readInstanceField", Prototype{TypeDescriptor::Int(), test_class})};
+ [&](MethodBuilder& method) {
+ const ir::FieldDecl* field =
+ dex_file.GetOrAddField(test_class, "instanceField", TypeDescriptor::Int());
+ Value result{method.MakeRegister()};
+ method.AddInstruction(Instruction::GetField(field->orig_index, result, Value::Parameter(0)));
+ method.BuildReturn(result, /*is_object=*/false);
+ method.Encode();
+ }(readInstanceField);
+
+ // Set an instance field
+ // void setInstanceField(TestClass obj) { obj.instanceField = 7; }
+ MethodBuilder setInstanceField{
+ cbuilder.CreateMethod("setInstanceField", Prototype{TypeDescriptor::Void(), test_class})};
+ [&](MethodBuilder& method) {
+ const ir::FieldDecl* field =
+ dex_file.GetOrAddField(test_class, "instanceField", TypeDescriptor::Int());
+ Value number{method.MakeRegister()};
+ method.BuildConst4(number, 7);
+ method.AddInstruction(Instruction::SetField(field->orig_index, Value::Parameter(0), number));
+ method.BuildReturn();
+ method.Encode();
+ }(setInstanceField);
+
slicer::MemView image{dex_file.CreateImage()};
std::ofstream out_file(outdir + "/simple.dex");
out_file.write(image.ptr<const char>(), image.size());
diff --git a/telecomm/java/android/telecom/Logging/Session.java b/telecomm/java/android/telecom/Logging/Session.java
index c45bd6b..4a8bae7 100644
--- a/telecomm/java/android/telecom/Logging/Session.java
+++ b/telecomm/java/android/telecom/Logging/Session.java
@@ -33,6 +33,8 @@
*/
public class Session {
+ public static final String LOG_TAG = "Session";
+
public static final String START_SESSION = "START_SESSION";
public static final String START_EXTERNAL_SESSION = "START_EXTERNAL_SESSION";
public static final String CREATE_SUBSESSION = "CREATE_SUBSESSION";
@@ -45,6 +47,9 @@
public static final String EXTERNAL_INDICATOR = "E-";
public static final String TRUNCATE_STRING = "...";
+ // Prevent infinite recursion by setting a reasonable limit.
+ private static final int SESSION_RECURSION_LIMIT = 25;
+
/**
* Initial value of mExecutionEndTimeMs and the final value of {@link #getLocalExecutionTime()}
* if the Session is canceled.
@@ -226,6 +231,15 @@
// Builds full session id recursively
private String getFullSessionId() {
+ return getFullSessionId(0);
+ }
+
+ // keep track of calls and bail if we hit the recursion limit
+ private String getFullSessionId(int parentCount) {
+ if (parentCount >= SESSION_RECURSION_LIMIT) {
+ Log.w(LOG_TAG, "getFullSessionId: Hit recursion limit!");
+ return TRUNCATE_STRING + mSessionId;
+ }
// Cache mParentSession locally to prevent a concurrency problem where
// Log.endParentSessions() is called while a logging statement is running (Log.i, for
// example) and setting mParentSession to null in a different thread after the null check
@@ -235,42 +249,57 @@
return mSessionId;
} else {
if (Log.VERBOSE) {
- return parentSession.getFullSessionId() +
+ return parentSession.getFullSessionId(parentCount + 1)
// Append "_X" to subsession to show subsession designation.
- SESSION_SEPARATION_CHAR_CHILD + mSessionId;
+ + SESSION_SEPARATION_CHAR_CHILD + mSessionId;
} else {
// Only worry about the base ID at the top of the tree.
- return parentSession.getFullSessionId();
+ return parentSession.getFullSessionId(parentCount + 1);
}
}
}
- // Print out the full Session tree from any subsession node
- public String printFullSessionTree() {
- // Get to the top of the tree
+ private Session getRootSession(String callingMethod) {
+ int currParentCount = 0;
Session topNode = this;
while (topNode.getParentSession() != null) {
+ if (currParentCount >= SESSION_RECURSION_LIMIT) {
+ Log.w(LOG_TAG, "getRootSession: Hit recursion limit from " + callingMethod);
+ break;
+ }
topNode = topNode.getParentSession();
+ currParentCount++;
}
- return topNode.printSessionTree();
+ return topNode;
+ }
+
+ // Print out the full Session tree from any subsession node
+ public String printFullSessionTree() {
+ return getRootSession("printFullSessionTree").printSessionTree();
}
// Recursively move down session tree using DFS, but print out each node when it is reached.
- public String printSessionTree() {
+ private String printSessionTree() {
StringBuilder sb = new StringBuilder();
- printSessionTree(0, sb);
+ printSessionTree(0, sb, 0);
return sb.toString();
}
- private void printSessionTree(int tabI, StringBuilder sb) {
+ private void printSessionTree(int tabI, StringBuilder sb, int currChildCount) {
+ // Prevent infinite recursion.
+ if (currChildCount >= SESSION_RECURSION_LIMIT) {
+ Log.w(LOG_TAG, "printSessionTree: Hit recursion limit!");
+ sb.append(TRUNCATE_STRING);
+ return;
+ }
sb.append(toString());
for (Session child : mChildSessions) {
sb.append("\n");
for (int i = 0; i <= tabI; i++) {
sb.append("\t");
}
- child.printSessionTree(tabI + 1, sb);
+ child.printSessionTree(tabI + 1, sb, currChildCount + 1);
}
}
@@ -279,11 +308,17 @@
// recent) will be truncated to "..."
public String getFullMethodPath(boolean truncatePath) {
StringBuilder sb = new StringBuilder();
- getFullMethodPath(sb, truncatePath);
+ getFullMethodPath(sb, truncatePath, 0);
return sb.toString();
}
- private synchronized void getFullMethodPath(StringBuilder sb, boolean truncatePath) {
+ private synchronized void getFullMethodPath(StringBuilder sb, boolean truncatePath,
+ int parentCount) {
+ if (parentCount >= SESSION_RECURSION_LIMIT) {
+ Log.w(LOG_TAG, "getFullMethodPath: Hit recursion limit!");
+ sb.append(TRUNCATE_STRING);
+ return;
+ }
// Return cached value for method path. When returning the truncated path, recalculate the
// full path without using the cached value.
if (!TextUtils.isEmpty(mFullMethodPathCache) && !truncatePath) {
@@ -296,7 +331,7 @@
// Check to see if the session has been renamed yet. If it has not, then the session
// has not been continued.
isSessionStarted = !mShortMethodName.equals(parentSession.mShortMethodName);
- parentSession.getFullMethodPath(sb, truncatePath);
+ parentSession.getFullMethodPath(sb, truncatePath, parentCount + 1);
sb.append(SUBSESSION_SEPARATION_CHAR);
}
// Encapsulate the external session's method name so it is obvious what part of the session
@@ -319,13 +354,10 @@
mFullMethodPathCache = sb.toString();
}
}
+
// Recursively move to the top of the tree to see if the parent session is external.
private boolean isSessionExternal() {
- if (getParentSession() == null) {
- return isExternal();
- } else {
- return getParentSession().isSessionExternal();
- }
+ return getRootSession("isSessionExternal").isExternal();
}
@Override
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 3b4b868..b40cca6 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -1442,6 +1442,9 @@
* foreground call is ended.
* <p>
* Requires permission {@link android.Manifest.permission#ANSWER_PHONE_CALLS}.
+ * <p>
+ * Note: this method CANNOT be used to end ongoing emergency calls and will return {@code false}
+ * if an attempt is made to end an emergency call.
*
* @return {@code true} if there is a call which will be rejected or terminated, {@code false}
* otherwise.
diff --git a/telephony/java/android/provider/Telephony.java b/telephony/java/android/provider/Telephony.java
index 83aa521..76fc097 100644
--- a/telephony/java/android/provider/Telephony.java
+++ b/telephony/java/android/provider/Telephony.java
@@ -629,7 +629,7 @@
}
/**
- * Contains all sent text-based SMS messages in the SMS app.
+ * Contains all draft text-based SMS messages in the SMS app.
*/
public static final class Draft implements BaseColumns, TextBasedSmsColumns {
@@ -745,7 +745,15 @@
}
/**
- * Contains all sent text-based SMS messages in the SMS app.
+ * Contains a view of SMS conversations (also referred to as threads). This is similar to
+ * {@link Threads}, but only includes SMS messages and columns relevant to SMS
+ * conversations.
+ * <p>
+ * Note that this view ignores any information about MMS messages, it is a
+ * view of conversations as if MMS messages did not exist at all. This means that all
+ * relevant information, such as snippets and message count, will ignore any MMS messages
+ * that might be in the same thread through other views and present only data based on the
+ * SMS messages in that thread.
*/
public static final class Conversations
implements BaseColumns, TextBasedSmsColumns {
@@ -2954,6 +2962,20 @@
* <P>Type: INTEGER</P>
*/
public static final String CHARSET = "charset";
+
+ /**
+ * Generates a Addr {@link Uri} for message, used to perform Addr table operation
+ * for mms.
+ *
+ * @param messageId the messageId used to generate Addr {@link Uri} dynamically
+ * @return the addrUri used to perform Addr table operation for mms
+ */
+ @NonNull
+ public static Uri getAddrUriForMessage(@NonNull String messageId) {
+ Uri addrUri = Mms.CONTENT_URI.buildUpon()
+ .appendPath(String.valueOf(messageId)).appendPath("addr").build();
+ return addrUri;
+ }
}
/**
@@ -2972,10 +2994,16 @@
}
/**
+ * The name of part table.
+ */
+ private static final String TABLE_PART = "part";
+
+ /**
* The {@code content://} style URL for this table. Can be appended with a part ID to
* address individual parts.
*/
- public static final Uri CONTENT_URI = Uri.withAppendedPath(Mms.CONTENT_URI, "part");
+ @NonNull
+ public static final Uri CONTENT_URI = Uri.withAppendedPath(Mms.CONTENT_URI, TABLE_PART);
/**
* The identifier of the message which this part belongs to.
@@ -3054,6 +3082,21 @@
* <P>Type: TEXT</P>
*/
public static final String TEXT = "text";
+
+ /**
+ * Generates a Part {@link Uri} for message, used to perform Part table operation
+ * for mms.
+ *
+ * @param messageId the messageId used to generate Part {@link Uri} dynamically
+ * @return the partUri used to perform Part table operation for mms
+ */
+ @NonNull
+ public static Uri getPartUriForMessage(@NonNull String messageId) {
+ Uri partUri = Mms.CONTENT_URI.buildUpon()
+ .appendPath(String.valueOf(messageId)).appendPath(
+ TABLE_PART).build();
+ return partUri;
+ }
}
/**
@@ -3158,6 +3201,8 @@
/**
* The {@code content://} style URL for locked messages in this table.
+ * <P>This {@link Uri} is used to check at most one locked message found in the union of MMS
+ * and SMS messages. Also this will return only _id column in response.</P>
*/
public static final Uri CONTENT_LOCKED_URI = Uri.parse(
"content://mms-sms/locked");
@@ -3976,6 +4021,53 @@
public static final String DEFAULT_SORT_ORDER = DELIVERY_TIME + " DESC";
/**
+ * The Epoch Unix timestamp when the device received the message.
+ * <P>Type: INTEGER</P>
+ */
+ public static final String RECEIVED_TIME = "received_time";
+
+ /**
+ * Indicates that whether the message has been broadcasted to the application.
+ * <P>Type: BOOLEAN</P>
+ */
+ public static final String MESSAGE_BROADCASTED = "message_broadcasted";
+
+ /**
+ * The Warning Area Coordinates Elements. This element is used for geo-fencing purpose.
+ *
+ * The geometry and its coordinates are separated vertical bar, the first item is the
+ * geometry type and the remaining items are the parameter of this geometry.
+ *
+ * Only circle and polygon are supported. The coordinates are represented in Horizontal
+ * coordinates format.
+ *
+ * Coordinate encoding:
+ * "latitude, longitude"
+ * where -90.00000 <= latitude <= 90.00000 and -180.00000 <= longitude <= 180.00000
+ *
+ * Polygon encoding:
+ * "polygon|lat1,lng1|lat2,lng2|...|latn,lngn"
+ * lat1,lng1 ... latn,lngn are the vertices coordinate of the polygon.
+ *
+ * Circle encoding:
+ * "circle|lat,lng|radius".
+ * lat,lng is the center of the circle. The unit of circle's radius is meter.
+ *
+ * Example:
+ * "circle|0,0|100" mean a circle which center located at (0,0) and its radius is 100 meter.
+ * "polygon|0,1.5|0,1|1,1|1,0" mean a polygon has vertices (0,1.5),(0,1),(1,1),(1,0).
+ *
+ * There could be more than one geometry store in this field, they are separated by a
+ * semicolon.
+ *
+ * Example:
+ * "circle|0,0|100;polygon|0,0|0,1.5|1,1|1,0;circle|100.123,100|200.123"
+ *
+ * <P>Type: TEXT</P>
+ */
+ public static final String GEOMETRIES = "geometries";
+
+ /**
* Query columns for instantiating {@link android.telephony.CellBroadcastMessage} objects.
*/
public static final String[] QUERY_COLUMNS = {
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 31cd608..44c50f0 100755
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -852,6 +852,19 @@
"carrier_metered_roaming_apn_types_strings";
/**
+ * APN types that are not allowed on cellular
+ * @hide
+ */
+ public static final String KEY_CARRIER_WWAN_DISALLOWED_APN_TYPES_STRING_ARRAY =
+ "carrier_wwan_disallowed_apn_types_string_array";
+
+ /**
+ * APN types that are not allowed on IWLAN
+ * @hide
+ */
+ public static final String KEY_CARRIER_WLAN_DISALLOWED_APN_TYPES_STRING_ARRAY =
+ "carrier_wlan_disallowed_apn_types_string_array";
+ /**
* CDMA carrier ERI (Enhanced Roaming Indicator) file name
* @hide
*/
@@ -2377,6 +2390,14 @@
public static final String KEY_CHECK_PRICING_WITH_CARRIER_FOR_DATA_ROAMING_BOOL =
"check_pricing_with_carrier_data_roaming_bool";
+ /**
+ * Determines whether we should show a notification when the phone established a data
+ * connection in roaming network, to warn users about possible roaming charges.
+ * @hide
+ */
+ public static final String KEY_SHOW_DATA_CONNECTED_ROAMING_NOTIFICATION_BOOL =
+ "show_data_connected_roaming_notification";
+
/**
* A list of 4 LTE RSRP thresholds above which a signal level is considered POOR,
* MODERATE, GOOD, or EXCELLENT, to be used in SignalStrength reporting.
@@ -2804,6 +2825,23 @@
"is_opportunistic_subscription_bool";
/**
+ * Configs used by the IMS stack.
+ */
+ public static final class Ims {
+ /** Prefix of all Ims.KEY_* constants. */
+ public static final String KEY_PREFIX = "ims.";
+
+ //TODO: Add configs related to IMS.
+
+ private Ims() {}
+
+ private static PersistableBundle getDefaults() {
+ PersistableBundle defaults = new PersistableBundle();
+ return defaults;
+ }
+ }
+
+ /**
* A list of 4 GSM RSSI thresholds above which a signal level is considered POOR,
* MODERATE, GOOD, or EXCELLENT, to be used in SignalStrength reporting.
*
@@ -2817,6 +2855,15 @@
public static final String KEY_GSM_RSSI_THRESHOLDS_INT_ARRAY =
"gsm_rssi_thresholds_int_array";
+ /**
+ * Determines whether Wireless Priority Service call is supported over IMS.
+ *
+ * See Wireless Priority Service from https://www.fcc.gov/general/wireless-priority-service-wps
+ * @hide
+ */
+ public static final String KEY_SUPPORT_WPS_OVER_IMS_BOOL =
+ "support_wps_over_ims_bool";
+
/** The default value for every variable. */
private final static PersistableBundle sDefaults;
@@ -2951,6 +2998,10 @@
new String[]{"default", "mms", "dun", "supl"});
sDefaults.putStringArray(KEY_CARRIER_METERED_ROAMING_APN_TYPES_STRINGS,
new String[]{"default", "mms", "dun", "supl"});
+ sDefaults.putStringArray(KEY_CARRIER_WWAN_DISALLOWED_APN_TYPES_STRING_ARRAY,
+ new String[]{""});
+ sDefaults.putStringArray(KEY_CARRIER_WLAN_DISALLOWED_APN_TYPES_STRING_ARRAY,
+ new String[]{""});
sDefaults.putIntArray(KEY_ONLY_SINGLE_DC_ALLOWED_INT_ARRAY,
new int[]{
4, /* IS95A */
@@ -3157,6 +3208,7 @@
sDefaults.putString(KEY_CARRIER_SETTINGS_ACTIVITY_COMPONENT_NAME_STRING, "");
sDefaults.putBoolean(KEY_CARRIER_CONFIG_APPLIED_BOOL, false);
sDefaults.putBoolean(KEY_CHECK_PRICING_WITH_CARRIER_FOR_DATA_ROAMING_BOOL, false);
+ sDefaults.putBoolean(KEY_SHOW_DATA_CONNECTED_ROAMING_NOTIFICATION_BOOL, false);
sDefaults.putIntArray(KEY_LTE_RSRP_THRESHOLDS_INT_ARRAY,
new int[] {
-128, /* SIGNAL_STRENGTH_POOR */
@@ -3217,6 +3269,8 @@
-97, /* SIGNAL_STRENGTH_GOOD */
-89, /* SIGNAL_STRENGTH_GREAT */
});
+ sDefaults.putBoolean(KEY_SUPPORT_WPS_OVER_IMS_BOOL, true);
+ sDefaults.putAll(Ims.getDefaults());
}
/**
@@ -3413,4 +3467,75 @@
return ICarrierConfigLoader.Stub
.asInterface(ServiceManager.getService(Context.CARRIER_CONFIG_SERVICE));
}
+
+ /**
+ * Gets the configuration values for a component using its prefix.
+ *
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+ *
+ * @param prefix prefix of the component.
+ * @param subId the subscription ID, normally obtained from {@link SubscriptionManager}.
+ *
+ * @see #getConfigForSubId
+ */
+ @Nullable
+ public PersistableBundle getConfigByComponentForSubId(String prefix, int subId) {
+ PersistableBundle configs = getConfigForSubId(subId);
+
+ if (configs == null) {
+ return null;
+ }
+
+ PersistableBundle ret = new PersistableBundle();
+ for (String configKey : configs.keySet()) {
+ if (configKey.startsWith(prefix)) {
+ addConfig(configKey, configs.get(configKey), ret);
+ }
+ }
+
+ return ret;
+ }
+
+ private void addConfig(String key, Object value, PersistableBundle configs) {
+ if (value instanceof String) {
+ configs.putString(key, (String) value);
+ }
+
+ if (value instanceof String[]) {
+ configs.putStringArray(key, (String[]) value);
+ }
+
+ if (value instanceof Integer) {
+ configs.putInt(key, (Integer) value);
+ }
+
+ if (value instanceof Long) {
+ configs.putLong(key, (Long) value);
+ }
+
+ if (value instanceof Double) {
+ configs.putDouble(key, (Double) value);
+ }
+
+ if (value instanceof Boolean) {
+ configs.putBoolean(key, (Boolean) value);
+ }
+
+ if (value instanceof int[]) {
+ configs.putIntArray(key, (int[]) value);
+ }
+
+ if (value instanceof double[]) {
+ configs.putDoubleArray(key, (double[]) value);
+ }
+
+ if (value instanceof boolean[]) {
+ configs.putBooleanArray(key, (boolean[]) value);
+ }
+
+ if (value instanceof long[]) {
+ configs.putLongArray(key, (long[]) value);
+ }
+ }
}
diff --git a/telephony/java/android/telephony/CellSignalStrengthGsm.java b/telephony/java/android/telephony/CellSignalStrengthGsm.java
index 127eabd..31b3a05 100644
--- a/telephony/java/android/telephony/CellSignalStrengthGsm.java
+++ b/telephony/java/android/telephony/CellSignalStrengthGsm.java
@@ -143,7 +143,7 @@
}
/**
- * Get the signal strength as dBm
+ * Get the signal strength as dBm.
*/
@Override
public int getDbm() {
@@ -163,18 +163,17 @@
}
/**
- * Return the Received Signal Strength Indicator
+ * Return the Received Signal Strength Indicator.
*
* @return the RSSI in dBm (-113, -51) or
* {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE}.
- * @hide
*/
public int getRssi() {
return mRssi;
}
/**
- * Return the Bit Error Rate
+ * Return the Bit Error Rate.
*
* @return the bit error rate (0-7, 99) as defined in TS 27.007 8.5 or
* {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE}.
diff --git a/telephony/java/android/telephony/NetworkServiceCallback.java b/telephony/java/android/telephony/NetworkServiceCallback.java
index 1c64bcd..89b9665 100644
--- a/telephony/java/android/telephony/NetworkServiceCallback.java
+++ b/telephony/java/android/telephony/NetworkServiceCallback.java
@@ -24,7 +24,6 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import java.lang.ref.WeakReference;
/**
* Network service callback. Object of this class is passed to NetworkServiceProvider upon
@@ -61,11 +60,11 @@
/** Request failed */
public static final int RESULT_ERROR_FAILED = 5;
- private final WeakReference<INetworkServiceCallback> mCallback;
+ private final INetworkServiceCallback mCallback;
/** @hide */
public NetworkServiceCallback(INetworkServiceCallback callback) {
- mCallback = new WeakReference<>(callback);
+ mCallback = callback;
}
/**
@@ -78,15 +77,14 @@
*/
public void onRequestNetworkRegistrationInfoComplete(int result,
@Nullable NetworkRegistrationInfo state) {
- INetworkServiceCallback callback = mCallback.get();
- if (callback != null) {
+ if (mCallback != null) {
try {
- callback.onRequestNetworkRegistrationInfoComplete(result, state);
+ mCallback.onRequestNetworkRegistrationInfoComplete(result, state);
} catch (RemoteException e) {
Rlog.e(mTag, "Failed to onRequestNetworkRegistrationInfoComplete on the remote");
}
} else {
- Rlog.e(mTag, "Weak reference of callback is null.");
+ Rlog.e(mTag, "onRequestNetworkRegistrationInfoComplete callback is null.");
}
}
}
\ No newline at end of file
diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java
index b75e515..f03a9dc 100644
--- a/telephony/java/android/telephony/PhoneNumberUtils.java
+++ b/telephony/java/android/telephony/PhoneNumberUtils.java
@@ -22,9 +22,11 @@
import com.android.i18n.phonenumbers.Phonenumber.PhoneNumber;
import android.annotation.IntDef;
+import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.Intent;
+import android.content.res.Resources;
import android.database.Cursor;
import android.location.CountryDetector;
import android.net.Uri;
@@ -164,6 +166,33 @@
return c == 'w'||c == 'W';
}
+ private static int sMinMatch = 0;
+
+ private static int getMinMatch() {
+ if (sMinMatch == 0) {
+ sMinMatch = Resources.getSystem().getInteger(
+ com.android.internal.R.integer.config_phonenumber_compare_min_match);
+ }
+ return sMinMatch;
+ }
+
+ /**
+ * A Test API to get current sMinMatch.
+ * @hide
+ */
+ @TestApi
+ public static int getMinMatchForTest() {
+ return getMinMatch();
+ }
+
+ /**
+ * A Test API to set sMinMatch.
+ * @hide
+ */
+ @TestApi
+ public static void setMinMatchForTest(int minMatch) {
+ sMinMatch = minMatch;
+ }
/** Returns true if ch is not dialable or alpha char */
private static boolean isSeparator(char ch) {
@@ -475,7 +504,7 @@
* enough for caller ID purposes.
*
* - Compares from right to left
- * - requires MIN_MATCH (7) characters to match
+ * - requires minimum characters to match
* - handles common trunk prefixes and international prefixes
* (basically, everything except the Russian trunk prefix)
*
@@ -491,6 +520,7 @@
int matched;
int numNonDialableCharsInA = 0;
int numNonDialableCharsInB = 0;
+ int minMatch = getMinMatch();
if (a == null || b == null) return a == b;
@@ -530,12 +560,12 @@
}
}
- if (matched < MIN_MATCH) {
+ if (matched < minMatch) {
int effectiveALen = a.length() - numNonDialableCharsInA;
int effectiveBLen = b.length() - numNonDialableCharsInB;
- // if the number of dialable chars in a and b match, but the matched chars < MIN_MATCH,
+ // if the number of dialable chars in a and b match, but the matched chars < minMatch,
// treat them as equal (i.e. 404-04 and 40404)
if (effectiveALen == effectiveBLen && effectiveALen == matched) {
return true;
@@ -545,7 +575,7 @@
}
// At least one string has matched completely;
- if (matched >= MIN_MATCH && (ia < 0 || ib < 0)) {
+ if (matched >= minMatch && (ia < 0 || ib < 0)) {
return true;
}
@@ -736,7 +766,7 @@
}
/**
- * Returns the rightmost MIN_MATCH (5) characters in the network portion
+ * Returns the rightmost minimum matched characters in the network portion
* in *reversed* order
*
* This can be used to do a database lookup against the column
@@ -747,7 +777,7 @@
public static String
toCallerIDMinMatch(String phoneNumber) {
String np = extractNetworkPortionAlt(phoneNumber);
- return internalGetStrippedReversed(np, MIN_MATCH);
+ return internalGetStrippedReversed(np, getMinMatch());
}
/**
@@ -1709,26 +1739,6 @@
return normalizedDigits.toString();
}
- // Three and four digit phone numbers for either special services,
- // or 3-6 digit addresses from the network (eg carrier-originated SMS messages) should
- // not match.
- //
- // This constant used to be 5, but SMS short codes has increased in length and
- // can be easily 6 digits now days. Most countries have SMS short code length between
- // 3 to 6 digits. The exceptions are
- //
- // Australia: Short codes are six or eight digits in length, starting with the prefix "19"
- // followed by an additional four or six digits and two.
- // Czechia: Codes are seven digits in length for MO and five (not billed) or
- // eight (billed) for MT direction
- //
- // see http://en.wikipedia.org/wiki/Short_code#Regional_differences for reference
- //
- // However, in order to loose match 650-555-1212 and 555-1212, we need to set the min match
- // to 7.
- @UnsupportedAppUsage
- static final int MIN_MATCH = 7;
-
/**
* Checks a given number against the list of
* emergency numbers provided by the RIL and SIM card.
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index fe81215..d72ca6b 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -2327,6 +2327,10 @@
* <p>
* The ISO-3166 country code is provided in lowercase 2 character format.
* <p>
+ * Note: In multi-sim, this returns a shared emergency network country iso from other
+ * subscription if the subscription used to create the TelephonyManager doesn't camp on
+ * a network due to some reason (e.g. pin/puk locked), or sim is absent in the corresponding
+ * slot.
* Note: Result may be unreliable on CDMA networks (use {@link #getPhoneType()} to determine
* if on a CDMA network).
* <p>
diff --git a/telephony/java/android/telephony/data/DataServiceCallback.java b/telephony/java/android/telephony/data/DataServiceCallback.java
index 5d8d793..11dc78a 100644
--- a/telephony/java/android/telephony/data/DataServiceCallback.java
+++ b/telephony/java/android/telephony/data/DataServiceCallback.java
@@ -27,7 +27,6 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import java.lang.ref.WeakReference;
import java.util.List;
/**
@@ -42,6 +41,8 @@
private static final String TAG = DataServiceCallback.class.getSimpleName();
+ private static final boolean DBG = true;
+
/**
* Result of data requests
* @hide
@@ -62,11 +63,11 @@
/** Request sent in illegal state */
public static final int RESULT_ERROR_ILLEGAL_STATE = 4;
- private final WeakReference<IDataServiceCallback> mCallback;
+ private final IDataServiceCallback mCallback;
/** @hide */
public DataServiceCallback(IDataServiceCallback callback) {
- mCallback = new WeakReference<>(callback);
+ mCallback = callback;
}
/**
@@ -78,13 +79,15 @@
*/
public void onSetupDataCallComplete(@ResultCode int result,
@Nullable DataCallResponse response) {
- IDataServiceCallback callback = mCallback.get();
- if (callback != null) {
+ if (mCallback != null) {
try {
- callback.onSetupDataCallComplete(result, response);
+ if (DBG) Rlog.d(TAG, "onSetupDataCallComplete");
+ mCallback.onSetupDataCallComplete(result, response);
} catch (RemoteException e) {
Rlog.e(TAG, "Failed to onSetupDataCallComplete on the remote");
}
+ } else {
+ Rlog.e(TAG, "onSetupDataCallComplete: callback is null!");
}
}
@@ -95,13 +98,15 @@
* @param result The result code. Must be one of the {@link ResultCode}.
*/
public void onDeactivateDataCallComplete(@ResultCode int result) {
- IDataServiceCallback callback = mCallback.get();
- if (callback != null) {
+ if (mCallback != null) {
try {
- callback.onDeactivateDataCallComplete(result);
+ if (DBG) Rlog.d(TAG, "onDeactivateDataCallComplete");
+ mCallback.onDeactivateDataCallComplete(result);
} catch (RemoteException e) {
Rlog.e(TAG, "Failed to onDeactivateDataCallComplete on the remote");
}
+ } else {
+ Rlog.e(TAG, "onDeactivateDataCallComplete: callback is null!");
}
}
@@ -112,13 +117,14 @@
* @param result The result code. Must be one of the {@link ResultCode}.
*/
public void onSetInitialAttachApnComplete(@ResultCode int result) {
- IDataServiceCallback callback = mCallback.get();
- if (callback != null) {
+ if (mCallback != null) {
try {
- callback.onSetInitialAttachApnComplete(result);
+ mCallback.onSetInitialAttachApnComplete(result);
} catch (RemoteException e) {
Rlog.e(TAG, "Failed to onSetInitialAttachApnComplete on the remote");
}
+ } else {
+ Rlog.e(TAG, "onSetInitialAttachApnComplete: callback is null!");
}
}
@@ -129,13 +135,14 @@
* @param result The result code. Must be one of the {@link ResultCode}.
*/
public void onSetDataProfileComplete(@ResultCode int result) {
- IDataServiceCallback callback = mCallback.get();
- if (callback != null) {
+ if (mCallback != null) {
try {
- callback.onSetDataProfileComplete(result);
+ mCallback.onSetDataProfileComplete(result);
} catch (RemoteException e) {
Rlog.e(TAG, "Failed to onSetDataProfileComplete on the remote");
}
+ } else {
+ Rlog.e(TAG, "onSetDataProfileComplete: callback is null!");
}
}
@@ -149,13 +156,14 @@
*/
public void onRequestDataCallListComplete(@ResultCode int result,
@NonNull List<DataCallResponse> dataCallList) {
- IDataServiceCallback callback = mCallback.get();
- if (callback != null) {
+ if (mCallback != null) {
try {
- callback.onRequestDataCallListComplete(result, dataCallList);
+ mCallback.onRequestDataCallListComplete(result, dataCallList);
} catch (RemoteException e) {
Rlog.e(TAG, "Failed to onRequestDataCallListComplete on the remote");
}
+ } else {
+ Rlog.e(TAG, "onRequestDataCallListComplete: callback is null!");
}
}
@@ -166,13 +174,15 @@
* @param dataCallList List of the current active data connection.
*/
public void onDataCallListChanged(@NonNull List<DataCallResponse> dataCallList) {
- IDataServiceCallback callback = mCallback.get();
- if (callback != null) {
+ if (mCallback != null) {
try {
- callback.onDataCallListChanged(dataCallList);
+ if (DBG) Rlog.d(TAG, "onDataCallListChanged");
+ mCallback.onDataCallListChanged(dataCallList);
} catch (RemoteException e) {
Rlog.e(TAG, "Failed to onDataCallListChanged on the remote");
}
+ } else {
+ Rlog.e(TAG, "onDataCallListChanged: callback is null!");
}
}
}
diff --git a/telephony/java/android/telephony/emergency/EmergencyNumber.java b/telephony/java/android/telephony/emergency/EmergencyNumber.java
index aaa98eb..733fb1e 100644
--- a/telephony/java/android/telephony/emergency/EmergencyNumber.java
+++ b/telephony/java/android/telephony/emergency/EmergencyNumber.java
@@ -22,6 +22,7 @@
import android.hardware.radio.V1_4.EmergencyServiceCategory;
import android.os.Parcel;
import android.os.Parcelable;
+import android.telephony.CarrierConfigManager;
import android.telephony.PhoneNumberUtils;
import android.telephony.Rlog;
@@ -301,6 +302,9 @@
* The character in the number string is only the dial pad
* character('0'-'9', '*', '+', or '#'). For example: 911.
*
+ * If the number starts with carrier prefix, the carrier prefix is configured in
+ * {@link CarrierConfigManager#KEY_EMERGENCY_NUMBER_PREFIX_STRING_ARRAY}.
+ *
* @return the dialing number.
*/
public @NonNull String getNumber() {
diff --git a/telephony/java/android/telephony/ims/ImsException.java b/telephony/java/android/telephony/ims/ImsException.java
index 8c686f7..6187e67 100644
--- a/telephony/java/android/telephony/ims/ImsException.java
+++ b/telephony/java/android/telephony/ims/ImsException.java
@@ -20,6 +20,7 @@
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.content.pm.PackageManager;
+import android.telephony.SubscriptionManager;
import android.text.TextUtils;
import java.lang.annotation.Retention;
@@ -38,11 +39,11 @@
*/
public static final int CODE_ERROR_UNSPECIFIED = 0;
/**
- * The operation has failed because there is no {@link ImsService} available to service it. This
- * may be due to an {@link ImsService} crash or other illegal state.
+ * The operation has failed because there is no remote process available to service it. This
+ * may be due to a process crash or other illegal state.
* <p>
* This is a temporary error and the operation may be retried until the connection to the
- * {@link ImsService} is restored.
+ * remote process is restored.
*/
public static final int CODE_ERROR_SERVICE_UNAVAILABLE = 1;
@@ -55,12 +56,23 @@
*/
public static final int CODE_ERROR_UNSUPPORTED_OPERATION = 2;
+ /**
+ * The subscription ID associated with this operation is invalid or not active.
+ * <p>
+ * This is a configuration error and there should be no retry. The subscription used for this
+ * operation is either invalid or has become inactive. The active subscriptions can be queried
+ * with {@link SubscriptionManager#getActiveSubscriptionInfoList()}.
+ * @hide
+ */
+ public static final int CODE_ERROR_INVALID_SUBSCRIPTION = 3;
+
/**@hide*/
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = "CODE_ERROR_", value = {
CODE_ERROR_UNSPECIFIED,
CODE_ERROR_SERVICE_UNAVAILABLE,
- CODE_ERROR_UNSUPPORTED_OPERATION
+ CODE_ERROR_UNSUPPORTED_OPERATION,
+ CODE_ERROR_INVALID_SUBSCRIPTION
})
public @interface ImsErrorCode {}
diff --git a/telephony/java/android/telephony/ims/ImsMmTelManager.java b/telephony/java/android/telephony/ims/ImsMmTelManager.java
index be58723..a1a7fcc 100644
--- a/telephony/java/android/telephony/ims/ImsMmTelManager.java
+++ b/telephony/java/android/telephony/ims/ImsMmTelManager.java
@@ -31,6 +31,7 @@
import android.os.Binder;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.ServiceSpecificException;
import android.telephony.AccessNetworkConstants;
import android.telephony.SubscriptionManager;
import android.telephony.ims.aidl.IImsCapabilityCallback;
@@ -375,6 +376,13 @@
c.setExecutor(executor);
try {
getITelephony().registerImsRegistrationCallback(mSubId, c.getBinder());
+ } catch (ServiceSpecificException e) {
+ if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
+ // Rethrow as runtime error to keep API compatible.
+ throw new IllegalArgumentException(e.getMessage());
+ } else {
+ throw new RuntimeException(e.getMessage());
+ }
} catch (RemoteException | IllegalStateException e) {
throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
}
@@ -390,8 +398,6 @@
* @param c The {@link RegistrationCallback} to be removed.
* @see SubscriptionManager.OnSubscriptionsChangedListener
* @see #registerImsRegistrationCallback(Executor, RegistrationCallback)
- * @throws IllegalArgumentException if the subscription ID associated with this callback is
- * invalid.
*/
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public void unregisterImsRegistrationCallback(@NonNull RegistrationCallback c) {
@@ -445,6 +451,13 @@
c.setExecutor(executor);
try {
getITelephony().registerMmTelCapabilityCallback(mSubId, c.getBinder());
+ } catch (ServiceSpecificException e) {
+ if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
+ // Rethrow as runtime error to keep API compatible.
+ throw new IllegalArgumentException(e.getMessage());
+ } else {
+ throw new RuntimeException(e.getMessage());
+ }
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
} catch (IllegalStateException e) {
@@ -460,8 +473,6 @@
* inactive subscription, it will result in a no-op.
* @param c The MmTel {@link CapabilityCallback} to be removed.
* @see #registerMmTelCapabilityCallback(Executor, CapabilityCallback)
- * @throws IllegalArgumentException if the subscription ID associated with this callback is
- * invalid.
*/
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public void unregisterMmTelCapabilityCallback(@NonNull CapabilityCallback c) {
@@ -482,12 +493,9 @@
* be enabled as long as the carrier has provisioned these services for the specified
* subscription. Other IMS services (SMS/UT) are not affected by this user setting and depend on
* carrier requirements.
- *
- * Modifying this value may also trigger an IMS registration or deregistration, depending on
- * whether or not the new value is enabled or disabled.
- *
+ * <p>
* Note: If the carrier configuration for advanced calling is not editable or hidden, this
- * method will do nothing and will instead always use the default value.
+ * method will always return the default value.
*
* @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL
* @see android.telephony.CarrierConfigManager#KEY_EDITABLE_ENHANCED_4G_LTE_BOOL
@@ -495,12 +503,21 @@
* @see android.telephony.CarrierConfigManager#KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL
* @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_AVAILABLE_BOOL
* @see #setAdvancedCallingSettingEnabled(boolean)
+ * @throws IllegalArgumentException if the subscription associated with this operation is not
+ * active (SIM is not inserted, ESIM inactive) or invalid.
* @return true if the user's setting for advanced calling is enabled, false otherwise.
*/
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public boolean isAdvancedCallingSettingEnabled() {
try {
return getITelephony().isAdvancedCallingSettingEnabled(mSubId);
+ } catch (ServiceSpecificException e) {
+ if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
+ // Rethrow as runtime error to keep API compatible.
+ throw new IllegalArgumentException(e.getMessage());
+ } else {
+ throw new RuntimeException(e.getMessage());
+ }
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
@@ -526,12 +543,20 @@
* @see android.telephony.CarrierConfigManager#KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL
* @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_AVAILABLE_BOOL
* @see #isAdvancedCallingSettingEnabled()
+ * @throws IllegalArgumentException if the subscription associated with this operation is not
+ * active (SIM is not inserted, ESIM inactive) or invalid.
*/
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
public void setAdvancedCallingSettingEnabled(boolean isEnabled) {
try {
getITelephony().setAdvancedCallingSettingEnabled(mSubId, isEnabled);
- return;
+ } catch (ServiceSpecificException e) {
+ if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
+ // Rethrow as runtime error to keep API compatible.
+ throw new IllegalArgumentException(e.getMessage());
+ } else {
+ throw new RuntimeException(e.getMessage());
+ }
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
@@ -597,6 +622,9 @@
/**
* The user's setting for whether or not they have enabled the "Video Calling" setting.
+ *
+ * @throws IllegalArgumentException if the subscription associated with this operation is not
+ * active (SIM is not inserted, ESIM inactive) or invalid.
* @return true if the user’s “Video Calling” setting is currently enabled.
* @see #setVtSettingEnabled(boolean)
*/
@@ -604,6 +632,13 @@
public boolean isVtSettingEnabled() {
try {
return getITelephony().isVtSettingEnabled(mSubId);
+ } catch (ServiceSpecificException e) {
+ if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
+ // Rethrow as runtime error to keep API compatible.
+ throw new IllegalArgumentException(e.getMessage());
+ } else {
+ throw new RuntimeException(e.getMessage());
+ }
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
@@ -611,13 +646,22 @@
/**
* Change the user's setting for Video Telephony and enable the Video Telephony capability.
+ *
+ * @throws IllegalArgumentException if the subscription associated with this operation is not
+ * active (SIM is not inserted, ESIM inactive) or invalid.
* @see #isVtSettingEnabled()
*/
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
public void setVtSettingEnabled(boolean isEnabled) {
try {
getITelephony().setVtSettingEnabled(mSubId, isEnabled);
- return;
+ } catch (ServiceSpecificException e) {
+ if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
+ // Rethrow as runtime error to keep API compatible.
+ throw new IllegalArgumentException(e.getMessage());
+ } else {
+ throw new RuntimeException(e.getMessage());
+ }
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
@@ -625,12 +669,22 @@
/**
* @return true if the user's setting for Voice over WiFi is enabled and false if it is not.
+ *
+ * @throws IllegalArgumentException if the subscription associated with this operation is not
+ * active (SIM is not inserted, ESIM inactive) or invalid.
* @see #setVoWiFiSettingEnabled(boolean)
*/
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public boolean isVoWiFiSettingEnabled() {
try {
return getITelephony().isVoWiFiSettingEnabled(mSubId);
+ } catch (ServiceSpecificException e) {
+ if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
+ // Rethrow as runtime error to keep API compatible.
+ throw new IllegalArgumentException(e.getMessage());
+ } else {
+ throw new RuntimeException(e.getMessage());
+ }
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
@@ -638,6 +692,9 @@
/**
* Sets the user's setting for whether or not Voice over WiFi is enabled.
+ *
+ * @throws IllegalArgumentException if the subscription associated with this operation is not
+ * active (SIM is not inserted, ESIM inactive) or invalid.
* @param isEnabled true if the user's setting for Voice over WiFi is enabled, false otherwise=
* @see #isVoWiFiSettingEnabled()
*/
@@ -645,13 +702,23 @@
public void setVoWiFiSettingEnabled(boolean isEnabled) {
try {
getITelephony().setVoWiFiSettingEnabled(mSubId, isEnabled);
- return;
+ } catch (ServiceSpecificException e) {
+ if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
+ // Rethrow as runtime error to keep API compatible.
+ throw new IllegalArgumentException(e.getMessage());
+ } else {
+ throw new RuntimeException(e.getMessage());
+ }
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
}
/**
+ * Returns the user's voice over WiFi roaming setting associated with the current subscription.
+ *
+ * @throws IllegalArgumentException if the subscription associated with this operation is not
+ * active (SIM is not inserted, ESIM inactive) or invalid.
* @return true if the user's setting for Voice over WiFi while roaming is enabled, false
* if disabled.
* @see #setVoWiFiRoamingSettingEnabled(boolean)
@@ -660,6 +727,13 @@
public boolean isVoWiFiRoamingSettingEnabled() {
try {
return getITelephony().isVoWiFiRoamingSettingEnabled(mSubId);
+ } catch (ServiceSpecificException e) {
+ if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
+ // Rethrow as runtime error to keep API compatible.
+ throw new IllegalArgumentException(e.getMessage());
+ } else {
+ throw new RuntimeException(e.getMessage());
+ }
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
@@ -667,15 +741,24 @@
/**
* Change the user's setting for Voice over WiFi while roaming.
+ *
* @param isEnabled true if the user's setting for Voice over WiFi while roaming is enabled,
* false otherwise.
+ * @throws IllegalArgumentException if the subscription associated with this operation is not
+ * active (SIM is not inserted, ESIM inactive) or invalid.
* @see #isVoWiFiRoamingSettingEnabled()
*/
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
public void setVoWiFiRoamingSettingEnabled(boolean isEnabled) {
try {
getITelephony().setVoWiFiRoamingSettingEnabled(mSubId, isEnabled);
- return;
+ } catch (ServiceSpecificException e) {
+ if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
+ // Rethrow as runtime error to keep API compatible.
+ throw new IllegalArgumentException(e.getMessage());
+ } else {
+ throw new RuntimeException(e.getMessage());
+ }
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
@@ -691,19 +774,31 @@
* - {@link #WIFI_MODE_WIFI_ONLY}
* - {@link #WIFI_MODE_CELLULAR_PREFERRED}
* - {@link #WIFI_MODE_WIFI_PREFERRED}
+ * @throws IllegalArgumentException if the subscription associated with this operation is not
+ * active (SIM is not inserted, ESIM inactive) or invalid.
* @see #setVoWiFiSettingEnabled(boolean)
*/
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
public void setVoWiFiNonPersistent(boolean isCapable, int mode) {
try {
getITelephony().setVoWiFiNonPersistent(mSubId, isCapable, mode);
- return;
+ } catch (ServiceSpecificException e) {
+ if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
+ // Rethrow as runtime error to keep API compatible.
+ throw new IllegalArgumentException(e.getMessage());
+ } else {
+ throw new RuntimeException(e.getMessage());
+ }
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
}
/**
+ * Returns the user's voice over WiFi Roaming mode setting associated with the device.
+ *
+ * @throws IllegalArgumentException if the subscription associated with this operation is not
+ * active (SIM is not inserted, ESIM inactive) or invalid.
* @return The Voice over WiFi Mode preference set by the user, which can be one of the
* following:
* - {@link #WIFI_MODE_WIFI_ONLY}
@@ -715,6 +810,13 @@
public @WiFiCallingMode int getVoWiFiModeSetting() {
try {
return getITelephony().getVoWiFiModeSetting(mSubId);
+ } catch (ServiceSpecificException e) {
+ if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
+ // Rethrow as runtime error to keep API compatible.
+ throw new IllegalArgumentException(e.getMessage());
+ } else {
+ throw new RuntimeException(e.getMessage());
+ }
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
@@ -727,13 +829,21 @@
* - {@link #WIFI_MODE_WIFI_ONLY}
* - {@link #WIFI_MODE_CELLULAR_PREFERRED}
* - {@link #WIFI_MODE_WIFI_PREFERRED}
+ * @throws IllegalArgumentException if the subscription associated with this operation is not
+ * active (SIM is not inserted, ESIM inactive) or invalid.
* @see #getVoWiFiModeSetting()
*/
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
public void setVoWiFiModeSetting(@WiFiCallingMode int mode) {
try {
getITelephony().setVoWiFiModeSetting(mSubId, mode);
- return;
+ } catch (ServiceSpecificException e) {
+ if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
+ // Rethrow as runtime error to keep API compatible.
+ throw new IllegalArgumentException(e.getMessage());
+ } else {
+ throw new RuntimeException(e.getMessage());
+ }
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
@@ -748,12 +858,21 @@
* - {@link #WIFI_MODE_WIFI_ONLY}
* - {@link #WIFI_MODE_CELLULAR_PREFERRED}
* - {@link #WIFI_MODE_WIFI_PREFERRED}
+ * @throws IllegalArgumentException if the subscription associated with this operation is not
+ * active (SIM is not inserted, ESIM inactive) or invalid.
* @see #setVoWiFiRoamingSettingEnabled(boolean)
*/
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public @WiFiCallingMode int getVoWiFiRoamingModeSetting() {
try {
return getITelephony().getVoWiFiRoamingModeSetting(mSubId);
+ } catch (ServiceSpecificException e) {
+ if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
+ // Rethrow as runtime error to keep API compatible.
+ throw new IllegalArgumentException(e.getMessage());
+ } else {
+ throw new RuntimeException(e.getMessage());
+ }
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
@@ -768,13 +887,21 @@
* - {@link #WIFI_MODE_WIFI_ONLY}
* - {@link #WIFI_MODE_CELLULAR_PREFERRED}
* - {@link #WIFI_MODE_WIFI_PREFERRED}
+ * @throws IllegalArgumentException if the subscription associated with this operation is not
+ * active (SIM is not inserted, ESIM inactive) or invalid.
* @see #getVoWiFiRoamingModeSetting()
*/
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
public void setVoWiFiRoamingModeSetting(@WiFiCallingMode int mode) {
try {
getITelephony().setVoWiFiRoamingModeSetting(mSubId, mode);
- return;
+ } catch (ServiceSpecificException e) {
+ if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
+ // Rethrow as runtime error to keep API compatible.
+ throw new IllegalArgumentException(e.getMessage());
+ } else {
+ throw new RuntimeException(e.getMessage());
+ }
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
@@ -787,13 +914,21 @@
* {@link android.provider.Settings.Secure#RTT_CALLING_MODE}, which is the global user setting
* for RTT. That value is enabled/disabled separately by the user through the Accessibility
* settings.
+ * @throws IllegalArgumentException if the subscription associated with this operation is not
+ * active (SIM is not inserted, ESIM inactive) or invalid.
* @param isEnabled if true RTT should be enabled during calls made on this subscription.
*/
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
public void setRttCapabilitySetting(boolean isEnabled) {
try {
getITelephony().setRttCapabilitySetting(mSubId, isEnabled);
- return;
+ } catch (ServiceSpecificException e) {
+ if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
+ // Rethrow as runtime error to keep API compatible.
+ throw new IllegalArgumentException(e.getMessage());
+ } else {
+ throw new RuntimeException(e.getMessage());
+ }
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
@@ -801,6 +936,9 @@
/**
* @return true if TTY over VoLTE is supported
+ *
+ * @throws IllegalArgumentException if the subscription associated with this operation is not
+ * active (SIM is not inserted, ESIM inactive) or invalid.
* @see android.telecom.TelecomManager#getCurrentTtyMode
* @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL
*/
@@ -808,6 +946,13 @@
boolean isTtyOverVolteEnabled() {
try {
return getITelephony().isTtyOverVolteEnabled(mSubId);
+ } catch (ServiceSpecificException e) {
+ if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
+ // Rethrow as runtime error to keep API compatible.
+ throw new IllegalArgumentException(e.getMessage());
+ } else {
+ throw new RuntimeException(e.getMessage());
+ }
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
diff --git a/telephony/java/android/telephony/ims/ImsRcsManager.java b/telephony/java/android/telephony/ims/ImsRcsManager.java
new file mode 100644
index 0000000..3c343dd
--- /dev/null
+++ b/telephony/java/android/telephony/ims/ImsRcsManager.java
@@ -0,0 +1,234 @@
+/*
+ * 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 android.telephony.ims;
+
+import android.Manifest;
+import android.annotation.CallbackExecutor;
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.content.Context;
+import android.os.Binder;
+import android.telephony.SubscriptionManager;
+import android.telephony.ims.aidl.IImsCapabilityCallback;
+import android.telephony.ims.feature.ImsFeature;
+import android.telephony.ims.feature.RcsFeature;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Manager for interfacing with the framework RCS services, including the User Capability Exchange
+ * (UCE) service, as well as managing user settings.
+ *
+ * Use {@link #createForSubscriptionId(Context, int)} to create an instance of this manager.
+ * @hide
+ */
+public class ImsRcsManager {
+
+ /**
+ * Receives RCS availability status updates from the ImsService.
+ *
+ * @see #isAvailable(int)
+ * @see #registerRcsAvailabilityCallback(Executor, AvailabilityCallback)
+ * @see #unregisterRcsAvailabilityCallback(AvailabilityCallback)
+ */
+ public static class AvailabilityCallback {
+
+ private static class CapabilityBinder extends IImsCapabilityCallback.Stub {
+
+ private final AvailabilityCallback mLocalCallback;
+ private Executor mExecutor;
+
+ CapabilityBinder(AvailabilityCallback c) {
+ mLocalCallback = c;
+ }
+
+ @Override
+ public void onCapabilitiesStatusChanged(int config) {
+ if (mLocalCallback == null) return;
+
+ Binder.withCleanCallingIdentity(() ->
+ mExecutor.execute(() -> mLocalCallback.onAvailabilityChanged(
+ new RcsFeature.RcsImsCapabilities(config))));
+ }
+
+ @Override
+ public void onQueryCapabilityConfiguration(int capability, int radioTech,
+ boolean isEnabled) {
+ // This is not used for public interfaces.
+ }
+
+ @Override
+ public void onChangeCapabilityConfigurationError(int capability, int radioTech,
+ @ImsFeature.ImsCapabilityError int reason) {
+ // This is not used for public interfaces
+ }
+
+ private void setExecutor(Executor executor) {
+ mExecutor = executor;
+ }
+ }
+
+ private final CapabilityBinder mBinder = new CapabilityBinder(this);
+
+ /**
+ * The availability of the feature's capabilities has changed to either available or
+ * unavailable.
+ * <p>
+ * If unavailable, the feature does not support the capability at the current time. This may
+ * be due to network or subscription provisioning changes, such as the IMS registration
+ * being lost, network type changing, or OMA-DM provisioning updates.
+ *
+ * @param capabilities The new availability of the capabilities.
+ */
+ public void onAvailabilityChanged(RcsFeature.RcsImsCapabilities capabilities) {
+ }
+
+ /**@hide*/
+ public final IImsCapabilityCallback getBinder() {
+ return mBinder;
+ }
+
+ private void setExecutor(Executor executor) {
+ mBinder.setExecutor(executor);
+ }
+ }
+
+ private final int mSubId;
+ private final Context mContext;
+
+
+ /**
+ * Create an instance of ImsRcsManager for the subscription id specified.
+ *
+ * @param context The context to create this ImsRcsManager instance within.
+ * @param subscriptionId The ID of the subscription that this ImsRcsManager will use.
+ * @see android.telephony.SubscriptionManager#getActiveSubscriptionInfoList()
+ * @throws IllegalArgumentException if the subscription is invalid.
+ * @hide
+ */
+ public static ImsRcsManager createForSubscriptionId(Context context, int subscriptionId) {
+ if (!SubscriptionManager.isValidSubscriptionId(subscriptionId)) {
+ throw new IllegalArgumentException("Invalid subscription ID");
+ }
+
+ return new ImsRcsManager(context, subscriptionId);
+ }
+
+ /**
+ * Use {@link #createForSubscriptionId(Context, int)} to create an instance of this class.
+ */
+ private ImsRcsManager(Context context, int subId) {
+ mContext = context;
+ mSubId = subId;
+ }
+
+ /**
+ * Registers an {@link AvailabilityCallback} with the system, which will provide RCS
+ * availability updates for the subscription specified.
+ *
+ * Use {@link SubscriptionManager.OnSubscriptionsChangedListener} to listen to
+ * subscription changed events and call
+ * {@link #unregisterRcsAvailabilityCallback(AvailabilityCallback)} to clean up after a
+ * subscription is removed.
+ * <p>
+ * When the callback is registered, it will initiate the callback c to be called with the
+ * current capabilities.
+ *
+ * @param executor The executor the callback events should be run on.
+ * @param c The RCS {@link AvailabilityCallback} to be registered.
+ * @see #unregisterRcsAvailabilityCallback(AvailabilityCallback)
+ * @throws ImsException if the subscription associated with this instance of
+ * {@link ImsRcsManager} is valid, but the ImsService associated with the subscription is not
+ * available. This can happen if the ImsService has crashed, for example, or if the subscription
+ * becomes inactive. See {@link ImsException#getCode()} for more information on the error codes.
+ */
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public void registerRcsAvailabilityCallback(@CallbackExecutor Executor executor,
+ @NonNull AvailabilityCallback c) throws ImsException {
+ if (c == null) {
+ throw new IllegalArgumentException("Must include a non-null AvailabilityCallback.");
+ }
+ if (executor == null) {
+ throw new IllegalArgumentException("Must include a non-null Executor.");
+ }
+ c.setExecutor(executor);
+ throw new UnsupportedOperationException("registerRcsAvailabilityCallback is not"
+ + "supported.");
+ }
+
+ /**
+ * Removes an existing RCS {@link AvailabilityCallback}.
+ * <p>
+ * When the subscription associated with this callback is removed (SIM removed, ESIM swap,
+ * etc...), this callback will automatically be unregistered. If this method is called for an
+ * inactive subscription, it will result in a no-op.
+ * @param c The RCS {@link AvailabilityCallback} to be removed.
+ * @see #registerRcsAvailabilityCallback(Executor, AvailabilityCallback)
+ */
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public void unregisterRcsAvailabilityCallback(@NonNull AvailabilityCallback c) {
+ if (c == null) {
+ throw new IllegalArgumentException("Must include a non-null AvailabilityCallback.");
+ }
+ throw new UnsupportedOperationException("unregisterRcsAvailabilityCallback is not"
+ + "supported.");
+ }
+
+ /**
+ * Query for the capability of an IMS RCS service provided by the framework.
+ * <p>
+ * This only reports the status of RCS capabilities provided by the framework, not necessarily
+ * RCS capabilities provided over-the-top by applications.
+ *
+ * @param capability The RCS capability to query.
+ * @return true if the RCS capability is capable for this subscription, false otherwise. This
+ * does not necessarily mean that we are registered for IMS and the capability is available, but
+ * rather the subscription is capable of this service over IMS.
+ * @see #isAvailable(int)
+ * @see android.telephony.CarrierConfigManager#KEY_USE_RCS_PRESENCE_BOOL
+ */
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public boolean isCapable(@RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability) {
+ throw new UnsupportedOperationException("isCapable is not supported.");
+ }
+
+ /**
+ * Query the availability of an IMS RCS capability.
+ * <p>
+ * This only reports the status of RCS capabilities provided by the framework, not necessarily
+ * RCS capabilities provided by over-the-top by applications.
+ *
+ * @param capability the RCS capability to query.
+ * @return true if the RCS capability is currently available for the associated subscription,
+ * false otherwise. If the capability is available, IMS is registered and the service is
+ * currently available over IMS.
+ * @see #isCapable(int)
+ */
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public boolean isAvailable(@RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability) {
+ throw new UnsupportedOperationException("isAvailable is not supported.");
+ }
+
+ /**
+ * @return A new {@link RcsUceAdapter} used for User Capability Exchange (UCE) operations for
+ * this subscription.
+ */
+ @NonNull
+ public RcsUceAdapter getUceAdapter() {
+ return new RcsUceAdapter(mSubId);
+ }
+}
diff --git a/telephony/java/android/telephony/ims/ProvisioningManager.java b/telephony/java/android/telephony/ims/ProvisioningManager.java
index cc037e3..effdf48 100644
--- a/telephony/java/android/telephony/ims/ProvisioningManager.java
+++ b/telephony/java/android/telephony/ims/ProvisioningManager.java
@@ -387,6 +387,24 @@
}
}
+ /**
+ * Notify the framework that an RCS autoconfiguration XML file has been received for
+ * provisioning.
+ * @param config The XML file to be read. ASCII/UTF8 encoded text if not compressed.
+ * @param isCompressed The XML file is compressed in gzip format and must be decompressed
+ * before being read.
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ public void notifyRcsAutoConfigurationReceived(@NonNull byte[] config, boolean isCompressed) {
+ if (config == null) {
+ throw new IllegalArgumentException("Must include a non-null config XML file.");
+ }
+ // TODO: Connect to ImsConfigImplBase.
+ throw new UnsupportedOperationException("notifyRcsAutoConfigurationReceived is not"
+ + "supported");
+ }
+
private static boolean isImsAvailableOnDevice() {
IPackageManager pm = IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
if (pm == null) {
diff --git a/tools/preload2/src/com/android/preload/classdataretrieval/ClassDataRetriever.java b/telephony/java/android/telephony/ims/RcsContactUceCapability.aidl
similarity index 61%
rename from tools/preload2/src/com/android/preload/classdataretrieval/ClassDataRetriever.java
rename to telephony/java/android/telephony/ims/RcsContactUceCapability.aidl
index f04360f..bef6a40 100644
--- a/tools/preload2/src/com/android/preload/classdataretrieval/ClassDataRetriever.java
+++ b/telephony/java/android/telephony/ims/RcsContactUceCapability.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Android Open Source Project
+ * 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.
@@ -14,16 +14,7 @@
* limitations under the License.
*/
-package com.android.preload.classdataretrieval;
-import com.android.ddmlib.Client;
+package android.telephony.ims;
-import java.util.Map;
-
-/**
- * Retrieve a class-to-classloader map for loaded classes from the client.
- */
-public interface ClassDataRetriever {
-
- public Map<String, String> getClassData(Client client);
-}
+parcelable RcsContactUceCapability;
diff --git a/telephony/java/android/telephony/ims/RcsContactUceCapability.java b/telephony/java/android/telephony/ims/RcsContactUceCapability.java
new file mode 100644
index 0000000..492170b
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsContactUceCapability.java
@@ -0,0 +1,291 @@
+/*
+ * 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 android.telephony.ims;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Contains the User Capability Exchange capabilities corresponding to a contact's URI.
+ * @hide
+ */
+public final class RcsContactUceCapability implements Parcelable {
+
+ /** Supports 1-to-1 chat */
+ public static final int CAPABILITY_CHAT_STANDALONE = (1 << 0);
+ /** Supports group chat */
+ public static final int CAPABILITY_CHAT_SESSION = (1 << 1);
+ /** Supports full store and forward group chat information. */
+ public static final int CAPABILITY_CHAT_SESSION_STORE_FORWARD = (1 << 2);
+ /**
+ * Supports file transfer via Message Session Relay Protocol (MSRP) without Store and Forward.
+ */
+ public static final int CAPABILITY_FILE_TRANSFER = (1 << 3);
+ /** Supports File Transfer Thumbnail */
+ public static final int CAPABILITY_FILE_TRANSFER_THUMBNAIL = (1 << 4);
+ /** Supports File Transfer with Store and Forward */
+ public static final int CAPABILITY_FILE_TRANSFER_STORE_FORWARD = (1 << 5);
+ /** Supports File Transfer via HTTP */
+ public static final int CAPABILITY_FILE_TRANSFER_HTTP = (1 << 6);
+ /** Supports file transfer via SMS */
+ public static final int CAPABILITY_FILE_TRANSFER_SMS = (1 << 7);
+ /** Supports image sharing */
+ public static final int CAPABILITY_IMAGE_SHARE = (1 << 8);
+ /** Supports video sharing during a circuit-switch call (IR.74)*/
+ public static final int CAPABILITY_VIDEO_SHARE_DURING_CS_CALL = (1 << 9);
+ /** Supports video share outside of voice call (IR.84) */
+ public static final int CAPABILITY_VIDEO_SHARE = (1 << 10);
+ /** Supports social presence information */
+ public static final int CAPABILITY_SOCIAL_PRESENCE = (1 << 11);
+ /** Supports capability discovery via presence */
+ public static final int CAPABILITY_DISCOVERY_VIA_PRESENCE = (1 << 12);
+ /** Supports IP Voice calling over LTE or IWLAN (IR.92/IR.51) */
+ public static final int CAPABILITY_IP_VOICE_CALL = (1 << 13);
+ /** Supports IP video calling (IR.94) */
+ public static final int CAPABILITY_IP_VIDEO_CALL = (1 << 14);
+ /** Supports Geolocation PUSH during 1-to-1 or multiparty chat */
+ public static final int CAPABILITY_GEOLOCATION_PUSH = (1 << 15);
+ /** Supports Geolocation PUSH via SMS for fallback. */
+ public static final int CAPABILITY_GEOLOCATION_PUSH_SMS = (1 << 16);
+ /** Supports Geolocation pull. */
+ public static final int CAPABILITY_GEOLOCATION_PULL = (1 << 17);
+ /** Supports Geolocation pull using file transfer support. */
+ public static final int CAPABILITY_GEOLOCATION_PULL_FILE_TRANSFER = (1 << 18);
+ /** Supports RCS voice calling */
+ public static final int CAPABILITY_RCS_VOICE_CALL = (1 << 19);
+ /** Supports RCS video calling */
+ public static final int CAPABILITY_RCS_VIDEO_CALL = (1 << 20);
+ /** Supports RCS video calling, where video media can not be dropped */
+ public static final int CAPABILITY_RCS_VIDEO_ONLY_CALL = (1 << 21);
+
+ /** @hide*/
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = "CAPABILITY_", flag = true, value = {
+ CAPABILITY_CHAT_STANDALONE,
+ CAPABILITY_CHAT_SESSION,
+ CAPABILITY_CHAT_SESSION_STORE_FORWARD,
+ CAPABILITY_FILE_TRANSFER,
+ CAPABILITY_FILE_TRANSFER_THUMBNAIL,
+ CAPABILITY_FILE_TRANSFER_STORE_FORWARD,
+ CAPABILITY_FILE_TRANSFER_HTTP,
+ CAPABILITY_FILE_TRANSFER_SMS,
+ CAPABILITY_IMAGE_SHARE,
+ CAPABILITY_VIDEO_SHARE_DURING_CS_CALL,
+ CAPABILITY_VIDEO_SHARE,
+ CAPABILITY_SOCIAL_PRESENCE,
+ CAPABILITY_DISCOVERY_VIA_PRESENCE,
+ CAPABILITY_IP_VOICE_CALL,
+ CAPABILITY_IP_VIDEO_CALL,
+ CAPABILITY_GEOLOCATION_PUSH,
+ CAPABILITY_GEOLOCATION_PUSH_SMS,
+ CAPABILITY_GEOLOCATION_PULL,
+ CAPABILITY_GEOLOCATION_PULL_FILE_TRANSFER,
+ CAPABILITY_RCS_VOICE_CALL,
+ CAPABILITY_RCS_VIDEO_CALL,
+ CAPABILITY_RCS_VIDEO_ONLY_CALL
+ })
+ public @interface CapabilityFlag {}
+
+ /**
+ * Builder to help construct {@link RcsContactUceCapability} instances.
+ */
+ public static class Builder {
+
+ private final RcsContactUceCapability mCapabilities;
+
+ /**
+ * Create the Builder, which can be used to set UCE capabilities as well as custom
+ * capability extensions.
+ * @param contact The contact URI that the capabilities are attached to.
+ */
+ public Builder(@NonNull Uri contact) {
+ mCapabilities = new RcsContactUceCapability(contact);
+ }
+
+ /**
+ * Add a UCE capability bit-field as well as the associated URI that the framework should
+ * use for those services. This is mainly used for capabilities that may use a URI separate
+ * from the contact's URI, for example the URI to use for VT calls.
+ * @param type The capability to map to a service URI that is different from the contact's
+ * URI.
+ */
+ public Builder add(@CapabilityFlag int type, @NonNull Uri serviceUri) {
+ mCapabilities.mCapabilities |= type;
+ // Put each of these capabilities into the map separately.
+ for (int shift = 0; shift < Integer.SIZE; shift++) {
+ int cap = type & (1 << shift);
+ if (cap != 0) {
+ mCapabilities.mServiceMap.put(cap, serviceUri);
+ // remove that capability from the field.
+ type &= ~cap;
+ }
+ if (type == 0) {
+ // no need to keep going, end early.
+ break;
+ }
+ }
+ return this;
+ }
+
+ /**
+ * Add a UCE capability flag that this contact supports.
+ * @param type the capability that the contact supports.
+ */
+ public Builder add(@CapabilityFlag int type) {
+ mCapabilities.mCapabilities |= type;
+ return this;
+ }
+
+ /**
+ * Add a carrier specific service tag.
+ * @param extension A string containing a carrier specific service tag that is an extension
+ * of the {@link CapabilityFlag}s that are defined here.
+ */
+ public Builder add(@NonNull String extension) {
+ mCapabilities.mExtensionTags.add(extension);
+ return this;
+ }
+
+ /**
+ * @return the constructed instance.
+ */
+ public RcsContactUceCapability build() {
+ return mCapabilities;
+ }
+ }
+
+ private final Uri mContactUri;
+ private int mCapabilities;
+ private List<String> mExtensionTags = new ArrayList<>();
+ private Map<Integer, Uri> mServiceMap = new HashMap<>();
+
+ /**
+ * Use {@link Builder} to build an instance of this interface.
+ * @param contact The URI associated with this capability information.
+ * @hide
+ */
+ RcsContactUceCapability(@NonNull Uri contact) {
+ mContactUri = contact;
+ }
+
+ private RcsContactUceCapability(Parcel in) {
+ mContactUri = in.readParcelable(Uri.class.getClassLoader());
+ mCapabilities = in.readInt();
+ in.readStringList(mExtensionTags);
+ // read mServiceMap as key,value pair
+ int mapSize = in.readInt();
+ for (int i = 0; i < mapSize; i++) {
+ mServiceMap.put(in.readInt(), in.readParcelable(Uri.class.getClassLoader()));
+ }
+ }
+
+ public static final Creator<RcsContactUceCapability> CREATOR =
+ new Creator<RcsContactUceCapability>() {
+ @Override
+ public RcsContactUceCapability createFromParcel(Parcel in) {
+ return new RcsContactUceCapability(in);
+ }
+
+ @Override
+ public RcsContactUceCapability[] newArray(int size) {
+ return new RcsContactUceCapability[size];
+ }
+ };
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeParcelable(mContactUri, 0);
+ out.writeInt(mCapabilities);
+ out.writeStringList(mExtensionTags);
+ // write mServiceMap as key,value pairs
+ int mapSize = mServiceMap.keySet().size();
+ out.writeInt(mapSize);
+ for (int key : mServiceMap.keySet()) {
+ out.writeInt(key);
+ out.writeParcelable(mServiceMap.get(key), 0);
+ }
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * Query for a capability
+ * @param type The capability flag to query.
+ * @return true if the capability flag specified is set, false otherwise.
+ */
+ public boolean isCapable(@CapabilityFlag int type) {
+ return (mCapabilities & type) > 0;
+ }
+
+ /**
+ * @return true if the extension service tag is set, false otherwise.
+ */
+ public boolean isCapable(@NonNull String extensionTag) {
+ return mExtensionTags.contains(extensionTag);
+ }
+
+ /**
+ * @return An immutable list containing all of the extension tags that have been set as capable.
+ * @throws UnsupportedOperationException if this list is modified.
+ */
+ public @NonNull List<String> getCapableExtensionTags() {
+ return Collections.unmodifiableList(mExtensionTags);
+ }
+
+ /**
+ * Retrieves the {@link Uri} associated with the capability being queried.
+ * <p>
+ * This will typically be the contact {@link Uri} available via {@link #getContactUri()} unless
+ * a different service {@link Uri} was associated with this capability using
+ * {@link Builder#add(int, Uri)}.
+ *
+ * @return a String containing the {@link Uri} associated with the service tag or
+ * {@code null} if this capability is not set as capable.
+ * @see #isCapable(int)
+ */
+ public @Nullable Uri getServiceUri(@CapabilityFlag int type) {
+ Uri result = mServiceMap.getOrDefault(type, null);
+ // If the capability is capable, but does not have a service URI associated, use the default
+ // contact URI.
+ if (result == null) {
+ return isCapable(type) ? getContactUri() : null;
+ }
+ return result;
+ }
+
+ /**
+ * @return the URI representing the contact associated with the capabilities.
+ */
+ public @NonNull Uri getContactUri() {
+ return mContactUri;
+ }
+}
diff --git a/telephony/java/android/telephony/ims/RcsUceAdapter.java b/telephony/java/android/telephony/ims/RcsUceAdapter.java
new file mode 100644
index 0000000..a6a7a84
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsUceAdapter.java
@@ -0,0 +1,276 @@
+/*
+ * 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 android.telephony.ims;
+
+import android.Manifest;
+import android.annotation.CallbackExecutor;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.content.Context;
+import android.net.Uri;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.List;
+import java.util.concurrent.Executor;
+
+/**
+ * Manages RCS User Capability Exchange for the subscription specified.
+ *
+ * @see ImsRcsManager#getUceAdapter() for information on creating an instance of this class.
+ * @hide
+ */
+public class RcsUceAdapter {
+
+ /**
+ * An unknown error has caused the request to fail.
+ */
+ public static final int ERROR_GENERIC_FAILURE = 1;
+ /**
+ * The carrier network does not have UCE support enabled for this subscriber.
+ */
+ public static final int ERROR_NOT_ENABLED = 2;
+ /**
+ * The data network that the device is connected to does not support UCE currently (e.g. it is
+ * 1x only currently).
+ */
+ public static final int ERROR_NOT_AVAILABLE = 3;
+ /**
+ * The network has responded with SIP 403 error and a reason "User not registered."
+ */
+ public static final int ERROR_NOT_REGISTERED = 4;
+ /**
+ * The network has responded to this request with a SIP 403 error and reason "not authorized for
+ * presence" for this subscriber.
+ */
+ public static final int ERROR_NOT_AUTHORIZED = 5;
+ /**
+ * The network has responded to this request with a SIP 403 error and no reason.
+ */
+ public static final int ERROR_FORBIDDEN = 6;
+ /**
+ * The contact URI requested is not provisioned for VoLTE or it is not known as an IMS
+ * subscriber to the carrier network.
+ */
+ public static final int ERROR_NOT_FOUND = 7;
+ /**
+ * The capabilities request contained too many URIs for the carrier network to handle. Retry
+ * with a lower number of contact numbers. The number varies per carrier.
+ */
+ // TODO: Try to integrate this into the API so that the service will split based on carrier.
+ public static final int ERROR_REQUEST_TOO_LARGE = 8;
+ /**
+ * The network did not respond to the capabilities request before the request timed out.
+ */
+ public static final int ERROR_REQUEST_TIMEOUT = 10;
+ /**
+ * The request failed due to the service having insufficient memory.
+ */
+ public static final int ERROR_INSUFFICIENT_MEMORY = 11;
+ /**
+ * The network was lost while trying to complete the request.
+ */
+ public static final int ERROR_LOST_NETWORK = 12;
+ /**
+ * The request has failed because the same request has already been added to the queue.
+ */
+ public static final int ERROR_ALREADY_IN_QUEUE = 13;
+
+ /**@hide*/
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = "ERROR_", value = {
+ ERROR_GENERIC_FAILURE,
+ ERROR_NOT_ENABLED,
+ ERROR_NOT_AVAILABLE,
+ ERROR_NOT_REGISTERED,
+ ERROR_NOT_AUTHORIZED,
+ ERROR_FORBIDDEN,
+ ERROR_NOT_FOUND,
+ ERROR_REQUEST_TOO_LARGE,
+ ERROR_REQUEST_TIMEOUT,
+ ERROR_INSUFFICIENT_MEMORY,
+ ERROR_LOST_NETWORK,
+ ERROR_ALREADY_IN_QUEUE
+ })
+ public @interface ErrorCode {}
+
+ /**
+ * The last publish has resulted in a "200 OK" response or the device is using SIP OPTIONS for
+ * UCE.
+ */
+ public static final int PUBLISH_STATE_200_OK = 1;
+
+ /**
+ * The hasn't published its capabilities since boot or hasn't gotten any publish response yet.
+ */
+ public static final int PUBLISH_STATE_NOT_PUBLISHED = 2;
+
+ /**
+ * The device has tried to publish its capabilities, which has resulted in an error. This error
+ * is related to the fact that the device is not VoLTE provisioned.
+ */
+ public static final int PUBLISH_STATE_VOLTE_PROVISION_ERROR = 3;
+
+ /**
+ * The device has tried to publish its capabilities, which has resulted in an error. This error
+ * is related to the fact that the device is not RCS or UCE provisioned.
+ */
+ public static final int PUBLISH_STATE_RCS_PROVISION_ERROR = 4;
+
+ /**
+ * The last publish resulted in a "408 Request Timeout" response.
+ */
+ public static final int PUBLISH_STATE_REQUEST_TIMEOUT = 5;
+
+ /**
+ * The last publish resulted in another unknown error, such as SIP 503 - "Service Unavailable"
+ * or SIP 423 - "Interval too short".
+ * <p>
+ * Device shall retry with exponential back-off.
+ */
+ public static final int PUBLISH_STATE_OTHER_ERROR = 6;
+
+ /**@hide*/
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = "PUBLISH_STATE_", value = {
+ PUBLISH_STATE_200_OK,
+ PUBLISH_STATE_NOT_PUBLISHED,
+ PUBLISH_STATE_VOLTE_PROVISION_ERROR,
+ PUBLISH_STATE_RCS_PROVISION_ERROR,
+ PUBLISH_STATE_REQUEST_TIMEOUT,
+ PUBLISH_STATE_OTHER_ERROR
+ })
+ public @interface PublishState {}
+
+
+ /**
+ * Provides a one-time callback for the response to a UCE request. After this callback is called
+ * by the framework, the reference to this callback will be discarded on the service side.
+ * @see #requestCapabilities(Executor, List, CapabilitiesCallback)
+ */
+ public static class CapabilitiesCallback {
+
+ /**
+ * Notify this application that the pending capability request has returned successfully.
+ * @param contactCapabilities List of capabilities associated with each contact requested.
+ */
+ public void onCapabilitiesReceived(
+ @NonNull List<RcsContactUceCapability> contactCapabilities) {
+
+ }
+
+ /**
+ * The pending request has resulted in an error and may need to be retried, depending on the
+ * error code.
+ * @param errorCode The reason for the framework being unable to process the request.
+ */
+ public void onError(@ErrorCode int errorCode) {
+
+ }
+ }
+
+ private final int mSubId;
+
+ /**
+ * Not to be instantiated directly, use
+ * {@link ImsRcsManager#createForSubscriptionId(Context, int)} and
+ * {@link ImsRcsManager#getUceAdapter()} to instantiate this manager class.
+ */
+ RcsUceAdapter(int subId) {
+ mSubId = subId;
+ }
+
+ /**
+ * Request the User Capability Exchange capabilities for one or more contacts.
+ * <p>
+ * Be sure to check the availability of this feature using
+ * {@link ImsRcsManager#isAvailable(int)} and ensuring
+ * {@link RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_OPTIONS_UCE} or
+ * {@link RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_PRESENCE_UCE} is enabled or else
+ * this operation will fail with {@link #ERROR_NOT_AVAILABLE} or {@link #ERROR_NOT_ENABLED}.
+ *
+ * @param executor The executor that will be used when the request is completed and the
+ * {@link CapabilitiesCallback} is called.
+ * @param contactNumbers A list of numbers that the capabilities are being requested for.
+ * @param c A one-time callback for when the request for capabilities completes or there is an
+ * error processing the request.
+ * @throws ImsException if the subscription associated with this instance of
+ * {@link RcsUceAdapter} is valid, but the ImsService associated with the subscription is not
+ * available. This can happen if the ImsService has crashed, for example, or if the subscription
+ * becomes inactive. See {@link ImsException#getCode()} for more information on the error codes.
+ */
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public void requestCapabilities(@CallbackExecutor Executor executor,
+ @NonNull List<Uri> contactNumbers,
+ @NonNull CapabilitiesCallback c) throws ImsException {
+ throw new UnsupportedOperationException("isUceSettingEnabled is not supported.");
+ }
+
+ /**
+ * Gets the last publish result from the UCE service if the device is using an RCS presence
+ * server.
+ * @return The last publish result from the UCE service. If the device is using SIP OPTIONS,
+ * this method will return {@link #PUBLISH_STATE_200_OK} as well.
+ * @throws ImsException if the subscription associated with this instance of
+ * {@link RcsUceAdapter} is valid, but the ImsService associated with the subscription is not
+ * available. This can happen if the ImsService has crashed, for example, or if the subscription
+ * becomes inactive. See {@link ImsException#getCode()} for more information on the error codes.
+ */
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public @PublishState int getUcePublishState() throws ImsException {
+ throw new UnsupportedOperationException("getPublishState is not supported.");
+ }
+
+ /**
+ * The user’s setting for whether or not Presence and User Capability Exchange (UCE) is enabled
+ * for the associated subscription.
+ *
+ * @return true if the user’s setting for UCE is enabled, false otherwise. If false,
+ * {@link ImsRcsManager#isCapable(int)} will return false for
+ * {@link RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_OPTIONS_UCE} and
+ * {@link RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_PRESENCE_UCE}
+ * @see #setUceSettingEnabled(boolean)
+ * @throws ImsException if the subscription associated with this instance of
+ * {@link RcsUceAdapter} is valid, but the ImsService associated with the subscription is not
+ * available. This can happen if the ImsService has crashed, for example, or if the subscription
+ * becomes inactive. See {@link ImsException#getCode()} for more information on the error codes.
+ */
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public boolean isUceSettingEnabled() throws ImsException {
+ // TODO: add SubscriptionController column for this property.
+ throw new UnsupportedOperationException("isUceSettingEnabled is not supported.");
+ }
+ /**
+ * Change the user’s setting for whether or not UCE is enabled for the associated subscription.
+ * @param isEnabled the user's setting for whether or not they wish for Presence and User
+ * Capability Exchange to be enabled. If false,
+ * {@link RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_OPTIONS_UCE} and
+ * {@link RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_PRESENCE_UCE} capability will be
+ * disabled, depending on which type of UCE the carrier supports.
+ * @see #isUceSettingEnabled()
+ * @throws ImsException if the subscription associated with this instance of
+ * {@link RcsUceAdapter} is valid, but the ImsService associated with the subscription is not
+ * available. This can happen if the ImsService has crashed, for example, or if the subscription
+ * becomes inactive. See {@link ImsException#getCode()} for more information on the error codes.
+ */
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ public void setUceSettingEnabled(boolean isEnabled) throws ImsException {
+ // TODO: add SubscriptionController column for this property.
+ throw new UnsupportedOperationException("setUceSettingEnabled is not supported.");
+ }
+}
diff --git a/telephony/java/android/telephony/ims/aidl/IImsConfig.aidl b/telephony/java/android/telephony/ims/aidl/IImsConfig.aidl
index 4433c1c..53e4596 100644
--- a/telephony/java/android/telephony/ims/aidl/IImsConfig.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IImsConfig.aidl
@@ -17,6 +17,8 @@
package android.telephony.ims.aidl;
+import android.os.PersistableBundle;
+
import android.telephony.ims.aidl.IImsConfigCallback;
import com.android.ims.ImsConfigListener;
@@ -37,4 +39,5 @@
int setConfigInt(int item, int value);
// Return result code defined in ImsConfig#OperationStatusConstants
int setConfigString(int item, String value);
+ void updateImsCarrierConfigs(in PersistableBundle bundle);
}
diff --git a/telephony/java/android/telephony/ims/aidl/IImsRcsFeature.aidl b/telephony/java/android/telephony/ims/aidl/IImsRcsFeature.aidl
index 691cfba..4b98b79 100644
--- a/telephony/java/android/telephony/ims/aidl/IImsRcsFeature.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IImsRcsFeature.aidl
@@ -16,10 +16,38 @@
package android.telephony.ims.aidl;
+import android.net.Uri;
+import android.telephony.ims.RcsContactUceCapability;
+import android.telephony.ims.aidl.IImsCapabilityCallback;
+import android.telephony.ims.aidl.IRcsFeatureListener;
+import android.telephony.ims.feature.CapabilityChangeRequest;
+
+import java.util.List;
+
/**
* See RcsFeature for more information.
* {@hide}
*/
interface IImsRcsFeature {
- //Empty Default Implementation
+ // Not oneway because we need to verify this completes before doing anything else.
+ void setListener(IRcsFeatureListener listener);
+ int queryCapabilityStatus();
+ // Inherited from ImsFeature
+ int getFeatureState();
+ oneway void addCapabilityCallback(IImsCapabilityCallback c);
+ oneway void removeCapabilityCallback(IImsCapabilityCallback c);
+ oneway void changeCapabilitiesConfiguration(in CapabilityChangeRequest r,
+ IImsCapabilityCallback c);
+ oneway void queryCapabilityConfiguration(int capability, int radioTech,
+ IImsCapabilityCallback c);
+ // RcsPresenceExchangeImplBase specific api
+ oneway void requestCapabilities(in List<Uri> uris, int operationToken);
+ oneway void updateCapabilities(in RcsContactUceCapability capabilities, int operationToken);
+ // RcsSipOptionsImplBase specific api
+ oneway void sendCapabilityRequest(in Uri contactUri,
+ in RcsContactUceCapability capabilities, int operationToken);
+ oneway void respondToCapabilityRequest(in String contactUri,
+ in RcsContactUceCapability ownCapabilities, int operationToken);
+ oneway void respondToCapabilityRequestWithError(in Uri contactUri, int code, in String reason,
+ int operationToken);
}
\ No newline at end of file
diff --git a/telephony/java/android/telephony/ims/aidl/IRcsFeatureListener.aidl b/telephony/java/android/telephony/ims/aidl/IRcsFeatureListener.aidl
new file mode 100644
index 0000000..881b477
--- /dev/null
+++ b/telephony/java/android/telephony/ims/aidl/IRcsFeatureListener.aidl
@@ -0,0 +1,42 @@
+/*
+ * 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 android.telephony.ims.aidl;
+
+import android.net.Uri;
+import android.telephony.ims.RcsContactUceCapability;
+
+import java.util.List;
+
+/**
+ * Listener interface for updates from the RcsFeature back to the framework.
+ * {@hide}
+ */
+interface IRcsFeatureListener {
+ //RcsCapabilityExchange specific
+ oneway void onCommandUpdate(int commandCode, int operationToken);
+ // RcsPresenceExchangeImplBase Specific
+ oneway void onNetworkResponse(int code, in String reason, int operationToken);
+ oneway void onCapabilityRequestResponsePresence(in List<RcsContactUceCapability> infos,
+ int operationToken);
+ oneway void onNotifyUpdateCapabilities();
+ oneway void onUnpublish();
+ // RcsSipOptionsImplBase specific
+ oneway void onCapabilityRequestResponseOptions(int code, in String reason,
+ in RcsContactUceCapability info, int operationToken);
+ oneway void onRemoteCapabilityRequest(in Uri contactUri, in RcsContactUceCapability remoteInfo,
+ int operationToken);
+}
\ No newline at end of file
diff --git a/telephony/java/android/telephony/ims/feature/ImsFeature.java b/telephony/java/android/telephony/ims/feature/ImsFeature.java
index 74af6bf..3a9979d 100644
--- a/telephony/java/android/telephony/ims/feature/ImsFeature.java
+++ b/telephony/java/android/telephony/ims/feature/ImsFeature.java
@@ -33,10 +33,8 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.Set;
-import java.util.WeakHashMap;
+import java.util.HashMap;
+import java.util.Map;
/**
* Base class for all IMS features that are supported by the framework. Use a concrete subclass
@@ -50,35 +48,6 @@
private static final String LOG_TAG = "ImsFeature";
/**
- * Action to broadcast when ImsService is up.
- * Internal use only.
- * Only defined here separately for compatibility purposes with the old ImsService.
- *
- * @hide
- */
- public static final String ACTION_IMS_SERVICE_UP =
- "com.android.ims.IMS_SERVICE_UP";
-
- /**
- * Action to broadcast when ImsService is down.
- * Internal use only.
- * Only defined here separately for compatibility purposes with the old ImsService.
- *
- * @hide
- */
- public static final String ACTION_IMS_SERVICE_DOWN =
- "com.android.ims.IMS_SERVICE_DOWN";
-
- /**
- * Part of the ACTION_IMS_SERVICE_UP or _DOWN intents.
- * A long value; the phone ID corresponding to the IMS service coming up or down.
- * Only defined here separately for compatibility purposes with the old ImsService.
- *
- * @hide
- */
- public static final String EXTRA_PHONE_ID = "android:phone_id";
-
- /**
* Invalid feature value
* @hide
*/
@@ -106,6 +75,16 @@
public static final int FEATURE_MAX = 3;
/**
+ * Used for logging purposes.
+ * @hide
+ */
+ public static final Map<Integer, String> FEATURE_LOG_MAP = new HashMap<Integer, String>() {{
+ put(FEATURE_EMERGENCY_MMTEL, "EMERGENCY_MMTEL");
+ put(FEATURE_MMTEL, "MMTEL");
+ put(FEATURE_RCS, "RCS");
+ }};
+
+ /**
* Integer values defining IMS features that are supported in ImsFeature.
* @hide
*/
@@ -132,19 +111,34 @@
public @interface ImsState {}
/**
- * This {@link ImsFeature}'s state is unavailable and should not be communicated with.
+ * This {@link ImsFeature}'s state is unavailable and should not be communicated with. This will
+ * remove all bindings back to the framework. Any attempt to communicate with the framework
+ * during this time will result in an {@link IllegalStateException}.
*/
public static final int STATE_UNAVAILABLE = 0;
/**
- * This {@link ImsFeature} state is initializing and should not be communicated with.
+ * This {@link ImsFeature} state is initializing and should not be communicated with. This will
+ * remove all bindings back to the framework. Any attempt to communicate with the framework
+ * during this time will result in an {@link IllegalStateException}.
*/
public static final int STATE_INITIALIZING = 1;
/**
- * This {@link ImsFeature} is ready for communication.
+ * This {@link ImsFeature} is ready for communication. Do not attempt to call framework methods
+ * until {@link #onFeatureReady()} is called.
*/
public static final int STATE_READY = 2;
/**
+ * Used for logging purposes.
+ * @hide
+ */
+ public static final Map<Integer, String> STATE_LOG_MAP = new HashMap<Integer, String>() {{
+ put(STATE_UNAVAILABLE, "UNAVAILABLE");
+ put(STATE_INITIALIZING, "INITIALIZING");
+ put(STATE_READY, "READY");
+ }};
+
+ /**
* Integer values defining the result codes that should be returned from
* {@link #changeEnabledCapabilities} when the framework tries to set a feature's capability.
* @hide
@@ -208,11 +202,14 @@
/**
* Contains the capabilities defined and supported by an ImsFeature in the form of a bit mask.
+ * <p>
+ * Typically this class is not used directly, but rather extended in subclasses of
+ * {@link ImsFeature} to provide service specific capabilities.
* @hide
- * @deprecated Use {@link MmTelFeature.MmTelCapabilities} instead.
*/
- @SystemApi // SystemApi only because it was leaked through type usage in a previous release.
+ @SystemApi
public static class Capabilities {
+ /** @deprecated Use getters and accessors instead. */
protected int mCapabilities = 0;
/**
@@ -305,12 +302,12 @@
/** @hide */
protected final Object mLock = new Object();
- private final Set<IImsFeatureStatusCallback> mStatusCallbacks = Collections.newSetFromMap(
- new WeakHashMap<IImsFeatureStatusCallback, Boolean>());
+ private final RemoteCallbackList<IImsFeatureStatusCallback> mStatusCallbacks =
+ new RemoteCallbackList<>();
private @ImsState int mState = STATE_UNAVAILABLE;
private int mSlotId = SubscriptionManager.INVALID_SIM_SLOT_INDEX;
- private final RemoteCallbackList<IImsCapabilityCallback> mCapabilityCallbacks
- = new RemoteCallbackList<>();
+ private final RemoteCallbackList<IImsCapabilityCallback> mCapabilityCallbacks =
+ new RemoteCallbackList<>();
private Capabilities mCapabilityStatus = new Capabilities();
/**
@@ -322,6 +319,16 @@
}
/**
+ * @return The SIM slot index associated with this ImsFeature.
+ *
+ * @see SubscriptionManager#getSubscriptionIds(int) for more information on getting the
+ * subscription IDs associated with this slot.
+ */
+ public final int getSlotIndex() {
+ return mSlotId;
+ }
+
+ /**
* @return The current state of the feature, defined as {@link #STATE_UNAVAILABLE},
* {@link #STATE_INITIALIZING}, or {@link #STATE_READY}.
* @hide
@@ -357,9 +364,7 @@
// If we have just connected, send queued status.
c.notifyImsFeatureStatus(getFeatureState());
// Add the callback if the callback completes successfully without a RemoteException.
- synchronized (mLock) {
- mStatusCallbacks.add(c);
- }
+ mStatusCallbacks.register(c);
} catch (RemoteException e) {
Log.w(LOG_TAG, "Couldn't notify feature state: " + e.getMessage());
}
@@ -371,29 +376,21 @@
*/
@VisibleForTesting
public void removeImsFeatureStatusCallback(@NonNull IImsFeatureStatusCallback c) {
- synchronized (mLock) {
- mStatusCallbacks.remove(c);
- }
+ mStatusCallbacks.unregister(c);
}
/**
* Internal method called by ImsFeature when setFeatureState has changed.
*/
private void notifyFeatureState(@ImsState int state) {
- synchronized (mLock) {
- for (Iterator<IImsFeatureStatusCallback> iter = mStatusCallbacks.iterator();
- iter.hasNext(); ) {
- IImsFeatureStatusCallback callback = iter.next();
- try {
- Log.i(LOG_TAG, "notifying ImsFeatureState=" + state);
- callback.notifyImsFeatureStatus(state);
- } catch (RemoteException e) {
- // remove if the callback is no longer alive.
- iter.remove();
- Log.w(LOG_TAG, "Couldn't notify feature state: " + e.getMessage());
- }
+ mStatusCallbacks.broadcast((c) -> {
+ try {
+ c.notifyImsFeatureStatus(state);
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, e + " notifyFeatureState() - Skipping "
+ + "callback.");
}
- }
+ });
}
/**
@@ -412,10 +409,23 @@
/**
* @hide
*/
- public final void removeCapabilityCallback(IImsCapabilityCallback c) {
+ final void removeCapabilityCallback(IImsCapabilityCallback c) {
mCapabilityCallbacks.unregister(c);
}
+ /**@hide*/
+ final void queryCapabilityConfigurationInternal(int capability, int radioTech,
+ IImsCapabilityCallback c) {
+ boolean enabled = queryCapabilityConfiguration(capability, radioTech);
+ try {
+ if (c != null) {
+ c.onQueryCapabilityConfiguration(capability, radioTech, enabled);
+ }
+ } catch (RemoteException e) {
+ Log.e(LOG_TAG, "queryCapabilityConfigurationInternal called on dead binder!");
+ }
+ }
+
/**
* @return the cached capabilities status for this feature.
* @hide
@@ -444,31 +454,36 @@
/**
* Called by the ImsFeature when the capabilities status has changed.
*
- * @param c A {@link Capabilities} containing the new Capabilities status.
+ * @param caps the new {@link Capabilities} status of the {@link ImsFeature}.
*
* @hide
*/
- protected final void notifyCapabilitiesStatusChanged(Capabilities c) {
+ protected final void notifyCapabilitiesStatusChanged(Capabilities caps) {
synchronized (mLock) {
- mCapabilityStatus = c.copy();
+ mCapabilityStatus = caps.copy();
}
- int count = mCapabilityCallbacks.beginBroadcast();
- try {
- for (int i = 0; i < count; i++) {
- try {
- mCapabilityCallbacks.getBroadcastItem(i).onCapabilitiesStatusChanged(
- c.mCapabilities);
- } catch (RemoteException e) {
- Log.w(LOG_TAG, e + " " + "notifyCapabilitiesStatusChanged() - Skipping " +
- "callback.");
- }
+ mCapabilityCallbacks.broadcast((callback) -> {
+ try {
+ callback.onCapabilitiesStatusChanged(caps.mCapabilities);
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, e + " notifyCapabilitiesStatusChanged() - Skipping "
+ + "callback.");
}
- } finally {
- mCapabilityCallbacks.finishBroadcast();
- }
+ });
}
/**
+ * Provides the ImsFeature with the ability to return the framework Capability Configuration
+ * for a provided Capability. If the framework calls {@link #changeEnabledCapabilities} and
+ * includes a capability A to enable or disable, this method should return the correct enabled
+ * status for capability A.
+ * @param capability The capability that we are querying the configuration for.
+ * @return true if the capability is enabled, false otherwise.
+ * @hide
+ */
+ public abstract boolean queryCapabilityConfiguration(int capability, int radioTech);
+
+ /**
* Features should override this method to receive Capability preference change requests from
* the framework using the provided {@link CapabilityChangeRequest}. If any of the capabilities
* in the {@link CapabilityChangeRequest} are not able to be completed due to an error,
@@ -490,7 +505,9 @@
public abstract void onFeatureRemoved();
/**
- * Called when the feature has been initialized and communication with the framework is set up.
+ * Called after this ImsFeature has been initialized and has been set to the
+ * {@link ImsState#STATE_READY} state.
+ * <p>
* Any attempt by this feature to access the framework before this method is called will return
* with an {@link IllegalStateException}.
* The IMS provider should use this method to trigger registration for this feature on the IMS
diff --git a/telephony/java/android/telephony/ims/feature/MmTelFeature.java b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
index 898ca48..056a0ab 100644
--- a/telephony/java/android/telephony/ims/feature/MmTelFeature.java
+++ b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
@@ -37,7 +37,6 @@
import android.telephony.ims.stub.ImsRegistrationImplBase;
import android.telephony.ims.stub.ImsSmsImplBase;
import android.telephony.ims.stub.ImsUtImplBase;
-import android.util.Log;
import com.android.ims.internal.IImsCallSession;
import com.android.ims.internal.IImsEcbm;
@@ -154,17 +153,13 @@
@Override
public void changeCapabilitiesConfiguration(CapabilityChangeRequest request,
IImsCapabilityCallback c) {
- synchronized (mLock) {
- MmTelFeature.this.requestChangeEnabledCapabilities(request, c);
- }
+ MmTelFeature.this.requestChangeEnabledCapabilities(request, c);
}
@Override
public void queryCapabilityConfiguration(int capability, int radioTech,
IImsCapabilityCallback c) {
- synchronized (mLock) {
- queryCapabilityConfigurationInternal(capability, radioTech, c);
- }
+ queryCapabilityConfigurationInternal(capability, radioTech, c);
}
@Override
@@ -381,18 +376,6 @@
}
}
- private void queryCapabilityConfigurationInternal(int capability, int radioTech,
- IImsCapabilityCallback c) {
- boolean enabled = queryCapabilityConfiguration(capability, radioTech);
- try {
- if (c != null) {
- c.onQueryCapabilityConfiguration(capability, radioTech, enabled);
- }
- } catch (RemoteException e) {
- Log.e(LOG_TAG, "queryCapabilityConfigurationInternal called on dead binder!");
- }
- }
-
/**
* The current capability status that this MmTelFeature has defined is available. This
* configuration will be used by the platform to figure out which capabilities are CURRENTLY
@@ -512,6 +495,7 @@
* @param capability The capability that we are querying the configuration for.
* @return true if the capability is enabled, false otherwise.
*/
+ @Override
public boolean queryCapabilityConfiguration(@MmTelCapabilities.MmTelCapability int capability,
@ImsRegistrationImplBase.ImsRegistrationTech int radioTech) {
// Base implementation - Override to provide functionality
diff --git a/telephony/java/android/telephony/ims/feature/RcsFeature.java b/telephony/java/android/telephony/ims/feature/RcsFeature.java
index a637e16..f69b434 100644
--- a/telephony/java/android/telephony/ims/feature/RcsFeature.java
+++ b/telephony/java/android/telephony/ims/feature/RcsFeature.java
@@ -16,8 +16,32 @@
package android.telephony.ims.feature;
+import android.annotation.CallbackExecutor;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.annotation.SystemApi;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.RemoteException;
+import android.telephony.ims.RcsContactUceCapability;
+import android.telephony.ims.aidl.IImsCapabilityCallback;
import android.telephony.ims.aidl.IImsRcsFeature;
+import android.telephony.ims.aidl.IRcsFeatureListener;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
+import android.telephony.ims.stub.RcsPresenceExchangeImplBase;
+import android.telephony.ims.stub.RcsSipOptionsImplBase;
+import android.util.Log;
+
+import com.android.internal.util.FunctionalUtils;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.List;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
/**
* Base implementation of the RcsFeature APIs. Any ImsService wishing to support RCS should extend
@@ -27,23 +51,329 @@
@SystemApi
public class RcsFeature extends ImsFeature {
- /**{@inheritDoc}*/
- private final IImsRcsFeature mImsRcsBinder = new IImsRcsFeature.Stub() {
- // Empty Default Implementation.
- };
+ private static final String LOG_TAG = "RcsFeature";
+ private static final class RcsFeatureBinder extends IImsRcsFeature.Stub {
+ // Reference the outer class in order to have better test coverage metrics instead of
+ // creating a inner class referencing the outer class directly.
+ private final RcsFeature mReference;
+ private final Executor mExecutor;
- public RcsFeature() {
- super();
+ RcsFeatureBinder(RcsFeature classRef, @CallbackExecutor Executor executor) {
+ mReference = classRef;
+ mExecutor = executor;
+ }
+
+ @Override
+ public void setListener(IRcsFeatureListener listener) {
+ mReference.setListener(listener);
+ }
+
+ @Override
+ public int queryCapabilityStatus() throws RemoteException {
+ return executeMethodAsyncForResult(
+ () -> mReference.queryCapabilityStatus().mCapabilities,
+ "queryCapabilityStatus");
+ }
+
+ @Override
+ public void addCapabilityCallback(IImsCapabilityCallback c) throws RemoteException {
+ executeMethodAsync(() -> mReference.addCapabilityCallback(c), "addCapabilityCallback");
+ }
+
+ @Override
+ public void removeCapabilityCallback(IImsCapabilityCallback c) throws RemoteException {
+ executeMethodAsync(() -> mReference.removeCapabilityCallback(c),
+ "removeCapabilityCallback");
+ }
+
+ @Override
+ public void changeCapabilitiesConfiguration(CapabilityChangeRequest r,
+ IImsCapabilityCallback c) throws RemoteException {
+ executeMethodAsync(() -> mReference.requestChangeEnabledCapabilities(r, c),
+ "changeCapabilitiesConfiguration");
+ }
+
+ @Override
+ public void queryCapabilityConfiguration(int capability, int radioTech,
+ IImsCapabilityCallback c) throws RemoteException {
+ executeMethodAsync(() -> mReference.queryCapabilityConfigurationInternal(capability,
+ radioTech, c), "queryCapabilityConfiguration");
+ }
+
+ @Override
+ public int getFeatureState() throws RemoteException {
+ return executeMethodAsyncForResult(mReference::getFeatureState, "getFeatureState");
+ }
+
+ // RcsPresenceExchangeImplBase specific APIS
+ @Override
+ public void requestCapabilities(List<Uri> uris, int operationToken) throws RemoteException {
+ executeMethodAsync(() -> mReference.getPresenceExchangeInternal()
+ .requestCapabilities(uris, operationToken), "requestCapabilities");
+ }
+ @Override
+ public void updateCapabilities(RcsContactUceCapability capabilities, int operationToken)
+ throws RemoteException {
+ executeMethodAsync(() -> mReference.getPresenceExchangeInternal()
+ .updateCapabilities(capabilities, operationToken),
+ "updateCapabilities");
+
+ }
+ // RcsSipOptionsImplBase specific APIS
+ @Override
+ public void sendCapabilityRequest(Uri contactUri, RcsContactUceCapability capabilities,
+ int operationToken) throws RemoteException {
+ executeMethodAsync(() -> mReference.getOptionsExchangeInternal()
+ .sendCapabilityRequest(contactUri, capabilities, operationToken),
+ "sendCapabilityRequest");
+
+ }
+ @Override
+ public void respondToCapabilityRequest(String contactUri,
+ RcsContactUceCapability ownCapabilities, int operationToken)
+ throws RemoteException {
+ executeMethodAsync(() -> mReference.getOptionsExchangeInternal()
+ .respondToCapabilityRequest(contactUri, ownCapabilities,
+ operationToken), "respondToCapabilityRequest");
+
+ }
+ @Override
+ public void respondToCapabilityRequestWithError(Uri contactUri, int code, String reason,
+ int operationToken) throws RemoteException {
+ executeMethodAsync(() -> mReference.getOptionsExchangeInternal()
+ .respondToCapabilityRequestWithError(contactUri, code, reason,
+ operationToken), "respondToCapabilityRequestWithError");
+ }
+
+ // Call the methods with a clean calling identity on the executor and wait indefinitely for
+ // the future to return.
+ private void executeMethodAsync(FunctionalUtils.ThrowingRunnable r, String errorLogName)
+ throws RemoteException {
+ // call with a clean calling identity on the executor and wait indefinitely for the
+ // future to return.
+ try {
+ CompletableFuture.runAsync(
+ () -> Binder.withCleanCallingIdentity(r), mExecutor).join();
+ } catch (CancellationException | CompletionException e) {
+ Log.w(LOG_TAG, "RcsFeatureBinder - " + errorLogName + " exception: "
+ + e.getMessage());
+ throw new RemoteException(e.getMessage());
+ }
+ }
+
+ private <T> T executeMethodAsyncForResult(FunctionalUtils.ThrowingSupplier<T> r,
+ String errorLogName) throws RemoteException {
+ // call with a clean calling identity on the executor and wait indefinitely for the
+ // future to return.
+ CompletableFuture<T> future = CompletableFuture.supplyAsync(
+ () -> Binder.withCleanCallingIdentity(r), mExecutor);
+ try {
+ return future.get();
+ } catch (ExecutionException | InterruptedException e) {
+ Log.w(LOG_TAG, "RcsFeatureBinder - " + errorLogName + " exception: "
+ + e.getMessage());
+ throw new RemoteException(e.getMessage());
+ }
+ }
}
/**
- * {@inheritDoc}
+ * Contains the capabilities defined and supported by a {@link RcsFeature} in the
+ * form of a bitmask. The capabilities that are used in the RcsFeature are
+ * defined as:
+ * {@link RcsImsCapabilityFlag#CAPABILITY_TYPE_OPTIONS_UCE}
+ * {@link RcsImsCapabilityFlag#CAPABILITY_TYPE_PRESENCE_UCE}
+ *
+ * The enabled capabilities of this RcsFeature will be set by the framework
+ * using {@link #changeEnabledCapabilities(CapabilityChangeRequest, CapabilityCallbackProxy)}.
+ * After the capabilities have been set, the RcsFeature may then perform the necessary bring up
+ * of the capability and notify the capability status as true using
+ * {@link #notifyCapabilitiesStatusChanged(RcsImsCapabilities)}. This will signal to the
+ * framework that the capability is available for usage.
+ * @hide
+ */
+ public static class RcsImsCapabilities extends Capabilities {
+ /** @hide*/
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = "CAPABILITY_TYPE_", flag = true, value = {
+ CAPABILITY_TYPE_OPTIONS_UCE,
+ CAPABILITY_TYPE_PRESENCE_UCE
+ })
+ public @interface RcsImsCapabilityFlag {}
+
+ /**
+ * This carrier supports User Capability Exchange using SIP OPTIONS as defined by the
+ * framework. If set, the RcsFeature should support capability exchange using SIP OPTIONS.
+ * If not set, this RcsFeature should not service capability requests.
+ * @hide
+ */
+ public static final int CAPABILITY_TYPE_OPTIONS_UCE = 1 << 0;
+
+ /**
+ * This carrier supports User Capability Exchange using a presence server as defined by the
+ * framework. If set, the RcsFeature should support capability exchange using a presence
+ * server. If not set, this RcsFeature should not publish capabilities or service capability
+ * requests using presence.
+ * @hide
+ */
+ public static final int CAPABILITY_TYPE_PRESENCE_UCE = 1 << 1;
+
+ /**@hide*/
+ public RcsImsCapabilities(@RcsImsCapabilityFlag int capabilities) {
+ super(capabilities);
+ }
+
+ /**@hide*/
+ private RcsImsCapabilities(Capabilities c) {
+ super(c.getMask());
+ }
+
+ /**@hide*/
+ @Override
+ public void addCapabilities(@RcsImsCapabilityFlag int capabilities) {
+ super.addCapabilities(capabilities);
+ }
+
+ /**@hide*/
+ @Override
+ public void removeCapabilities(@RcsImsCapabilityFlag int capabilities) {
+ super.removeCapabilities(capabilities);
+ }
+
+ /**@hide*/
+ @Override
+ public boolean isCapable(@RcsImsCapabilityFlag int capabilities) {
+ return super.isCapable(capabilities);
+ }
+ }
+
+ private final RcsFeatureBinder mImsRcsBinder;
+ private IRcsFeatureListener mListenerBinder;
+ private RcsPresenceExchangeImplBase mPresExchange;
+ private RcsSipOptionsImplBase mSipOptions;
+
+ /**
+ * Create a new RcsFeature.
+ * <p>
+ * Method stubs called from the framework will be called asynchronously. To specify the
+ * {@link Executor} that the methods stubs will be called, use
+ * {@link RcsFeature#RcsFeature(Executor)} instead.
+ */
+ public RcsFeature() {
+ super();
+ // Run on the Binder threads that call them.
+ mImsRcsBinder = new RcsFeatureBinder(this, Runnable::run);
+ }
+
+ /**
+ * Create a new RcsFeature using the Executor specified for methods being called by the
+ * framework.
+ * @param executor The executor for the framework to use when making calls to this service.
+ * @hide
+ */
+ public RcsFeature(@NonNull Executor executor) {
+ super();
+ if (executor == null) {
+ throw new IllegalArgumentException("executor can not be null.");
+ }
+ // Run on the Binder thread by default.
+ mImsRcsBinder = new RcsFeatureBinder(this, executor);
+ }
+
+ /**
+ * Query the current {@link RcsImsCapabilities} status set by the RcsFeature. If a capability is
+ * set, the {@link RcsFeature} has brought up the capability and is ready for framework
+ * requests. To change the status of the capabilities
+ * {@link #notifyCapabilitiesStatusChanged(RcsImsCapabilities)} should be called.
+ * @hide
+ */
+ @Override
+ public final RcsImsCapabilities queryCapabilityStatus() {
+ return new RcsImsCapabilities(super.queryCapabilityStatus());
+ }
+
+ /**
+ * Notify the framework that the capabilities status has changed. If a capability is enabled,
+ * this signals to the framework that the capability has been initialized and is ready.
+ * Call {@link #queryCapabilityStatus()} to return the current capability status.
+ * @hide
+ */
+ public final void notifyCapabilitiesStatusChanged(@NonNull RcsImsCapabilities c) {
+ if (c == null) {
+ throw new IllegalArgumentException("RcsImsCapabilities must be non-null!");
+ }
+ super.notifyCapabilitiesStatusChanged(c);
+ }
+
+ /**
+ * Provides the RcsFeature with the ability to return the framework capability configuration set
+ * by the framework. When the framework calls
+ * {@link #changeEnabledCapabilities(CapabilityChangeRequest, CapabilityCallbackProxy)} to
+ * enable or disable capability A, this method should return the correct configuration for
+ * capability A afterwards (until it has changed).
+ * @hide
+ */
+ public boolean queryCapabilityConfiguration(
+ @RcsImsCapabilities.RcsImsCapabilityFlag int capability,
+ @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) {
+ // Base Implementation - Override to provide functionality
+ return false;
+ }
+ /**
+ * Called from the framework when the {@link RcsImsCapabilities} that have been configured for
+ * this {@link RcsFeature} has changed.
+ * <p>
+ * For each newly enabled capability flag, the corresponding capability should be brought up in
+ * the {@link RcsFeature} and registered on the network. For each newly disabled capability
+ * flag, the corresponding capability should be brought down, and deregistered. Once a new
+ * capability has been initialized and is ready for usage, the status of that capability should
+ * also be set to true using {@link #notifyCapabilitiesStatusChanged(RcsImsCapabilities)}. This
+ * will notify the framework that the capability is ready.
+ * <p>
+ * If for some reason one or more of these capabilities can not be enabled/disabled,
+ * {@link CapabilityCallbackProxy#onChangeCapabilityConfigurationError(int, int, int)} should
+ * be called for each capability change that resulted in an error.
+ * @hide
*/
@Override
public void changeEnabledCapabilities(CapabilityChangeRequest request,
CapabilityCallbackProxy c) {
- // Do nothing for base implementation.
+ // Base Implementation - Override to provide functionality
+ }
+
+ /**
+ * Retrieve the implementation of SIP OPTIONS for this {@link RcsFeature}.
+ * <p>
+ * Will only be requested by the framework if capability exchange via SIP OPTIONS is
+ * configured as capable during a
+ * {@link #changeEnabledCapabilities(CapabilityChangeRequest, CapabilityCallbackProxy)}
+ * operation and the RcsFeature sets the status of the capability to true using
+ * {@link #notifyCapabilitiesStatusChanged(RcsImsCapabilities)}.
+ *
+ * @return An instance of {@link RcsSipOptionsImplBase} that implements SIP options exchange if
+ * it is supported by the device.
+ * @hide
+ */
+ public RcsSipOptionsImplBase getOptionsExchangeImpl() {
+ // Base Implementation, override to implement functionality
+ return new RcsSipOptionsImplBase();
+ }
+
+ /**
+ * Retrieve the implementation of UCE presence for this {@link RcsFeature}.
+ * Will only be requested by the framework if presence exchang is configured as capable during
+ * a {@link #changeEnabledCapabilities(CapabilityChangeRequest, CapabilityCallbackProxy)}
+ * operation and the RcsFeature sets the status of the capability to true using
+ * {@link #notifyCapabilitiesStatusChanged(RcsImsCapabilities)}.
+ *
+ * @return An instance of {@link RcsPresenceExchangeImplBase} that implements presence
+ * exchange if it is supported by the device.
+ * @hide
+ */
+ public RcsPresenceExchangeImplBase getPresenceExchangeImpl() {
+ // Base Implementation, override to implement functionality.
+ return new RcsPresenceExchangeImplBase();
}
/**{@inheritDoc}*/
@@ -65,4 +395,40 @@
public final IImsRcsFeature getBinder() {
return mImsRcsBinder;
}
+
+ /**@hide*/
+ public IRcsFeatureListener getListener() {
+ synchronized (mLock) {
+ return mListenerBinder;
+ }
+ }
+
+ private void setListener(IRcsFeatureListener listener) {
+ synchronized (mLock) {
+ mListenerBinder = listener;
+ if (mListenerBinder != null) {
+ onFeatureReady();
+ }
+ }
+ }
+
+ private RcsPresenceExchangeImplBase getPresenceExchangeInternal() {
+ synchronized (mLock) {
+ if (mPresExchange == null) {
+ mPresExchange = getPresenceExchangeImpl();
+ mPresExchange.initialize(this);
+ }
+ return mPresExchange;
+ }
+ }
+
+ private RcsSipOptionsImplBase getOptionsExchangeInternal() {
+ synchronized (mLock) {
+ if (mSipOptions == null) {
+ mSipOptions = getOptionsExchangeImpl();
+ mSipOptions.initialize(this);
+ }
+ return mSipOptions;
+ }
+ }
}
diff --git a/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java b/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java
index 321bfff..3e135cc 100644
--- a/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java
@@ -19,6 +19,7 @@
import android.annotation.IntDef;
import android.annotation.SystemApi;
import android.content.Context;
+import android.os.PersistableBundle;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.telephony.ims.aidl.IImsConfig;
@@ -182,6 +183,11 @@
return retVal;
}
+ @Override
+ public void updateImsCarrierConfigs(PersistableBundle bundle) throws RemoteException {
+ getImsConfigImpl().updateImsCarrierConfigs(bundle);
+ }
+
private ImsConfigImplBase getImsConfigImpl() throws RemoteException {
ImsConfigImplBase ref = mImsConfigImplBaseWeakReference.get();
if (ref == null) {
@@ -342,6 +348,17 @@
}
/**
+ * The framework has received an RCS autoconfiguration XML file for provisioning.
+ *
+ * @param config The XML file to be read, if not compressed, it should be in ASCII/UTF8 format.
+ * @param isCompressed The XML file is compressed in gzip format and must be decompressed
+ * before being read.
+ * @hide
+ */
+ public void notifyRcsAutoConfigurationReceived(byte[] config, boolean isCompressed) {
+ }
+
+ /**
* Sets the configuration value for this ImsService.
*
* @param item an integer key.
@@ -387,4 +404,11 @@
// Base Implementation - To be overridden.
return null;
}
+
+ /**
+ * @hide
+ */
+ public void updateImsCarrierConfigs(PersistableBundle bundle) {
+ // Base Implementation - Should be overridden
+ }
}
diff --git a/telephony/java/android/telephony/ims/stub/ImsFeatureConfiguration.java b/telephony/java/android/telephony/ims/stub/ImsFeatureConfiguration.java
index dfb6e2c..1a839fc 100644
--- a/telephony/java/android/telephony/ims/stub/ImsFeatureConfiguration.java
+++ b/telephony/java/android/telephony/ims/stub/ImsFeatureConfiguration.java
@@ -21,7 +21,6 @@
import android.os.Parcelable;
import android.telephony.ims.feature.ImsFeature;
import android.util.ArraySet;
-import android.util.Pair;
import java.util.Set;
@@ -80,7 +79,7 @@
@Override
public String toString() {
- return "{s=" + slotId + ", f=" + featureType + "}";
+ return "{s=" + slotId + ", f=" + ImsFeature.FEATURE_LOG_MAP.get(featureType) + "}";
}
}
diff --git a/telephony/java/android/telephony/ims/stub/RcsCapabilityExchange.java b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchange.java
new file mode 100644
index 0000000..fda295a
--- /dev/null
+++ b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchange.java
@@ -0,0 +1,122 @@
+/*
+ * 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 android.telephony.ims.stub;
+
+import android.annotation.IntDef;
+import android.os.RemoteException;
+import android.telephony.ims.ImsException;
+import android.telephony.ims.aidl.IRcsFeatureListener;
+import android.telephony.ims.feature.ImsFeature;
+import android.telephony.ims.feature.RcsFeature;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Base class for different types of Capability exchange, presence using
+ * {@link RcsPresenceExchangeImplBase} and SIP OPTIONS exchange using {@link RcsSipOptionsImplBase}.
+ *
+ * @hide
+ */
+public class RcsCapabilityExchange {
+
+ /** Service is unknown. */
+ public static final int COMMAND_CODE_SERVICE_UNKNOWN = 0;
+ /** The command completed successfully. */
+ public static final int COMMAND_CODE_SUCCESS = 1;
+ /** The command failed with an unknown error. */
+ public static final int COMMAND_CODE_GENERIC_FAILURE = 2;
+ /** Invalid parameter(s). */
+ public static final int COMMAND_CODE_INVALID_PARAM = 3;
+ /** Fetch error. */
+ public static final int COMMAND_CODE_FETCH_ERROR = 4;
+ /** Request timed out. */
+ public static final int COMMAND_CODE_REQUEST_TIMEOUT = 5;
+ /** Failure due to insufficient memory available. */
+ public static final int COMMAND_CODE_INSUFFICIENT_MEMORY = 6;
+ /** Network connection is lost. */
+ public static final int COMMAND_CODE_LOST_NETWORK_CONNECTION = 7;
+ /** Requested feature/resource is not supported. */
+ public static final int COMMAND_CODE_NOT_SUPPORTED = 8;
+ /** Contact or resource is not found. */
+ public static final int COMMAND_CODE_NOT_FOUND = 9;
+ /** Service is not available. */
+ public static final int COMMAND_CODE_SERVICE_UNAVAILABLE = 10;
+ /** No Change in Capabilities */
+ public static final int COMMAND_CODE_NO_CHANGE_IN_CAP = 11;
+
+ /** @hide*/
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = "COMMAND_CODE_", value = {
+ COMMAND_CODE_SERVICE_UNKNOWN,
+ COMMAND_CODE_SUCCESS,
+ COMMAND_CODE_GENERIC_FAILURE,
+ COMMAND_CODE_INVALID_PARAM,
+ COMMAND_CODE_FETCH_ERROR,
+ COMMAND_CODE_REQUEST_TIMEOUT,
+ COMMAND_CODE_INSUFFICIENT_MEMORY,
+ COMMAND_CODE_LOST_NETWORK_CONNECTION,
+ COMMAND_CODE_NOT_SUPPORTED,
+ COMMAND_CODE_NOT_FOUND,
+ COMMAND_CODE_SERVICE_UNAVAILABLE,
+ COMMAND_CODE_NO_CHANGE_IN_CAP
+ })
+ public @interface CommandCode {}
+
+
+ private RcsFeature mFeature;
+
+ /** @hide */
+ public final void initialize(RcsFeature feature) {
+ mFeature = feature;
+ }
+
+ /** @hide */
+ protected final IRcsFeatureListener getListener() throws ImsException {
+ IRcsFeatureListener listener = mFeature.getListener();
+ if (listener == null) {
+ throw new ImsException("Connection to Framework has not been established, wait for "
+ + "onFeatureReady().", ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ }
+ return mFeature.getListener();
+ }
+
+ /**
+ * Provides the framework with an update as to whether or not a command completed successfully
+ * locally. This includes capabilities requests and updates from the network. If it does not
+ * complete successfully, then the framework may retry the command again later, depending on the
+ * error. If the command does complete successfully, the framework will then wait for network
+ * updates.
+ *
+ * @param code The result of the pending command. If {@link #COMMAND_CODE_SUCCESS}, further
+ * updates will be sent for this command using the associated operationToken.
+ * @param operationToken the token associated with the pending command.
+ * @throws ImsException If this {@link RcsCapabilityExchange} instance is not currently
+ * connected to the framework. This can happen if the {@link RcsFeature} is not
+ * {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received the
+ * {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare cases when the
+ * Telephony stack has crashed.
+ */
+ public final void onCommandUpdate(@CommandCode int code, int operationToken)
+ throws ImsException {
+ try {
+ getListener().onCommandUpdate(code, operationToken);
+ } catch (RemoteException e) {
+ throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ }
+ }
+}
diff --git a/telephony/java/android/telephony/ims/stub/RcsPresenceExchangeImplBase.java b/telephony/java/android/telephony/ims/stub/RcsPresenceExchangeImplBase.java
new file mode 100644
index 0000000..055fca5
--- /dev/null
+++ b/telephony/java/android/telephony/ims/stub/RcsPresenceExchangeImplBase.java
@@ -0,0 +1,246 @@
+/*
+ * 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 android.telephony.ims.stub;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.net.Uri;
+import android.os.RemoteException;
+import android.telephony.ims.ImsException;
+import android.telephony.ims.RcsContactUceCapability;
+import android.telephony.ims.feature.ImsFeature;
+import android.telephony.ims.feature.RcsFeature;
+import android.util.Log;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.List;
+
+/**
+ * Base implementation for RCS User Capability Exchange using Presence. Any ImsService implementing
+ * this service must implement the stub methods {@link #requestCapabilities(List, int)} and
+ * {@link #updateCapabilities(RcsContactUceCapability, int)}.
+ *
+ * @hide
+ */
+public class RcsPresenceExchangeImplBase extends RcsCapabilityExchange {
+
+ private static final String LOG_TAG = "RcsPresenceExchangeIB";
+
+ /**
+ * The request has resulted in any other 4xx/5xx/6xx that is not covered below. No retry will be
+ * attempted.
+ */
+ public static final int RESPONSE_SUBSCRIBE_GENERIC_FAILURE = -1;
+
+ /**
+ * The request has succeeded with a “200” message from the network.
+ */
+ public static final int RESPONSE_SUCCESS = 0;
+
+ /**
+ * The request has resulted in a “403” (User Not Registered) error from the network. Will retry
+ * capability polling with an exponential backoff.
+ */
+ public static final int RESPONSE_NOT_REGISTERED = 1;
+
+ /**
+ * The request has resulted in a “403” (not authorized (Requestor)) error from the network. No
+ * retry will be attempted.
+ */
+ public static final int RESPONSE_NOT_AUTHORIZED_FOR_PRESENCE = 2;
+
+ /**
+ * The request has resulted in a "403” (Forbidden) or other “403” error from the network and
+ * will be handled the same as “404” Not found. No retry will be attempted.
+ */
+ public static final int RESPONSE_FORBIDDEN = 3;
+
+ /**
+ * The request has resulted in a “404” (Not found) result from the network. No retry will be
+ * attempted.
+ */
+ public static final int RESPONSE_NOT_FOUND = 4;
+
+ /**
+ * The request has resulted in a “408” response. Retry after exponential backoff.
+ */
+ public static final int RESPONSE_SIP_REQUEST_TIMEOUT = 5;
+
+ /**
+ * The network has responded with a “413” (Too Large) response from the network. Capability
+ * request contains too many items and must be shrunk before the request will be accepted.
+ */
+ public static final int RESPONSE_SUBSCRIBE_TOO_LARGE = 6;
+
+ /**
+ * The request has resulted in a “423” response. Retry after exponential backoff.
+ */
+ public static final int RESPONSE_SIP_INTERVAL_TOO_SHORT = 7;
+
+ /**
+ * The request has resulted in a “503” response. Retry after exponential backoff.
+ */
+ public static final int RESPONSE_SIP_SERVICE_UNAVAILABLE = 8;
+
+ /** @hide*/
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = "RESPONSE_", value = {
+ RESPONSE_SUBSCRIBE_GENERIC_FAILURE,
+ RESPONSE_SUCCESS,
+ RESPONSE_NOT_REGISTERED,
+ RESPONSE_NOT_AUTHORIZED_FOR_PRESENCE,
+ RESPONSE_FORBIDDEN,
+ RESPONSE_NOT_FOUND,
+ RESPONSE_SIP_REQUEST_TIMEOUT,
+ RESPONSE_SUBSCRIBE_TOO_LARGE,
+ RESPONSE_SIP_INTERVAL_TOO_SHORT,
+ RESPONSE_SIP_SERVICE_UNAVAILABLE
+ })
+ public @interface PresenceResponseCode {}
+
+ /**
+ * Provide the framework with a subsequent network response update to
+ * {@link #updateCapabilities(RcsContactUceCapability, int)} and
+ * {@link #requestCapabilities(List, int)} operations.
+ *
+ * @param code The SIP response code sent from the network for the operation token specified.
+ * @param reason The optional reason response from the network. If the network provided no
+ * reason with the code, the string should be empty.
+ * @param operationToken The token associated with the operation this service is providing a
+ * response for.
+ * @throws ImsException If this {@link RcsPresenceExchangeImplBase} instance is not currently
+ * connected to the framework. This can happen if the {@link RcsFeature} is not
+ * {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received the
+ * {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare cases when the
+ * Telephony stack has crashed.
+ */
+ public final void onNetworkResponse(@PresenceResponseCode int code, @NonNull String reason,
+ int operationToken) throws ImsException {
+ try {
+ getListener().onNetworkResponse(code, reason, operationToken);
+ } catch (RemoteException e) {
+ throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ }
+ }
+
+ /**
+ * Provides the framework with the requested contacts’ capabilities requested by the framework
+ * using {@link #requestCapabilities(List, int)}.
+ *
+ * @throws ImsException If this {@link RcsPresenceExchangeImplBase} instance is not currently
+ * connected to the framework. This can happen if the {@link RcsFeature} is not
+ * {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received the
+ * {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare cases when the
+ * Telephony stack has crashed.
+ */
+ public final void onCapabilityRequestResponse(@NonNull List<RcsContactUceCapability> infos,
+ int operationToken) throws ImsException {
+ try {
+ getListener().onCapabilityRequestResponsePresence(infos, operationToken);
+ } catch (RemoteException e) {
+ throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ }
+ }
+
+ /**
+ * Trigger the framework to provide a capability update using
+ * {@link #updateCapabilities(RcsContactUceCapability, int)}.
+ * <p>
+ * This is typically used when trying to generate an initial PUBLISH for a new subscription to
+ * the network. The device will cache all presence publications after boot until this method is
+ * called once.
+ * @throws ImsException If this {@link RcsPresenceExchangeImplBase} instance is not currently
+ * connected to the framework. This can happen if the {@link RcsFeature} is not
+ * {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received the
+ * {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare cases when the
+ * Telephony stack has crashed.
+ */
+ public final void onNotifyUpdateCapabilites() throws ImsException {
+ try {
+ getListener().onNotifyUpdateCapabilities();
+ } catch (RemoteException e) {
+ throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ }
+ }
+
+ /**
+ * Notify the framework that the device’s capabilities have been unpublished from the network.
+ *
+ * @throws ImsException If this {@link RcsPresenceExchangeImplBase} instance is not currently
+ * connected to the framework. This can happen if the {@link RcsFeature} is not
+ * {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received the
+ * {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare cases when the
+ * Telephony stack has crashed.
+ */
+ public final void onUnpublish() throws ImsException {
+ try {
+ getListener().onUnpublish();
+ } catch (RemoteException e) {
+ throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ }
+ }
+
+ /**
+ * The user capabilities of one or multiple contacts have been requested by the framework.
+ * <p>
+ * The implementer must follow up this call with an {@link #onCommandUpdate(int, int)} call to
+ * indicate whether or not this operation succeeded. If this operation succeeds, network
+ * response updates should be sent to the framework using
+ * {@link #onNetworkResponse(int, String, int)}. When the operation is completed,
+ * {@link #onCapabilityRequestResponse(List, int)} should be called with the presence
+ * information for the contacts specified.
+ * @param uris A {@link List} of the {@link Uri}s that the framework is requesting the UCE
+ * capabilities for.
+ * @param operationToken The token associated with this operation. Updates to this request using
+ * {@link #onCommandUpdate(int, int)}, {@link #onNetworkResponse(int, String, int)}, and
+ * {@link #onCapabilityRequestResponse(List, int)} must use the same operation token
+ * in response.
+ */
+ public void requestCapabilities(@NonNull List<Uri> uris, int operationToken) {
+ // Stub - to be implemented by service
+ Log.w(LOG_TAG, "requestCapabilities called with no implementation.");
+ try {
+ getListener().onCommandUpdate(COMMAND_CODE_NOT_SUPPORTED, operationToken);
+ } catch (RemoteException | ImsException e) {
+ // Do not do anything, this is a stub implementation.
+ }
+ }
+
+ /**
+ * The capabilities of this device have been updated and should be published to the network.
+ * <p>
+ * The implementer must follow up this call with an {@link #onCommandUpdate(int, int)} call to
+ * indicate whether or not this operation succeeded. If this operation succeeds, network
+ * response updates should be sent to the framework using
+ * {@link #onNetworkResponse(int, String, int)}.
+ * @param capabilities The capabilities for this device.
+ * @param operationToken The token associated with this operation. Any subsequent
+ * {@link #onCommandUpdate(int, int)} or {@link #onNetworkResponse(int, String, int)}
+ * calls regarding this update must use the same token.
+ */
+ public void updateCapabilities(@NonNull RcsContactUceCapability capabilities,
+ int operationToken) {
+ // Stub - to be implemented by service
+ Log.w(LOG_TAG, "updateCapabilities called with no implementation.");
+ try {
+ getListener().onCommandUpdate(COMMAND_CODE_NOT_SUPPORTED, operationToken);
+ } catch (RemoteException | ImsException e) {
+ // Do not do anything, this is a stub implementation.
+ }
+ }
+}
diff --git a/telephony/java/android/telephony/ims/stub/RcsSipOptionsImplBase.java b/telephony/java/android/telephony/ims/stub/RcsSipOptionsImplBase.java
new file mode 100644
index 0000000..1c68fc0
--- /dev/null
+++ b/telephony/java/android/telephony/ims/stub/RcsSipOptionsImplBase.java
@@ -0,0 +1,203 @@
+/*
+ * 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 android.telephony.ims.stub;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.Uri;
+import android.os.RemoteException;
+import android.telephony.ims.ImsException;
+import android.telephony.ims.RcsContactUceCapability;
+import android.telephony.ims.feature.ImsFeature;
+import android.telephony.ims.feature.RcsFeature;
+import android.util.Log;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Base implementation for RCS User Capability Exchange using SIP OPTIONS.
+ *
+ * @hide
+ */
+public class RcsSipOptionsImplBase extends RcsCapabilityExchange {
+
+ private static final String LOG_TAG = "RcsSipOptionsImplBase";
+
+ /**
+ * Indicates a SIP response from the remote user other than 200, 480, 408, 404, or 604.
+ */
+ public static final int RESPONSE_GENERIC_FAILURE = -1;
+
+ /**
+ * Indicates that the remote user responded with a 200 OK response.
+ */
+ public static final int RESPONSE_SUCCESS = 0;
+
+ /**
+ * Indicates that the remote user responded with a 480 TEMPORARY UNAVAILABLE response.
+ */
+ public static final int RESPONSE_TEMPORARILY_UNAVAILABLE = 1;
+
+ /**
+ * Indicates that the remote user responded with a 408 REQUEST TIMEOUT response.
+ */
+ public static final int RESPONSE_REQUEST_TIMEOUT = 2;
+
+ /**
+ * Indicates that the remote user responded with a 404 NOT FOUND response.
+ */
+ public static final int RESPONSE_NOT_FOUND = 3;
+
+ /**
+ * Indicates that the remote user responded with a 604 DOES NOT EXIST ANYWHERE response.
+ */
+ public static final int RESPONSE_DOES_NOT_EXIST_ANYWHERE = 4;
+
+ /** @hide*/
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = "RESPONSE_", value = {
+ RESPONSE_GENERIC_FAILURE,
+ RESPONSE_SUCCESS,
+ RESPONSE_TEMPORARILY_UNAVAILABLE,
+ RESPONSE_REQUEST_TIMEOUT,
+ RESPONSE_NOT_FOUND,
+ RESPONSE_DOES_NOT_EXIST_ANYWHERE
+ })
+ public @interface SipResponseCode {}
+
+ /**
+ * Send the response of a SIP OPTIONS capability exchange to the framework. If {@code code} is
+ * {@link #RESPONSE_SUCCESS}, info must be non-null.
+ * @param code The SIP response code that was sent by the network in response to the request
+ * sent by {@link #sendCapabilityRequest(Uri, RcsContactUceCapability, int)}.
+ * @param reason The optional SIP response reason sent by the network. If none was sent, this
+ * should be an empty string.
+ * @param info the contact's UCE capabilities associated with the capability request.
+ * @param operationToken The token associated with the original capability request, set by
+ * {@link #sendCapabilityRequest(Uri, RcsContactUceCapability, int)}.
+ * @throws ImsException If this {@link RcsSipOptionsImplBase} instance is not currently
+ * connected to the framework. This can happen if the {@link RcsFeature} is not
+ * {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received the
+ * {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare cases when the
+ * Telephony stack has crashed.
+ */
+ public final void onCapabilityRequestResponse(@SipResponseCode int code, @NonNull String reason,
+ @Nullable RcsContactUceCapability info, int operationToken) throws ImsException {
+ try {
+ getListener().onCapabilityRequestResponseOptions(code, reason, info, operationToken);
+ } catch (RemoteException e) {
+ throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ }
+ }
+
+ /**
+ * Inform the framework of a query for this device's UCE capabilities.
+ * <p>
+ * The framework will respond via the
+ * {@link #respondToCapabilityRequest(String, RcsContactUceCapability, int)} or
+ * {@link #respondToCapabilityRequestWithError(Uri, int, String, int)} method.
+ * @param contactUri The URI associated with the remote contact that is requesting capabilities.
+ * @param remoteInfo The remote contact's capability information.
+ * @param operationToken An unique operation token that you have generated that will be returned
+ * by the framework in
+ * {@link #respondToCapabilityRequest(String, RcsContactUceCapability, int)}.
+ * @throws ImsException If this {@link RcsSipOptionsImplBase} instance is not currently
+ * connected to the framework. This can happen if the {@link RcsFeature} is not
+ * {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received the
+ * {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare cases when the
+ * Telephony stack has crashed.
+ */
+ public final void onRemoteCapabilityRequest(@NonNull Uri contactUri,
+ @NonNull RcsContactUceCapability remoteInfo, int operationToken) throws ImsException {
+ try {
+ getListener().onRemoteCapabilityRequest(contactUri, remoteInfo, operationToken);
+ } catch (RemoteException e) {
+ throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ }
+ }
+
+ /**
+ * Push one's own capabilities to a remote user via the SIP OPTIONS presence exchange mechanism
+ * in order to receive the capabilities of the remote user in response.
+ * <p>
+ * The implementer must call
+ * {@link #onCapabilityRequestResponse(int, String, RcsContactUceCapability, int)} to send the
+ * response of this query back to the framework.
+ * @param contactUri The URI of the remote user that we wish to get the capabilities of.
+ * @param capabilities The capabilities of this device to send to the remote user.
+ * @param operationToken A token generated by the framework that will be passed through
+ * {@link #onCapabilityRequestResponse(int, String, RcsContactUceCapability, int)} when this
+ * operation has succeeded.
+ */
+ public void sendCapabilityRequest(@NonNull Uri contactUri,
+ @NonNull RcsContactUceCapability capabilities, int operationToken) {
+ // Stub - to be implemented by service
+ Log.w(LOG_TAG, "sendCapabilityRequest called with no implementation.");
+ try {
+ getListener().onCommandUpdate(COMMAND_CODE_NOT_SUPPORTED, operationToken);
+ } catch (RemoteException | ImsException e) {
+ // Do not do anything, this is a stub implementation.
+ }
+ }
+
+ /**
+ * Respond to a remote capability request from the contact specified with the capabilities of
+ * this device.
+ * <p>
+ * The framework will use the same token and uri as what was passed in to
+ * {@link #onRemoteCapabilityRequest(Uri, RcsContactUceCapability, int)}.
+ * @param contactUri The URI of the remote contact.
+ * @param ownCapabilities The capabilities of this device.
+ * @param operationToken The token generated by the framework that this service obtained when
+ * {@link #onRemoteCapabilityRequest(Uri, RcsContactUceCapability, int)} was called.
+ */
+ public void respondToCapabilityRequest(@NonNull String contactUri,
+ @NonNull RcsContactUceCapability ownCapabilities, int operationToken) {
+ // Stub - to be implemented by service
+ Log.w(LOG_TAG, "respondToCapabilityRequest called with no implementation.");
+ try {
+ getListener().onCommandUpdate(COMMAND_CODE_NOT_SUPPORTED, operationToken);
+ } catch (RemoteException | ImsException e) {
+ // Do not do anything, this is a stub implementation.
+ }
+ }
+
+ /**
+ * Respond to a remote capability request from the contact specified with the specified error.
+ * <p>
+ * The framework will use the same token and uri as what was passed in to
+ * {@link #onRemoteCapabilityRequest(Uri, RcsContactUceCapability, int)}.
+ * @param contactUri A URI containing the remote contact.
+ * @param code The SIP response code to respond with.
+ * @param reason A non-null String containing the reason associated with the SIP code.
+ * @param operationToken The token provided by the framework when
+ * {@link #onRemoteCapabilityRequest(Uri, RcsContactUceCapability, int)} was called.
+ *
+ */
+ public void respondToCapabilityRequestWithError(@NonNull Uri contactUri,
+ @SipResponseCode int code, @NonNull String reason, int operationToken) {
+ // Stub - to be implemented by service
+ Log.w(LOG_TAG, "respondToCapabiltyRequestWithError called with no implementation.");
+ try {
+ getListener().onCommandUpdate(COMMAND_CODE_NOT_SUPPORTED, operationToken);
+ } catch (RemoteException | ImsException e) {
+ // Do not do anything, this is a stub implementation.
+ }
+ }
+}
diff --git a/telephony/java/com/android/internal/telephony/CbGeoUtils.java b/telephony/java/com/android/internal/telephony/CbGeoUtils.java
new file mode 100644
index 0000000..c973b67
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/CbGeoUtils.java
@@ -0,0 +1,359 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import android.annotation.NonNull;
+import android.telephony.Rlog;
+import android.text.TextUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+
+/**
+ * This utils class is specifically used for geo-targeting of CellBroadcast messages.
+ * The coordinates used by this utils class are latitude and longitude, but some algorithms in this
+ * class only use them as coordinates on plane, so the calculation will be inaccurate. So don't use
+ * this class for anything other then geo-targeting of cellbroadcast messages.
+ */
+public class CbGeoUtils {
+ /** Geometric interface. */
+ public interface Geometry {
+ /**
+ * Determines if the given point {@code p} is inside the geometry.
+ * @param p point in latitude, longitude format.
+ * @return {@code True} if the given point is inside the geometry.
+ */
+ boolean contains(LatLng p);
+ }
+
+ /**
+ * Tolerance for determining if the value is 0. If the absolute value of a value is less than
+ * this tolerance, it will be treated as 0.
+ */
+ public static final double EPS = 1e-7;
+
+ /** The radius of earth. */
+ public static final int EARTH_RADIUS_METER = 6371 * 1000;
+
+ private static final String TAG = "CbGeoUtils";
+
+ /** The identifier of geometry in the encoded string. */
+ private static final String CIRCLE_SYMBOL = "circle";
+ private static final String POLYGON_SYMBOL = "polygon";
+
+ /** Point represent by (latitude, longitude). */
+ public static class LatLng {
+ public final double lat;
+ public final double lng;
+
+ /**
+ * Constructor.
+ * @param lat latitude, range [-90, 90]
+ * @param lng longitude, range [-180, 180]
+ */
+ public LatLng(double lat, double lng) {
+ this.lat = lat;
+ this.lng = lng;
+ }
+
+ /**
+ * @param p the point use to calculate the subtraction result.
+ * @return the result of this point subtract the given point {@code p}.
+ */
+ public LatLng subtract(LatLng p) {
+ return new LatLng(lat - p.lat, lng - p.lng);
+ }
+
+ /**
+ * Calculate the distance in meter between this point and the given point {@code p}.
+ * @param p the point use to calculate the distance.
+ * @return the distance in meter.
+ */
+ public double distance(LatLng p) {
+ double dlat = Math.sin(0.5 * Math.toRadians(lat - p.lat));
+ double dlng = Math.sin(0.5 * Math.toRadians(lng - p.lng));
+ double x = dlat * dlat
+ + dlng * dlng * Math.cos(Math.toRadians(lat)) * Math.cos(Math.toRadians(p.lat));
+ return 2 * Math.atan2(Math.sqrt(x), Math.sqrt(1 - x)) * EARTH_RADIUS_METER;
+ }
+ }
+
+ /**
+ * The class represents a simple polygon with at least 3 points.
+ */
+ public static class Polygon implements Geometry {
+ /**
+ * In order to reduce the loss of precision in floating point calculations, all vertices
+ * of the polygon are scaled. Set the value of scale to 1000 can take into account the
+ * actual distance accuracy of 1 meter if the EPS is 1e-7 during the calculation.
+ */
+ private static final double SCALE = 1000.0;
+
+ private final List<LatLng> mVertices;
+ private final List<Point> mScaledVertices;
+ private final LatLng mOrigin;
+
+ /**
+ * Constructs a simple polygon from the given vertices. The adjacent two vertices are
+ * connected to form an edge of the polygon. The polygon has at least 3 vertices, and the
+ * last vertices and the first vertices must be adjacent.
+ *
+ * The longitude difference in the vertices should be less than 180 degree.
+ */
+ public Polygon(@NonNull List<LatLng> vertices) {
+ mVertices = vertices;
+
+ // Find the point with smallest longitude as the mOrigin point.
+ int idx = 0;
+ for (int i = 1; i < vertices.size(); i++) {
+ if (vertices.get(i).lng < vertices.get(idx).lng) {
+ idx = i;
+ }
+ }
+ mOrigin = vertices.get(idx);
+
+ mScaledVertices = vertices.stream()
+ .map(latLng -> convertAndScaleLatLng(latLng))
+ .collect(Collectors.toList());
+ }
+
+ public List<LatLng> getVertices() {
+ return mVertices;
+ }
+
+ /**
+ * Check if the given point {@code p} is inside the polygon. This method counts the number
+ * of times the polygon winds around the point P, A.K.A "winding number". The point is
+ * outside only when this "winding number" is 0.
+ *
+ * If a point is on the edge of the polygon, it is also considered to be inside the polygon.
+ */
+ @Override
+ public boolean contains(LatLng latLng) {
+ Point p = convertAndScaleLatLng(latLng);
+
+ int n = mScaledVertices.size();
+ int windingNumber = 0;
+ for (int i = 0; i < n; i++) {
+ Point a = mScaledVertices.get(i);
+ Point b = mScaledVertices.get((i + 1) % n);
+
+ // CCW is counterclockwise
+ // CCW = ab x ap
+ // CCW > 0 -> ap is on the left side of ab
+ // CCW == 0 -> ap is on the same line of ab
+ // CCW < 0 -> ap is on the right side of ab
+ int ccw = sign(crossProduct(b.subtract(a), p.subtract(a)));
+
+ if (ccw == 0) {
+ if (Math.min(a.x, b.x) <= p.x && p.x <= Math.max(a.x, b.x)
+ && Math.min(a.y, b.y) <= p.y && p.y <= Math.max(a.y, b.y)) {
+ return true;
+ }
+ } else {
+ if (sign(a.y - p.y) <= 0) {
+ // upward crossing
+ if (ccw > 0 && sign(b.y - p.y) > 0) {
+ ++windingNumber;
+ }
+ } else {
+ // downward crossing
+ if (ccw < 0 && sign(b.y - p.y) <= 0) {
+ --windingNumber;
+ }
+ }
+ }
+ }
+ return windingNumber != 0;
+ }
+
+ /**
+ * Move the given point {@code latLng} to the coordinate system with {@code mOrigin} as the
+ * origin and scale it. {@code mOrigin} is selected from the vertices of a polygon, it has
+ * the smallest longitude value among all of the polygon vertices.
+ *
+ * @param latLng the point need to be converted and scaled.
+ * @Return a {@link Point} object.
+ */
+ private Point convertAndScaleLatLng(LatLng latLng) {
+ double x = latLng.lat - mOrigin.lat;
+ double y = latLng.lng - mOrigin.lng;
+
+ // If the point is in different hemispheres(western/eastern) than the mOrigin, and the
+ // edge between them cross the 180th meridian, then its relative coordinates will be
+ // extended.
+ // For example, suppose the longitude of the mOrigin is -178, and the longitude of the
+ // point to be converted is 175, then the longitude after the conversion is -8.
+ // calculation: (-178 - 8) - (-178).
+ if (sign(mOrigin.lng) != 0 && sign(mOrigin.lng) != sign(latLng.lng)) {
+ double distCross0thMeridian = Math.abs(mOrigin.lng) + Math.abs(latLng.lng);
+ if (sign(distCross0thMeridian * 2 - 360) > 0) {
+ y = sign(mOrigin.lng) * (360 - distCross0thMeridian);
+ }
+ }
+ return new Point(x * SCALE, y * SCALE);
+ }
+
+ private static double crossProduct(Point a, Point b) {
+ return a.x * b.y - a.y * b.x;
+ }
+
+ static final class Point {
+ public final double x;
+ public final double y;
+
+ Point(double x, double y) {
+ this.x = x;
+ this.y = y;
+ }
+
+ public Point subtract(Point p) {
+ return new Point(x - p.x, y - p.y);
+ }
+ }
+ }
+
+ /** The class represents a circle. */
+ public static class Circle implements Geometry {
+ private final LatLng mCenter;
+ private final double mRadiusMeter;
+
+ public Circle(LatLng center, double radiusMeter) {
+ this.mCenter = center;
+ this.mRadiusMeter = radiusMeter;
+ }
+
+ public LatLng getCenter() {
+ return mCenter;
+ }
+
+ public double getRadius() {
+ return mRadiusMeter;
+ }
+
+ @Override
+ public boolean contains(LatLng p) {
+ return mCenter.distance(p) <= mRadiusMeter;
+ }
+ }
+
+ /**
+ * Parse the geometries from the encoded string {@code str}. The string must follow the
+ * geometry encoding specified by {@link android.provider.Telephony.CellBroadcasts#GEOMETRIES}.
+ */
+ @NonNull
+ public static List<Geometry> parseGeometriesFromString(@NonNull String str) {
+ List<Geometry> geometries = new ArrayList<>();
+ for (String geometryStr : str.split("\\s*;\\s*")) {
+ String[] geoParameters = geometryStr.split("\\s*\\|\\s*");
+ switch (geoParameters[0]) {
+ case CIRCLE_SYMBOL:
+ geometries.add(new Circle(parseLatLngFromString(geoParameters[1]),
+ Double.parseDouble(geoParameters[2])));
+ break;
+ case POLYGON_SYMBOL:
+ List<LatLng> vertices = new ArrayList<>(geoParameters.length - 1);
+ for (int i = 1; i < geoParameters.length; i++) {
+ vertices.add(parseLatLngFromString(geoParameters[i]));
+ }
+ geometries.add(new Polygon(vertices));
+ break;
+ default:
+ Rlog.e(TAG, "Invalid geometry format " + geometryStr);
+ }
+ }
+ return geometries;
+ }
+
+ /**
+ * Encode a list of geometry objects to string. The encoding format is specified by
+ * {@link android.provider.Telephony.CellBroadcasts#GEOMETRIES}.
+ *
+ * @param geometries the list of geometry objects need to be encoded.
+ * @return the encoded string.
+ */
+ @NonNull
+ public static String encodeGeometriesToString(@NonNull List<Geometry> geometries) {
+ return geometries.stream()
+ .map(geometry -> encodeGeometryToString(geometry))
+ .filter(encodedStr -> !TextUtils.isEmpty(encodedStr))
+ .collect(Collectors.joining(";"));
+ }
+
+
+ /**
+ * Encode the geometry object to string. The encoding format is specified by
+ * {@link android.provider.Telephony.CellBroadcasts#GEOMETRIES}.
+ * @param geometry the geometry object need to be encoded.
+ * @return the encoded string.
+ */
+ @NonNull
+ private static String encodeGeometryToString(@NonNull Geometry geometry) {
+ StringBuilder sb = new StringBuilder();
+ if (geometry instanceof Polygon) {
+ sb.append(POLYGON_SYMBOL);
+ for (LatLng latLng : ((Polygon) geometry).getVertices()) {
+ sb.append("|");
+ sb.append(latLng.lat);
+ sb.append(",");
+ sb.append(latLng.lng);
+ }
+ } else if (geometry instanceof Circle) {
+ sb.append(CIRCLE_SYMBOL);
+ Circle circle = (Circle) geometry;
+
+ // Center
+ sb.append("|");
+ sb.append(circle.getCenter().lat);
+ sb.append(",");
+ sb.append(circle.getCenter().lng);
+
+ // Radius
+ sb.append("|");
+ sb.append(circle.getRadius());
+ } else {
+ Rlog.e(TAG, "Unsupported geometry object " + geometry);
+ return null;
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Parse {@link LatLng} from {@link String}. Latitude and longitude are separated by ",".
+ * Example: "13.56,-55.447".
+ *
+ * @param str encoded lat/lng string.
+ * @Return {@link LatLng} object.
+ */
+ @NonNull
+ public static LatLng parseLatLngFromString(@NonNull String str) {
+ String[] latLng = str.split("\\s*,\\s*");
+ return new LatLng(Double.parseDouble(latLng[0]), Double.parseDouble(latLng[1]));
+ }
+
+ /**
+ * @Return the sign of the given value {@code value} with the specified tolerance. Return 1
+ * means the sign is positive, -1 means negative, 0 means the value will be treated as 0.
+ */
+ public static int sign(double value) {
+ if (value > EPS) return 1;
+ if (value < -EPS) return -1;
+ return 0;
+ }
+}
diff --git a/telephony/java/com/android/internal/telephony/cdma/BearerData.java b/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
similarity index 100%
rename from telephony/java/com/android/internal/telephony/cdma/BearerData.java
rename to telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaSmsAddress.java b/telephony/java/com/android/internal/telephony/cdma/sms/CdmaSmsAddress.java
similarity index 100%
rename from telephony/java/com/android/internal/telephony/cdma/CdmaSmsAddress.java
rename to telephony/java/com/android/internal/telephony/cdma/sms/CdmaSmsAddress.java
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaSmsSubaddress.java b/telephony/java/com/android/internal/telephony/cdma/sms/CdmaSmsSubaddress.java
similarity index 100%
rename from telephony/java/com/android/internal/telephony/cdma/CdmaSmsSubaddress.java
rename to telephony/java/com/android/internal/telephony/cdma/sms/CdmaSmsSubaddress.java
diff --git a/telephony/java/com/android/internal/telephony/cdma/SmsEnvelope.java b/telephony/java/com/android/internal/telephony/cdma/sms/SmsEnvelope.java
similarity index 100%
rename from telephony/java/com/android/internal/telephony/cdma/SmsEnvelope.java
rename to telephony/java/com/android/internal/telephony/cdma/sms/SmsEnvelope.java
diff --git a/test-base/Android.mk b/test-base/Android.mk
deleted file mode 100644
index a9d30cf..0000000
--- a/test-base/Android.mk
+++ /dev/null
@@ -1,29 +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.
-#
-
-LOCAL_PATH:= $(call my-dir)
-
-ifeq ($(HOST_OS),linux)
-# Build the legacy-performance-test-hostdex library
-# =================================================
-# This contains the android.test.PerformanceTestCase class only
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := src/android/test/PerformanceTestCase.java
-LOCAL_MODULE := legacy-performance-test-hostdex
-
-include $(BUILD_HOST_DALVIK_STATIC_JAVA_LIBRARY)
-endif # HOST_OS == linux
diff --git a/test-mock/Android.bp b/test-mock/Android.bp
index a5cd175..fd469a0 100644
--- a/test-mock/Android.bp
+++ b/test-mock/Android.bp
@@ -26,7 +26,6 @@
],
srcs_lib: "framework",
- srcs_lib_whitelist_dirs: ["core/java"],
srcs_lib_whitelist_pkgs: ["android"],
compile_dex: true,
}
diff --git a/tests/net/Android.bp b/tests/net/Android.bp
index 5260b30..502aa97 100644
--- a/tests/net/Android.bp
+++ b/tests/net/Android.bp
@@ -56,6 +56,7 @@
"androidx.test.rules",
"FrameworksNetCommonTests",
"frameworks-base-testutils",
+ "frameworks-net-integration-testutils",
"framework-protos",
"mockito-target-minus-junit4",
"net-tests-utils",
diff --git a/tests/net/integration/Android.bp b/tests/net/integration/Android.bp
new file mode 100644
index 0000000..16a68d7
--- /dev/null
+++ b/tests/net/integration/Android.bp
@@ -0,0 +1,31 @@
+//
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Utilities for testing framework code both in integration and unit tests.
+java_library {
+ name: "frameworks-net-integration-testutils",
+ srcs: ["util/**/*.java", "util/**/*.kt"],
+ static_libs: [
+ "androidx.annotation_annotation",
+ "androidx.test.rules",
+ "junit",
+ "net-tests-utils",
+ ],
+ libs: [
+ "services.core",
+ "services.net",
+ ],
+}
diff --git a/tests/net/integration/util/com/android/server/ConnectivityServiceTestUtils.kt b/tests/net/integration/util/com/android/server/ConnectivityServiceTestUtils.kt
new file mode 100644
index 0000000..fa2b99c
--- /dev/null
+++ b/tests/net/integration/util/com/android/server/ConnectivityServiceTestUtils.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server
+
+import android.net.ConnectivityManager.TYPE_BLUETOOTH
+import android.net.ConnectivityManager.TYPE_ETHERNET
+import android.net.ConnectivityManager.TYPE_MOBILE
+import android.net.ConnectivityManager.TYPE_NONE
+import android.net.ConnectivityManager.TYPE_TEST
+import android.net.ConnectivityManager.TYPE_VPN
+import android.net.ConnectivityManager.TYPE_WIFI
+import android.net.NetworkCapabilities.TRANSPORT_BLUETOOTH
+import android.net.NetworkCapabilities.TRANSPORT_CELLULAR
+import android.net.NetworkCapabilities.TRANSPORT_ETHERNET
+import android.net.NetworkCapabilities.TRANSPORT_TEST
+import android.net.NetworkCapabilities.TRANSPORT_VPN
+import android.net.NetworkCapabilities.TRANSPORT_WIFI
+
+fun transportToLegacyType(transport: Int) = when (transport) {
+ TRANSPORT_BLUETOOTH -> TYPE_BLUETOOTH
+ TRANSPORT_CELLULAR -> TYPE_MOBILE
+ TRANSPORT_ETHERNET -> TYPE_ETHERNET
+ TRANSPORT_TEST -> TYPE_TEST
+ TRANSPORT_VPN -> TYPE_VPN
+ TRANSPORT_WIFI -> TYPE_WIFI
+ else -> TYPE_NONE
+}
\ No newline at end of file
diff --git a/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java b/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
new file mode 100644
index 0000000..1e8d83c
--- /dev/null
+++ b/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
@@ -0,0 +1,267 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET;
+import static android.net.NetworkCapabilities.TRANSPORT_VPN;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI_AWARE;
+
+import static com.android.server.ConnectivityServiceTestUtilsKt.transportToLegacyType;
+
+import static junit.framework.Assert.assertTrue;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.LinkProperties;
+import android.net.Network;
+import android.net.NetworkAgent;
+import android.net.NetworkCapabilities;
+import android.net.NetworkFactory;
+import android.net.NetworkInfo;
+import android.net.NetworkMisc;
+import android.net.NetworkSpecifier;
+import android.net.SocketKeepalive;
+import android.net.UidRange;
+import android.os.ConditionVariable;
+import android.os.HandlerThread;
+import android.os.Message;
+import android.util.Log;
+
+import com.android.server.connectivity.ConnectivityConstants;
+import com.android.testutils.HandlerUtilsKt;
+import com.android.testutils.TestableNetworkCallback;
+
+import java.util.Set;
+
+public class NetworkAgentWrapper implements TestableNetworkCallback.HasNetwork {
+ private final NetworkInfo mNetworkInfo;
+ private final NetworkCapabilities mNetworkCapabilities;
+ private final HandlerThread mHandlerThread;
+ private final Context mContext;
+ private final String mLogTag;
+
+ private final ConditionVariable mDisconnected = new ConditionVariable();
+ private final ConditionVariable mPreventReconnectReceived = new ConditionVariable();
+ private int mScore;
+ private NetworkAgent mNetworkAgent;
+ private int mStartKeepaliveError = SocketKeepalive.ERROR_UNSUPPORTED;
+ private int mStopKeepaliveError = SocketKeepalive.NO_KEEPALIVE;
+ private Integer mExpectedKeepaliveSlot = null;
+
+ public NetworkAgentWrapper(int transport, LinkProperties linkProperties, Context context)
+ throws Exception {
+ final int type = transportToLegacyType(transport);
+ final String typeName = ConnectivityManager.getNetworkTypeName(type);
+ mNetworkInfo = new NetworkInfo(type, 0, typeName, "Mock");
+ mNetworkCapabilities = new NetworkCapabilities();
+ mNetworkCapabilities.addTransportType(transport);
+ switch (transport) {
+ case TRANSPORT_ETHERNET:
+ mScore = 70;
+ break;
+ case TRANSPORT_WIFI:
+ mScore = 60;
+ break;
+ case TRANSPORT_CELLULAR:
+ mScore = 50;
+ break;
+ case TRANSPORT_WIFI_AWARE:
+ mScore = 20;
+ break;
+ case TRANSPORT_VPN:
+ mNetworkCapabilities.removeCapability(NET_CAPABILITY_NOT_VPN);
+ mScore = ConnectivityConstants.VPN_DEFAULT_SCORE;
+ break;
+ default:
+ throw new UnsupportedOperationException("unimplemented network type");
+ }
+ mContext = context;
+ mLogTag = "Mock-" + typeName;
+ mHandlerThread = new HandlerThread(mLogTag);
+ mHandlerThread.start();
+
+ mNetworkAgent = makeNetworkAgent(linkProperties);
+ }
+
+ protected InstrumentedNetworkAgent makeNetworkAgent(LinkProperties linkProperties)
+ throws Exception {
+ return new InstrumentedNetworkAgent(this, linkProperties);
+ }
+
+ public static class InstrumentedNetworkAgent extends NetworkAgent {
+ private final NetworkAgentWrapper mWrapper;
+
+ public InstrumentedNetworkAgent(NetworkAgentWrapper wrapper, LinkProperties lp) {
+ super(wrapper.mHandlerThread.getLooper(), wrapper.mContext, wrapper.mLogTag,
+ wrapper.mNetworkInfo, wrapper.mNetworkCapabilities, lp, wrapper.mScore,
+ new NetworkMisc(), NetworkFactory.SerialNumber.NONE);
+ mWrapper = wrapper;
+ }
+
+ @Override
+ public void unwanted() {
+ mWrapper.mDisconnected.open();
+ }
+
+ @Override
+ public void startSocketKeepalive(Message msg) {
+ int slot = msg.arg1;
+ if (mWrapper.mExpectedKeepaliveSlot != null) {
+ assertEquals((int) mWrapper.mExpectedKeepaliveSlot, slot);
+ }
+ onSocketKeepaliveEvent(slot, mWrapper.mStartKeepaliveError);
+ }
+
+ @Override
+ public void stopSocketKeepalive(Message msg) {
+ onSocketKeepaliveEvent(msg.arg1, mWrapper.mStopKeepaliveError);
+ }
+
+ @Override
+ protected void preventAutomaticReconnect() {
+ mWrapper.mPreventReconnectReceived.open();
+ }
+
+ @Override
+ protected void addKeepalivePacketFilter(Message msg) {
+ Log.i(mWrapper.mLogTag, "Add keepalive packet filter.");
+ }
+
+ @Override
+ protected void removeKeepalivePacketFilter(Message msg) {
+ Log.i(mWrapper.mLogTag, "Remove keepalive packet filter.");
+ }
+ }
+
+ public void adjustScore(int change) {
+ mScore += change;
+ mNetworkAgent.sendNetworkScore(mScore);
+ }
+
+ public int getScore() {
+ return mScore;
+ }
+
+ public void explicitlySelected(boolean explicitlySelected, boolean acceptUnvalidated) {
+ mNetworkAgent.explicitlySelected(explicitlySelected, acceptUnvalidated);
+ }
+
+ public void addCapability(int capability) {
+ mNetworkCapabilities.addCapability(capability);
+ mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
+ }
+
+ public void removeCapability(int capability) {
+ mNetworkCapabilities.removeCapability(capability);
+ mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
+ }
+
+ public void setUids(Set<UidRange> uids) {
+ mNetworkCapabilities.setUids(uids);
+ mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
+ }
+
+ public void setSignalStrength(int signalStrength) {
+ mNetworkCapabilities.setSignalStrength(signalStrength);
+ mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
+ }
+
+ public void setNetworkSpecifier(NetworkSpecifier networkSpecifier) {
+ mNetworkCapabilities.setNetworkSpecifier(networkSpecifier);
+ mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
+ }
+
+ public void setNetworkCapabilities(NetworkCapabilities nc, boolean sendToConnectivityService) {
+ mNetworkCapabilities.set(nc);
+ if (sendToConnectivityService) {
+ mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
+ }
+ }
+
+ public void connect() {
+ assertNotEquals("MockNetworkAgents can only be connected once",
+ getNetworkInfo().getDetailedState(), NetworkInfo.DetailedState.CONNECTED);
+ mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTED, null, null);
+ mNetworkAgent.sendNetworkInfo(mNetworkInfo);
+ }
+
+ public void suspend() {
+ mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.SUSPENDED, null, null);
+ mNetworkAgent.sendNetworkInfo(mNetworkInfo);
+ }
+
+ public void resume() {
+ mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTED, null, null);
+ mNetworkAgent.sendNetworkInfo(mNetworkInfo);
+ }
+
+ public void disconnect() {
+ mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.DISCONNECTED, null, null);
+ mNetworkAgent.sendNetworkInfo(mNetworkInfo);
+ }
+
+ @Override
+ public Network getNetwork() {
+ return new Network(mNetworkAgent.netId);
+ }
+
+ public void expectPreventReconnectReceived(long timeoutMs) {
+ assertTrue(mPreventReconnectReceived.block(timeoutMs));
+ }
+
+ public void expectDisconnected(long timeoutMs) {
+ assertTrue(mDisconnected.block(timeoutMs));
+ }
+
+ public void sendLinkProperties(LinkProperties lp) {
+ mNetworkAgent.sendLinkProperties(lp);
+ }
+
+ public void setStartKeepaliveEvent(int reason) {
+ mStartKeepaliveError = reason;
+ }
+
+ public void setStopKeepaliveEvent(int reason) {
+ mStopKeepaliveError = reason;
+ }
+
+ public void setExpectedKeepaliveSlot(Integer slot) {
+ mExpectedKeepaliveSlot = slot;
+ }
+
+ public NetworkAgent getNetworkAgent() {
+ return mNetworkAgent;
+ }
+
+ public NetworkInfo getNetworkInfo() {
+ return mNetworkInfo;
+ }
+
+ public NetworkCapabilities getNetworkCapabilities() {
+ return mNetworkCapabilities;
+ }
+
+ public void waitForIdle(long timeoutMs) {
+ HandlerUtilsKt.waitForIdle(mHandlerThread, timeoutMs);
+ }
+}
diff --git a/tests/net/integration/util/com/android/server/TestNetIdManager.kt b/tests/net/integration/util/com/android/server/TestNetIdManager.kt
new file mode 100644
index 0000000..eb290dc
--- /dev/null
+++ b/tests/net/integration/util/com/android/server/TestNetIdManager.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server
+
+import java.util.concurrent.atomic.AtomicInteger
+
+/**
+ * A [NetIdManager] that generates ID starting from [NetIdManager.MAX_NET_ID] and decreasing, rather
+ * than starting from [NetIdManager.MIN_NET_ID] and increasing.
+ *
+ * Useful for testing ConnectivityService, to minimize the risk of test ConnectivityService netIDs
+ * overlapping with netIDs used by the real ConnectivityService on the device.
+ *
+ * IDs may still overlap if many networks have been used on the device (so the "real" netIDs
+ * are close to MAX_NET_ID), but this is typically not the case when running unit tests. Also, there
+ * is no way to fully solve the overlap issue without altering ID allocation in non-test code, as
+ * the real ConnectivityService could start using netIds that have been used by the test in the
+ * past.
+ */
+class TestNetIdManager : NetIdManager() {
+ private val nextId = AtomicInteger(MAX_NET_ID)
+ override fun reserveNetId() = nextId.decrementAndGet()
+ override fun releaseNetId(id: Int) = Unit
+}
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index e08cb16..a028a54 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -28,8 +28,6 @@
import static android.net.ConnectivityManager.TYPE_MOBILE;
import static android.net.ConnectivityManager.TYPE_MOBILE_FOTA;
import static android.net.ConnectivityManager.TYPE_MOBILE_MMS;
-import static android.net.ConnectivityManager.TYPE_NONE;
-import static android.net.ConnectivityManager.TYPE_VPN;
import static android.net.ConnectivityManager.TYPE_WIFI;
import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_DNS;
import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_FALLBACK;
@@ -69,6 +67,7 @@
import static android.net.NetworkPolicyManager.RULE_REJECT_METERED;
import static android.net.RouteInfo.RTN_UNREACHABLE;
+import static com.android.server.ConnectivityServiceTestUtilsKt.transportToLegacyType;
import static com.android.testutils.ConcurrentUtilsKt.await;
import static com.android.testutils.ConcurrentUtilsKt.durationOf;
import static com.android.testutils.ExceptionUtils.ignoreExceptions;
@@ -86,6 +85,7 @@
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Matchers.anyInt;
@@ -93,6 +93,7 @@
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -105,6 +106,7 @@
import static org.mockito.Mockito.when;
import android.annotation.NonNull;
+import android.app.AlarmManager;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
@@ -125,6 +127,7 @@
import android.net.ConnectivityManager.TooManyRequestsException;
import android.net.ConnectivityThread;
import android.net.IDnsResolver;
+import android.net.IIpConnectivityMetrics;
import android.net.INetd;
import android.net.INetworkMonitor;
import android.net.INetworkMonitorCallbacks;
@@ -139,12 +142,8 @@
import android.net.LinkProperties;
import android.net.MatchAllNetworkSpecifier;
import android.net.Network;
-import android.net.NetworkAgent;
import android.net.NetworkCapabilities;
import android.net.NetworkFactory;
-import android.net.NetworkInfo;
-import android.net.NetworkInfo.DetailedState;
-import android.net.NetworkMisc;
import android.net.NetworkRequest;
import android.net.NetworkSpecifier;
import android.net.NetworkStack;
@@ -168,7 +167,6 @@
import android.os.HandlerThread;
import android.os.INetworkManagementService;
import android.os.Looper;
-import android.os.Message;
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
@@ -265,10 +263,12 @@
// timeout. For this, our assertions should run fast enough to leave less than
// (mService.mLingerDelayMs - TEST_CALLBACK_TIMEOUT_MS) between the time callbacks are
// supposedly fired, and the time we call expectCallback.
- private final static int TEST_CALLBACK_TIMEOUT_MS = 200;
+ private static final int TEST_CALLBACK_TIMEOUT_MS = 200;
// Chosen to be less than TEST_CALLBACK_TIMEOUT_MS. This ensures that requests have time to
// complete before callbacks are verified.
- private final static int TEST_REQUEST_TIMEOUT_MS = 150;
+ private static final int TEST_REQUEST_TIMEOUT_MS = 150;
+
+ private static final int UNREASONABLY_LONG_ALARM_WAIT_MS = 1000;
private static final String CLAT_PREFIX = "v4-";
private static final String MOBILE_IFNAME = "test_rmnet_data0";
@@ -276,15 +276,19 @@
private static final String[] EMPTY_STRING_ARRAY = new String[0];
private MockContext mServiceContext;
- private WrappedConnectivityService mService;
+ private HandlerThread mCsHandlerThread;
+ private ConnectivityService mService;
private WrappedConnectivityManager mCm;
- private MockNetworkAgent mWiFiNetworkAgent;
- private MockNetworkAgent mCellNetworkAgent;
- private MockNetworkAgent mEthernetNetworkAgent;
+ private TestNetworkAgentWrapper mWiFiNetworkAgent;
+ private TestNetworkAgentWrapper mCellNetworkAgent;
+ private TestNetworkAgentWrapper mEthernetNetworkAgent;
private MockVpn mMockVpn;
private Context mContext;
private INetworkPolicyListener mPolicyListener;
+ private WrappedMultinetworkPolicyTracker mPolicyTracker;
+ private HandlerThread mAlarmManagerThread;
+ @Mock IIpConnectivityMetrics mIpConnectivityMetrics;
@Mock IpConnectivityMetrics.Logger mMetricsService;
@Mock DefaultNetworkMetrics mDefaultNetworkMetrics;
@Mock INetworkManagementService mNetworkManagementService;
@@ -296,6 +300,7 @@
@Mock PackageManager mPackageManager;
@Mock UserManager mUserManager;
@Mock NotificationManager mNotificationManager;
+ @Mock AlarmManager mAlarmManager;
private ArgumentCaptor<ResolverParamsParcel> mResolverParamsParcelCaptor =
ArgumentCaptor.forClass(ResolverParamsParcel.class);
@@ -368,6 +373,7 @@
if (Context.NOTIFICATION_SERVICE.equals(name)) return mNotificationManager;
if (Context.NETWORK_STACK_SERVICE.equals(name)) return mNetworkStack;
if (Context.USER_SERVICE.equals(name)) return mUserManager;
+ if (Context.ALARM_SERVICE.equals(name)) return mAlarmManager;
return super.getSystemService(name);
}
@@ -397,25 +403,20 @@
}
}
- public void waitForIdle(int timeoutMsAsInt) {
- long timeoutMs = timeoutMsAsInt;
- HandlerUtilsKt.waitForIdle(mService.mHandlerThread, timeoutMs);
- waitForIdle(mCellNetworkAgent, timeoutMs);
- waitForIdle(mWiFiNetworkAgent, timeoutMs);
- waitForIdle(mEthernetNetworkAgent, timeoutMs);
- HandlerUtilsKt.waitForIdle(mService.mHandlerThread, timeoutMs);
- HandlerUtilsKt.waitForIdle(ConnectivityThread.get(), timeoutMs);
+ private void waitForIdle() {
+ HandlerUtilsKt.waitForIdle(mCsHandlerThread, TIMEOUT_MS);
+ waitForIdle(mCellNetworkAgent, TIMEOUT_MS);
+ waitForIdle(mWiFiNetworkAgent, TIMEOUT_MS);
+ waitForIdle(mEthernetNetworkAgent, TIMEOUT_MS);
+ HandlerUtilsKt.waitForIdle(mCsHandlerThread, TIMEOUT_MS);
+ HandlerUtilsKt.waitForIdle(ConnectivityThread.get(), TIMEOUT_MS);
}
- public void waitForIdle(MockNetworkAgent agent, long timeoutMs) {
+ private void waitForIdle(TestNetworkAgentWrapper agent, long timeoutMs) {
if (agent == null) {
return;
}
- HandlerUtilsKt.waitForIdle(agent.mHandlerThread, timeoutMs);
- }
-
- private void waitForIdle() {
- waitForIdle(TIMEOUT_MS);
+ agent.waitForIdle(timeoutMs);
}
@Test
@@ -429,7 +430,7 @@
// Bring up a network that we can use to send messages to ConnectivityService.
ConditionVariable cv = waitForConnectivityBroadcasts(1);
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(false);
waitFor(cv);
Network n = mWiFiNetworkAgent.getNetwork();
@@ -449,7 +450,7 @@
public void verifyThatNotWaitingForIdleCausesRaceConditions() throws Exception {
// Bring up a network that we can use to send messages to ConnectivityService.
ConditionVariable cv = waitForConnectivityBroadcasts(1);
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(false);
waitFor(cv);
Network n = mWiFiNetworkAgent.getNetwork();
@@ -469,7 +470,7 @@
fail("expected race condition at least once in " + attempts + " attempts");
}
- private class MockNetworkAgent implements TestableNetworkCallback.HasNetwork {
+ private class TestNetworkAgentWrapper extends NetworkAgentWrapper {
private static final int VALIDATION_RESULT_BASE = NETWORK_VALIDATION_PROBE_DNS
| NETWORK_VALIDATION_PROBE_HTTP
| NETWORK_VALIDATION_PROBE_HTTPS;
@@ -480,86 +481,35 @@
| NETWORK_VALIDATION_RESULT_PARTIAL;
private static final int VALIDATION_RESULT_INVALID = 0;
- private final INetworkMonitor mNetworkMonitor;
- private final NetworkInfo mNetworkInfo;
- private final NetworkCapabilities mNetworkCapabilities;
- private final HandlerThread mHandlerThread;
- private final ConditionVariable mDisconnected = new ConditionVariable();
- private final ConditionVariable mNetworkStatusReceived = new ConditionVariable();
- private final ConditionVariable mPreventReconnectReceived = new ConditionVariable();
- private int mScore;
- private NetworkAgent mNetworkAgent;
- private int mStartKeepaliveError = SocketKeepalive.ERROR_UNSUPPORTED;
- private int mStopKeepaliveError = SocketKeepalive.NO_KEEPALIVE;
- private Integer mExpectedKeepaliveSlot = null;
- // Contains the redirectUrl from networkStatus(). Before reading, wait for
- // mNetworkStatusReceived.
- private String mRedirectUrl;
-
+ private INetworkMonitor mNetworkMonitor;
private INetworkMonitorCallbacks mNmCallbacks;
private int mNmValidationResult = VALIDATION_RESULT_BASE;
private String mNmValidationRedirectUrl = null;
private boolean mNmProvNotificationRequested = false;
- void setNetworkValid() {
- mNmValidationResult = VALIDATION_RESULT_VALID;
- mNmValidationRedirectUrl = null;
- }
+ private final ConditionVariable mNetworkStatusReceived = new ConditionVariable();
+ // Contains the redirectUrl from networkStatus(). Before reading, wait for
+ // mNetworkStatusReceived.
+ private String mRedirectUrl;
- void setNetworkInvalid() {
- mNmValidationResult = VALIDATION_RESULT_INVALID;
- mNmValidationRedirectUrl = null;
- }
-
- void setNetworkPortal(String redirectUrl) {
- setNetworkInvalid();
- mNmValidationRedirectUrl = redirectUrl;
- }
-
- void setNetworkPartial() {
- mNmValidationResult = VALIDATION_RESULT_PARTIAL;
- mNmValidationRedirectUrl = null;
- }
-
- void setNetworkPartialValid() {
- mNmValidationResult = VALIDATION_RESULT_PARTIAL | VALIDATION_RESULT_VALID;
- mNmValidationRedirectUrl = null;
- }
-
- MockNetworkAgent(int transport) throws Exception {
+ TestNetworkAgentWrapper(int transport) throws Exception {
this(transport, new LinkProperties());
}
- MockNetworkAgent(int transport, LinkProperties linkProperties) throws Exception {
- final int type = transportToLegacyType(transport);
- final String typeName = ConnectivityManager.getNetworkTypeName(type);
- mNetworkInfo = new NetworkInfo(type, 0, typeName, "Mock");
- mNetworkCapabilities = new NetworkCapabilities();
- mNetworkCapabilities.addTransportType(transport);
- switch (transport) {
- case TRANSPORT_ETHERNET:
- mScore = 70;
- break;
- case TRANSPORT_WIFI:
- mScore = 60;
- break;
- case TRANSPORT_CELLULAR:
- mScore = 50;
- break;
- case TRANSPORT_WIFI_AWARE:
- mScore = 20;
- break;
- case TRANSPORT_VPN:
- mNetworkCapabilities.removeCapability(NET_CAPABILITY_NOT_VPN);
- mScore = ConnectivityConstants.VPN_DEFAULT_SCORE;
- break;
- default:
- throw new UnsupportedOperationException("unimplemented network type");
- }
- mHandlerThread = new HandlerThread("Mock-" + typeName);
- mHandlerThread.start();
+ TestNetworkAgentWrapper(int transport, LinkProperties linkProperties)
+ throws Exception {
+ super(transport, linkProperties, mServiceContext);
+ // Waits for the NetworkAgent to be registered, which includes the creation of the
+ // NetworkMonitor.
+ waitForIdle(TIMEOUT_MS);
+ }
+
+ @Override
+ protected InstrumentedNetworkAgent makeNetworkAgent(LinkProperties linkProperties)
+ throws Exception {
mNetworkMonitor = mock(INetworkMonitor.class);
+
final Answer validateAnswer = inv -> {
new Thread(ignoreExceptions(this::onValidationRequested)).start();
return null;
@@ -576,56 +526,20 @@
any() /* name */,
nmCbCaptor.capture());
- mNetworkAgent = new NetworkAgent(mHandlerThread.getLooper(), mServiceContext,
- "Mock-" + typeName, mNetworkInfo, mNetworkCapabilities,
- linkProperties, mScore, new NetworkMisc(), NetworkFactory.SerialNumber.NONE) {
- @Override
- public void unwanted() { mDisconnected.open(); }
-
- @Override
- public void startSocketKeepalive(Message msg) {
- int slot = msg.arg1;
- if (mExpectedKeepaliveSlot != null) {
- assertEquals((int) mExpectedKeepaliveSlot, slot);
- }
- onSocketKeepaliveEvent(slot, mStartKeepaliveError);
- }
-
- @Override
- public void stopSocketKeepalive(Message msg) {
- onSocketKeepaliveEvent(msg.arg1, mStopKeepaliveError);
- }
-
+ final InstrumentedNetworkAgent na = new InstrumentedNetworkAgent(this, linkProperties) {
@Override
public void networkStatus(int status, String redirectUrl) {
mRedirectUrl = redirectUrl;
mNetworkStatusReceived.open();
}
-
- @Override
- protected void preventAutomaticReconnect() {
- mPreventReconnectReceived.open();
- }
-
- @Override
- protected void addKeepalivePacketFilter(Message msg) {
- Log.i(TAG, "Add keepalive packet filter.");
- }
-
- @Override
- protected void removeKeepalivePacketFilter(Message msg) {
- Log.i(TAG, "Remove keepalive packet filter.");
- }
};
- assertEquals(mNetworkAgent.netId, nmNetworkCaptor.getValue().netId);
+ assertEquals(na.netId, nmNetworkCaptor.getValue().netId);
mNmCallbacks = nmCbCaptor.getValue();
mNmCallbacks.onNetworkMonitorCreated(mNetworkMonitor);
- // Waits for the NetworkAgent to be registered, which includes the creation of the
- // NetworkMonitor.
- waitForIdle();
+ return na;
}
private void onValidationRequested() throws Exception {
@@ -645,55 +559,11 @@
}
}
- public void adjustScore(int change) {
- mScore += change;
- mNetworkAgent.sendNetworkScore(mScore);
- }
-
- public int getScore() {
- return mScore;
- }
-
- public void explicitlySelected(boolean explicitlySelected, boolean acceptUnvalidated) {
- mNetworkAgent.explicitlySelected(explicitlySelected, acceptUnvalidated);
- }
-
- public void addCapability(int capability) {
- mNetworkCapabilities.addCapability(capability);
- mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
- }
-
- public void removeCapability(int capability) {
- mNetworkCapabilities.removeCapability(capability);
- mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
- }
-
- public void setUids(Set<UidRange> uids) {
- mNetworkCapabilities.setUids(uids);
- mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
- }
-
- public void setSignalStrength(int signalStrength) {
- mNetworkCapabilities.setSignalStrength(signalStrength);
- mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
- }
-
- public void setNetworkSpecifier(NetworkSpecifier networkSpecifier) {
- mNetworkCapabilities.setNetworkSpecifier(networkSpecifier);
- mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
- }
-
- public void setNetworkCapabilities(NetworkCapabilities nc,
- boolean sendToConnectivityService) {
- mNetworkCapabilities.set(nc);
- if (sendToConnectivityService) {
- mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
- }
- }
-
+ /**
+ * Connect without adding any internet capability.
+ */
public void connectWithoutInternet() {
- mNetworkInfo.setDetailedState(DetailedState.CONNECTED, null, null);
- mNetworkAgent.sendNetworkInfo(mNetworkInfo);
+ super.connect();
}
/**
@@ -710,23 +580,21 @@
* @param hasInternet Indicate if network should pretend to have NET_CAPABILITY_INTERNET.
*/
public void connect(boolean validated, boolean hasInternet) {
- assertEquals("MockNetworkAgents can only be connected once",
- mNetworkInfo.getDetailedState(), DetailedState.IDLE);
- assertFalse(mNetworkCapabilities.hasCapability(NET_CAPABILITY_INTERNET));
+ assertFalse(getNetworkCapabilities().hasCapability(NET_CAPABILITY_INTERNET));
- NetworkCallback callback = null;
+ ConnectivityManager.NetworkCallback callback = null;
final ConditionVariable validatedCv = new ConditionVariable();
if (validated) {
setNetworkValid();
NetworkRequest request = new NetworkRequest.Builder()
- .addTransportType(mNetworkCapabilities.getTransportTypes()[0])
+ .addTransportType(getNetworkCapabilities().getTransportTypes()[0])
.clearCapabilities()
.build();
- callback = new NetworkCallback() {
+ callback = new ConnectivityManager.NetworkCallback() {
public void onCapabilitiesChanged(Network network,
NetworkCapabilities networkCapabilities) {
if (network.equals(getNetwork()) &&
- networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED)) {
+ networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED)) {
validatedCv.open();
}
}
@@ -763,47 +631,29 @@
connect(false);
}
- public void suspend() {
- mNetworkInfo.setDetailedState(DetailedState.SUSPENDED, null, null);
- mNetworkAgent.sendNetworkInfo(mNetworkInfo);
+ void setNetworkValid() {
+ mNmValidationResult = VALIDATION_RESULT_VALID;
+ mNmValidationRedirectUrl = null;
}
- public void resume() {
- mNetworkInfo.setDetailedState(DetailedState.CONNECTED, null, null);
- mNetworkAgent.sendNetworkInfo(mNetworkInfo);
+ void setNetworkInvalid() {
+ mNmValidationResult = VALIDATION_RESULT_INVALID;
+ mNmValidationRedirectUrl = null;
}
- public void disconnect() {
- mNetworkInfo.setDetailedState(DetailedState.DISCONNECTED, null, null);
- mNetworkAgent.sendNetworkInfo(mNetworkInfo);
+ void setNetworkPortal(String redirectUrl) {
+ setNetworkInvalid();
+ mNmValidationRedirectUrl = redirectUrl;
}
- public Network getNetwork() {
- return new Network(mNetworkAgent.netId);
+ void setNetworkPartial() {
+ mNmValidationResult = VALIDATION_RESULT_PARTIAL;
+ mNmValidationRedirectUrl = null;
}
- public ConditionVariable getPreventReconnectReceived() {
- return mPreventReconnectReceived;
- }
-
- public ConditionVariable getDisconnectedCV() {
- return mDisconnected;
- }
-
- public void sendLinkProperties(LinkProperties lp) {
- mNetworkAgent.sendLinkProperties(lp);
- }
-
- public void setStartKeepaliveError(int error) {
- mStartKeepaliveError = error;
- }
-
- public void setStopKeepaliveError(int error) {
- mStopKeepaliveError = error;
- }
-
- public void setExpectedKeepaliveSlot(Integer slot) {
- mExpectedKeepaliveSlot = slot;
+ void setNetworkPartialValid() {
+ mNmValidationResult = VALIDATION_RESULT_PARTIAL | VALIDATION_RESULT_VALID;
+ mNmValidationRedirectUrl = null;
}
public String waitForRedirectUrl() {
@@ -811,12 +661,12 @@
return mRedirectUrl;
}
- public NetworkAgent getNetworkAgent() {
- return mNetworkAgent;
+ public void expectDisconnected() {
+ expectDisconnected(TIMEOUT_MS);
}
- public NetworkCapabilities getNetworkCapabilities() {
- return mNetworkCapabilities;
+ public void expectPreventReconnectReceived() {
+ expectPreventReconnectReceived(TIMEOUT_MS);
}
}
@@ -995,15 +845,15 @@
private boolean mConnected = false;
// Careful ! This is different from mNetworkAgent, because MockNetworkAgent does
// not inherit from NetworkAgent.
- private MockNetworkAgent mMockNetworkAgent;
+ private TestNetworkAgentWrapper mMockNetworkAgent;
public MockVpn(int userId) {
super(startHandlerThreadAndReturnLooper(), mServiceContext, mNetworkManagementService,
userId);
}
- public void setNetworkAgent(MockNetworkAgent agent) {
- waitForIdle(agent, TIMEOUT_MS);
+ public void setNetworkAgent(TestNetworkAgentWrapper agent) {
+ agent.waitForIdle(TIMEOUT_MS);
mMockNetworkAgent = agent;
mNetworkAgent = agent.getNetworkAgent();
mNetworkCapabilities.set(agent.getNetworkCapabilities());
@@ -1070,187 +920,45 @@
}
}
- private class FakeWakeupMessage extends WakeupMessage {
- private static final int UNREASONABLY_LONG_WAIT = 1000;
-
- public FakeWakeupMessage(Context context, Handler handler, String cmdName, int cmd) {
- super(context, handler, cmdName, cmd);
- }
-
- public FakeWakeupMessage(Context context, Handler handler, String cmdName, int cmd,
- int arg1, int arg2, Object obj) {
- super(context, handler, cmdName, cmd, arg1, arg2, obj);
- }
-
- @Override
- public void schedule(long when) {
- long delayMs = when - SystemClock.elapsedRealtime();
- if (delayMs < 0) delayMs = 0;
- if (delayMs > UNREASONABLY_LONG_WAIT) {
- fail("Attempting to send msg more than " + UNREASONABLY_LONG_WAIT +
- "ms into the future: " + delayMs);
- }
- Message msg = mHandler.obtainMessage(mCmd, mArg1, mArg2, mObj);
- mHandler.sendMessageDelayed(msg, delayMs);
- }
-
- @Override
- public void cancel() {
- mHandler.removeMessages(mCmd, mObj);
- }
-
- @Override
- public void onAlarm() {
- throw new AssertionError("Should never happen. Update this fake.");
+ private void mockVpn(int uid) {
+ synchronized (mService.mVpns) {
+ int userId = UserHandle.getUserId(uid);
+ mMockVpn = new MockVpn(userId);
+ // This has no effect unless the VPN is actually connected, because things like
+ // getActiveNetworkForUidInternal call getNetworkAgentInfoForNetId on the VPN
+ // netId, and check if that network is actually connected.
+ mService.mVpns.put(userId, mMockVpn);
}
}
- private class WrappedMultinetworkPolicyTracker extends MultinetworkPolicyTracker {
- public volatile boolean configRestrictsAvoidBadWifi;
- public volatile int configMeteredMultipathPreference;
+ private void setUidRulesChanged(int uidRules) throws RemoteException {
+ mPolicyListener.onUidRulesChanged(Process.myUid(), uidRules);
+ }
- public WrappedMultinetworkPolicyTracker(Context c, Handler h, Runnable r) {
+ private void setRestrictBackgroundChanged(boolean restrictBackground) throws RemoteException {
+ mPolicyListener.onRestrictBackgroundChanged(restrictBackground);
+ }
+
+ private Nat464Xlat getNat464Xlat(NetworkAgentWrapper mna) {
+ return mService.getNetworkAgentInfoForNetwork(mna.getNetwork()).clatd;
+ }
+
+ private static class WrappedMultinetworkPolicyTracker extends MultinetworkPolicyTracker {
+ volatile boolean mConfigRestrictsAvoidBadWifi;
+ volatile int mConfigMeteredMultipathPreference;
+
+ WrappedMultinetworkPolicyTracker(Context c, Handler h, Runnable r) {
super(c, h, r);
}
@Override
public boolean configRestrictsAvoidBadWifi() {
- return configRestrictsAvoidBadWifi;
+ return mConfigRestrictsAvoidBadWifi;
}
@Override
public int configMeteredMultipathPreference() {
- return configMeteredMultipathPreference;
- }
- }
-
- private class WrappedConnectivityService extends ConnectivityService {
- public WrappedMultinetworkPolicyTracker wrappedMultinetworkPolicyTracker;
- private MockableSystemProperties mSystemProperties;
-
- public WrappedConnectivityService(Context context, INetworkManagementService netManager,
- INetworkStatsService statsService, INetworkPolicyManager policyManager,
- IpConnectivityLog log, INetd netd, IDnsResolver dnsResolver) {
- super(context, netManager, statsService, policyManager, dnsResolver, log, netd);
- mNetd = netd;
- mLingerDelayMs = TEST_LINGER_DELAY_MS;
- }
-
- @Override
- protected MockableSystemProperties getSystemProperties() {
- // Minimal approach to overriding system properties: let most calls fall through to real
- // device values, and only override ones values that are important to this test.
- mSystemProperties = spy(new MockableSystemProperties());
- when(mSystemProperties.getInt("net.tcp.default_init_rwnd", 0)).thenReturn(0);
- when(mSystemProperties.getBoolean("ro.radio.noril", false)).thenReturn(false);
- return mSystemProperties;
- }
-
- @Override
- protected Tethering makeTethering() {
- return mock(Tethering.class);
- }
-
- @Override
- protected ProxyTracker makeProxyTracker() {
- return mock(ProxyTracker.class);
- }
-
- @Override
- protected int reserveNetId() {
- while (true) {
- final int netId = super.reserveNetId();
-
- // Don't overlap test NetIDs with real NetIDs as binding sockets to real networks
- // can have odd side-effects, like network validations succeeding.
- Context context = InstrumentationRegistry.getContext();
- final Network[] networks = ConnectivityManager.from(context).getAllNetworks();
- boolean overlaps = false;
- for (Network network : networks) {
- if (netId == network.netId) {
- overlaps = true;
- break;
- }
- }
- if (overlaps) continue;
-
- return netId;
- }
- }
-
- @Override
- protected boolean queryUserAccess(int uid, int netId) {
- return true;
- }
-
- public Nat464Xlat getNat464Xlat(MockNetworkAgent mna) {
- return getNetworkAgentInfoForNetwork(mna.getNetwork()).clatd;
- }
-
- @Override
- public MultinetworkPolicyTracker createMultinetworkPolicyTracker(
- Context c, Handler h, Runnable r) {
- final WrappedMultinetworkPolicyTracker tracker = new WrappedMultinetworkPolicyTracker(c, h, r);
- return tracker;
- }
-
- public WrappedMultinetworkPolicyTracker getMultinetworkPolicyTracker() {
- return (WrappedMultinetworkPolicyTracker) mMultinetworkPolicyTracker;
- }
-
- @Override
- protected NetworkStackClient getNetworkStack() {
- return mNetworkStack;
- }
-
- @Override
- public WakeupMessage makeWakeupMessage(
- Context context, Handler handler, String cmdName, int cmd, Object obj) {
- return new FakeWakeupMessage(context, handler, cmdName, cmd, 0, 0, obj);
- }
-
- @Override
- public boolean hasService(String name) {
- // Currenty, the only relevant service that ConnectivityService checks for is
- // ETHERNET_SERVICE.
- return Context.ETHERNET_SERVICE.equals(name);
- }
-
- @Override
- protected IpConnectivityMetrics.Logger metricsLogger() {
- return mMetricsService;
- }
-
- @Override
- protected void registerNetdEventCallback() {
- }
-
- public void mockVpn(int uid) {
- synchronized (mVpns) {
- int userId = UserHandle.getUserId(uid);
- mMockVpn = new MockVpn(userId);
- // This has no effect unless the VPN is actually connected, because things like
- // getActiveNetworkForUidInternal call getNetworkAgentInfoForNetId on the VPN
- // netId, and check if that network is actually connected.
- mVpns.put(userId, mMockVpn);
- }
- }
-
- public void waitForIdle(int timeoutMs) {
- HandlerUtilsKt.waitForIdle(mHandlerThread, timeoutMs);
- }
-
- public void waitForIdle() {
- waitForIdle(TIMEOUT_MS);
- }
-
- public void setUidRulesChanged(int uidRules) throws RemoteException {
- mPolicyListener.onUidRulesChanged(Process.myUid(), uidRules);
- }
-
- public void setRestrictBackgroundChanged(boolean restrictBackground)
- throws RemoteException {
- mPolicyListener.onRestrictBackgroundChanged(restrictBackground);
+ return mConfigMeteredMultipathPreference;
}
}
@@ -1296,13 +1004,22 @@
LocalServices.addService(
NetworkPolicyManagerInternal.class, mock(NetworkPolicyManagerInternal.class));
- mService = new WrappedConnectivityService(mServiceContext,
+ mAlarmManagerThread = new HandlerThread("TestAlarmManager");
+ mAlarmManagerThread.start();
+ initAlarmManager(mAlarmManager, mAlarmManagerThread.getThreadHandler());
+
+ mCsHandlerThread = new HandlerThread("TestConnectivityService");
+ final ConnectivityService.Dependencies deps = makeDependencies();
+ mService = new ConnectivityService(mServiceContext,
mNetworkManagementService,
mStatsService,
mNpm,
+ mMockDnsResolver,
mock(IpConnectivityLog.class),
mMockNetd,
- mMockDnsResolver);
+ deps);
+ mService.mLingerDelayMs = TEST_LINGER_DELAY_MS;
+ verify(deps).makeMultinetworkPolicyTracker(any(), any(), any());
final ArgumentCaptor<INetworkPolicyListener> policyListenerCaptor =
ArgumentCaptor.forClass(INetworkPolicyListener.class);
@@ -1313,7 +1030,7 @@
// getSystemService() correctly.
mCm = new WrappedConnectivityManager(InstrumentationRegistry.getContext(), mService);
mService.systemReady();
- mService.mockVpn(Process.myUid());
+ mockVpn(Process.myUid());
mCm.bindProcessToNetwork(null);
// Ensure that the default setting for Captive Portals is used for most tests
@@ -1322,6 +1039,57 @@
setPrivateDnsSettings(PRIVATE_DNS_MODE_OFF, "ignored.example.com");
}
+ private ConnectivityService.Dependencies makeDependencies() {
+ final MockableSystemProperties systemProperties = spy(new MockableSystemProperties());
+ when(systemProperties.getInt("net.tcp.default_init_rwnd", 0)).thenReturn(0);
+ when(systemProperties.getBoolean("ro.radio.noril", false)).thenReturn(false);
+
+ final ConnectivityService.Dependencies deps = mock(ConnectivityService.Dependencies.class);
+ doReturn(mCsHandlerThread).when(deps).makeHandlerThread();
+ doReturn(new TestNetIdManager()).when(deps).makeNetIdManager();
+ doReturn(mNetworkStack).when(deps).getNetworkStack();
+ doReturn(systemProperties).when(deps).getSystemProperties();
+ doReturn(mock(Tethering.class)).when(deps).makeTethering(any(), any(), any(), any(), any());
+ doReturn(mock(ProxyTracker.class)).when(deps).makeProxyTracker(any(), any());
+ doReturn(mMetricsService).when(deps).getMetricsLogger();
+ doReturn(true).when(deps).queryUserAccess(anyInt(), anyInt());
+ doReturn(mIpConnectivityMetrics).when(deps).getIpConnectivityMetrics();
+ doReturn(true).when(deps).hasService(Context.ETHERNET_SERVICE);
+ doAnswer(inv -> {
+ mPolicyTracker = new WrappedMultinetworkPolicyTracker(
+ inv.getArgument(0), inv.getArgument(1), inv.getArgument(2));
+ return mPolicyTracker;
+ }).when(deps).makeMultinetworkPolicyTracker(any(), any(), any());
+
+ return deps;
+ }
+
+ private static void initAlarmManager(final AlarmManager am, final Handler alarmHandler) {
+ doAnswer(inv -> {
+ final long when = inv.getArgument(1);
+ final WakeupMessage wakeupMsg = inv.getArgument(3);
+ final Handler handler = inv.getArgument(4);
+
+ long delayMs = when - SystemClock.elapsedRealtime();
+ if (delayMs < 0) delayMs = 0;
+ if (delayMs > UNREASONABLY_LONG_ALARM_WAIT_MS) {
+ fail("Attempting to send msg more than " + UNREASONABLY_LONG_ALARM_WAIT_MS
+ + "ms into the future: " + delayMs);
+ }
+ alarmHandler.postDelayed(() -> handler.post(wakeupMsg::onAlarm), wakeupMsg /* token */,
+ delayMs);
+
+ return null;
+ }).when(am).setExact(eq(AlarmManager.ELAPSED_REALTIME_WAKEUP), anyLong(), anyString(),
+ any(WakeupMessage.class), any());
+
+ doAnswer(inv -> {
+ final WakeupMessage wakeupMsg = inv.getArgument(0);
+ alarmHandler.removeCallbacksAndMessages(wakeupMsg /* token */);
+ return null;
+ }).when(am).cancel(any(WakeupMessage.class));
+ }
+
@After
public void tearDown() throws Exception {
setAlwaysOnNetworks(false);
@@ -1338,6 +1106,9 @@
mEthernetNetworkAgent = null;
}
FakeSettingsProvider.clearSettingsProvider();
+
+ mCsHandlerThread.quitSafely();
+ mAlarmManagerThread.quitSafely();
}
private void mockDefaultPackages() throws Exception {
@@ -1357,21 +1128,6 @@
}));
}
- private static int transportToLegacyType(int transport) {
- switch (transport) {
- case TRANSPORT_ETHERNET:
- return TYPE_ETHERNET;
- case TRANSPORT_WIFI:
- return TYPE_WIFI;
- case TRANSPORT_CELLULAR:
- return TYPE_MOBILE;
- case TRANSPORT_VPN:
- return TYPE_VPN;
- default:
- return TYPE_NONE;
- }
- }
-
private void verifyActiveNetwork(int transport) {
// Test getActiveNetworkInfo()
assertNotNull(mCm.getActiveNetworkInfo());
@@ -1449,8 +1205,8 @@
@Test
public void testLingering() throws Exception {
verifyNoNetwork();
- mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
assertNull(mCm.getActiveNetworkInfo());
assertNull(mCm.getActiveNetwork());
// Test bringing up validated cellular.
@@ -1474,7 +1230,7 @@
assertTrue(mCm.getAllNetworks()[0].equals(mCellNetworkAgent.getNetwork()) ||
mCm.getAllNetworks()[1].equals(mCellNetworkAgent.getNetwork()));
// Test cellular linger timeout.
- waitFor(mCellNetworkAgent.getDisconnectedCV());
+ mCellNetworkAgent.expectDisconnected();
waitForIdle();
assertLength(1, mCm.getAllNetworks());
verifyActiveNetwork(TRANSPORT_WIFI);
@@ -1490,13 +1246,13 @@
@Test
public void testValidatedCellularOutscoresUnvalidatedWiFi() throws Exception {
// Test bringing up unvalidated WiFi
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
ConditionVariable cv = waitForConnectivityBroadcasts(1);
mWiFiNetworkAgent.connect(false);
waitFor(cv);
verifyActiveNetwork(TRANSPORT_WIFI);
// Test bringing up unvalidated cellular
- mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(false);
waitForIdle();
verifyActiveNetwork(TRANSPORT_WIFI);
@@ -1505,7 +1261,7 @@
waitForIdle();
verifyActiveNetwork(TRANSPORT_WIFI);
// Test bringing up validated cellular
- mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
cv = waitForConnectivityBroadcasts(2);
mCellNetworkAgent.connect(true);
waitFor(cv);
@@ -1525,13 +1281,13 @@
@Test
public void testUnvalidatedWifiOutscoresUnvalidatedCellular() throws Exception {
// Test bringing up unvalidated cellular.
- mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
ConditionVariable cv = waitForConnectivityBroadcasts(1);
mCellNetworkAgent.connect(false);
waitFor(cv);
verifyActiveNetwork(TRANSPORT_CELLULAR);
// Test bringing up unvalidated WiFi.
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
cv = waitForConnectivityBroadcasts(2);
mWiFiNetworkAgent.connect(false);
waitFor(cv);
@@ -1551,7 +1307,7 @@
@Test
public void testUnlingeringDoesNotValidate() throws Exception {
// Test bringing up unvalidated WiFi.
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
ConditionVariable cv = waitForConnectivityBroadcasts(1);
mWiFiNetworkAgent.connect(false);
waitFor(cv);
@@ -1559,7 +1315,7 @@
assertFalse(mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()).hasCapability(
NET_CAPABILITY_VALIDATED));
// Test bringing up validated cellular.
- mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
cv = waitForConnectivityBroadcasts(2);
mCellNetworkAgent.connect(true);
waitFor(cv);
@@ -1579,13 +1335,13 @@
@Test
public void testCellularOutscoresWeakWifi() throws Exception {
// Test bringing up validated cellular.
- mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
ConditionVariable cv = waitForConnectivityBroadcasts(1);
mCellNetworkAgent.connect(true);
waitFor(cv);
verifyActiveNetwork(TRANSPORT_CELLULAR);
// Test bringing up validated WiFi.
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
cv = waitForConnectivityBroadcasts(2);
mWiFiNetworkAgent.connect(true);
waitFor(cv);
@@ -1606,45 +1362,41 @@
public void testReapingNetwork() throws Exception {
// Test bringing up WiFi without NET_CAPABILITY_INTERNET.
// Expect it to be torn down immediately because it satisfies no requests.
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
- ConditionVariable cv = mWiFiNetworkAgent.getDisconnectedCV();
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.connectWithoutInternet();
- waitFor(cv);
+ mWiFiNetworkAgent.expectDisconnected();
// Test bringing up cellular without NET_CAPABILITY_INTERNET.
// Expect it to be torn down immediately because it satisfies no requests.
- mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
- cv = mCellNetworkAgent.getDisconnectedCV();
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mCellNetworkAgent.connectWithoutInternet();
- waitFor(cv);
+ mCellNetworkAgent.expectDisconnected();
// Test bringing up validated WiFi.
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
- cv = waitForConnectivityBroadcasts(1);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+ final ConditionVariable cv = waitForConnectivityBroadcasts(1);
mWiFiNetworkAgent.connect(true);
waitFor(cv);
verifyActiveNetwork(TRANSPORT_WIFI);
// Test bringing up unvalidated cellular.
// Expect it to be torn down because it could never be the highest scoring network
// satisfying the default request even if it validated.
- mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
- cv = mCellNetworkAgent.getDisconnectedCV();
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(false);
- waitFor(cv);
+ mCellNetworkAgent.expectDisconnected();
verifyActiveNetwork(TRANSPORT_WIFI);
- cv = mWiFiNetworkAgent.getDisconnectedCV();
mWiFiNetworkAgent.disconnect();
- waitFor(cv);
+ mWiFiNetworkAgent.expectDisconnected();
}
@Test
public void testCellularFallback() throws Exception {
// Test bringing up validated cellular.
- mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
ConditionVariable cv = waitForConnectivityBroadcasts(1);
mCellNetworkAgent.connect(true);
waitFor(cv);
verifyActiveNetwork(TRANSPORT_CELLULAR);
// Test bringing up validated WiFi.
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
cv = waitForConnectivityBroadcasts(2);
mWiFiNetworkAgent.connect(true);
waitFor(cv);
@@ -1676,13 +1428,13 @@
@Test
public void testWiFiFallback() throws Exception {
// Test bringing up unvalidated WiFi.
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
ConditionVariable cv = waitForConnectivityBroadcasts(1);
mWiFiNetworkAgent.connect(false);
waitFor(cv);
verifyActiveNetwork(TRANSPORT_WIFI);
// Test bringing up validated cellular.
- mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
cv = waitForConnectivityBroadcasts(2);
mCellNetworkAgent.connect(true);
waitFor(cv);
@@ -1760,7 +1512,7 @@
// Test unvalidated networks
ConditionVariable cv = waitForConnectivityBroadcasts(1);
- mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(false);
genericNetworkCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
cellNetworkCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
@@ -1775,7 +1527,7 @@
assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
cv = waitForConnectivityBroadcasts(2);
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(false);
genericNetworkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
wifiNetworkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
@@ -1799,7 +1551,7 @@
assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback);
// Test validated networks
- mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(true);
genericNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
@@ -1812,7 +1564,7 @@
assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback);
assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(true);
genericNetworkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
genericNetworkCallback.expectCallback(CallbackRecord.LOSING, mCellNetworkAgent);
@@ -1850,9 +1602,9 @@
TestNetworkCallback defaultCallback = new TestNetworkCallback();
mCm.registerDefaultNetworkCallback(defaultCallback);
- mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
- mEthernetNetworkAgent = new MockNetworkAgent(TRANSPORT_ETHERNET);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+ mEthernetNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_ETHERNET);
mCellNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
@@ -1891,7 +1643,7 @@
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
for (int i = 0; i < 4; i++) {
- MockNetworkAgent oldNetwork, newNetwork;
+ TestNetworkAgentWrapper oldNetwork, newNetwork;
if (i % 2 == 0) {
mWiFiNetworkAgent.adjustScore(-15);
oldNetwork = mWiFiNetworkAgent;
@@ -1943,7 +1695,7 @@
mCm.registerNetworkCallback(request, callback);
- mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(false); // Score: 10
callback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
defaultCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
@@ -1952,7 +1704,7 @@
// Bring up wifi with a score of 20.
// Cell stays up because it would satisfy the default request if it validated.
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(false); // Score: 20
callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
defaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
@@ -1968,7 +1720,7 @@
// Bring up wifi with a score of 70.
// Cell is lingered because it would not satisfy any request, even if it validated.
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.adjustScore(50);
mWiFiNetworkAgent.connect(false); // Score: 70
callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
@@ -1987,7 +1739,7 @@
// Bring up wifi, then validate it. Previous versions would immediately tear down cell, but
// it's arguably correct to linger it, since it was the default network before it validated.
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(true);
callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
// TODO: Investigate sending validated before losing.
@@ -2008,12 +1760,12 @@
assertEquals(null, mCm.getActiveNetwork());
// If a network is lingering, and we add and remove a request from it, resume lingering.
- mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(true);
callback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
defaultCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(true);
defaultCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
@@ -2044,7 +1796,7 @@
mCm.requestNetwork(cellRequest, noopCallback);
// Now connect wifi, and expect it to become the default network.
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(true);
callback.expectAvailableThenValidatedCallbacks(mWiFiNetworkAgent);
defaultCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
@@ -2065,7 +1817,7 @@
TestNetworkCallback trackDefaultCallback = new TestNetworkCallback();
mCm.registerDefaultNetworkCallback(trackDefaultCallback);
trackDefaultCallback.expectAvailableCallbacksValidated(mWiFiNetworkAgent);
- mEthernetNetworkAgent = new MockNetworkAgent(TRANSPORT_ETHERNET);
+ mEthernetNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_ETHERNET);
mEthernetNetworkAgent.connect(true);
callback.expectAvailableCallbacksUnvalidated(mEthernetNetworkAgent);
callback.expectCallback(CallbackRecord.LOSING, mWiFiNetworkAgent);
@@ -2100,8 +1852,8 @@
TestNetworkCallback defaultCallback = new TestNetworkCallback();
mCm.registerDefaultNetworkCallback(defaultCallback);
- mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mCellNetworkAgent.connect(true);
callback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
@@ -2142,12 +1894,12 @@
mCm.registerNetworkCallback(request, callback);
// Bring up validated cell.
- mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(true);
callback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
// Bring up unvalidated wifi with explicitlySelected=true.
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.explicitlySelected(true, false);
mWiFiNetworkAgent.connect(false);
callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
@@ -2170,7 +1922,7 @@
// Disconnect wifi, and then reconnect, again with explicitlySelected=true.
mWiFiNetworkAgent.disconnect();
callback.expectCallback(CallbackRecord.LOST, mWiFiNetworkAgent);
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.explicitlySelected(true, false);
mWiFiNetworkAgent.connect(false);
callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
@@ -2181,7 +1933,7 @@
callback.expectCallback(CallbackRecord.LOST, mWiFiNetworkAgent);
// Reconnect, again with explicitlySelected=true, but this time validate.
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.explicitlySelected(true, false);
mWiFiNetworkAgent.connect(true);
callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
@@ -2191,7 +1943,7 @@
// BUG: the network will no longer linger, even though it's validated and outscored.
// TODO: fix this.
- mEthernetNetworkAgent = new MockNetworkAgent(TRANSPORT_ETHERNET);
+ mEthernetNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_ETHERNET);
mEthernetNetworkAgent.connect(true);
callback.expectAvailableThenValidatedCallbacks(mEthernetNetworkAgent);
assertEquals(mEthernetNetworkAgent.getNetwork(), mCm.getActiveNetwork());
@@ -2202,7 +1954,7 @@
// wifi immediately.
mWiFiNetworkAgent.disconnect();
callback.expectCallback(CallbackRecord.LOST, mWiFiNetworkAgent);
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.explicitlySelected(true, true);
mWiFiNetworkAgent.connect(false);
callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
@@ -2215,7 +1967,7 @@
// Check that the network is not scored specially and that the device prefers cell data.
mWiFiNetworkAgent.disconnect();
callback.expectCallback(CallbackRecord.LOST, mWiFiNetworkAgent);
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.explicitlySelected(false, true);
mWiFiNetworkAgent.connect(false);
callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
@@ -2277,7 +2029,7 @@
assertTrue(testFactory.getMyStartRequested());
// Now bring in a higher scored network.
- MockNetworkAgent testAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ TestNetworkAgentWrapper testAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
// Rather than create a validated network which complicates things by registering it's
// own NetworkRequest during startup, just bump up the score to cancel out the
// unvalidated penalty.
@@ -2371,18 +2123,17 @@
@Test
public void testMMSonWiFi() throws Exception {
// Test bringing up cellular without MMS NetworkRequest gets reaped
- mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
mCellNetworkAgent.addCapability(NET_CAPABILITY_MMS);
- ConditionVariable cv = mCellNetworkAgent.getDisconnectedCV();
mCellNetworkAgent.connectWithoutInternet();
- waitFor(cv);
+ mCellNetworkAgent.expectDisconnected();
waitForIdle();
assertEmpty(mCm.getAllNetworks());
verifyNoNetwork();
// Test bringing up validated WiFi.
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
- cv = waitForConnectivityBroadcasts(1);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+ final ConditionVariable cv = waitForConnectivityBroadcasts(1);
mWiFiNetworkAgent.connect(true);
waitFor(cv);
verifyActiveNetwork(TRANSPORT_WIFI);
@@ -2394,23 +2145,22 @@
mCm.requestNetwork(builder.build(), networkCallback);
// Test bringing up unvalidated cellular with MMS
- mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
mCellNetworkAgent.addCapability(NET_CAPABILITY_MMS);
mCellNetworkAgent.connectWithoutInternet();
networkCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
verifyActiveNetwork(TRANSPORT_WIFI);
// Test releasing NetworkRequest disconnects cellular with MMS
- cv = mCellNetworkAgent.getDisconnectedCV();
mCm.unregisterNetworkCallback(networkCallback);
- waitFor(cv);
+ mCellNetworkAgent.expectDisconnected();
verifyActiveNetwork(TRANSPORT_WIFI);
}
@Test
public void testMMSonCell() throws Exception {
// Test bringing up cellular without MMS
- mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
ConditionVariable cv = waitForConnectivityBroadcasts(1);
mCellNetworkAgent.connect(false);
waitFor(cv);
@@ -2423,16 +2173,16 @@
mCm.requestNetwork(builder.build(), networkCallback);
// Test bringing up MMS cellular network
- MockNetworkAgent mmsNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ TestNetworkAgentWrapper
+ mmsNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
mmsNetworkAgent.addCapability(NET_CAPABILITY_MMS);
mmsNetworkAgent.connectWithoutInternet();
networkCallback.expectAvailableCallbacksUnvalidated(mmsNetworkAgent);
verifyActiveNetwork(TRANSPORT_CELLULAR);
// Test releasing MMS NetworkRequest does not disconnect main cellular NetworkAgent
- cv = mmsNetworkAgent.getDisconnectedCV();
mCm.unregisterNetworkCallback(networkCallback);
- waitFor(cv);
+ mmsNetworkAgent.expectDisconnected();
verifyActiveNetwork(TRANSPORT_CELLULAR);
}
@@ -2446,12 +2196,12 @@
mCm.registerNetworkCallback(request, callback);
// Bring up validated mobile data.
- mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(true);
callback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
// Bring up wifi with partial connectivity.
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.connectWithPartialConnectivity();
callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
callback.expectCapabilitiesWith(NET_CAPABILITY_PARTIAL_CONNECTIVITY, mWiFiNetworkAgent);
@@ -2484,7 +2234,7 @@
// Disconnect and reconnect wifi with partial connectivity again.
mWiFiNetworkAgent.disconnect();
callback.expectCallback(CallbackRecord.LOST, mWiFiNetworkAgent);
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.connectWithPartialConnectivity();
callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
callback.expectCapabilitiesWith(NET_CAPABILITY_PARTIAL_CONNECTIVITY, mWiFiNetworkAgent);
@@ -2500,7 +2250,7 @@
// If user accepted partial connectivity before, and device reconnects to that network
// again, but now the network has full connectivity. The network shouldn't contain
// NET_CAPABILITY_PARTIAL_CONNECTIVITY.
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
// acceptUnvalidated is also used as setting for accepting partial networks.
mWiFiNetworkAgent.explicitlySelected(true /* explicitlySelected */,
true /* acceptUnvalidated */);
@@ -2524,7 +2274,7 @@
// The user accepted partial connectivity and selected "don't ask again". Now the user
// reconnects to the partial connectivity network. Switch to wifi as soon as partial
// connectivity is detected.
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.explicitlySelected(true /* explicitlySelected */,
true /* acceptUnvalidated */);
mWiFiNetworkAgent.connectWithPartialConnectivity();
@@ -2548,7 +2298,7 @@
// If the user accepted partial connectivity, and the device auto-reconnects to the partial
// connectivity network, it should contain both PARTIAL_CONNECTIVITY and VALIDATED.
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.explicitlySelected(false /* explicitlySelected */,
true /* acceptUnvalidated */);
@@ -2580,7 +2330,7 @@
// Bring up a network with a captive portal.
// Expect onAvailable callback of listen for NET_CAPABILITY_CAPTIVE_PORTAL.
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
String redirectUrl = "http://android.com/path";
mWiFiNetworkAgent.connectWithCaptivePortal(redirectUrl);
captivePortalCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
@@ -2629,7 +2379,7 @@
// Bring up a network with a captive portal.
// Expect onAvailable callback of listen for NET_CAPABILITY_CAPTIVE_PORTAL.
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
String firstRedirectUrl = "http://example.com/firstPath";
mWiFiNetworkAgent.connectWithCaptivePortal(firstRedirectUrl);
captivePortalCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
@@ -2642,7 +2392,7 @@
// Bring up a network with a captive portal.
// Expect onAvailable callback of listen for NET_CAPABILITY_CAPTIVE_PORTAL.
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
String secondRedirectUrl = "http://example.com/secondPath";
mWiFiNetworkAgent.connectWithCaptivePortal(secondRedirectUrl);
captivePortalCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
@@ -2680,7 +2430,7 @@
mCm.registerNetworkCallback(validatedRequest, validatedCallback);
// Bring up wifi.
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(true);
validatedCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
Network wifiNetwork = mWiFiNetworkAgent.getNetwork();
@@ -2740,14 +2490,12 @@
setCaptivePortalMode(Settings.Global.CAPTIVE_PORTAL_MODE_AVOID);
// Bring up a network with a captive portal.
// Expect it to fail to connect and not result in any callbacks.
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
String firstRedirectUrl = "http://example.com/firstPath";
- ConditionVariable disconnectCv = mWiFiNetworkAgent.getDisconnectedCV();
- ConditionVariable avoidCv = mWiFiNetworkAgent.getPreventReconnectReceived();
mWiFiNetworkAgent.connectWithCaptivePortal(firstRedirectUrl);
- waitFor(disconnectCv);
- waitFor(avoidCv);
+ mWiFiNetworkAgent.expectDisconnected();
+ mWiFiNetworkAgent.expectPreventReconnectReceived();
assertNoCallbacks(captivePortalCallback, validatedCallback);
}
@@ -2846,7 +2594,7 @@
LocalStringNetworkSpecifier nsFoo = new LocalStringNetworkSpecifier("foo");
LocalStringNetworkSpecifier nsBar = new LocalStringNetworkSpecifier("bar");
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(false);
cEmpty1.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
cEmpty2.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
@@ -2973,7 +2721,7 @@
public void writeToParcel(Parcel dest, int flags) {}
}
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(false);
UidAwareNetworkSpecifier networkSpecifier = new UidAwareNetworkSpecifier();
@@ -3026,14 +2774,14 @@
cellNetworkCallback.assertNoCallback();
// Bring up cell and expect CALLBACK_AVAILABLE.
- mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(true);
cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
defaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
assertEquals(defaultNetworkCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
// Bring up wifi and expect CALLBACK_AVAILABLE.
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(true);
cellNetworkCallback.assertNoCallback();
defaultNetworkCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
@@ -3046,7 +2794,7 @@
assertEquals(defaultNetworkCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
// Bring up cell. Expect no default network callback, since it won't be the default.
- mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(true);
cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
defaultNetworkCallback.assertNoCallback();
@@ -3065,7 +2813,8 @@
assertEquals(null, mCm.getActiveNetwork());
final int uid = Process.myUid();
- final MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN);
+ final TestNetworkAgentWrapper
+ vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN);
final ArraySet<UidRange> ranges = new ArraySet<>();
ranges.add(new UidRange(uid, uid));
mMockVpn.setNetworkAgent(vpnNetworkAgent);
@@ -3090,7 +2839,7 @@
mCm.requestNetwork(cellRequest, cellNetworkCallback);
// Bring up the mobile network.
- mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(true);
// We should get onAvailable(), onCapabilitiesChanged(), and
@@ -3160,7 +2909,7 @@
waitForIdle();
}
- private boolean isForegroundNetwork(MockNetworkAgent network) {
+ private boolean isForegroundNetwork(TestNetworkAgentWrapper network) {
NetworkCapabilities nc = mCm.getNetworkCapabilities(network.getNetwork());
assertNotNull(nc);
return nc.hasCapability(NET_CAPABILITY_FOREGROUND);
@@ -3179,13 +2928,13 @@
mCm.registerNetworkCallback(request, callback);
mCm.registerNetworkCallback(fgRequest, fgCallback);
- mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(true);
callback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
fgCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
assertTrue(isForegroundNetwork(mCellNetworkAgent));
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(true);
// When wifi connects, cell lingers.
@@ -3278,7 +3027,7 @@
}
});
- mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
// Don't request that the network validate, because otherwise connect() will block until
// the network gets NET_CAPABILITY_VALIDATED, after all the callbacks below have fired,
// and we won't actually measure anything.
@@ -3295,7 +3044,7 @@
onAvailableDispatchingDuration <= CONNECT_TIME_LIMIT_MS);
// Give wifi a high enough score that we'll linger cell when wifi comes up.
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.adjustScore(40);
mWiFiNetworkAgent.connect(false);
@@ -3338,7 +3087,7 @@
assertTrue(testFactory.getMyStartRequested());
// Bring up wifi. The factory stops looking for a network.
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
// Score 60 - 40 penalty for not validated yet, then 60 when it validates
testFactory.expectAddRequestsWithScores(20, 60);
mWiFiNetworkAgent.connect(true);
@@ -3355,7 +3104,7 @@
// Bring up cell data and check that the factory stops looking.
assertLength(1, mCm.getAllNetworks());
- mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
testFactory.expectAddRequestsWithScores(10, 50); // Unvalidated, then validated
mCellNetworkAgent.connect(true);
cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
@@ -3384,48 +3133,46 @@
@Test
public void testAvoidBadWifiSetting() throws Exception {
final ContentResolver cr = mServiceContext.getContentResolver();
- final WrappedMultinetworkPolicyTracker tracker = mService.getMultinetworkPolicyTracker();
final String settingName = Settings.Global.NETWORK_AVOID_BAD_WIFI;
- tracker.configRestrictsAvoidBadWifi = false;
+ mPolicyTracker.mConfigRestrictsAvoidBadWifi = false;
String[] values = new String[] {null, "0", "1"};
for (int i = 0; i < values.length; i++) {
Settings.Global.putInt(cr, settingName, 1);
- tracker.reevaluate();
+ mPolicyTracker.reevaluate();
waitForIdle();
String msg = String.format("config=false, setting=%s", values[i]);
assertTrue(mService.avoidBadWifi());
- assertFalse(msg, tracker.shouldNotifyWifiUnvalidated());
+ assertFalse(msg, mPolicyTracker.shouldNotifyWifiUnvalidated());
}
- tracker.configRestrictsAvoidBadWifi = true;
+ mPolicyTracker.mConfigRestrictsAvoidBadWifi = true;
Settings.Global.putInt(cr, settingName, 0);
- tracker.reevaluate();
+ mPolicyTracker.reevaluate();
waitForIdle();
assertFalse(mService.avoidBadWifi());
- assertFalse(tracker.shouldNotifyWifiUnvalidated());
+ assertFalse(mPolicyTracker.shouldNotifyWifiUnvalidated());
Settings.Global.putInt(cr, settingName, 1);
- tracker.reevaluate();
+ mPolicyTracker.reevaluate();
waitForIdle();
assertTrue(mService.avoidBadWifi());
- assertFalse(tracker.shouldNotifyWifiUnvalidated());
+ assertFalse(mPolicyTracker.shouldNotifyWifiUnvalidated());
Settings.Global.putString(cr, settingName, null);
- tracker.reevaluate();
+ mPolicyTracker.reevaluate();
waitForIdle();
assertFalse(mService.avoidBadWifi());
- assertTrue(tracker.shouldNotifyWifiUnvalidated());
+ assertTrue(mPolicyTracker.shouldNotifyWifiUnvalidated());
}
@Test
public void testAvoidBadWifi() throws Exception {
final ContentResolver cr = mServiceContext.getContentResolver();
- final WrappedMultinetworkPolicyTracker tracker = mService.getMultinetworkPolicyTracker();
// Pretend we're on a carrier that restricts switching away from bad wifi.
- tracker.configRestrictsAvoidBadWifi = true;
+ mPolicyTracker.mConfigRestrictsAvoidBadWifi = true;
// File a request for cell to ensure it doesn't go down.
final TestNetworkCallback cellNetworkCallback = new TestNetworkCallback();
@@ -3444,17 +3191,17 @@
mCm.registerNetworkCallback(validatedWifiRequest, validatedWifiCallback);
Settings.Global.putInt(cr, Settings.Global.NETWORK_AVOID_BAD_WIFI, 0);
- tracker.reevaluate();
+ mPolicyTracker.reevaluate();
// Bring up validated cell.
- mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(true);
cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
defaultCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
Network cellNetwork = mCellNetworkAgent.getNetwork();
// Bring up validated wifi.
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(true);
defaultCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
validatedWifiCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
@@ -3476,14 +3223,14 @@
// Simulate switching to a carrier that does not restrict avoiding bad wifi, and expect
// that we switch back to cell.
- tracker.configRestrictsAvoidBadWifi = false;
- tracker.reevaluate();
+ mPolicyTracker.mConfigRestrictsAvoidBadWifi = false;
+ mPolicyTracker.reevaluate();
defaultCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
assertEquals(mCm.getActiveNetwork(), cellNetwork);
// Switch back to a restrictive carrier.
- tracker.configRestrictsAvoidBadWifi = true;
- tracker.reevaluate();
+ mPolicyTracker.mConfigRestrictsAvoidBadWifi = true;
+ mPolicyTracker.reevaluate();
defaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
assertEquals(mCm.getActiveNetwork(), wifiNetwork);
@@ -3498,7 +3245,7 @@
// Disconnect and reconnect wifi to clear the one-time switch above.
mWiFiNetworkAgent.disconnect();
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(true);
defaultCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
validatedWifiCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
@@ -3512,7 +3259,7 @@
// Simulate the user selecting "switch" and checking the don't ask again checkbox.
Settings.Global.putInt(cr, Settings.Global.NETWORK_AVOID_BAD_WIFI, 1);
- tracker.reevaluate();
+ mPolicyTracker.reevaluate();
// We now switch to cell.
defaultCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
@@ -3525,11 +3272,11 @@
// Simulate the user turning the cellular fallback setting off and then on.
// We switch to wifi and then to cell.
Settings.Global.putString(cr, Settings.Global.NETWORK_AVOID_BAD_WIFI, null);
- tracker.reevaluate();
+ mPolicyTracker.reevaluate();
defaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
assertEquals(mCm.getActiveNetwork(), wifiNetwork);
Settings.Global.putInt(cr, Settings.Global.NETWORK_AVOID_BAD_WIFI, 1);
- tracker.reevaluate();
+ mPolicyTracker.reevaluate();
defaultCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
assertEquals(mCm.getActiveNetwork(), cellNetwork);
@@ -3547,14 +3294,13 @@
@Test
public void testMeteredMultipathPreferenceSetting() throws Exception {
final ContentResolver cr = mServiceContext.getContentResolver();
- final WrappedMultinetworkPolicyTracker tracker = mService.getMultinetworkPolicyTracker();
final String settingName = Settings.Global.NETWORK_METERED_MULTIPATH_PREFERENCE;
for (int config : Arrays.asList(0, 3, 2)) {
for (String setting: Arrays.asList(null, "0", "2", "1")) {
- tracker.configMeteredMultipathPreference = config;
+ mPolicyTracker.mConfigMeteredMultipathPreference = config;
Settings.Global.putString(cr, settingName, setting);
- tracker.reevaluate();
+ mPolicyTracker.reevaluate();
waitForIdle();
final int expected = (setting != null) ? Integer.parseInt(setting) : config;
@@ -3575,7 +3321,7 @@
final TestNetworkCallback networkCallback = new TestNetworkCallback();
mCm.requestNetwork(nr, networkCallback, TEST_REQUEST_TIMEOUT_MS);
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(false);
networkCallback.expectAvailableCallbacks(mWiFiNetworkAgent, false, false, false,
TEST_CALLBACK_TIMEOUT_MS);
@@ -3595,7 +3341,7 @@
final TestNetworkCallback networkCallback = new TestNetworkCallback();
mCm.requestNetwork(nr, networkCallback, TEST_REQUEST_TIMEOUT_MS);
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(false);
networkCallback.expectAvailableCallbacks(mWiFiNetworkAgent, false, false, false,
TEST_CALLBACK_TIMEOUT_MS);
@@ -3623,7 +3369,7 @@
networkCallback.expectCallback(CallbackRecord.UNAVAILABLE, null);
// create a network satisfying request - validate that request not triggered
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(false);
networkCallback.assertNoCallback();
}
@@ -3646,7 +3392,7 @@
networkCallback.assertNoCallback();
// create a network satisfying request - validate that request not triggered
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(false);
networkCallback.assertNoCallback();
}
@@ -3876,7 +3622,7 @@
assertNull(mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()));
}
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
ConditionVariable cv = waitForConnectivityBroadcasts(1);
mWiFiNetworkAgent.connect(true);
waitFor(cv);
@@ -3940,10 +3686,10 @@
callback.expectError(PacketKeepalive.ERROR_HARDWARE_UNSUPPORTED);
// Check that a started keepalive can be stopped.
- mWiFiNetworkAgent.setStartKeepaliveError(PacketKeepalive.SUCCESS);
+ mWiFiNetworkAgent.setStartKeepaliveEvent(PacketKeepalive.SUCCESS);
ka = mCm.startNattKeepalive(myNet, validKaInterval, callback, myIPv4, 12345, dstIPv4);
callback.expectStarted();
- mWiFiNetworkAgent.setStopKeepaliveError(PacketKeepalive.SUCCESS);
+ mWiFiNetworkAgent.setStopKeepaliveEvent(PacketKeepalive.SUCCESS);
ka.stop();
callback.expectStopped();
@@ -3961,7 +3707,7 @@
ka = mCm.startNattKeepalive(myNet, validKaInterval, callback, myIPv4, 12345, dstIPv4);
callback.expectStarted();
mWiFiNetworkAgent.disconnect();
- waitFor(mWiFiNetworkAgent.getDisconnectedCV());
+ mWiFiNetworkAgent.expectDisconnected();
callback.expectError(PacketKeepalive.ERROR_INVALID_NETWORK);
// ... and that stopping it after that has no adverse effects.
@@ -3972,7 +3718,7 @@
// Reconnect.
myNet = connectKeepaliveNetwork(lp);
- mWiFiNetworkAgent.setStartKeepaliveError(PacketKeepalive.SUCCESS);
+ mWiFiNetworkAgent.setStartKeepaliveEvent(PacketKeepalive.SUCCESS);
// Check that keepalive slots start from 1 and increment. The first one gets slot 1.
mWiFiNetworkAgent.setExpectedKeepaliveSlot(1);
@@ -4092,12 +3838,12 @@
}
// Check that a started keepalive can be stopped.
- mWiFiNetworkAgent.setStartKeepaliveError(SocketKeepalive.SUCCESS);
+ mWiFiNetworkAgent.setStartKeepaliveEvent(SocketKeepalive.SUCCESS);
try (SocketKeepalive ka = mCm.createSocketKeepalive(
myNet, testSocket, myIPv4, dstIPv4, executor, callback)) {
ka.start(validKaInterval);
callback.expectStarted();
- mWiFiNetworkAgent.setStopKeepaliveError(SocketKeepalive.SUCCESS);
+ mWiFiNetworkAgent.setStopKeepaliveEvent(SocketKeepalive.SUCCESS);
ka.stop();
callback.expectStopped();
@@ -4137,7 +3883,7 @@
ka.start(validKaInterval);
callback.expectStarted();
mWiFiNetworkAgent.disconnect();
- waitFor(mWiFiNetworkAgent.getDisconnectedCV());
+ mWiFiNetworkAgent.expectDisconnected();
callback.expectError(SocketKeepalive.ERROR_INVALID_NETWORK);
// ... and that stopping it after that has no adverse effects.
@@ -4150,7 +3896,7 @@
// Reconnect.
myNet = connectKeepaliveNetwork(lp);
- mWiFiNetworkAgent.setStartKeepaliveError(SocketKeepalive.SUCCESS);
+ mWiFiNetworkAgent.setStartKeepaliveEvent(SocketKeepalive.SUCCESS);
// Check that keepalive slots start from 1 and increment. The first one gets slot 1.
mWiFiNetworkAgent.setExpectedKeepaliveSlot(1);
@@ -4187,7 +3933,7 @@
// assertFalse(isUdpPortInUse(srcPort2));
mWiFiNetworkAgent.disconnect();
- waitFor(mWiFiNetworkAgent.getDisconnectedCV());
+ mWiFiNetworkAgent.expectDisconnected();
mWiFiNetworkAgent = null;
}
@@ -4263,7 +4009,7 @@
testSocketV6.close();
mWiFiNetworkAgent.disconnect();
- waitFor(mWiFiNetworkAgent.getDisconnectedCV());
+ mWiFiNetworkAgent.expectDisconnected();
mWiFiNetworkAgent = null;
}
@@ -4279,8 +4025,8 @@
lp.addLinkAddress(new LinkAddress(myIPv4, 25));
lp.addRoute(new RouteInfo(InetAddress.getByName("192.0.2.254")));
Network myNet = connectKeepaliveNetwork(lp);
- mWiFiNetworkAgent.setStartKeepaliveError(SocketKeepalive.SUCCESS);
- mWiFiNetworkAgent.setStopKeepaliveError(SocketKeepalive.SUCCESS);
+ mWiFiNetworkAgent.setStartKeepaliveEvent(SocketKeepalive.SUCCESS);
+ mWiFiNetworkAgent.setStopKeepaliveEvent(SocketKeepalive.SUCCESS);
TestSocketKeepaliveCallback callback = new TestSocketKeepaliveCallback(executor);
@@ -4316,7 +4062,7 @@
// assertFalse(isUdpPortInUse(srcPort));
mWiFiNetworkAgent.disconnect();
- waitFor(mWiFiNetworkAgent.getDisconnectedCV());
+ mWiFiNetworkAgent.expectDisconnected();
mWiFiNetworkAgent = null;
}
@@ -4379,9 +4125,9 @@
TestNetworkPinner.pin(mServiceContext, wifiRequest);
assertNull(mCm.getBoundNetworkForProcess());
- mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(true);
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(false);
// When wi-fi connects, expect to be pinned.
@@ -4394,7 +4140,7 @@
assertNotPinnedToWifi();
// Reconnecting does not cause the pin to come back.
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(false);
assertFalse(TestNetworkPinner.awaitPin(100));
assertNotPinnedToWifi();
@@ -4416,14 +4162,14 @@
// Pinning takes effect even if the pinned network is the default when the pin is set...
TestNetworkPinner.pin(mServiceContext, wifiRequest);
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(false);
assertTrue(TestNetworkPinner.awaitPin(100));
assertPinnedToWifiWithWifiDefault();
// ... and is maintained even when that network is no longer the default.
cv = waitForConnectivityBroadcasts(1);
- mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mCellNetworkAgent.connect(true);
waitFor(cv);
assertPinnedToWifiWithCellDefault();
@@ -4526,7 +4272,7 @@
ConditionVariable broadcastCV = waitForConnectivityBroadcasts(1);
verifyNoNetwork();
- MockNetworkAgent wifiAware = new MockNetworkAgent(TRANSPORT_WIFI_AWARE);
+ TestNetworkAgentWrapper wifiAware = new TestNetworkAgentWrapper(TRANSPORT_WIFI_AWARE);
assertNull(mCm.getActiveNetworkInfo());
Network[] allNetworks = mCm.getAllNetworks();
@@ -4599,7 +4345,7 @@
// Verify direct routes are added when network agent is first registered in
// ConnectivityService.
- MockNetworkAgent networkAgent = new MockNetworkAgent(TRANSPORT_WIFI, lp);
+ TestNetworkAgentWrapper networkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, lp);
networkAgent.connect(true);
networkCallback.expectCallback(CallbackRecord.AVAILABLE, networkAgent);
networkCallback.expectCallback(CallbackRecord.NETWORK_CAPS_UPDATED, networkAgent);
@@ -4631,8 +4377,8 @@
@Test
public void testStatsIfacesChanged() throws Exception {
- mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
Network[] onlyCell = new Network[] {mCellNetworkAgent.getNetwork()};
Network[] onlyWifi = new Network[] {mWiFiNetworkAgent.getNetwork()};
@@ -4708,7 +4454,7 @@
// Clear any interactions that occur as a result of CS starting up.
reset(mMockDnsResolver);
- mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
waitForIdle();
verify(mMockDnsResolver, never()).setResolverConfiguration(any());
verifyNoMoreInteractions(mMockDnsResolver);
@@ -4791,7 +4537,7 @@
.addTransportType(TRANSPORT_CELLULAR).build();
mCm.requestNetwork(cellRequest, cellNetworkCallback);
- mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
waitForIdle();
// CS tells netd about the empty DNS config for this network.
verify(mMockDnsResolver, never()).setResolverConfiguration(any());
@@ -4880,7 +4626,7 @@
.addTransportType(TRANSPORT_CELLULAR).build();
mCm.requestNetwork(cellRequest, cellNetworkCallback);
- mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
waitForIdle();
LinkProperties lp = new LinkProperties();
mCellNetworkAgent.sendLinkProperties(lp);
@@ -5015,7 +4761,7 @@
mCm.registerDefaultNetworkCallback(defaultCallback);
defaultCallback.assertNoCallback();
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(false);
genericNetworkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
@@ -5025,14 +4771,16 @@
vpnNetworkCallback.assertNoCallback();
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
- final MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN);
+ final TestNetworkAgentWrapper
+ vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN);
final ArraySet<UidRange> ranges = new ArraySet<>();
ranges.add(new UidRange(uid, uid));
mMockVpn.setNetworkAgent(vpnNetworkAgent);
mMockVpn.setUids(ranges);
// VPN networks do not satisfy the default request and are automatically validated
// by NetworkMonitor
- assertFalse(NetworkMonitorUtils.isValidationRequired(vpnNetworkAgent.mNetworkCapabilities));
+ assertFalse(NetworkMonitorUtils.isValidationRequired(
+ vpnNetworkAgent.getNetworkCapabilities()));
vpnNetworkAgent.setNetworkValid();
vpnNetworkAgent.connect(false);
@@ -5110,13 +4858,14 @@
final TestNetworkCallback defaultCallback = new TestNetworkCallback();
mCm.registerDefaultNetworkCallback(defaultCallback);
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(true);
defaultCallback.expectAvailableThenValidatedCallbacks(mWiFiNetworkAgent);
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
- MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN);
+ TestNetworkAgentWrapper
+ vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN);
final ArraySet<UidRange> ranges = new ArraySet<>();
ranges.add(new UidRange(uid, uid));
mMockVpn.setNetworkAgent(vpnNetworkAgent);
@@ -5140,13 +4889,14 @@
final TestNetworkCallback defaultCallback = new TestNetworkCallback();
mCm.registerDefaultNetworkCallback(defaultCallback);
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(true);
defaultCallback.expectAvailableThenValidatedCallbacks(mWiFiNetworkAgent);
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
- MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN);
+ TestNetworkAgentWrapper
+ vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN);
final ArraySet<UidRange> ranges = new ArraySet<>();
ranges.add(new UidRange(uid, uid));
mMockVpn.setNetworkAgent(vpnNetworkAgent);
@@ -5170,14 +4920,15 @@
mCm.registerDefaultNetworkCallback(callback);
// Bring up Ethernet.
- mEthernetNetworkAgent = new MockNetworkAgent(TRANSPORT_ETHERNET);
+ mEthernetNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_ETHERNET);
mEthernetNetworkAgent.connect(true);
callback.expectAvailableThenValidatedCallbacks(mEthernetNetworkAgent);
callback.assertNoCallback();
// Bring up a VPN that has the INTERNET capability, initially unvalidated.
final int uid = Process.myUid();
- final MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN);
+ final TestNetworkAgentWrapper
+ vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN);
final ArraySet<UidRange> ranges = new ArraySet<>();
ranges.add(new UidRange(uid, uid));
mMockVpn.setNetworkAgent(vpnNetworkAgent);
@@ -5199,9 +4950,10 @@
assertFalse(nc.hasCapability(NET_CAPABILITY_VALIDATED));
assertTrue(nc.hasCapability(NET_CAPABILITY_INTERNET));
- assertFalse(NetworkMonitorUtils.isValidationRequired(vpnNetworkAgent.mNetworkCapabilities));
+ assertFalse(NetworkMonitorUtils.isValidationRequired(
+ vpnNetworkAgent.getNetworkCapabilities()));
assertTrue(NetworkMonitorUtils.isPrivateDnsValidationRequired(
- vpnNetworkAgent.mNetworkCapabilities));
+ vpnNetworkAgent.getNetworkCapabilities()));
// Pretend that the VPN network validates.
vpnNetworkAgent.setNetworkValid();
@@ -5229,7 +4981,8 @@
mCm.registerNetworkCallback(vpnNetworkRequest, vpnNetworkCallback);
vpnNetworkCallback.assertNoCallback();
- final MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN);
+ final TestNetworkAgentWrapper
+ vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN);
final ArraySet<UidRange> ranges = new ArraySet<>();
ranges.add(new UidRange(uid, uid));
mMockVpn.setNetworkAgent(vpnNetworkAgent);
@@ -5246,7 +4999,7 @@
assertFalse(nc.hasCapability(NET_CAPABILITY_NOT_METERED));
// Connect cell and use it as an underlying network.
- mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(true);
mService.setUnderlyingNetworksForVpn(
@@ -5257,7 +5010,7 @@
&& caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI)
&& !caps.hasCapability(NET_CAPABILITY_NOT_METERED));
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
mWiFiNetworkAgent.connect(true);
@@ -5327,7 +5080,8 @@
mCm.registerNetworkCallback(vpnNetworkRequest, vpnNetworkCallback);
vpnNetworkCallback.assertNoCallback();
- final MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN);
+ final TestNetworkAgentWrapper
+ vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN);
final ArraySet<UidRange> ranges = new ArraySet<>();
ranges.add(new UidRange(uid, uid));
mMockVpn.setNetworkAgent(vpnNetworkAgent);
@@ -5345,7 +5099,7 @@
assertFalse(nc.hasCapability(NET_CAPABILITY_NOT_METERED));
// Connect to Cell; Cell is the default network.
- mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(true);
vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent,
@@ -5354,7 +5108,7 @@
&& !caps.hasCapability(NET_CAPABILITY_NOT_METERED));
// Connect to WiFi; WiFi is the new default.
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
mWiFiNetworkAgent.connect(true);
@@ -5382,7 +5136,7 @@
public void testIsActiveNetworkMeteredOverWifi() throws Exception {
// Returns true by default when no network is available.
assertTrue(mCm.isActiveNetworkMetered());
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
mWiFiNetworkAgent.connect(true);
waitForIdle();
@@ -5394,7 +5148,7 @@
public void testIsActiveNetworkMeteredOverCell() throws Exception {
// Returns true by default when no network is available.
assertTrue(mCm.isActiveNetworkMetered());
- mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
mCellNetworkAgent.removeCapability(NET_CAPABILITY_NOT_METERED);
mCellNetworkAgent.connect(true);
waitForIdle();
@@ -5406,14 +5160,15 @@
public void testIsActiveNetworkMeteredOverVpnTrackingPlatformDefault() throws Exception {
// Returns true by default when no network is available.
assertTrue(mCm.isActiveNetworkMetered());
- mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
mCellNetworkAgent.removeCapability(NET_CAPABILITY_NOT_METERED);
mCellNetworkAgent.connect(true);
waitForIdle();
assertTrue(mCm.isActiveNetworkMetered());
// Connect VPN network. By default it is using current default network (Cell).
- MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN);
+ TestNetworkAgentWrapper
+ vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN);
final ArraySet<UidRange> ranges = new ArraySet<>();
final int uid = Process.myUid();
ranges.add(new UidRange(uid, uid));
@@ -5429,7 +5184,7 @@
assertTrue(mCm.isActiveNetworkMetered());
// Connect WiFi.
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
mWiFiNetworkAgent.connect(true);
waitForIdle();
@@ -5460,20 +5215,21 @@
public void testIsActiveNetworkMeteredOverVpnSpecifyingUnderlyingNetworks() throws Exception {
// Returns true by default when no network is available.
assertTrue(mCm.isActiveNetworkMetered());
- mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
mCellNetworkAgent.removeCapability(NET_CAPABILITY_NOT_METERED);
mCellNetworkAgent.connect(true);
waitForIdle();
assertTrue(mCm.isActiveNetworkMetered());
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
mWiFiNetworkAgent.connect(true);
waitForIdle();
assertFalse(mCm.isActiveNetworkMetered());
// Connect VPN network.
- MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN);
+ TestNetworkAgentWrapper
+ vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN);
final ArraySet<UidRange> ranges = new ArraySet<>();
final int uid = Process.myUid();
ranges.add(new UidRange(uid, uid));
@@ -5531,14 +5287,15 @@
public void testIsActiveNetworkMeteredOverAlwaysMeteredVpn() throws Exception {
// Returns true by default when no network is available.
assertTrue(mCm.isActiveNetworkMetered());
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
mWiFiNetworkAgent.connect(true);
waitForIdle();
assertFalse(mCm.isActiveNetworkMetered());
// Connect VPN network.
- MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN);
+ TestNetworkAgentWrapper
+ vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN);
final ArraySet<UidRange> ranges = new ArraySet<>();
final int uid = Process.myUid();
ranges.add(new UidRange(uid, uid));
@@ -5582,21 +5339,21 @@
.build();
mCm.registerNetworkCallback(cellRequest, cellNetworkCallback);
- mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(true);
cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
- mService.setUidRulesChanged(RULE_REJECT_ALL);
+ setUidRulesChanged(RULE_REJECT_ALL);
cellNetworkCallback.expectBlockedStatusCallback(true, mCellNetworkAgent);
// ConnectivityService should cache it not to invoke the callback again.
- mService.setUidRulesChanged(RULE_REJECT_METERED);
+ setUidRulesChanged(RULE_REJECT_METERED);
cellNetworkCallback.assertNoCallback();
- mService.setUidRulesChanged(RULE_NONE);
+ setUidRulesChanged(RULE_NONE);
cellNetworkCallback.expectBlockedStatusCallback(false, mCellNetworkAgent);
- mService.setUidRulesChanged(RULE_REJECT_METERED);
+ setUidRulesChanged(RULE_REJECT_METERED);
cellNetworkCallback.expectBlockedStatusCallback(true, mCellNetworkAgent);
// Restrict the network based on UID rule and NOT_METERED capability change.
@@ -5607,18 +5364,18 @@
cellNetworkCallback.expectCapabilitiesWithout(NET_CAPABILITY_NOT_METERED,
mCellNetworkAgent);
cellNetworkCallback.expectBlockedStatusCallback(true, mCellNetworkAgent);
- mService.setUidRulesChanged(RULE_ALLOW_METERED);
+ setUidRulesChanged(RULE_ALLOW_METERED);
cellNetworkCallback.expectBlockedStatusCallback(false, mCellNetworkAgent);
- mService.setUidRulesChanged(RULE_NONE);
+ setUidRulesChanged(RULE_NONE);
cellNetworkCallback.assertNoCallback();
// Restrict the network based on BackgroundRestricted.
- mService.setRestrictBackgroundChanged(true);
+ setRestrictBackgroundChanged(true);
cellNetworkCallback.expectBlockedStatusCallback(true, mCellNetworkAgent);
- mService.setRestrictBackgroundChanged(true);
+ setRestrictBackgroundChanged(true);
cellNetworkCallback.assertNoCallback();
- mService.setRestrictBackgroundChanged(false);
+ setRestrictBackgroundChanged(false);
cellNetworkCallback.expectBlockedStatusCallback(false, mCellNetworkAgent);
cellNetworkCallback.assertNoCallback();
@@ -5631,18 +5388,18 @@
mCm.registerDefaultNetworkCallback(defaultCallback);
// No Networkcallbacks invoked before any network is active.
- mService.setUidRulesChanged(RULE_REJECT_ALL);
- mService.setUidRulesChanged(RULE_NONE);
- mService.setUidRulesChanged(RULE_REJECT_METERED);
+ setUidRulesChanged(RULE_REJECT_ALL);
+ setUidRulesChanged(RULE_NONE);
+ setUidRulesChanged(RULE_REJECT_METERED);
defaultCallback.assertNoCallback();
- mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(true);
defaultCallback.expectAvailableCallbacksUnvalidatedAndBlocked(mCellNetworkAgent);
defaultCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mCellNetworkAgent);
// Allow to use the network after switching to NOT_METERED network.
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
mWiFiNetworkAgent.connect(true);
defaultCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
@@ -5658,8 +5415,8 @@
defaultCallback.expectBlockedStatusCallback(false, mCellNetworkAgent);
// Verify there's no Networkcallbacks invoked after data saver on/off.
- mService.setRestrictBackgroundChanged(true);
- mService.setRestrictBackgroundChanged(false);
+ setRestrictBackgroundChanged(true);
+ setRestrictBackgroundChanged(false);
defaultCallback.assertNoCallback();
mCellNetworkAgent.disconnect();
@@ -5709,7 +5466,7 @@
mCm.registerNetworkCallback(networkRequest, networkCallback);
// Prepare ipv6 only link properties.
- mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
final int cellNetId = mCellNetworkAgent.getNetwork().netId;
final LinkProperties cellLp = new LinkProperties();
cellLp.setInterfaceName(MOBILE_IFNAME);
@@ -5756,7 +5513,7 @@
verify(mMockDnsResolver, times(1)).startPrefix64Discovery(cellNetId);
// When NAT64 prefix discovery succeeds, LinkProperties are updated and clatd is started.
- Nat464Xlat clat = mService.getNat464Xlat(mCellNetworkAgent);
+ Nat464Xlat clat = getNat464Xlat(mCellNetworkAgent);
assertNull(mCm.getLinkProperties(mCellNetworkAgent.getNetwork()).getNat64Prefix());
mService.mNetdEventCallback.onNat64PrefixEvent(cellNetId, true /* added */,
kNat64PrefixString, 96);
@@ -5863,7 +5620,7 @@
.build();
mCm.registerNetworkCallback(networkRequest, networkCallback);
- mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
final LinkProperties cellLp = new LinkProperties();
cellLp.setInterfaceName(MOBILE_IFNAME);
mCellNetworkAgent.sendLinkProperties(cellLp);
@@ -5873,7 +5630,7 @@
verify(mNetworkManagementService, times(1)).addIdleTimer(eq(MOBILE_IFNAME), anyInt(),
eq(ConnectivityManager.TYPE_MOBILE));
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
final LinkProperties wifiLp = new LinkProperties();
wifiLp.setInterfaceName(WIFI_IFNAME);
mWiFiNetworkAgent.sendLinkProperties(wifiLp);
@@ -5898,7 +5655,7 @@
eq(ConnectivityManager.TYPE_MOBILE));
// reconnect wifi
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
wifiLp.setInterfaceName(WIFI_IFNAME);
mWiFiNetworkAgent.sendLinkProperties(wifiLp);
mWiFiNetworkAgent.connect(true);
@@ -5944,7 +5701,7 @@
public void testTcpBufferReset() throws Exception {
final String testTcpBufferSizes = "1,2,3,4,5,6";
- mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
reset(mMockNetd);
// Switching default network updates TCP buffer sizes.
mCellNetworkAgent.connect(false);
@@ -5960,7 +5717,7 @@
@Test
public void testGetGlobalProxyForNetwork() throws Exception {
final ProxyInfo testProxyInfo = ProxyInfo.buildDirectProxy("test", 8888);
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
final Network wifiNetwork = mWiFiNetworkAgent.getNetwork();
when(mService.mProxyTracker.getGlobalProxy()).thenReturn(testProxyInfo);
assertEquals(testProxyInfo, mService.getProxyForNetwork(wifiNetwork));
@@ -5969,7 +5726,7 @@
@Test
public void testGetProxyForActiveNetwork() throws Exception {
final ProxyInfo testProxyInfo = ProxyInfo.buildDirectProxy("test", 8888);
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(true);
waitForIdle();
assertNull(mService.getProxyForNetwork(null));
@@ -5988,14 +5745,15 @@
final ProxyInfo testProxyInfo = ProxyInfo.buildDirectProxy("test", 8888);
// Set up a WiFi network with no proxy
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(true);
waitForIdle();
assertNull(mService.getProxyForNetwork(null));
// Set up a VPN network with a proxy
final int uid = Process.myUid();
- final MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN);
+ final TestNetworkAgentWrapper
+ vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN);
final ArraySet<UidRange> ranges = new ArraySet<>();
ranges.add(new UidRange(uid, uid));
mMockVpn.setUids(ranges);
@@ -6044,7 +5802,7 @@
lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null));
// The uid range needs to cover the test app so the network is visible to it.
final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER));
- final MockNetworkAgent vpnNetworkAgent = establishVpn(lp, VPN_UID, vpnRange);
+ final TestNetworkAgentWrapper vpnNetworkAgent = establishVpn(lp, VPN_UID, vpnRange);
// Connected VPN should have interface rules set up. There are two expected invocations,
// one during VPN uid update, one during VPN LinkProperties update
@@ -6070,7 +5828,8 @@
lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null));
// The uid range needs to cover the test app so the network is visible to it.
final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER));
- final MockNetworkAgent vpnNetworkAgent = establishVpn(lp, Process.SYSTEM_UID, vpnRange);
+ final TestNetworkAgentWrapper vpnNetworkAgent = establishVpn(
+ lp, Process.SYSTEM_UID, vpnRange);
// Legacy VPN should not have interface rules set up
verify(mMockNetd, never()).firewallAddUidInterfaceRules(any(), any());
@@ -6085,7 +5844,8 @@
lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), RTN_UNREACHABLE));
// The uid range needs to cover the test app so the network is visible to it.
final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER));
- final MockNetworkAgent vpnNetworkAgent = establishVpn(lp, Process.SYSTEM_UID, vpnRange);
+ final TestNetworkAgentWrapper vpnNetworkAgent = establishVpn(
+ lp, Process.SYSTEM_UID, vpnRange);
// IPv6 unreachable route should not be misinterpreted as a default route
verify(mMockNetd, never()).firewallAddUidInterfaceRules(any(), any());
@@ -6098,7 +5858,7 @@
lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null));
// The uid range needs to cover the test app so the network is visible to it.
final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER));
- final MockNetworkAgent vpnNetworkAgent = establishVpn(lp, VPN_UID, vpnRange);
+ final TestNetworkAgentWrapper vpnNetworkAgent = establishVpn(lp, VPN_UID, vpnRange);
// Connected VPN should have interface rules set up. There are two expected invocations,
// one during VPN uid update, one during VPN LinkProperties update
@@ -6147,7 +5907,7 @@
lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null));
// The uid range needs to cover the test app so the network is visible to it.
final UidRange vpnRange = UidRange.createForUser(VPN_USER);
- final MockNetworkAgent vpnNetworkAgent = establishVpn(lp, VPN_UID,
+ final TestNetworkAgentWrapper vpnNetworkAgent = establishVpn(lp, VPN_UID,
Collections.singleton(vpnRange));
reset(mMockNetd);
@@ -6169,9 +5929,10 @@
}
- private MockNetworkAgent establishVpn(LinkProperties lp, int establishingUid,
+ private TestNetworkAgentWrapper establishVpn(LinkProperties lp, int establishingUid,
Set<UidRange> vpnRange) throws Exception {
- final MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN, lp);
+ final TestNetworkAgentWrapper
+ vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN, lp);
vpnNetworkAgent.getNetworkCapabilities().setEstablishingVpnAppUid(establishingUid);
mMockVpn.setNetworkAgent(vpnNetworkAgent);
mMockVpn.connect();
diff --git a/tests/net/java/com/android/server/connectivity/TetheringTest.java b/tests/net/java/com/android/server/connectivity/TetheringTest.java
index 8c522f4..c030c3e 100644
--- a/tests/net/java/com/android/server/connectivity/TetheringTest.java
+++ b/tests/net/java/com/android/server/connectivity/TetheringTest.java
@@ -469,6 +469,8 @@
if (emulateInterfaceStatusChanged) {
assertEquals(1, mTetheringDependencies.isTetheringSupportedCalls);
verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_AVAILABLE_TETHER);
+ verify(mWifiManager).updateInterfaceIpState(
+ TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED);
}
verifyNoMoreInteractions(mNMService);
verifyNoMoreInteractions(mWifiManager);
@@ -534,6 +536,8 @@
verify(mNMService, times(1)).startTethering(any(String[].class));
verifyNoMoreInteractions(mNMService);
verify(mWifiManager).updateInterfaceIpState(
+ TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED);
+ verify(mWifiManager).updateInterfaceIpState(
TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_LOCAL_ONLY);
verifyNoMoreInteractions(mWifiManager);
verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_ACTIVE_LOCAL_ONLY);
@@ -554,6 +558,8 @@
.setInterfaceConfig(eq(TEST_WLAN_IFNAME), any(InterfaceConfiguration.class));
verify(mNMService, times(1)).stopTethering();
verify(mNMService, times(1)).setIpForwardingEnabled(false);
+ verify(mWifiManager, times(3)).updateInterfaceIpState(
+ TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED);
verifyNoMoreInteractions(mNMService);
verifyNoMoreInteractions(mWifiManager);
// Asking for the last error after the per-interface state machine
@@ -739,6 +745,8 @@
assertEquals(1, mTetheringDependencies.isTetheringSupportedCalls);
verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_AVAILABLE_TETHER);
+ verify(mWifiManager).updateInterfaceIpState(
+ TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED);
verifyNoMoreInteractions(mNMService);
verifyNoMoreInteractions(mWifiManager);
}
@@ -768,6 +776,8 @@
verify(mNMService, times(1)).startTethering(any(String[].class));
verifyNoMoreInteractions(mNMService);
verify(mWifiManager).updateInterfaceIpState(
+ TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED);
+ verify(mWifiManager).updateInterfaceIpState(
TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_TETHERED);
verifyNoMoreInteractions(mWifiManager);
verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_ACTIVE_TETHER);
@@ -805,6 +815,8 @@
.setInterfaceConfig(eq(TEST_WLAN_IFNAME), any(InterfaceConfiguration.class));
verify(mNMService, times(1)).stopTethering();
verify(mNMService, times(1)).setIpForwardingEnabled(false);
+ verify(mWifiManager, times(3)).updateInterfaceIpState(
+ TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED);
verifyNoMoreInteractions(mNMService);
verifyNoMoreInteractions(mWifiManager);
// Asking for the last error after the per-interface state machine
@@ -842,6 +854,8 @@
verify(mNetd, times(1)).interfaceSetCfg(argThat(p -> TEST_WLAN_IFNAME.equals(p.ifName)));
verify(mNMService, times(1)).tetherInterface(TEST_WLAN_IFNAME);
verify(mWifiManager).updateInterfaceIpState(
+ TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED);
+ verify(mWifiManager).updateInterfaceIpState(
TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_TETHERED);
// TODO: Figure out why this isn't exactly once, for sendTetherStateChangedBroadcast().
assertTrue(1 <= mTetheringDependencies.isTetheringSupportedCalls);
diff --git a/tests/net/java/com/android/server/connectivity/tethering/EntitlementManagerTest.java b/tests/net/java/com/android/server/connectivity/tethering/EntitlementManagerTest.java
index 2b2e8a7..5217e26 100644
--- a/tests/net/java/com/android/server/connectivity/tethering/EntitlementManagerTest.java
+++ b/tests/net/java/com/android/server/connectivity/tethering/EntitlementManagerTest.java
@@ -406,6 +406,13 @@
}
@Test
+ public void verifyPermissionWhenProvisioningNotStarted() {
+ assertTrue(mEnMgr.isCellularUpstreamPermitted());
+ setupForRequiredProvisioning();
+ assertFalse(mEnMgr.isCellularUpstreamPermitted());
+ }
+
+ @Test
public void testRunTetherProvisioning() {
setupForRequiredProvisioning();
// 1. start ui provisioning, upstream is mobile
diff --git a/tools/dump-coverage/README.md b/tools/dump-coverage/README.md
index 2bab4bc..d1c10bc 100644
--- a/tools/dump-coverage/README.md
+++ b/tools/dump-coverage/README.md
@@ -16,7 +16,7 @@
Then we can run the command to dump the data:
```
-adb shell 'am attach-agent com.android.deskclock /system/lib/libdumpcoverage.so=dump:/data/data/com.android.deskclock/folder-to-use'
+adb shell 'am attach-agent com.android.deskclock /system/lib/libdumpcoverage.so=dump:/data/data/com.android.deskclock/folder-to-use/coverage-file.ec'
```
We can also reset the coverage information with
@@ -28,10 +28,10 @@
then perform more actions, then dump the data again. To get the files, we can get
```
-adb pull /data/data/com.android.deskclock/folder-to-use ~/path-on-your-computer
+adb pull /data/data/com.android.deskclock/folder-to-use/coverage-file.ec ~/path-on-your-computer
```
-And you should have timestamped `.exec` files on your machine under the folder `~/path-on-your-computer`
+And you should have `coverage-file.ec` on your machine under the folder `~/path-on-your-computer`
# Details
diff --git a/tools/dump-coverage/dump_coverage.cc b/tools/dump-coverage/dump_coverage.cc
index 3de1865..0808e77 100644
--- a/tools/dump-coverage/dump_coverage.cc
+++ b/tools/dump-coverage/dump_coverage.cc
@@ -18,20 +18,10 @@
#include <jvmti.h>
#include <string.h>
-#include <atomic>
-#include <ctime>
#include <fstream>
-#include <iomanip>
-#include <iostream>
-#include <istream>
-#include <memory>
-#include <sstream>
-#include <string>
-#include <vector>
using std::get;
using std::tuple;
-using std::chrono::system_clock;
namespace dump_coverage {
@@ -87,35 +77,11 @@
return java_result_array;
}
-// Gets the filename to write execution data to
-// dirname: the directory in which to place the file
-// outputs <dirname>/YYYY-MM-DD-HH-MM-SS.SSS.exec
-static std::string GetFilename(const std::string& dirname) {
- system_clock::time_point time_point = system_clock::now();
- auto seconds = std::chrono::time_point_cast<std::chrono::seconds>(time_point);
- auto fractional_time = time_point - seconds;
- auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(fractional_time);
-
- std::time_t time = system_clock::to_time_t(time_point);
- auto tm = *std::gmtime(&time);
-
- std::ostringstream oss;
- oss
- << dirname
- << "/"
- << std::put_time(&tm, "%Y-%m-%d-%H-%M-%S.")
- << std::setfill('0') << std::setw(3) << millis.count()
- << ".ec";
- return oss.str();
-}
-
-// Writes the execution data to a file
-// data, length: represent the data, as a sequence of bytes
-// dirname: directory name to contain the file
+// Writes the execution data to a file.
+// data, length: represent the data, as a sequence of bytes.
+// filename: file to write coverage data to.
// returns JNI_ERR if there is an error in writing the file, otherwise JNI_OK.
-static jint WriteFile(const char* data, int length, const std::string& dirname) {
- auto filename = GetFilename(dirname);
-
+static jint WriteFile(const char* data, int length, const std::string& filename) {
LOG(INFO) << "Writing file of length " << length << " to '" << filename
<< "'";
std::ofstream file(filename, std::ios::binary);
@@ -136,11 +102,11 @@
return JNI_OK;
}
-// Grabs execution data and writes it to a file
-// dirname: directory name to contain the file
+// Grabs execution data and writes it to a file.
+// filename: file to write coverage data to.
// returns JNI_ERR if there is an error writing the file.
// Will crash if the Agent isn't found or if any Java Exception occurs.
-static jint Dump(const std::string& dirname) {
+static jint Dump(const std::string& filename) {
LOG(INFO) << "Dumping file";
JNIEnv* env = GetJNIEnv();
@@ -152,12 +118,12 @@
int result_len = env->GetArrayLength(java_result_array);
- return WriteFile((const char*) result_ptr, result_len, dirname);
+ return WriteFile((const char*) result_ptr, result_len, filename);
}
// Resets execution data, performing the equivalent of
// Agent.getInstance().reset();
-// args: should be empty
+// args: should be empty.
// returns JNI_ERR if the arguments are invalid.
// Will crash if the Agent isn't found or if any Java Exception occurs.
static jint Reset(const std::string& args) {
diff --git a/tools/preload-check/Android.bp b/tools/preload-check/Android.bp
index 2488341..87b31d2 100644
--- a/tools/preload-check/Android.bp
+++ b/tools/preload-check/Android.bp
@@ -19,4 +19,5 @@
libs: ["tradefed"],
test_suites: ["general-tests"],
required: ["preload-check-device"],
+ data: [":preload-check-device"],
}
diff --git a/tools/preload-check/device/Android.bp b/tools/preload-check/device/Android.bp
index 7782b0d..f40d8ba 100644
--- a/tools/preload-check/device/Android.bp
+++ b/tools/preload-check/device/Android.bp
@@ -20,7 +20,6 @@
sdk_version: "current",
srcs: ["src/**/*.java"],
- test_suites: ["general-tests"],
dex_preopt: {
enabled: false,
},
diff --git a/tools/preload2/Android.bp b/tools/preload2/Android.bp
deleted file mode 100644
index 5809421..0000000
--- a/tools/preload2/Android.bp
+++ /dev/null
@@ -1,50 +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.
-
-java_library_host {
- name: "preload2",
-
- srcs: ["src/**/*.java"],
-
- // To connect to devices (and take hprof dumps).
- static_libs: [
- "ddmlib-prebuilt",
- "tools-common-prebuilt",
-
- // To process hprof dumps.
- "perflib-prebuilt",
-
- "trove-prebuilt",
- "guavalib",
-
- // For JDWP access we use the framework in the JDWP tests from Apache Harmony, for
- // convenience (and to not depend on internal JDK APIs).
- "apache-harmony-jdwp-tests",
- "junit",
- ],
-
- // Copy to build artifacts
- dist: {
- targets: [
- "dist_files",
- ],
- },
-}
-
-// Copy the preload-tool shell script to the host's bin directory.
-sh_binary_host {
- name: "preload-tool",
- src: "preload-tool",
- required: ["preload2"],
-}
diff --git a/tools/preload2/preload-tool b/tools/preload2/preload-tool
deleted file mode 100644
index 322b62f..0000000
--- a/tools/preload2/preload-tool
+++ /dev/null
@@ -1,37 +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.
-
-# This script is used on the host only. It uses a common subset
-# shell dialect that should work well. It is partially derived
-# from art/tools/art.
-
-function follow_links() {
- if [ z"$BASH_SOURCE" != z ]; then
- file="$BASH_SOURCE"
- else
- file="$0"
- fi
- while [ -h "$file" ]; do
- # On Mac OS, readlink -f doesn't work.
- file="$(readlink "$file")"
- done
- echo "$file"
-}
-
-
-PROG_NAME="$(follow_links)"
-PROG_DIR="$(cd "${PROG_NAME%/*}" ; pwd -P)"
-ANDROID_ROOT=$PROG_DIR/..
-
-java -cp $ANDROID_ROOT/framework/preload2.jar com.android.preload.Main $@
diff --git a/tools/preload2/src/com/android/preload/ClientUtils.java b/tools/preload2/src/com/android/preload/ClientUtils.java
deleted file mode 100644
index 71ef025..0000000
--- a/tools/preload2/src/com/android/preload/ClientUtils.java
+++ /dev/null
@@ -1,224 +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.preload;
-
-import com.android.ddmlib.AndroidDebugBridge;
-import com.android.ddmlib.AndroidDebugBridge.IClientChangeListener;
-import com.android.ddmlib.Client;
-import com.android.ddmlib.IDevice;
-
-/**
- * Helper class for common communication with a Client (the ddms name for a running application).
- *
- * Instances take a default timeout parameter that's applied to all functions without explicit
- * timeout. Timeouts are in milliseconds.
- */
-public class ClientUtils {
-
- private int defaultTimeout;
-
- public ClientUtils() {
- this(10000);
- }
-
- public ClientUtils(int defaultTimeout) {
- this.defaultTimeout = defaultTimeout;
- }
-
- /**
- * Shortcut for findClient with default timeout.
- */
- public Client findClient(IDevice device, String processName, int processPid) {
- return findClient(device, processName, processPid, defaultTimeout);
- }
-
- /**
- * Find the client with the given process name or process id. The name takes precedence over
- * the process id (if valid). Stop looking after the given timeout.
- *
- * @param device The device to communicate with.
- * @param processName The name of the process. May be null.
- * @param processPid The pid of the process. Values less than or equal to zero are ignored.
- * @param timeout The amount of milliseconds to wait, at most.
- * @return The client, if found. Otherwise null.
- */
- public Client findClient(IDevice device, String processName, int processPid, int timeout) {
- WaitForClient wfc = new WaitForClient(device, processName, processPid, timeout);
- return wfc.get();
- }
-
- /**
- * Shortcut for findAllClients with default timeout.
- */
- public Client[] findAllClients(IDevice device) {
- return findAllClients(device, defaultTimeout);
- }
-
- /**
- * Retrieve all clients known to the given device. Wait at most the given timeout.
- *
- * @param device The device to investigate.
- * @param timeout The amount of milliseconds to wait, at most.
- * @return An array of clients running on the given device. May be null depending on the
- * device implementation.
- */
- public Client[] findAllClients(IDevice device, int timeout) {
- if (device.hasClients()) {
- return device.getClients();
- }
- WaitForClients wfc = new WaitForClients(device, timeout);
- return wfc.get();
- }
-
- private static class WaitForClient implements IClientChangeListener {
-
- private IDevice device;
- private String processName;
- private int processPid;
- private long timeout;
- private Client result;
-
- public WaitForClient(IDevice device, String processName, int processPid, long timeout) {
- this.device = device;
- this.processName = processName;
- this.processPid = processPid;
- this.timeout = timeout;
- this.result = null;
- }
-
- public Client get() {
- synchronized (this) {
- AndroidDebugBridge.addClientChangeListener(this);
-
- // Maybe it's already there.
- if (result == null) {
- result = searchForClient(device);
- }
-
- if (result == null) {
- try {
- wait(timeout);
- } catch (InterruptedException e) {
- // Note: doesn't guard for spurious wakeup.
- }
- }
- }
-
- AndroidDebugBridge.removeClientChangeListener(this);
- return result;
- }
-
- private Client searchForClient(IDevice device) {
- if (processName != null) {
- Client tmp = device.getClient(processName);
- if (tmp != null) {
- return tmp;
- }
- }
- if (processPid > 0) {
- String name = device.getClientName(processPid);
- if (name != null && !name.isEmpty()) {
- Client tmp = device.getClient(name);
- if (tmp != null) {
- return tmp;
- }
- }
- }
- if (processPid > 0) {
- // Try manual search.
- for (Client cl : device.getClients()) {
- if (cl.getClientData().getPid() == processPid
- && cl.getClientData().getClientDescription() != null) {
- return cl;
- }
- }
- }
- return null;
- }
-
- private boolean isTargetClient(Client c) {
- if (processPid > 0 && c.getClientData().getPid() == processPid) {
- return true;
- }
- if (processName != null
- && processName.equals(c.getClientData().getClientDescription())) {
- return true;
- }
- return false;
- }
-
- @Override
- public void clientChanged(Client arg0, int arg1) {
- synchronized (this) {
- if ((arg1 & Client.CHANGE_INFO) != 0 && (arg0.getDevice() == device)) {
- if (isTargetClient(arg0)) {
- result = arg0;
- notifyAll();
- }
- }
- }
- }
- }
-
- private static class WaitForClients implements IClientChangeListener {
-
- private IDevice device;
- private long timeout;
-
- public WaitForClients(IDevice device, long timeout) {
- this.device = device;
- this.timeout = timeout;
- }
-
- public Client[] get() {
- synchronized (this) {
- AndroidDebugBridge.addClientChangeListener(this);
-
- if (device.hasClients()) {
- return device.getClients();
- }
-
- try {
- wait(timeout); // Note: doesn't guard for spurious wakeup.
- } catch (InterruptedException exc) {
- }
-
- // We will be woken up when the first client data arrives. Sleep a little longer
- // to give (hopefully all of) the rest of the clients a chance to become available.
- // Note: a loop with timeout is brittle as well and complicated, just accept this
- // for now.
- try {
- Thread.sleep(500);
- } catch (InterruptedException exc) {
- }
- }
-
- AndroidDebugBridge.removeClientChangeListener(this);
-
- return device.getClients();
- }
-
- @Override
- public void clientChanged(Client arg0, int arg1) {
- synchronized (this) {
- if ((arg1 & Client.CHANGE_INFO) != 0 && (arg0.getDevice() == device)) {
- notifyAll();
- }
- }
- }
- }
-}
diff --git a/tools/preload2/src/com/android/preload/DeviceUtils.java b/tools/preload2/src/com/android/preload/DeviceUtils.java
deleted file mode 100644
index 18cab7b..0000000
--- a/tools/preload2/src/com/android/preload/DeviceUtils.java
+++ /dev/null
@@ -1,420 +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.preload;
-
-import com.android.ddmlib.AdbCommandRejectedException;
-import com.android.ddmlib.AndroidDebugBridge;
-import com.android.ddmlib.AndroidDebugBridge.IDeviceChangeListener;
-import com.android.preload.classdataretrieval.hprof.Hprof;
-import com.android.ddmlib.DdmPreferences;
-import com.android.ddmlib.IDevice;
-import com.android.ddmlib.IShellOutputReceiver;
-import com.android.ddmlib.SyncException;
-import com.android.ddmlib.TimeoutException;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.Date;
-import java.util.concurrent.Future;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Helper class for some device routines.
- */
-public class DeviceUtils {
-
- // Locations
- private static final String PRELOADED_CLASSES_FILE = "/etc/preloaded-classes";
- // Shell commands
- private static final String CREATE_EMPTY_PRELOADED_CMD = "touch " + PRELOADED_CLASSES_FILE;
- private static final String DELETE_CACHE_CMD = "rm /data/dalvik-cache/*/*boot.art";
- private static final String DELETE_PRELOADED_CMD = "rm " + PRELOADED_CLASSES_FILE;
- private static final String READ_PRELOADED_CMD = "cat " + PRELOADED_CLASSES_FILE;
- private static final String START_SHELL_CMD = "start";
- private static final String STOP_SHELL_CMD = "stop";
- private static final String REMOUNT_SYSTEM_CMD = "mount -o rw,remount /system";
- private static final String UNSET_BOOTCOMPLETE_CMD = "setprop dev.bootcomplete \"0\"";
-
- public static void init(int debugPort) {
- DdmPreferences.setSelectedDebugPort(debugPort);
-
- Hprof.init();
-
- AndroidDebugBridge.init(true);
-
- AndroidDebugBridge.createBridge();
- }
-
- /**
- * Run a command in the shell on the device.
- */
- public static void doShell(IDevice device, String cmdline, long timeout, TimeUnit unit) {
- doShell(device, cmdline, new NullShellOutputReceiver(), timeout, unit);
- }
-
- /**
- * Run a command in the shell on the device. Collects and returns the console output.
- */
- public static String doShellReturnString(IDevice device, String cmdline, long timeout,
- TimeUnit unit) {
- CollectStringShellOutputReceiver rec = new CollectStringShellOutputReceiver();
- doShell(device, cmdline, rec, timeout, unit);
- return rec.toString();
- }
-
- /**
- * Run a command in the shell on the device, directing all output to the given receiver.
- */
- public static void doShell(IDevice device, String cmdline, IShellOutputReceiver receiver,
- long timeout, TimeUnit unit) {
- try {
- device.executeShellCommand(cmdline, receiver, timeout, unit);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-
- /**
- * Run am start on the device.
- */
- public static void doAMStart(IDevice device, String name, String activity) {
- doShell(device, "am start -n " + name + " /." + activity, 30, TimeUnit.SECONDS);
- }
-
- /**
- * Find the device with the given serial. Give up after the given timeout (in milliseconds).
- */
- public static IDevice findDevice(String serial, int timeout) {
- WaitForDevice wfd = new WaitForDevice(serial, timeout);
- return wfd.get();
- }
-
- /**
- * Get all devices ddms knows about. Wait at most for the given timeout.
- */
- public static IDevice[] findDevices(int timeout) {
- WaitForDevice wfd = new WaitForDevice(null, timeout);
- wfd.get();
- return AndroidDebugBridge.getBridge().getDevices();
- }
-
- /**
- * Return the build type of the given device. This is the value of the "ro.build.type"
- * system property.
- */
- public static String getBuildType(IDevice device) {
- try {
- Future<String> buildType = device.getSystemProperty("ro.build.type");
- return buildType.get(500, TimeUnit.MILLISECONDS);
- } catch (Exception e) {
- }
- return null;
- }
-
- /**
- * Check whether the given device has a pre-optimized boot image. More precisely, checks
- * whether /system/framework/ * /boot.art exists.
- */
- public static boolean hasPrebuiltBootImage(IDevice device) {
- String ret =
- doShellReturnString(device, "ls /system/framework/*/boot.art", 500, TimeUnit.MILLISECONDS);
-
- return !ret.contains("No such file or directory");
- }
-
- /**
- * Write over the preloaded-classes file with an empty or existing file and regenerate the boot
- * image as necessary.
- *
- * @param device
- * @param pcFile
- * @param bootTimeout
- * @throws AdbCommandRejectedException
- * @throws IOException
- * @throws TimeoutException
- * @throws SyncException
- * @return true if successfully overwritten, false otherwise
- */
- public static boolean overwritePreloaded(IDevice device, File pcFile, long bootTimeout)
- throws AdbCommandRejectedException, IOException, TimeoutException, SyncException {
- boolean writeEmpty = (pcFile == null);
- if (writeEmpty) {
- // Check if the preloaded-classes file is already empty.
- String oldContent =
- doShellReturnString(device, READ_PRELOADED_CMD, 1, TimeUnit.SECONDS);
- if (oldContent.trim().equals("")) {
- System.out.println("Preloaded-classes already empty.");
- return true;
- }
- }
-
- // Stop the system server etc.
- doShell(device, STOP_SHELL_CMD, 1, TimeUnit.SECONDS);
- // Remount the read-only system partition
- doShell(device, REMOUNT_SYSTEM_CMD, 1, TimeUnit.SECONDS);
- // Delete the preloaded-classes file
- doShell(device, DELETE_PRELOADED_CMD, 1, TimeUnit.SECONDS);
- // Delete the dalvik cache files
- doShell(device, DELETE_CACHE_CMD, 1, TimeUnit.SECONDS);
- if (writeEmpty) {
- // Write an empty preloaded-classes file
- doShell(device, CREATE_EMPTY_PRELOADED_CMD, 500, TimeUnit.MILLISECONDS);
- } else {
- // Push the new preloaded-classes file
- device.pushFile(pcFile.getAbsolutePath(), PRELOADED_CLASSES_FILE);
- }
- // Manually reset the boot complete flag
- doShell(device, UNSET_BOOTCOMPLETE_CMD, 1, TimeUnit.SECONDS);
- // Restart system server on the device
- doShell(device, START_SHELL_CMD, 1, TimeUnit.SECONDS);
- // Wait for the boot complete flag and return the outcome.
- return waitForBootComplete(device, bootTimeout);
- }
-
- private static boolean waitForBootComplete(IDevice device, long timeout) {
- // Do a loop checking each second whether bootcomplete. Wait for at most the given
- // threshold.
- Date startDate = new Date();
- for (;;) {
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- // Ignore spurious wakeup.
- }
- // Check whether bootcomplete.
- String ret =
- doShellReturnString(device, "getprop dev.bootcomplete", 500, TimeUnit.MILLISECONDS);
- if (ret.trim().equals("1")) {
- break;
- }
- System.out.println("Still not booted: " + ret);
-
- // Check whether we timed out. This is a simplistic check that doesn't take into account
- // things like switches in time.
- Date endDate = new Date();
- long seconds =
- TimeUnit.SECONDS.convert(endDate.getTime() - startDate.getTime(), TimeUnit.MILLISECONDS);
- if (seconds > timeout) {
- return false;
- }
- }
-
- return true;
- }
-
- /**
- * Enable method-tracing on device. The system should be restarted after this.
- */
- public static void enableTracing(IDevice device) {
- // Disable selinux.
- doShell(device, "setenforce 0", 100, TimeUnit.MILLISECONDS);
-
- // Make the profile directory world-writable.
- doShell(device, "chmod 777 /data/dalvik-cache/profiles", 100, TimeUnit.MILLISECONDS);
-
- // Enable streaming method tracing with a small 1K buffer.
- doShell(device, "setprop dalvik.vm.method-trace true", 100, TimeUnit.MILLISECONDS);
- doShell(device, "setprop dalvik.vm.method-trace-file "
- + "/data/dalvik-cache/profiles/zygote.trace.bin", 100, TimeUnit.MILLISECONDS);
- doShell(device, "setprop dalvik.vm.method-trace-file-siz 1024", 100, TimeUnit.MILLISECONDS);
- doShell(device, "setprop dalvik.vm.method-trace-stream true", 100, TimeUnit.MILLISECONDS);
- }
-
- private static class NullShellOutputReceiver implements IShellOutputReceiver {
- @Override
- public boolean isCancelled() {
- return false;
- }
-
- @Override
- public void flush() {}
-
- @Override
- public void addOutput(byte[] arg0, int arg1, int arg2) {}
- }
-
- private static class CollectStringShellOutputReceiver implements IShellOutputReceiver {
-
- private StringBuilder builder = new StringBuilder();
-
- @Override
- public String toString() {
- String ret = builder.toString();
- // Strip trailing newlines. They are especially ugly because adb uses DOS line endings.
- while (ret.endsWith("\r") || ret.endsWith("\n")) {
- ret = ret.substring(0, ret.length() - 1);
- }
- return ret;
- }
-
- @Override
- public void addOutput(byte[] arg0, int arg1, int arg2) {
- builder.append(new String(arg0, arg1, arg2));
- }
-
- @Override
- public void flush() {}
-
- @Override
- public boolean isCancelled() {
- return false;
- }
- }
-
- private static class WaitForDevice {
-
- private String serial;
- private long timeout;
- private IDevice device;
-
- public WaitForDevice(String serial, long timeout) {
- this.serial = serial;
- this.timeout = timeout;
- device = null;
- }
-
- public IDevice get() {
- if (device == null) {
- WaitForDeviceListener wfdl = new WaitForDeviceListener(serial);
- synchronized (wfdl) {
- AndroidDebugBridge.addDeviceChangeListener(wfdl);
-
- // Check whether we already know about this device.
- IDevice[] devices = AndroidDebugBridge.getBridge().getDevices();
- if (serial != null) {
- for (IDevice d : devices) {
- if (serial.equals(d.getSerialNumber())) {
- // Only accept if there are clients already. Else wait for the callback informing
- // us that we now have clients.
- if (d.hasClients()) {
- device = d;
- }
-
- break;
- }
- }
- } else {
- if (devices.length > 0) {
- device = devices[0];
- }
- }
-
- if (device == null) {
- try {
- wfdl.wait(timeout);
- } catch (InterruptedException e) {
- // Ignore spurious wakeups.
- }
- device = wfdl.getDevice();
- }
-
- AndroidDebugBridge.removeDeviceChangeListener(wfdl);
- }
- }
-
- if (device != null) {
- // Wait for clients.
- WaitForClientsListener wfcl = new WaitForClientsListener(device);
- synchronized (wfcl) {
- AndroidDebugBridge.addDeviceChangeListener(wfcl);
-
- if (!device.hasClients()) {
- try {
- wfcl.wait(timeout);
- } catch (InterruptedException e) {
- // Ignore spurious wakeups.
- }
- }
-
- AndroidDebugBridge.removeDeviceChangeListener(wfcl);
- }
- }
-
- return device;
- }
-
- private static class WaitForDeviceListener implements IDeviceChangeListener {
-
- private String serial;
- private IDevice device;
-
- public WaitForDeviceListener(String serial) {
- this.serial = serial;
- }
-
- public IDevice getDevice() {
- return device;
- }
-
- @Override
- public void deviceChanged(IDevice arg0, int arg1) {
- // We may get a device changed instead of connected. Handle like a connection.
- deviceConnected(arg0);
- }
-
- @Override
- public void deviceConnected(IDevice arg0) {
- if (device != null) {
- // Ignore updates.
- return;
- }
-
- if (serial == null || serial.equals(arg0.getSerialNumber())) {
- device = arg0;
- synchronized (this) {
- notifyAll();
- }
- }
- }
-
- @Override
- public void deviceDisconnected(IDevice arg0) {
- // Ignore disconnects.
- }
-
- }
-
- private static class WaitForClientsListener implements IDeviceChangeListener {
-
- private IDevice myDevice;
-
- public WaitForClientsListener(IDevice myDevice) {
- this.myDevice = myDevice;
- }
-
- @Override
- public void deviceChanged(IDevice arg0, int arg1) {
- if (arg0 == myDevice && (arg1 & IDevice.CHANGE_CLIENT_LIST) != 0) {
- // Got a client list, done here.
- synchronized (this) {
- notifyAll();
- }
- }
- }
-
- @Override
- public void deviceConnected(IDevice arg0) {
- }
-
- @Override
- public void deviceDisconnected(IDevice arg0) {
- }
-
- }
- }
-
-}
diff --git a/tools/preload2/src/com/android/preload/DumpData.java b/tools/preload2/src/com/android/preload/DumpData.java
deleted file mode 100644
index d997224..0000000
--- a/tools/preload2/src/com/android/preload/DumpData.java
+++ /dev/null
@@ -1,91 +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.preload;
-
-import java.util.Date;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * Holds the collected data for a process.
- */
-public class DumpData {
- /**
- * Name of the package (=application).
- */
- String packageName;
-
- /**
- * A map of class name to a string for the classloader. This may be a toString equivalent,
- * or just a unique ID.
- */
- Map<String, String> dumpData;
-
- /**
- * The Date when this data was captured. Mostly for display purposes.
- */
- Date date;
-
- /**
- * A cached value for the number of boot classpath classes (classloader value in dumpData is
- * null).
- */
- int bcpClasses;
-
- public DumpData(String packageName, Map<String, String> dumpData, Date date) {
- this.packageName = packageName;
- this.dumpData = dumpData;
- this.date = date;
-
- countBootClassPath();
- }
-
- public String getPackageName() {
- return packageName;
- }
-
- public Date getDate() {
- return date;
- }
-
- public Map<String, String> getDumpData() {
- return dumpData;
- }
-
- public void countBootClassPath() {
- bcpClasses = 0;
- for (Map.Entry<String, String> e : dumpData.entrySet()) {
- if (e.getValue() == null) {
- bcpClasses++;
- }
- }
- }
-
- // Return an inverted mapping.
- public Map<String, Set<String>> invertData() {
- Map<String, Set<String>> ret = new HashMap<>();
- for (Map.Entry<String, String> e : dumpData.entrySet()) {
- if (!ret.containsKey(e.getValue())) {
- ret.put(e.getValue(), new HashSet<String>());
- }
- ret.get(e.getValue()).add(e.getKey());
- }
- return ret;
- }
-}
\ No newline at end of file
diff --git a/tools/preload2/src/com/android/preload/DumpDataIO.java b/tools/preload2/src/com/android/preload/DumpDataIO.java
deleted file mode 100644
index 28625c5..0000000
--- a/tools/preload2/src/com/android/preload/DumpDataIO.java
+++ /dev/null
@@ -1,141 +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.preload;
-
-import org.xml.sax.Attributes;
-import org.xml.sax.InputSource;
-import org.xml.sax.SAXException;
-import org.xml.sax.XMLReader;
-import org.xml.sax.helpers.DefaultHandler;
-
-import java.io.File;
-import java.io.FileReader;
-import java.text.DateFormat;
-import java.util.Collection;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.Map;
-
-import javax.xml.parsers.SAXParser;
-import javax.xml.parsers.SAXParserFactory;
-
-/**
- * Helper class for serialization and deserialization of a collection of DumpData objects to XML.
- */
-public class DumpDataIO {
-
- /**
- * Serialize the given collection to an XML document. Returns the produced string.
- */
- public static String serialize(Collection<DumpData> data) {
- // We'll do this by hand, constructing a DOM or similar is too complicated for our simple
- // use case.
-
- StringBuilder sb = new StringBuilder();
- sb.append("<preloaded-classes-data>\n");
-
- for (DumpData d : data) {
- serialize(d, sb);
- }
-
- sb.append("</preloaded-classes-data>\n");
- return sb.toString();
- }
-
- private static void serialize(DumpData d, StringBuilder sb) {
- sb.append("<data package=\"" + d.packageName + "\" date=\"" +
- DateFormat.getDateTimeInstance().format(d.date) +"\">\n");
-
- for (Map.Entry<String, String> e : d.dumpData.entrySet()) {
- sb.append("<class name=\"" + e.getKey() + "\" classloader=\"" + e.getValue() + "\"/>\n");
- }
-
- sb.append("</data>\n");
- }
-
- /**
- * Load a collection of DumpData objects from the given file.
- */
- public static Collection<DumpData> deserialize(File f) throws Exception {
- // Use SAX parsing. Our format is very simple. Don't do any schema validation or such.
-
- SAXParserFactory spf = SAXParserFactory.newInstance();
- spf.setNamespaceAware(false);
- SAXParser saxParser = spf.newSAXParser();
-
- XMLReader xmlReader = saxParser.getXMLReader();
- DumpDataContentHandler ddch = new DumpDataContentHandler();
- xmlReader.setContentHandler(ddch);
- xmlReader.parse(new InputSource(new FileReader(f)));
-
- return ddch.data;
- }
-
- private static class DumpDataContentHandler extends DefaultHandler {
- Collection<DumpData> data = new LinkedList<DumpData>();
- DumpData openData = null;
-
- @Override
- public void startElement(String uri, String localName, String qName, Attributes attributes)
- throws SAXException {
- if (qName.equals("data")) {
- if (openData != null) {
- throw new IllegalStateException();
- }
- String pkg = attributes.getValue("package");
- String dateString = attributes.getValue("date");
-
- if (pkg == null || dateString == null) {
- throw new IllegalArgumentException();
- }
-
- try {
- Date date = DateFormat.getDateTimeInstance().parse(dateString);
- openData = new DumpData(pkg, new HashMap<String, String>(), date);
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- } else if (qName.equals("class")) {
- if (openData == null) {
- throw new IllegalStateException();
- }
- String className = attributes.getValue("name");
- String classLoader = attributes.getValue("classloader");
-
- if (className == null || classLoader == null) {
- throw new IllegalArgumentException();
- }
-
- openData.dumpData.put(className, classLoader.equals("null") ? null : classLoader);
- }
- }
-
- @Override
- public void endElement(String uri, String localName, String qName) throws SAXException {
- if (qName.equals("data")) {
- if (openData == null) {
- throw new IllegalStateException();
- }
- openData.countBootClassPath();
-
- data.add(openData);
- openData = null;
- }
- }
- }
-}
diff --git a/tools/preload2/src/com/android/preload/DumpTableModel.java b/tools/preload2/src/com/android/preload/DumpTableModel.java
deleted file mode 100644
index d97cbf0..0000000
--- a/tools/preload2/src/com/android/preload/DumpTableModel.java
+++ /dev/null
@@ -1,93 +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.preload;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import javax.swing.table.AbstractTableModel;
-
-/**
- * A table model for collected DumpData. This is both the internal storage as well as the model
- * for display.
- */
-public class DumpTableModel extends AbstractTableModel {
-
- private List<DumpData> data = new ArrayList<DumpData>();
-
- public void addData(DumpData d) {
- data.add(d);
- fireTableRowsInserted(data.size() - 1, data.size() - 1);
- }
-
- public void clear() {
- int size = data.size();
- if (size > 0) {
- data.clear();
- fireTableRowsDeleted(0, size - 1);
- }
- }
-
- public List<DumpData> getData() {
- return data;
- }
-
- @Override
- public int getRowCount() {
- return data.size();
- }
-
- @Override
- public int getColumnCount() {
- return 4;
- }
-
- @Override
- public String getColumnName(int column) {
- switch (column) {
- case 0:
- return "Package";
- case 1:
- return "Date";
- case 2:
- return "# All Classes";
- case 3:
- return "# Boot Classpath Classes";
-
- default:
- throw new IndexOutOfBoundsException(String.valueOf(column));
- }
- }
-
- @Override
- public Object getValueAt(int rowIndex, int columnIndex) {
- DumpData d = data.get(rowIndex);
- switch (columnIndex) {
- case 0:
- return d.packageName;
- case 1:
- return d.date;
- case 2:
- return d.dumpData.size();
- case 3:
- return d.bcpClasses;
-
- default:
- throw new IndexOutOfBoundsException(String.valueOf(columnIndex));
- }
- }
-}
\ No newline at end of file
diff --git a/tools/preload2/src/com/android/preload/Main.java b/tools/preload2/src/com/android/preload/Main.java
deleted file mode 100644
index 2265e95..0000000
--- a/tools/preload2/src/com/android/preload/Main.java
+++ /dev/null
@@ -1,341 +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.preload;
-
-import com.android.ddmlib.Client;
-import com.android.ddmlib.IDevice;
-import com.android.preload.actions.ClearTableAction;
-import com.android.preload.actions.ComputeThresholdAction;
-import com.android.preload.actions.ComputeThresholdXAction;
-import com.android.preload.actions.DeviceSpecific;
-import com.android.preload.actions.ExportAction;
-import com.android.preload.actions.ImportAction;
-import com.android.preload.actions.ReloadListAction;
-import com.android.preload.actions.RunMonkeyAction;
-import com.android.preload.actions.ScanAllPackagesAction;
-import com.android.preload.actions.ScanPackageAction;
-import com.android.preload.actions.ShowDataAction;
-import com.android.preload.actions.WritePreloadedClassesAction;
-import com.android.preload.classdataretrieval.ClassDataRetriever;
-import com.android.preload.classdataretrieval.hprof.Hprof;
-import com.android.preload.classdataretrieval.jdwp.JDWPClassDataRetriever;
-import com.android.preload.ui.IUI;
-import com.android.preload.ui.SequenceUI;
-import com.android.preload.ui.SwingUI;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.NoSuchElementException;
-
-import javax.swing.Action;
-import javax.swing.DefaultListModel;
-
-public class Main {
-
- /**
- * Enable tracing mode. This is a work-in-progress to derive compiled-methods data, so it is
- * off for now.
- */
- public final static boolean ENABLE_TRACING = false;
-
- /**
- * Ten-second timeout.
- */
- public final static int DEFAULT_TIMEOUT_MILLIS = 10 * 1000;
-
- /**
- * Hprof timeout. Two minutes.
- */
- public final static int HPROF_TIMEOUT_MILLIS = 120 * 1000;
-
- private IDevice device;
- private static ClientUtils clientUtils;
-
- private DumpTableModel dataTableModel;
- private DefaultListModel<Client> clientListModel;
-
- private IUI ui;
-
- // Actions that need to be updated once a device is selected.
- private Collection<DeviceSpecific> deviceSpecificActions;
-
- // Current main instance.
- private static Main top;
- private static boolean useJdwpClassDataRetriever = false;
-
- public final static String CLASS_PRELOAD_BLACKLIST = "android.app.AlarmManager$" + "|"
- + "android.app.SearchManager$" + "|" + "android.os.FileObserver$" + "|"
- + "com.android.server.PackageManagerService\\$AppDirObserver$" + "|" +
-
-
- // Threads
- "android.os.AsyncTask$" + "|" + "android.pim.ContactsAsyncHelper$" + "|"
- + "android.webkit.WebViewClassic\\$1$" + "|" + "java.lang.ProcessManager$" + "|"
- + "(.*\\$NoPreloadHolder$)";
-
- public final static String SCAN_ALL_CMD = "scan-all";
- public final static String SCAN_PACKAGE_CMD = "scan";
- public final static String COMPUTE_FILE_CMD = "comp";
- public final static String EXPORT_CMD = "export";
- public final static String IMPORT_CMD = "import";
- public final static String WRITE_CMD = "write";
-
- /**
- * @param args
- */
- public static void main(String[] args) {
- Main m;
- if (args.length > 0 && args[0].equals("--seq")) {
- m = createSequencedMain(args);
- } else {
- m = new Main(new SwingUI());
- }
-
- top = m;
- m.startUp();
- }
-
- public Main(IUI ui) {
- this.ui = ui;
-
- clientListModel = new DefaultListModel<Client>();
- dataTableModel = new DumpTableModel();
-
- clientUtils = new ClientUtils(DEFAULT_TIMEOUT_MILLIS); // Client utils with 10s timeout.
-
- List<Action> actions = new ArrayList<Action>();
- actions.add(new ReloadListAction(clientUtils, null, clientListModel));
- actions.add(new ClearTableAction(dataTableModel));
- actions.add(new RunMonkeyAction(null, dataTableModel));
- actions.add(new ScanPackageAction(clientUtils, null, dataTableModel));
- actions.add(new ScanAllPackagesAction(clientUtils, null, dataTableModel));
- actions.add(new ComputeThresholdAction("Compute preloaded-classes", dataTableModel, 2,
- CLASS_PRELOAD_BLACKLIST));
- actions.add(new ComputeThresholdAction("Compute compiled-classes", dataTableModel, 1,
- null));
- actions.add(new ComputeThresholdXAction("Compute(X)", dataTableModel,
- CLASS_PRELOAD_BLACKLIST));
- actions.add(new WritePreloadedClassesAction(clientUtils, null, dataTableModel));
- actions.add(new ShowDataAction(dataTableModel));
- actions.add(new ImportAction(dataTableModel));
- actions.add(new ExportAction(dataTableModel));
-
- deviceSpecificActions = new ArrayList<DeviceSpecific>();
- for (Action a : actions) {
- if (a instanceof DeviceSpecific) {
- deviceSpecificActions.add((DeviceSpecific)a);
- }
- }
-
- ui.prepare(clientListModel, dataTableModel, actions);
- }
-
- /**
- * @param args
- * @return
- */
- private static Main createSequencedMain(String[] args) {
- SequenceUI ui = new SequenceUI();
- Main main = new Main(ui);
-
- Iterator<String> it = Arrays.asList(args).iterator();
- it.next(); // --seq
- // Setup
- ui.choice("#" + it.next()); // Device.
- ui.confirmNo(); // Prepare: no.
- // Actions
- try {
- while (it.hasNext()) {
- String op = it.next();
- // Operation: Scan a single package
- if (SCAN_PACKAGE_CMD.equals(op)) {
- System.out.println("Scanning package.");
- ui.action(ScanPackageAction.class);
- ui.client(it.next());
- // Operation: Scan all packages
- } else if (SCAN_ALL_CMD.equals(op)) {
- System.out.println("Scanning all packages.");
- ui.action(ScanAllPackagesAction.class);
- // Operation: Export the output to a file
- } else if (EXPORT_CMD.equals(op)) {
- System.out.println("Exporting data.");
- ui.action(ExportAction.class);
- ui.output(new File(it.next()));
- // Operation: Import the input from a file or directory
- } else if (IMPORT_CMD.equals(op)) {
- System.out.println("Importing data.");
- File file = new File(it.next());
- if (!file.exists()) {
- throw new RuntimeException(
- String.format("File does not exist, %s.", file.getAbsolutePath()));
- } else if (file.isFile()) {
- ui.action(ImportAction.class);
- ui.input(file);
- } else if (file.isDirectory()) {
- for (File content : file.listFiles()) {
- ui.action(ImportAction.class);
- ui.input(content);
- }
- }
- // Operation: Compute preloaded classes with specific threshold
- } else if (COMPUTE_FILE_CMD.equals(op)) {
- System.out.println("Compute preloaded classes.");
- ui.action(ComputeThresholdXAction.class);
- ui.input(it.next());
- ui.confirmYes();
- ui.output(new File(it.next()));
- // Operation: Write preloaded classes from a specific file
- } else if (WRITE_CMD.equals(op)) {
- System.out.println("Writing preloaded classes.");
- ui.action(WritePreloadedClassesAction.class);
- ui.input(new File(it.next()));
- }
- }
- } catch (NoSuchElementException e) {
- System.out.println("Failed to parse action sequence correctly.");
- throw e;
- }
-
- return main;
- }
-
- public static IUI getUI() {
- return top.ui;
- }
-
- public static ClassDataRetriever getClassDataRetriever() {
- if (useJdwpClassDataRetriever) {
- return new JDWPClassDataRetriever();
- } else {
- return new Hprof(HPROF_TIMEOUT_MILLIS);
- }
- }
-
- public IDevice getDevice() {
- return device;
- }
-
- public void setDevice(IDevice device) {
- this.device = device;
- for (DeviceSpecific ds : deviceSpecificActions) {
- ds.setDevice(device);
- }
- }
-
- public DefaultListModel<Client> getClientListModel() {
- return clientListModel;
- }
-
- static class DeviceWrapper {
- IDevice device;
-
- public DeviceWrapper(IDevice d) {
- device = d;
- }
-
- @Override
- public String toString() {
- return device.getName() + " (#" + device.getSerialNumber() + ")";
- }
- }
-
- private void startUp() {
- getUI().showWaitDialog();
- initDevice();
-
- // Load clients.
- new ReloadListAction(clientUtils, getDevice(), clientListModel).run();
-
- getUI().hideWaitDialog();
- getUI().ready();
- }
-
- private void initDevice() {
- DeviceUtils.init(DEFAULT_TIMEOUT_MILLIS);
-
- IDevice devices[] = DeviceUtils.findDevices(DEFAULT_TIMEOUT_MILLIS);
- if (devices == null || devices.length == 0) {
- throw new RuntimeException("Could not find any devices...");
- }
-
- getUI().hideWaitDialog();
-
- DeviceWrapper deviceWrappers[] = new DeviceWrapper[devices.length];
- for (int i = 0; i < devices.length; i++) {
- deviceWrappers[i] = new DeviceWrapper(devices[i]);
- }
-
- DeviceWrapper ret = Main.getUI().showChoiceDialog("Choose a device", "Choose device",
- deviceWrappers);
- if (ret != null) {
- setDevice(ret.device);
- } else {
- System.exit(0);
- }
-
- boolean prepare = Main.getUI().showConfirmDialog("Prepare device?",
- "Do you want to prepare the device? This is highly recommended.");
- if (prepare) {
- String buildType = DeviceUtils.getBuildType(device);
- if (buildType == null || (!buildType.equals("userdebug") && !buildType.equals("eng"))) {
- Main.getUI().showMessageDialog("Need a userdebug or eng build! (Found " + buildType
- + ")");
- return;
- }
- if (DeviceUtils.hasPrebuiltBootImage(device)) {
- Main.getUI().showMessageDialog("Cannot prepare a device with pre-optimized boot "
- + "image!");
- return;
- }
-
- if (ENABLE_TRACING) {
- DeviceUtils.enableTracing(device);
- }
-
- Main.getUI().showMessageDialog("The device will reboot. This will potentially take a "
- + "long time. Please be patient.");
- boolean success = false;
- try {
- success = DeviceUtils.overwritePreloaded(device, null, 15 * 60);
- } catch (Exception e) {
- System.err.println(e);
- } finally {
- if (!success) {
- Main.getUI().showMessageDialog(
- "Removing preloaded-classes failed unexpectedly!");
- }
- }
- }
- }
-
- public static Map<String, String> findAndGetClassData(IDevice device, String packageName)
- throws Exception {
- Client client = clientUtils.findClient(device, packageName, -1);
- if (client == null) {
- throw new RuntimeException("Could not find client...");
- }
- System.out.println("Found client: " + client);
-
- return getClassDataRetriever().getClassData(client);
- }
-
-}
diff --git a/tools/preload2/src/com/android/preload/actions/AbstractThreadedAction.java b/tools/preload2/src/com/android/preload/actions/AbstractThreadedAction.java
deleted file mode 100644
index 5787d85..0000000
--- a/tools/preload2/src/com/android/preload/actions/AbstractThreadedAction.java
+++ /dev/null
@@ -1,39 +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.preload.actions;
-
-import com.android.preload.Main;
-import java.awt.event.ActionEvent;
-
-import javax.swing.AbstractAction;
-
-public abstract class AbstractThreadedAction extends AbstractAction implements Runnable {
-
- protected AbstractThreadedAction(String title) {
- super(title);
- }
-
- @Override
- public void actionPerformed(ActionEvent e) {
- if (Main.getUI().isSingleThreaded()) {
- run();
- } else {
- new Thread(this).start();
- }
- }
-
-}
diff --git a/tools/preload2/src/com/android/preload/actions/AbstractThreadedDeviceSpecificAction.java b/tools/preload2/src/com/android/preload/actions/AbstractThreadedDeviceSpecificAction.java
deleted file mode 100644
index 7906417..0000000
--- a/tools/preload2/src/com/android/preload/actions/AbstractThreadedDeviceSpecificAction.java
+++ /dev/null
@@ -1,45 +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.preload.actions;
-
-import com.android.ddmlib.IDevice;
-
-import java.awt.event.ActionEvent;
-
-public abstract class AbstractThreadedDeviceSpecificAction extends AbstractThreadedAction
- implements DeviceSpecific {
-
- protected IDevice device;
-
- protected AbstractThreadedDeviceSpecificAction(String title, IDevice device) {
- super(title);
- this.device = device;
- }
-
- @Override
- public void setDevice(IDevice device) {
- this.device = device;
- }
-
- @Override
- public void actionPerformed(ActionEvent e) {
- if (device == null) {
- return;
- }
- super.actionPerformed(e);
- }
-}
diff --git a/tools/preload2/src/com/android/preload/actions/ClearTableAction.java b/tools/preload2/src/com/android/preload/actions/ClearTableAction.java
deleted file mode 100644
index c0e4795..0000000
--- a/tools/preload2/src/com/android/preload/actions/ClearTableAction.java
+++ /dev/null
@@ -1,37 +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.preload.actions;
-
-import com.android.preload.DumpTableModel;
-
-import java.awt.event.ActionEvent;
-
-import javax.swing.AbstractAction;
-
-public class ClearTableAction extends AbstractAction {
- private final DumpTableModel dataTableModel;
-
- public ClearTableAction(DumpTableModel dataTableModel) {
- super("Clear");
- this.dataTableModel = dataTableModel;
- }
-
- @Override
- public void actionPerformed(ActionEvent e) {
- dataTableModel.clear();
- }
-}
\ No newline at end of file
diff --git a/tools/preload2/src/com/android/preload/actions/ComputeThresholdAction.java b/tools/preload2/src/com/android/preload/actions/ComputeThresholdAction.java
deleted file mode 100644
index 3a7f7f7..0000000
--- a/tools/preload2/src/com/android/preload/actions/ComputeThresholdAction.java
+++ /dev/null
@@ -1,148 +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.preload.actions;
-
-import com.android.preload.DumpData;
-import com.android.preload.DumpTableModel;
-import com.android.preload.Main;
-
-import java.awt.event.ActionEvent;
-import java.io.File;
-import java.io.PrintWriter;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.TreeSet;
-import java.util.regex.Pattern;
-
-import javax.swing.AbstractAction;
-
-/**
- * Compute an intersection of classes from the given data. A class is in the intersection if it
- * appears in at least the number of threshold given packages. An optional blacklist can be
- * used to filter classes from the intersection.
- */
-public class ComputeThresholdAction extends AbstractThreadedAction {
- protected int threshold;
- private Pattern blacklist;
- private DumpTableModel dataTableModel;
-
- /**
- * Create an action with the given parameters. The blacklist is a regular expression
- * that filters classes.
- */
- public ComputeThresholdAction(String name, DumpTableModel dataTableModel, int threshold,
- String blacklist) {
- super(name);
- this.dataTableModel = dataTableModel;
- this.threshold = threshold;
- if (blacklist != null) {
- this.blacklist = Pattern.compile(blacklist);
- }
- }
-
- @Override
- public void actionPerformed(ActionEvent e) {
- List<DumpData> data = dataTableModel.getData();
- if (data.size() == 0) {
- Main.getUI().showMessageDialog("No data available, please scan packages or run "
- + "monkeys.");
- return;
- }
- if (data.size() == 1) {
- Main.getUI().showMessageDialog("Cannot compute list from only one data set, please "
- + "scan packages or run monkeys.");
- return;
- }
-
- super.actionPerformed(e);
- }
-
- @Override
- public void run() {
- Main.getUI().showWaitDialog();
-
- Map<String, Set<String>> uses = new HashMap<String, Set<String>>();
- for (DumpData d : dataTableModel.getData()) {
- Main.getUI().updateWaitDialog("Merging " + d.getPackageName());
- updateClassUse(d.getPackageName(), uses, getBootClassPathClasses(d.getDumpData()));
- }
-
- Main.getUI().updateWaitDialog("Computing thresholded set");
- Set<String> result = fromThreshold(uses, blacklist, threshold);
- Main.getUI().hideWaitDialog();
-
- boolean ret = Main.getUI().showConfirmDialog("Computed a set with " + result.size()
- + " classes, would you like to save to disk?", "Save?");
- if (ret) {
- File f = Main.getUI().showSaveDialog();
- if (f != null) {
- saveSet(result, f);
- }
- }
- }
-
- private Set<String> fromThreshold(Map<String, Set<String>> classUses, Pattern blacklist,
- int threshold) {
- TreeSet<String> ret = new TreeSet<>(); // TreeSet so it's nicely ordered by name.
-
- for (Map.Entry<String, Set<String>> e : classUses.entrySet()) {
- if (e.getValue().size() >= threshold) {
- if (blacklist == null || !blacklist.matcher(e.getKey()).matches()) {
- ret.add(e.getKey());
- }
- }
- }
-
- return ret;
- }
-
- private static void updateClassUse(String pkg, Map<String, Set<String>> classUses,
- Set<String> classes) {
- for (String className : classes) {
- Set<String> old = classUses.get(className);
- if (old == null) {
- classUses.put(className, new HashSet<String>());
- }
- classUses.get(className).add(pkg);
- }
- }
-
- private static Set<String> getBootClassPathClasses(Map<String, String> source) {
- Set<String> ret = new HashSet<>();
- for (Map.Entry<String, String> e : source.entrySet()) {
- if (e.getValue() == null) {
- ret.add(e.getKey());
- }
- }
- return ret;
- }
-
- private static void saveSet(Set<String> result, File f) {
- try {
- PrintWriter out = new PrintWriter(f);
- for (String s : result) {
- out.println(s);
- }
- out.close();
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-}
\ No newline at end of file
diff --git a/tools/preload2/src/com/android/preload/actions/ComputeThresholdXAction.java b/tools/preload2/src/com/android/preload/actions/ComputeThresholdXAction.java
deleted file mode 100644
index 3ec0a4c..0000000
--- a/tools/preload2/src/com/android/preload/actions/ComputeThresholdXAction.java
+++ /dev/null
@@ -1,41 +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.preload.actions;
-
-import com.android.preload.DumpTableModel;
-import com.android.preload.Main;
-
-public class ComputeThresholdXAction extends ComputeThresholdAction {
-
- public ComputeThresholdXAction(String name, DumpTableModel dataTableModel,
- String blacklist) {
- super(name, dataTableModel, 1, blacklist);
- }
-
- @Override
- public void run() {
- String value = Main.getUI().showInputDialog("Threshold?");
-
- if (value != null) {
- try {
- threshold = Integer.parseInt(value);
- super.run();
- } catch (Exception exc) {
- }
- }
- }
-}
\ No newline at end of file
diff --git a/tools/preload2/src/com/android/preload/actions/DeviceSpecific.java b/tools/preload2/src/com/android/preload/actions/DeviceSpecific.java
deleted file mode 100644
index 35a8f26..0000000
--- a/tools/preload2/src/com/android/preload/actions/DeviceSpecific.java
+++ /dev/null
@@ -1,38 +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.preload.actions;
-
-import com.android.ddmlib.IDevice;
-
-/**
- * Marks an action as being device-specific. The user must set the device through the specified
- * method if the device selection changes.
- *
- * Implementors must tolerate a null device (for example, with a no-op). This includes calling
- * any methods before setDevice has been called.
- */
-public interface DeviceSpecific {
-
- /**
- * Set the device that should be used. Note that there is no restriction on calling other
- * methods of the implementor before a setDevice call. Neither is device guaranteed to be
- * non-null.
- *
- * @param device The device to use going forward.
- */
- public void setDevice(IDevice device);
-}
diff --git a/tools/preload2/src/com/android/preload/actions/ExportAction.java b/tools/preload2/src/com/android/preload/actions/ExportAction.java
deleted file mode 100644
index 848a568..0000000
--- a/tools/preload2/src/com/android/preload/actions/ExportAction.java
+++ /dev/null
@@ -1,62 +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.preload.actions;
-
-import com.android.preload.DumpDataIO;
-import com.android.preload.DumpTableModel;
-import com.android.preload.Main;
-import java.awt.event.ActionEvent;
-import java.io.File;
-import java.io.PrintWriter;
-
-public class ExportAction extends AbstractThreadedAction {
- private File lastSaveFile;
- private DumpTableModel dataTableModel;
-
- public ExportAction(DumpTableModel dataTableModel) {
- super("Export data");
- this.dataTableModel = dataTableModel;
- }
-
- @Override
- public void actionPerformed(ActionEvent e) {
- lastSaveFile = Main.getUI().showSaveDialog();
- if (lastSaveFile != null) {
- super.actionPerformed(e);
- }
- }
-
- @Override
- public void run() {
- Main.getUI().showWaitDialog();
-
- String serialized = DumpDataIO.serialize(dataTableModel.getData());
-
- if (serialized != null) {
- try {
- PrintWriter out = new PrintWriter(lastSaveFile);
- out.println(serialized);
- out.close();
-
- Main.getUI().hideWaitDialog();
- } catch (Exception e) {
- Main.getUI().hideWaitDialog();
- Main.getUI().showMessageDialog("Failed writing: " + e.getMessage());
- }
- }
- }
-}
\ No newline at end of file
diff --git a/tools/preload2/src/com/android/preload/actions/ImportAction.java b/tools/preload2/src/com/android/preload/actions/ImportAction.java
deleted file mode 100644
index bfeeb83..0000000
--- a/tools/preload2/src/com/android/preload/actions/ImportAction.java
+++ /dev/null
@@ -1,68 +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.preload.actions;
-
-import com.android.preload.DumpData;
-import com.android.preload.DumpDataIO;
-import com.android.preload.DumpTableModel;
-import com.android.preload.Main;
-
-import java.awt.event.ActionEvent;
-import java.io.File;
-import java.util.Collection;
-
-import javax.swing.AbstractAction;
-
-public class ImportAction extends AbstractThreadedAction {
- private File[] lastOpenFiles;
- private DumpTableModel dataTableModel;
-
- public ImportAction(DumpTableModel dataTableModel) {
- super("Import data");
- this.dataTableModel = dataTableModel;
- }
-
- @Override
- public void actionPerformed(ActionEvent e) {
- lastOpenFiles = Main.getUI().showOpenDialog(true);
- if (lastOpenFiles != null) {
- super.actionPerformed(e);
- }
- }
-
- @Override
- public void run() {
- Main.getUI().showWaitDialog();
-
- try {
- for (File f : lastOpenFiles) {
- try {
- Collection<DumpData> data = DumpDataIO.deserialize(f);
-
- for (DumpData d : data) {
- dataTableModel.addData(d);
- }
- } catch (Exception e) {
- Main.getUI().showMessageDialog("Failed reading: " + e.getMessage());
- }
- }
- } finally {
- Main.getUI().hideWaitDialog();
- }
-
- }
-}
\ No newline at end of file
diff --git a/tools/preload2/src/com/android/preload/actions/ReloadListAction.java b/tools/preload2/src/com/android/preload/actions/ReloadListAction.java
deleted file mode 100644
index 29f0557..0000000
--- a/tools/preload2/src/com/android/preload/actions/ReloadListAction.java
+++ /dev/null
@@ -1,68 +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.preload.actions;
-
-import com.android.ddmlib.Client;
-import com.android.ddmlib.IDevice;
-import com.android.preload.ClientUtils;
-
-import java.util.Arrays;
-import java.util.Comparator;
-
-import javax.swing.DefaultListModel;
-
-public class ReloadListAction extends AbstractThreadedDeviceSpecificAction {
-
- private ClientUtils clientUtils;
- private final DefaultListModel<Client> clientListModel;
-
- public ReloadListAction(ClientUtils utils, IDevice device,
- DefaultListModel<Client> clientListModel) {
- super("Reload", device);
- this.clientUtils = utils;
- this.clientListModel = clientListModel;
- }
-
- @Override
- public void run() {
- Client[] clients = clientUtils.findAllClients(device);
- if (clients != null) {
- Arrays.sort(clients, new ClientComparator());
- }
- clientListModel.removeAllElements();
- for (Client c : clients) {
- clientListModel.addElement(c);
- }
- }
-
- private static class ClientComparator implements Comparator<Client> {
-
- @Override
- public int compare(Client o1, Client o2) {
- String s1 = o1.getClientData().getClientDescription();
- String s2 = o2.getClientData().getClientDescription();
-
- if (s1 == null || s2 == null) {
- // Not good, didn't get all data?
- return (s1 == null) ? -1 : 1;
- }
-
- return s1.compareTo(s2);
- }
-
- }
-}
\ No newline at end of file
diff --git a/tools/preload2/src/com/android/preload/actions/RunMonkeyAction.java b/tools/preload2/src/com/android/preload/actions/RunMonkeyAction.java
deleted file mode 100644
index 29464fc..0000000
--- a/tools/preload2/src/com/android/preload/actions/RunMonkeyAction.java
+++ /dev/null
@@ -1,124 +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.preload.actions;
-
-import com.android.ddmlib.IDevice;
-import com.android.preload.DeviceUtils;
-import com.android.preload.DumpData;
-import com.android.preload.DumpTableModel;
-import com.android.preload.Main;
-
-import java.awt.event.ActionEvent;
-import java.util.Date;
-import java.util.Map;
-import java.util.concurrent.TimeUnit;
-
-import javax.swing.AbstractAction;
-
-public class RunMonkeyAction extends AbstractAction implements DeviceSpecific {
-
- private final static String DEFAULT_MONKEY_PACKAGES =
- "com.android.calendar,com.android.gallery3d";
-
- private IDevice device;
- private DumpTableModel dataTableModel;
-
- public RunMonkeyAction(IDevice device, DumpTableModel dataTableModel) {
- super("Run monkey");
- this.device = device;
- this.dataTableModel = dataTableModel;
- }
-
- @Override
- public void setDevice(IDevice device) {
- this.device = device;
- }
-
- @Override
- public void actionPerformed(ActionEvent e) {
- String packages = Main.getUI().showInputDialog("Please enter packages name to run with"
- + " the monkey, or leave empty for default.");
- if (packages == null) {
- return;
- }
- if (packages.isEmpty()) {
- packages = DEFAULT_MONKEY_PACKAGES;
- }
- Runnable r = new RunMonkeyRunnable(packages);
- if (Main.getUI().isSingleThreaded()) {
- r.run();
- } else {
- new Thread(r).start();
- }
- }
-
- private class RunMonkeyRunnable implements Runnable {
-
- private String packages;
- private final static int ITERATIONS = 1000;
-
- public RunMonkeyRunnable(String packages) {
- this.packages = packages;
- }
-
- @Override
- public void run() {
- Main.getUI().showWaitDialog();
-
- try {
- String pkgs[] = packages.split(",");
-
- for (String pkg : pkgs) {
- Main.getUI().updateWaitDialog("Running monkey on " + pkg);
-
- try {
- // Stop running app.
- forceStop(pkg);
-
- // Little bit of breather here.
- try {
- Thread.sleep(1000);
- } catch (Exception e) {
- }
-
- DeviceUtils.doShell(device, "monkey -p " + pkg + " " + ITERATIONS, 1,
- TimeUnit.MINUTES);
-
- Main.getUI().updateWaitDialog("Retrieving heap data for " + pkg);
- Map<String, String> data = Main.findAndGetClassData(device, pkg);
- DumpData dumpData = new DumpData(pkg, data, new Date());
- dataTableModel.addData(dumpData);
- } catch (Exception e) {
- e.printStackTrace();
- } finally {
- // Stop running app.
- forceStop(pkg);
- }
- }
- } finally {
- Main.getUI().hideWaitDialog();
- }
- }
-
- private void forceStop(String packageName) {
- // Stop running app.
- DeviceUtils.doShell(device, "force-stop " + packageName, 5, TimeUnit.SECONDS);
- DeviceUtils.doShell(device, "kill " + packageName, 5, TimeUnit.SECONDS);
- DeviceUtils.doShell(device, "kill `pid " + packageName + "`", 5, TimeUnit.SECONDS);
- }
- }
-}
\ No newline at end of file
diff --git a/tools/preload2/src/com/android/preload/actions/ScanAllPackagesAction.java b/tools/preload2/src/com/android/preload/actions/ScanAllPackagesAction.java
deleted file mode 100644
index d74b8a3..0000000
--- a/tools/preload2/src/com/android/preload/actions/ScanAllPackagesAction.java
+++ /dev/null
@@ -1,63 +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.preload.actions;
-
-import com.android.ddmlib.Client;
-import com.android.ddmlib.IDevice;
-import com.android.preload.ClientUtils;
-import com.android.preload.DumpData;
-import com.android.preload.DumpTableModel;
-import com.android.preload.Main;
-
-import java.util.Date;
-import java.util.Map;
-
-public class ScanAllPackagesAction extends AbstractThreadedDeviceSpecificAction {
-
- private ClientUtils clientUtils;
- private DumpTableModel dataTableModel;
-
- public ScanAllPackagesAction(ClientUtils utils, IDevice device, DumpTableModel dataTableModel) {
- super("Scan all packages", device);
- this.clientUtils = utils;
- this.dataTableModel = dataTableModel;
- }
-
- @Override
- public void run() {
- Main.getUI().showWaitDialog();
-
- try {
- Client[] clients = clientUtils.findAllClients(device);
- for (Client c : clients) {
- String pkg = c.getClientData().getClientDescription();
- Main.getUI().showWaitDialog();
- Main.getUI().updateWaitDialog("Retrieving heap data for " + pkg);
-
- try {
- Map<String, String> data = Main.getClassDataRetriever().getClassData(c);
- DumpData dumpData = new DumpData(pkg, data, new Date());
- dataTableModel.addData(dumpData);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- } finally {
- Main.getUI().hideWaitDialog();
- }
- }
-}
\ No newline at end of file
diff --git a/tools/preload2/src/com/android/preload/actions/ScanPackageAction.java b/tools/preload2/src/com/android/preload/actions/ScanPackageAction.java
deleted file mode 100644
index 98492bd..0000000
--- a/tools/preload2/src/com/android/preload/actions/ScanPackageAction.java
+++ /dev/null
@@ -1,97 +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.preload.actions;
-
-import com.android.ddmlib.Client;
-import com.android.ddmlib.IDevice;
-import com.android.preload.ClientUtils;
-import com.android.preload.DumpData;
-import com.android.preload.DumpTableModel;
-import com.android.preload.Main;
-
-import java.util.Date;
-import java.util.Map;
-
-public class ScanPackageAction extends AbstractThreadedDeviceSpecificAction {
-
- private ClientUtils clientUtils;
- private DumpTableModel dataTableModel;
-
- public ScanPackageAction(ClientUtils utils, IDevice device, DumpTableModel dataTableModel) {
- super("Scan package", device);
- this.clientUtils = utils;
- this.dataTableModel = dataTableModel;
- }
-
- @Override
- public void run() {
- Main.getUI().showWaitDialog();
-
- try {
- Client client = Main.getUI().getSelectedClient();
- if (client != null) {
- work(client);
- } else {
- Client[] clients = clientUtils.findAllClients(device);
- if (clients.length > 0) {
- ClientWrapper[] clientWrappers = new ClientWrapper[clients.length];
- for (int i = 0; i < clientWrappers.length; i++) {
- clientWrappers[i] = new ClientWrapper(clients[i]);
- }
- Main.getUI().hideWaitDialog();
-
- ClientWrapper ret = Main.getUI().showChoiceDialog("Choose a package to scan",
- "Choose package",
- clientWrappers);
- if (ret != null) {
- work(ret.client);
- }
- }
- }
- } finally {
- Main.getUI().hideWaitDialog();
- }
- }
-
- private void work(Client c) {
- String pkg = c.getClientData().getClientDescription();
- Main.getUI().showWaitDialog();
- Main.getUI().updateWaitDialog("Retrieving heap data for " + pkg);
-
- try {
- Map<String, String> data = Main.findAndGetClassData(device, pkg);
- DumpData dumpData = new DumpData(pkg, data, new Date());
- dataTableModel.addData(dumpData);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-
- private static class ClientWrapper {
- private Client client;
-
- public ClientWrapper(Client c) {
- client = c;
- }
-
- @Override
- public String toString() {
- return client.getClientData().getClientDescription() + " (pid "
- + client.getClientData().getPid() + ")";
- }
- }
-}
\ No newline at end of file
diff --git a/tools/preload2/src/com/android/preload/actions/ShowDataAction.java b/tools/preload2/src/com/android/preload/actions/ShowDataAction.java
deleted file mode 100644
index 2bb175f..0000000
--- a/tools/preload2/src/com/android/preload/actions/ShowDataAction.java
+++ /dev/null
@@ -1,94 +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.preload.actions;
-
-import com.android.preload.DumpData;
-import com.android.preload.DumpTableModel;
-import com.android.preload.Main;
-
-import java.awt.BorderLayout;
-import java.awt.event.ActionEvent;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import javax.swing.AbstractAction;
-import javax.swing.JFrame;
-import javax.swing.JScrollPane;
-import javax.swing.JTextArea;
-
-public class ShowDataAction extends AbstractAction {
- private DumpTableModel dataTableModel;
-
- public ShowDataAction(DumpTableModel dataTableModel) {
- super("Show data");
- this.dataTableModel = dataTableModel;
- }
-
- @Override
- public void actionPerformed(ActionEvent e) {
- // TODO(agampe): Auto-generated method stub
- int selRow = Main.getUI().getSelectedDataTableRow();
- if (selRow != -1) {
- DumpData data = dataTableModel.getData().get(selRow);
- Map<String, Set<String>> inv = data.invertData();
-
- StringBuilder builder = new StringBuilder();
-
- // First bootclasspath.
- add(builder, "Boot classpath:", inv.get(null));
-
- // Now everything else.
- for (String k : inv.keySet()) {
- if (k != null) {
- builder.append("==================\n\n");
- add(builder, k, inv.get(k));
- }
- }
-
- JFrame newFrame = new JFrame(data.getPackageName() + " " + data.getDate());
- newFrame.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
- newFrame.getContentPane().add(new JScrollPane(new JTextArea(builder.toString())),
- BorderLayout.CENTER);
- newFrame.setSize(800, 600);
- newFrame.setLocationRelativeTo(null);
- newFrame.setVisible(true);
- }
- }
-
- private void add(StringBuilder builder, String head, Set<String> set) {
- builder.append(head);
- builder.append('\n');
- addSet(builder, set);
- builder.append('\n');
- }
-
- private void addSet(StringBuilder builder, Set<String> set) {
- if (set == null) {
- builder.append(" NONE\n");
- return;
- }
- List<String> sorted = new ArrayList<>(set);
- Collections.sort(sorted);
- for (String s : sorted) {
- builder.append(s);
- builder.append('\n');
- }
- }
-}
\ No newline at end of file
diff --git a/tools/preload2/src/com/android/preload/actions/WritePreloadedClassesAction.java b/tools/preload2/src/com/android/preload/actions/WritePreloadedClassesAction.java
deleted file mode 100644
index 9b97f11..0000000
--- a/tools/preload2/src/com/android/preload/actions/WritePreloadedClassesAction.java
+++ /dev/null
@@ -1,60 +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.preload.actions;
-
-import com.android.ddmlib.Client;
-import com.android.ddmlib.IDevice;
-import com.android.preload.ClientUtils;
-import com.android.preload.DeviceUtils;
-import com.android.preload.DumpData;
-import com.android.preload.DumpTableModel;
-import com.android.preload.Main;
-
-import java.awt.event.ActionEvent;
-import java.io.File;
-import java.util.Date;
-import java.util.Map;
-
-public class WritePreloadedClassesAction extends AbstractThreadedDeviceSpecificAction {
- private File preloadedClassFile;
-
- public WritePreloadedClassesAction(ClientUtils utils, IDevice device, DumpTableModel dataTableModel) {
- super("Write preloaded classes action", device);
- }
-
- @Override
- public void actionPerformed(ActionEvent e) {
- File[] files = Main.getUI().showOpenDialog(true);
- if (files != null && files.length > 0) {
- preloadedClassFile = files[0];
- super.actionPerformed(e);
- }
- }
-
- @Override
- public void run() {
- Main.getUI().showWaitDialog();
- try {
- // Write the new file with a 5-minute timeout
- DeviceUtils.overwritePreloaded(device, preloadedClassFile, 5 * 60);
- } catch (Exception e) {
- System.err.println(e);
- } finally {
- Main.getUI().hideWaitDialog();
- }
- }
-}
diff --git a/tools/preload2/src/com/android/preload/classdataretrieval/hprof/GeneralHprofDumpHandler.java b/tools/preload2/src/com/android/preload/classdataretrieval/hprof/GeneralHprofDumpHandler.java
deleted file mode 100644
index 8d797ee..0000000
--- a/tools/preload2/src/com/android/preload/classdataretrieval/hprof/GeneralHprofDumpHandler.java
+++ /dev/null
@@ -1,70 +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.preload.classdataretrieval.hprof;
-
-import com.android.ddmlib.Client;
-import com.android.ddmlib.ClientData.IHprofDumpHandler;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class GeneralHprofDumpHandler implements IHprofDumpHandler {
-
- private List<IHprofDumpHandler> handlers = new ArrayList<>();
-
- public void addHandler(IHprofDumpHandler h) {
- synchronized (handlers) {
- handlers.add(h);
- }
- }
-
- public void removeHandler(IHprofDumpHandler h) {
- synchronized (handlers) {
- handlers.remove(h);
- }
- }
-
- private List<IHprofDumpHandler> getIterationList() {
- synchronized (handlers) {
- return new ArrayList<>(handlers);
- }
- }
-
- @Override
- public void onEndFailure(Client arg0, String arg1) {
- List<IHprofDumpHandler> iterList = getIterationList();
- for (IHprofDumpHandler h : iterList) {
- h.onEndFailure(arg0, arg1);
- }
- }
-
- @Override
- public void onSuccess(String arg0, Client arg1) {
- List<IHprofDumpHandler> iterList = getIterationList();
- for (IHprofDumpHandler h : iterList) {
- h.onSuccess(arg0, arg1);
- }
- }
-
- @Override
- public void onSuccess(byte[] arg0, Client arg1) {
- List<IHprofDumpHandler> iterList = getIterationList();
- for (IHprofDumpHandler h : iterList) {
- h.onSuccess(arg0, arg1);
- }
- }
- }
\ No newline at end of file
diff --git a/tools/preload2/src/com/android/preload/classdataretrieval/hprof/Hprof.java b/tools/preload2/src/com/android/preload/classdataretrieval/hprof/Hprof.java
deleted file mode 100644
index 84ec8b7..0000000
--- a/tools/preload2/src/com/android/preload/classdataretrieval/hprof/Hprof.java
+++ /dev/null
@@ -1,228 +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.preload.classdataretrieval.hprof;
-
-import com.android.ddmlib.Client;
-import com.android.ddmlib.ClientData;
-import com.android.ddmlib.ClientData.IHprofDumpHandler;
-import com.android.preload.classdataretrieval.ClassDataRetriever;
-import com.android.preload.ui.NullProgressMonitor;
-import com.android.tools.perflib.captures.MemoryMappedFileBuffer;
-import com.android.tools.perflib.heap.ClassObj;
-import com.android.tools.perflib.heap.Queries;
-import com.android.tools.perflib.heap.Snapshot;
-
-import java.io.BufferedOutputStream;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Set;
-
-public class Hprof implements ClassDataRetriever {
-
- private static GeneralHprofDumpHandler hprofHandler;
-
- public static void init() {
- synchronized(Hprof.class) {
- if (hprofHandler == null) {
- ClientData.setHprofDumpHandler(hprofHandler = new GeneralHprofDumpHandler());
- }
- }
- }
-
- public static File doHprof(Client client, int timeout) {
- GetHprof gh = new GetHprof(client, timeout);
- return gh.get();
- }
-
- /**
- * Return a map of class names to class-loader names derived from the hprof dump.
- *
- * @param hprofLocalFile
- */
- public static Map<String, String> analyzeHprof(File hprofLocalFile) throws Exception {
- Snapshot snapshot = Snapshot.createSnapshot(new MemoryMappedFileBuffer(hprofLocalFile));
-
- Map<String, Set<ClassObj>> classes = Queries.classes(snapshot, null);
- Map<String, String> retValue = new HashMap<String, String>();
- for (Map.Entry<String, Set<ClassObj>> e : classes.entrySet()) {
- for (ClassObj c : e.getValue()) {
- String cl = c.getClassLoader() == null ? null : c.getClassLoader().toString();
- String cName = c.getClassName();
- int aDepth = 0;
- while (cName.endsWith("[]")) {
- cName = cName.substring(0, cName.length()-2);
- aDepth++;
- }
- String newName = transformPrimitiveClass(cName);
- if (aDepth > 0) {
- // Need to use kind-a descriptor syntax. If it was transformed, it is primitive.
- if (newName.equals(cName)) {
- newName = "L" + newName + ";";
- }
- for (int i = 0; i < aDepth; i++) {
- newName = "[" + newName;
- }
- }
- retValue.put(newName, cl);
- }
- }
-
- // Free up memory.
- snapshot.dispose();
-
- return retValue;
- }
-
- private static Map<String, String> primitiveMapping;
-
- static {
- primitiveMapping = new HashMap<>();
- primitiveMapping.put("boolean", "Z");
- primitiveMapping.put("byte", "B");
- primitiveMapping.put("char", "C");
- primitiveMapping.put("double", "D");
- primitiveMapping.put("float", "F");
- primitiveMapping.put("int", "I");
- primitiveMapping.put("long", "J");
- primitiveMapping.put("short", "S");
- primitiveMapping.put("void", "V");
- }
-
- private static String transformPrimitiveClass(String name) {
- String rep = primitiveMapping.get(name);
- if (rep != null) {
- return rep;
- }
- return name;
- }
-
- private static class GetHprof implements IHprofDumpHandler {
-
- private File target;
- private long timeout;
- private Client client;
-
- public GetHprof(Client client, long timeout) {
- this.client = client;
- this.timeout = timeout;
- }
-
- public File get() {
- synchronized (this) {
- hprofHandler.addHandler(this);
- client.dumpHprof();
- if (target == null) {
- try {
- wait(timeout);
- } catch (Exception e) {
- System.out.println(e);
- }
- }
- }
-
- hprofHandler.removeHandler(this);
- return target;
- }
-
- private void wakeUp() {
- synchronized (this) {
- notifyAll();
- }
- }
-
- @Override
- public void onEndFailure(Client arg0, String arg1) {
- System.out.println("GetHprof.onEndFailure");
- if (client == arg0) {
- wakeUp();
- }
- }
-
- private static File createTargetFile() {
- try {
- return File.createTempFile("ddms", ".hprof");
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
-
- @Override
- public void onSuccess(String arg0, Client arg1) {
- System.out.println("GetHprof.onSuccess");
- if (client == arg1) {
- try {
- target = createTargetFile();
- arg1.getDevice().getSyncService().pullFile(arg0,
- target.getAbsoluteFile().toString(), new NullProgressMonitor());
- } catch (Exception e) {
- if (target != null) {
- target.delete();
- }
- e.printStackTrace();
- target = null;
- }
- wakeUp();
- }
- }
-
- @Override
- public void onSuccess(byte[] arg0, Client arg1) {
- System.out.println("GetHprof.onSuccess");
- if (client == arg1) {
- try {
- target = createTargetFile();
- BufferedOutputStream out =
- new BufferedOutputStream(new FileOutputStream(target));
- out.write(arg0);
- out.close();
- } catch (Exception e) {
- if (target != null) {
- target.delete();
- }
- e.printStackTrace();
- target = null;
- }
- wakeUp();
- }
- }
- }
-
- private int timeout;
-
- public Hprof(int timeout) {
- this.timeout = timeout;
- }
-
- @Override
- public Map<String, String> getClassData(Client client) {
- File hprofLocalFile = Hprof.doHprof(client, timeout);
- if (hprofLocalFile == null) {
- throw new RuntimeException("Failed getting dump...");
- }
- System.out.println("Dump file is " + hprofLocalFile);
-
- try {
- return analyzeHprof(hprofLocalFile);
- } catch (Exception e) {
- throw new RuntimeException(e);
- } finally {
- hprofLocalFile.delete();
- }
- }
-}
diff --git a/tools/preload2/src/com/android/preload/classdataretrieval/jdwp/JDWPClassDataRetriever.java b/tools/preload2/src/com/android/preload/classdataretrieval/jdwp/JDWPClassDataRetriever.java
deleted file mode 100644
index dbd4c89..0000000
--- a/tools/preload2/src/com/android/preload/classdataretrieval/jdwp/JDWPClassDataRetriever.java
+++ /dev/null
@@ -1,221 +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.preload.classdataretrieval.jdwp;
-
-import com.android.ddmlib.Client;
-import com.android.preload.classdataretrieval.ClassDataRetriever;
-
-import org.apache.harmony.jpda.tests.framework.jdwp.CommandPacket;
-import org.apache.harmony.jpda.tests.framework.jdwp.JDWPCommands;
-import org.apache.harmony.jpda.tests.framework.jdwp.JDWPConstants;
-import org.apache.harmony.jpda.tests.framework.jdwp.ReplyPacket;
-import org.apache.harmony.jpda.tests.jdwp.share.JDWPTestCase;
-import org.apache.harmony.jpda.tests.jdwp.share.JDWPUnitDebuggeeWrapper;
-import org.apache.harmony.jpda.tests.share.JPDALogWriter;
-import org.apache.harmony.jpda.tests.share.JPDATestOptions;
-
-import java.util.HashMap;
-import java.util.Map;
-
-public class JDWPClassDataRetriever extends JDWPTestCase implements ClassDataRetriever {
-
- private final Client client;
-
- public JDWPClassDataRetriever() {
- this(null);
- }
-
- public JDWPClassDataRetriever(Client client) {
- this.client = client;
- }
-
-
- @Override
- protected String getDebuggeeClassName() {
- return "<unset>";
- }
-
- @Override
- public Map<String, String> getClassData(Client client) {
- return new JDWPClassDataRetriever(client).retrieve();
- }
-
- private Map<String, String> retrieve() {
- if (client == null) {
- throw new IllegalStateException();
- }
-
- settings = createTestOptions("localhost:" + String.valueOf(client.getDebuggerListenPort()));
- settings.setDebuggeeSuspend("n");
-
- logWriter = new JPDALogWriter(System.out, "", false);
-
- try {
- internalSetUp();
-
- return retrieveImpl();
- } catch (Exception e) {
- e.printStackTrace();
- return null;
- } finally {
- internalTearDown();
- }
- }
-
- private Map<String, String> retrieveImpl() {
- try {
- // Suspend the app.
- {
- CommandPacket packet = new CommandPacket(
- JDWPCommands.VirtualMachineCommandSet.CommandSetID,
- JDWPCommands.VirtualMachineCommandSet.SuspendCommand);
- ReplyPacket reply = debuggeeWrapper.vmMirror.performCommand(packet);
- if (reply.getErrorCode() != JDWPConstants.Error.NONE) {
- return null;
- }
- }
-
- // List all classes.
- CommandPacket packet = new CommandPacket(
- JDWPCommands.VirtualMachineCommandSet.CommandSetID,
- JDWPCommands.VirtualMachineCommandSet.AllClassesCommand);
- ReplyPacket reply = debuggeeWrapper.vmMirror.performCommand(packet);
-
- if (reply.getErrorCode() != JDWPConstants.Error.NONE) {
- return null;
- }
-
- int classCount = reply.getNextValueAsInt();
- System.out.println("Runtime reported " + classCount + " classes.");
-
- Map<Long, String> classes = new HashMap<Long, String>();
- Map<Long, String> arrayClasses = new HashMap<Long, String>();
-
- for (int i = 0; i < classCount; i++) {
- byte refTypeTag = reply.getNextValueAsByte();
- long typeID = reply.getNextValueAsReferenceTypeID();
- String signature = reply.getNextValueAsString();
- /* int status = */ reply.getNextValueAsInt();
-
- switch (refTypeTag) {
- case JDWPConstants.TypeTag.CLASS:
- case JDWPConstants.TypeTag.INTERFACE:
- classes.put(typeID, signature);
- break;
-
- case JDWPConstants.TypeTag.ARRAY:
- arrayClasses.put(typeID, signature);
- break;
- }
- }
-
- Map<String, String> result = new HashMap<String, String>();
-
- // Parse all classes.
- for (Map.Entry<Long, String> entry : classes.entrySet()) {
- long typeID = entry.getKey();
- String signature = entry.getValue();
-
- if (!checkClass(typeID, signature, result)) {
- System.err.println("Issue investigating " + signature);
- }
- }
-
- // For arrays, look at the leaf component type.
- for (Map.Entry<Long, String> entry : arrayClasses.entrySet()) {
- long typeID = entry.getKey();
- String signature = entry.getValue();
-
- if (!checkArrayClass(typeID, signature, result)) {
- System.err.println("Issue investigating " + signature);
- }
- }
-
- return result;
- } finally {
- // Resume the app.
- {
- CommandPacket packet = new CommandPacket(
- JDWPCommands.VirtualMachineCommandSet.CommandSetID,
- JDWPCommands.VirtualMachineCommandSet.ResumeCommand);
- /* ReplyPacket reply = */ debuggeeWrapper.vmMirror.performCommand(packet);
- }
- }
- }
-
- private boolean checkClass(long typeID, String signature, Map<String, String> result) {
- CommandPacket packet = new CommandPacket(
- JDWPCommands.ReferenceTypeCommandSet.CommandSetID,
- JDWPCommands.ReferenceTypeCommandSet.ClassLoaderCommand);
- packet.setNextValueAsReferenceTypeID(typeID);
- ReplyPacket reply = debuggeeWrapper.vmMirror.performCommand(packet);
- if (reply.getErrorCode() != JDWPConstants.Error.NONE) {
- return false;
- }
-
- long classLoaderID = reply.getNextValueAsObjectID();
-
- // TODO: Investigate the classloader to have a better string?
- String classLoaderString = (classLoaderID == 0) ? null : String.valueOf(classLoaderID);
-
- result.put(getClassName(signature), classLoaderString);
-
- return true;
- }
-
- private boolean checkArrayClass(long typeID, String signature, Map<String, String> result) {
- // Classloaders of array classes are the same as the component class'.
- CommandPacket packet = new CommandPacket(
- JDWPCommands.ReferenceTypeCommandSet.CommandSetID,
- JDWPCommands.ReferenceTypeCommandSet.ClassLoaderCommand);
- packet.setNextValueAsReferenceTypeID(typeID);
- ReplyPacket reply = debuggeeWrapper.vmMirror.performCommand(packet);
- if (reply.getErrorCode() != JDWPConstants.Error.NONE) {
- return false;
- }
-
- long classLoaderID = reply.getNextValueAsObjectID();
-
- // TODO: Investigate the classloader to have a better string?
- String classLoaderString = (classLoaderID == 0) ? null : String.valueOf(classLoaderID);
-
- // For array classes, we *need* the signature directly.
- result.put(signature, classLoaderString);
-
- return true;
- }
-
- private static String getClassName(String signature) {
- String withoutLAndSemicolon = signature.substring(1, signature.length() - 1);
- return withoutLAndSemicolon.replace('/', '.');
- }
-
-
- private static JPDATestOptions createTestOptions(String address) {
- JPDATestOptions options = new JPDATestOptions();
- options.setAttachConnectorKind();
- options.setTimeout(1000);
- options.setWaitingTime(1000);
- options.setTransportAddress(address);
- return options;
- }
-
- @Override
- protected JDWPUnitDebuggeeWrapper createDebuggeeWrapper() {
- return new PreloadDebugeeWrapper(settings, logWriter);
- }
-}
diff --git a/tools/preload2/src/com/android/preload/classdataretrieval/jdwp/PreloadDebugeeWrapper.java b/tools/preload2/src/com/android/preload/classdataretrieval/jdwp/PreloadDebugeeWrapper.java
deleted file mode 100644
index b9df6d0..0000000
--- a/tools/preload2/src/com/android/preload/classdataretrieval/jdwp/PreloadDebugeeWrapper.java
+++ /dev/null
@@ -1,40 +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.preload.classdataretrieval.jdwp;
-
-import org.apache.harmony.jpda.tests.framework.LogWriter;
-import org.apache.harmony.jpda.tests.jdwp.share.JDWPManualDebuggeeWrapper;
-import org.apache.harmony.jpda.tests.share.JPDATestOptions;
-
-import java.io.IOException;
-
-public class PreloadDebugeeWrapper extends JDWPManualDebuggeeWrapper {
-
- public PreloadDebugeeWrapper(JPDATestOptions options, LogWriter writer) {
- super(options, writer);
- }
-
- @Override
- protected Process launchProcess(String cmdLine) throws IOException {
- return null;
- }
-
- @Override
- protected void WaitForProcessExit(Process process) {
- }
-
-}
diff --git a/tools/preload2/src/com/android/preload/ui/IUI.java b/tools/preload2/src/com/android/preload/ui/IUI.java
deleted file mode 100644
index 9371463..0000000
--- a/tools/preload2/src/com/android/preload/ui/IUI.java
+++ /dev/null
@@ -1,45 +0,0 @@
-package com.android.preload.ui;
-
-import com.android.ddmlib.Client;
-import java.io.File;
-import java.util.List;
-import javax.swing.Action;
-import javax.swing.ListModel;
-import javax.swing.table.TableModel;
-
-/**
- * UI abstraction for the tool. This allows a graphical mode, command line mode,
- * or silent mode.
- */
-public interface IUI {
-
- void prepare(ListModel<Client> clientListModel, TableModel dataTableModel,
- List<Action> actions);
-
- void ready();
-
- boolean isSingleThreaded();
-
- Client getSelectedClient();
-
- int getSelectedDataTableRow();
-
- void showWaitDialog();
-
- void updateWaitDialog(String s);
-
- void hideWaitDialog();
-
- void showMessageDialog(String s);
-
- boolean showConfirmDialog(String title, String message);
-
- String showInputDialog(String message);
-
- <T> T showChoiceDialog(String title, String message, T[] choices);
-
- File showSaveDialog();
-
- File[] showOpenDialog(boolean multi);
-
-}
diff --git a/tools/preload2/src/com/android/preload/ui/NullProgressMonitor.java b/tools/preload2/src/com/android/preload/ui/NullProgressMonitor.java
deleted file mode 100644
index f45aad0..0000000
--- a/tools/preload2/src/com/android/preload/ui/NullProgressMonitor.java
+++ /dev/null
@@ -1,39 +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.preload.ui;
-
-import com.android.ddmlib.SyncService.ISyncProgressMonitor;
-
-public class NullProgressMonitor implements ISyncProgressMonitor {
-
- @Override
- public void advance(int arg0) {}
-
- @Override
- public boolean isCanceled() {
- return false;
- }
-
- @Override
- public void start(int arg0) {}
-
- @Override
- public void startSubTask(String arg0) {}
-
- @Override
- public void stop() {}
-}
\ No newline at end of file
diff --git a/tools/preload2/src/com/android/preload/ui/SequenceUI.java b/tools/preload2/src/com/android/preload/ui/SequenceUI.java
deleted file mode 100644
index dc6a4f3..0000000
--- a/tools/preload2/src/com/android/preload/ui/SequenceUI.java
+++ /dev/null
@@ -1,222 +0,0 @@
-package com.android.preload.ui;
-
-import com.android.ddmlib.Client;
-import com.android.ddmlib.ClientData;
-import java.io.File;
-import java.util.LinkedList;
-import java.util.List;
-import javax.swing.Action;
-import javax.swing.ListModel;
-import javax.swing.table.TableModel;
-
-public class SequenceUI implements IUI {
-
- private ListModel<Client> clientListModel;
- @SuppressWarnings("unused")
- private TableModel dataTableModel;
- private List<Action> actions;
-
- private List<Object> sequence = new LinkedList<>();
-
- public SequenceUI() {
- }
-
- @Override
- public boolean isSingleThreaded() {
- return true;
- }
-
- @Override
- public void prepare(ListModel<Client> clientListModel, TableModel dataTableModel,
- List<Action> actions) {
- this.clientListModel = clientListModel;
- this.dataTableModel = dataTableModel;
- this.actions = actions;
- }
-
- public SequenceUI action(Action a) {
- sequence.add(a);
- return this;
- }
-
- public SequenceUI action(Class<? extends Action> actionClass) {
- for (Action a : actions) {
- if (actionClass.equals(a.getClass())) {
- sequence.add(a);
- return this;
- }
- }
- throw new IllegalArgumentException("No action of class " + actionClass + " found.");
- }
-
- public SequenceUI confirmYes() {
- sequence.add(Boolean.TRUE);
- return this;
- }
-
- public SequenceUI confirmNo() {
- sequence.add(Boolean.FALSE);
- return this;
- }
-
- public SequenceUI input(String input) {
- sequence.add(input);
- return this;
- }
-
- public SequenceUI input(File... f) {
- sequence.add(f);
- return this;
- }
-
- public SequenceUI output(File f) {
- sequence.add(f);
- return this;
- }
-
- public SequenceUI tableRow(int i) {
- sequence.add(i);
- return this;
- }
-
- private class ClientSelector {
- private String pkg;
-
- public ClientSelector(String pkg) {
- this.pkg = pkg;
- }
-
- public Client getClient() {
- for (int i = 0; i < clientListModel.getSize(); i++) {
- ClientData cd = clientListModel.getElementAt(i).getClientData();
- if (cd != null) {
- String s = cd.getClientDescription();
- if (pkg.equals(s)) {
- return clientListModel.getElementAt(i);
- }
- }
- }
- throw new RuntimeException("Didn't find client " + pkg);
- }
- }
-
- public SequenceUI client(String pkg) {
- sequence.add(new ClientSelector(pkg));
- return this;
- }
-
- public SequenceUI choice(String pattern) {
- sequence.add(pattern);
- return this;
- }
-
- @Override
- public void ready() {
- // Run the actions.
- // No iterator or foreach loop as the sequence will be emptied while running.
- try {
- while (!sequence.isEmpty()) {
- Object next = sequence.remove(0);
- if (next instanceof Action) {
- ((Action)next).actionPerformed(null);
- } else {
- throw new IllegalStateException("Didn't expect a non-action: " + next);
- }
- }
- } catch (Exception e) {
- e.printStackTrace(System.out);
- }
-
- // Now shut down.
- System.exit(0);
- }
-
- @Override
- public Client getSelectedClient() {
- Object next = sequence.remove(0);
- if (next instanceof ClientSelector) {
- return ((ClientSelector)next).getClient();
- }
- throw new IllegalStateException("Unexpected: " + next);
- }
-
- @Override
- public int getSelectedDataTableRow() {
- Object next = sequence.remove(0);
- if (next instanceof Integer) {
- return ((Integer)next).intValue();
- }
- throw new IllegalStateException("Unexpected: " + next);
- }
-
- @Override
- public void showWaitDialog() {
- }
-
- @Override
- public void updateWaitDialog(String s) {
- System.out.println(s);
- }
-
- @Override
- public void hideWaitDialog() {
- }
-
- @Override
- public void showMessageDialog(String s) {
- System.out.println(s);
- }
-
- @Override
- public boolean showConfirmDialog(String title, String message) {
- Object next = sequence.remove(0);
- if (next instanceof Boolean) {
- return ((Boolean)next).booleanValue();
- }
- throw new IllegalStateException("Unexpected: " + next);
- }
-
- @Override
- public String showInputDialog(String message) {
- Object next = sequence.remove(0);
- if (next instanceof String) {
- return (String)next;
- }
- throw new IllegalStateException("Unexpected: " + next);
- }
-
- @Override
- public <T> T showChoiceDialog(String title, String message, T[] choices) {
- Object next = sequence.remove(0);
- if (next instanceof String) {
- String s = (String)next;
- for (T t : choices) {
- if (t.toString().contains(s)) {
- return t;
- }
- }
- return null;
- }
- throw new IllegalStateException("Unexpected: " + next);
- }
-
- @Override
- public File showSaveDialog() {
- Object next = sequence.remove(0);
- if (next instanceof File) {
- System.out.println(next);
- return (File)next;
- }
- throw new IllegalStateException("Unexpected: " + next);
- }
-
- @Override
- public File[] showOpenDialog(boolean multi) {
- Object next = sequence.remove(0);
- if (next instanceof File[]) {
- return (File[])next;
- }
- throw new IllegalStateException("Unexpected: " + next);
- }
-
-}
diff --git a/tools/preload2/src/com/android/preload/ui/SwingUI.java b/tools/preload2/src/com/android/preload/ui/SwingUI.java
deleted file mode 100644
index cab3744..0000000
--- a/tools/preload2/src/com/android/preload/ui/SwingUI.java
+++ /dev/null
@@ -1,291 +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.preload.ui;
-
-import com.android.ddmlib.Client;
-import com.android.ddmlib.ClientData;
-
-import java.awt.BorderLayout;
-import java.awt.Component;
-import java.awt.Dimension;
-import java.io.File;
-import java.util.List;
-
-import javax.swing.Action;
-import javax.swing.DefaultListCellRenderer;
-import javax.swing.JDialog;
-import javax.swing.JFileChooser;
-import javax.swing.JFrame;
-import javax.swing.JLabel;
-import javax.swing.JList;
-import javax.swing.JOptionPane;
-import javax.swing.JProgressBar;
-import javax.swing.JScrollPane;
-import javax.swing.JTable;
-import javax.swing.JToolBar;
-import javax.swing.ListModel;
-import javax.swing.SwingUtilities;
-import javax.swing.table.TableModel;
-
-public class SwingUI extends JFrame implements IUI {
-
- private JList<Client> clientList;
- private JTable dataTable;
-
- // Shared file chooser, means the directory is retained.
- private JFileChooser jfc;
-
- public SwingUI() {
- super("Preloaded-classes computation");
- }
-
- @Override
- public boolean isSingleThreaded() {
- return false;
- }
-
- @Override
- public void prepare(ListModel<Client> clientListModel, TableModel dataTableModel,
- List<Action> actions) {
- getContentPane().add(new JScrollPane(clientList = new JList<Client>(clientListModel)),
- BorderLayout.WEST);
- clientList.setCellRenderer(new ClientListCellRenderer());
- // clientList.addListSelectionListener(listener);
-
- dataTable = new JTable(dataTableModel);
- getContentPane().add(new JScrollPane(dataTable), BorderLayout.CENTER);
-
- JToolBar toolbar = new JToolBar(JToolBar.HORIZONTAL);
- for (Action a : actions) {
- if (a == null) {
- toolbar.addSeparator();
- } else {
- toolbar.add(a);
- }
- }
- getContentPane().add(toolbar, BorderLayout.PAGE_START);
-
- setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
- setBounds(100, 100, 800, 600);
-
- setVisible(true);
- }
-
- @Override
- public void ready() {
- }
-
- @Override
- public Client getSelectedClient() {
- return clientList.getSelectedValue();
- }
-
- @Override
- public int getSelectedDataTableRow() {
- return dataTable.getSelectedRow();
- }
-
- private JDialog currentWaitDialog = null;
-
- @Override
- public void showWaitDialog() {
- if (currentWaitDialog == null) {
- currentWaitDialog = new JDialog(this, "Please wait...", true);
- currentWaitDialog.getContentPane().add(new JLabel("Please be patient."),
- BorderLayout.CENTER);
- JProgressBar progress = new JProgressBar(JProgressBar.HORIZONTAL);
- progress.setIndeterminate(true);
- currentWaitDialog.getContentPane().add(progress, BorderLayout.SOUTH);
- currentWaitDialog.setSize(200, 100);
- currentWaitDialog.setLocationRelativeTo(null);
- showWaitDialogLater();
- }
- }
-
- private void showWaitDialogLater() {
- SwingUtilities.invokeLater(new Runnable() {
- @Override
- public void run() {
- if (currentWaitDialog != null) {
- currentWaitDialog.setVisible(true); // This is blocking.
- }
- }
- });
- }
-
- @Override
- public void updateWaitDialog(String s) {
- if (currentWaitDialog != null) {
- ((JLabel) currentWaitDialog.getContentPane().getComponent(0)).setText(s);
- Dimension prefSize = currentWaitDialog.getPreferredSize();
- Dimension curSize = currentWaitDialog.getSize();
- if (prefSize.width > curSize.width || prefSize.height > curSize.height) {
- currentWaitDialog.setSize(Math.max(prefSize.width, curSize.width),
- Math.max(prefSize.height, curSize.height));
- currentWaitDialog.invalidate();
- }
- }
- }
-
- @Override
- public void hideWaitDialog() {
- if (currentWaitDialog != null) {
- currentWaitDialog.setVisible(false);
- currentWaitDialog = null;
- }
- }
-
- @Override
- public void showMessageDialog(String s) {
- // Hide the wait dialog...
- if (currentWaitDialog != null) {
- currentWaitDialog.setVisible(false);
- }
-
- try {
- JOptionPane.showMessageDialog(this, s);
- } finally {
- // And reshow it afterwards...
- if (currentWaitDialog != null) {
- showWaitDialogLater();
- }
- }
- }
-
- @Override
- public boolean showConfirmDialog(String title, String message) {
- // Hide the wait dialog...
- if (currentWaitDialog != null) {
- currentWaitDialog.setVisible(false);
- }
-
- try {
- return JOptionPane.showConfirmDialog(this, title, message, JOptionPane.YES_NO_OPTION)
- == JOptionPane.YES_OPTION;
- } finally {
- // And reshow it afterwards...
- if (currentWaitDialog != null) {
- showWaitDialogLater();
- }
- }
- }
-
- @Override
- public String showInputDialog(String message) {
- // Hide the wait dialog...
- if (currentWaitDialog != null) {
- currentWaitDialog.setVisible(false);
- }
-
- try {
- return JOptionPane.showInputDialog(message);
- } finally {
- // And reshow it afterwards...
- if (currentWaitDialog != null) {
- showWaitDialogLater();
- }
- }
- }
-
- @Override
- @SuppressWarnings("unchecked")
- public <T> T showChoiceDialog(String title, String message, T[] choices) {
- // Hide the wait dialog...
- if (currentWaitDialog != null) {
- currentWaitDialog.setVisible(false);
- }
-
- try{
- return (T)JOptionPane.showInputDialog(this,
- title,
- message,
- JOptionPane.QUESTION_MESSAGE,
- null,
- choices,
- choices[0]);
- } finally {
- // And reshow it afterwards...
- if (currentWaitDialog != null) {
- showWaitDialogLater();
- }
- }
- }
-
- @Override
- public File showSaveDialog() {
- // Hide the wait dialog...
- if (currentWaitDialog != null) {
- currentWaitDialog.setVisible(false);
- }
-
- try{
- if (jfc == null) {
- jfc = new JFileChooser();
- }
-
- int ret = jfc.showSaveDialog(this);
- if (ret == JFileChooser.APPROVE_OPTION) {
- return jfc.getSelectedFile();
- } else {
- return null;
- }
- } finally {
- // And reshow it afterwards...
- if (currentWaitDialog != null) {
- showWaitDialogLater();
- }
- }
- }
-
- @Override
- public File[] showOpenDialog(boolean multi) {
- // Hide the wait dialog...
- if (currentWaitDialog != null) {
- currentWaitDialog.setVisible(false);
- }
-
- try{
- if (jfc == null) {
- jfc = new JFileChooser();
- }
-
- jfc.setMultiSelectionEnabled(multi);
- int ret = jfc.showOpenDialog(this);
- if (ret == JFileChooser.APPROVE_OPTION) {
- return jfc.getSelectedFiles();
- } else {
- return null;
- }
- } finally {
- // And reshow it afterwards...
- if (currentWaitDialog != null) {
- showWaitDialogLater();
- }
- }
- }
-
- private class ClientListCellRenderer extends DefaultListCellRenderer {
-
- @Override
- public Component getListCellRendererComponent(JList<?> list, Object value, int index,
- boolean isSelected, boolean cellHasFocus) {
- ClientData cd = ((Client) value).getClientData();
- String s = cd.getClientDescription() + " (pid " + cd.getPid() + ")";
- return super.getListCellRendererComponent(list, s, index, isSelected, cellHasFocus);
- }
- }
-}